DEV Community

Marco Gonzalez
Marco Gonzalez

Posted on

MCP new specs in Practice: Testing the Stateless Revolution on AWS AgentCore Gateway

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":{}}
Enter fullscreen mode Exit fullscreen mode

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": {}
  }
}}
Enter fullscreen mode Exit fullscreen mode

Three things changed:

  • Mcp-Session-Id is gone. Replaced by MCP-Protocol-Version and Mcp-Method headers.
  • Client identity that used to live in the initialize handshake now travels inline via _meta on every request.
  • Auth is explicit at the HTTP layer (the Authorization header), not implicit through a session.

Response Format

Before (2025-x):

{"jsonrpc":"2.0","id":1,"result":{"tools":[...]}}
Enter fullscreen mode Exit fullscreen mode

After (2026-07-28):

{
  "jsonrpc": "2.0",
  "id": "test-1",
  "result": {
    "tools": ["..."],
    "resultType": "complete",
    "ttlMs": 0,
    "cacheScope": "private"
  }
}
Enter fullscreen mode Exit fullscreen mode

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_JWT authorizer (Amazon Cognito, client_credentials grant)
  • 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": {}
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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":{}}
Enter fullscreen mode Exit fullscreen mode

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":{}}
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "jsonrpc": "2.0",
  "id": 0,
  "error": {
    "code": -32001,
    "message": "Missing Bearer token"
  }
}
Enter fullscreen mode Exit fullscreen mode

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":{}}
Enter fullscreen mode Exit fullscreen mode

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"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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"}
  }
}
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "jsonrpc": "2.0",
  "id": "test-5a",
  "error": {
    "code": -32602,
    "message": "Missing required '_meta' for protocol version 2026-07-28"
  }
}
Enter fullscreen mode Exit fullscreen mode

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 @@@ }
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "jsonrpc": "2.0",
  "id": "unknown",
  "error": {
    "code": -32600,
    "message": "Invalid request - Malformed JSON-RPC request"
  }
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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

Happy learning πŸš€

Top comments (0)