Skip to content

feat: Add custom context support for MCP transports #819

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

grimmerk
Copy link
Contributor

This PR adds support for passing custom context data from transport implementations to MCP handlers (tools, resources, prompts). This enables server implementations to inject application-specific context like tenant ID, user info, feature flags, etc. without relying on closures or global state.

Motivation and Context

Currently, MCP server implementations have no standard way to pass request-specific context (e.g., authentication info, tenant data) from the transport layer to tool/resource/prompt handlers. This limitation forces developers to use workarounds like closures or session-based context maps.

This PR solves this problem by:

  • Adding a customContext field to MessageExtraInfo
  • Passing it through RequestHandlerExtra to all handlers
  • Providing a setCustomContext() method on all transport implementations

Implementation Details

Core Changes:

  • types.ts: Added optional customContext?: Record<string, unknown> to MessageExtraInfo
  • shared/protocol.ts: Added customContext to RequestHandlerExtra, passed through in _onrequest
  • shared/transport.ts: Added optional setCustomContext() method to Transport interface

Transport Implementations:

  • InMemoryTransport: Stores and merges custom context with message extra info
  • SSEServerTransport: Injects custom context in handleMessage()
  • StreamableHTTPServerTransport: Includes custom context in all onmessage() calls
  • StdioServerTransport: Passes custom context with processed messages

Usage Example:

// Set custom context on transport
const transport =new StreamableHTTPServerTransport({
  sessionIdGenerator: undefined,
  enableJsonResponse: true, 
});

transport.setCustomContext({
  tenantId: authContext.user?.tenantId,
  userId: authContext.user?.id,
  featureFlags: { betaMode: true }
});

// Connect transport to server
await server.connect(transport);

// Access in tool handler
server.tool('search', async ({ query }, extra) => {
  const tenantId = extra.customContext?.tenantId;
  const userId = extra.customContext?.userId;
  // Use context for tenant-specific operations
});

How Has This Been Tested?

  • ✅ Unit test added for InMemoryTransport custom context propagation
  • ✅ Manually tested with SSE and Streamable HTTP transports in a real application
  • ✅ All existing tests continue to pass
  • ✅ Verified the same implementation pattern is applied consistently across all transports

Breaking Changes

None. All changes are backwards compatible:

  • customContext is an optional field
  • setCustomContext() is an optional method
  • Existing code will continue to work without modifications

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

This feature was developed to address the need for multi-tenant MCP server implementations where request-specific context needs to be available throughout the handler chain. The implementation follows existing patterns in the codebase (similar to how authInfo and requestInfo are handled) and maintains full backwards compatibility.

Implementation Notes:

  • All transport implementations follow the same pattern for consistency
  • Testing focused on InMemoryTransport as it covers the core functionality
  • Manual testing confirmed the feature works correctly with SSE and Streamable HTTP in production scenarios

Future Enhancements:

  • Consider adding TypeScript generics for type-safe custom context (would require broader changes)
  • Add integration tests for all transport types
  • Update documentation with custom context examples

- Add customContext field to MessageExtraInfo interface
- Pass customContext through RequestHandlerExtra to handlers
- Update InMemoryTransport to support custom context
- Add unit test to verify custom context propagation
- Enable transport implementations to inject arbitrary context data

This allows MCP server implementations to access custom context
(e.g., tenant ID, user info, feature flags) passed from the
transport layer to tool/resource/prompt handlers.
@grimmerk grimmerk requested a review from a team as a code owner July 29, 2025 10:00
@grimmerk grimmerk requested a review from ihrpr July 29, 2025 10:00
@dr3s
Copy link

dr3s commented Jul 31, 2025

One thing I had also addressed in a similar way was dependency injection. Is this PR intending to limit the context to data or support request locals generally that can be used for db connections etc...?

@grimmerk
Copy link
Contributor Author

grimmerk commented Jul 31, 2025

One thing I had also addressed in a similar way was dependency injection. Is this PR intending to limit the context to data or support request locals generally that can be used for db connections etc...?

@dr3s The current implementation passes an optional JS object with a Record<string, unknown> type as the custom context, allowing it to be used as a data context or for other JS objects (as property value), such as a DB connection object.

@dr3s
Copy link

dr3s commented Jul 31, 2025

@grimmerk the reason I asked is because I had tried using RequestHandlerExtra in this way but switched back to closures because it seems some code expected it to just be data. Do you think there's any concern using it for request scoped dependencies?

I tried using a generics approach to improve typesafe access to dependencies but was challenged by support in the sdk and RequestHandlerExtra

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.

2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy