Skip to content

Module: MCP

Optional. Everything in this chapter binds only an implementation that advertises the module IRI https://schema.sempods.org/module/mcp at the conformance endpoint (SPS-CORE-005).

A pod's MCP endpoint is an agent-facing projection of surfaces the core chapters already define. It adds no authority and no data path of its own — which is the property most of the requirements below exist to preserve.

Status: descriptive. See ../../GOVERNANCE.md.

Profiles: the Model Context Protocol, JSON-RPC 2.0, RFC 9728, RFC 8252 §7.3. Error codes are ../core/index.md §5.

1. The endpoint

SPS-MCP-001 — An implementation MUST serve POST {pod}/_system/mcp speaking JSON-RPC 2.0 over application/json.

SPS-MCP-002 — It MUST support initialize, notifications/initialized, tools/list and tools/call.

SPS-MCP-003 — It MUST answer resources/list and prompts/list with an empty collection. Every capability is exposed as a tool.

The empty answers are not placeholders. Several clients probe those methods on connect, and a method not found is noise a reader of the logs then has to learn to ignore.

SPS-MCP-004 — Error responses MUST use JSON-RPC codes: parse error, invalid request, method or tool not found, invalid params, internal error. A rejected bearer MUST be a distinct code paired with HTTP 401, and an unknown pod a distinct code paired with HTTP 404.

2. Authentication modes

SPS-MCP-005 — Anonymous access MUST be supported. With no bearer at all, a caller MUST be able to initialize, list tools, and use the read tools against the pod's public contexts.

Anonymous is a supported mode on a pod's own endpoint, not a degraded one. An implementation MUST NOT require authentication for a public read here.

SPS-MCP-006 — Write tools MUST require a bearer.

SPS-MCP-007 — An invalid bearer — expired, tampered, wrong signature — MUST be rejected. An implementation MUST NOT silently downgrade it to anonymous.

SPS-MCP-008 — The sandbox for a bearer MUST be the grants resolved server-side for its (client, subject) pair, unioned with the pod's public contexts only where the token carries public-read (SPS-GRANT-020).

SPS-MCP-009 — Every 401 MUST carry WWW-Authenticate: Bearer naming the pod's realm and the pod-level Protected Resource Metadata URL.

The pod is the protected resource for an MCP caller exactly as it is for an HTTP caller, so both get the same metadata address. A separate MCP-level resource identity would fork the OAuth flow for no gain.

3. The authorize tool

SPS-MCP-010 — An implementation MUST expose a synthetic authorize tool, and it MUST be visible in tools/list in every authentication mode.

Most MCP clients are defensive: they list contexts, see nothing writable, and tell the person to reconnect by hand rather than calling a write tool that would have produced the 401 the OAuth flow needs. A tool the model can call the moment more access is wanted is what covers that case, and it has to be visible before the caller has any access at all.

SPS-MCP-011 — Calling it without sufficient access MUST produce the 401 challenge of SPS-MCP-009. Calling it with a context-granted bearer and no re-authorization request MUST be an idempotent acknowledgement.

SPS-MCP-029 — The authorize tool MUST declare exactly one argument, a boolean reauthorize, OPTIONAL and defaulting to false. Its schema is closed like every other (SPS-MCP-024).

SPS-MCP-030 — Its acknowledgement result MUST be an object naming the pod and the contexts the caller may write. A challenge is not a result: it is the 401 of SPS-MCP-009, not a tool error.

The tool is synthetic — it projects no HTTP operation — so a client cannot infer its shape from anywhere else. Left undefined, each implementation invents a flag and the flow that exists to recover access stops working across implementations, which is the one thing it cannot afford.

SPS-MCP-012 — Where reauthorize is true, the implementation MUST issue the challenge even for a bearer that would otherwise suffice, and MUST revoke the refresh tokens of the affected (pod, client, subject).

An explicit re-authorization means review the current consent. Leaving parallel sessions able to rotate around the consent screen would make the review cosmetic.

SPS-MCP-013 — An implementation MUST distinguish the client's automatic replay after the OAuth round trip from a fresh re-authorization request, and MUST answer the replay with the acknowledgement rather than a second challenge.

The two calls are near-identical: same tool, same arguments, same client. What separates them is the bearer — the replay carries a token issued at or after the moment the challenge was recorded, and a different token identity. An implementation that cannot tell them apart puts the client in a challenge loop.

SPS-MCP-014 — A recorded challenge MUST expire, and MUST be consumable exactly once.

SPS-MCP-031 — Some clients treat the MCP URL itself as the protected-resource identifier. An implementation MUST therefore serve Protected Resource Metadata for that URL too — at the host-rooted form RFC 9728 §3.1 constructs from it, and at the append form on the MCP URL. Both MUST return the pod-level document.

The MCP URL is an alternative spelling of the same protected resource, not a resource of its own: the pod stays the unit of access control (SPS-AUTH-045).

SPS-MCP-032 — An implementation MUST NOT serve Authorization Server Metadata for the MCP URL.

The MCP URL is not an issuer identifier, and RFC 8414 §3.3 requires the issuer a document serves to match the URL it was fetched from. There is no document that could satisfy both.

4. Client registration

SPS-MCP-015 — An end-user MCP client MUST use dynamic registration (SPS-AUTH-008). An implementation MUST NOT expect one to present a did:web: identity.

Such a client has no stable origin of its own, so there is nothing for a did:web: identifier to name.

SPS-MCP-016 — Where a client re-registers with the same stable metadata, the implementation MUST return the existing client identifier rather than minting a new one. The dedup key MUST be derived from the registration's stable parts, with loopback redirect ports normalised away (SPS-AUTH-020).

Clients that keep no persistent state re-register on every reconnect. Without dedup, each reconnect mints an identifier the person never consented to and orphans the consent they gave — so the pod accumulates dead grants and the user is asked again every time. Normalising the loopback port is what makes the key survive the ephemeral port a desktop client picks per launch.

5. Tools

SPS-MCP-017 — The tool catalogue MUST cover context discovery, query, retrieval, resource read, resource write, and slot-level property editing:

Read Write
list_contexts create_resource
sparql_select, sparql_graph update_resource
find delete_resource
get_resource set_property_values
get_property_values add_property_value
remove_property_value
clear_property_values

SPS-MCP-018 — Every tool MUST be a projection of the HTTP surface the core chapters define. An implementation MUST NOT give a tool an authority, a sandbox or a write path the HTTP surface does not have.

SPS-MCP-019 — The query tools MUST dispatch through the same validation and the same sandbox as the HTTP query surface (SPS-SPARQL-020).

SPS-MCP-020 — A write tool MUST carry its target context as an argument, and MUST resolve exactly one.

SPS-MCP-021 — Tools MUST accept absolute IRIs only, and MUST perform no prefix or CURIE expansion (SPS-CRUD-025).

SPS-MCP-022 — An implementation MUST accept any absolute resource IRI, including one in the pod's own control-plane area (SPS-CRUD-011).

SPS-MCP-023 — Where a tool wraps one of the three operations for which SPS-CRUD-044 defines an outcome word — adding a value to a slot, clearing a slot, removing an edge — its result MUST carry that same word. A tool wrapping any other write MUST NOT invent one.

A wholesale replace has no second case to report, which is why SPS-CRUD-044 gives it no word and why this requirement must not demand one: an implementation asked to mirror a word that does not exist has to make one up, and two implementations would make up different ones.

An agent and a plain HTTP client reporting the same event in different words is a support burden that never ends, and the two surfaces have no reason to disagree.

Closed schemas

SPS-MCP-024 — Every tool schema MUST declare that no additional arguments are permitted, and the implementation MUST enforce it: a call carrying an argument the tool does not declare MUST be rejected before dispatch.

SPS-MCP-025 — An unknown argument MUST NOT be silently dropped.

This is the module's most important requirement and the one an implementation is most likely to get wrong by doing the ordinary thing. The caller is a language model, and a model that invents a plausible argument — a filter the specification does not have, a mistyped context, a second body field alongside the real one — must not have it ignored. Ignoring it executes a different operation than the one the model asked for, broader on a read and wrong on a write, and returns success. Failing closed turns a hallucination into an error message the model can act on.

SPS-MCP-026 — The advertised schema and the enforcement MUST come from one source, so they cannot drift.

6. Session instructions

SPS-MCP-027initialize SHOULD return per-session instructions naming the pod, the contexts granted with their permission levels, which of them are writable, and how to discover the pod's vocabulary.

SPS-MCP-028 — Where instructions are returned, they MUST be regenerated on every initialize, so that a reconnect after a consent change reflects the new access.

A pod pins no vocabulary, so a client cannot assume one. Telling it at connect time what is there and how to look is what replaces the schema an agent would otherwise guess at.

7. Not defined here

A hosted multi-pod MCP service — one connection fronting many pods, including pods run by other people — is a different thing from a pod's own endpoint and is not specified by this module. Two differences are worth knowing so nothing here is read into it: such a service has no anonymous mode, and toward each pod it is an ordinary OAuth client rather than part of the pod.

Also outside: which upstream MCP protocol revisions an implementation negotiates, per-client quirks, and audit-log shape.