Skip to content

Streamable Http - fix server initialization validation in Stateless servers #377

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 14 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,19 +312,19 @@ 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);
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) {
Expand Down Expand Up @@ -364,15 +364,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}`);
});

```
Expand Down
160 changes: 160 additions & 0 deletions src/examples/client/multipleClientsParallel.ts
Original file line number Diff line number Diff line change
@@ -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);
});
Loading