Federation
How one request reaches several independent servers and locations, and comes back with every item attributable, every failure visible, and no token used where it was not issued. Draft, and deliberately domain-neutral.
Draft. This section is a working draft, published for comment. Its MUST and SHOULD language states what the working group intends, but it is not yet a conformance requirement, and it may change incompatibly before it is. Do not claim conformance with it. If you build against it, tell the working group what broke: that is the evidence that moves a draft.
This section is written in domain-neutral terms. It describes parties, locations and servers rather than dealer groups, rooftops and DMS platforms, because the problem is not specific to retail automotive. Automotive appears only in the examples.
It profiles the 2026-07-28 revision of the Model Context Protocol and adds nothing to the base
protocol. Every mechanism below is either a constraint on something MCP already defines (request
metadata headers, x-mcp-header, the token pass-through prohibition) or a field carried inside a
tool result. It is published as the candidate extension io.automotivemcp/federation.
11.1 The problem
One request often has to reach more than one system. An agent asked to find a vehicle "near me" reads inventory at several businesses. A group that operates several locations runs them on several servers, sometimes from different vendors. A single platform hosts many unrelated tenants behind one endpoint.
Each of those is easy on its own. What is not specified anywhere is what the combined answer looks like: how a location is named so that two servers mean the same one, where each item came from, what the caller is told when one of five systems does not answer, and how the system doing the fanning out gets permission to call the others without reusing a token that was never meant for them.
Without those rules every aggregator invents its own, and the caller cannot tell a complete answer from a partial one, or a figure from the business that owns it from one an intermediary rewrote.
11.2 Terms
| Term | Meaning |
|---|---|
| Location | One operating unit that holds its own data and makes its own commitments: a store, a branch, a clinic. In retail automotive, a rooftop. It is the tenant of §3.2. |
| Group | A set of locations under common control. A group is a label, never a tenant (§11.3.3). |
| Member server | An MCP server that is authoritative for the data of one or more locations. |
| Federating server | An MCP server that answers a request by calling one or more member servers and combining their results. It is a member server's client. |
| Source | The member server that produced a given item, named by its canonical server URI (§11.6). |
A server can be a member for its own locations and a federating server for others at the same time. The rules below apply per request, by role.
11.3 Location identity
11.3.1 The identifier
Every location MUST have a location identifier, location_id, with these properties:
- It MUST be an absolute
httpsURI. Its origin is the authority: the party that assigned the identifier and answers for it. - It MUST be stable. A location keeps its identifier when it is renamed, changes systems, or changes ownership. An authority MUST NOT reassign a retired identifier to a different location.
- It MUST NOT contain personal data, and SHOULD NOT contain anything a reader would take as a commercial secret (internal account numbers, contract identifiers).
- Two identifiers are equal only when they are identical strings. Implementations MUST NOT normalize, case-fold or resolve them before comparing.
https://locations.example-platform.com/l/4f1c9a
https://group.example-motors.com/locations/north-austinThe dealership_id of §3.2 is a deprecated alias of location_id. For this version (v0.1), a
server MUST accept either name wherever a location is required, MUST treat them as the same
identifier, and MUST reject a call that supplies both with different values. Clients SHOULD send
location_id. The alias is scheduled for removal in the next MINOR version (v0.2). A member server
MAY keep using its own internal identifier inside its own system, but anything that crosses a
server boundary, and every provenance record (§11.6), MUST use the location_id.
11.3.2 Same location, several authorities
A location will often be known to several systems, each of which assigned its own identifier. An
authority MAY publish that two identifiers denote the same location through an alias_of link
on its location record. A federating server MAY use an alias to route, but MUST report the
identifier the source used in provenance, and MUST NOT treat an alias asserted by one authority as
binding on another.
Resolving which identifier is canonical when authorities disagree is not specified (§11.11).
11.3.3 Groups are labels, not tenants
A group MAY have a group_id with the same properties as a location_id. A group identifier MUST
NOT be accepted where a location is required. A token, a scope or a tool call that names a group
MUST be expanded to an explicit set of locations, each authorized separately, before any data is
read. This restates §3.2 for the federated case: a group is never a blended tenant.
11.4 Addressing a location on the wire
A tool that operates on a location's data MUST take the location as an argument named
location_id, at the top level of its inputSchema, and SHOULD annotate it with
"x-mcp-header": "Location".
{
"name": "inventory.search_vehicles",
"inputSchema": {
"type": "object",
"properties": {
"location_id": {
"type": "string",
"format": "uri",
"x-mcp-header": "Location"
},
"body_style": { "type": "string" }
},
"required": ["location_id"]
}
}Under the Streamable HTTP transport the client then mirrors the value into an
Mcp-Param-Location header alongside the Mcp-Method and Mcp-Name headers MCP already requires.
That lets a gateway in front of a multi-tenant server route and rate-limit by location without
parsing the body, which is the purpose MCP gives those headers.
Because a gateway may now act on that header, the MCP rules about it are load-bearing here and are restated:
- The
location_idproperty MUST be reachable from the schema root throughpropertieskeys only. MCP invalidates anx-mcp-headerannotation underitems,oneOf,anyOf,allOf,not, a conditional, or a$ref, and a client will drop the tool. - A server that processes the body MUST reject a request whose
Mcp-Param-Locationdoes not match the body value with400 Bad Requestand JSON-RPC error-32020(HeaderMismatch). - An intermediary that routes or authorizes by
Mcp-Param-LocationSHOULD reject a request whoseMCP-Protocol-Versionpredates header and body validation, rather than trust an unvalidated header. - Authorization MUST be decided on the location in the body, after validation, and never on the header alone.
A single tool call MUST name exactly one location. A request that needs several locations is several calls, which a federating server issues on the caller's behalf (§11.7).
11.5 Namespacing
Federation does not change tool names. A federating server MUST expose tools under the same
domain.verb_object names as §5, and MUST NOT prefix or suffix a tool name with a server, vendor
or location to tell members apart. The member is chosen by the location_id argument, not by the
name. This keeps Mcp-Name meaningful to every gateway on the path and keeps the catalog at
/tools.json the only enumeration of tools.
Identifiers inside results are another matter. A resource id is unique only within its source
and location (§3.3). A federating server combining results from several sources:
- MUST NOT present two items with the same
idfrom different sources as the same item. - MUST NOT rewrite a member's
idwithout recording the original in provenance (§11.6). - SHOULD let the caller refer back to an item by the pair of its
sourceand its sourceid, and MUST accept that pair wherever it accepts its own identifier for the item.
Extension data that a member adds beyond the published schemas MUST sit under that member's own reverse-DNS namespace, so that two members' extensions cannot collide in a combined result.
11.6 Provenance
Every item a federating server returns that it did not itself produce MUST carry a provenance
object:
| Field | Required | Meaning |
|---|---|---|
source | MUST | Canonical URI of the member server that produced the item, in the form MCP uses for a server's resource indicator (RFC 8707). |
location_id | MUST | The location the item belongs to (§11.3). |
source_id | MUST | The item's id as the source issued it. |
retrieved_at | MUST | When the federating server received it, RFC 3339 with an explicit offset. |
transformed | MUST | true if the federating server changed any field other than adding provenance. |
via | MAY | The chain of federating servers the item passed through, nearest first, when federation is nested. |
{
"id": "veh_8812",
"vin": "1HGCV1F3XRA012345",
"price": { "amount": 3149900, "currency": "USD" },
"provenance": {
"source": "https://mcp.example-dms.com",
"location_id": "https://group.example-motors.com/locations/north-austin",
"source_id": "veh_8812",
"retrieved_at": "2026-10-08T14:02:11-05:00",
"transformed": false
}
}The rules that give provenance its value:
- A federating server MUST NOT present an item as its own when it came from a member.
- A federating server MUST NOT alter a figure the source committed to (a price, a payment, an
availability flag) while leaving
transformedfalse. Where it computes a figure itself, it MUST mark the item transformed, and SHOULD say which fields it computed. - An item derived from several sources (a de-duplicated listing, a merged record) MUST carry the
provenance of every source it was derived from, as a
derived_fromarray of provenance objects, and MUST be marked transformed. - Provenance is not a signature. It records a claim by the federating server. Whether a caller can verify a figure end to end, without trusting the intermediary, is the subject of substantiation (§13) and is not solved here.
11.7 Fan-out and partial failure
11.7.1 Reads
A federating server that answers one call by calling several members MUST return, alongside the
items, a federation object describing the outcome at each member:
{
"items": [],
"federation": {
"status": "partial",
"members": [
{ "source": "https://mcp.example-dms.com",
"location_id": "https://group.example-motors.com/locations/north-austin",
"status": "ok", "item_count": 14 },
{ "source": "https://mcp.other-dms.example",
"location_id": "https://group.example-motors.com/locations/round-rock",
"status": "timeout", "retryable": true, "retry_after": 30 }
]
}
}statusat the top level MUST becompleteonly when every member the request needed returnedok. It MUST bepartialwhen at least one did and at least one did not, andfailedwhen none did.- Each member entry has a
statusofok,error,timeout,unauthorizedorskipped.skippedmeans the federating server chose not to call it (a circuit breaker, a policy decision), and MUST be reported, not hidden. - A federating server MUST NOT omit a member it was asked to reach, and MUST NOT report
okfor a member that returned an error. - An empty result and a failed result MUST be distinguishable.
okwith anitem_countof0means the member answered and had nothing. - A member's error detail MUST NOT be relayed if it contains personal data (§6.3). A federating
server SHOULD map member errors to the structured codes of §6 and the
retryableandretry_afterfields, and drop the rest. - A partial result is a successful MCP response. It MUST NOT be returned as a JSON-RPC error, and a caller MUST NOT treat it as complete.
A federating server SHOULD apply a per-member deadline and return what it has rather than let the slowest member decide the latency of the whole answer. Pagination across members MUST be carried in the single opaque cursor of §3.4, so that the caller never handles a member's cursor directly.
11.7.2 Writes
A tool call that changes state MUST be addressed to exactly one location and MUST be executed by exactly one member. A federating server MUST NOT fan a mutating call out to several members, and MUST NOT emulate a multi-member transaction. There is no partial success for a write in this profile: a write either happened at its one location or it did not.
Where a task genuinely needs changes at several locations (holding a vehicle at one store while booking an appointment at another), the caller issues separate calls and owns the sequence. Compensation across members is not specified. For this version the prohibition is a decision, not an open question: relaxing it later would need a compensation model.
11.8 Calling a member: token exchange
11.8.1 No pass-through
MCP 2026-07-28 is explicit: a server MUST only accept tokens that are valid for use with its own resources and MUST NOT accept or transit any other tokens, and a server that calls an upstream API MUST NOT pass through the token it received from the MCP client. A federating server is exactly that kind of server. It therefore:
- MUST NOT forward the caller's access token to a member, in any header or argument.
- MUST NOT call a member on a user's behalf with a standing credential of its own whose authority is broader than the user's. That is the confused deputy MCP warns about, and it is the most likely shortcut a federating implementation will take.
- MUST obtain, for each member, a separate token whose audience is that member.
11.8.2 The exchange
A federating server obtains a member token with OAuth 2.0 Token Exchange (RFC 8693) at the member's authorization server:
| Parameter | Value |
|---|---|
grant_type | urn:ietf:params:oauth:grant-type:token-exchange |
subject_token | The access token the federating server received from its caller. |
subject_token_type | urn:ietf:params:oauth:token-type:access_token |
actor_token | A credential identifying the federating server itself. |
resource | The member's canonical server URI (RFC 8707). |
scope | The scopes of §6.2 the member call needs, and no more. |
The issued token, and the member's validation of it, MUST satisfy all of these:
- Audience. Its audience is the member, and the member MUST reject it otherwise, exactly as it would any other token.
- No escalation. Its scopes MUST be a subset of the subject token's, and its locations a subset of the locations the subject token was authorized for. An exchange can only narrow.
- No outliving. It MUST expire no later than the subject token.
- Accountability. It MUST carry an
actclaim (RFC 8693 §4.1) naming the federating server, so the member can record who called on whose behalf. Nested federation produces nestedactclaims; a member MAY refuse a chain deeper than it is willing to audit.
This requires the member's authorization server to accept subject tokens issued by the caller's authorization server. That trust is configured between the parties and is not specified here. It is the same shape MCP's Enterprise-Managed Authorization extension uses (a token exchange followed by a JWT grant), applied between servers instead of between an enterprise identity provider and a server.
A request that is not on any user's behalf (a public inventory read, a nightly sync) MAY use the federating server's own client credentials at the member, provided the member scopes that credential to what it would show an anonymous or service caller.
11.9 Declaring federation
A server that supports this section declares the extension in its discovery response:
{
"extensions": {
"io.automotivemcp/federation": {
"version": "0.1-draft",
"roles": ["federating", "member"],
"max_fan_out": 25,
"token_exchange": true
}
}
}Where a conformant server publishes this, and how a caller finds the member servers for a set of locations in the first place, is discovery (§10) and is not yet written.
11.10 Security and privacy considerations
- Isolation survives aggregation. Each location's data is authorized separately, by the member that holds it, on a token issued for that member. A federating server that caches results MUST key the cache by location and by the authorization the result was fetched under.
- Amplification. One call can become many. A federating server MUST rate-limit by the cost of
the fan-out, not by the count of inbound calls, and MUST honor each member's
rate_limitedsignals (§6.4) without converting them into retries against the same member. - Errors leak. A member's error relayed verbatim can disclose another tenant's data or the existence of a record. See §11.7.1.
- Headers are untrusted until validated. See §11.4.
11.11 Open questions
- Canonical identity across authorities. When two authorities both claim to name a location, who wins? A registry is the obvious answer and the expensive one.
- Should provenance be signed? A signed provenance record would let a caller verify a figure without trusting the federating server. It overlaps substantially with substantiation (§13).
- Cross-authorization-server trust for token exchange is assumed, not specified. Without it §11.8 cannot be implemented between unrelated businesses, only within one platform or group. This is open for a decision by the project owner and working group.
- Should the location header be required rather than recommended, so that every gateway can depend on it?