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:
- Maintains its own Redis state for session persistence
- Can handle requests independently
- Shares the same codebase and configuration
- Can be scaled horizontally based on demand
-
Redis serves as the central state store, providing:
- Session state persistence across server restarts
- OAuth token storage and management
- Shared state between server instances
- Enables stateless server instances
-
MCP Authorization Server (highlighted in pink) manages all OAuth-related operations:
- Implements OAuth 2.1 with PKCE for secure authentication
- Handles token issuance and refresh
- Manages consent flows
- 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:
- Change the import statement
- Set the
REDIS_URLenvironment variable (required for production) - 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:
- LLM Client initiates a request to an MCP server
- Server responds with a 401 Unauthorized and redirect link
- User logs in to the OAuth provider and grants requested scopes
- Server returns an auth code to the client
- Client exchanges the code for access + refresh tokens
- Tokens are used for subsequent requests
- MCP server calls the third-party service
MCPToolKit supports two deployment models for the authorization server:
-
Embedded Authorization Server
- MCP server acts as both Identity Provider and Relying Party
- Handles login, consent, and token issuance directly
- Manages token lifetimes, refresh logic, and revocation
- Best for standalone applications
-
External Authorization Server
- MCP server acts as a Relying Party
- Delegates OAuth flow to external services (e.g., Stytch)
- Focuses on tool-level access control
- Best for integrating with existing identity infrastructure
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:
- Organization-Level Visibility: View all connected apps authorized across your organization
- Granular Permissions: See which members have granted access and which scopes they've authorized
- Access Management: Revoke access for specific users or apps at any time
- User-Friendly Consent: Present RBAC permissions in logical groupings
- Implied Permissions: Users can only give an app the same permissions they have
- Human-in-the-Loop: Require human approval for high-risk actions
@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.)
- Python 3.9+
- Redis instance (for session state persistence)
- OAuth provider credentials (if using delegated auth)
Same as the MCP Python SDK.