Skip to content

Commit f27587e

Browse files
Document relatedRequestId routing for bidirectional MCP streams
Add comprehensive documentation for the relatedRequestId routing feature that enables proper server-to-client request routing in Streamable HTTP transport. This feature is essential for bidirectional communication patterns like elicitation where the server needs to send requests back to the client while maintaining proper request-response pairing. Related: cloudflare/agents#654 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 965efb9 commit f27587e

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed

src/content/docs/agents/model-context-protocol/transport.mdx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,68 @@ With these few changes, your MCP server will support both transport methods, mak
109109
While most MCP clients have not yet adopted the new Streamable HTTP transport, you can start testing it today using [`mcp-remote`](https://www.npmjs.com/package/mcp-remote), an adapter that lets MCP clients that otherwise only support local connections work with remote MCP servers.
110110
111111
Follow [this guide](/agents/guides/test-remote-mcp-server/) for instructions on how to connect to your remote MCP server from Claude Desktop, Cursor, Windsurf, and other local MCP clients, using the [`mcp-remote` local proxy](https://www.npmjs.com/package/mcp-remote).
112+
113+
## Bidirectional streams with relatedRequestId
114+
115+
The Streamable HTTP transport enables true bidirectional communication between MCP clients and servers, allowing servers to initiate requests back to clients while maintaining the correct request-response pairing. This is essential for features like [elicitation](/agents/concepts/human-in-the-loop/), where the server needs to request input from the user during tool execution.
116+
117+
### How relatedRequestId routing works
118+
119+
When a client sends a request to an MCP server over Streamable HTTP, the server maintains a mapping between the request ID and the HTTP stream. If the server needs to send a server-to-client request (such as `elicitation/create`) while processing the original request, it can use the `relatedRequestId` option to route that message through the same stream.
120+
121+
The `WorkerTransport` class automatically handles this routing by:
122+
123+
1. Mapping each client request ID to its originating HTTP stream
124+
2. Using the `relatedRequestId` option from `TransportSendOptions` to determine which stream should receive server-initiated requests
125+
3. Falling back to a standalone GET stream for messages without a `relatedRequestId`
126+
127+
### When to use relatedRequestId
128+
129+
Use `relatedRequestId` when your MCP server needs to:
130+
131+
- Request user input during tool execution (elicitation)
132+
- Send progress updates related to a specific client request
133+
- Make callbacks to the client in response to an ongoing operation
134+
135+
Without `relatedRequestId`, server-initiated messages are sent through a separate standalone stream, which may not maintain the proper context for request-response pairing.
136+
137+
### Implementation example
138+
139+
The Agents SDK handles `relatedRequestId` routing automatically when using `McpAgent`. Here is how the transport layer routes messages:
140+
141+
<TypeScriptExample>
142+
143+
```ts title="Transport routing implementation"
144+
import type {
145+
Transport,
146+
TransportSendOptions
147+
} from "@modelcontextprotocol/sdk/shared/transport.js";
148+
import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
149+
150+
// Simplified example of relatedRequestId routing
151+
async send(
152+
message: JSONRPCMessage,
153+
options?: TransportSendOptions
154+
): Promise<void> {
155+
// Check relatedRequestId first to route server-to-client requests
156+
// through the same stream as the originating client request
157+
let requestId = options?.relatedRequestId;
158+
159+
// Then override with message.id for responses/errors
160+
if (isJSONRPCResponse(message) || isJSONRPCError(message)) {
161+
requestId = message.id;
162+
}
163+
164+
// Route to the appropriate stream based on requestId
165+
const stream = this.getStreamForRequest(requestId);
166+
await stream.send(message);
167+
}
168+
```
169+
170+
</TypeScriptExample>
171+
172+
This routing mechanism ensures that server-initiated requests maintain the proper context and are delivered through the correct bidirectional stream, enabling seamless request-response flows for complex interactions like elicitation.
173+
174+
:::note
175+
The `relatedRequestId` routing feature is specific to Streamable HTTP transport and is handled automatically by the `WorkerTransport` class in the Agents SDK. SSE transport does not support this bidirectional routing pattern.
176+
:::

0 commit comments

Comments
 (0)