# MCP 2026-07-28 Migration

This version of MCP Gateway requires clients and adapters that support MCP `2026-07-28`. Requests are independent: the gateway authorizes each request and forwards it to a ready backend instance without maintaining MCP transport sessions.

## What Changed

| Previous behavior | Current behavior |
| --- | --- |
| `initialize` and `notifications/initialized` handshake | No handshake; optional `server/discover` request |
| Session ID selects a backend instance | Each request can use a different ready instance |
| Protocol version and capabilities established during initialization | Version and capabilities supplied with every request |
| Standalone GET event stream and session DELETE | POST requests with JSON or request-scoped SSE responses |
| Resume a stream with `Last-Event-ID` | Open a new request; streams are not resumable |
| Portal connection depends on a session ID | Portal discovers the server and runs tools without a session |

The public URLs remain `/mcp` for registered tools and `/adapters/{name}/mcp` for an adapter. Authentication and resource permissions still apply to every call. Agent/conversation sessions under `/sessions` are separate from MCP transport sessions and are not removed by this change.

## Before Upgrading

1. Check that your MCP client and every adapter support revision `2026-07-28`. A stateless gateway cannot translate requests for a legacy-only adapter.
2. Test the upgraded gateway and adapters in a separate deployment. Keep old and new images out of the same backend pool during migration.
3. Back up resource data and configuration, and record the image tags or digests needed for rollback. Check stored-data compatibility before rolling an older gateway back onto data written by a newer version.
4. Drain the old deployment before switching traffic. Existing MCP transport sessions cannot resume on the new version.

Clients or adapters that cannot upgrade must continue using a compatible previous gateway image, pinned by tag or digest. There is no legacy-mode setting or automatic protocol downgrade in this version. Consult the selected release's maintenance policy; an image remaining available does not guarantee security updates.

## Send a Request

Send each JSON-RPC request as a POST to the MCP endpoint. Include these headers:

| Header | Requirement |
| --- | --- |
| `Content-Type` | `application/json` |
| `Accept` | Include both `application/json` and `text/event-stream` |
| `MCP-Protocol-Version` | `2026-07-28`, matching the version in the request body |
| `Mcp-Method` | The JSON-RPC method |
| `Mcp-Name` | Required for `tools/call` and `prompts/get` (`params.name`), and `resources/read` (`params.uri`) |
| `Mcp-Param-*` | Required for arguments annotated with `x-mcp-header` in the tool's input schema |

Every request's `params._meta` must contain `io.modelcontextprotocol/protocolVersion` and `io.modelcontextprotocol/clientCapabilities`. The optional `clientInfo` value is descriptive metadata, not an authenticated identity.

Both the public gateway and first-party Tools endpoint reject legacy handshakes and validate the method, name or URI, protocol version, and required metadata before execution. The body must be a single JSON-RPC 2.0 request with a client capabilities object.

For example, use `Mcp-Method: server/discover` with this body:

```json
{
  "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"
      }
    }
  }
}
```

Discovery is optional. You can send `tools/list` or `tools/call` directly with the same per-request metadata. Use a compatible MCP SDK to produce schema-derived headers and the protocol's Base64 sentinel encoding when values are not safe plain HTTP header values.

Mirrored `x-mcp-header` annotations must be on directly addressable object properties of type `string`, `integer`, or `boolean`, optionally nullable. Nested object properties are supported. Annotations inside schema compositions, referenced definitions, or array items are rejected; unannotated compositions remain allowed.

`Mcp-Session-Id`, `Last-Event-ID`, and the gateway's old `session_id` query parameter no longer affect routing. Do not use them to carry application state. Tools that need state across calls should return an explicit application handle and accept it as an argument on subsequent calls.

## Streaming and Results

Responses can be JSON or SSE. Request-scoped SSE can deliver progress before the final response. Closing the response stream cancels the request; a failed connection is not permission to automatically retry a tool with side effects.

Adapters that support change notifications accept `subscriptions/listen` with a notification filter such as:

```json
{
  "notifications": {
    "toolsListChanged": true
  },
  "_meta": {
    "io.modelcontextprotocol/protocolVersion": "2026-07-28",
    "io.modelcontextprotocol/clientCapabilities": {}
  }
}
```

This is the request's `params` object. Check the server's supported capabilities and acknowledgement; not every adapter implements subscriptions. The portal's advanced console displays incoming events incrementally and provides a cancel control.

Modern responses include `resultType`. An `input_required` result is incomplete, not a successful tool completion; the portal reports it as such. Full interactive continuation is not implemented by the portal. Complete discovery/list/read results include cache hints. The first-party gateway returns permission-filtered tool catalogs in a deterministic order with `cacheScope: "private"` and `ttlMs: 0`. Never share those catalogs across authorization contexts or treat a cached catalog as authorization to execute a tool.

## Deployment Settings

- Set `PublicOrigin` to the externally visible origin. Use HTTPS for cloud deployments before sending bearer tokens. OAuth resource metadata uses this public origin behind TLS termination.
- Add permitted browser origins under `Mcp:AllowedOrigins` when necessary. Missing Origin is accepted for non-browser clients; unlisted supplied origins, including `null`, receive `403`. This check does not configure CORS: intentional cross-origin browser access also requires a separate CORS policy.
- `Kubernetes__Namespace` controls the namespace used for resource deployment and backend routing; the default remains `adapter`.
- Local Kubernetes deployments explicitly use shared Redis resource storage. Removing MCP transport sessions does not remove adapter/tool metadata storage or authorize deleting existing Redis/Cosmos data.
- Keep streaming unbuffered at your ingress and set timeouts appropriate for your subscriptions. The Application Gateway template disables request/response buffering and sets a 600-second backend timeout. Backends should send keepalives during long quiet periods.
- When validating a checkout, supply exact gateway and Tools images to the [deployment script](../deployment/Deploy-McpGateway.ps1). It reads the checked-out Kubernetes manifest rather than downloading one from another branch.

See the [deployment instructions](../README.md#getting-started---deploy-to-azure) and [end-to-end test instructions](../deployment/e2e/README.md) for setup and verification commands.

## Troubleshooting

| Response | Check |
| --- | --- |
| `400`, unsupported protocol version | Upgrade the client/adapter or use a compatible previous gateway image |
| `400`, missing or mismatched headers | Send the required headers and matching per-request metadata; refresh the tool schema if annotated parameters changed |
| `401` | Supply a valid token for the gateway's configured tenant and audience |
| `403` | Check resource permissions and the supplied Origin |
| `404` | Check the adapter name and whether the backend implements the requested method |
| `405` on GET/DELETE | Use POST; standalone streams and session deletion are not supported |
| `406` | Include both `application/json` and `text/event-stream` with nonzero quality in `Accept` |
| `413` | Keep each MCP request body at or below 4 MB |
| `415` | Send the request body as `Content-Type: application/json` |
| `503` | Check that the selected adapter or tool gateway has ready pods |

Downstream protocol errors retain their HTTP status and JSON-RPC body. Do not interpret every `400` or `404` as a reason to fall back to legacy initialization.

Protocol details: [MCP 2026-07-28](https://modelcontextprotocol.io/specification/2026-07-28).