Repository navigation
Agent Ready
oCMS exposes a set of standards-based discovery surfaces so AI agents can find the programmable interface of any oCMS-powered site from a single request to the homepage. The checks are those exercised by isitagentready.com.
| Surface | Path | Spec |
|---|---|---|
| Sitemap | /sitemap.xml |
sitemaps.org |
| Robots | /robots.txt |
RFC 9309 |
| Content preferences |
Content-Signal: directive inside /robots.txt
|
contentsignals.org, draft-romm-aipref-contentsignals |
| Homepage link header |
Link: response header on GET /
|
RFC 8288 |
| Markdown for Agents |
Accept: text/markdown on GET / and GET /{slug}
|
Cloudflare Markdown for Agents |
| API Catalog | /.well-known/api-catalog |
RFC 9727 (linkset format RFC 9264) |
| Agent Skills index | /.well-known/agent-skills/index.json |
Cloudflare Agent Skills Discovery RFC v0.2.0 |
| MCP Server Card | /.well-known/mcp/server-card.json |
draft SEP-1649 (REST-only card while the MCP module is off) |
| Security contact | /.well-known/security.txt |
RFC 9116 (unrelated, included for completeness) |
The API Catalog, Agent Skills index and MCP Server Card build absolute URLs
from the configured site_url, so each answers 503 Service Unavailable
until it is set. The MCP Server Card additionally requires site_url to be
a plain http(s) origin (scheme and host, no path).
The Link header on GET / advertises three relations:
-
rel="api-catalog"→/.well-known/api-catalog -
rel="service-desc"→/api/v2/openapi.json -
rel="service-doc"→/api/v2/docs(Swagger UI)
| Config key | Default | Purpose |
|---|---|---|
robots_content_signal |
search=yes, ai-train=no, ai-input=yes |
Value emitted as Content-Signal: ... line in robots.txt. Set to off, none, or disabled to suppress the directive. |
mcp_server_version |
empty |
serverInfo.version of the REST-bridge MCP Server Card served while the MCP module is off (empty → 0.0.0). While the module is on, the card reports the running MCP server's own version. |
Both keys live in the admin Config table and can be edited via
/admin/config.
oCMS ships an MCP transport as the opt-in MCP Server module: Streamable
HTTP at POST /api/mcp with read-only tools (see
Module: MCP Server). The server card is always served and
follows the module's state:
-
Module active: the card names the endpoint in the SEP-1649
transportobject ("type": "streamable-http","endpoint": "https://…/api/mcp"). It declares the draft's$schema, card formatversion, preferred supportedprotocolVersion, andcapabilities.tools, and still links the REST API undercapabilities.rest.openapi. ItsserverInfo(ocms, titleoCMS, the module version) is exactly what the endpoint reports atinitialize. -
Module inactive (the default): the card omits the schema and protocol
fields, keeps
"transport": null, and declares only the REST fallback, so it never advertises an endpoint that would answer 404.
seo.BuildMCPServerCard receives the endpoint from
FrontendHandler.SetMCPEndpointProvider, which cmd/ocms/main.go wires to
the module registry's active status. Because toggling the module changes the
card, it is served with Cache-Control: no-cache.
The homepage (/) and page routes (/{slug}, and their language-prefixed
variants) honor Accept: text/markdown. When the header signals a
markdown preference, the handler returns a plain-text Markdown
representation in place of the HTML theme output. Markdown carries absolute
URLs, so it is served only when site_url is configured; otherwise the
request falls through to HTML:
Content-Type: text/markdown; charset=utf-8-
Vary: Accept(also set on the HTML response so reverse-proxy caches keep the two representations keyed separately) -
X-Markdown-Tokens: <n>— coarse whitespace-token count for agents that estimate context budget - HTML remains the default for any
Acceptvalue that does not prefertext/markdown(browsers with*/*,text/html, or no header)
The page body is converted from stored HTML (TinyMCE output) using
JohannesKaufmann/html-to-markdown/v2.
<script> and <iframe> are dropped by construction since CommonMark
has no analogue; see the drift test
TestFrontendHandler_Page_Markdown_ScriptsStripped.
A 2 MB size cap on the input HTML guards against CPU DoS from a single oversized page; on cap overflow the handler logs the error and falls back to the HTML representation so callers never see a blank response. Authentication parity with the HTML path is enforced by running the negotiation branch after the draft-preview guard — drafts remain admin/editor-only in both representations.
Implementation lives in internal/seo/markdown/negotiate.go; the
wiring is in internal/handler/frontend.go (Home and Page).
The sha256 field of the ocms-rest-api skill is the hex SHA-256 of the
exact OpenAPI JSON bytes served at /api/v2/openapi.json. cmd/ocms/main.go
wires those bytes into the frontend handler (SetOpenAPISpecProvider) once
the v2 API is built; the digest is computed with seo.ComputeSHA256Hex on
the first request to the index and cached for the process lifetime, since
the spec is deterministic from compiled-in Go types. If no provider is wired
or the computation fails, the field is an empty string and the next request
retries.
# Boot
OCMS_SESSION_SECRET=test-secret-key-32-bytes-long!!! make dev &
sleep 3
# Link header on homepage
curl -sI http://localhost:8080/ | grep -i '^link:'
# Markdown for Agents on homepage and single page
curl -sD- -H 'Accept: text/markdown' http://localhost:8080/ | head -20
curl -sD- -H 'Accept: text/markdown' http://localhost:8080/<slug> | head -20
# Content-Signal directive in robots.txt
curl -s http://localhost:8080/robots.txt | grep -i '^content-signal:'
# RFC 9727 catalog (expects application/linkset+json)
curl -sS -D - http://localhost:8080/.well-known/api-catalog
# Agent Skills v0.2.0 index
curl -s http://localhost:8080/.well-known/agent-skills/index.json | jq .
# MCP Server Card (SEP-1649); shows a transport object once the MCP module is active
curl -s http://localhost:8080/.well-known/mcp/server-card.json | jq .
# MCP endpoint (module active): 405 for GET, 401 with a Bearer challenge for POST
curl -si http://localhost:8080/api/mcp | head -1
curl -si -X POST http://localhost:8080/api/mcp | grep -i '^www-authenticate'After deploying changes to a public site, re-run the scanner: https://isitagentready.com/www.example.com. The categories that should flip to pass after this change are: Link headers, Content Signals, API Catalog, MCP Server Card, Agent Skills, and Markdown for Agents (which promotes the site to Level 3 — Agent-Readable).
-
WebMCP —
navigator.modelContext.provideContext()calls on the admin dashboard to expose oCMS actions as in-browser tools. Phase 2. -
OAuth 2.0 / OIDC discovery — currently oCMS authenticates REST and
MCP clients via static API keys (MCP keys need the
mcp:accesspermission). Publishing/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resourcerequires a real authorization server. Phase 3. -
Web Bot Auth — informational only in the scan; requires a JWKS at
/.well-known/http-message-signatures-directoryand signed outbound requests. Phase 3.
- SEO — sitemap, robots, meta tags
- REST API — the surface advertised by the API Catalog and Agent Skills
- Security — security contact surface and HTTP headers
- Module: MCP Server — the MCP endpoint the server card names
Getting Started
Core Features
- Content Management
- Media Library
- Taxonomy
- Menu Builder
- Forms
- Multi-Language
- SEO
- Feeds
- Video Embedding
- Agent-Ready
Administration
API & Integrations
Security
Theme Development
Module Development
Built-in Modules
- Module: Sentinel
- Module: Developer
- Module: DB Manager
- Module: Migrator
- Module: Informer
- Module: Demo Mode
- Module: Internal Analytics
- Module: External Analytics
- Module: Privacy
- Module: Embed
- Module: MCP Server
Deployment