Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 1 addition & 2 deletions docs/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ Key examples:
- [`jsonResponseStreamableHttp.ts`](../src/examples/server/jsonResponseStreamableHttp.ts) – `enableJsonResponse: true`, no SSE
- [`standaloneSseWithGetStreamableHttp.ts`](../src/examples/server/standaloneSseWithGetStreamableHttp.ts) – notifications with Streamable HTTP GET + SSE

See the MCP spec for full transport details:
`https://modelcontextprotocol.io/specification/2025-03-26/basic/transports`
See the MCP spec for full transport details: `https://modelcontextprotocol.io/specification/2025-11-25/basic/transports`

### Stateless vs stateful sessions

Expand Down
8 changes: 4 additions & 4 deletions src/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ npx tsx src/examples/client/simpleClientCredentials.ts

### Backwards Compatible Client

A client that implements backwards compatibility according to the [MCP specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#backwards-compatibility), allowing it to work with both new and legacy servers. This client demonstrates:
A client that implements backwards compatibility according to the [MCP specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#backwards-compatibility), allowing it to work with both new and legacy servers. This client demonstrates:

- The client first POSTs an initialize request to the server URL:
- If successful, it uses the Streamable HTTP transport
Expand Down Expand Up @@ -83,7 +83,7 @@ These examples demonstrate how to set up an MCP server on a single node with dif

##### Simple Streamable HTTP Server

A server that implements the Streamable HTTP transport (protocol version 2025-03-26).
A server that implements the Streamable HTTP transport (protocol version 2025-11-25).

- Basic server setup with Express and the Streamable HTTP transport
- Session management with an in-memory event store for resumability
Expand Down Expand Up @@ -166,7 +166,7 @@ npx tsx src/examples/server/simpleSseServer.ts

#### Streamable Http Backwards Compatible Server with SSE

A server that supports both Streamable HTTP and SSE transports, adhering to the [MCP specification for backwards compatibility](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#backwards-compatibility).
A server that supports both Streamable HTTP and SSE transports, adhering to the [MCP specification for backwards compatibility](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#backwards-compatibility).

- Single MCP server instance with multiple transport options
- Support for Streamable HTTP requests at `/mcp` endpoint (GET/POST/DELETE)
Expand Down Expand Up @@ -337,7 +337,7 @@ To test the backwards compatibility features:
# Legacy SSE server (protocol version 2024-11-05)
npx tsx src/examples/server/simpleSseServer.ts

# Streamable HTTP server (protocol version 2025-03-26)
# Streamable HTTP server (protocol version 2025-11-25)
npx tsx src/examples/server/simpleStreamableHttp.ts

# Backwards compatible server (supports both protocols)
Expand Down
16 changes: 10 additions & 6 deletions src/examples/server/jsonResponseStreamableHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ const getServer = () => {
);

// Register a simple tool that returns a greeting
server.tool(
server.registerTool(
'greet',
'A simple greeting tool',
{
name: z.string().describe('Name to greet')
description: 'A simple greeting tool',
inputSchema: {
name: z.string().describe('Name to greet')
}
},
async ({ name }): Promise<CallToolResult> => {
return {
Expand All @@ -40,11 +42,13 @@ const getServer = () => {
);

// Register a tool that sends multiple greetings with notifications
server.tool(
server.registerTool(
'multi-greet',
'A tool that sends different greetings with delays between them',
{
name: z.string().describe('Name to greet')
description: 'A tool that sends different greetings with delays between them',
inputSchema: {
name: z.string().describe('Name to greet')
}
},
async ({ name }, extra): Promise<CallToolResult> => {
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
Expand Down
10 changes: 6 additions & 4 deletions src/examples/server/simpleSseServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ const getServer = () => {
{ capabilities: { logging: {} } }
);

server.tool(
server.registerTool(
'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)
description: 'Starts sending periodic notifications',
inputSchema: {
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 }, extra): Promise<CallToolResult> => {
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
Expand Down
20 changes: 12 additions & 8 deletions src/examples/server/simpleStatelessStreamableHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ const getServer = () => {
);

// Register a simple prompt
server.prompt(
server.registerPrompt(
'greeting-template',
'A simple greeting prompt template',
{
name: z.string().describe('Name to include in greeting')
description: 'A simple greeting prompt template',
argsSchema: {
name: z.string().describe('Name to include in greeting')
}
},
async ({ name }): Promise<GetPromptResult> => {
return {
Expand All @@ -38,12 +40,14 @@ const getServer = () => {
);

// Register a tool specifically for testing resumability
server.tool(
server.registerTool(
'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)
description: 'Starts sending periodic notifications for testing resumability',
inputSchema: {
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 }, extra): Promise<CallToolResult> => {
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
Expand Down Expand Up @@ -78,7 +82,7 @@ const getServer = () => {
);

// Create a simple resource at a fixed URI
server.resource(
server.registerResource(
'greeting-resource',
'https://example.com/greetings/default',
{ mimeType: 'text/plain' },
Expand Down
36 changes: 21 additions & 15 deletions src/examples/server/simpleStreamableHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,18 @@ const getServer = () => {
);

// Register a tool that sends multiple greetings with notifications (with annotations)
server.tool(
server.registerTool(
'multi-greet',
'A tool that sends different greetings with delays between them',
{
name: z.string().describe('Name to greet')
},
{
title: 'Multiple Greeting Tool',
readOnlyHint: true,
openWorldHint: false
description: 'A tool that sends different greetings with delays between them',
inputSchema: {
name: z.string().describe('Name to greet')
},
annotations: {
title: 'Multiple Greeting Tool',
readOnlyHint: true,
openWorldHint: false
}
},
async ({ name }, extra): Promise<CallToolResult> => {
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
Expand Down Expand Up @@ -121,11 +123,13 @@ const getServer = () => {
);
// Register a tool that demonstrates form elicitation (user input collection with a schema)
// This creates a closure that captures the server instance
server.tool(
server.registerTool(
'collect-user-info',
'A tool that collects user information through form elicitation',
{
infoType: z.enum(['contact', 'preferences', 'feedback']).describe('Type of information to collect')
description: 'A tool that collects user information through form elicitation',
inputSchema: {
infoType: z.enum(['contact', 'preferences', 'feedback']).describe('Type of information to collect')
}
},
async ({ infoType }, extra): Promise<CallToolResult> => {
let message: string;
Expand Down Expand Up @@ -302,12 +306,14 @@ const getServer = () => {
);

// Register a tool specifically for testing resumability
server.tool(
server.registerTool(
'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)
description: 'Starts sending periodic notifications for testing resumability',
inputSchema: {
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 }, extra): Promise<CallToolResult> => {
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
Expand Down
18 changes: 10 additions & 8 deletions src/examples/server/sseAndStreamableHttpCompatibleServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { createMcpExpressApp } from '../../server/express.js';
/**
* This example server demonstrates backwards compatibility with both:
* 1. The deprecated HTTP+SSE transport (protocol version 2024-11-05)
* 2. The Streamable HTTP transport (protocol version 2025-03-26)
* 2. The Streamable HTTP transport (protocol version 2025-11-25)
*
* It maintains a single MCP server instance but exposes two transport options:
* - /mcp: The new Streamable HTTP endpoint (supports GET/POST/DELETE)
Expand All @@ -29,12 +29,14 @@ const getServer = () => {
);

// Register a simple tool that sends notifications over time
server.tool(
server.registerTool(
'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)
description: 'Starts sending periodic notifications for testing resumability',
inputSchema: {
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 }, extra): Promise<CallToolResult> => {
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
Expand Down Expand Up @@ -77,7 +79,7 @@ const app = createMcpExpressApp();
const transports: Record<string, StreamableHTTPServerTransport | SSEServerTransport> = {};

//=============================================================================
// STREAMABLE HTTP TRANSPORT (PROTOCOL VERSION 2025-03-26)
// STREAMABLE HTTP TRANSPORT (PROTOCOL VERSION 2025-11-25)
//=============================================================================

// Handle all MCP Streamable HTTP requests (GET, POST, DELETE) on a single endpoint
Expand Down Expand Up @@ -214,10 +216,10 @@ app.listen(PORT, error => {
==============================================
SUPPORTED TRANSPORT OPTIONS:

1. Streamable Http(Protocol version: 2025-03-26)
1. Streamable Http(Protocol version: 2025-11-25)
Endpoint: /mcp
Methods: GET, POST, DELETE
Usage:
Usage:
- Initialize with POST to /mcp
- Establish SSE stream with GET to /mcp
- Send requests with POST to /mcp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};

const addResource = (name: string, content: string) => {
const uri = `https://mcp-example.com/dynamic/${encodeURIComponent(name)}`;
server.resource(
server.registerResource(
name,
uri,
{ mimeType: 'text/plain', description: `Dynamic resource: ${name}` },
Expand Down
Loading