From 71aeb9a3a653e9da0cb29eb3c228ab60eddfcaa3 Mon Sep 17 00:00:00 2001 From: ihrpr Date: Wed, 9 Apr 2025 21:38:57 +0100 Subject: [PATCH] add examples with notifications --- src/examples/client/simpleStreamableHttp.ts | 25 +++++++++- .../server/jsonResponseStreamableHttp.ts | 44 ++++++++++++++++ src/examples/server/simpleStreamableHttp.ts | 50 +++++++++++++++++-- 3 files changed, 113 insertions(+), 6 deletions(-) diff --git a/src/examples/client/simpleStreamableHttp.ts b/src/examples/client/simpleStreamableHttp.ts index 589c1f41..b17add14 100644 --- a/src/examples/client/simpleStreamableHttp.ts +++ b/src/examples/client/simpleStreamableHttp.ts @@ -10,7 +10,8 @@ import { GetPromptRequest, GetPromptResultSchema, ListResourcesRequest, - ListResourcesResultSchema + ListResourcesResultSchema, + LoggingMessageNotificationSchema } from '../../types.js'; async function main(): Promise { @@ -19,12 +20,17 @@ async function main(): Promise { name: 'example-client', version: '1.0.0' }); + const transport = new StreamableHTTPClientTransport( new URL('http://localhost:3000/mcp') ); // Connect the client using the transport and initialize the server await client.connect(transport); + client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => { + console.log(`Notification received: ${notification.params.level} - ${notification.params.data}`); + }); + console.log('Connected to MCP server'); // List available tools @@ -46,6 +52,23 @@ async function main(): Promise { const greetResult = await client.request(greetRequest, CallToolResultSchema); console.log('Greeting result:', greetResult.content[0].text); + // Call the new 'multi-greet' tool + console.log('\nCalling multi-greet tool (with notifications)...'); + const multiGreetRequest: CallToolRequest = { + method: 'tools/call', + params: { + name: 'multi-greet', + arguments: { name: 'MCP User' } + } + }; + const multiGreetResult = await client.request(multiGreetRequest, CallToolResultSchema); + console.log('Multi-greet results:'); + multiGreetResult.content.forEach(item => { + if (item.type === 'text') { + console.log(`- ${item.text}`); + } + }); + // List available prompts try { const promptsRequest: ListPromptsRequest = { diff --git a/src/examples/server/jsonResponseStreamableHttp.ts b/src/examples/server/jsonResponseStreamableHttp.ts index 2e83b54a..9b053631 100644 --- a/src/examples/server/jsonResponseStreamableHttp.ts +++ b/src/examples/server/jsonResponseStreamableHttp.ts @@ -9,6 +9,10 @@ import { CallToolResult } from '../../types.js'; const server = new McpServer({ name: 'json-response-streamable-http-server', version: '1.0.0', +}, { + capabilities: { + logging: {}, + } }); // Register a simple tool that returns a greeting @@ -30,6 +34,46 @@ server.tool( } ); +// Register a tool that sends multiple greetings with notifications +server.tool( + 'multi-greet', + 'A tool that sends different greetings with delays between them', + { + name: z.string().describe('Name to greet'), + }, + async ({ name }, { sendNotification }): Promise => { + const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + + await sendNotification({ + method: "notifications/message", + params: { level: "debug", data: `Starting multi-greet for ${name}` } + }); + + await sleep(1000); // Wait 1 second before first greeting + + await sendNotification({ + method: "notifications/message", + params: { level: "info", data: `Sending first greeting to ${name}` } + }); + + await sleep(1000); // Wait another second before second greeting + + await sendNotification({ + method: "notifications/message", + params: { level: "info", data: `Sending second greeting to ${name}` } + }); + + return { + content: [ + { + type: 'text', + text: `Good morning, ${name}!`, + } + ], + }; + } +); + const app = express(); app.use(express.json()); diff --git a/src/examples/server/simpleStreamableHttp.ts b/src/examples/server/simpleStreamableHttp.ts index e6ebe4b9..74c3ef53 100644 --- a/src/examples/server/simpleStreamableHttp.ts +++ b/src/examples/server/simpleStreamableHttp.ts @@ -9,7 +9,7 @@ import { CallToolResult, GetPromptResult, ReadResourceResult } from '../../types const server = new McpServer({ name: 'simple-streamable-http-server', version: '1.0.0', -}); +}, { capabilities: { logging: {} } }); // Register a simple tool that returns a greeting server.tool( @@ -30,6 +30,46 @@ server.tool( } ); +// Register a tool that sends multiple greetings with notifications +server.tool( + 'multi-greet', + 'A tool that sends different greetings with delays between them', + { + name: z.string().describe('Name to greet'), + }, + async ({ name }, { sendNotification }): Promise => { + const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + + await sendNotification({ + method: "notifications/message", + params: { level: "debug", data: `Starting multi-greet for ${name}` } + }); + + await sleep(1000); // Wait 1 second before first greeting + + await sendNotification({ + method: "notifications/message", + params: { level: "info", data: `Sending first greeting to ${name}` } + }); + + await sleep(1000); // Wait another second before second greeting + + await sendNotification({ + method: "notifications/message", + params: { level: "info", data: `Sending second greeting to ${name}` } + }); + + return { + content: [ + { + type: 'text', + text: `Good morning, ${name}!`, + } + ], + }; + } +); + // Register a simple prompt server.prompt( 'greeting-template', @@ -81,7 +121,7 @@ app.post('/mcp', async (req: Request, res: Response) => { // Check for existing session ID const sessionId = req.headers['mcp-session-id'] as string | undefined; let transport: StreamableHTTPServerTransport; - + if (sessionId && transports[sessionId]) { // Reuse existing transport transport = transports[sessionId]; @@ -90,14 +130,14 @@ app.post('/mcp', async (req: Request, res: Response) => { transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), }); - + // Connect the transport to the MCP server BEFORE handling the request // so responses can flow back through the same transport await server.connect(transport); - + // After handling the request, if we get a session ID back, store the transport await transport.handleRequest(req, res, req.body); - + // Store the transport by session ID for future requests if (transport.sessionId) { transports[transport.sessionId] = transport;