Skip to content

Module: MCP

Development snapshot — not a release

Core 0.1-dev · Source revision and matching artifacts.

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: this text decides, and can still change. 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.

For HTTP authorization, this chapter selects MCP 2025-11-25 Authorization: the Authorization Server Discovery, Resource Parameter Implementation and Access Token Usage sections, and Token Audience Binding and Validation. Within those capabilities it profiles RFC 9728 §§2–7, RFC 8414 §§2–4 and 6 and RFC 8707 §§2–3. SPS-MCP-033–038 select the resource identity, discovery addresses and single-resource credentials. This selection does not incorporate the rest of that authorization revision, its draft OAuth 2.1 profile, Client ID Metadata Documents or additional transports. Registration and consent remain the flows in core auth and §4 below.

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 — Protocol errors 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.

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 absolute endpoint-specific Protected Resource Metadata URL required by SPS-MCP-033 in resource_metadata.

The realm remains the pod base. It is a challenge label, not a discovery input: a standard client starts from its configured MCP URL and the metadata hint. For a write tool called without a bearer token, the challenge follows SPS-CORE-015:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="https://example.org/alice", resource_metadata="https://example.org/.well-known/oauth-protected-resource/alice/_system/mcp"

With a rejected token, the challenge includes invalid_token:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="https://example.org/alice", error="invalid_token", resource_metadata="https://example.org/.well-known/oauth-protected-resource/alice/_system/mcp"

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, where that bearer names a person, MUST end what the affected (pod, client) holds for them — the refresh tokens and any authorization code not yet exchanged — across every equivalent identity URI (SPS-AUTH-061) rather than the one the bearer carries.

An explicit re-authorization means review the current consent. A family recorded under another of the person's URIs rotates around that screen; so does a code from an earlier consent, which SPS-AUTH-062 does not catch on its own — recording a challenge changes no decision, so the older answer still stands behind that code. The sweep and an exchange already under way order themselves by SPS-AUTH-063: both write first, so a family seeded during the sweep does not outlive it.

A service token names no person (SPS-AUTH-017) and an anonymous public-read subject is synthetic and per-request, so for neither is there a person whose credentials the sweep could reach. Both get the challenge.

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 — Withdrawn in 0.1-dev without replacement. Some clients treat the MCP URL itself as the protected-resource identifier. An implementation MUST therefore serve Protected Resource Metadata at the append form on the MCP URL, and it MUST return the pod-level document.

SPS-MCP-032 — Withdrawn in 0.1-dev without replacement. An implementation MUST NOT serve Authorization Server Metadata for the MCP URL.

Discovery and resource binding

Let P be the canonical pod base and M its MCP URL, P followed by /_system/mcp. M identifies the protected MCP resource; P remains the authorization-server issuer under SPS-AUTH-028. A client configured with M learns the issuer from resource metadata. It does not strip a path suffix, parse the realm or need a second configuration field for P.

SPS-MCP-033 — The server MUST serve unauthenticated Protected Resource Metadata for M at the RFC 9728 §3.1 URL, inserting /.well-known/oauth-protected-resource before M's path. The document MUST set resource to exactly M, authorization_servers to [P], and include header in bearer_methods_supported. SPS-AUTH-046's prohibition on enumerating context IRIs applies to this document too.

SPS-MCP-034 — The server MUST also serve the pod's Authorization Server Metadata at the RFC 8414 §3.1 URL for issuer P, inserting /.well-known/oauth-authorization-server before its path, or using that origin-root path for a pathless P. This unauthenticated document MUST be the same as the one required by SPS-AUTH-066, including issuer: P and the pod's authorization, token, registration and signing-key URLs.

These routes require cooperation from the host of a path-scoped pod. A deployment that controls only /alice can still implement core; advertising MCP additionally requires these host-rooted routes. A root pod and several path pods can share an origin because their metadata URLs differ. The pod's append-form resource document continues to describe P; it is not the MCP document.

SPS-MCP-035 — An MCP client MUST use the selected MCP discovery rules with its configured canonical endpoint M as the expected resource, including when it initiates authorization after successful anonymous initialization. It MUST validate resource metadata against exactly M under RFC 9728 §3.3, and authorization-server metadata against the issuer selected from that validated resource document under RFC 8414 §3.3. The pod-local deviations in SPS-AUTH-067 and SPS-AUTH-068 MUST NOT be applied to MCP. Rejected metadata MUST NOT be used to start authorization or send credentials, including by falling back to a parent resource or another issuer.

The client validates the issuer it discovered, without inferring a pod boundary from URL paths. The server's [P] choice is a separate requirement. A client cannot detect a trusted resource server's false declaration merely by comparing two mutually consistent documents. Server-side credential binding supplies the boundary even when origins or signing keys are shared.

SPS-MCP-036 — A client requesting MCP access MUST send exactly one RFC 8707 resource parameter equal to M in authorization and token requests, including refresh and supported service-client token requests. The pod's authorization server MUST support that resource and MUST reject any supplied resource set other than that single M with invalid_target under RFC 8707. Requests with no resource parameter retain the core flow, subject to SPS-MCP-037 for an already resource-bound credential.

SPS-MCP-037 — The authorization server MUST bind an MCP authorization code and any resulting refresh credential to M, in addition to their existing client and subject binding. Exchanging either MUST require the same single resource=M; missing or different resource values MUST fail with invalid_target. An authorization code or refresh credential issued without that binding MUST NOT be exchanged for MCP access; that attempt MUST fail with invalid_target.

SPS-MCP-038 — An MCP access token MUST have iss=P and a JWT aud identifying only M, as a string or a single-element array. The MCP server MUST reject tokens with a missing or different issuer or audience with 401 and the SPS-MCP-009 challenge, even if their signature is valid. A pod advertising this module MUST NOT accept a token bound to M at its other protected-resource endpoints; it MUST reject such a bearer with the endpoint's normal 401 challenge. Existing grant resolution and scope limits remain in force.

This is an audience distinction, not an additional grant or an MCP-specific issuer. A code, refresh credential or access token for Alice cannot authorize Bob. Existing pod-wide credentials do not acquire MCP access merely because the module is enabled; clients obtain a new MCP-bound authorization. Token lifetime, revocation and refresh rotation remain in core auth.

Discovery cases

These are contract examples, not claims about a running implementation. All URLs below share the origin https://example.org; paths are shown for readability.

Pod P MCP resource M Resource metadata path Issuer metadata path
https://example.org https://example.org/_system/mcp /.well-known/oauth-protected-resource/_system/mcp /.well-known/oauth-authorization-server
https://example.org/alice https://example.org/alice/_system/mcp /.well-known/oauth-protected-resource/alice/_system/mcp /.well-known/oauth-authorization-server/alice
https://example.org/bob https://example.org/bob/_system/mcp /.well-known/oauth-protected-resource/bob/_system/mcp /.well-known/oauth-authorization-server/bob
Case Expected result
Anonymous initialization and public read Success without OAuth; a later protected call supplies the MCP metadata hint.
Proactive authorization, without a 401 Standard discovery starts from M and finds its path-specific resource metadata.
Alice's resource document names the origin, the parent pod or Bob's MCP endpoint Reject: none is the configured M.
Issuer metadata fetched for Alice names Bob or Alice's /_system/auth URL Reject: neither is the advertised issuer.
Alice's authorization or token request names Bob's M Alice's authorization server rejects invalid_target.
Alice's code or refresh credential is used at Bob Reject under the core credential binding; no Bob token is issued.
Validly signed bearer names another issuer, the pod audience, another MCP endpoint, or multiple audiences MCP rejects with 401; no anonymous fallback.
Alice's MCP token is sent to Alice's ordinary HTTP data surface or Bob's MCP endpoint Reject with the receiving endpoint's 401 challenge.

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. The write denial specified by SPS-CORE-018 MUST be mapped to an MCP tool execution error over HTTP 200, preserving its protection of context existence.

The MCP tool execution error is a JSON-RPC result with isError: true. The HTTP 200 reports delivery of that result; the write has failed. Missing or rejected credentials retain the 401 challenge defined above.

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-027 — initialize 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.