McpToolKit
A serverless, multi-tenant implementation of MCP servers that runs on Vercel with fluid compute mode, allowing multiple users to connect to the same endpoint while maintaining session state through Redis.
A serverless, multi-tenant implementation of MCP servers that runs on Vercel with fluid compute mode, allowing multiple users to connect to the same endpoint while maintaining session state through Redis.
The standard FastMCP framework faces significant challenges in production environments: - State Management: Traditional FastMCP servers maintain state in memory, making horizontal scaling difficult - Serverless Limitations: Serverless environments require stateless architectures, which FastMCP wasn't designed for - Multi-tenant Support: Running multiple tenants on the same server requires complex session management
Managing authentication for MCP servers is complex: - Tool-Level Auth: Users should only authenticate when a tool requires it - Third-Party Integration: Supporting OAuth for services like Notion, Slack, etc. requires complex token management - Security: Managing multiple authentication flows while maintaining security is challenging
MCPToolKit provides a production-ready framework that solves these problems while maintaining full compatibility with FastMCP. Here's how it works:
graph TD
A[LLM Client<br/>e.g. Claude, ChatGPT, Cursor] --> B[Load Balancer]
B --> C[MCP Server Instance 1<br/>with Redis State]
B --> D[MCP Server Instance 2<br/>with Redis State]
B --> E[MCP Server Instance N<br/>with Redis State]
C --> F[Redis<br/>Session State & OAuth Tokens]
D --> F
E --> F
C --> H[MCP Authorization Server<br/>OAuth 2.1 & PKCE]
D --> H
E --> H
H --> G[OAuth Providers<br/>Notion, Slack, etc.]
style H fill:#f9f,stroke:#333,stroke-width:2px
This architecture diagram illustrates how MCPToolKit enables production-ready MCP servers:
LLM Clients (e.g., Claude, ChatGPT, Cursor) initiate requests to the MCP server. These clients can be any application that needs to interact with MCP tools.
Load Balancer distributes incoming requests across multiple MCP server instances, enabling horizontal scaling and high availability.
MCP Server Instances (1 through N) handle tool execution and resource access. Each instance:
Can be scaled horizontally based on demand
Redis serves as the central state store, providing:
Enables stateless server instances
MCP Authorization Server (highlighted in pink) manages all OAuth-related operations:
Centralizes OAuth logic for all server instances
OAuth Providers (e.g., Notion, Slack) are the third-party services that users can authenticate with. The authorization server manages these connections securely.
This architecture enables: - True horizontal scaling through stateless server instances - Centralized OAuth management - High availability through multiple server instances - Secure token management - Consistent user experience across sessions
Migrating from FastMCP to MCPToolKit is straightforward. Here's how to update your existing FastMCP server:
# Before (FastMCP)
- from mcp.server.fastmcp import FastMCP
-
- # Create an MCP server
- mcp = FastMCP("Demo")
# After (MCPToolKit)
+ from mcptoolkit import MCPToolKit
+ import os
+
+ # Create a production-ready MCP server
+ mcp = MCPToolKit(
+ name="Demo",
+ redis_url=os.environ["REDIS_URL"] # Required: Set REDIS_URL in your environment
+ )
# Your tools and resources remain exactly the same
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"
The migration requires just a few simple changes:
1. Change the import statement
2. Set the REDIS_URL
environment variable (required for production)
3. That's it! All your existing tools, resources, and prompts continue to work exactly as before
For local development, you can set the environment variable:
export REDIS_URL="redis://localhost:6379/0"
For serverless deployments, you'll also need to update your deployment configuration:
# Before (FastMCP)
- # api/index.py
- from mcp.server.fastmcp import FastMCP
-
- mcp = FastMCP("Demo")
- app = mcp.create_fastapi_app()
# After (MCPToolKit)
+ # api/index.py
+ from mcptoolkit.vercel import create_vercel_app
+ import os
+
+ app = create_vercel_app(
+ name="Demo",
+ redis_url=os.environ["REDIS_URL"] # Required: Set REDIS_URL in your environment
+ )
Key Features: - Redis-Backed State: Session state persists across server restarts and function invocations - Serverless Ready: Designed for Vercel, AWS Lambda, and other serverless platforms - Horizontal Scaling: State persistence enables true horizontal scaling - Multi-tenant Support: Multiple users can connect to the same endpoint with isolated sessions
from mcptoolkit import MCPToolKit, requires_auth
from mcptoolkit.auth.providers import NotionProvider, SlackProvider
# Define default scopes for each provider
default_notion_scopes = [
"read:database",
"write:page",
"read:page"
]
default_slack_scopes = [
"channels:read",
"chat:write",
"reactions:write"
]
server = MCPToolKit(name="Auth Server")
@server.tool()
@requires_auth(provider=NotionProvider(
scopes=default_notion_scopes,
consent_required=True # Require explicit user consent
))
def notion_search(query: str, ctx: Context) -> str:
# Access authenticated Notion client with specific scopes
notion = ctx.get_oauth_client("notion")
return notion.search(query)
@server.tool()
@requires_auth(provider=SlackProvider(
scopes=default_slack_scopes,
consent_required=True
))
def slack_message(channel: str, message: str, ctx: Context) -> str:
# Access authenticated Slack client with specific scopes
slack = ctx.get_oauth_client("slack")
return slack.post_message(channel, message)
Key Features: - Lazy Authentication: Users only authenticate when a tool requires it - Provider Support: Built-in support for common providers (Notion, Slack, etc.) - Token Management: Automatic token refresh and storage - Security: Secure token storage and transmission - Granular Scopes: Fine-grained control over OAuth permissions - Consent Management: User-friendly consent screens with logical permission groupings - Human-in-the-Loop: Optional approval requirements for high-risk actions
MCPToolKit implements a secure OAuth 2.1 flow with PKCE:
MCPToolKit supports two deployment models for the authorization server:
Best for standalone applications
External Authorization Server
Both models support: - OAuth 2.1 with PKCE - Dynamic Client Registration - Authorization Server Metadata (RFC 8414) - Custom scopes based on Resources/Actions - End user consent management - Granular scope definitions per provider - Organization-level visibility and control - Implied permissions (users can only grant permissions they have)
MCPToolKit provides comprehensive consent and access management:
@server.tool()
@requires_auth(provider=NotionProvider(
scopes=["delete:database"],
human_approval_required=True # Require explicit human approval
))
def delete_database(database_id: str, ctx: Context) -> str:
# This action will require explicit human approval
notion = ctx.get_oauth_client("notion")
return notion.delete_database(database_id)
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
spec:
replicas: 3
template:
spec:
containers:
- name: mcp-server
image: your-mcp-server
env:
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: redis-credentials
key: url
# api/index.py
from mcptoolkit.vercel import create_vercel_app
app = create_vercel_app(
name="Serverless MCP",
redis_url=os.environ.get("REDIS_URL")
)
Install MCPToolKit:
pip install mcp-python-sdk
Create your server:
from mcptoolkit import MCPToolKit, requires_auth
server = MCPToolKit(
name="My Production Server",
redis_url="redis://localhost:6379/0"
)
@server.tool()
def public_tool() -> str:
return "This tool doesn't require auth"
@server.tool()
@requires_auth(provider="notion")
def notion_tool() -> str:
return "This tool requires Notion auth"
Deploy to your preferred platform (Kubernetes, Vercel, etc.)
Same as the MCP Python SDK.