logo
Free, unlimited AI code reviews that run on commit
git-lrc git-lrc GitHub Install Now We'd appreciate a star git-lrc - Free, unlimited AI code reviews that run on commit | Product Hunt git-lrc - Free, unlimited AI code reviews that run on commit | Product Hunt

express-protocol-middleware

A set of Express routing handlers designed to seamlessly embed the Model Context Protocol (MCP) within Node.js web servers, offering unified management for both persistent, context-aware sessions and isolated, single-invocation communications with Large Language Models (LLMs). Features built-in Server-Sent Events (SSE) support for real-time interaction.

Author

express-protocol-middleware logo

jhgaylor

No License

Quick Info

GitHub GitHub Stars 11
NPM Weekly Downloads 0
Tools 1
Last Updated 2026-02-19

Tags

apisprotocolrequestscontext protocolmcp handlerrequest handling

express-protocol-middleware

A collection of middleware utilities for integrating the Model Context Protocol (MCP) specification directly into Express-based applications, streamlining the bidirectional data flow between LLMs and external tooling.

npm version npm downloads License: MIT Node.js Version TypeScript CI codecov

Understanding Model Context Protocol (MCP)

Model Context Protocol (MCP) establishes an open, structured methodology for interfacing Artificial Intelligence models (LLMs) with external services, data reservoirs, and executable tools. This standardization allows AI agents to dynamically retrieve current information and perform defined actions via a consistent communication layer.

Core Capabilities

  • Session-Based Operation: Supports sustained, long-lived conversational contexts identified by session tokens, utilizing Server-Sent Events (SSE) for push notifications.
  • Non-Persistent Handling: Offers an isolated execution path where every incoming request is treated as brand new, without any stored prior state.
  • SSE Conduit: Specialized endpoints facilitating MCP communication over Server-Sent Events, supporting both initialization (GET) and message transmission (POST).
  • Type Safety: Engineered entirely in TypeScript to ensure robust, compile-time verification of interfaces.
  • Adaptable Configuration: Allows granular customization over session lifecycle management, error interception, and hook execution.
  • Express Native: Integrates seamlessly within the Express request pipeline via standard middleware attachment.

Acquisition

Install via npm package manager:

bash npm install express-mcp-handler

Or using yarn:

bash yarn add express-mcp-handler

Or pnpm:

bash pnpm add express-mcp-handler

Mandatory Prerequisites

This library necessitates the following peer dependencies to function correctly:

  • express version 4.0.0 or newer
  • @modelcontextprotocol/sdk version 1.10.2 or newer
  • zod version 3.0.0 or newer

Ensure these are installed alongside this package:

bash npm install express @modelcontextprotocol/sdk zod

Rapid Initialization Example

This basic blueprint demonstrates mounting the non-persistent handler:

ts import express from 'express'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { statelessHandler } from 'express-mcp-handler';

const app = express(); app.use(express.json());

// Define a function that produces a fresh McpServer instance for every incoming transaction const serverFactory = () => new McpServer({ name: 'my-mcp-server', version: '1.0.0', });

// Deploy the stateless processor middleware app.post('/mcp', statelessHandler(serverFactory));

app.listen(3000, () => { console.log('Express MCP processor operational on port 3000'); });

Deployment Strategies

express-mcp-handler provides three distinct processing strategies tailored to various operational requirements:

Persistent Context Handling (statefulHandler)

Utilize statefulHandler to forge and maintain continuous contexts (sessions) between the client and server, vital for multi-turn AI workflows:

ts import express from 'express'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { statefulHandler } from 'express-mcp-handler'; import { randomUUID } from 'node:crypto';

const app = express(); app.use(express.json());

// Instantiate the core MCP server engine const server = new McpServer({ name: 'my-server', version: '1.0.0', });

// Setup operational parameters const handlerConfiguration = { sessionIdGenerator: randomUUID, // Utility to fabricate unique session identifiers onSessionInitialized: (sessionId: string) => { console.log(Session established: ${sessionId}); // Logic for pre-allocating resources per session }, onSessionClosed: (sessionId: string) => { console.log(Session terminated: ${sessionId}); // Resource deallocation and cleanup routines }, onError: (error: Error, sessionId?: string) => { console.error(Transaction failure in session ${sessionId}:, error); // Integration with error logging/alerting systems } };

// Register handlers for various HTTP verbs app.post('/mcp', statefulHandler(server, handlerConfiguration)); app.get('/mcp', statefulHandler(server, handlerConfiguration)); app.delete('/mcp', statefulHandler(server, handlerConfiguration));

app.listen(3000, () => { console.log('Express Persistent MCP engine active on port 3000'); });

Key behaviors of the stateful processor:

  • Originates a new session context if no mcp-session-id header is present.
  • Echoes a mcp-session-id header in responses for subsequent message correlation.
  • Manages the bidirectional flow over Server-Sent Events (SSE).
  • Implements automatic garbage collection for inactive session resources.

Non-Persistent Context Handling (statelessHandler)

Choose statelessHandler for transactional interactions demanding absolute isolation, highly suitable for serverless functions or simple, discrete queries:

ts import express from 'express'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { statelessHandler } from 'express-mcp-handler';

const app = express(); app.use(express.json());

// A factory providing a distinct McpServer instance for every HTTP invocation const serverFactory = () => new McpServer({ name: 'stateless-mcp-server', version: '1.0.0', });

// Configuration focusing purely on error interception const settings = { onError: (error: Error) => { console.error('Critical MCP error:', error); // Implement external metric tracking here } };

app.post('/mcp', statelessHandler(serverFactory, settings));

app.listen(3000, () => { console.log('Express Stateless MCP node active on port 3000'); });

Every stateless transaction adheres to:

  • Instantiating a fresh transport and server entity.
  • Guaranteeing zero context leakage between transactions.
  • Optimal deployment choice for ephemeral environments.

Real-Time Streaming (sseHandlers)

Use sseHandlers to establish dedicated communication conduits using Server-Sent Events (SSE) for MCP operations, perfect for streaming responses:

ts import express from 'express'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { sseHandlers } from 'express-mcp-handler';

const app = express(); app.use(express.json());

// Factory to provision an McpServer instance per persistent SSE channel const serverFactory = () => new McpServer({ name: 'sse-mcp-server', version: '1.0.0', });

// Configure the SSE transport handlers const handlers = sseHandlers(serverFactory, { onError: (error: Error, sessionId?: string) => { console.error([SSE Stream][${sessionId || 'unidentified'}], error); }, onClose: (sessionId: string) => { console.log([SSE Stream] Connection drain: ${sessionId}); // Necessary cleanup for session resources }, });

// Assign handlers to distinct paths app.get('/sse', handlers.getHandler); // Stream establishment app.post('/messages', handlers.postHandler); // Message input over the stream

app.listen(3002, () => { console.log('Express MCP Real-Time Stream server operational on port 3002'); });

SSE handlers expose dual endpoints:

  • GET /sse: Initializes the persistent SSE connection and returns the session identifier.
  • POST /messages: Transmits MCP payloads using the session ID supplied via the query parameters.

Programmatic Interface Reference

statefulHandler

ts function statefulHandler( server: McpServer, options: { sessionIdGenerator: () => string; onSessionInitialized?: (sessionId: string) => void; onSessionClosed?: (sessionId: string) => void; onError?: (error: Error, sessionId?: string) => void; onInvalidSession?: (req: express.Request) => void; } ): express.RequestHandler;

Argument Type Rationale
server McpServer The core protocol processing engine instance.
options.sessionIdGenerator () => string Mandatory routine for generating session keys.
options.onSessionInitialized (sessionId: string) => void Optional: Hook executed immediately upon session creation.
options.onSessionClosed (sessionId: string) => void Optional: Hook executed when session resources are reclaimed.
options.onError (error: Error, sessionId?: string) => void Optional: Centralized error logging hook.
options.onInvalidSession (req: express.Request) => void Optional: Invoked if an incoming request references a non-existent session.

statelessHandler

ts function statelessHandler( serverFactory: () => McpServer, options?: { sessionIdGenerator?: () => string; onClose?: (req: express.Request, res: express.Response) => void; onError?: (error: Error) => void; } ): express.RequestHandler;

Argument Type Rationale
serverFactory () => McpServer A callable function that instantiates a new server object per request.
options.sessionIdGenerator () => string Optional: Overrides default session ID generation logic for the transport layer.
options.onClose (req: express.Request, res: express.Response) => void Optional: Executed when the HTTP transaction concludes.
options.onError (error: Error) => void Optional: Handler for processing exceptions during stateless execution.

sseHandlers

ts function sseHandlers( serverFactory: ServerFactory, options: SSEHandlerOptions ): { getHandler: express.RequestHandler; postHandler: express.RequestHandler; };

Argument Type Rationale
serverFactory ServerFactory The factory function responsible for providing isolated McpServer instances per SSE connection.
options.onError (error: Error, sessionId?: string) => void Optional: Error interception for the SSE transport, logging both the issue and the associated session ID.
options.onClose (sessionId: string) => void Optional: Notification handler triggered upon the disconnection of an established SSE session, receiving the sessionId.

Exception Management

Custom error interception is uniformly available across all handler mechanisms via their respective configuration objects:

ts // Example configuration for stateful endpoint error interception const handlerConfiguration = { // ... other settings onError: (error: Error, sessionId?: string) => { console.error(Contextual failure in session ${sessionId}:, error); // Forward error telemetry to an external observability platform Sentry.captureException(error, { extra: { sessionId } }); } };

TypeScript Interoperability

Since the package is fundamentally built with TypeScript, consumers benefit from full type inference and static analysis:

ts import { statefulHandler, StatefulHandlerOptions } from 'express-mcp-handler'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';

// Strongly typed configuration object const typedConfiguration: StatefulHandlerOptions = { sessionIdGenerator: () => String(Date.now()), onError: (exception, id) => { // The IDE understands the exact types for exception and id console.error(Error processing session ${id}:, exception); } };

const engine = new McpServer({ name: 'typed-server', version: '1.0.0', });

// Type-checked middleware mounting app.post('/mcp', statefulHandler(engine, typedConfiguration));

Development Guidelines

To contribute code or enhancements:

bash git clone https://github.com/jhgaylor/express-mcp-handler.git cd express-mcp-handler npm install npm run build npm test

Test Integrity

The project prioritizes comprehensive automated testing, enforced via CI pipelines.

Testing is executed using Jest, with coverage metrics reported via Codecov, ensuring sustained quality assurance across all feature branches.

Continuous Integration Flow

GitHub Actions orchestrates the CI/CD process. Each commit pushed to the main branch or submitted via a Pull Request will trigger:

  1. Code style and lint validation.
  2. Project compilation.
  3. Execution of the full test suite along with coverage reporting.
  4. Upload of coverage reports to Codecov.

The current CI status is visible via the badge at the document's apex or directly on the Actions dashboard.

Distributed under the terms of the MIT License.

Publishing Procedure

To release an update to the npm registry:

Authenticate with npm if necessary: bash npm login

Publish the package (this automatically invokes the prepublishOnly build script): bash npm publish

To increment the version, create a git tag, and push the changes: bash npm version patch # Or minor, major selector git push origin main --tags

Handler Functionality Summary

Processing Mode Typical Use Case State Persistence Real-Time Streaming
statelessHandler Simple, transactional calls Absent Absent
statefulHandler Multi-turn dialogues, context retention Present Supported
sseHandlers Continuous, server-initiated feeds Present Primary Focus

Debugging Tips

Missing mcp-session-id Header
Verify the client logic correctly captures and retransmits the session ID returned in the initial response header.

Transport Link Failure
Investigate network latency or ensure the consuming client implements robust handling for dropped SSE streams.

Revision History

Detailed chronological documentation of modifications resides in CHANGELOG.md.

WIKIPEDIA CONTEXT: XMLHttpRequest (XHR) represents a fundamental JavaScript interface enabling asynchronous HTTP requests between a client-side script and a remote server. This mechanism is central to the AJAX programming paradigm, moving past the legacy reliance on full page reloads for data retrieval or submission. Its inception dates back to 2000, originating within Microsoft Outlook development, leading to its first implementation in Internet Explorer 5.


== Historical Milestones == The foundational concept was pioneered by Microsoft developers around 2000. IE5 (1999) was the first browser to support this functionality, albeit using proprietary ActiveXObject constructors (Msxml2.XMLHTTP or Microsoft.XMLHTTP). By the release of Internet Explorer 7 (2006), the standardized XMLHttpRequest identifier achieved universal adoption across major browsers, including Mozilla's Gecko (2002), Safari 1.2 (2004), and Opera 8.0 (2005).

=== Standardization Evolution === The W3C formalized the specification, publishing its first Working Draft on April 5, 2006. Level 2 specifications, introduced February 25, 2008, enhanced XHR capabilities by adding support for progress monitoring, enabling cross-origin data fetching, and facilitating raw byte stream processing. In late 2011, the Level 2 features were merged back into the primary document. Development responsibility transitioned to WHATWG near the end of 2012, where it is currently maintained as a living standard described using Web IDL.

== Operational Flow == Utilizing XMLHttpRequest typically involves a sequence of distinct programming calls:

  1. Instantiation: Creation of an XMLHttpRequest object instance via its constructor.
  2. Configuration (open): Invoking .open() to define the request method (GET, POST, etc.), target Uniform Resource Identifier (URI), and whether the operation should execute synchronously or asynchronously.
  3. Event Binding (Async): For asynchronous calls, setting up event handlers (e.g., onreadystatechange) to process status changes.
  4. Transmission (send): Dispatching the request to the server using the .send() method, optionally including a payload body.
  5. Response Processing: Monitoring state transitions. Upon reaching state 4 (the 'done' state), the retrieved data is typically accessible in the responseText property.

Beyond these core steps, XHR offers extensive control over request formatting (custom headers), data uploading, parsing the server response (e.g., automatic JSON deserialization), and implementing timeouts or abort mechanisms.

See Also

`