-
Notifications
You must be signed in to change notification settings - Fork 780
How to use MCP client in a multi-user application? #243
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
Comments
What Ive found is you need a new instance of MCPServer per user connecting. Docs are misleading here: https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#http-with-sse FastMcp also does this as a "FastMCPSession": https://github.com/punkpeye/fastmcp/blob/main/src/FastMCP.ts#L424 But to your point, with Streamable HTTP transport in the spec, how long do we need to support SSE |
Thanks for the reference to FastMCP! I was not aware of it. I see two related issues here:
|
i have write a package to deploy mcp server for multi-user.
|
…roxy_config Add MCP proxy address config support, better error messages, redesigned Config panel
Just going to leave this here: server.py from fastmcp import FastMCP, Context
import logging
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("mcp-server")
# Create MCP server
mcp = FastMCP("Echo Context Server")
@mcp.tool()
async def echo_context(message: str, ctx: Context) -> dict:
"""
Echo back the message and user context from request headers.
Args:
message: A message to echo back
Returns:
A dictionary with the message and user context
"""
# Get HTTP request
request = ctx.get_http_request()
# Extract user context from headers
user_id = request.headers.get("X-User-ID", "unknown")
auth_token = request.headers.get("Authorization", "").replace("Bearer ", "")
logger.info(f"Request from user: {user_id}")
# Return the message and user context
return {
"message": message,
"user_context": {
"user_id": user_id,
"auth_token": auth_token[:5] + "..." if auth_token else "none"
}
}
if __name__ == "__main__":
logger.info("Starting MCP server on port 3000...")
# Use the streamable-http transport with correct parameters
mcp.run(transport="streamable-http", host="0.0.0.0", port=3000) client.py: import asyncio
import json
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
async def list_tools():
"""List all available tools and their schemas"""
print("\n--- Listing Available Tools ---")
# Create a transport without any specific user context
transport = StreamableHttpTransport(
url="http://localhost:3000/mcp"
)
# Create client with the configured transport
client = Client(transport)
try:
# Use the client to list all tools
async with client:
tools = await client.list_tools()
print("Available tools:")
for tool in tools:
print(f"\nTool: {tool.name}")
print(f"Description: {tool.description}")
print("Parameters schema:")
# Pretty print the input schema
print(json.dumps(tool.inputSchema, indent=2))
except Exception as e:
print(f"Error: {e}")
async def call_with_user(user_id, auth_token, message):
"""Call echo_context tool with specific user context in headers"""
print(f"\n--- Calling as user: {user_id} ---")
# Set up headers with user context
headers = {
"X-User-ID": user_id,
"Authorization": f"Bearer {auth_token}",
"Accept": "application/json, text/event-stream"
}
# Skip Authorization header if token is empty
if not auth_token:
headers.pop("Authorization")
# Create a StreamableHttpTransport with headers
transport = StreamableHttpTransport(
url="http://localhost:3000/mcp",
headers=headers
)
# Create client with the configured transport
client = Client(transport)
try:
# Use the client to call the tool
async with client:
result = await client.call_tool("echo_context", {"message": message})
print(f"Result: {result}")
except Exception as e:
print(f"Error: {e}")
async def main():
# First, list all tools to inspect their schemas
await list_tools()
# Then call with different user contexts
await call_with_user("alice", "alice-token-123", "Hello from Alice!")
await call_with_user("bob", "bob-token-456", "Hello from Bob!")
await call_with_user("anonymous", "", "Hello without auth!")
if __name__ == "__main__":
asyncio.run(main()) Using the request headers and ctx object you can inject the user's id or name or whatever you want on behalf of whom you are executing a toolcall. |
I'm working on a web based chat application. Unlike desktop apps (Claude Desktop, Cursor IDE, ...), at each moment multiple users might be working with my app. What is the recommended way of using MCP client in this scenario?
The text was updated successfully, but these errors were encountered: