Calculator Learning Demo - SSE (Legacy) Transport
โ ๏ธ DEPRECATED TRANSPORT โ ๏ธ This repository demonstrates the legacy HTTP + SSE transport for educational purposes only. For new projects, please use the modern Streamable HTTP transport.
๐ฏ Overview
This repository provides a reference implementation of an MCP server using the classic two-endpoint HTTP + Server-Sent Events (SSE) transport. It is intentionally designed to teach the concepts, complexities, and limitations of this deprecated pattern compared to modern, single-endpoint transports.
๐ง Educational Echo Tool
This server includes an optional educational tool for learning MCP concepts:
- Tool Name: Configurable via
SAMPLE_TOOL_NAMEenvironment variable - Functionality: Simple echo tool that demonstrates basic MCP tool patterns
- Usage: Set
SAMPLE_TOOL_NAME=your_tool_nameto add the tool to the server - Purpose: Provides a minimal example for understanding MCP tool registration and execution
Key Characteristics
- Asymmetric Channels: Utilizes
GET /ssefor a persistent server-to-client event stream and a separatePOST /messagesendpoint for client-to-server commands. - Ephemeral Session State: Each connection establishes a new, isolated session on the server. All state (e.g., calculation history) is stored in memory and is lost when the connection closes.
- No Resumability: If the SSE connection is lost, the session cannot be recovered. The client must establish a new session.
- Network Dependency: Requires careful handling of network proxies and firewalls that may buffer or block SSE streams.
๐ Transport Comparison
This table compares the four primary MCP transport mechanisms demonstrated in the learning series. The implementation in this repository is highlighted.
| Dimension | STDIO | SSE (Legacy) | Streamable HTTP (Stateful) | Streamable HTTP (Stateless) |
|---|---|---|---|---|
| Transport Layer | Local Pipes (stdin/stdout) |
โ
2 ร HTTP endpoints (GET+POST) |
Single HTTP endpoint /mcp |
Single HTTP endpoint /mcp |
| Bidirectional Stream | โ Yes (full duplex) | โ ๏ธ ServerโClient only | โ Yes (server push + client stream) | โ Yes (within each request) |
| State Management | Ephemeral (Process Memory) | โ Ephemeral (Session Memory) | Persistent (Session State) | โ None (Stateless) |
| Resumability | โ None | โ None | โ
Yes (Last-Event-Id) |
โ None (by design) |
| Scalability | โ ๏ธ Single Process | โ Multi-Client | โ Horizontal (Sticky Sessions) | โพ๏ธ Infinite (Serverless) |
| Security | ๐ Process Isolation | ๐ Network Exposed | ๐ Network Exposed | ๐ Network Exposed |
| Ideal Use Case | CLI Tools, IDE Plugins | โ Legacy Web Apps | Enterprise APIs, Workflows | Serverless, Edge Functions |
๐ Architecture and Flow
The legacy SSE transport pattern requires a two-step communication flow. The client first establishes a long-lived GET request to the /sse endpoint to listen for events. The server responds with a unique sessionId, which the client must then include as a query parameter in all subsequent POST requests to the /messages endpoint. This allows the server to route incoming commands to the correct session and event stream.
sequenceDiagram
participant Client
participant Server
Note over Client,Server: Connection Establishment
Client->>Server: GET /sse
Server-->>Client: Establishes text/event-stream connection
Server-->>Client: event: endpoint\ndata: /messages?sessionId=abc-123
Note over Client,Server: Client-to-Server Request
Client->>Server: POST /messages?sessionId=abc-123
Note right of Client: Body: {"jsonrpc":"2.0", "method":"tools/call", ...}
Server-->>Client: 202 Accepted (Response will be sent via SSE stream)
Note over Client,Server: Server-to-Client Response / Notification
Server-->>Client: event: message\ndata: {"jsonrpc":"2.0", "id":1, "result":...}
โจ Feature Compliance
This server implements a limited subset of the MCP Latest Standard to demonstrate the core SSE pattern. Features requiring more complex state or interaction models are stubbed or not implemented.
| Name | Status | Implementation |
|---|---|---|
calculate |
Core โ | Basic arithmetic operations (add, subtract, multiply, divide, power, sqrt). |
batch_calculate |
Not Implemented | Returns JSON-RPC error -32601 Method not found. |
advanced_calculate |
Not Implemented | Returns JSON-RPC error -32601 Method not found. |
demo_progress |
Extended โ | Simulates progress by logging to the console; designed to show how events would be pushed. |
explain-calculation |
Core โ | Returns a Markdown explanation prompt. |
generate-problems |
Core โ | Returns a Markdown practice problem prompt. |
calculator-tutor |
Core โ | Returns a Markdown tutoring content prompt. |
solve_math_problem |
Stub | Returns a message: "Limited support in SSE demo". |
explain_formula |
Stub | Returns a message: "Limited support in SSE demo". |
calculator_assistant |
Stub | Returns a message: "Limited support in SSE demo". |
calculator://constants |
Core โ | Resource for static JSON constants. |
calculator://history/* |
Extended โ | Resource for session-specific calculation history. |
calculator://stats |
Extended โ | Resource for session-specific usage statistics. |
formulas://library |
Not Implemented | This resource is not included in this example. |
๐ Getting Started
Prerequisites
- Node.js (v18.x or higher)
- npm or yarn
Installation
# Clone the repository
git clone https://github.com/modelcontextprotocol/mcp-server-examples.git
cd mcp-server-examples/sse
# Install dependencies
npm install
# Build the project
npm run build
Running the Server
# Start the server on port 1923
npm start
# Or, run in development mode with auto-reload
npm run dev
Testing with MCP Inspector
You can interact with the running server using the official MCP Inspector CLI, which understands the two-endpoint SSE transport.
npx @modelcontextprotocol/inspector --cli http://localhost:1923/sse --transport sse
๐ API Usage Examples
The following curl examples demonstrate the required two-terminal interaction.
1. Connect and Establish Session
In your first terminal, establish a persistent connection to the /sse endpoint. This terminal will now display all events sent from the server for your session.
# In Terminal 1: Keep this running to see server-sent events
# The -N flag disables buffering, showing events as they arrive.
curl -N http://localhost:1923/sse
# Server Response:
# event: endpoint
# data: /messages?sessionId=YOUR_UNIQUE_SESSION_ID
Copy the sessionId from the response data. You will need it for the next step.
2. Call a Tool
In a second terminal, use the sessionId you just received to make a POST request to the /messages endpoint.
# In Terminal 2: Send a command
# Replace YOUR_UNIQUE_SESSION_ID with the actual ID from Terminal 1.
curl -X POST 'http://localhost:1923/messages?sessionId=YOUR_UNIQUE_SESSION_ID' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "calculate",
"arguments": { "op": "multiply", "a": 7, "b": 6 }
}
}'
The server will respond with 202 Accepted in Terminal 2. The actual result of the calculation will appear as a message event in Terminal 1.
๐ง State Management Model
State is ephemeral and scoped to the SSE session. This model is fundamental to the transport's design and limitations.
- Session Registry: As seen in
src/server/index.ts, a globaltransportsobject maps eachsessionIdto its activeSSEServerTransportinstance. This is the core routing mechanism. - In-Memory Isolation: Each new connection to
/ssetriggers thecreateCalculatorServer()factory, which creates a new, isolatedMcpServerinstance. State, such as thecalculationHistoryarray, is local to that instance and not shared between sessions. - No Persistence: When a client disconnects, the
transport.onclosehandler inindex.tsfires, which removes the session from the global registry (delete transports[sessionId]). The server instance and all its associated in-memory state are then garbage collected.
๐ก๏ธ Security Model
As a network-exposed service, this transport relies on HTTP-based security patterns.
- Session ID: The
sessionIdgenerated bycrypto.randomUUID()acts as an ephemeral bearer token for the duration of the connection. It authenticatesPOSTrequests to a specific client's event stream. - CORS: The server in
src/server/index.tsis explicitly configured withcors()middleware to allow cross-origin requests, which is essential for browser-based clients. The allowed origin can be restricted for production environments via theCORS_ORIGINenvironment variable. - Input Validation: All incoming tool parameters are rigorously validated against Zod schemas defined in
src/types/calculator.tsto prevent invalid data from causing runtime errors. - No Resumability: While a limitation, the lack of resumability also provides a security benefit: a lost or stolen
sessionIdis only useful as long as the original SSE connection is active, limiting its exposure.
๐งช Testing
This project includes a test suite covering the server's functionality.
# Run all tests (51 passing, 8 skipped expected)
npm test
# Run tests with code coverage report
npm run test:coverage
# Run tests in watch mode for development
npm run test:watch