Amendment A102 · Smeldr core v1.24.0
Some content types have no business being on the public web. A HomePage managed entirely via MCP tools, a PlatformConfig set once at launch, a NavTree built from a CMS dashboard — these are admin objects, not pages. Yet Smeldr registered HTML routes for all modules, which meant a browser visiting /home-pages would receive a JSON blob instead of a 404. Confusing for humans, potentially confusing for crawlers.
smeldr.APIOnly() fixes this with a single option:
smeldr.NewModule((*HomePage)(nil),
smeldr.At("/home-pages"),
smeldr.Repo(repo),
smeldr.MCP(smeldr.MCPWrite),
smeldr.APIOnly(),
)
Now:
GET /home-pageswithAccept: text/html→ 404GET /home-pageswithAccept: application/json→ 200 JSON (unchanged)- All MCP tools present —
create_home_page,update_home_page,publish_home_page, etc. smeldr-cliworks via the REST API without any changes- Preview token bypass still works for agents (they use JSON)
The response is 404, not 406. 404 says "this URL has no browsable surface" — search engines won't attempt to index it. 406 would say "I can't produce HTML" which leaks the fact that content is there. The defensive posture matches how Smeldr already handles Draft content: 404 intentionally hides existence.
One guard is added: APIOnly() and SingleInstance() cannot be combined — SingleInstance serves HTML at the module prefix, which is exactly what APIOnly forbids. NewModule panics at startup if both are supplied.
Routing variants summary (v1.24.0):
| Option | HTML surface | MCP tools |
|---|---|---|
| *(default)* | Full | Full set |
SingleInstance() | GET /{prefix} only | list_{type}s suppressed |
Standalone() | GET /{slug} | Full set |
APIOnly() | None | Full set |