Skip to content

Commit ba197ae

Browse files
committed
refactor: remove support for legacy SSE protocol
1 parent 451c467 commit ba197ae

File tree

4 files changed

+28
-128
lines changed

4 files changed

+28
-128
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ You can test the MCP server using the MCP Inspector:
218218
2. In your browser, open the MCP Inspector (the URL will be shown in the terminal)
219219

220220
3. Configure the connection:
221-
- **Transport**: Streamable HTTP or SSE
222-
- **URL**: `http://localhost:3000/mcp` (for Streamable HTTP) or `http://localhost:3000/sse` (for legacy SSE)
221+
- **Transport**: Streamable HTTP
222+
- **URL**: `http://localhost:3000/mcp`
223223

224224
4. Click **Connect** and explore the available tools
225225

docs/blog/01-release/release.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Before we play a bit with the sample, let's have a look at the main services imp
6363
| ------- | ---- | ---- |
6464
| Agent Web App (`agent-webapp`) | Chat UI + streaming + session history | Azure Static Web Apps, Lit web components |
6565
| Agent API (`agent-api`) | LangChain.js v1 agent orchestration + auth + history | Azure Functions, Node.js |
66-
| Burger MCP Server (`burger-mcp`) | Exposes burger API as tools over MCP (Streamable HTTP + SSE) | Azure Functions, Express, MCP SDK |
66+
| Burger MCP Server (`burger-mcp`) | Exposes burger API as tools over MCP (Streamable HTTP) | Azure Functions, Express, MCP SDK |
6767
| Burger API (`burger-api`) | Business logic: burgers, toppings, orders lifecycle | Azure Functions, Cosmos DB |
6868

6969
Here's a simplified view of how they interact:

packages/burger-mcp/README.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ This is the Burger MCP server, exposing the Burger API as a Model Context Protoc
1818
This server supports the following transport types:
1919

2020
- **Streamable HTTP**
21-
- **SSE** (legacy protocol, for backward compatibility)
2221
- **Stdio** (currently only supported when starting the server locally with `npm start:local`)
2322

2423
The remote server is deployed with [Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-overview).
@@ -56,9 +55,6 @@ First, you need to start the Burger API and Burger MCP server locally.
5655
4. Put `http://localhost:3000/mcp` in the URL field and click on the **Connect** button.
5756
5. In the **Tools** tab, select **List Tools**. Click on a tool and select **Run Tool**.
5857

59-
> [!NOTE]
60-
> This application also provides an SSE endpoint if you use `/sse` instead of `/mcp` in the URL field.
61-
6258
## Development
6359

6460
### Getting started
@@ -73,7 +69,7 @@ You can run the following command to run the application server:
7369
npm start
7470
```
7571

76-
This will start the application server. The MCP server is then available at `http://localhost:3000/mcp` or `http://localhost:3000/sse` for the streamable HTTP and SSE endpoints, respectively.
72+
This will start the application server. The MCP server is then available at `http://localhost:3000/mcp` for the streamable HTTP endpoints.
7773

7874
Alternatively, you can run the MCP server with stdio transport using:
7975

packages/burger-mcp/src/server.ts

Lines changed: 24 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import process from 'node:process';
2-
import { randomUUID } from 'node:crypto';
32
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
4-
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
5-
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
63
import express, { Request, Response } from 'express';
74
import { burgerApiUrl } from './config.js';
85
import { getMcpServer } from './mcp.js';
@@ -14,77 +11,42 @@ app.get('/', (_request: Request, response: Response) => {
1411
response.send({ status: 'up', message: `Burger MCP server running (Using burger API URL: ${burgerApiUrl})` });
1512
});
1613

17-
// Store transports by session ID
18-
const transports: Record<string, StreamableHTTPServerTransport | SSEServerTransport> = {};
19-
20-
// ----------------------------------------------------------------------------
21-
// New streamable HTTP transport
22-
// ----------------------------------------------------------------------------
23-
2414
// Handle all MCP Streamable HTTP requests (GET, POST, DELETE) on a single endpoint
2515
app.all('/mcp', async (request: Request, response: Response) => {
2616
console.log(`Received ${request.method} request to /mcp`);
2717

28-
try {
29-
// Check for existing session ID
30-
const sessionId = request.headers['mcp-session-id'] as string | undefined;
31-
let transport: StreamableHTTPServerTransport;
32-
33-
if (sessionId && transports[sessionId]) {
34-
// Check if the transport is of the correct type
35-
const existingTransport = transports[sessionId];
36-
if (existingTransport instanceof StreamableHTTPServerTransport) {
37-
// Reuse existing transport
38-
transport = existingTransport;
39-
} else {
40-
// Transport exists but is not a StreamableHTTPServerTransport (could be SSEServerTransport)
41-
response.status(400).json({
42-
jsonrpc: '2.0',
43-
error: {
44-
code: -32_000,
45-
message: 'Bad Request: Session exists but uses a different transport protocol',
46-
},
47-
id: null,
48-
});
49-
return;
50-
}
51-
} else if (!sessionId && request.method === 'POST' && isInitializeRequest(request.body)) {
52-
transport = new StreamableHTTPServerTransport({
53-
sessionIdGenerator: () => randomUUID(),
54-
onsessioninitialized(sessionId) {
55-
// Store the transport by session ID when session is initialized
56-
console.log(`StreamableHTTP session initialized with ID: ${sessionId}`);
57-
transports[sessionId] = transport;
58-
},
59-
});
60-
61-
// Set up onclose handler to clean up transport when closed
62-
transport.onclose = () => {
63-
const sid = transport.sessionId;
64-
if (sid && transports[sid]) {
65-
console.log(`Transport closed for session ${sid}, removing from transports map`);
66-
delete transports[sid];
67-
}
68-
};
69-
70-
// Connect the transport to the MCP server
71-
const server = getMcpServer();
72-
await server.connect(transport);
73-
} else {
74-
// Invalid request - no session ID or not initialization request
75-
response.status(400).json({
18+
// Reject unsupported methods (these are only needed for stateful sessions)
19+
if (request.method === 'GET' || request.method === 'DELETE') {
20+
response.writeHead(405).end(
21+
JSON.stringify({
7622
jsonrpc: '2.0',
7723
error: {
7824
code: -32_000,
79-
message: 'Bad Request: No valid session ID provided',
25+
message: 'Method not allowed.',
8026
},
8127
id: null,
82-
});
83-
return;
84-
}
28+
}),
29+
);
30+
return;
31+
}
32+
33+
try {
34+
const transport = new StreamableHTTPServerTransport({
35+
sessionIdGenerator: undefined,
36+
});
37+
38+
// Connect the transport to the MCP server
39+
const server = getMcpServer();
40+
await server.connect(transport);
8541

8642
// Handle the request with the transport
8743
await transport.handleRequest(request, response, request.body);
44+
45+
// Clean up when the response is closed
46+
response.on('close', async () => {
47+
await transport.close();
48+
await server.close();
49+
});
8850
} catch (error) {
8951
console.error('Error handling MCP request:', error);
9052
if (!response.headersSent) {
@@ -100,48 +62,6 @@ app.all('/mcp', async (request: Request, response: Response) => {
10062
}
10163
});
10264

103-
// ----------------------------------------------------------------------------
104-
// Deprecated SSE transport
105-
// ----------------------------------------------------------------------------
106-
107-
app.get('/sse', async (request: Request, response: Response) => {
108-
console.log('Received GET request to /sse (deprecated SSE transport)');
109-
const transport = new SSEServerTransport('/messages', response);
110-
transports[transport.sessionId] = transport;
111-
response.on('close', () => {
112-
delete transports[transport.sessionId];
113-
});
114-
const server = getMcpServer();
115-
await server.connect(transport);
116-
});
117-
118-
app.post('/messages', async (request: Request, response: Response) => {
119-
const sessionId = request.query.sessionId as string;
120-
let transport: SSEServerTransport;
121-
const existingTransport = transports[sessionId];
122-
if (existingTransport instanceof SSEServerTransport) {
123-
// Reuse existing transport
124-
transport = existingTransport;
125-
} else {
126-
// Transport exists but is not a SSEServerTransport (could be StreamableHTTPServerTransport)
127-
response.status(400).json({
128-
jsonrpc: '2.0',
129-
error: {
130-
code: -32_000,
131-
message: 'Bad Request: Session exists but uses a different transport protocol',
132-
},
133-
id: null,
134-
});
135-
return;
136-
}
137-
138-
if (transport) {
139-
await transport.handlePostMessage(request, response, request.body);
140-
} else {
141-
response.status(400).send('No transport found for sessionId');
142-
}
143-
});
144-
14565
// Start the server
14666
const PORT = process.env.FUNCTIONS_CUSTOMHANDLER_PORT || process.env.PORT || 3000;
14767
app.listen(PORT, () => {
@@ -151,21 +71,5 @@ app.listen(PORT, () => {
15171
// Handle server shutdown
15272
process.on('SIGINT', async () => {
15373
console.log('Shutting down server...');
154-
155-
// Close all active transports to properly clean up resources
156-
for (const sessionId in transports) {
157-
if (Object.hasOwn(transports, sessionId)) {
158-
try {
159-
console.log(`Closing transport for session ${sessionId}`);
160-
// eslint-disable-next-line no-await-in-loop
161-
await transports[sessionId].close();
162-
delete transports[sessionId];
163-
} catch (error) {
164-
console.error(`Error closing transport for session ${sessionId}:`, error);
165-
}
166-
}
167-
}
168-
169-
console.log('Server shutdown complete');
17074
process.exit(0);
17175
});

0 commit comments

Comments
 (0)