-
Notifications
You must be signed in to change notification settings - Fork 4.8k
add streamableHttp server support for everything server #1496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@cliffhall @Dogacel, please take a look. |
Can we document this in code? That was one thing that gave me a hard time to get started. |
@Dogacel updated the docs. I am unsure if this could be achieved with |
src/everything/streamableHttp.ts
Outdated
|
||
// Handle the request with existing transport - no need to reconnect | ||
// The existing transport is already connected to the server | ||
await transport.handleRequest(req, res, req.body); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you find it necessary to pass the req.body
argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Because StreamableHTTPServerTransport
expects it: https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/server/streamableHttp.ts#L138
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @shivdeepak, thanks for this. I tested it with your Inspector changes and it worked flawlessly. Just a few requests on message shape.
@cliffhall thanks for the review. cleaned it all up, and tested it. |
Not working now. Error from MCP server: Error: Error POSTing to endpoint (HTTP 400): {"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":"InternalServerError: stream is not readable"},"id":null}
at StreamableHTTPClientTransport.send (file:///Users/cliffhall/Projects/mcp-inspector/node_modules/@modelcontextprotocol/sdk/dist/esm/client/streamableHttp.js:263:23) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting errors because of message ids being null.
~~Hi @cliffhall ~~
|
I was able to reproduce the issue. it happens on connect. and it's happening after I removed i didn't catch it the last time because i forgot to run I am currently looking into why req.body is needed. |
@cliffhall I have addressed the two issues you raised.
PR is now ready for review. |
The problem with doing this is that it always bypasses parsing, which includes a adding a maximum message size and encoding taken from the content type parameters. What happens when we pass in the
|
This is a twist. Presumably we need an We still don't know why the client is hitting the |
Yes. The Inspector frontend talks to the Inspector backend over Given how stdio works, it makes sense for it to be implemented that way. But I do agree that for Streamable HTTP, the client side implementation should also use the I will spend some time today get that working. I will also look into why initialize is failing for this PR. |
|
src/everything/streamableHttp.ts
Outdated
|
||
const app = express(); | ||
|
||
app.use(express.json()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shivdeepak Remove this app.use(express.json())
. It wasn't necessary in sse.ts
and it is what's causing the Parse error problem. If you remove this line, you can also remove the third argument to handleRequest
and get past problem the with the initialize
message.
That moves us on to the next error:
Error from MCP server: Error: Error POSTing to endpoint (HTTP 400): {"jsonrpc":"2.0","error":{"code":-32000,"message":"Bad Request: No valid session ID provided"}}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More testing, and I have an idea that it's the inspector sending things incorrectly.
- SSE wanted a GET request to
/sse
followed POSTS to/message
. - Streamable wants a POST to
/mcp
followed by more POSTS to/mcp
with GET used to fetch SSE messages from the server. - I'm seeing us send to
/mcp
with my changes, but it's doing a GET first, like with SSE.

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm tweaking the Inspector PR now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shivdeepak Remove this
app.use(express.json())
. It wasn't necessary insse.ts
and it is what's causing the Parse error problem. If you remove this line, you can also remove the third argument tohandleRequest
and get past problem the with theinitialize
message.That moves us on to the next error:
Error from MCP server: Error: Error POSTing to endpoint (HTTP 400): {"jsonrpc":"2.0","error":{"code":-32000,"message":"Bad Request: No valid session ID provided"}}
@cliffhall I figured it out.
When we remove the express.json()
middleware, the req.body
becomes undefined
. And therefore the following condition doesn't pass and the code execution enters the final else block, which returns http 400 error response. (the error you are referring to); it never reaches transport.handleRequest(req, res)
.
} else if (!sessionId && isInitializeRequest(req.body)) {
But if we don't remove express.json()
middleware, the req payload is consumed by the middleware, therefore
transport.handleRequest(req, res)
doesn't provide request payload to the transport anymore.
I see two approaches to solve this problem
- new approach: remove
express.json()
middleware and also removeisInitializeRequest(req.body)
check, and let the transport server handle this check. This way we can removereq.body
fromtransport.handleRequest(req, res)
. - current approach: or, go with the
express.json()
middleware, keep theisInitializeRequest(req.body)
check, and then keep thereq.body
intransport.handleRequest(req, res, req.body)
I went with option 2 because the original author of StreamableHTTPServerTransport
implemented his examples this way.
I can modify the source code to go with option 1, and that way we don't have to pass req.body
to transport.handleRequest(req, res)
.
IMHO, either approach is fine. Option 1 may be a little cleaner. Let me know what you would like me to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the new approach could make sense. I still don't think the second (original) approach is good because, as I mentioned before, it always bypasses parsing, which includes a adding a maximum message size and encoding taken from the content type parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cliffhall made the fixes, and tested them against your changes from yesterday -- https://github.com/cliffhall/mcp-inspector/tree/3a2e2485273a4dd1bfb5186ceee73fab8f8b8b9a
- removed
express.json()
middleware - removed
isInitializeRequest(req.body)
check - removed
req.body
fromtransport.handleRequest(req, res, req.body)
9507ddd
to
d23c18c
Compare
9570904
to
c25d1bc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All good. Working fine with the newly improved Inspector, and nothing changed that affects previous functionality.
} | ||
|
||
// Check for Last-Event-ID header for resumability | ||
const lastEventId = req.headers['last-event-id'] as string | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% certain how to test this part. I think we need to beef up the logic in the Inspector to make the Reconnect button send the last-event-id
header.
But this endpoint is working correctly for initiating and maintaining the SSE stream, and the only thing we're doing here is logging the fact that this proxy saw the header. The SDK deals with recognizing that header and doing a replay from it, so I think this server is just fine.
Description
Since Streamable HTTP transport landed on
typescript-sdk
a few days back, this PR uses that and adds Streamable HTTP server support to everything server. This directly addresses the current open issue: #1247Server Details
Motivation and Context
I am also trying to add support for Streamable HTTP on inspector, and
everything
server reference implementation would help testing it out.How Has This Been Tested?
The server can be started with the new entry point
npm run start:streamableHttp
.I tested it with the development version inspector with Streamable HTTP support to ensure functionality works.
Breaking Changes
No. This is an additional server, separate from the existing SSE implementation.
Types of changes
Checklist
Additional context