Skip to content

Conversation

@jerome3o-anthropic
Copy link
Member

Summary

Add a middleware pattern that allows transforming JSON-RPC messages before sending and after receiving. This provides a clean way to extend protocol messages (e.g., adding custom capabilities to initialize requests) without needing to subclass or override protocol methods.

This is the TypeScript SDK equivalent of modelcontextprotocol/python-sdk#1911 for the Python SDK.

Motivation

When clients need to add custom fields to MCP messages (like protocol extensions or custom capabilities), they currently have to override protocol methods and duplicate internal logic. This is error-prone and creates maintenance burden.

A middleware approach provides a clean escape hatch that:

  • Doesn't require subclassing
  • Works with any message type (requests, responses, notifications)
  • Supports both sync and async transformations
  • Can be composed (multiple middleware functions)

Example Usage

import { Client, isJSONRPCRequest } from '@modelcontextprotocol/sdk/client';

const client = new Client({
    name: 'my-client',
    version: '1.0.0'
}, {
    sendMiddleware: [
        (message) => {
            if (isJSONRPCRequest(message) && message.method === 'initialize') {
                // Add custom capabilities
                return {
                    ...message,
                    params: {
                        ...message.params,
                        capabilities: {
                            ...message.params?.capabilities,
                            extensions: {
                                'io.modelcontextprotocol/ui': { mimeTypes: ['text/html'] }
                            }
                        }
                    }
                };
            }
            return message;
        }
    ]
});

Implementation

  • Added MessageMiddleware type: (message: JSONRPCMessageLike) => JSONRPCMessageLike | Promise<JSONRPCMessageLike>
  • Added JSONRPCMessageLike type alias for all JSON-RPC message types
  • Added sendMiddleware and receiveMiddleware to ProtocolOptions
  • Middleware is applied in:
    • connect() for incoming messages (receive middleware)
    • request() for outgoing requests (send middleware)
    • notification() for outgoing notifications (send middleware)
  • Both sync and async middleware functions are supported

Add a middleware pattern that allows transforming JSON-RPC messages before
sending and after receiving. This provides a clean way to extend protocol
messages (e.g., adding custom capabilities to initialize requests) without
needing to subclass or override protocol methods.

Middleware functions receive a JSON-RPC message and return a (possibly
transformed) message. Both sync and async middleware are supported.

Usage example:
    const client = new Client({
        name: 'my-client',
        version: '1.0.0'
    }, {
        sendMiddleware: [
            (message) => {
                // Transform outgoing message
                if (isJSONRPCRequest(message) && message.method === 'initialize') {
                    // Add custom capabilities
                }
                return message;
            }
        ],
        receiveMiddleware: [
            (message) => {
                // Transform incoming message
                return message;
            }
        ]
    });

Changes:
- Add MessageMiddleware type and JSONRPCMessageLike type alias
- Add sendMiddleware and receiveMiddleware to ProtocolOptions
- Apply middleware in connect() for incoming messages
- Apply middleware in request() and notification() for outgoing messages
@jerome3o-anthropic jerome3o-anthropic requested a review from a team as a code owner January 19, 2026 16:52
@changeset-bot
Copy link

changeset-bot bot commented Jan 19, 2026

⚠️ No Changeset found

Latest commit: d4f8dfa

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 19, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/client@1406

@modelcontextprotocol/server

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server@1406

@modelcontextprotocol/express

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/express@1406

@modelcontextprotocol/hono

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/hono@1406

@modelcontextprotocol/node

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/node@1406

commit: d4f8dfa

Only await middleware application when there's actually middleware
configured. This preserves synchronous behavior for existing tests
that depend on immediate message queuing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants