MCP Stateless Protocol (2026‑07‑28): Technical Guide and Implementation Walkthrough
This article explains the 2026‑07‑28 MCP stateless protocol changes, the removal of session concepts, new self‑describing request mechanisms, and provides step‑by‑step examples using the webman‑mcp PHP SDK and Neuron‑AI integration, along with compatibility challenges and mitigation strategies.
1. Overview: Stateless Protocol
The MCP 2026‑07‑28 release candidate introduces the core change of making the protocol layer completely stateless. Six SEP enhancements together with the W3C Trace Context convention (SEP‑414) remove the initialize handshake, the Mcp-Session-Id header, long‑lived SSE streams, and the free‑floating server‑to‑client request channel, replacing them with explicit self‑describing metadata.
Replace initialize handshake with version/identity/capability fields inside each request’s _meta (SEP‑2575).
Replace session‑sticky routing with a stateless explicit handle ( create_basket() → basket_id) (SEP‑2567).
Replace persistent SSE with inline InputRequiredResult (MRTR) (SEP‑2260 / SEP‑2322).
Replace deep‑packet parsing for routing with Mcp-Method / Mcp-Name header mirroring (SEP‑2243).
Introduce cache‑control fields ttlMs and cacheScope for list results (SEP‑2549).
Propagate trace context via _meta keys traceparent, tracestate, baggage (SEP‑414).
2. Protocol Changes Compared to 2025‑11‑25
2.1 Handshake and Session Removal – SEP‑2575 / SEP‑2567
Previously (2025‑11‑25) the client performed a POST initialize, received a Mcp-Session-Id, and then attached that header to every request, causing routing to be pinned to the instance that issued the ID. After the 2026‑07‑28 change, the protocol version, client software identity, and client capabilities are carried inside the request body’s _meta field, making each request self‑contained.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "query_order_snapshot",
"arguments": {"order_no": "ORDER-20260621-1001"},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": {"name": "neuron-agent", "version": "1.0.0"},
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}The three fields protocolVersion, clientInfo, and clientCapabilities are mandatory and must be declared per request, without inferring anything from prior traffic.
The protocol version is also mirrored in the HTTP header MCP-Protocol-Version; the header and body must match.
A new optional RPC server/discover lets a client query the server’s supported version, capabilities, and identity; if the version mismatches the server returns UnsupportedProtocolVersionError with a list of supported versions.
Missing required capabilities trigger MissingRequiredClientCapabilityError (code -32003, HTTP 400).
2.2 Server‑to‑Client Request Reconstruction – SEP‑2260 / SEP‑2322 (MRTR)
Even a stateless protocol sometimes needs the server to ask the client for additional input (elicitation, sampling, or roots/list queries). SEP‑2260 mandates that such server‑initiated requests can only be issued while handling the original client request, prohibiting free‑floating server pushes. SEP‑2322 replaces the previous SSE‑based reply with an inline InputRequiredResult payload.
{
"resultType": "inputRequired",
"inputRequests": {
"confirm": {
"type": "elicitation",
"message": "确认删除 3 个文件吗?",
"schema": {"type": "boolean"}
}
},
"requestState": "eyJzdGVwIjoxLCJmaWxlcyI6WyJhIiwiYiIsImMiXX0="
} inputRequestsmaps keys to request shapes such as CreateMessageRequest, ElicitRequest, or ListRootsRequest. requestState is an opaque server‑encoded state that the client must return unchanged.
After the client supplies the answers, it re‑issues the original request with the original parameters plus inputResponses (keyed the same as inputRequests) and the unchanged requestState, allowing any server replica to retry without sticky routing.
2.3 Routable and Cacheable Traffic – SEP‑2243 / SEP‑2549
SEP‑2243 requires every streamable HTTP POST to include the Mcp-Method header (mirroring the JSON‑RPC method) and the Mcp-Name header (mirroring params.name or params.uri). The server must reject mismatched header/body pairs with HeaderMismatch (HTTP 400).
POST /mcp/order HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: query_order_snapshotSEP‑2243 also adds the x-mcp-header JSON‑Schema extension, allowing tools to expose basic‑type parameters as Mcp-Param-{Name} headers for region/tenant routing. Non‑ASCII values must be base64‑encoded.
SEP‑2549 introduces the CacheableResult interface for list‑type RPCs, with mandatory fields ttlMs (milliseconds, 0 means immediate expiry) and cacheScope ("public" or "private"). This provides a stable cache key at the deployment level, supplementing but not replacing HTTP Cache‑Control.
3. Practical Implementation with webman‑mcp
3.1 Installation and Scaffold
composer require tinywan/webman-mcp
php webman make:mcp-server Calculator
php webman make:mcp-tool CalculatorThe commands generate app/mcp/CalculatorServer.php and app/mcp/CalculatorTool.php.
3.2 Defining a Tool
<?php
declare(strict_types=1);
namespace app\mcp;
use Tinywan\Mcp\Contracts\ToolInterface;
use Tinywan\Mcp\Runtime\ExecutionContext;
use Tinywan\Mcp\Tool\Content\TextContent;
use Tinywan\Mcp\Tool\ToolCall;
use Tinywan\Mcp\Tool\ToolDefinition;
use Tinywan\Mcp\Tool\ToolResult;
final class CalculatorTool implements ToolInterface {
public function definition(): ToolDefinition {
return new ToolDefinition(
'calculate',
'Add two numbers.',
[
'type' => 'object',
'properties' => [
'left' => ['type' => 'number'],
'right' => ['type' => 'number']
],
'required' => ['left', 'right'],
'additionalProperties' => false,
],
[
'type' => 'object',
'properties' => [
'value' => ['type' => 'number']
],
'required' => ['value'],
'additionalProperties' => false,
]
);
}
public function call(ToolCall $call, ExecutionContext $context): ToolResult {
$value = (float) $call->arguments['left'] + (float) $call->arguments['right'];
return ToolResult::success(
content: [new TextContent((string) $value)],
structuredContent: ['value' => $value]
);
}
}The ToolDefinition declares both inputSchema and outputSchema; webman‑mcp validates them against JSON Schema 2020‑12 before and after execution.
3.3 Registering the Server
<?php
declare(strict_types=1);
namespace app\mcp;
use Tinywan\Mcp\Registry\RegisteredTool;
use Tinywan\Mcp\Registry\ServerDefinition;
use Tinywan\Mcp\Registry\ServerIdentity;
use Tinywan\Mcp\Security\AllowAllAuthorizer;
use Tinywan\Mcp\Security\AllowAnonymousAuthenticator;
final class CalculatorServer {
public static function definition(): ServerDefinition {
$tool = new CalculatorTool();
return new ServerDefinition(
'calculator',
'/mcp/calculator',
new ServerIdentity('Calculator', '1.0.0'),
[new RegisteredTool($tool->definition(), CalculatorTool::class)],
new AllowAnonymousAuthenticator(), // demo only
new AllowAllAuthorizer() // demo only
);
}
}Register the server in config/plugin/tinywan/webman-mcp/servers.php:
<?php
use app\mcp\CalculatorServer;
return [
'servers' => [CalculatorServer::definition()],
];3.4 Running and Validation
# List registered servers
php webman mcp:list
# Validate configuration
php webman mcp:inspect
# Start the webman process
php start.php startExample curl request (note that _meta and the three MCP headers are mandatory):
curl -X POST http://127.0.0.1:8118/mcp/calculator \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-protocol-version: 2026-07-28" \
-H "mcp-method: tools/call" \
-H "mcp-name: calculate" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "calculate",
"arguments": {"left": 2, "right": 3},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
"io.modelcontextprotocol/clientInfo": {"name": "curl", "version": "1.0.0"}
}
}
}' _metamust be an object containing the three namespaced fields.
The three MCP headers ( mcp-protocol-version, mcp-method, mcp-name) must mirror the body; otherwise the server returns HeaderMismatch (‑32020).
Any missing field causes the request to be rejected – this is the self‑describing requirement of the 2026‑07‑28 protocol.
4. Consuming the MCP Server with Neuron‑AI
4.1 Local MCP Server (stdio)
<?php
declare(strict_types=1);
namespace app
euron;
use NeuronAI\MCP\McpConnector;
use NeuronAI\Agent;
final class CalculatorAgent extends Agent {
protected function tools(): array {
return [
...McpConnector::make([
'command' => 'php',
'args' => ['/var/www/chunfen/mcp_server.php'],
])->tools(),
];
}
}4.2 Remote MCP Server (Streamable HTTP)
protected function tools(): array {
return [
...McpConnector::make([
'url' => 'http://erp.chutang66.com/mcp/order',
'token' => env('MCP_TOKEN'),
'timeout'=> 30,
'headers'=> ['X-Tenant' => 'chunfen'],
])->tools(),
];
}When the agent decides to call a tool, Neuron automatically builds the appropriate MCP request, sends it to the server, and returns the result as if the tool were defined locally.
5. Compatibility Friction and Mitigation
No _meta : Server returns “params._meta must be an object” because the new protocol requires every request to be self‑describing.
No header mirroring : Server returns Header mismatch (‑32020) as SEP‑2243 mandates matching headers.
Attempting initialize : Server returns “Method not found” (‑32601) because the handshake was removed.
Protocol version mismatch : Server returns “Unsupported protocol version” (‑32022) because the version is hard‑coded and no downgrade is performed.
Recommended mitigation paths (ordered by preference):
A. Wait for or push client upgrades to the 2026‑07‑28 SDKs (Go, Python) that support the stateless mode natively.
B. Add a “lenient mode” switch to webman‑mcp that makes _meta and header checks optional and provides a compatibility stub for initialize. This eases transition but deviates from the spec.
C. Deploy a protocol‑translation gateway (e.g., mcpsense‑proxy) that speaks the old handshake protocol externally and translates to the new stateless format internally.
D. Extend webman‑mcp to support dual‑stack operation, handling both the old 2025‑11‑25 handshake and the new stateless flow.
Short‑term: use option B or D for a smooth transition. Long‑term: follow option A and migrate fully to the pure stateless protocol. If you control all clients (custom agents), you can adopt 2026‑07‑28 immediately without friction.
References
SEP‑2575 — Make MCP Stateless
SEP‑2567 — Sessionless MCP via Explicit State Handles
SEP‑2260 — Require Server requests to be associated with a Client request
SEP‑2322 — Multi Round‑Trip Requests (MRTR)
SEP‑2243 — HTTP Header Standardization for Streamable HTTP Transport
SEP‑2549 — TTL for List Results
SEP‑414 — Document OpenTelemetry Trace Context Propagation Conventions
Official announcement: 2026‑07‑28 release candidate
Reference document: muster — Stateless protocol (MCP 2026‑07‑28)
webman‑mcp · Neuron‑AI · Neuron MCP Connector documentation
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Open Source Tech Hub
Sharing cutting-edge internet technologies and practical AI resources.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
