Skip to content

Commit c4d53d7

Browse files
Pass props to do streamable (#246)
* Add failing test * One-line fix for the failing test * Add changeset
1 parent dc0e8de commit c4d53d7

5 files changed

Lines changed: 134 additions & 3 deletions

File tree

.changeset/fair-kings-grab.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"agents": patch
3+
---
4+
5+
Ensure we are passing ctx.props to McpAgent for the Streamable transport

packages/agents/src/mcp/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,7 @@ export abstract class McpAgent<
952952
const isInitialized = await doStub.isInitialized();
953953

954954
if (isInitializationRequest) {
955+
await doStub._init(ctx.props);
955956
await doStub.setInitialized();
956957
} else if (!isInitialized) {
957958
// if we have gotten here, then a session id that was never initialized

packages/agents/src/tests/mcp-sse.test.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ describe("test", () => {
7676
expect(jsonResponse.jsonrpc).toBe("2.0");
7777
expect(jsonResponse.id).toBe("1");
7878
expect(jsonResponse.result.tools).toBeDefined();
79-
expect(jsonResponse.result.tools.length).toBe(1);
79+
expect(jsonResponse.result.tools.length).toBe(2);
8080
expect(jsonResponse.result.tools[0]).toEqual({
8181
name: "greet",
8282
description: "A simple greeting tool",
@@ -110,7 +110,7 @@ describe("test", () => {
110110
const sessionId = lines[1].split("=")[1];
111111
expect(sessionId).toBeDefined();
112112

113-
// send a message to the session to list the tools
113+
// send a message to the session to invoke the greet tool
114114
const toolsRequest = new Request(
115115
`http://example.com/sse/message?sessionId=${sessionId}`,
116116
{
@@ -156,4 +156,66 @@ describe("test", () => {
156156
},
157157
});
158158
});
159+
160+
it("should pass props to the agent", async () => {
161+
const ctx = createExecutionContext();
162+
163+
const request = new Request("http://example.com/sse");
164+
const sseStream = await worker.fetch(request, env, ctx);
165+
166+
const reader = sseStream.body?.getReader();
167+
let { done, value } = await reader!.read();
168+
const event = new TextDecoder().decode(value);
169+
170+
// parse the session id from the event
171+
const lines = event.split("\n");
172+
const sessionId = lines[1].split("=")[1];
173+
expect(sessionId).toBeDefined();
174+
175+
// send a message to the session to invoke the getPropsTestValue tool
176+
const toolsRequest = new Request(
177+
`http://example.com/sse/message?sessionId=${sessionId}`,
178+
{
179+
method: "POST",
180+
headers: {
181+
"Content-Type": "application/json",
182+
},
183+
body: JSON.stringify({
184+
jsonrpc: "2.0",
185+
method: "tools/call",
186+
id: "2",
187+
params: {
188+
name: "getPropsTestValue",
189+
arguments: {},
190+
},
191+
}),
192+
}
193+
);
194+
195+
const toolsResponse = await worker.fetch(toolsRequest, env, ctx);
196+
expect(toolsResponse.status).toBe(202);
197+
expect(toolsResponse.headers.get("Content-Type")).toBe("text/event-stream");
198+
expect(await toolsResponse.text()).toBe("Accepted");
199+
200+
({ done, value } = await reader!.read());
201+
202+
expect(done).toBe(false);
203+
const toolsEvent = new TextDecoder().decode(value);
204+
const jsonResponse = JSON.parse(
205+
toolsEvent.split("\n")[1].replace("data: ", "")
206+
);
207+
208+
expect(jsonResponse).toEqual({
209+
jsonrpc: "2.0",
210+
id: "2",
211+
result: {
212+
content: [
213+
{
214+
type: "text",
215+
text: "123",
216+
},
217+
],
218+
},
219+
});
220+
});
159221
});

packages/agents/src/tests/mcp-streamable-http.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,4 +477,46 @@ describe("McpAgent Streamable HTTP Transport", () => {
477477
expect(text2).toContain('"id":"req-2"');
478478
expect(text2).toContain("Hello, Connection2"); // tools/call result
479479
});
480+
481+
it("should pass props to the agent", async () => {
482+
const ctx = createExecutionContext();
483+
const sessionId = await initializeServer(ctx);
484+
485+
const toolCallMessage: JSONRPCMessage = {
486+
jsonrpc: "2.0",
487+
method: "tools/call",
488+
params: {
489+
name: "getPropsTestValue",
490+
arguments: {},
491+
},
492+
id: "call-1",
493+
};
494+
495+
const response = await sendPostRequest(
496+
ctx,
497+
baseUrl,
498+
toolCallMessage,
499+
sessionId
500+
);
501+
expect(response.status).toBe(200);
502+
503+
const text = await readSSEEvent(response);
504+
const eventLines = text.split("\n");
505+
const dataLine = eventLines.find((line) => line.startsWith("data:"));
506+
expect(dataLine).toBeDefined();
507+
508+
const eventData = JSON.parse(dataLine!.substring(5));
509+
expect(eventData).toMatchObject({
510+
jsonrpc: "2.0",
511+
result: {
512+
content: [
513+
{
514+
type: "text",
515+
text: "123",
516+
},
517+
],
518+
},
519+
id: "call-1",
520+
});
521+
});
480522
});

packages/agents/src/tests/worker.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ export type Env = {
77
MCP_OBJECT: DurableObjectNamespace<McpAgent>;
88
};
99

10-
export class TestMcpAgent extends McpAgent {
10+
type State = unknown;
11+
12+
type Props = {
13+
testValue: string;
14+
};
15+
16+
export class TestMcpAgent extends McpAgent<Env, State, Props> {
1117
server = new McpServer(
1218
{ name: "test-server", version: "1.0.0" },
1319
{ capabilities: { logging: {} } }
@@ -22,13 +28,28 @@ export class TestMcpAgent extends McpAgent {
2228
return { content: [{ type: "text", text: `Hello, ${name}!` }] };
2329
}
2430
);
31+
32+
this.server.tool(
33+
"getPropsTestValue",
34+
{},
35+
async (): Promise<CallToolResult> => {
36+
return {
37+
content: [{ type: "text", text: this.props.testValue }],
38+
};
39+
}
40+
);
2541
}
2642
}
2743

2844
export default {
2945
fetch(request: Request, env: Env, ctx: ExecutionContext) {
3046
const url = new URL(request.url);
3147

48+
// set some props that should be passed init
49+
ctx.props = {
50+
testValue: "123",
51+
};
52+
3253
if (url.pathname === "/sse" || url.pathname === "/sse/message") {
3354
return TestMcpAgent.serveSSE("/sse").fetch(request, env, ctx);
3455
}

0 commit comments

Comments
 (0)