Repository navigation
174: MCP stateless - #194
JaeYeonLee0621 wants to merge 2 commits into
Conversation
|
I will write my review after the next release but for testing you can use the https://github.com/modelcontextprotocol/inspector. You can then start Aqueduct locally and route to a locally hosted MCP server (e.g. |
natkam
left a comment
There was a problem hiding this comment.
Yay, so much code removed! Nice 😁
Just a few small things (mostly regarding inaccurate comments and documentation), but nothing terribly serious.
| MCP-compatible tools. | ||
| implements the **2026-07-28** version of the streamable HTTP transport specification, which has a **stateless protocol | ||
| core**. There is no handshake, no session id, and no server-initiated stream — every request is self-contained and can | ||
| land on any gateway instance behind a plain round-robin load balancer. |
There was a problem hiding this comment.
Question: Do we have a round-robin load balancer? Even if we do, I'm not sure if this deployment detail is relevant here in the API documentation...
| # NOTE: the ``vllm`` extra was removed because ``vllm==0.8.4`` requires | ||
| # ``opentelemetry-api<1.27`` while ``mcp`` (v2, 2026-07-28 protocol) requires ``>=1.28`` -- | ||
| # they cannot share one environment. vllm is unused by the codebase (never imported), | ||
| # and the ``INTEGRATION_TEST_BACKEND=vllm`` mode is incomplete, so it was dropped entirely. |
There was a problem hiding this comment.
This comment makes sense as a PR comment, but not in the actual project file.
| Each POST is a self-contained JSON-RPC exchange: the gateway requires the | ||
| 2026-07-28 ``Mcp-Method`` header, then relays the request to the configured | ||
| upstream server and returns its response. There is no session, no GET stream, | ||
| and no DELETE endpoint. |
There was a problem hiding this comment.
The last sentence sounds like a description of what changed in this PR, but I don't think it's relevant here in the docstring of an endpoint that explicitly requires POST.
| The gateway always advertises the latest protocol version and the JSON-RPC | ||
| method, mirroring the client's ``Mcp-Method``/``Mcp-Name`` headers when sent and | ||
| otherwise deriving them from the message body. This makes every relayed request | ||
| self-describing so it can land on any instance behind a round-robin load balancer. |
There was a problem hiding this comment.
Most of this paragraph doesn't really describe what this function is doing, does it? It's also almost as long as the function's body. It's probably faster to read the function's body than this text, so I would just delete that paragraph from the docstring.
| *args: Any, | ||
| **kwargs: Any, | ||
| ) -> JsonResponse | StreamingHttpResponse: | ||
| request: ASGIRequest, name: str | None, json_rpc_message: Any = None, *args: Any, **kwargs: Any |
There was a problem hiding this comment.
Just to confirm that it's not an oversight: does json_rpc_message have to be Any? The annotation has changed - until now it's been JSONRPCMessage | None - but I see we still make some assumptions about the object itself (e.g. that it won't explode when we call model_dump_json on it in _relay). The docstring of _relay also explicitly mentions relaying a JSON-RPC message. In principle, a type adapter's validate_python should return the same type of the object that it received, so maybe the type annotation here could be improved somehow?
| The MCP gateway acts as a bridge between your client application and MCP servers. You can request tools, call functions, | ||
| and receive responses through standard HTTP requests while the gateway manages the underlying session. | ||
| Aqueduct acts as a stateless bridge between your client application and MCP servers. Each `POST` carries the protocol | ||
| version, client identity, and capabilities either in HTTP headers or in the JSON-RPC `_meta` field. The gateway |
There was a problem hiding this comment.
This description sounds like the requests to Aqueduct could carry the "client identity" and all the other details in the _meta field. But do we actually support it? I think right now we require the user's token to be sent in the header. But maybe I misunderstood or missed something here?
Here's a suggestion of an update to this paragraph (feel free to come up with something else, but please ask your agent to make it sound like human language):
Each call to an MCP server through Aqueduct is an independent, self-contained exchange. There is no handshake and no session to manage, and nothing is held in memory between requests. The gateway checks the request headers, forwards it to the configured upstream server, and returns its response (a JSON message or an SSE stream) unchanged.
Also, since we actually require token authentication for this endpoint, I would say the Required headers section should mention it, even though it's not a part of the MCP protocol.
| self.assertEqual(r1.json(), r2.json()) | ||
|
|
||
| def test_relay_streams_sse_response(self): | ||
| from asgiref.sync import async_to_sync |
There was a problem hiding this comment.
Please put the imports at the top of the file :)
| [dependency-groups] | ||
| dev = [ | ||
| "celery-types>=0.26.0", | ||
| "channels[daphne]>=4.3.1", |
There was a problem hiding this comment.
Not sure if we still need channels now that we don't have to run the mock MCP server :)
MCP: stateful → stateless (2026-07-28 protocol)
mcp1.25.0 → 2.2.0 (stateless protocol core).mcp-session-id, no GET listening stream, no DELETE endpoint.POSTis now a self-contained exchange: the gateway requires theMcp-Method/Mcp-Nameheaders and relays the request to the configured upstream, returning its response (JSON or passthrough SSE). No state held between requests.+) https://blog.modelcontextprotocol.io/posts/2026-07-28/
+) But how.. could i test it before deploying in production environment? -> staging chat ai