universal-notebook-mcp
# universal-notebook-mcp
Run Jupyter notebooks from any AI editor. Gives your AI assistant full access to read, edit, and execute `.ipynb` files with live kernel output — without needing to open JupyterLab.
Works in **Antigravity, Cursor, Windsurf, Claude Desktop, Claude Code**, and any MCP-compatible tool.
Works on **Windows, macOS, and Linux**.
---
## Quick start
### 1. Install Python 3.10+
Skip this step if you already have Python 3.10 or later (`python --version` to check).
<details>
<summary><b>Windows</b></summary>
Download from [python.org](https://www.python.org/downloads/) and run the installer.
Make sure to check **"Add Python to PATH"** during setup.
Or with winget:
```powershell
winget install Python.Python.3.11
```
</details>
<details>
<summary><b>macOS</b></summary>
```bash
brew install python@3.11
```
Or download from [python.org](https://www.python.org/downloads/).
</details>
<details>
<summary><b>Linux</b></summary>
```bash
# Debian / Ubuntu
sudo apt install python3.11 python3.11-pip
# Fedora / RHEL
sudo dnf install python3.11
# Arch
sudo pacman -S python
```
</details>
---
### 2. Install the package
```bash
pip install universal-notebook-mcp
```
> On macOS/Linux, if `pip` maps to Python 3.9, use `pip3.11` instead.
> On Windows, `pip` from the Python 3.11 installer works directly.
Verify it installed:
```bash
nb-mcp --help
```
---
### 3. Add to your editor
Pick your editor below and paste the config. Replace the path with the folder that contains your notebooks.
> ⚠️ Use the **real absolute path** — MCP clients pass arguments as literal strings and do not expand editor variables like `${workspaceFolder}`.
>
> **Windows paths**: use forward slashes or escape backslashes: `C:/Users/you/notebooks` or `C:\\Users\\you\\notebooks`
<details>
<summary><b>Antigravity</b></summary>
Create `.antigravity/mcp.json` in your project folder:
```json
{
"mcpServers": {
"notebook": {
"command": "nb-mcp",
"args": ["--workspace-root", "/absolute/path/to/notebooks"]
}
}
}
```
Or go to `Settings → MCP Servers` and add the same block.
</details>
<details>
<summary><b>Cursor</b></summary>
`~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per-project):
```json
{
"mcpServers": {
"notebook": {
"command": "nb-mcp",
"args": ["--workspace-root", "/absolute/path/to/notebooks"]
}
}
}
```
</details>
<details>
<summary><b>Windsurf</b></summary>
`~/.codeium/windsurf/mcp_config.json`:
```json
{
"mcpServers": {
"notebook": {
"command": "nb-mcp",
"args": ["--workspace-root", "/absolute/path/to/notebooks"]
}
}
}
```
</details>
<details>
<summary><b>Claude Desktop</b></summary>
Config file location:
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"notebook": {
"command": "nb-mcp",
"args": ["--workspace-root", "/absolute/path/to/notebooks"]
}
}
}
```
</details>
<details>
<summary><b>Claude Code</b></summary>
```bash
claude mcp add notebook -- nb-mcp --workspace-root /absolute/path/to/notebooks
```
</details>
### 4. Reload your editor and go
Your AI can now work with notebooks. Try:
> *"List the cells in my notebook"*
> *"Run cell 3 and show me the output"*
> *"Fix the error in cell 5 and re-run it"*
---
## What it can do
| | Tool | What it does |
|---|---|---|
| 📖 | `notebook_list_cells` | See all cells (type, tags, first line) |
| 📖 | `notebook_read_cell` | Read full source + saved outputs of a cell |
| 📖 | `notebook_read_cell_output` | Read just the outputs (stream, result, error) |
| 📖 | `notebook_read_metadata` | Read notebook metadata (kernel, language, etc.) |
| 📖 | `notebook_list_stages` | List all pipeline stage tags in the notebook |
| ✏️ | `notebook_edit_cell` | Edit a cell's source |
| ✏️ | `notebook_insert_cell` | Insert a new cell at any position |
| ✏️ | `notebook_delete_cell` | Delete a cell |
| ✏️ | `notebook_edit_cell_metadata` | Add or update cell tags and metadata |
| ✏️ | `notebook_edit_metadata` | Update notebook-level metadata |
| ▶️ | `notebook_run_cell` | Execute one cell and get its output |
| ▶️ | `notebook_run_range` | Execute a range of cells |
| ▶️ | `notebook_run_all` | Execute all cells |
| ▶️ | `notebook_run_pipeline` | Execute all cells tagged with a stage name |
| 🔧 | `notebook_restart_kernel` | Clear kernel state (variables, imports) |
| 🔧 | `notebook_list_kernels` | List all installed kernel environments |
| 🔧 | `notebook_list_active_kernels` | See which notebooks have a live kernel |
**Kernel state persists across calls** — variables and imports from one cell are available in the next, just like a normal Jupyter session.
**Edits are checkpointed** — every edit creates a timestamped backup (`.checkpoint_<timestamp>.ipynb`) before writing, so you can always roll back.
---
## Troubleshooting
### `nb-mcp: command not found` (or `'nb-mcp' is not recognized` on Windows)
The install directory isn't on your PATH. Find where pip installed it:
```bash
# macOS / Linux
python3 -m site --user-scripts # or: which nb-mcp after activating your venv
# Windows (PowerShell)
python -c "import sys; print(sys.prefix + r'\Scripts')"
```
Then either use the full path in your MCP config:
```json
"command": "C:\\Users\\you\\AppData\\Local\\Programs\\Python\\Python311\\Scripts\\nb-mcp.exe"
```
Or add the Scripts/bin directory to your PATH permanently.
---
### `ModuleNotFoundError` when running a cell
The kernel doesn't have your packages installed. Register your environment:
```bash
pip install ipykernel
python -m ipykernel install --user --name myenv --display-name "My Env"
```
Then restart the kernel via `notebook_restart_kernel` or ask your AI to switch kernels.
List available kernels:
```bash
jupyter kernelspec list
```
---
### Windows: path format in MCP config
Both of these work:
```json
"C:/Users/you/notebooks" ✓ forward slashes
"C:\\Users\\you\\notebooks" ✓ escaped backslashes
```
Avoid raw backslashes — they break JSON:
```json
"C:\Users\you\notebooks" ✗ invalid JSON
```
---
<details>
<summary><b>For developers — running tests, contributing</b></summary>
```bash
git clone https://github.com/your-org/universal-notebook-mcp.git
cd universal-notebook-mcp
# macOS / Linux
pip3.11 install -e ".[dev]"
# Windows (PowerShell)
python -m pip install -e ".[dev]"
```
Run the tests:
```bash
python -m pytest # all tests
python -m pytest -m "not integration" # unit tests only (no kernel needed)
python -m pytest -m integration -v # integration tests (needs ipykernel)
```
Or use `make` targets on macOS/Linux:
```bash
make test # unit only
make test-all # unit + integration
make coverage # coverage report
make lint # ruff linter
```
**Project layout:**
```
src/universal_notebook_mcp/
server.py ← MCP tool surface (17 tools, FastMCP, stdio)
notebook_adapter.py ← nbformat cell CRUD + checkpoint backups
kernel_session.py ← jupyter_client async kernel lifecycle
notebook_runner.py ← cell execution + output capture
tests/
conftest.py ← shared fixtures (mocked kernel, workspace)
fixtures/ ← simple.ipynb, pipeline.ipynb, error.ipynb
test_*.py ← 125 tests (116 unit + 9 integration)
```
</details>
---
## Security
All notebook paths are sandboxed to `--workspace-root`. Paths that escape it (e.g. `../secret.ipynb`) or that aren't `.ipynb` files are rejected with an error.
## License
MIT
TDQS
Scored across 17 tools
Each tool targets a distinct operation on notebooks, cells, metadata, or kernels. There is no overlap, and descriptions clearly differentiate purposes (e.g., list_cells vs read_cell, run_all vs run_pipeline).
All tools follow the `notebook_verb_noun` pattern consistently. Verbs like delete, edit, insert, list, read, restart, run are used uniformly, making it easy to predict tool behavior from its name.
With 17 tools, the set covers cell manipulation, metadata editing, execution modes, and kernel management without being excessive. Each tool serves a clear purpose, and the count is well-suited to the domain.
Core workflows like CRUD for cells, multiple execution modes, and kernel management are covered. However, missing notebook creation (e.g., `notebook_create`) and explicit save/export operations are minor gaps that agents may need to work around.