Skip to content

Commit 693fdc3

Browse files
authored
Merge pull request #266 from modelcontextprotocol/ihrpr/http-streamable-server
Server implementation of Streamable HTTP transport
2 parents 0d0af54 + 88dc565 commit 693fdc3

File tree

7 files changed

+1759
-33
lines changed

7 files changed

+1759
-33
lines changed

src/client/index.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ test("should initialize with matching protocol version", async () => {
6666
protocolVersion: LATEST_PROTOCOL_VERSION,
6767
}),
6868
}),
69+
expect.objectContaining({
70+
relatedRequestId: undefined,
71+
}),
6972
);
7073

7174
// Should have the instructions returned

src/server/mcp.test.ts

+63-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
ListPromptsResultSchema,
1212
GetPromptResultSchema,
1313
CompleteResultSchema,
14+
LoggingMessageNotificationSchema,
1415
} from "../types.js";
1516
import { ResourceTemplate } from "./mcp.js";
1617
import { completable } from "./completable.js";
@@ -85,6 +86,8 @@ describe("ResourceTemplate", () => {
8586
const abortController = new AbortController();
8687
const result = await template.listCallback?.({
8788
signal: abortController.signal,
89+
sendRequest: () => { throw new Error("Not implemented") },
90+
sendNotification: () => { throw new Error("Not implemented") }
8891
});
8992
expect(result?.resources).toHaveLength(1);
9093
expect(list).toHaveBeenCalled();
@@ -318,7 +321,7 @@ describe("tool()", () => {
318321

319322
// This should succeed
320323
mcpServer.tool("tool1", () => ({ content: [] }));
321-
324+
322325
// This should also succeed and not throw about request handlers
323326
mcpServer.tool("tool2", () => ({ content: [] }));
324327
});
@@ -376,6 +379,63 @@ describe("tool()", () => {
376379
expect(receivedSessionId).toBe("test-session-123");
377380
});
378381

382+
test("should provide sendNotification within tool call", async () => {
383+
const mcpServer = new McpServer(
384+
{
385+
name: "test server",
386+
version: "1.0",
387+
},
388+
{ capabilities: { logging: {} } },
389+
);
390+
391+
const client = new Client(
392+
{
393+
name: "test client",
394+
version: "1.0",
395+
},
396+
{
397+
capabilities: {
398+
tools: {},
399+
},
400+
},
401+
);
402+
403+
let receivedLogMessage: string | undefined;
404+
const loggingMessage = "hello here is log message 1";
405+
406+
client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => {
407+
receivedLogMessage = notification.params.data as string;
408+
});
409+
410+
mcpServer.tool("test-tool", async ({ sendNotification }) => {
411+
await sendNotification({ method: "notifications/message", params: { level: "debug", data: loggingMessage } });
412+
return {
413+
content: [
414+
{
415+
type: "text",
416+
text: "Test response",
417+
},
418+
],
419+
};
420+
});
421+
422+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
423+
await Promise.all([
424+
client.connect(clientTransport),
425+
mcpServer.server.connect(serverTransport),
426+
]);
427+
await client.request(
428+
{
429+
method: "tools/call",
430+
params: {
431+
name: "test-tool",
432+
},
433+
},
434+
CallToolResultSchema,
435+
);
436+
expect(receivedLogMessage).toBe(loggingMessage);
437+
});
438+
379439
test("should allow client to call server tools", async () => {
380440
const mcpServer = new McpServer({
381441
name: "test server",
@@ -815,7 +875,7 @@ describe("resource()", () => {
815875
},
816876
],
817877
}));
818-
878+
819879
// This should also succeed and not throw about request handlers
820880
mcpServer.resource("resource2", "test://resource2", async () => ({
821881
contents: [
@@ -1321,7 +1381,7 @@ describe("prompt()", () => {
13211381
},
13221382
],
13231383
}));
1324-
1384+
13251385
// This should also succeed and not throw about request handlers
13261386
mcpServer.prompt("prompt2", async () => ({
13271387
messages: [

src/server/mcp.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import {
3737
PromptArgument,
3838
GetPromptResult,
3939
ReadResourceResult,
40+
ServerRequest,
41+
ServerNotification,
4042
} from "../types.js";
4143
import { Completable, CompletableDef } from "./completable.js";
4244
import { UriTemplate, Variables } from "../shared/uriTemplate.js";
@@ -694,9 +696,9 @@ export type ToolCallback<Args extends undefined | ZodRawShape = undefined> =
694696
Args extends ZodRawShape
695697
? (
696698
args: z.objectOutputType<Args, ZodTypeAny>,
697-
extra: RequestHandlerExtra,
699+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
698700
) => CallToolResult | Promise<CallToolResult>
699-
: (extra: RequestHandlerExtra) => CallToolResult | Promise<CallToolResult>;
701+
: (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => CallToolResult | Promise<CallToolResult>;
700702

701703
type RegisteredTool = {
702704
description?: string;
@@ -717,15 +719,15 @@ export type ResourceMetadata = Omit<Resource, "uri" | "name">;
717719
* Callback to list all resources matching a given template.
718720
*/
719721
export type ListResourcesCallback = (
720-
extra: RequestHandlerExtra,
722+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
721723
) => ListResourcesResult | Promise<ListResourcesResult>;
722724

723725
/**
724726
* Callback to read a resource at a given URI.
725727
*/
726728
export type ReadResourceCallback = (
727729
uri: URL,
728-
extra: RequestHandlerExtra,
730+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
729731
) => ReadResourceResult | Promise<ReadResourceResult>;
730732

731733
type RegisteredResource = {
@@ -740,7 +742,7 @@ type RegisteredResource = {
740742
export type ReadResourceTemplateCallback = (
741743
uri: URL,
742744
variables: Variables,
743-
extra: RequestHandlerExtra,
745+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
744746
) => ReadResourceResult | Promise<ReadResourceResult>;
745747

746748
type RegisteredResourceTemplate = {
@@ -760,9 +762,9 @@ export type PromptCallback<
760762
> = Args extends PromptArgsRawShape
761763
? (
762764
args: z.objectOutputType<Args, ZodTypeAny>,
763-
extra: RequestHandlerExtra,
765+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
764766
) => GetPromptResult | Promise<GetPromptResult>
765-
: (extra: RequestHandlerExtra) => GetPromptResult | Promise<GetPromptResult>;
767+
: (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => GetPromptResult | Promise<GetPromptResult>;
766768

767769
type RegisteredPrompt = {
768770
description?: string;

0 commit comments

Comments
 (0)