-
Notifications
You must be signed in to change notification settings - Fork 3
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
feat: add PoC of stateless HTTP transport #24
base: main
Are you sure you want to change the base?
Conversation
This is an implementation of a stateless transport over HTTP for MCP. It's not part of the official spec, but something similar will probably be added in the future. Functionally, it just presents a single HTTP endpoint (conventionally at `/mcp`) that accepts JSONRPC messages as POST bodies, and returns JSONRPC messages as responses. Each inbound message is validated and gets its own 'server' instance. The handler then takes care of initializing the server (which in a stateless protocol is the responsibility of the client), then forwards the inbound message to the server, and returns the server's response. We could easily add middleware for things like authentication, similar to in #21. This also comes with a 'client' implementation which is similar to the stdio and sse clients in the official SDK and returns a pair of read/write streams for use with a `ClientSession`. This is useful for testing or writing example MCP clients. The idea is that this transport will be compatible with the [HTTP transport in the unofficial Go SDK][go-sdk], so we can start building examples of usage. Notably this client also supports custom headers, which will be useful for authentication. [go-sdk]: https://github.com/metoro-io/mcp-golang/blob/main/examples/http_example/README.md
b3ddf55
to
2ffc1de
Compare
This client transport is a stateless transport that uses JSONRPC over HTTP POST, foregoing stateful communication and therefore avoiding some of the complexity of the SSE transport (and the need for persistent connections). It does mean that server-to-client communication is not possible. This transport is also implemented in Python in [this PR], and the TypeScript client has been tested against the Python server. [this PR]: grafana/mcp-grafana#24
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.
It's not part of the official spec, but something similar will probably be added in the future.
Will it be a pain to maintain the unofficial client if this doesn't get merged into the official spec? I remember reading somewhere that we'd have to maintain our own implementation anyway, but just confirming we are ok with that outcome.
Everything else LGTM!
src/mcp_grafana/transports/http.py
Outdated
logger.debug("Waiting for request body") | ||
body = await write_stream_reader.receive() | ||
|
||
logger.debug(f"Connecting to HTTP endpoint: {url}") |
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.
nit: This has bitten me before when the URLs contain usernames/passwords 😬 but its behind debug so might be ok!
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 wouldn't say that's ok, we commonly enable debug logging in prod as needed. We should make sure to clean the url before writing it.
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.
Good catch, fixed.
This is an implementation of a stateless transport over HTTP for MCP. It's not part of the official spec, but something similar will probably be added in the future.
Functionally, it just presents a single HTTP endpoint (conventionally at
/mcp
) that accepts JSONRPC messages as POST bodies, and returns JSONRPC messages as responses.Each inbound message is validated and gets its own 'server' instance. The handler then takes care of initializing the server (which in a stateless protocol is the responsibility of the client), then forwards the inbound message to the server, and returns the server's response.
We could easily add middleware for things like authentication, similar to in #21.
This also comes with a 'client' implementation which is similar to the stdio and sse clients in the official SDK and returns a pair of read/write streams for use with a
ClientSession
. This is useful for testing or writing example MCP clients.The idea is that this transport will be compatible with the HTTP transport in the unofficial Go SDK, so we can start building examples of usage. Notably this client also supports custom headers, which will be useful for authentication.
You can test this out locally by running it like this:
uvx --with-editable . mcp-grafana --transport http
Then calling it like this: