The Jupyter MCP Server supports dual-mode operation:
- MCP_SERVER Mode (Standalone) - Connects to remote Jupyter servers via HTTP/WebSocket
- JUPYTER_SERVER Mode (Extension) - Runs embedded in Jupyter Server with direct API access
Both modes share the same tool implementations, with automatic backend selection based on configuration.
┌─────────────────────────────────────────────────────────────────┐
│ MCP Client │
│ (Claude Desktop, VS Code, Cursor, etc.) │
└────────────┬────────────────────────────────────┬───────────────┘
│ │
│ stdio/SSE │ HTTP/SSE
│ │
┌────────▼────────────┐ ┌───────────▼──────────────┐
│ MCP_SERVER Mode │ │ JUPYTER_SERVER Mode │
│ (Standalone) │ │ (Extension) │
│ │ │ │
│ CLI Layer │ │ Extension Handlers │
│ (CLI.py) │ │ (handlers.py) │
└──────────┬──────────┘ └──────────┬───────────────┘
│ │
│ Configuration │ Configuration
│ │
┌──────────▼──────────┐ ┌──────────▼──────────────┐
│ Server Layer │ │ Extension Context │
│ (server.py) │ │ (context.py) │
│ │ │ │
│ - FastMCP Server │ │ - ServerApp Access │
│ - Tool Wrappers │ │ - Manager Access │
│ - Error Handling │ │ - Backend Selection │
└──────────┬──────────┘ └──────────┬──────────────┘
│ │
│ Tool Delegation │ Tool Delegation
│ │
┌──────▼────────────────────────────────▼──────┐
│ Tool Implementation Layer │
│ (jupyter_mcp_server/tools/) │
│ │
│ 14 Tools in 3 Categories: │
│ • Server Management (2) │
│ • Multi-Notebook Management (5) │
│ • Cell Operations (7) │
│ │
│ Each tool implements: │
│ - Dual-mode execution logic │
│ - Backend abstraction │
│ - Error handling and recovery │
└──────────┬───────────────────────┬───────────┘
│ │
│ Mode Selection │ Backend Selection
│ │
┌────────▼────────┐ ┌────────▼────────┐
│ Remote Backend │ │ Local Backend │
│ │ │ │
│ - HTTP Clients │ │ - Direct API │
│ - WebSocket │ │ - Zero Overhead │
│ - Client Libs │ │ - YDoc Support │
└────────┬────────┘ └────────┬────────┘
│ │
│ HTTP/WS │ Direct Python API
│ │
┌──────▼──────────┐ ┌───────▼────────┐
│ Remote Jupyter │ │ Local Jupyter │
│ Server │ │ Server │
└─────────────────┘ └────────────────┘
Command-Line Interface - Primary entry point for users and MCP clients:
Key Features:
- Configuration Management: Handles all startup configuration via command-line options and environment variables
- Transport Selection: Supports both
stdio(for direct MCP client integration) andstreamable-http(for HTTP-based clients) - Auto-Enrollment: Automatically connects to specified notebooks on startup
- Provider Support: Supports both
jupyteranddatalayerproviders - URL Resolution: Intelligent URL and token resolution with fallback mechanisms
Integration:
- Calls
server.pyfunctions to initialize the MCP server - Passes configuration to
ServerContextfor mode detection - Handles kernel startup and notebook enrollment lifecycle
Backend Abstraction - Unified interface for notebook and kernel operations:
LocalBackend - Complete implementation using local Jupyter Server APIs:
- Uses
serverapp.contents_managerfor file operations - Uses
serverapp.kernel_managerfor kernel operations - Direct Python API calls with minimal overhead
- Supports both file-based and YDoc collaborative editing
RemoteBackend - Placeholder implementation for HTTP/WebSocket access:
- Designed for
jupyter_server_client,jupyter_kernel_client,jupyter_nbmodel_client - Maintains 100% backward compatibility with existing MCP_SERVER mode
- Currently marked as "Not Implemented" - to be refactored from server.py
Multiple Context Managers:
MCP Server Context (server_context.py::ServerContext):
- Singleton managing server mode for standalone MCP_SERVER mode
- Provides HTTP clients for remote Jupyter server access
- Mode detection based on configuration
Extension Context (jupyter_extension/context.py::ServerContext):
- Singleton managing server mode for JUPYTER_SERVER extension mode
- Provides direct access to serverapp managers (contents_manager, kernel_manager)
- Handles configuration from Jupyter extension traits
Mode Detection:
- JUPYTER_SERVER: When running as extension, serverapp available
- MCP_SERVER: When running standalone, connects via HTTP
FastMCP Integration - Core MCP protocol implementation:
# Global MCP server instance with CORS support
mcp = FastMCPWithCORS(name="Jupyter MCP Server", json_response=False, stateless_http=True)
notebook_manager = NotebookManager()
server_context = ServerContext.get_instance()
# Tool registration and execution
@mcp.tool()
async def list_files(path: str = "", max_depth: int = 1, ...) -> str:
"""List files and directories in Jupyter server filesystem"""
return await safe_notebook_operation(
lambda: ListFilesTool().execute(
mode=server_context.mode,
server_client=server_context.server_client,
contents_manager=server_context.contents_manager,
path=path,
max_depth=max_depth,
...
)
)Key Responsibilities:
- Tool Registration: All 14 MCP tools are registered as FastMCP decorators
- Mode Detection: Automatically detects and initializes appropriate server mode
- Error Handling: Provides
safe_notebook_operation()wrapper with retry logic - Resource Management: Manages notebook connections and kernel lifecycle
- Protocol Bridge: Translates between MCP protocol and internal tool implementations
Transport Support:
- stdio: Direct communication with MCP clients via standard input/output
- streamable-http: HTTP-based communication with SSE (Server-Sent Events) support
- CORS Middleware: Enables cross-origin requests for web-based MCP clients
Built-in Tool Implementations - Complete set of Jupyter operations:
# Tool Categories and Examples
# Server Management (2 tools)
class ListFilesTool(BaseTool): # File system exploration
class ListKernelsTool(BaseTool): # Kernel management
# Multi-Notebook Management (5 tools)
class UseNotebookTool(BaseTool): # Connect/create notebooks
class ListNotebooksTool(BaseTool): # List managed notebooks
class RestartNotebookTool(BaseTool): # Restart kernels
class UnuseNotebookTool(BaseTool): # Disconnect notebooks
class ReadNotebookTool(BaseTool): # Read notebook content
# Cell Operations (7 tools)
class InsertCellTool(BaseTool): # Insert new cells
class DeleteCellTool(BaseTool): # Delete cells
class OverwriteCellSourceTool(BaseTool): # Modify cell content
class ExecuteCellTool(BaseTool): # Execute cells with streaming
class ReadCellTool(BaseTool): # Read individual cells
class ExecuteCodeTool(BaseTool): # Execute arbitrary code
class InsertExecuteCodeCellTool(BaseTool): # Combined insert+executeImplementation Architecture:
- BaseTool Abstract Class: Defines
execute()method signature with dual-mode support - ServerMode Enum: Distinguishes between
MCP_SERVERandJUPYTER_SERVERmodes - Dual-Mode Logic: Each tool implements both local and remote execution paths
- Backend Integration: Tools automatically select appropriate backend based on mode
Tool Categories:
- Server Management: File system and kernel introspection
- Multi-Notebook Management: Notebook lifecycle and connection management
- Cell Operations: Fine-grained cell manipulation and execution
Dynamic Tool Registry (get_registered_tools()):
- Queries FastMCP's
list_tools()to get all registered tools - Returns tool metadata (name, description, parameters, inputSchema)
- Used by Jupyter extension to expose tools without hardcoding
- Supports both FastMCP tools and jupyter-mcp-tools integration
Extension App (extension.py::JupyterMCPServerExtensionApp):
class JupyterMCPServerExtensionApp(ExtensionApp):
name = "jupyter_mcp_server"
# Configuration traits
document_url = Unicode("local", config=True)
runtime_url = Unicode("local", config=True)
document_id = Unicode("notebook.ipynb", config=True)
def initialize_settings(self):
# Store config in Tornado settings
# Initialize ServerContext with JUPYTER_SERVER modeHandlers (handlers.py):
MCPHealthHandler: GET /mcp/healthzMCPToolsListHandler: GET /mcp/tools/list (usesget_registered_tools())MCPToolsCallHandler: POST /mcp/tools/callMCPSSEHandler: SSE endpoint for MCP protocol
Extension Context (context.py::ServerContext):
class ServerContext:
_serverapp: Optional[Any] = None
_context_type: str = "unknown"
def update(self, context_type: str, serverapp: Any):
"""Called by extension to register serverapp."""
def is_local_document(self) -> bool:
"""Check if document operations use local access."""
def get_contents_manager(self):
"""Get local contents_manager from serverapp."""Purpose: Manages notebook connections and kernel lifecycle.
Key Features:
- Tracks managed notebooks with kernel associations
- Supports both local (JUPYTER_SERVER) and remote (MCP_SERVER) modes
- Provides
NotebookConnectioncontext manager for Y.js document access
Local vs Remote:
- Local mode: Notebooks tracked with
is_local=True, no WebSocket connections - Remote mode: Establishes WebSocket connections via
NbModelClient
class NotebookManager:
def add_notebook(self, name, kernel, server_url="local", ...):
"""Add notebook with mode detection (local vs remote)."""
def get_current_connection(self):
"""Get WebSocket connection (MCP_SERVER mode only)."""Pre/Post Hook System - Observability and extensibility layer:
class HookEvent(str, Enum):
BEFORE_TOOL_CALL = "before_tool_call"
AFTER_TOOL_CALL = "after_tool_call"
BEFORE_EXECUTE = "before_execute"
AFTER_EXECUTE = "after_execute"
KERNEL_LIFECYCLE = "kernel_lifecycle"Key Components:
- HookRegistry: Singleton that manages handler registration and event dispatch
- HookHandler Protocol: Interface for custom handlers with
propagate_errorsflag @with_hooksDecorator: Applied to all tool functions inserver.pyto fireBEFORE_TOOL_CALL/AFTER_TOOL_CALLevents- Context Correlation: Before/after event pairs share a context dict for handler state
Built-in OTel Handler (otel_hook.py):
- Emits OpenTelemetry spans for tool calls, code execution, and kernel lifecycle
- Uses
FileSpanExporterto write spans as JSONL - Activated via
--otel-fileCLI arg,JUPYTER_MCP_OTEL_FILEenv var, or Jupyter traitlet - Non-propagating (
propagate_errors = False) — never disrupts tool execution
Integration Points:
server.py: All tools decorated with@with_hooks; kernel lifecycle events fired on use/restart/unuseexecute_cell_tool.py,execute_code_tool.py: FireBEFORE_EXECUTE/AFTER_EXECUTEaround kernel executionutils.py: Fires execution hooks in shared utility functions
Start Command:
jupyter-mcp-server start \
--transport streamable-http \
--document-url http://localhost:8888 \
--runtime-url http://localhost:8888 \
--document-token MY_TOKEN \
--runtime-token MY_TOKEN \
--port 4040Behavior:
- ServerContext initialized with
mode=ServerMode.MCP_SERVER - Tools use HTTP clients for remote Jupyter server access
- Notebook connections use
NbModelClientfor WebSocket (Y.js documents) - Uses RemoteBackend (placeholder implementation)
Start Command:
jupyter server \
--JupyterMCPServerExtensionApp.document_url=local \
--JupyterMCPServerExtensionApp.runtime_url=local \
--JupyterMCPServerExtensionApp.document_id=notebook.ipynbConfiguration File (jupyter_server_config.py):
c.ServerApp.jpserver_extensions = {"jupyter_mcp_server": True}
c.JupyterMCPServerExtensionApp.document_url = "local"
c.JupyterMCPServerExtensionApp.runtime_url = "local"Backend Selection:
- LocalBackend: Used when
document_url="local"orruntime_url="local"- Direct access to
serverapp.contents_manager,serverapp.kernel_manager - No network overhead, maximum performance
- Supports both file-based and YDoc collaborative editing
- Direct access to
- RemoteBackend: Used when connecting to remote Jupyter servers
- HTTP/WebSocket access via client libraries
- Placeholder implementation (to be completed)
Behavior:
- Extension auto-enabled (via
jupyter-config/file) - ServerContext updated with
mode=ServerMode.JUPYTER_SERVER - Tools automatically select LocalBackend for optimal performance
- Cell reading tools parse notebook JSON from file system or YDoc
MCP Client
→ POST /mcp/tools/call {"tool_name": "list_notebooks"}
→ MCPSSEHandler (or MCPToolsCallHandler)
→ FastMCP calls @mcp.tool() wrapper
→ ListNotebooksTool().execute(
mode=JUPYTER_SERVER,
notebook_manager=notebook_manager
)
→ notebook_manager.list_all_notebooks()
→ Returns managed notebooks from memory
← TSV-formatted table
← Tool result
← JSON-RPC response
← SSE message
← Tool result displayed
MCP Client
→ POST /mcp/tools/call {"tool_name": "read_cell", "arguments": {"cell_index": 0}}
→ MCPSSEHandler (or MCPToolsCallHandler)
→ FastMCP calls @mcp.tool() wrapper
→ ReadCellTool().execute(
mode=JUPYTER_SERVER,
contents_manager=serverapp.contents_manager,
notebook_manager=notebook_manager
)
→ LocalBackend.get_notebook_content(notebook_path)
→ contents_manager.get(notebook_path, content=True, type='notebook')
→ Direct file system access (no HTTP)
← Notebook JSON content
→ Parse cells and format response
← Cell information with metadata and source
← Tool result
← JSON-RPC response
← SSE message
← Cell content displayed
MCP Client
→ POST /mcp/tools/call {"tool_name": "execute_cell", "arguments": {"cell_index": 0}}
→ FastMCP calls @mcp.tool() wrapper
→ ExecuteCellTool().execute(
mode=MCP_SERVER,
notebook_manager=notebook_manager
)
→ notebook_manager.get_current_connection()
→ NbModelClient establishes WebSocket to Y.js document
→ Access collaborative Y.js document
→ Execute code via kernel connection
→ HTTP/WebSocket to remote kernel
→ Real-time execution with progress updates
← Execution outputs with rich formatting
← Tool result
← Response
← Outputs displayed
1. CLI startup (CLI.py)
↓
2. Configuration parsing and validation
↓
3. ServerContext initialization with mode detection
↓
4. FastMCP server initialization (server.py)
↓
5. Tool instance creation (14 tool implementations)
↓
6. @mcp.tool() wrapper registration
↓
7. FastMCP internal tool registry
↓
8. Dynamic tool discovery via get_registered_tools()
↓
9. Extension handlers expose tools via /mcp/tools/list
↓
10. MCP clients discover and invoke tools
jupyter_mcp_server/
├── __init__.py # Package initialization
├── __main__.py # Module entry point (imports CLI)
├── __version__.py # Version information (0.17.1)
│
├── CLI.py # 🏠 Command-Line Interface (Primary Entry Point)
│ ├── Command parsing and validation
│ ├── Environment variable handling
│ ├── Transport selection (stdio/streamable-http)
│ ├── Provider support (jupyter/datalayer)
│ ├── Auto-enrollment of notebooks
│ └── Server lifecycle management
│
├── server.py # 🔧 FastMCP Server Layer
│ ├── MCP protocol implementation
│ ├── Tool registration (14 @mcp.tool decorators)
│ ├── Error handling with safe_notebook_operation()
│ ├── Resource management and cleanup
│ ├── Dynamic tool registry (get_registered_tools())
│ └── Transport support (stdio + streamable-http)
│
├── tools/ # 🛠️ Built-in Tool Implementations
│ ├── __init__.py # Exports BaseTool, ServerMode
│ ├── _base.py # Abstract base class for all tools
│ │
│ # Server Management Tools (2)
│ ├── list_files_tool.py # File system exploration
│ ├── list_kernels_tool.py # Kernel introspection
│ │
│ # Multi-Notebook Management Tools (5)
│ ├── use_notebook_tool.py # Connect/create notebooks
│ ├── list_notebooks_tool.py # List managed notebooks
│ ├── restart_notebook_tool.py # Restart kernels
│ ├── unuse_notebook_tool.py # Disconnect notebooks
│ ├── read_notebook_tool.py # Read notebook content
│ │
│ # Cell Operation Tools (7)
│ ├── read_cell_tool.py # Read individual cells
│ ├── insert_cell_tool.py # Insert new cells
│ ├── delete_cell_tool.py # Delete cells
│ ├── overwrite_cell_source_tool.py # Modify cell content
│ ├── execute_cell_tool.py # Execute cells with streaming
│ ├── execute_code_tool.py # Execute arbitrary code
│ └── insert_execute_code_cell # Combined insert+execute (inline in server.py)
│
├── config.py # ⚙️ Configuration Management
│ ├── Singleton config object (JupyterMCPConfig)
│ ├── Environment variable parsing
│ ├── URL and token resolution
│ └── Provider-specific settings
│
├── notebook_manager.py # 📚 Notebook Lifecycle Management
│ ├── Multi-notebook support
│ ├── Kernel connection management
│ ├── Context managers for resources
│ └── Dual-mode operation (local/remote)
│
├── server_context.py # 🎯 Server Context (MCP_SERVER mode)
│ ├── Mode detection and initialization
│ ├── HTTP client management
│ └── Configuration state management
│
├── utils.py # 🧰 Utility Functions
│ ├── Execution utilities (local/remote)
│ ├── Output processing and formatting
│ ├── Kernel management helpers
│ └── YDoc integration support
│
├── hooks.py # 🔍 Hook System
│ ├── HookEvent enum (5 event types)
│ ├── HookHandler protocol
│ ├── HookRegistry singleton
│ └── @with_hooks decorator
│
├── otel_hook.py # 📡 OpenTelemetry Integration
│ ├── OTelHookHandler (span emission)
│ ├── FileSpanExporter (JSONL output)
│ └── maybe_register_otel() auto-setup
│
├── enroll.py # 🔗 Auto-Enrollment System
│ ├── Automatic notebook connection
│ ├── Kernel startup and management
│ └── Configuration-based initialization
│
├── models.py # 📋 Data Models
│ ├── Pydantic models for API
│ ├── Cell and Notebook structures
│ └── Configuration validation
│
└── jupyter_extension/ # 🔌 Jupyter Server Extension
├── extension.py # Jupyter extension app
├── handlers.py # HTTP request handlers
├── context.py # Extension context manager
├── backends/ # Backend implementations
│ ├── base.py # Backend interface
│ ├── local_backend.py # Local API (Complete)
│ └── remote_backend.py # Remote API (Placeholder)
└── protocol/ # Protocol implementation
└── messages.py # MCP message models
Version: 0.2.0 Last Updated: October 2025 Status: Complete implementation with dual-mode architecture and backend abstraction