modelcontextprotocol / modelcontextprotocol/typescript-sdk

StreamableHTTPServerTransport rejects JSON-only Accept with 406 even when enableJsonResponse: true

Open
#1,944 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug fix proposed P2 ready for work
Dominant language
TypeScript
Stars
13.4k
Forks
2.2k
Avg merge
3d 15h
Merged PRs (30d)
4

Description

Summary

StreamableHTTPServerTransport returns 406 Not Acceptable when a client sends Accept: application/json without also including text/event-stream, even when the transport is constructed with enableJsonResponse: true. In that mode the transport is operating in pure request/response — there is no SSE stream to negotiate — so requiring text/event-stream in Accept is incorrect.

This blocks buyer agents and validators that probe MCP servers with a plain JSON Accept (a reasonable default for JSON-RPC over HTTP). Every MCP seller implementation in the AdCP ecosystem hits this; we currently ship a tiny Express middleware that rewrites the Accept header before the transport sees it, which is an escape hatch we'd rather not need.

Reproduction

import express from 'express';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';

const app = express();
app.use(express.json());

app.post('/mcp', async (req, res) => {
  const server = new McpServer({ name: 'repro', version: '0.0.0' });
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: undefined,
    enableJsonResponse: true, // pure request/response, no SSE
  });
  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

app.listen(3000);
# Fails with 406 Not Acceptable
curl -sS -X POST http://localhost:3000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"c","version":"0"}}}'

# Succeeds when both are advertised
curl -sS -X POST http://localhost:3000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"c","version":"0"}}}'

Expected behavior

When enableJsonResponse: true and the response will be a plain JSON body (no SSE), the Accept check should accept application/json alone. text/event-stream should only be required when the transport is about to open a stream.

Proposed fix

In the POST handler's Accept validation:

  • If enableJsonResponse is true and the request is not one the transport would stream for, require only application/json (or a wildcard that matches it) in Accept.
  • Keep the existing stricter check for the streaming path: when the response will be SSE, continue to require text/event-stream.
  • GET (SSE subscription) is unaffected — that path genuinely needs text/event-stream.

Happy to send a PR if the approach looks right.

Context

Filed from the AdCP client SDK (@adcp/client), which ships a temporary middleware that normalizes Accept before the transport sees it. We'd like to drop that once this is fixed upstream.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at StreamableHTTPServerTransport.handleRequest, focusing on the POST handler's Accept validation and how enableJsonResponse distinguishes JSON responses from SSE streams. Run the provided curl reproduction, then verify that JSON-only Accept succeeds for non-streaming responses while SSE POST and GET negotiation retain their existing requirements.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend-api-design
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.