ipynb-mcp
README.md
# ipynb-mcp
An agent-agnostic MCP server that lets any AI agent (Claude Code, OpenAI Codex, etc.) drive a local **JupyterLab** session via the [Model Context Protocol](https://modelcontextprotocol.io).
## Getting started with an AI agent
After installation, use one of the following prompts to kick off a session.
**Fresh start (no server running):**
> Start a new notebook project using ipynb-mcp. Venv is at `~/lib/python/uv-osm`, notebook dir is `/path/to/notebooks`. Project goal: [one sentence description].
**Reconnect to an already-running JupyterLab:**
> Continue a notebook project using ipynb-mcp. JupyterLab is already running on port 8888 with token `[token]`. Notebook dir is `/path/to/notebooks`. Continue working on `[notebook.ipynb]`.
The token is printed to the terminal when JupyterLab starts — look for a line like:
```
http://localhost:8888/lab?token=abc123...
```
### Known limitation: browser reload required
When the agent writes or updates cells via the REST API, JupyterLab's browser tab does not auto-refresh. After the agent makes changes, reload the notebook manually with **File → Reload Notebook from Disk** to see the latest cells and outputs.
---
## How it works
```
┌─────────────────────┐ stdio (MCP) ┌───────────────────┐
│ AI agent │◄─────────────►│ ipynb-mcp │
│ (Claude Code / │ │ (this server) │
│ OpenAI Codex / │ │ │ │
│ any MCP client) │ │ Jupyter REST API │
└─────────────────────┘ │ + kernel WS │
│ │ │
│ JupyterLab │
│ (subprocess) │
└───────────────────┘
```
Unlike colab-mcp (which proxies tools from a remote browser extension), **ipynb-mcp talks directly to the Jupyter REST API and kernel WebSocket** — no browser extension required.
## Installation
This project uses the shared `uv-osm` virtual environment. Always activate it before running any commands.
### Prerequisites
```bash
source ~/lib/python/uv-osm/bin/activate && uv pip install jupyterlab
```
### Install ipynb-mcp
```bash
source ~/lib/python/uv-osm/bin/activate && cd ipynb-mcp && uv pip install -e .
```
> **Note:** Use `uv pip install` (not plain `pip install`). Never install packages globally.
## Configuring your agent
Use the full venv binary path in all agent configs so the server always runs inside the correct environment regardless of the shell's active venv.
### Claude Code
Register the server once, **from your project root directory** (not from inside the `ipynb-mcp` source folder — Claude Code scopes MCP servers to the directory where the command is run):
```bash
cd /path/to/your/project
claude mcp add ipynb-mcp -- /home/<user>/lib/python/uv-osm/bin/ipynb-mcp
```
Verify it registered correctly by running `/mcp` in the Claude Code prompt — you should see `ipynb-mcp · ✔ connected` in the server list. If the server doesn't appear, check that you ran `claude mcp add` from the right directory. The server starts on demand when a tool is called; no manual pre-launch needed.
Or add to `.claude/mcp.json` in your project:
```json
{
"mcpServers": {
"ipynb-mcp": {
"command": "/home/<user>/lib/python/uv-osm/bin/ipynb-mcp",
"args": []
}
}
}
```
Replace `/home/<user>` with your actual home directory (or use the `~` expansion if your client supports it).
### Generic `mcp.json` (OpenAI Codex, Cursor, Zed, etc.)
Most MCP-compatible clients read a configuration file. Create or extend `mcp.json`:
```json
{
"mcpServers": {
"ipynb-mcp": {
"command": "/home/<user>/lib/python/uv-osm/bin/ipynb-mcp",
"args": [],
"env": {
"IPYNB_MCP_NOTEBOOK_DIR": "/path/to/your/notebooks",
"IPYNB_MCP_PORT": "8888"
}
}
}
}
```
Using the full venv path ensures the server finds all its dependencies without needing the shell to be activated first.
### Environment variables
| Variable | Default | Description |
|---|---|---|
| `IPYNB_MCP_NOTEBOOK_DIR` | `$PWD` | Root directory for notebooks |
| `IPYNB_MCP_PORT` | `8888` | Port for JupyterLab |
## Available tools
| Tool | Description |
|---|---|
| `start_jupyter` | Launch JupyterLab and open the browser tab |
| `open_notebook` | Create or open a notebook; opens it in the browser |
| `get_cells` | List all cells (id, type, source, outputs) |
| `add_code_cell` | Insert a Python code cell |
| `add_text_cell` | Insert a Markdown cell |
| `update_cell` | Replace a cell's source (clears outputs) |
| `delete_cell` | Remove a cell |
| `move_cell` | Reorder a cell |
| `run_code_cell` | Execute a code cell on a live kernel |
## Typical agent workflow
```
agent → start_jupyter() # launches JupyterLab, opens browser
agent → open_notebook("work.ipynb")
agent → add_text_cell(path, "# My analysis")
agent → add_code_cell(path, "import pandas as pd\ndf = pd.read_csv('data.csv')")
agent → run_code_cell(path, cell_id) # executes, returns outputs
agent → get_cells(path) # inspect outputs
agent → update_cell(path, cell_id, "...fixed code...")
agent → run_code_cell(path, cell_id)
```
## Differences from colab-mcp
| | colab-mcp | ipynb-mcp |
|---|---|---|
| Runtime | Google Colab (remote) | Local JupyterLab |
| Architecture | Proxy → browser extension → Colab | Direct Jupyter REST + kernel WS |
| Browser extension | Required | Not required |
| Internet | Required | Optional (fully offline capable) |
| Agent compatibility | Any MCP client | Any MCP client |
| GPU / TPU | Via Colab (free tier limits) | Via local hardware |
| Persistence | Session-based | Standard filesystem |
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues