Merge pull request #377 from modelcontextprotocol/ihrpr/example-clients
Streamable Http - fix server initialization validation in Stateless servers
This commit is contained in:
commit
73f464f76a
9 changed files with 806 additions and 445 deletions
36
README.md
36
README.md
|
|
@ -310,19 +310,23 @@ For simpler use cases where session management isn't needed:
|
|||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
|
||||
sessionIdGenerator: undefined, // set to undefined for stateless servers
|
||||
});
|
||||
|
||||
// Setup routes for the server
|
||||
const setupServer = async () => {
|
||||
await server.connect(transport);
|
||||
};
|
||||
|
||||
app.post('/mcp', async (req: Request, res: Response) => {
|
||||
console.log('Received MCP request:', req.body);
|
||||
// In stateless mode, create a new instance of transport and server for each request
|
||||
// to ensure complete isolation. A single instance would cause request ID collisions
|
||||
// when multiple clients connect concurrently.
|
||||
|
||||
try {
|
||||
await transport.handleRequest(req, res, req.body);
|
||||
const server = getServer();
|
||||
const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
|
||||
sessionIdGenerator: undefined,
|
||||
});
|
||||
await server.connect(transport);
|
||||
await transport.handleRequest(req, res, req.body);
|
||||
res.on('close', () => {
|
||||
console.log('Request closed');
|
||||
transport.close();
|
||||
server.close();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error handling MCP request:', error);
|
||||
if (!res.headersSent) {
|
||||
|
|
@ -362,15 +366,11 @@ app.delete('/mcp', async (req: Request, res: Response) => {
|
|||
}));
|
||||
});
|
||||
|
||||
|
||||
// Start the server
|
||||
const PORT = 3000;
|
||||
setupServer().then(() => {
|
||||
app.listen(PORT, () => {
|
||||
console.log(`MCP Streamable HTTP Server listening on port ${PORT}`);
|
||||
});
|
||||
}).catch(error => {
|
||||
console.error('Failed to set up the server:', error);
|
||||
process.exit(1);
|
||||
app.listen(PORT, () => {
|
||||
console.log(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`);
|
||||
});
|
||||
|
||||
```
|
||||
|
|
|
|||
160
src/examples/client/multipleClientsParallel.ts
Normal file
160
src/examples/client/multipleClientsParallel.ts
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import { Client } from '../../client/index.js';
|
||||
import { StreamableHTTPClientTransport } from '../../client/streamableHttp.js';
|
||||
import {
|
||||
CallToolRequest,
|
||||
CallToolResultSchema,
|
||||
LoggingMessageNotificationSchema,
|
||||
CallToolResult,
|
||||
} from '../../types.js';
|
||||
|
||||
/**
|
||||
* Multiple Clients MCP Example
|
||||
*
|
||||
* This client demonstrates how to:
|
||||
* 1. Create multiple MCP clients in parallel
|
||||
* 2. Each client calls a single tool
|
||||
* 3. Track notifications from each client independently
|
||||
*/
|
||||
|
||||
// Command line args processing
|
||||
const args = process.argv.slice(2);
|
||||
const serverUrl = args[0] || 'http://localhost:3000/mcp';
|
||||
|
||||
interface ClientConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
toolName: string;
|
||||
toolArguments: Record<string, string | number | boolean>;
|
||||
}
|
||||
|
||||
async function createAndRunClient(config: ClientConfig): Promise<{ id: string; result: CallToolResult }> {
|
||||
console.log(`[${config.id}] Creating client: ${config.name}`);
|
||||
|
||||
const client = new Client({
|
||||
name: config.name,
|
||||
version: '1.0.0'
|
||||
});
|
||||
|
||||
const transport = new StreamableHTTPClientTransport(new URL(serverUrl));
|
||||
|
||||
// Set up client-specific error handler
|
||||
client.onerror = (error) => {
|
||||
console.error(`[${config.id}] Client error:`, error);
|
||||
};
|
||||
|
||||
// Set up client-specific notification handler
|
||||
client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => {
|
||||
console.log(`[${config.id}] Notification: ${notification.params.data}`);
|
||||
});
|
||||
|
||||
try {
|
||||
// Connect to the server
|
||||
await client.connect(transport);
|
||||
console.log(`[${config.id}] Connected to MCP server`);
|
||||
|
||||
// Call the specified tool
|
||||
console.log(`[${config.id}] Calling tool: ${config.toolName}`);
|
||||
const toolRequest: CallToolRequest = {
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: config.toolName,
|
||||
arguments: {
|
||||
...config.toolArguments,
|
||||
// Add client ID to arguments for identification in notifications
|
||||
caller: config.id
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const result = await client.request(toolRequest, CallToolResultSchema);
|
||||
console.log(`[${config.id}] Tool call completed`);
|
||||
|
||||
// Keep the connection open for a bit to receive notifications
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
// Disconnect
|
||||
await transport.close();
|
||||
console.log(`[${config.id}] Disconnected from MCP server`);
|
||||
|
||||
return { id: config.id, result };
|
||||
} catch (error) {
|
||||
console.error(`[${config.id}] Error:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
console.log('MCP Multiple Clients Example');
|
||||
console.log('============================');
|
||||
console.log(`Server URL: ${serverUrl}`);
|
||||
console.log('');
|
||||
|
||||
try {
|
||||
// Define client configurations
|
||||
const clientConfigs: ClientConfig[] = [
|
||||
{
|
||||
id: 'client1',
|
||||
name: 'basic-client-1',
|
||||
toolName: 'start-notification-stream',
|
||||
toolArguments: {
|
||||
interval: 3, // 1 second between notifications
|
||||
count: 5 // Send 5 notifications
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'client2',
|
||||
name: 'basic-client-2',
|
||||
toolName: 'start-notification-stream',
|
||||
toolArguments: {
|
||||
interval: 2, // 2 seconds between notifications
|
||||
count: 3 // Send 3 notifications
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'client3',
|
||||
name: 'basic-client-3',
|
||||
toolName: 'start-notification-stream',
|
||||
toolArguments: {
|
||||
interval: 1, // 0.5 second between notifications
|
||||
count: 8 // Send 8 notifications
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Start all clients in parallel
|
||||
console.log(`Starting ${clientConfigs.length} clients in parallel...`);
|
||||
console.log('');
|
||||
|
||||
const clientPromises = clientConfigs.map(config => createAndRunClient(config));
|
||||
const results = await Promise.all(clientPromises);
|
||||
|
||||
// Display results from all clients
|
||||
console.log('\n=== Final Results ===');
|
||||
results.forEach(({ id, result }) => {
|
||||
console.log(`\n[${id}] Tool result:`);
|
||||
if (Array.isArray(result.content)) {
|
||||
result.content.forEach((item: { type: string; text?: string }) => {
|
||||
if (item.type === 'text' && item.text) {
|
||||
console.log(` ${item.text}`);
|
||||
} else {
|
||||
console.log(` ${item.type} content:`, item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log(` Unexpected result format:`, result);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n=== All clients completed successfully ===');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error running multiple clients:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Start the example
|
||||
main().catch((error: unknown) => {
|
||||
console.error('Error running MCP multiple clients example:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
197
src/examples/client/parallelToolCallsClient.ts
Normal file
197
src/examples/client/parallelToolCallsClient.ts
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
import { Client } from '../../client/index.js';
|
||||
import { StreamableHTTPClientTransport } from '../../client/streamableHttp.js';
|
||||
import {
|
||||
ListToolsRequest,
|
||||
ListToolsResultSchema,
|
||||
CallToolResultSchema,
|
||||
LoggingMessageNotificationSchema,
|
||||
CallToolResult,
|
||||
} from '../../types.js';
|
||||
|
||||
/**
|
||||
* Parallel Tool Calls MCP Client
|
||||
*
|
||||
* This client demonstrates how to:
|
||||
* 1. Start multiple tool calls in parallel
|
||||
* 2. Track notifications from each tool call using a caller parameter
|
||||
*/
|
||||
|
||||
// Command line args processing
|
||||
const args = process.argv.slice(2);
|
||||
const serverUrl = args[0] || 'http://localhost:3000/mcp';
|
||||
|
||||
async function main(): Promise<void> {
|
||||
console.log('MCP Parallel Tool Calls Client');
|
||||
console.log('==============================');
|
||||
console.log(`Connecting to server at: ${serverUrl}`);
|
||||
|
||||
let client: Client;
|
||||
let transport: StreamableHTTPClientTransport;
|
||||
|
||||
try {
|
||||
// Create client with streamable HTTP transport
|
||||
client = new Client({
|
||||
name: 'parallel-tool-calls-client',
|
||||
version: '1.0.0'
|
||||
});
|
||||
|
||||
client.onerror = (error) => {
|
||||
console.error('Client error:', error);
|
||||
};
|
||||
|
||||
// Connect to the server
|
||||
transport = new StreamableHTTPClientTransport(new URL(serverUrl));
|
||||
await client.connect(transport);
|
||||
console.log('Successfully connected to MCP server');
|
||||
|
||||
// Set up notification handler with caller identification
|
||||
client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => {
|
||||
console.log(`Notification: ${notification.params.data}`);
|
||||
});
|
||||
|
||||
console.log("List tools")
|
||||
const toolsRequest = await listTools(client);
|
||||
console.log("Tools: ", toolsRequest)
|
||||
|
||||
|
||||
// 2. Start multiple notification tools in parallel
|
||||
console.log('\n=== Starting Multiple Notification Streams in Parallel ===');
|
||||
const toolResults = await startParallelNotificationTools(client);
|
||||
|
||||
// Log the results from each tool call
|
||||
for (const [caller, result] of Object.entries(toolResults)) {
|
||||
console.log(`\n=== Tool result for ${caller} ===`);
|
||||
result.content.forEach((item: { type: string; text?: string; }) => {
|
||||
if (item.type === 'text') {
|
||||
console.log(` ${item.text}`);
|
||||
} else {
|
||||
console.log(` ${item.type} content:`, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Wait for all notifications (10 seconds)
|
||||
console.log('\n=== Waiting for all notifications ===');
|
||||
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||
|
||||
// 4. Disconnect
|
||||
console.log('\n=== Disconnecting ===');
|
||||
await transport.close();
|
||||
console.log('Disconnected from MCP server');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error running client:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List available tools on the server
|
||||
*/
|
||||
async function listTools(client: Client): Promise<void> {
|
||||
try {
|
||||
const toolsRequest: ListToolsRequest = {
|
||||
method: 'tools/list',
|
||||
params: {}
|
||||
};
|
||||
const toolsResult = await client.request(toolsRequest, ListToolsResultSchema);
|
||||
|
||||
console.log('Available tools:');
|
||||
if (toolsResult.tools.length === 0) {
|
||||
console.log(' No tools available');
|
||||
} else {
|
||||
for (const tool of toolsResult.tools) {
|
||||
console.log(` - ${tool.name}: ${tool.description}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Tools not supported by this server: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start multiple notification tools in parallel with different configurations
|
||||
* Each tool call includes a caller parameter to identify its notifications
|
||||
*/
|
||||
async function startParallelNotificationTools(client: Client): Promise<Record<string, CallToolResult>> {
|
||||
try {
|
||||
// Define multiple tool calls with different configurations
|
||||
const toolCalls = [
|
||||
{
|
||||
caller: 'fast-notifier',
|
||||
request: {
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'start-notification-stream',
|
||||
arguments: {
|
||||
interval: 2, // 0.5 second between notifications
|
||||
count: 10, // Send 10 notifications
|
||||
caller: 'fast-notifier' // Identify this tool call
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
caller: 'slow-notifier',
|
||||
request: {
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'start-notification-stream',
|
||||
arguments: {
|
||||
interval: 5, // 2 seconds between notifications
|
||||
count: 5, // Send 5 notifications
|
||||
caller: 'slow-notifier' // Identify this tool call
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
caller: 'burst-notifier',
|
||||
request: {
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'start-notification-stream',
|
||||
arguments: {
|
||||
interval: 1, // 0.1 second between notifications
|
||||
count: 3, // Send just 3 notifications
|
||||
caller: 'burst-notifier' // Identify this tool call
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
console.log(`Starting ${toolCalls.length} notification tools in parallel...`);
|
||||
|
||||
// Start all tool calls in parallel
|
||||
const toolPromises = toolCalls.map(({ caller, request }) => {
|
||||
console.log(`Starting tool call for ${caller}...`);
|
||||
return client.request(request, CallToolResultSchema)
|
||||
.then(result => ({ caller, result }))
|
||||
.catch(error => {
|
||||
console.error(`Error in tool call for ${caller}:`, error);
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for all tool calls to complete
|
||||
const results = await Promise.all(toolPromises);
|
||||
|
||||
// Organize results by caller
|
||||
const resultsByTool: Record<string, CallToolResult> = {};
|
||||
results.forEach(({ caller, result }) => {
|
||||
resultsByTool[caller] = result;
|
||||
});
|
||||
|
||||
return resultsByTool;
|
||||
} catch (error) {
|
||||
console.error(`Error starting parallel notification tools:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Start the client
|
||||
main().catch((error: unknown) => {
|
||||
console.error('Error running MCP client:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -5,74 +5,78 @@ import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
|
|||
import { z } from 'zod';
|
||||
import { CallToolResult, isInitializeRequest } from '../../types.js';
|
||||
|
||||
|
||||
// Create an MCP server with implementation details
|
||||
const server = new McpServer({
|
||||
name: 'json-response-streamable-http-server',
|
||||
version: '1.0.0',
|
||||
}, {
|
||||
capabilities: {
|
||||
logging: {},
|
||||
}
|
||||
});
|
||||
const getServer = () => {
|
||||
const server = new McpServer({
|
||||
name: 'json-response-streamable-http-server',
|
||||
version: '1.0.0',
|
||||
}, {
|
||||
capabilities: {
|
||||
logging: {},
|
||||
}
|
||||
});
|
||||
|
||||
// Register a simple tool that returns a greeting
|
||||
server.tool(
|
||||
'greet',
|
||||
'A simple greeting tool',
|
||||
{
|
||||
name: z.string().describe('Name to greet'),
|
||||
},
|
||||
async ({ name }): Promise<CallToolResult> => {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Hello, ${name}!`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
// Register a simple tool that returns a greeting
|
||||
server.tool(
|
||||
'greet',
|
||||
'A simple greeting tool',
|
||||
{
|
||||
name: z.string().describe('Name to greet'),
|
||||
},
|
||||
async ({ name }): Promise<CallToolResult> => {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Hello, ${name}!`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// 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<CallToolResult> => {
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
// 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<CallToolResult> => {
|
||||
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 sendNotification({
|
||||
method: "notifications/message",
|
||||
params: { level: "debug", data: `Starting multi-greet for ${name}` }
|
||||
});
|
||||
|
||||
await sleep(1000); // Wait 1 second before first greeting
|
||||
await sleep(1000); // Wait 1 second before first greeting
|
||||
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: { level: "info", data: `Sending first greeting to ${name}` }
|
||||
});
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: { level: "info", data: `Sending first greeting to ${name}` }
|
||||
});
|
||||
|
||||
await sleep(1000); // Wait another second before second greeting
|
||||
await sleep(1000); // Wait another second before second greeting
|
||||
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: { level: "info", data: `Sending second greeting to ${name}` }
|
||||
});
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: { level: "info", data: `Sending second greeting to ${name}` }
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Good morning, ${name}!`,
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Good morning, ${name}!`,
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
return server;
|
||||
}
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
|
@ -104,6 +108,7 @@ app.post('/mcp', async (req: Request, res: Response) => {
|
|||
});
|
||||
|
||||
// Connect the transport to the MCP server BEFORE handling the request
|
||||
const server = getServer();
|
||||
await server.connect(transport);
|
||||
await transport.handleRequest(req, res, req.body);
|
||||
return; // Already handled
|
||||
|
|
@ -153,6 +158,5 @@ app.listen(PORT, () => {
|
|||
// Handle server shutdown
|
||||
process.on('SIGINT', async () => {
|
||||
console.log('Shutting down server...');
|
||||
await server.close();
|
||||
process.exit(0);
|
||||
});
|
||||
|
|
@ -15,60 +15,63 @@ import { CallToolResult } from '../../types.js';
|
|||
*/
|
||||
|
||||
// Create an MCP server instance
|
||||
const server = new McpServer({
|
||||
name: 'simple-sse-server',
|
||||
version: '1.0.0',
|
||||
}, { capabilities: { logging: {} } });
|
||||
const getServer = () => {
|
||||
const server = new McpServer({
|
||||
name: 'simple-sse-server',
|
||||
version: '1.0.0',
|
||||
}, { capabilities: { logging: {} } });
|
||||
|
||||
server.tool(
|
||||
'start-notification-stream',
|
||||
'Starts sending periodic notifications',
|
||||
{
|
||||
interval: z.number().describe('Interval in milliseconds between notifications').default(1000),
|
||||
count: z.number().describe('Number of notifications to send').default(10),
|
||||
},
|
||||
async ({ interval, count }, { sendNotification }): Promise<CallToolResult> => {
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let counter = 0;
|
||||
server.tool(
|
||||
'start-notification-stream',
|
||||
'Starts sending periodic notifications',
|
||||
{
|
||||
interval: z.number().describe('Interval in milliseconds between notifications').default(1000),
|
||||
count: z.number().describe('Number of notifications to send').default(10),
|
||||
},
|
||||
async ({ interval, count }, { sendNotification }): Promise<CallToolResult> => {
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let counter = 0;
|
||||
|
||||
// Send the initial notification
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "info",
|
||||
data: `Starting notification stream with ${count} messages every ${interval}ms`
|
||||
}
|
||||
});
|
||||
|
||||
// Send periodic notifications
|
||||
while (counter < count) {
|
||||
counter++;
|
||||
await sleep(interval);
|
||||
|
||||
try {
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "info",
|
||||
data: `Notification #${counter} at ${new Date().toISOString()}`
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error sending notification:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Completed sending ${count} notifications every ${interval}ms`,
|
||||
// Send the initial notification
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "info",
|
||||
data: `Starting notification stream with ${count} messages every ${interval}ms`
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Send periodic notifications
|
||||
while (counter < count) {
|
||||
counter++;
|
||||
await sleep(interval);
|
||||
|
||||
try {
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "info",
|
||||
data: `Notification #${counter} at ${new Date().toISOString()}`
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error sending notification:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Completed sending ${count} notifications every ${interval}ms`,
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
return server;
|
||||
};
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
|
@ -96,6 +99,7 @@ app.get('/mcp', async (req: Request, res: Response) => {
|
|||
};
|
||||
|
||||
// Connect the transport to the MCP server
|
||||
const server = getServer();
|
||||
await server.connect(transport);
|
||||
|
||||
// Start the SSE transport to begin streaming
|
||||
|
|
@ -163,7 +167,6 @@ process.on('SIGINT', async () => {
|
|||
console.error(`Error closing transport for session ${sessionId}:`, error);
|
||||
}
|
||||
}
|
||||
await server.close();
|
||||
console.log('Server shutdown complete');
|
||||
process.exit(0);
|
||||
});
|
||||
|
|
@ -4,108 +4,111 @@ import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
|
|||
import { z } from 'zod';
|
||||
import { CallToolResult, GetPromptResult, ReadResourceResult } from '../../types.js';
|
||||
|
||||
// Create an MCP server with implementation details
|
||||
const server = new McpServer({
|
||||
name: 'stateless-streamable-http-server',
|
||||
version: '1.0.0',
|
||||
}, { capabilities: { logging: {} } });
|
||||
const getServer = () => {
|
||||
// Create an MCP server with implementation details
|
||||
const server = new McpServer({
|
||||
name: 'stateless-streamable-http-server',
|
||||
version: '1.0.0',
|
||||
}, { capabilities: { logging: {} } });
|
||||
|
||||
// Register a simple prompt
|
||||
server.prompt(
|
||||
'greeting-template',
|
||||
'A simple greeting prompt template',
|
||||
{
|
||||
name: z.string().describe('Name to include in greeting'),
|
||||
},
|
||||
async ({ name }): Promise<GetPromptResult> => {
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: {
|
||||
type: 'text',
|
||||
text: `Please greet ${name} in a friendly manner.`,
|
||||
// Register a simple prompt
|
||||
server.prompt(
|
||||
'greeting-template',
|
||||
'A simple greeting prompt template',
|
||||
{
|
||||
name: z.string().describe('Name to include in greeting'),
|
||||
},
|
||||
async ({ name }): Promise<GetPromptResult> => {
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: {
|
||||
type: 'text',
|
||||
text: `Please greet ${name} in a friendly manner.`,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Register a tool specifically for testing resumability
|
||||
server.tool(
|
||||
'start-notification-stream',
|
||||
'Starts sending periodic notifications for testing resumability',
|
||||
{
|
||||
interval: z.number().describe('Interval in milliseconds between notifications').default(100),
|
||||
count: z.number().describe('Number of notifications to send (0 for 100)').default(10),
|
||||
},
|
||||
async ({ interval, count }, { sendNotification }): Promise<CallToolResult> => {
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let counter = 0;
|
||||
|
||||
while (count === 0 || counter < count) {
|
||||
counter++;
|
||||
try {
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "info",
|
||||
data: `Periodic notification #${counter} at ${new Date().toISOString()}`
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error sending notification:", error);
|
||||
}
|
||||
// Wait for the specified interval
|
||||
await sleep(interval);
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Started sending periodic notifications every ${interval}ms`,
|
||||
// Register a tool specifically for testing resumability
|
||||
server.tool(
|
||||
'start-notification-stream',
|
||||
'Starts sending periodic notifications for testing resumability',
|
||||
{
|
||||
interval: z.number().describe('Interval in milliseconds between notifications').default(100),
|
||||
count: z.number().describe('Number of notifications to send (0 for 100)').default(10),
|
||||
},
|
||||
async ({ interval, count }, { sendNotification }): Promise<CallToolResult> => {
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let counter = 0;
|
||||
|
||||
while (count === 0 || counter < count) {
|
||||
counter++;
|
||||
try {
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "info",
|
||||
data: `Periodic notification #${counter} at ${new Date().toISOString()}`
|
||||
}
|
||||
});
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
catch (error) {
|
||||
console.error("Error sending notification:", error);
|
||||
}
|
||||
// Wait for the specified interval
|
||||
await sleep(interval);
|
||||
}
|
||||
|
||||
// Create a simple resource at a fixed URI
|
||||
server.resource(
|
||||
'greeting-resource',
|
||||
'https://example.com/greetings/default',
|
||||
{ mimeType: 'text/plain' },
|
||||
async (): Promise<ReadResourceResult> => {
|
||||
return {
|
||||
contents: [
|
||||
{
|
||||
uri: 'https://example.com/greetings/default',
|
||||
text: 'Hello, world!',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Started sending periodic notifications every ${interval}ms`,
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Create a simple resource at a fixed URI
|
||||
server.resource(
|
||||
'greeting-resource',
|
||||
'https://example.com/greetings/default',
|
||||
{ mimeType: 'text/plain' },
|
||||
async (): Promise<ReadResourceResult> => {
|
||||
return {
|
||||
contents: [
|
||||
{
|
||||
uri: 'https://example.com/greetings/default',
|
||||
text: 'Hello, world!',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
return server;
|
||||
}
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
|
||||
sessionIdGenerator: undefined,
|
||||
});
|
||||
|
||||
// Setup routes for the server
|
||||
const setupServer = async () => {
|
||||
await server.connect(transport);
|
||||
};
|
||||
|
||||
app.post('/mcp', async (req: Request, res: Response) => {
|
||||
console.log('Received MCP request:', req.body);
|
||||
const server = getServer();
|
||||
try {
|
||||
await transport.handleRequest(req, res, req.body);
|
||||
const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
|
||||
sessionIdGenerator: undefined,
|
||||
});
|
||||
await server.connect(transport);
|
||||
await transport.handleRequest(req, res, req.body);
|
||||
res.on('close', () => {
|
||||
console.log('Request closed');
|
||||
transport.close();
|
||||
server.close();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error handling MCP request:', error);
|
||||
if (!res.headersSent) {
|
||||
|
|
@ -145,28 +148,15 @@ app.delete('/mcp', async (req: Request, res: Response) => {
|
|||
}));
|
||||
});
|
||||
|
||||
|
||||
// Start the server
|
||||
const PORT = 3000;
|
||||
setupServer().then(() => {
|
||||
app.listen(PORT, () => {
|
||||
console.log(`MCP Streamable HTTP Server listening on port ${PORT}`);
|
||||
});
|
||||
}).catch(error => {
|
||||
console.error('Failed to set up the server:', error);
|
||||
process.exit(1);
|
||||
app.listen(PORT, () => {
|
||||
console.log(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`);
|
||||
});
|
||||
|
||||
// Handle server shutdown
|
||||
process.on('SIGINT', async () => {
|
||||
console.log('Shutting down server...');
|
||||
try {
|
||||
console.log(`Closing transport`);
|
||||
await transport.close();
|
||||
} catch (error) {
|
||||
console.error(`Error closing transport:`, error);
|
||||
}
|
||||
|
||||
await server.close();
|
||||
console.log('Server shutdown complete');
|
||||
process.exit(0);
|
||||
});
|
||||
|
|
@ -7,164 +7,167 @@ import { CallToolResult, GetPromptResult, isInitializeRequest, ReadResourceResul
|
|||
import { InMemoryEventStore } from '../shared/inMemoryEventStore.js';
|
||||
|
||||
// Create an MCP server with implementation details
|
||||
const server = new McpServer({
|
||||
name: 'simple-streamable-http-server',
|
||||
version: '1.0.0',
|
||||
}, { capabilities: { logging: {} } });
|
||||
const getServer = () => {
|
||||
const server = new McpServer({
|
||||
name: 'simple-streamable-http-server',
|
||||
version: '1.0.0',
|
||||
}, { capabilities: { logging: {} } });
|
||||
|
||||
// Log the capability invocation details
|
||||
server.onCapabilityChange((event) => {
|
||||
switch (event.action) {
|
||||
case 'invoked':
|
||||
console.log(`${event.capabilityType} invocation ${event.invocationIndex}: '${event.capabilityName}' started`);
|
||||
break;
|
||||
case 'completed':
|
||||
console.log(`${event.capabilityType} invocation ${event.invocationIndex}: '${event.capabilityName}' completed in ${event.durationMs}ms`);
|
||||
break;
|
||||
case 'error':
|
||||
console.log(`${event.capabilityType} invocation ${event.invocationIndex}: '${event.capabilityName}' failed in ${event.durationMs}ms: ${event.error}`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Register a simple tool that returns a greeting
|
||||
server.tool(
|
||||
'greet',
|
||||
'A simple greeting tool',
|
||||
{
|
||||
name: z.string().describe('Name to greet'),
|
||||
},
|
||||
async ({ name }): Promise<CallToolResult> => {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Hello, ${name}!`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// 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<CallToolResult> => {
|
||||
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',
|
||||
'A simple greeting prompt template',
|
||||
{
|
||||
name: z.string().describe('Name to include in greeting'),
|
||||
},
|
||||
async ({ name }): Promise<GetPromptResult> => {
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: {
|
||||
type: 'text',
|
||||
text: `Please greet ${name} in a friendly manner.`,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Register a tool specifically for testing resumability
|
||||
server.tool(
|
||||
'start-notification-stream',
|
||||
'Starts sending periodic notifications for testing resumability',
|
||||
{
|
||||
interval: z.number().describe('Interval in milliseconds between notifications').default(100),
|
||||
count: z.number().describe('Number of notifications to send (0 for 100)').default(50),
|
||||
},
|
||||
async ({ interval, count }, { sendNotification }): Promise<CallToolResult> => {
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let counter = 0;
|
||||
|
||||
while (count === 0 || counter < count) {
|
||||
counter++;
|
||||
try {
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "info",
|
||||
data: `Periodic notification #${counter} at ${new Date().toISOString()}`
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error sending notification:", error);
|
||||
}
|
||||
// Wait for the specified interval
|
||||
await sleep(interval);
|
||||
// Log the capability invocation details
|
||||
server.onCapabilityChange((event) => {
|
||||
switch (event.action) {
|
||||
case 'invoked':
|
||||
console.log(`${event.capabilityType} invocation ${event.invocationIndex}: '${event.capabilityName}' started`);
|
||||
break;
|
||||
case 'completed':
|
||||
console.log(`${event.capabilityType} invocation ${event.invocationIndex}: '${event.capabilityName}' completed in ${event.durationMs}ms`);
|
||||
break;
|
||||
case 'error':
|
||||
console.log(`${event.capabilityType} invocation ${event.invocationIndex}: '${event.capabilityName}' failed in ${event.durationMs}ms: ${event.error}`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Started sending periodic notifications every ${interval}ms`,
|
||||
// Register a simple tool that returns a greeting
|
||||
server.tool(
|
||||
'greet',
|
||||
'A simple greeting tool',
|
||||
{
|
||||
name: z.string().describe('Name to greet'),
|
||||
},
|
||||
async ({ name }): Promise<CallToolResult> => {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Hello, ${name}!`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// 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<CallToolResult> => {
|
||||
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',
|
||||
'A simple greeting prompt template',
|
||||
{
|
||||
name: z.string().describe('Name to include in greeting'),
|
||||
},
|
||||
async ({ name }): Promise<GetPromptResult> => {
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: {
|
||||
type: 'text',
|
||||
text: `Please greet ${name} in a friendly manner.`,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Register a tool specifically for testing resumability
|
||||
server.tool(
|
||||
'start-notification-stream',
|
||||
'Starts sending periodic notifications for testing resumability',
|
||||
{
|
||||
interval: z.number().describe('Interval in milliseconds between notifications').default(100),
|
||||
count: z.number().describe('Number of notifications to send (0 for 100)').default(50),
|
||||
},
|
||||
async ({ interval, count }, { sendNotification }): Promise<CallToolResult> => {
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let counter = 0;
|
||||
|
||||
while (count === 0 || counter < count) {
|
||||
counter++;
|
||||
try {
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "info",
|
||||
data: `Periodic notification #${counter} at ${new Date().toISOString()}`
|
||||
}
|
||||
});
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
catch (error) {
|
||||
console.error("Error sending notification:", error);
|
||||
}
|
||||
// Wait for the specified interval
|
||||
await sleep(interval);
|
||||
}
|
||||
|
||||
// Create a simple resource at a fixed URI
|
||||
server.resource(
|
||||
'greeting-resource',
|
||||
'https://example.com/greetings/default',
|
||||
{ mimeType: 'text/plain' },
|
||||
async (): Promise<ReadResourceResult> => {
|
||||
return {
|
||||
contents: [
|
||||
{
|
||||
uri: 'https://example.com/greetings/default',
|
||||
text: 'Hello, world!',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Started sending periodic notifications every ${interval}ms`,
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Create a simple resource at a fixed URI
|
||||
server.resource(
|
||||
'greeting-resource',
|
||||
'https://example.com/greetings/default',
|
||||
{ mimeType: 'text/plain' },
|
||||
async (): Promise<ReadResourceResult> => {
|
||||
return {
|
||||
contents: [
|
||||
{
|
||||
uri: 'https://example.com/greetings/default',
|
||||
text: 'Hello, world!',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
return server;
|
||||
};
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
|
@ -207,6 +210,7 @@ app.post('/mcp', async (req: Request, res: Response) => {
|
|||
|
||||
// Connect the transport to the MCP server BEFORE handling the request
|
||||
// so responses can flow back through the same transport
|
||||
const server = getServer();
|
||||
await server.connect(transport);
|
||||
|
||||
await transport.handleRequest(req, res, req.body);
|
||||
|
|
@ -303,7 +307,6 @@ process.on('SIGINT', async () => {
|
|||
console.error(`Error closing transport for session ${sessionId}:`, error);
|
||||
}
|
||||
}
|
||||
await server.close();
|
||||
console.log('Server shutdown complete');
|
||||
process.exit(0);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,52 +18,54 @@ import { InMemoryEventStore } from '../shared/inMemoryEventStore.js';
|
|||
* - /messages: The deprecated POST endpoint for older clients (POST to send messages)
|
||||
*/
|
||||
|
||||
const getServer = () => {
|
||||
const server = new McpServer({
|
||||
name: 'backwards-compatible-server',
|
||||
version: '1.0.0',
|
||||
}, { capabilities: { logging: {} } });
|
||||
|
||||
const server = new McpServer({
|
||||
name: 'backwards-compatible-server',
|
||||
version: '1.0.0',
|
||||
}, { capabilities: { logging: {} } });
|
||||
// Register a simple tool that sends notifications over time
|
||||
server.tool(
|
||||
'start-notification-stream',
|
||||
'Starts sending periodic notifications for testing resumability',
|
||||
{
|
||||
interval: z.number().describe('Interval in milliseconds between notifications').default(100),
|
||||
count: z.number().describe('Number of notifications to send (0 for 100)').default(50),
|
||||
},
|
||||
async ({ interval, count }, { sendNotification }): Promise<CallToolResult> => {
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let counter = 0;
|
||||
|
||||
// Register a simple tool that sends notifications over time
|
||||
server.tool(
|
||||
'start-notification-stream',
|
||||
'Starts sending periodic notifications for testing resumability',
|
||||
{
|
||||
interval: z.number().describe('Interval in milliseconds between notifications').default(100),
|
||||
count: z.number().describe('Number of notifications to send (0 for 100)').default(50),
|
||||
},
|
||||
async ({ interval, count }, { sendNotification }): Promise<CallToolResult> => {
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let counter = 0;
|
||||
|
||||
while (count === 0 || counter < count) {
|
||||
counter++;
|
||||
try {
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "info",
|
||||
data: `Periodic notification #${counter} at ${new Date().toISOString()}`
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error sending notification:", error);
|
||||
}
|
||||
// Wait for the specified interval
|
||||
await sleep(interval);
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Started sending periodic notifications every ${interval}ms`,
|
||||
while (count === 0 || counter < count) {
|
||||
counter++;
|
||||
try {
|
||||
await sendNotification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "info",
|
||||
data: `Periodic notification #${counter} at ${new Date().toISOString()}`
|
||||
}
|
||||
});
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
catch (error) {
|
||||
console.error("Error sending notification:", error);
|
||||
}
|
||||
// Wait for the specified interval
|
||||
await sleep(interval);
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Started sending periodic notifications every ${interval}ms`,
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
return server;
|
||||
};
|
||||
|
||||
// Create Express application
|
||||
const app = express();
|
||||
|
|
@ -125,6 +127,7 @@ app.all('/mcp', async (req: Request, res: Response) => {
|
|||
};
|
||||
|
||||
// Connect the transport to the MCP server
|
||||
const server = getServer();
|
||||
await server.connect(transport);
|
||||
} else {
|
||||
// Invalid request - no session ID or not initialization request
|
||||
|
|
@ -167,6 +170,7 @@ app.get('/sse', async (req: Request, res: Response) => {
|
|||
res.on("close", () => {
|
||||
delete transports[transport.sessionId];
|
||||
});
|
||||
const server = getServer();
|
||||
await server.connect(transport);
|
||||
});
|
||||
|
||||
|
|
@ -237,7 +241,6 @@ process.on('SIGINT', async () => {
|
|||
console.error(`Error closing transport for session ${sessionId}:`, error);
|
||||
}
|
||||
}
|
||||
await server.close();
|
||||
console.log('Server shutdown complete');
|
||||
process.exit(0);
|
||||
});
|
||||
|
|
@ -463,6 +463,11 @@ export class StreamableHTTPServerTransport implements Transport {
|
|||
* Returns true if the session is valid, false otherwise
|
||||
*/
|
||||
private validateSession(req: IncomingMessage, res: ServerResponse): boolean {
|
||||
if (this.sessionIdGenerator === undefined) {
|
||||
// If the sessionIdGenerator ID is not set, the session management is disabled
|
||||
// and we don't need to validate the session ID
|
||||
return true;
|
||||
}
|
||||
if (!this._initialized) {
|
||||
// If the server has not been initialized yet, reject all requests
|
||||
res.writeHead(400).end(JSON.stringify({
|
||||
|
|
@ -475,11 +480,7 @@ export class StreamableHTTPServerTransport implements Transport {
|
|||
}));
|
||||
return false;
|
||||
}
|
||||
if (this.sessionId === undefined) {
|
||||
// If the session ID is not set, the session management is disabled
|
||||
// and we don't need to validate the session ID
|
||||
return true;
|
||||
}
|
||||
|
||||
const sessionId = req.headers["mcp-session-id"];
|
||||
|
||||
if (!sessionId) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue