Skip to content

Commit f4d1131

Browse files
Merge pull request #158 from jwvictor/add-sessionid-to-context
Add sessionID to RequestHandlerExtra type for tool invocations so multi-user services can distinguish users
2 parents 66e1508 + a22092b commit f4d1131

File tree

4 files changed

+71
-1
lines changed

4 files changed

+71
-1
lines changed

src/inMemory.ts

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export class InMemoryTransport implements Transport {
1111
onclose?: () => void;
1212
onerror?: (error: Error) => void;
1313
onmessage?: (message: JSONRPCMessage) => void;
14+
sessionId?: string;
1415

1516
/**
1617
* Creates a pair of linked in-memory transports that can communicate with each other. One should be passed to a Client and one to a Server.

src/server/mcp.test.ts

+53
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,59 @@ describe("tool()", () => {
323323
mcpServer.tool("tool2", () => ({ content: [] }));
324324
});
325325

326+
test("should pass sessionId to tool callback via RequestHandlerExtra", async () => {
327+
const mcpServer = new McpServer({
328+
name: "test server",
329+
version: "1.0",
330+
});
331+
332+
const client = new Client(
333+
{
334+
name: "test client",
335+
version: "1.0",
336+
},
337+
{
338+
capabilities: {
339+
tools: {},
340+
},
341+
},
342+
);
343+
344+
let receivedSessionId: string | undefined;
345+
mcpServer.tool("test-tool", async (extra) => {
346+
receivedSessionId = extra.sessionId;
347+
return {
348+
content: [
349+
{
350+
type: "text",
351+
text: "Test response",
352+
},
353+
],
354+
};
355+
});
356+
357+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
358+
// Set a test sessionId on the server transport
359+
serverTransport.sessionId = "test-session-123";
360+
361+
await Promise.all([
362+
client.connect(clientTransport),
363+
mcpServer.server.connect(serverTransport),
364+
]);
365+
366+
await client.request(
367+
{
368+
method: "tools/call",
369+
params: {
370+
name: "test-tool",
371+
},
372+
},
373+
CallToolResultSchema,
374+
);
375+
376+
expect(receivedSessionId).toBe("test-session-123");
377+
});
378+
326379
test("should allow client to call server tools", async () => {
327380
const mcpServer = new McpServer({
328381
name: "test server",

src/shared/protocol.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ export type RequestHandlerExtra = {
8888
* An abort signal used to communicate if the request was cancelled from the sender's side.
8989
*/
9090
signal: AbortSignal;
91+
92+
/**
93+
* The session ID from the transport, if available.
94+
*/
95+
sessionId?: string;
9196
};
9297

9398
/**
@@ -307,9 +312,15 @@ export abstract class Protocol<
307312
const abortController = new AbortController();
308313
this._requestHandlerAbortControllers.set(request.id, abortController);
309314

315+
// Create extra object with both abort signal and sessionId from transport
316+
const extra: RequestHandlerExtra = {
317+
signal: abortController.signal,
318+
sessionId: this._transport?.sessionId,
319+
};
320+
310321
// Starting with Promise.resolve() puts any synchronous errors into the monad as well.
311322
Promise.resolve()
312-
.then(() => handler(request, { signal: abortController.signal }))
323+
.then(() => handler(request, extra))
313324
.then(
314325
(result) => {
315326
if (abortController.signal.aborted) {

src/shared/transport.ts

+5
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ export interface Transport {
4141
* Callback for when a message (request or response) is received over the connection.
4242
*/
4343
onmessage?: (message: JSONRPCMessage) => void;
44+
45+
/**
46+
* The session ID generated for this connection.
47+
*/
48+
sessionId?: string;
4449
}

0 commit comments

Comments
 (0)