Skip to main content

No project description provided

Project description

Jupyter Server MCP Extension

PyPI version conda-forge version

A configurable MCP (Model Context Protocol) server extension for Jupyter Server that allows dynamic registration of Python functions as tools accessible to MCP clients from a running Jupyter Server.

https://github.com/user-attachments/assets/aa779b1c-a443-48d7-b3eb-13f27a4333b3

Overview

This extension provides a simplified, trait-based approach to exposing Jupyter functionality through the MCP protocol. It can dynamically load and register tools from various Python packages, making them available to AI assistants and other MCP clients.

Key Features

  • Simplified Architecture: Direct function registration without complex abstractions
  • Configurable Tool Loading: Register tools via string specifications (module:function)
  • Automatic Tool Discovery: Python packages can expose tools via entrypoints
  • Jupyter Integration: Seamless integration with Jupyter Server extension system
  • Streamable HTTP Transport: FastMCP-based HTTP server with proper MCP protocol support
  • Stdio Proxy: Stable jupyter-server-mcp-proxy / python -m jupyter_server_mcp.proxy entry point that auto-discovers the running Jupyter MCP server — client configuration stays the same even if the port changes, and it is launchable via uvx from outside the Jupyter environment
  • Multi-Instance Ready: Set mcp_port = 0 to ask the OS for a free ephemeral port, so multiple Jupyter servers can run in parallel; the stdio proxy auto-discovers whichever port was chosen
  • Traitlets Configuration: Full configuration support through Jupyter's traitlets system

Installation

Install jupyter-server-mcp into the same environment as Jupyter Server or JupyterLab.

pip

python -m pip install jupyter-server-mcp

conda / mamba / micromamba

conda install -c conda-forge jupyter-server-mcp
mamba install -c conda-forge jupyter-server-mcp
micromamba install -c conda-forge jupyter-server-mcp

pixi

In an existing Pixi workspace:

# If conda-forge is not already configured for the workspace
pixi project channel add conda-forge
pixi add jupyter-server-mcp

Quick Start

1. Basic Configuration

Create a jupyter_config.py file:

c = get_config()

# Basic MCP server settings
c.MCPExtensionApp.mcp_name = "My Jupyter MCP Server"

# The MCP server listens on port 3001 by default. Override it with:
# c.MCPExtensionApp.mcp_port = 8080
# Set to 0 to let the OS pick a free port — useful when running multiple
# Jupyter servers side by side. The stdio proxy (below) auto-discovers the
# chosen port, so clients do not need to be reconfigured.
# c.MCPExtensionApp.mcp_port = 0

# Register tools from existing packages
c.MCPExtensionApp.mcp_tools = [
    # Standard library tools
    "os:getcwd",
    "json:dumps",
    "time:time",
    
    # Jupyter AI Tools - Notebook operations  
    "jupyter_ai_tools.toolkits.notebook:read_notebook",
    "jupyter_ai_tools.toolkits.notebook:edit_cell",
    
    # JupyterLab Commands Toolkit
    "jupyterlab_commands_toolkit.tools:list_all_commands",
    "jupyterlab_commands_toolkit.tools:execute_command",
]

2. Start Jupyter Server

jupyter lab --config=jupyter_config.py

By default, the MCP server listens on port 3001. If that port is already in use — for example when a second Jupyter server is already running — startup fails with a clear error instead of silently choosing another port. To run multiple Jupyter servers side-by-side, set c.MCPExtensionApp.mcp_port = 0 so the OS assigns a free port. The stdio proxy (see below) auto-discovers whichever port was chosen, so client configuration does not need to change.

Any trait can also be set on the command line:

jupyter lab --MCPExtensionApp.mcp_port=8080

3. CLI MCP Client Configuration

There are two supported ways to wire an MCP client to this extension:

  1. Stdio proxy (recommended) — the client launches a small stdio proxy (jupyter-server-mcp-proxy or python -m jupyter_server_mcp.proxy), which auto-discovers the running Jupyter MCP server and bridges stdio to its HTTP endpoint. This keeps working unchanged when multiple Jupyter servers run side-by-side or when mcp_port = 0 picks a different port each run.
  2. Direct HTTP — point the client at http://localhost:3001/mcp (or whichever port you configured). Works well when you run a single Jupyter server on a stable port.

When multiple Jupyter servers are running on the same machine, the stdio proxy picks the one whose Jupyter root directory is the most specific ancestor of the MCP client's current working directory. If no server's root directory contains that working directory, or if several tie, the proxy refuses to guess and asks you to disambiguate with --url or by setting JUPYTER_SERVER_MCP_URL.

The list below is intentionally curated rather than exhaustive and focuses on terminal-based coding agents. For a broader, community-maintained directory of MCP-compatible clients, see the MCP client directory: https://modelcontextprotocol.io/clients.

Option A — Stdio proxy (recommended)

The proxy can be launched in two ways:

  • uvx — ideal for MCP clients that are not installed inside the same Python environment as Jupyter. uv installs jupyter-server-mcp into a cached, ephemeral environment and runs its jupyter-server-mcp-proxy console script. The runtime info file the extension writes is stored in the per-user Jupyter runtime directory, so auto-discovery works across environments.
  • python -m jupyter_server_mcp.proxy — use when the client is already running in an environment that has jupyter-server-mcp installed.

The examples below show the uvx form. Swap in the python -m form by replacing "command": "uvx", "args": ["--from", "jupyter-server-mcp", "jupyter-server-mcp-proxy"] with "command": "python", "args": ["-m", "jupyter_server_mcp.proxy"].

Claude Code

Add the following to .mcp.json:

{
  "mcpServers": {
    "jupyter-mcp": {
      "command": "uvx",
      "args": ["--from", "jupyter-server-mcp", "jupyter-server-mcp-proxy"]
    }
  }
}

Or use the claude CLI:

claude mcp add jupyter-mcp -- uvx --from jupyter-server-mcp jupyter-server-mcp-proxy

Codex

Use the codex CLI:

codex mcp add jupyter-mcp -- uvx --from jupyter-server-mcp jupyter-server-mcp-proxy

Or add the following to ~/.codex/config.toml:

[mcp_servers.jupyter-mcp]
command = "uvx"
args = ["--from", "jupyter-server-mcp", "jupyter-server-mcp-proxy"]

OpenCode

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "jupyter-mcp": {
      "type": "local",
      "command": ["uvx", "--from", "jupyter-server-mcp", "jupyter-server-mcp-proxy"],
      "enabled": true
    }
  }
}

Gemini CLI

{
  "mcpServers": {
    "jupyter-mcp": {
      "command": "uvx",
      "args": ["--from", "jupyter-server-mcp", "jupyter-server-mcp-proxy"]
    }
  }
}

Copilot CLI

{
  "mcpServers": {
    "jupyter-mcp": {
      "type": "local",
      "command": "uvx",
      "args": ["--from", "jupyter-server-mcp", "jupyter-server-mcp-proxy"],
      "tools": ["*"]
    }
  }
}

Pin to a specific version with --from jupyter-server-mcp==X.Y.Z when you need to match the server side exactly.

The proxy accepts a few optional arguments (append them to args):

  • --url URL — bypass auto-discovery and connect to an explicit MCP endpoint
  • --runtime-dir DIR — look in a specific Jupyter runtime directory
  • --cwd DIR — use a different directory when disambiguating between servers

JUPYTER_SERVER_MCP_URL is equivalent to --url and takes precedence over discovery when set.

Option B — Direct HTTP

The extension exposes a FastMCP streamable HTTP endpoint at http://localhost:3001/mcp by default. Override the port with c.MCPExtensionApp.mcp_port if you need a different one; replace 3001 below with whatever you chose. If a client asks for a transport type, pick HTTP or Streamable HTTP.

Note: Direct HTTP requires a fixed, known port. If you set c.MCPExtensionApp.mcp_port = 0 for multi-instance support, use the stdio proxy instead — the ephemeral port is not suitable as a stable URL.

OpenCode

Use opencode mcp add, or add the following to opencode.json or ~/.config/opencode/opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "jupyter-mcp": {
      "type": "remote",
      "url": "http://localhost:3001/mcp",
      "enabled": true
    }
  }
}

Mistral Vibe

Add the following to ./.vibe/config.toml or ~/.vibe/config.toml:

[[mcp_servers]]
name = "jupyter-mcp"
transport = "streamable-http"
url = "http://localhost:3001/mcp"

Claude Code

Add the following to .mcp.json:

{
  "mcpServers": {
    "jupyter-mcp": {
      "type": "http",
      "url": "http://localhost:3001/mcp"
    }
  }
}

Or use the claude CLI:

claude mcp add --transport http jupyter-mcp http://localhost:3001/mcp

Codex

Use the codex CLI:

codex mcp add jupyter-mcp --url http://localhost:3001/mcp

Or add the following to ~/.codex/config.toml:

[mcp_servers.jupyter-mcp]
url = "http://localhost:3001/mcp"

Gemini CLI

Add the following to .gemini/settings.json:

{
  "mcpServers": {
    "jupyter-mcp": {
      "httpUrl": "http://localhost:3001/mcp"
    }
  }
}

Copilot CLI

Use /mcp add in interactive mode, or add the following to ~/.copilot/mcp-config.json:

{
  "mcpServers": {
    "jupyter-mcp": {
      "type": "http",
      "url": "http://localhost:3001/mcp",
      "tools": ["*"]
    }
  }
}

Architecture

Core Components

MCPServer (jupyter_server_mcp.mcp_server.MCPServer)

A simplified LoggingConfigurable class that manages FastMCP integration:

from jupyter_server_mcp.mcp_server import MCPServer

# Create server
server = MCPServer(name="My Server", port=8080)

# Register functions
def my_tool(message: str) -> str:
    return f"Hello, {message}!"

server.register_tool(my_tool)

# Start server
await server.start_server()

Key Methods:

  • register_tool(func, name=None, description=None) - Register a Python function
  • register_tools(tools) - Register multiple functions (list or dict)
  • list_tools() - Get list of registered tools
  • start_server(host=None) - Start the HTTP MCP server

MCPExtensionApp (jupyter_server_mcp.extension.MCPExtensionApp)

Jupyter Server extension that manages the MCP server lifecycle:

Configuration Traits:

  • mcp_name - Server name (default: "Jupyter MCP Server")
  • mcp_port - Server port (default: 3001). Set to 0 to let the OS pick a free port — useful when running multiple servers side by side.
  • mcp_tools - List of tools to register (format: "module:function")
  • use_tool_discovery - Enable automatic tool discovery via entrypoints (default: True)

Tool Registration

Tools can be registered in two ways:

1. Manual Configuration

Specify tools directly in your Jupyter configuration using module:function format:

c.MCPExtensionApp.mcp_tools = [
    "os:getcwd",
    "jupyter_ai_tools.toolkits.notebook:read_notebook",
]

2. Automatic Discovery via Entrypoints

Python packages can expose tools automatically using the jupyter_server_mcp.tools entrypoint group.

In your package's pyproject.toml:

[project.entry-points."jupyter_server_mcp.tools"]
my_package_tools = "my_package.tools:TOOLS"

In my_package/tools.py:

# Option 1: Define as a list
TOOLS = [
    "my_package.operations:create_file",
    "my_package.operations:delete_file",
]

# Option 2: Define as a function
def get_tools():
    return [
        "my_package.operations:create_file",
        "my_package.operations:delete_file",
    ]

Tools from entrypoints are discovered automatically when the extension starts. To disable automatic discovery:

c.MCPExtensionApp.use_tool_discovery = False

Configuration Examples

Minimal Setup

c = get_config()

# The MCP server defaults to port 3001. Override it with:
# c.MCPExtensionApp.mcp_port = 8080
# Or set to 0 to let the OS pick a free port (requires the stdio proxy).

Full Configuration

c = get_config()

# MCP Server Configuration
c.MCPExtensionApp.mcp_name = "Advanced Jupyter MCP Server"
c.MCPExtensionApp.mcp_port = 8080
c.MCPExtensionApp.mcp_tools = [
    # File system operations (jupyter-ai-tools)
    "jupyter_ai_tools.toolkits.file_system:read",
    "jupyter_ai_tools.toolkits.file_system:write", 
    "jupyter_ai_tools.toolkits.file_system:edit",
    "jupyter_ai_tools.toolkits.file_system:ls",
    "jupyter_ai_tools.toolkits.file_system:glob",
    
    # Notebook operations (jupyter-ai-tools)
    "jupyter_ai_tools.toolkits.notebook:read_notebook",
    "jupyter_ai_tools.toolkits.notebook:edit_cell",
    "jupyter_ai_tools.toolkits.notebook:add_cell", 
    "jupyter_ai_tools.toolkits.notebook:delete_cell",
    "jupyter_ai_tools.toolkits.notebook:create_notebook",
    
    # Git operations (jupyter-ai-tools)
    "jupyter_ai_tools.toolkits.git:git_status",
    "jupyter_ai_tools.toolkits.git:git_add",
    "jupyter_ai_tools.toolkits.git:git_commit",
    "jupyter_ai_tools.toolkits.git:git_push",
    
    # JupyterLab operations (jupyterlab-commands-toolkit)
    "jupyterlab_commands_toolkit.tools:clear_all_outputs_in_notebook",
    "jupyterlab_commands_toolkit.tools:open_document",
    "jupyterlab_commands_toolkit.tools:open_markdown_file_in_preview_mode",
    "jupyterlab_commands_toolkit.tools:show_diff_of_current_notebook",
    
    # Utility functions  
    "os:getcwd",
    "json:dumps",
    "time:time",
    "platform:system",
]

Running Tests

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Run with coverage
pytest --cov=jupyter_server_mcp tests/

Project Structure

jupyter_server_mcp/
├── jupyter_server_mcp/
│   ├── __init__.py
│   ├── mcp_server.py      # Core MCP server implementation
│   ├── extension.py       # Jupyter Server extension
│   ├── proxy.py           # Stdio MCP proxy entrypoint (python -m jupyter_server_mcp.proxy)
│   └── runtime.py         # Runtime info-file helpers shared between the extension and the proxy
├── tests/
│   ├── test_mcp_server.py # MCPServer tests
│   ├── test_extension.py  # Extension tests
│   ├── test_proxy.py      # Stdio proxy tests
│   └── test_runtime.py    # Runtime info-file helper tests
├── demo/
│   ├── jupyter_config.py  # Example configuration
│   └── *.py              # Debug/diagnostic scripts
└── pyproject.toml         # Package configuration

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass: pytest tests/
  5. Submit a pull request

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jupyter_server_mcp-0.3.0a0.tar.gz (36.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jupyter_server_mcp-0.3.0a0-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file jupyter_server_mcp-0.3.0a0.tar.gz.

File metadata

  • Download URL: jupyter_server_mcp-0.3.0a0.tar.gz
  • Upload date:
  • Size: 36.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for jupyter_server_mcp-0.3.0a0.tar.gz
Algorithm Hash digest
SHA256 8552b6b9adc4c560f8d5bcd61538f4da93fde45ca62fdf93e64ea33dd116f3f0
MD5 9603189c667884682beba58bc00b38e3
BLAKE2b-256 83f3e19a0fb5b50beeb743a22fbd7c735c1e0f789c7611bdc543149c0168d8d4

See more details on using hashes here.

File details

Details for the file jupyter_server_mcp-0.3.0a0-py3-none-any.whl.

File metadata

File hashes

Hashes for jupyter_server_mcp-0.3.0a0-py3-none-any.whl
Algorithm Hash digest
SHA256 bcae93682eb7133c23db9794c1cc374334c5228f1885e8d53943bada4554ab3d
MD5 6ecbefee09d0d2a51670f9e8190918a3
BLAKE2b-256 e7039d518dfbdd8b885f2e20323f078c6adcc0a6a7275e609fcc3d3cc1e0cb4b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page