On July 28, 2026, the Model Context Protocol published the most significant revision since its launch. MCP is now a stateless protocol. No more session handshakes. No more sticky load balancing. No more shared session stores for horizontal scaling. Every request is self-contained.
I wanted to see if the reality matched the promise, so I spent a day testing the new spec against a live AWS AgentCore Gateway with real Lambda tools behind it. This post is what I found.
A note on perspective: I am writing this as both an AAIF Ambassador and an AWS Community Builder. MCP is an open standard hosted by the Agentic AI Foundation (AAIF) under the Linux Foundation. AgentCore Gateway is AWS's managed implementation of that standard. Testing a managed cloud implementation against the open spec it claims to support felt like exactly the right way to evaluate whether MCP 7-28 delivers what it promises.
Table of Contents
The Seven Breaking Changes
MCP 7-28 is not a minor version bump. Here are the seven changes that matter, ordered by developer impact:
| # | Change | Why it matters |
|---|---|---|
| 1 |
Stateless Protocol: server/discover replaces initialize. Client identity moves into _meta per request. Mcp-Session-Id is gone. |
Eliminates sticky sessions and shared session stores. MCP servers become standard HTTPS endpoints that scale horizontally behind any load balancer. |
| 2 |
HTTP-Native Routing: New Mcp-Method and Mcp-Name headers expose intent outside the body. W3C Trace Context arrives via _meta. |
Gateways and load balancers can route and observe MCP traffic without parsing JSON-RPC bodies. Observability tools work out of the box. |
| 3 |
Cache Metadata: Responses carry resultType, ttlMs, and cacheScope. |
Proxies and CDNs can cache MCP responses natively. No custom caching logic needed at the application layer. |
| 4 |
Server-to-Client Without SSE: Long-lived SSE streams replaced by Multi Round-Trip Requests (MRTR) with InputRequiredResult and requestState tokens. |
Servers can ask clients questions during a request without holding a persistent connection open. Friendlier to serverless and short-lived compute. |
| 5 | Authorization Hardening: Six SEPs align MCP with production OAuth 2.0 and OpenID Connect. | Auth is now a spec-level concern, not an implementation detail. Every compliant gateway enforces the same model. |
| 6 |
Cleaner Error Handling: Unknown methods β HTTP 404. Bad version β HTTP 400 + code -32022 + supported versions list in data. |
Monitoring, alerting, and load balancers can react to MCP failures using standard HTTP status codes. No body parsing required. |
| 7 |
JSON Schema 2020-12: Full support for oneOf, anyOf, allOf, conditionals, $ref/$defs in tool definitions. |
Tool schemas can express complex, conditional input structures. Removes the need for workarounds in production tool definitions. |
The deprecation list is worth noting: Roots, Sampling, and Logging are on a 12-month advisory deprecation window. If your tooling relies on any of those, you have until mid-2027 to migrate.
Here is what actually changes on the wire.
Request Format
Before (pre-7-28, session-based):
POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Session-Id: 1868a90c-3a3f-4f5b-9c2d-abc123def456
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}
After (2026-07-28, stateless):
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/list
Authorization: Bearer eyJ...
{"jsonrpc":"2.0","id":"test-1","method":"tools/list","params":{
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": {
"name": "mcp-blog-test",
"version": "1.0.0"
},
"io.modelcontextprotocol/clientCapabilities": {}
}
}}
Three things changed:
-
Mcp-Session-Idis gone. Replaced byMCP-Protocol-VersionandMcp-Methodheaders. - Client identity that used to live in the
initializehandshake now travels inline via_metaon every request. - Auth is explicit at the HTTP layer (the
Authorizationheader), not implicit through a session.
Response Format
Before (2025-x):
{"jsonrpc":"2.0","id":1,"result":{"tools":[...]}}
After (2026-07-28):
{
"jsonrpc": "2.0",
"id": "test-1",
"result": {
"tools": ["..."],
"resultType": "complete",
"ttlMs": 0,
"cacheScope": "private"
}
}
The new resultType, ttlMs, and cacheScope fields are HTTP-native caching metadata. They tell proxies and CDNs how to cache MCP responses without needing custom logic. This is a small addition with large infrastructure implications.
Error Handling
| Scenario | Before (pre-7-28) | After (2026-07-28) |
|---|---|---|
| Unknown method | HTTP 200 + in-body error | HTTP 404 |
| Bad protocol version | Undefined behavior | HTTP 400 + code -32022 + supported versions list |
| Missing auth | Implementation-specific | HTTP 401 + code -32001
|
| Malformed JSON-RPC | Implementation-specific | HTTP 400 + code -32600
|
The shift from "everything returns HTTP 200 with errors buried in JSON" to "use HTTP status codes properly" is significant. Monitoring tools, load balancers, and alerting systems can now detect MCP failures without parsing response bodies.
Setting Up the Test
I created a minimal AgentCore Gateway with two Lambda-backed demo tools (echo_message and get_current_time) to test the protocol changes in isolation.
Infrastructure:
- Region: us-east-1
-
Gateway: AgentCore Gateway with
CUSTOM_JWTauthorizer (Amazon Cognito,client_credentialsgrant) - Backend: AWS Lambda, Python 3.12, 128MB
-
MCP versions enabled:
2025-03-26,2025-11-25,2026-07-28-- all three active simultaneously - Total cost: Under $0.001 for all testing (no standing charges on AgentCore Gateway)
The setup was five bash scripts run in sequence: IAM role, Cognito pool, Lambda function, gateway creation, and test execution. The gateway was created with --protocol-type MCP, --authorizer-type CUSTOM_JWT, --exception-level DEBUG, and supportedVersions set to all three active versions simultaneously.
One gotcha worth calling out: UpdateGateway replaces supportedVersions -- it does not append. If you add 2026-07-28 without including your existing versions, you will drop them. Always read the current config first with get-gateway.
What the Tests Revealed
I ran six tests against the live gateway. Here is what came back.
Test 1: Stateless tools/list (MCP 2026-07-28)
The main event. No initialize, no session -- just send the request.
Request:
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/list
Authorization: Bearer <cognito-token>
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": "test-1",
"method": "tools/list",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": {"name": "mcp-728-blog-test", "version": "1.0.0"},
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
Response:
{
"jsonrpc": "2.0",
"id": "test-1",
"result": {
"tools": [
{
"name": "lambda-mcp-demo___echo_message",
"description": "Echoes back the input message. Useful for protocol-level tracing.",
"inputSchema": {
"type": "object",
"properties": {
"message": {
"description": "The message to echo back",
"type": "string"
}
},
"required": ["message"]
}
},
{
"name": "lambda-mcp-demo___get_current_time",
"description": "Returns the current UTC timestamp. Demo tool for MCP 7-28 blog.",
"inputSchema": {
"type": "object",
"properties": {
"timezone": {
"description": "Optional timezone label (display only)",
"type": "string"
}
}
}
}
],
"resultType": "complete",
"ttlMs": 0,
"cacheScope": "private"
}
}
HTTP 200. Tools returned with full cache metadata. No session needed. The spec works as advertised.
Test 2: Backward Compatibility (MCP 2025-11-25)
Same gateway, same Lambda, older protocol version. No _meta required, no new headers.
Request:
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2025-11-25
Authorization: Bearer <cognito-token>
Content-Type: application/json
{"jsonrpc":"2.0","id":"test-2","method":"tools/list","params":{}}
Response: HTTP 200: same tools returned, but no resultType, no ttlMs, no cacheScope. The protocol version drives the wire format. Old clients see old responses. New clients see new metadata. Same gateway, same backend, different wire.
Test 3: Auth Enforcement (No Token)
Request: Same as Test 1 but with the Authorization header removed entirely.
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/list
Content-Type: application/json
{"jsonrpc":"2.0","id":"test-3","method":"tools/list","params":{}}
Response:
{
"jsonrpc": "2.0",
"id": 0,
"error": {
"code": -32001,
"message": "Missing Bearer token"
}
}
HTTP 401. The gateway rejected the request before the Lambda was invoked. I confirmed this in CloudWatch -- no Lambda invocation log for this request. Auth enforcement happens at the gateway layer, not the tool layer. This is what the six SEPs promised, and it works.
Test 4: Unsupported Version
Request: Valid auth, valid JSON-RPC, but a version that doesn't exist.
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2099-01-01
Authorization: Bearer <cognito-token>
Content-Type: application/json
{"jsonrpc":"2.0","id":"test-4","method":"tools/list","params":{}}
Response:
{
"jsonrpc": "2.0",
"id": "test-4",
"error": {
"code": -32022,
"message": "Unsupported protocol version: 2099-01-01",
"data": {
"requested": "2099-01-01",
"supported": ["2025-03-26", "2025-11-25", "2026-07-28"]
}
}
}
HTTP 400 with the new error code -32022. The data field lists all supported versions -- clients can discover valid versions from the error itself. Pre-7-28, this scenario had undefined behavior.
Test 5: Old Session Patterns Rejected
Request: A pre-7-28 SSE-era initialization call sent with the new protocol version header: simulating a client that upgrades the header but not its request pattern.
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: session/initialize
Authorization: Bearer <cognito-token>
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": "test-5a",
"method": "session/initialize",
"params": {
"protocolVersion": "2026-07-28",
"clientInfo": {"name": "old-client", "version": "0.1.0"}
}
}
Response:
{
"jsonrpc": "2.0",
"id": "test-5a",
"error": {
"code": -32602,
"message": "Missing required '_meta' for protocol version 2026-07-28"
}
}
HTTP 400. The gateway enforces _meta as required for 2026-07-28 requests. You cannot accidentally fall back to deprecated session behavior. The protocol is self-enforcing.
Test 6: Malformed Requests
Request: Garbled body with no Content-Type header: simulating a misconfigured client or a corrupted payload.
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Authorization: Bearer <cognito-token>
this is not json { broken @@@ }
Response:
{
"jsonrpc": "2.0",
"id": "unknown",
"error": {
"code": -32600,
"message": "Invalid request - Malformed JSON-RPC request"
}
}
HTTP 400. Error code -32600 (Invalid Request). The gateway rejects at the HTTP/parse layer before MCP logic even runs. Clean, predictable, debuggable.
Lambda Execution Profile
CloudWatch confirmed the Lambda behind the gateway runs at the expected minimal footprint:
Duration: 2.00 ms | Billed: 2 ms | Memory: 128 MB | Used: 37 MB (tools/list)
Duration: 7.77 ms | Billed: 8 ms | Memory: 128 MB | Used: 37 MB (tools/call)
Init Duration: 94.15 ms (cold start)
The gateway adds no measurable overhead. A tools/list call completed in 2ms. A tools/call to get_current_time took 7.77ms including business logic. Cold start was 94ms -- standard for Python 3.12 Lambda.
Summary
| Test | Protocol Version | Expected | Result |
|---|---|---|---|
| Stateless tools/list | 2026-07-28 | HTTP 200 + cache metadata | Confirmed |
| Backward compat | 2025-11-25 | HTTP 200, no new fields | Confirmed |
| No auth token | 2026-07-28 | HTTP 401 before Lambda | Confirmed |
| Bad version (2099-01-01) | N/A | HTTP 400 + -32022
|
Confirmed |
| Old session pattern | 2026-07-28 | Rejected, _meta required |
Confirmed |
| Malformed JSON | N/A | HTTP 400 + -32600
|
Confirmed |
Takeaways
Three takeaways from a day of testing:
Horizontal scaling is now trivial. Stateless MCP means you can put MCP servers behind any load balancer, any CDN, any API gateway -- no affinity, no shared state. This is the change that makes MCP viable for production traffic volumes.
Auth is no longer optional. The gateway enforced OAuth before the Lambda even saw the request. With the six SEPs baked into the spec, there is now a standard way to secure MCP endpoints that every implementation can follow. Production teams no longer need to invent their own auth layer.
Error handling is finally debuggable. HTTP status codes, machine-readable error payloads, and supported version discovery in error responses mean you can build monitoring and alerting around MCP without custom parsers.
What's Next
Deprecation watch: Roots, Sampling, and Logging are on a 12-month advisory deprecation timeline. If your MCP tooling uses any of these, start planning the migration now.
AAIF's agentgateway is the open-source counterpart to managed gateways like AgentCore. My next post will compare how agentgateway v1.3 and AgentCore Gateway each handle MCP 7-28 -- same protocol, different deployment models, different tradeoffs.
AGNTCon + MCPCon Japan is September 10-11 in Tokyo. It will be the first major community event after this release. If you are building with MCP in production, that is where the conversations will happen.
Links
- MCP 2026-07-28 Specification
- How AgentCore Gateway Supports MCP 2026-07-28 -- Sean Eichenberger and Luca Chang
- Agentic AI Foundation (AAIF)
- AAIF agentgateway Project
- AgentCore Gateway Documentation
- AgentCore Gateway Quickstart
Happy learning π
Top comments (0)