Summary
The prose instructs MCP callers to send context_id as a tool argument and calls it "Required for continuity". No request schema declares it — 0 of 103 at v3.1.1, 0 of 103 at v3.1.20. Both reference SDKs refuse or silently drop it on MCP, neither reference server ever sets or reads it, no MCP round-trip test exists in either SDK, and the compliance bundle does not grade it.
So a conformant MCP client following the documentation sends a field the schema does not model, to a server neither reference implementation teaches how to read.
The prose (verified at ?ref=v3.1.1)
docs/building/by-layer/L2/context-sessions.mdx:
// Follow-up - must include context_id
const result2 = await mcp.call('get_products', {
...
context_id: contextId, // Required for continuity
"A2A: Context is handled automatically by the protocol / MCP: Requires manual context_id management."
docs/building/by-layer/L0/mcp-guide.mdx, §Context Management (MCP-Specific):
"Critical: MCP requires manual context management. You must pass context_id to maintain conversation state."
with request.arguments.context_id = this.contextId;.
The same file is internally inconsistent — earlier it says the caller "MAY echo it on subsequent calls to thread a session", two sections before "You must pass".
The schemas
core/protocol-envelope.json declares context_id on the RESPONSE:
"Session/conversation identifier for tracking related operations across multiple task invocations. Managed by the protocol layer to maintain conversational context. Distinct from context (per-request opaque echo…)."
Request side, counted over the non-bundled *-request.json:
| version |
request schemas |
declaring context_id |
| 3.1.1 |
103 |
0 |
| 3.1.20 |
103 |
0 |
| 3.2.0-rc.1 |
374 |
4 |
3.2.0-rc.1 adds "context_id": {"type": "string", "minLength": 1} to list-products, request-proposals, refine-proposals and decline-proposals only, never required — and not to create_media_buy or update_media_buy, which are the tools the L2 example itself uses.
ContextObject, the type of the request's context field, has zero properties at 3.1.1, so it cannot carry the id either.
The reference implementations disagree with the prose
Python SDK (adcp 6.6.0) refuses it outright — client.py:587-596:
if context_id:
if not isinstance(self.adapter, A2AAdapter):
raise TypeError(f"context_id is only supported for A2A protocol; got {protocol}")
protocols/mcp.py contains zero occurrences of context_id. There is no MCP path.
TypeScript SDK (adcp-client) drops it — TaskExecutor.ts:1531-1542:
"Pass the caller's A2A session ids (contextId for conversation binding, taskId for resuming a non-terminal server-side task). The adapter drops these on the wire for MCP (no session concept there)."
On A2A it rides message.contextId (protocols/a2a.ts:625,714) — the A2A envelope, not the AdCP body. tools.generated.ts: 4 of 64 request interfaces declare context_id; 33 of 40 response interfaces do.
Neither reference server implements it. The TS server never sets context_id on a response (wrap-envelope.ts, responses.ts, structured-serialize.ts: zero hits) and never reads one from a request; its A2A path threads A2A's own Task.contextId (server/a2a-adapter.ts:462-469,594). The Python server has one occurrence, a2a_server.py:677, also on the A2A pb.Task.
No MCP round-trip test exists in either SDK. The A2A ones do (test/lib/a2a-context-retention.test.js, test/storyboard-a2a-context-continuity.test.js), and both assert on the A2A Message envelope. The one MCP-adjacent test asserts the opposite behaviour — test/lib/task-executor-async-patterns.test.js:550-563, "does not invent an MCP continuation tool for a returned pause": the mocked response carries context_id: 'mcp-context' and the test asserts callTool is called exactly once.
Not graded. context_id appears zero times under _schemas/3.1/compliance/. The only related check in later versions is a2a_context_continuity, which grades A2A 0.3.0 §7.1, not AdCP.
Why it stays hidden
63 of the 103 request schemas at 3.1 are additionalProperties: true, so a client following the prose gets no validation error — the field is simply unmodelled and ignored.
Expected
Pick one and make the three artifacts agree:
- Context continuation is real on MCP — then
context_id belongs on the request envelope (alongside adcp_version in core/version-envelope.json, or a shared request envelope), not on 4 of 374 tools, and the reference servers should read it.
- Context continuation is A2A-only — then the L2 and L0 prose should say so instead of instructing MCP callers to send a field that does not exist, and
ProtocolEnvelope.context_id should say it is only actionable on A2A.
Either way the "MAY echo" / "You must pass" inconsistency inside mcp-guide.mdx should be resolved.
How this was found
Implementing an AdCP sales agent that serves MCP, A2A and REST from one registry. We followed the prose and accepted context_id via an x-context-id header on MCP, because there was no declared field — then found the buyer could never learn their id, because no response builder we could write had a conformant way to be told one back.
Summary
The prose instructs MCP callers to send
context_idas a tool argument and calls it "Required for continuity". No request schema declares it — 0 of 103 at v3.1.1, 0 of 103 at v3.1.20. Both reference SDKs refuse or silently drop it on MCP, neither reference server ever sets or reads it, no MCP round-trip test exists in either SDK, and the compliance bundle does not grade it.So a conformant MCP client following the documentation sends a field the schema does not model, to a server neither reference implementation teaches how to read.
The prose (verified at
?ref=v3.1.1)docs/building/by-layer/L2/context-sessions.mdx:docs/building/by-layer/L0/mcp-guide.mdx, §Context Management (MCP-Specific):with
request.arguments.context_id = this.contextId;.The same file is internally inconsistent — earlier it says the caller "MAY echo it on subsequent calls to thread a session", two sections before "You must pass".
The schemas
core/protocol-envelope.jsondeclarescontext_idon the RESPONSE:Request side, counted over the non-bundled
*-request.json:context_id3.2.0-rc.1 adds
"context_id": {"type": "string", "minLength": 1}tolist-products,request-proposals,refine-proposalsanddecline-proposalsonly, neverrequired— and not tocreate_media_buyorupdate_media_buy, which are the tools the L2 example itself uses.ContextObject, the type of the request'scontextfield, has zero properties at 3.1.1, so it cannot carry the id either.The reference implementations disagree with the prose
Python SDK (
adcp6.6.0) refuses it outright —client.py:587-596:protocols/mcp.pycontains zero occurrences ofcontext_id. There is no MCP path.TypeScript SDK (
adcp-client) drops it —TaskExecutor.ts:1531-1542:On A2A it rides
message.contextId(protocols/a2a.ts:625,714) — the A2A envelope, not the AdCP body.tools.generated.ts: 4 of 64 request interfaces declarecontext_id; 33 of 40 response interfaces do.Neither reference server implements it. The TS server never sets
context_idon a response (wrap-envelope.ts,responses.ts,structured-serialize.ts: zero hits) and never reads one from a request; its A2A path threads A2A's ownTask.contextId(server/a2a-adapter.ts:462-469,594). The Python server has one occurrence,a2a_server.py:677, also on the A2Apb.Task.No MCP round-trip test exists in either SDK. The A2A ones do (
test/lib/a2a-context-retention.test.js,test/storyboard-a2a-context-continuity.test.js), and both assert on the A2A Message envelope. The one MCP-adjacent test asserts the opposite behaviour —test/lib/task-executor-async-patterns.test.js:550-563, "does not invent an MCP continuation tool for a returned pause": the mocked response carriescontext_id: 'mcp-context'and the test assertscallToolis called exactly once.Not graded.
context_idappears zero times under_schemas/3.1/compliance/. The only related check in later versions isa2a_context_continuity, which grades A2A 0.3.0 §7.1, not AdCP.Why it stays hidden
63 of the 103 request schemas at 3.1 are
additionalProperties: true, so a client following the prose gets no validation error — the field is simply unmodelled and ignored.Expected
Pick one and make the three artifacts agree:
context_idbelongs on the request envelope (alongsideadcp_versionincore/version-envelope.json, or a shared request envelope), not on 4 of 374 tools, and the reference servers should read it.ProtocolEnvelope.context_idshould say it is only actionable on A2A.Either way the "MAY echo" / "You must pass" inconsistency inside
mcp-guide.mdxshould be resolved.How this was found
Implementing an AdCP sales agent that serves MCP, A2A and REST from one registry. We followed the prose and accepted
context_idvia anx-context-idheader on MCP, because there was no declared field — then found the buyer could never learn their id, because no response builder we could write had a conformant way to be told one back.