vscode-notebook-mcp
Enables AI agents to work with Jupyter notebooks open in VS Code, including listing and opening notebooks, reading and editing cells in the editor, running cells in the live Jupyter kernel, inspecting kernel variables, and managing kernel state and outputs.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@vscode-notebook-mcpadd a cell that plots df and run it in my open notebook"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
vscode-notebook-mcp
Jupyter notebooks for AI agents other than GitHub Copilot. Copilot works on a notebook inside VS Code: it edits cells in the open editor and runs them in the kernel you're using. Agents outside VS Code — Claude Code, Codex, or any other MCP client — can't. They can only rewrite the .ipynb file on disk while the same notebook is open in VS Code, and that breaks in three ways:
File conflicts you can't save past. The editor and the file now disagree. VS Code reports that the file on disk is newer, and saving means choosing between throwing away your unsaved work and overwriting the agent's changes. Outputs and edits get lost either way.
No access to the running kernel. The agent can't run a cell in the kernel you're using or read its variables, so it re-runs whole notebooks from scratch or guesses.
Lost context. Nothing tells the agent which code the kernel actually ran, so it reasons about variables that no longer match the code on screen.
This extension closes that gap. It exposes the notebooks open in VS Code to MCP-compatible agents, so they work the way Copilot does:
Edit in the editor, not on disk — no file conflicts. Changes go through VS Code's notebook API, into the document you have open: undoable, marked in the editor, other cells' outputs untouched, and saved by VS Code itself only when asked, so there is never a second version of the file to conflict with.
Run in the live kernel. Cells run in the kernel VS Code is already using, so variables survive between the agent's steps; long runs, interrupts and restarts are handled.
Change only what's needed. Replace one piece of a cell instead of rewriting it, address cells by stable ids, and get compact outputs instead of whole HTML tables.
Keep the context straight. The agent can see which cells were edited since they ran or never ran in this kernel, rerun just what's stale, and inspect variables without adding cells.
Stay out of the way. It keeps your kernel and Python interpreter choices, doesn't hide your terminal, and connects each Claude Code session to the right VS Code window.
This is a fork. It is based on caryan/vscode-notebook-mcp by Colm Ryan (MIT), forked at v0.3.0 (commit
9e8061a). The original license and copyright are kept in LICENSE.How the fork was made. The changes from 0.4.0 on — token authentication and DNS-rebinding protection, reliable execution tracking, Windows support, partial edits, stable cell ids, long-run handling, output budgets, stale-cell tracking, variable inspection, agent-edit markers and the stdio bridge — were designed, implemented and tested mainly by Claude Code (Anthropic's coding agent, model Claude Opus 5.5), working from a code review it wrote of the original project and directed and reviewed by YukHin. See CHANGELOG.md for the full list and the commit history for details.
Status: pre-alpha.
Tools (22)
Discovery
Tool | Description |
| List all open notebooks with URI, file name, cell count, and which is active |
| Open a |
| List cells with stable id, index, kind, language, preview, execution state, and run state (fresh / edited since run / not run in this kernel) |
| Does the kernel match the code on screen? Edited, not-run and downstream cells, running cells, unsaved changes |
| Full source of a cell |
| Current outputs of a cell (text, errors, images), within an output budget |
Cell manipulation
Tool | Description |
| Insert a code or markdown cell at any position; optionally execute |
| Replace one exact piece of text in a cell, leaving the rest untouched |
| Several replace / whole-cell edits across cells, all-or-nothing |
| Replace the whole contents of a cell |
| Delete a cell |
| Save the editor's in-memory notebook to its |
Execution
Tool | Description |
| Execute a code cell and return this run's outputs; long runs report |
| Run a range, everything from the first stale cell on ( |
| Wait for a running cell without starting it again |
| Kernel variables with type / shape / dtype / columns, or previews of named ones — no cell added, execution count unchanged |
| Stop running and queued cells, keeping kernel state |
| Clear outputs of one cell |
| Clear outputs of every cell |
Kernel
Tool | Description |
| Language, status, notebook URI |
| Make sure the notebook has a kernel. With no arguments it keeps the running kernel or attaches the workspace's selected Python interpreter; with |
| Restart the kernel, clearing in-memory state but keeping cell outputs |
All tools accept an optional notebook_uri (omitted → uses the active notebook editor) and response_format ("markdown" or "json"). Cell tools take cell_id (from notebook_list_cells; it survives inserts and deletes) or index. Tools that return outputs take max_output_chars (default 20,000; long text keeps its start and end) and max_images (default 4), and return one form per output (no HTML duplicate of a DataFrame, no ANSI color codes).
Related MCP server: Jupyter MCP Server
Installation
Requirements
VS Code 1.85 or later, with the Python and Jupyter extensions (
ms-python.python,ms-toolsai.jupyter).Node.js 18 or later on PATH, to build the extension and to run the bridge that Claude Code talks to.
A Python environment with
ipykernelfor your notebooks (what Jupyter in VS Code needs anyway).Claude Code, or another MCP client.
Install from the Marketplace
In VS Code, open the Extensions view, search for Notebook MCP for Claude Code & Codex, and install it. Or from a terminal:
code --install-extension yukhin.vscode-notebook-mcpOr build from source
git clone https://github.com/YukHin/vscode-notebook-mcp.git
cd vscode-notebook-mcp
npm install
npx @vscode/vsce package --no-dependencies
code --install-extension vscode-notebook-mcp-0.6.1.vsixThe extension id is yukhin.vscode-notebook-mcp. If you installed an earlier build published as colmryan.vscode-notebook-mcp, uninstall it first: code --uninstall-extension colmryan.vscode-notebook-mcp.
On Windows PowerShell, if scripts are disabled (npm.ps1 cannot be loaded), use npm.cmd and npx.cmd instead, or run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once.
Installing or updating an extension makes VS Code restart the extension host in every open window, which stops running Jupyter kernels there. Do it when no notebook has state you want to keep.
After installing, each VS Code window shows a notebook icon with :49777 (or the next free port) on the right of the status bar. Click it for the server info.
Connecting Claude Code
In VS Code, open the command palette (Ctrl+Shift+P / Cmd+Shift+P) and run Notebook MCP: Add to Claude Code, then confirm. This runs, for all your projects:
claude mcp add --scope user notebook -- node ~/.vscode-notebook-mcp/bridge.jsIf VS Code can't find the
claudecommand, the command is copied to your clipboard instead; run it in a terminal (Copy Claude Code Command does the same).Start a new Claude Code session from your project folder. Claude Code loads MCP servers when a session starts.
Check the connection from that folder:
claude mcp get notebookIt should say "Connected". The bridge talks to the VS Code window whose workspace contains the folder Claude Code runs in, so open the project folder in VS Code first. With several windows open, each project reaches its own window; to point at a different folder, set
NOTEBOOK_MCP_WORKSPACE.
No token is stored in Claude Code's config: the bridge reads it from ~/.vscode-notebook-mcp/<pid>.json, which each window writes when it starts.
Other MCP clients can use the bridge the same way (a stdio server: node ~/.vscode-notebook-mcp/bridge.js), or talk HTTP directly to the URL in that file with Authorization: Bearer <token>. To keep the token out of a checked-in .mcp.json, reference an environment variable: "headers": { "Authorization": "Bearer ${NOTEBOOK_MCP_TOKEN}" }.
Using it
Open the notebook in VS Code, then ask Claude Code about it in plain language. It sees the notebook as it is in the editor (including unsaved changes and the running kernel), and its edits land in the editor, where you can undo them.
Examples of what to ask:
"Look at
analysis.ipynb. Which cells are out of date, and bring the kernel up to date." →notebook_status, thennotebook_run_cellswithmode: "stale"."In the cell that loads the data, change the date filter to 2024 and rerun everything after it." →
notebook_replace_in_cell(changes only that line), thennotebook_run_cells."What's in the kernel right now? I need the shape of
dfand the columns." →notebook_inspect_variables(no cell is added, the execution count doesn't change)."Run the training cell; it takes about 10 minutes." →
notebook_run_cellreturnsrunningafter its timeout with the output so far; Claude Code keeps waiting withnotebook_wait_cell, or stops it withnotebook_interrupt_kernelif you ask."Fix the error in cell 7 and save the notebook." → edit, run,
notebook_save.
What you'll see in VS Code:
Cells Claude changed or inserted show Changed by agent / Inserted by agent in their cell status bar until you edit or run them. Undo with Ctrl+Z (Cmd+Z) in the notebook.
The first time Claude inspects variables, Jupyter asks "Do you want to grant Kernel access to the extension Notebook MCP for Claude Code & Codex?". Choose Allow (once). To change it later, run Jupyter: Manage Access To Jupyter Kernels.
To approve every change before it's applied, turn on the setting
notebook-mcp.confirmEdits: a diff opens and a notification asks Apply or Reject (no answer in 2 minutes rejects).Edits stay in the editor until saved. Claude Code is told to use these tools, not its file tools, for open notebooks, and to call
notebook_savewhen done.
The kernel is chosen the way you'd expect: if the notebook already has a running kernel, it is kept; otherwise the workspace's selected Python interpreter is used. Claude Code only switches to a different kernel if you ask it to (switching discards the kernel's variables), and it puts your workspace's Python interpreter back afterwards.
Troubleshooting
Symptom | What to do |
| Open that folder (or a parent) in VS Code, or set |
Claude Code doesn't list the notebook tools | Start a new Claude Code session; check |
The status bar shows the notebook icon with | Click it for the error, then Restart. Another program may hold the ports 49777–49876; set |
| Run Jupyter: Manage Access To Jupyter Kernels and allow Notebook MCP for Claude Code & Codex. |
An interrupt doesn't stop a cell on Windows |
|
You think the token leaked | Run Notebook MCP: Regenerate Access Token. The bridge picks up the new one automatically. |
For development (running from source with F5, tests), see Development.
Security
The server listens on 127.0.0.1 only, rejects requests whose Host isn't 127.0.0.1/localhost on its port (DNS rebinding), rejects any request with an Origin header (web pages), sends no CORS headers, and requires the bearer token on /mcp. If the token leaks, run Notebook MCP: Regenerate Access Token.
Configuration
Setting | Default | Description |
|
| Preferred port. Auto-increments if busy (up to +99). |
|
| Review each agent change first: a diff opens and a notification asks to apply or reject (no answer in 2 minutes rejects). |
Cells the agent changed or inserted show Changed by agent / Inserted by agent in their cell status bar until you edit or run them; undo with Ctrl+Z (Cmd+Z).
Commands
Notebook MCP: Add to Claude CodeNotebook MCP: Copy Claude Code CommandNotebook MCP: Regenerate Access TokenNotebook MCP: Restart ServerNotebook MCP: Show Server Info
Architecture
┌─────────────────────────────────────────────────────────┐
│ 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.) │
└───────────────────────────────┘Implementation notes
Writes go through
vscode.NotebookEdit+vscode.WorkspaceEdit, preserving undo/redo.Execution dispatches
notebook.cell.executeand waits by pollingcell.executionSummary.successfor abooleanvalue (VS Code creates the summary as soon as execution starts butsuccessonly becomes a boolean when the kernel finishes).Inserted cells are tagged with a metadata id so the executor can find them again after the index shifts due to other concurrent edits.
Multi-notebook: every tool accepts
notebook_uri. Resolution: explicit URI → look up invscode.workspace.notebookDocuments(or open it); otherwise fall back tovscode.window.activeNotebookEditor.
Kernel selection
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 (registerInterpreterinsrc/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 (
controllerIdForInterpreterinsrc/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_launcherWhere
normalizedInterpreterPathstrips just thebin/segment of a venv path while keeping thepythonfilename — so/x/y/.venv/bin/pythonbecomes/x/y/.venv/python(system paths like/usr/bin/python3are left untouched). SeegetKernelIdandgetInterpreterKernelSpecNamein 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.
Development
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 testsPress F5 in VS Code to launch the Extension Development Host.
Running the tests
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:
uvon PATH — used to manage the Python venv attest/fixtures/python/.venvthat 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:
Non-execution tools (
notebooks.test.ts,cells.test.ts) — list/open/get/insert/edit/delete/clear, error paths, active-editor fallback. Run without a Python kernel.Execution + kernel (
execution.test.ts,kernel.test.ts) — depend onselectVenvKernelattaching the venv's interpreter; see Kernel selection.Plotly (
plotly.test.ts) — verifies a Plotly figure flows through the hidden webview renderer to a PNG image content block.
Dependencies
Runtime (dependencies)
@modelcontextprotocol/sdk— the MCP server itself. We useMcpServer(one per session, seesrc/mcp/server.ts) andStreamableHTTPServerTransportto speak MCP over the embedded HTTP server. Tool registration insrc/mcp/tools/*.tsis all SDK API.plotly.js-dist-min— bundled intodist/plotly.min.jsby thecopy:plotlyscript and loaded inside thenotebook-mcp.plotlyRendererwebview, which runs as a background editor tab. The webview converts Plotly figure JSON to PNG viaPlotly.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 ofplotly.jskeeps the VSIX smaller and avoids pulling in Plotly's full build toolchain.zod— schema definitions insrc/schemas/index.tsand per-tool input validation acrosscells.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.0to matchengines.vscode.esbuild— bundler used bynpm run buildto produce the singledist/extension.jsthat VS Code loads. Chosen overtscfor build speed and tree-shaking;--external:vscodekeeps the host-provided module out of the bundle.typescript— providestscfor thetypecheckscript (tsc --noEmit) and thebuild:testscript (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 attest/integration/suite/index.tsdiscovers*.test.jsfiles underout/test/integration/suite/and feeds them to Mocha.@vscode/test-electron— downloads a stable VS Code into.vscode-test/, installsms-toolsai.jupyterinto it, and launches it with the extension under development plus the test workspace fixture. Seetest/integration/runTest.ts.glob+@types/glob— used by the Mocha bootstrapper to discover compiled test files.
Building a VSIX
To produce an installable .vsix file:
npx @vscode/vsce packageThis 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.
References
Other projects along similar lines to this project:
datalayer/jupyter-mcp-server — MCP server that talks to a running Jupyter server via HTTP/WebSocket.
olavocarvalho/vscode-runtime-notebook-mcp — similar architecture as this project (VS Code Notebook API + embedded HTTP MCP server).
The original author built the upstream project fresh, to learn and to have room to tweak the design and develop new features such as reading Plotly images; this fork builds on that work.
License
MIT — see LICENSE. Copyright (c) 2026 Colm Ryan for the original project and (c) 2026 YukHin for the fork's changes, both under the MIT license.
This server cannot be deployed
Maintenance
Related MCP Connectors
System-of-record notebook for AI coding agents: pages, datastores, tasks, skills over MCP.
Persistent memory, knowledge graph, task management and more for AI agents via MCP.
The OpenRouter MCP server plugs OpenRouter into the AI tools you already use. Once connected, your assistant can pull live OpenRouter data (models, prices, your credits, rankings, and docs) and send quick test messages, all without leaving your editor.
Persistent memory and cross-session learning for AI coding assistants (hosted remote MCP).
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceExposes VSCode Jupyter notebooks to MCP-compatible AI agents, enabling them to read, edit, and run cells against the same kernel.MIT
- AlicenseAqualityCmaintenanceEnables AI agents to interact with Jupyter notebooks via MCP tools for querying, modifying, executing, and setting up notebooks, with state preservation and real-time collaboration.445Apache 2.0
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to read, edit, execute cells, and capture outputs from Jupyter notebooks directly within VS Code or Cursor via the Model Context Protocol.8 npm18MIT
- AlicenseNot gradedqualityBmaintenanceVS Code extension that bridges local Jupyter Notebooks with AI Agents via MCP, enabling direct control of the active notebook tab.2MIT