MCP 2026-07-28 contract
August 1, 2026 · View on GitHub
Apache Doris MCP Server uses one MCP SDK v2 protocol core for Streamable HTTP and stdio. The MCP wire revision, Doris MCP Server package version, and Python SDK version are independent identifiers.
Authoritative protocol references:
Compatibility matrix
| Client protocol | Streamable HTTP | stdio | Contract |
|---|---|---|---|
2026-07-28 | Preferred at /mcp | Preferred | Stateless, self-contained requests; release-gated |
2025-11-25 | Opt-in at /mcp/legacy | Migration only | Legacy initialization accepted; no HTTP session is created |
2025-06-18 | Opt-in at /mcp/legacy | Not release-gated | HTTP initialization, stateless tool discovery, and tool calls are regression-tested; Dify 1.16.1 validated |
2025-03-26 and older | Not guaranteed | Not guaranteed | Upgrade the client |
HTTP+SSE 2024-11-05 | Not supported | Not applicable | Retired separate SSE endpoint is absent |
ENABLE_LEGACY_HTTP_ADAPTER=true enables only the isolated legacy HTTP path.
It does not change /mcp, restore pre-1.0 tool names, create a session, bypass
authorization, or change the Server's tool catalog.
Modern request metadata
Every 2026-07-28 request carries these values in params._meta:
io.modelcontextprotocol/protocolVersion:2026-07-28io.modelcontextprotocol/clientCapabilities: request capabilities or{}io.modelcontextprotocol/clientInfo: client name and version (recommended)
server/discover is optional and reports supported protocol versions,
capabilities, and deterministic product identity. Normal requests remain
self-contained.
Streamable HTTP contract
Send one JSON-RPC request per POST /mcp:
| Header | Required | Rule |
|---|---|---|
Content-Type | yes | application/json |
Accept | yes | include application/json and text/event-stream |
MCP-Protocol-Version | yes | match the version in _meta |
Mcp-Method | yes | match JSON-RPC method exactly |
Mcp-Name | named calls | match params.name or params.uri |
Header names are case-insensitive; method and name values are not. Missing or
mismatched required headers return HTTP 400 plus protocol HeaderMismatch
(-32020). Unsupported protocol revisions return
UnsupportedProtocolVersion (-32022).
Example:
curl --request POST http://127.0.0.1:3000/mcp \
--header 'Content-Type: application/json' \
--header 'Accept: application/json, text/event-stream' \
--header 'MCP-Protocol-Version: 2026-07-28' \
--header 'Mcp-Method: server/discover' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "server/discover",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
"io.modelcontextprotocol/clientInfo": {
"name": "example-client",
"version": "1.0.0"
}
}
}
}'
If a name/URI is not safe as a plain ASCII header value, use the transport specification's Base64 header encoding.
stdio contract
stdio carries the same JSON-RPC body and _meta, without HTTP headers. The
Host launches the Server process and exchanges frames over stdin/stdout. All
ordinary logging must remain off stdout.
Modern stdio is stateless at the request contract. Legacy stdio may accept the older initialization sequence only as a bounded migration path.
List pagination
resources/list, tools/list, and prompts/list return at most
MCP_LIST_PAGE_SIZE stable ordered entries. When nextCursor is present, pass
it unchanged as the next request's cursor.
Cursors are HMAC-authenticated explicit state handles bound to:
- list type, scope, and resource;
- visible-list snapshot;
- authorization context/principal;
- expiry.
They do not depend on a protocol session, transport connection, or worker
memory. Reuse after visibility change, expiry, tampering, principal change, or
for another list returns Invalid Params.
A launch-local secret is shared among workers created by one CLI process. Set
one high-entropy MCP_STATE_HANDLE_SECRET across independently launched
replicas when a load balancer may route subsequent pages to another instance.
Handles are signed, not encrypted, and never contain credentials, SQL, or
query data. See ADR 0002.
List failures are not successful empty lists. Permission, backend, and internal failures keep their typed failure semantics so an empty successful collection means the visible collection is actually empty.
Tool schemas
Tool inputSchema and outputSchema use JSON Schema 2020-12. Before execution:
- visible definitions are compiled and checked against schema budgets;
- call arguments are validated without echoing rejected values;
- successful structured output is validated before crossing the protocol boundary.
Schemas are self-contained. Only same-document fragment references are allowed; no HTTP, file, or relative external reference is fetched. Recursive references and oversized schemas/instances fail closed.
Default hard schema limits include 64 KiB serialized size, 2,048 nodes, depth 32, 64 composition branches, and 64 references. Input/structured output instances are limited to 1 MiB, 10,000 nodes, depth 32, and bounded strings.
Trace context
Clients can supply W3C traceparent, tracestate, and baggage carrier fields
in params._meta. The Server:
- validates size and shape before propagation;
- ignores malformed fields independently;
- redacts credential-like baggage values;
- scopes context to one request;
- never copies trace carriers into model-facing tool/resource/prompt results.
The same message-level carrier works on HTTP and stdio; these fields are not separate MCP HTTP headers.
Subscription boundary
The Server does not advertise subscriptions/listen. Tool and prompt catalogs
have no mutation channel, and Doris metadata does not currently provide this
process with a reliable cross-worker change-event source. Cache expiry or
polling is not treated as an event.
Discovery therefore reports listChanged: false and resource
subscribe: false. Clients refresh explicitly. See
ADR 0001.
Protocol errors and recovery
- Header/body disagreement: fix the request; do not retry unchanged.
- Unsupported revision: upgrade or select the explicit legacy adapter.
- Invalid cursor/state handle: restart the list without a cursor.
- Invalid tool arguments: use the current discovered Schema.
- Stale domain manifest: rediscover the domain.
- Interrupted stateless HTTP request: retry with a new JSON-RPC request ID.
Do not rely on Mcp-Session-Id, sticky sessions, the removed HTTP GET stream,
Last-Event-ID, or resumable SSE behavior.