Multi-User Deployments
Jupyter MCP Server can be deployed in multi-user environments where multiple people access Jupyter notebooks concurrently. This guide explains the architecture patterns, deployment options, and best practices for multi-user scenarios.
Understanding Multi-User Challengesβ
The Concurrency Problemβ
As described in issue #181, when multiple users share a single MCP server instance connected to a shared JupyterLab, they can interfere with each other's work:
Example Scenario:
- User 1's agent activates
user1notebook.ipynb - Simultaneously, User 2's agent activates
user2notebook.ipynb - User 1's agent writes code to the currently active notebook (now
user2notebook.ipynb) - Result: User 1's code appears in User 2's notebook
This happens because:
- MCP server maintains a single "active notebook" state
- Multiple agents share the same MCP server instance
- Notebook operations are context-dependent (operating on "current" notebook)
Deployment Architecturesβ
There are three primary architecture patterns for multi-user deployments:
1. One MCP Server Per User (Recommended)β
Best for: Most multi-user scenarios, especially with JupyterHub
Each user gets their own isolated MCP server instance, preventing any cross-user interference.
βββββββββββββββββββββββββββββββββββββββββββββββ
β JupyterHub / Multi-User β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β User 1 β
β βββββββββββββββ βββββββββββββββββββ β
β β MCP Client βββββββΆβ MCP Server β β
β β (Agent) β β (Extension) β β
β βββββββββββββββ β β β β
β β JupyterLab β β
β β Single-User β β
β βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β User 2 β
β βββββββββββββββ βββββββββββββββββββ β
β β MCP Client βββββββΆβ MCP Server β β
β β (Agent) β β (Extension) β β
β βββββββββββββββ β β β β
β β JupyterLab β β
β β Single-User β β
β βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββ
Advantages:
- β Complete isolation between users
- β No state conflicts
- β Scales naturally with JupyterHub
- β Leverages existing JupyterHub authentication
Implementation: See JupyterHub Deployment
2. Stateless Tool Operationsβ
Best for: Shared environments where one-MCP-per-user is not feasible
Make all notebook operations explicitly reference the target notebook by path.
Current Status:
Currently, some MCP tools maintain "active notebook" state. Work is ongoing to make operations fully stateless by requiring explicit notebook paths in all tool calls.
Track progress: Issue #181
Future API (Planned):
{
"tool": "execute_cell",
"arguments": {
"notebook": "user1/analysis.ipynb",
"cell_index": 0
}
}
3. Session-Based Isolationβ
Best for: Advanced scenarios with custom session management
Use unique session identifiers to isolate user contexts within a shared MCP server.
This approach is not yet implemented in Jupyter MCP Server. Follow development discussions in issue #181.
JupyterHub Deploymentβ
JupyterHub is the recommended platform for multi-user Jupyter deployments with MCP.
Architectureβ
JupyterHub naturally provides user isolation:
- Each user gets a dedicated single-user Jupyter server
- MCP server runs as an extension inside each single-user server
- No shared state between users
Setup Stepsβ
1. Install in Single-User Environmentβ
Configure your JupyterHub spawner to install the required packages:
# jupyterhub_config.py
c.Spawner.environment = {
'JUPYTERHUB_ALLOW_TOKEN_IN_URL': '1'
}
# Install packages in the single-user image or environment
# Example for DockerSpawner:
c.DockerSpawner.image = 'your-custom-image:latest'
Single-User Environment Requirements:
# Dockerfile for single-user image
FROM jupyter/minimal-notebook:latest
RUN pip install "jupyter-mcp-server>=0.15.0" \
"jupyterlab==4.4.1" \
"jupyter-collaboration==4.0.2" \
"ipykernel"
RUN pip uninstall -y pycrdt datalayer_pycrdt && \
pip install datalayer_pycrdt==0.12.17
2. Configure MCP Extensionβ
The MCP server extension loads automatically when the single-user server starts.
Verify Installation:
# Inside single-user server
jupyter server extension list
# Should show: jupyter_mcp_server enabled
3. User Access Configurationβ
Each user needs:
- A JupyterHub account
- An API token with
access:serversscope - MCP client configuration pointing to their unique URL
Per-User MCP Client Configuration:
{
"mcpServers": {
"jupyter": {
"command": "npx",
"args": ["mcp-remote", "https://hub.example.com/user/alice/mcp"],
"env": {
"JUPYTERHUB_API_TOKEN": "alice-api-token"
}
}
}
}
Creating User Tokensβ
Option 1: JupyterHub Admin UI
- Login as admin
- Navigate to Token management
- Create token for user with
access:serversscope
Option 2: JupyterHub API
# Create token for specific user
jupyterhub token alice --note "MCP Client Token" --scope access:servers
Option 3: User Self-Service
# Allow users to create their own tokens
c.JupyterHub.load_roles = [
{
"name": "user",
"scopes": ["self", "access:servers"],
}
]
User Isolationβ
JupyterHub ensures:
- File System Isolation: Each user has separate home directory
- Process Isolation: Separate kernel processes per user
- Network Isolation: Each single-user server on different port/container
- Authentication: JupyterHub manages user identity and access
Shared JupyterLab (Not Recommended)β
Sharing a single JupyterLab instance among multiple users with a shared MCP server causes the concurrency problems described in issue #181.
This deployment pattern is NOT recommended.
Why It Causes Problemsβ
β PROBLEMATIC SETUP:
User 1 Agent ββ
ββββΆ Single MCP Server βββΆ Shared JupyterLab
User 2 Agent ββ (shared state) (all users)
Problem: Both users' agents share the same "active notebook" state
If You Must Shareβ
If you absolutely must use a shared JupyterLab:
- Use Explicit Notebook Paths: Always specify full notebook paths
- Implement Access Controls: Use file system permissions to isolate user directories
- Monitor for Conflicts: Log and alert on concurrent access
- Consider Alternatives: Strongly recommend migrating to JupyterHub
Datalayer Hosted Platformβ
For enterprise deployments, consider Datalayer which provides:
- Built-in Multi-User Support: Isolated notebook environments per user
- Managed Authentication: SSO, OAuth2, SAML integration
- Resource Management: CPU/memory limits per user
- Collaboration Features: Real-time notebook co-editing
- Audit Logging: Track all user actions
Configurationβ
{
"mcpServers": {
"datalayer": {
"command": "uvx",
"args": ["jupyter-mcp-server@latest"],
"env": {
"PROVIDER": "datalayer",
"DATALAYER_URL": "https://app.datalayer.ai",
"DATALAYER_TOKEN": "your-user-token"
}
}
}
}
Best Practicesβ
For Multi-User Deploymentsβ
1. Use JupyterHubβ
β Do:
- Deploy with JupyterHub for proper user isolation
- Run MCP server as extension in each single-user server
- Use JupyterHub's authentication and authorization
β Don't:
- Share a single JupyterLab instance among multiple users
- Use a single MCP server instance for multiple users
- Rely on application-level isolation instead of OS-level
2. Token Managementβ
β Do:
- Create unique API tokens for each user
- Use minimal token scopes (
access:serversonly) - Implement token expiration policies
- Provide users with self-service token creation
β Don't:
- Share tokens between users
- Use admin-scoped tokens for users
- Store tokens in shared configuration files
3. Resource Managementβ
# jupyterhub_config.py
# Limit resources per user
c.Spawner.cpu_limit = 2
c.Spawner.mem_limit = '4G'
c.Spawner.cpu_guarantee = 1
c.Spawner.mem_guarantee = '1G'
# Limit concurrent servers
c.JupyterHub.concurrent_spawn_limit = 10
# Cull idle servers
c.JupyterHub.services = [
{
'name': 'idle-culler',
'command': [
'python3', '-m', 'jupyterhub_idle_culler',
'--timeout=3600'
]
}
]
4. Monitoring and Loggingβ
β Do:
- Enable audit logging for all MCP operations
- Monitor resource usage per user
- Set up alerts for unusual activity
- Track concurrent user counts
Example Logging Configuration:
# jupyterhub_config.py
import logging
c.JupyterHub.log_level = 'INFO'
c.Application.log_format = '%(asctime)s [%(name)s] %(levelname)s: %(message)s'
# Log to file
c.JupyterHub.extra_log_file = '/var/log/jupyterhub/jupyterhub.log'
Scaling Considerationsβ
Small Teams (< 10 users)β
Recommended Setup:
- Single JupyterHub server
- Local storage for notebooks
- Simple spawner (LocalProcessSpawner or DockerSpawner)
Medium Organizations (10-100 users)β
Recommended Setup:
- Kubernetes-based JupyterHub (Zero to JupyterHub)
- Shared network storage (NFS, EFS)
- KubeSpawner for pod management
- Load balancing for hub
Large Enterprises (100+ users)β
Recommended Setup:
- Multi-hub deployment with load balancing
- Distributed storage (Ceph, GlusterFS)
- Auto-scaling for user servers
- Integration with enterprise SSO
- Consider Datalayer platform
Troubleshooting Multi-User Issuesβ
Users Seeing Each Other's Notebooksβ
Symptom: User A sees notebooks created by User B
Causes:
- Shared JupyterLab instance (see issue #181)
- Incorrect file permissions
- Shared home directory
Solutions:
- β Switch to JupyterHub with per-user isolation
- β
Verify file system permissions:
ls -la ~ - β Check spawner configuration for home directory isolation
Concurrent Execution Conflictsβ
Symptom: Code from one user appears in another user's notebook
Cause: Shared MCP server with stateful "active notebook"
Solutions:
- β Deploy one MCP server per user (JupyterHub extension mode)
- β Wait for stateless tool operations (track issue #181)
Authentication Failures in Multi-User Environmentβ
Symptom: Users can't connect to their MCP server
Causes:
- Missing or incorrect API tokens
- Token lacking required scopes
JUPYTERHUB_ALLOW_TOKEN_IN_URLnot set
Solutions:
- β
Verify token has
access:serversscope - β
Check
JUPYTERHUB_ALLOW_TOKEN_IN_URL=1in spawner environment - β
Test token with curl:
curl -H "Authorization: token TOKEN" https://hub.example.com/user/alice/api
Migration Guideβ
From Shared JupyterLab to JupyterHubβ
-
Setup JupyterHub
pip install jupyterhub
jupyterhub --generate-config -
Create User Accounts
# Add system users
sudo adduser alice
sudo adduser bob -
Install MCP Server in Single-User Environment
- Create a conda/virtual environment for single-user servers
- Install required packages (see JupyterHub setup)
-
Configure Spawner
# jupyterhub_config.py
c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner'
c.DockerSpawner.image = 'your-mcp-enabled-image:latest' -
Migrate User Notebooks
# Copy notebooks to user directories
cp -r /shared/notebooks/alice/* /home/alice/
chown -R alice:alice /home/alice/ -
Update MCP Client Configurations
- Provide each user with their unique MCP endpoint URL
- Generate individual API tokens