loading…
Search for a command to run...
loading…
Exposes VSCode Jupyter notebooks to MCP-compatible AI agents, enabling them to read, edit, and run cells against the same kernel.
Exposes VSCode Jupyter notebooks to MCP-compatible AI agents, enabling them to read, edit, and run cells against the same kernel.
A VS Code extension that exposes the Jupyter notebooks in your VSCode editor to MCP-compatible AI agents (e.g. Claude Code). The agent reads, edits, and runs cells against the same kernel VS Code is using.
Status: pre-alpha, single-developer project. Built fresh; not a fork.
| Tool | Description |
|---|---|
notebook_list_open |
List all open notebooks with URI, file name, cell count, and which is active |
notebook_open |
Open a .ipynb file (absolute path, file:// URI, or workspace-relative) so its cells become available |
notebook_list_cells |
List cells with index, kind, language, preview, execution state |
notebook_get_cell_content |
Full source of a cell |
notebook_get_cell_output |
Outputs of a cell (text, errors, images as base64) |
| Tool | Description |
|---|---|
notebook_insert_cell |
Insert a code or markdown cell at any position; optionally execute |
notebook_edit_cell |
Replace contents of an existing cell |
notebook_delete_cell |
Delete a cell by index |
| Tool | Description |
|---|---|
notebook_run_cell |
Execute an existing code cell and return outputs |
notebook_clear_cell_output |
Clear outputs of one cell |
notebook_clear_all_outputs |
Clear outputs of every cell |
| Tool | Description |
|---|---|
notebook_get_kernel_info |
Language, status, notebook URI |
notebook_select_kernel |
Attach a kernel by python_path (interpreter path — no controller id or prior VS Code selection needed), or by kernel_id, or pop the picker (see Kernel selection) |
All tools accept an optional notebook_uri (omitted → uses the active notebook editor) and response_format ("markdown" or "json").
npm install
npm run build
Then open this folder in VS Code and press F5 to launch an Extension Development Host with the extension loaded.{
"mcpServers": {
"notebook": {
"url": "http://127.0.0.1:49777/mcp"
}
}
}
.ipynb file in the Extension Development Host. Look for the 🪐 :49777 indicator in the status bar.| Setting | Default | Description |
|---|---|---|
notebook-mcp.port |
49777 |
Preferred port. Auto-increments if busy (up to +99). |
Notebook MCP: Restart ServerNotebook MCP: Show Server Info┌─────────────────────────────────────────────────────────┐
│ VS Code window │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Jupyter extension (ms-toolsai.jupyter) │ │
│ │ │ │
│ │ Notebook document ◄──► Kernel ──► Outputs │ │
│ └───────────────────────────────────────────────────┘ │
│ ▲ │
│ │ vscode.NotebookEdit, │
│ │ notebook.cell.execute, │
│ │ jupyter.kernels.getKernel │
│ │ │
│ ┌───────────────────────┴───────────────────────────┐ │
│ │ This extension │ │
│ │ │ │
│ │ HTTP server :49777 ──► MCP tools (13) │ │
│ │ (StreamableHTTPServerTransport) │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│ HTTP (MCP protocol)
▼
┌───────────────────────────────┐
│ AI agent │
│ (Claude Code, Cursor, etc.) │
└───────────────────────────────┘
vscode.NotebookEdit + vscode.WorkspaceEdit, preserving undo/redo.notebook.cell.execute and waits by polling cell.executionSummary.success for a boolean value (VS Code creates the summary as soon as execution starts but success only becomes a boolean when the kernel finishes).notebook_uri. Resolution: explicit URI → look up in vscode.workspace.notebookDocuments (or open it); otherwise fall back to vscode.window.activeNotebookEditor.The agent-friendly way to attach a kernel is to give notebook_select_kernel a python_path — the absolute path to the interpreter you want (e.g. a venv's bin/python):
notebook_select_kernel({ "python_path": "/abs/path/to/.venv/bin/python" })
You don't have to know any controller id, and the interpreter doesn't have to be one you've already selected in VS Code — the tool does both the discovery and the id computation for you. (kernel_id is still accepted for the rare case you already hold a controller id; without either argument the tool falls back to popping VS Code's kernel picker, which is useless to a headless agent.)
Under the hood this navigates two pieces of friction — the Jupyter extension exposes no public API to enumerate or assign controllers, and controller ids for interpreter-backed Python kernels are derived strings it computes internally:
No controller until the interpreter is known. A venv you've never selected in VS Code has no controller to bind to. Given python_path, the tool registers the interpreter with the Python extension first (registerInterpreter in src/utils/pythonEnv.ts: refresh discovery, resolve the path, set it active for the workspace) so Jupyter creates one — the same thing "Python: Select Interpreter → Enter interpreter path…" would do, done programmatically.
Computing the id. The controller id is built from the interpreter path (controllerIdForInterpreter in src/utils/kernel.ts). Empirically (verified by reading VS Code's "wanted kernel … all: …" log line) the format for an interpreter-backed kernel is:
.jvsc74a57bd0<sha256(normalizedInterpreterPath)>.<normPath>.<normPath>.-m#ipykernel_launcher
Where normalizedInterpreterPath strips just the bin/ segment of a venv path while keeping the python filename — so /x/y/.venv/bin/python becomes /x/y/.venv/python (system paths like /usr/bin/python3 are left untouched). See getKernelId and getInterpreterKernelSpecName in microsoft/vscode-jupyter.
The controller lands a beat after the interpreter becomes known, so the tool retries notebook.selectKernel for a few seconds (selectKernelById) rather than firing once and risking a silent no-op. This is brittle to upstream changes in the Jupyter extension, but the failure mode is informative — VS Code logs both the requested id and the available ones (wanted kernel DOES NOT EXIST, wanted: <id>, all: <ids>) so a changed format can be reverse-engineered the same way. The integration suite exercises the full path (including a brand-new, never-registered venv) in test/integration/suite/kernel.test.ts.
Still open / not yet handled: other forms of "kernel" (a global kernelspec name, a remote Jupyter server URL), and version probing of the interpreter.
npm install
npm run build # esbuild bundle
npm run watch # rebuild on change
npm run typecheck # tsc --noEmit
npm test # @vscode/test-electron + Mocha integration tests
Press F5 in VS Code to launch the Extension Development Host.
npm test runs the integration suite under @vscode/test-electron. It downloads a fresh VS Code Insiders build, installs ms-python.python and ms-toolsai.jupyter into it, opens the test workspace at test/fixtures/workspace/, and runs Mocha specs that drive the MCP tools through an in-memory transport (no HTTP). Test files live in test/integration/suite/.
Why Insiders: macOS refuses to launch a second extension-host of the same bundle id, so running tests against stable VS Code fails when you already have stable VS Code open ("currently only supported if no other instance of Code is running"). Insiders and stable have distinct bundle ids and coexist. Override with
VSCODE_TEST_CHANNEL=stableif you don't have stable open.
Prerequisites:
test/fixtures/python/.venv that supplies the test kernel (ipykernel + matplotlib + numpy + plotly). Pinned to Python 3.13 via .python-version.The pretest hook builds the extension (npm run build), compiles the tests (npm run build:test → out/), and runs scripts/setup-test-python.sh which is idempotent (uv sync only).
Notes on what the tests cover:
notebooks.test.ts, cells.test.ts) — list/open/get/insert/edit/delete/clear, error paths, active-editor fallback. Run without a Python kernel.execution.test.ts, kernel.test.ts) — depend on selectVenvKernel attaching the venv's interpreter; see Kernel selection.plotly.test.ts) — verifies a Plotly figure flows through the hidden webview renderer to a PNG image content block.Runtime (dependencies)
@modelcontextprotocol/sdk — the MCP server itself. We use McpServer (one per session, see src/mcp/server.ts) and StreamableHTTPServerTransport to speak MCP over the embedded HTTP server. Tool registration in src/mcp/tools/*.ts is all SDK API.plotly.js-dist-min — bundled into dist/plotly.min.js by the copy:plotly script and loaded inside the hidden notebook-mcp.plotlyRenderer webview. The webview converts Plotly figure JSON to PNG via Plotly.newPlot + Plotly.toImage. We can't run Plotly in the extension host (no DOM), and shipping the bundle locally avoids a runtime CDN fetch under the webview's strict CSP. Using -dist-min (the prebuilt minified bundle) instead of plotly.js keeps the VSIX smaller and avoids pulling in Plotly's full build toolchain.zod — schema definitions in src/schemas/index.ts and per-tool input validation across cells.ts / kernel.ts / notebooks.ts. The MCP SDK accepts zod schemas directly when registering tools, so we get validation + JSON Schema generation for free.Dev (devDependencies)
@types/node — Node typings for extension-host code (Buffer, process, http, etc.).@types/vscode — VS Code Extension API typings. Pinned to ^1.85.0 to match engines.vscode.esbuild — bundler used by npm run build to produce the single dist/extension.js that VS Code loads. Chosen over tsc for build speed and tree-shaking; --external:vscode keeps the host-provided module out of the bundle.typescript — provides tsc for the typecheck script (tsc --noEmit) and the build:test script (tsc -p tsconfig.test.json → out/). Esbuild handles the production bundle; tsc handles the test build because Mocha loads test files directly.mocha + @types/mocha — test runner, the convention with @vscode/test-electron. The bootstrapper at test/integration/suite/index.ts discovers *.test.js files under out/test/integration/suite/ and feeds them to Mocha.@vscode/test-electron — downloads a stable VS Code into .vscode-test/, installs ms-toolsai.jupyter into it, and launches it with the extension under development plus the test workspace fixture. See test/integration/runTest.ts.glob + @types/glob — used by the Mocha bootstrapper to discover compiled test files.To produce an installable .vsix file:
npx @vscode/vsce package
This runs vscode:prepublish (which builds via esbuild) and writes vscode-notebook-mcp-<version>.vsix to the repo root. Install it with:
code --install-extension vscode-notebook-mcp-<version>.vsix
.vscodeignore keeps the package small by excluding TS sources, node_modules, source maps, and dev configs — only the bundled dist/, package.json, README.md, and LICENSE are shipped.
Other projects along similar lines to this project:
I built this fresh to learn and so I have room to tweak the design and develop new features such as reading Plotly images.
MIT
Run in your terminal:
claude mcp add vscode-notebook-mcp -- npx CSA PROJECT - FZCO © 2026 IFZA Business Park, DDP, Premises Number 31174 - 001
Security
Low riskAutomated heuristic from public metadata — not a security guarantee.