Skip to main content
Glama
Happypig375

VS Code Jupyter MCP Server

by Happypig375

Jupyter MCP Server

Marketplace version Marketplace installs GitHub repository

A notebook-specific MCP server that runs inside VS Code. An external MCP client can inspect, edit, execute, create, export, and manage notebooks open in the VS Code windows the user is working in. It has no dependency on Copilot Chat or Cursor. Notebook document operations use VS Code's native API; kernel operations require the installed ms-toolsai.jupyter extension.

Install and connect

  1. Install Jupyter MCP Server (publisher Happypig375) from the Marketplace, or run code --install-extension Happypig375.vscode-jupyter-mcp-server.

  2. Check the $(notebook) MCP status item and copy its URL, or use the output channel. The default endpoint is http://127.0.0.1:51303/mcp.

  3. Configure the external client to use Streamable HTTP and that copied endpoint.

The tool contract below describes 0.5.0; see the changelog for earlier versions. After an upgrade, refresh or reconnect the client's MCP tool catalog if it still advertises old parameters. The connected server's tools/list response is authoritative.

Related MCP server: Agentic Jupyter MCP

How this project differs

The design is an in-extension broker over the user's live, shared in-memory NotebookDocument. Windows register as peers, duplicate URIs are disambiguated with short opaque notebookRef handles, and one broker port is taken over safely if its owner closes. The decisions below explain which documented ideas we adopted and which remain outside this project's boundary.

Design or workflow

This project's decision, benefit, and tradeoff

Relevant comparison

Shared live notebook document

Adopted. Read and edit VS Code's current document, including unsaved changes. The agent and user share notebook state; the notebook must be open in VS Code.

vscode-inmemory-notebook-mcp also supports live notebooks and external clients. mcp-jupyter-complete uses file edits and reloads; Datalayer supports standalone Jupyter Server workflows.

Window routing

Implemented inside the extension host. One window owns the broker, peers can take over its port, and short refs distinguish the same URI in different windows. This avoids a separate daemon process.

vscode-inmemory-notebook-mcp uses a daemon with per-window bridge workers.

Long-running execution

Adopted for 0.4.0. run_cells returns an executionId; get_execution follows that same run after a wait budget expires or waitMs: 0 returns immediately.

The operation-ID and polling pattern comes from vscode-inmemory-notebook-mcp.

Pushed output streaming

Not implemented. Bounded polling serves request/response clients. Pushed events would add client notification support and event-history management that this workflow does not require.

vscode-inmemory-notebook-mcp documents polling and streaming.

Cell locking

Not implemented. The server checks explicit targets and execution freshness without imposing an edit-ownership policy on the user and agent. Locking would require a separate coordination policy.

vscode-inmemory-notebook-mcp exposes cell-lock tools.

Per-run interruption

Not implemented as an isolated cancellation tool. interrupt_kernels explicitly requests a kernel-wide interrupt; stopping observation of a run does not prove its code stopped.

This server's interrupt implementation uses the notebook kernel command.

Provider startup and administration

Colab initialization and runtime verification in 0.5.0. MCP handles CPU/server setup through VS Code's native Chromium driver, lists actual kernel choices for explicit selection, starts and mounts the runtime, then proves kernel/filesystem identity. Requires the supported local Colab extension and a native debugging connection; authentication may require the user. Terminal inspection and explicit closure handle preparation blockers; Drive and general resource administration are not implemented.

The Colab VS Code guide documents its provider-owned connection and administration flow.

Whole-notebook reads, cell anchors, and export were inspired by vscode-inmemory-notebook-mcp; the execution-wait and output-capture pattern came from vscode-runtime-notebook-mcp.

Sources and repository documentation were checked on 2026-09-06. The Colab setup row was rechecked on 2026-10-19 against the guide, installed 0.9.7 bundle and corresponding source linked below; its preparation-terminal scope was rechecked against the guide and implemented tools on 2026-10-20 (Hong Kong time). This is a bounded, documentation-scoped comparison; it does not claim competitor installation or behavioral testing.

Tools

Existing-notebook tools use notebookRef, or notebookRefs for a batch. Prefer the short ref from list_notebooks; a plain URI is also accepted when it identifies one open notebook. open_notebooks accepts file URIs in uris and an optional windowId; create_notebook accepts title and an optional windowId.

Cell references accept zero-based numeric indices or the string cell_id returned by a read. String references are matched as IDs first, so a native ID containing only digits (for example "40025805") remains an ID; send a JSON number to request an index. Numeric strings still fall back to indices when no exact ID matches. Prefer real cell IDs; an index:N fallback can change after insertion, deletion, or movement. Notebook refs remain valid across cell edits and resolve only against currently open notebooks.

Tool

Category

Description

inspect_colab_environment_preflight

Read

Aggregate bounded preparation blockers, native-listener candidates and sanitized app identity/count corroboration without changing editor or runtime state

list_colab_terminals

Read

Inspect bounded terminal metadata and short-lived references in one window; never reads commands or history

close_colab_terminals

Manage

Close only explicitly selected, authorized terminals from that window's latest inventory; reports uncertain or partial closure

prepare_colab_environment

Manage

Prepare a local workspace and native connection, optionally preserving one strictly verified clean configured Workspace across a tracked normal Windows restart

get_colab_environment_preparation

Read

Follow durable preparation after restart and freshly verify the exact workspace/native connection

create_notebook

Create

Create a workspace file or an untitled notebook in an empty window and open it

list_notebooks

Read

List grouped connected windows, including empty windows, with windowId, windowLabel, URI, and routable notebookRef

read_notebook

Read

Read an outline by default, or select source, outputs, or all; optionally target cellIds

search_cells

Read

Search cell source and output text with match locations

get_kernel_info

Read

Report observed active runtime fields and explicit unavailable reasons

get_execution

Read

Inspect a tracked executionId, or recover the notebook's latest run; optionally wait for its result

list_kernels

Read

List exact registered kernel/controller IDs; read-only

configure_kernel

Manage

Generic Jupyter setup, tracked Colab CPU initialization, or exact prior Colab server reuse with runtime verification

mount_colab_server

Manage

Recover the exact MCP-created Colab server mount for an already active idle Python kernel, then verify it

list_setup_kernels

Read

List actual kernel choices from an exact active Colab setup; fresh listing invalidates earlier choice IDs

select_setup_kernel

Manage

Select one exact ID from that setup's latest listing, then continue startup and verification

get_kernel_setup / cancel_kernel_setup

Manage

Poll or cancel Colab initialization, including cleanup and retained-server/cell status

check_colab

Diagnose

Prove the active idle kernel matches a Colab filesystem root through a fresh nonce and confirm cleanup

edit_cells

Write

Insert, edit, delete, or exact-text replace cells; optional explicit rerun

move_cells

Write

Move cells while preserving content, outputs, and metadata

clear_cell_outputs

Write

Clear outputs and execution state

run_cells

Execute

Start a tracked, ordered run; waitMs bounds the caller's wait, and observation continues independently

select_kernel

Manage

Select an exact ID from list_kernels; no fallback or start

restart_kernels

Manage

Request notebook-kernel restarts; provider confirmation may be required

interrupt_kernels

Manage

Request kernel interrupts; completion cannot be confirmed

open_notebooks

Manage

Open file URIs, preserving an existing live model

save_notebooks

Manage

Persist file-backed notebooks, including remote execution state

upload_file / download_file

File transfer

Hashed transfer between hostPath and kernelPath; native Colab Contents transport for files above 4 MiB

export_notebook

Manage

Export as markdown, Python, or HTML

Editing cell metadata

edit_cells.edits[].metadata represents the cell's nbformat metadata, such as tags, jupyter.source_hidden, or presentation fields. Inserted cells store that map; edit and replace shallow-merge it with existing nbformat metadata. Omitting it preserves existing metadata. VS Code's native cell ID, attachments, and execution-count fields remain separate and are preserved. The saved .ipynb receives the supplied fields inside each cell's metadata object.

For insert, cellId is an anchor: a numeric index or stable ID inserts the new cell immediately after that cell. TOP inserts before the first cell and BOTTOM inserts after the last. This preserves the existing insertion behavior while making the position explicit.

This follows the ipynb extension's serializer and native cell envelope, checked on 2026-10-12. Standalone metadata regressions exercise serialized fields and reopening with a bounded serializer fixture. Setting NOTEBOOK_SERIALIZER_BUNDLE to an installed ipynb bundle additionally checks its pure conversion function without activating that extension; this is a serialization check, not a live editor/save integration test.

Reading without excess output

read_notebook returns notebook and cell metadata in every view. source adds source text, outputs adds saved output, and all adds both within the requested limits. Source slicing uses 1-based inclusive startLine and endLine; maxSourceChars defaults to 12,000 per cell, with 0 requesting unbounded source explicitly. Truncation is reported.

For outputs, outputMode selects summary, preferred text, or full textual representations. maxOutputChars bounds each cell's returned output. Binary images are summarized rather than decoded. Saved output can be stale after an edit; saving does not refresh it.

Jupyter and providers

The package declares ms-toolsai.jupyter as a dependency. Kernel-backed tools (list_kernels, configure_kernel, mount_colab_server, check_colab, select_kernel, run_cells, restart/interrupt, and file transfer) are registered when that extension is present; presence does not guarantee every runtime API is available. get_kernel_info and get_execution remain exposed for read-only diagnostics.

list_kernels lists currently registered controllers, including those contributed by other extensions; it does not discover every dormant provider. configure_kernel defaults to generic Jupyter setup and may display a picker, authentication, or consent UI. Its JSON response distinguishes an observed active kernel, pending setup, and no ready kernel; ready remains false because observation alone does not prove responsiveness or provider identity. select_kernel requires an exact listed ID and passes the actual target editor to the public selection command. Inspection, listing, and file transfer do not start or select a kernel. File transfer requires the public API of an active idle Python kernel; see Microsoft's kernel execution and authorization sample.

Colab setup and checking

23 October source update: environment recovery and window correspondence now target the exact Windows 1.143.0 build identified below. Independent source review, structural checks and deterministic suites pass. An isolated native picker fixture passes; the separate environment fixture passed its behavior assertions but failed cleanup. A reviewed cleanup-only correction then closed both owned development windows normally, with independent process and port checks. The failed full fixture remains recorded. This change has not yet been installed or validated by a default-profile 1.143.0 restore or real Colab setup. See the bounded validation record. The dated 1.142.0 checks below remain historical evidence. Normal Colab runtime verification is separate from recovery compatibility.

If VS Code initially suggests local kernels for the notebook, setup chooses Select Another Kernel... → Colab. If the source picker opens directly, setup chooses Colab there. VS Code can reopen the source picker during a provider handoff; setup allows one additional Colab selection after observing a new empty prompt. It does not accept a suggested local kernel as a default.

Before an input action, MCP brings its exact bound VS Code page to the foreground through Chromium and verifies focus. Binding and kernel listing remain read-only; input guards check focus again before typing or submitting.

Colab activation is checked before opening any picker, even if VS Code reports the extension as active. A cached activation failure returns colab_activation_failed; inspect Colab's Output log, restore connectivity and reload the window before retrying. Failed activation can leave Google's authentication provider unregistered until that reload.

configure_kernel({ notebookRef, runtime: "colab" }) performs Colab initialization inside MCP. It uses the installed Colab 0.9.7 extension and a loopback Chromium debugging connection to operate its normal picker through fixed, bounded DOM projections. Orca and VS Code's smoke-test driver are not required or used by this path. A local, trusted VS Code window with an open folder/workspace is required. Remote extension hosts and unsupported provider versions return a blocker before setup.

From an ordinary empty VS Code window, call prepare_colab_environment({ windowId, folderUri }) before open_notebooks. Use the file URI of the notebook's existing local project folder; a folder already trusted by the user retains its normal trust. The tool does not change files or trust settings in that folder. Omitting folderUri creates an isolated folder, which may require user trust before MCP and Jupyter extensions start. If a native connection already works, preparation opens the requested or generated folder directly. Otherwise, the default route currently supports the default installed Windows VS Code 1.143.0 profile with exactly one clean empty local window. It checks the installed CLI, main process identity and all window evidence, arms a bounded helper, then requests normal VS Code quit. It never forces termination or saves/discards user work. Open documents, notebooks, terminals, other windows and unsupported installations block that automatic path. While still active, the extension host disarms an unconfirmed quit after five seconds; deactivation allows a normal slow shutdown to finish, and a new host in the same original app disarms a stale attempt. VS Code does not expose the quit-veto result. Concurrent extension disabling/reloading or unrelated exits cannot always be distinguished from the requested shutdown; the original deadline remains bounded.

prepare_colab_environment({ windowId, restartMode: "restore-clean-workspace" }) selects a separate, narrower route for one already configured Workspace. It supports only the default local Windows VS Code 1.143.0 build at commit 2d0b7506a36c1d6db9a466440131a8e01eab2289, exactly one corroborated native window, and a trusted local file-backed or untitled Workspace with 1–64 local roots. It first captures and revalidates only that exact Workspace URI and ordered root identities around the marker-bound native probe and pinned install/process/window checks. If the binding already works, dirty or untitled documents, arbitrary tab contents, terminals, debugging, quit/restore policy, kernels and tracked background work do not block this no-restart result; launch arguments and saved-state metadata are not inspected, no restart authorization is created, and the minimal receipt reports restartRequired: false. If the marker is unavailable and an actual restart is needed, window.restoreWindows must resolve to all or preserve, window.confirmBeforeClose must resolve to never, every open notebook must be saved and file-backed, and dirty or untitled text documents, dirty tabs, terminals, debugging, webviews, terminal tabs, virtual tab resources and unknown tab inputs block the route. The actual restart also requires an affirmative no-kernel result and no active or unsettled notebook execution, Colab setup, or file transfer.

Before a restored restart, MCP snapshots the exact Workspace URI, ordered roots and names, file-backed text/notebook/custom/diff tab identities, tab multiplicity and group membership, and document/notebook identities and versions. Document identities and versions are an exact pre-quit mutation guard; they are not a post-launch loaded-editor requirement. MCP also reads bounded metadata from the default profile's Code/Workspaces directory and User/globalStorage/storage.json backup registry. It never reads backup payloads, deletes stale state, or writes VS Code configuration, trust or private state. Another untitled Workspace or a registered empty-window backup blocks the route. The snapshot is revalidated before and after helper arming and immediately before normal quit under a process-local mutation exclusion. A per-request token holds the same exclusion continuously while the request cleans an older active receipt, reserves a port, and builds its new receipt; only that internal request may transfer provisional ownership to a durable preparation ID. Polling and initialization cannot adopt the request's unbound handle. The complete receipt reserves space for helper and failure fields before the active slot is claimed. Only the owning receipt after it is proven durably failed or ready can relinquish its durable ID; the in-progress request keeps the handle until it transfers to a new pending receipt or exits safely. Expiry must first be recorded and read back as failure, and polling an older receipt cannot release or replace a newer block. A failed final check durably disarms the helper, and an unconfirmed disarm keeps the owning exclusion held.

For this restored route, the helper relaunches the pinned application with the same default user-data and extensions directories plus the owned debugging port, with no positional Workspace argument and no --new-window; VS Code's last-session restoration must recreate the approved Workspace, ordered roots and tab-group resource multiplicity. Readiness is withheld until a distinct new main process is bounded by a persisted pre-spawn timestamp, the pinned installation/profile/version/commit and sole window are reverified, the current untitled-Workspace identity/no-other-untitled invariant and zero-empty-window-backup invariant hold, and the exact marker-bound native probe succeeds. Workspace/folder backup counts may be pruned by VS Code at startup and are not required to remain equal. Once a receipt is ready, polling checks only the Workspace identity, trust and a fresh marker-bound native connection; it does not reapply the old editor, terminal, kernel or saved-state checks. The receipt never claims runtime readiness. folderUri, when supplied, may only identify an existing root and never converts or opens a different folder.

inspect_colab_environment_preflight({}) is the read-only companion to that workflow. It queries every connected broker peer with a bounded 30-second per-peer deadline and returns counts and booleans for workspace shape, notebooks and their active/visible editors, text documents and their active/visible editors, tabs, terminals, debugging, quit/restore policy and native-listener candidates. Rejected, timed-out, oversized or invalid peer responses are reported as unavailable instead of failing the whole inspection. It also reports sanitized default-install/version/main-process corroboration and compares the connected-peer count with the independently observed app-window count. For the exact default local Windows VS Code 1.143.0 build at commit 2d0b7506a36c1d6db9a466440131a8e01eab2289, it can derive a candidate numeric window ID from this extension's public ExtensionContext.logUri. It validates the exact extension ID and default-profile path shape without reading or enumerating any log, then corroborates the ID against one unique sanitized code --status row under the verified main process. The broker reports a mapping only when every peer responds, all main-process/commit/native-row evidence agrees, every derived ID is unique, and the derived set exactly equals the observed native-window set. Count equality alone remains insufficient; unsupported versions or commits, remote or malformed contexts, duplicate IDs, extra windows, timeouts and mismatches remain unmapped. This mapping is point-in-time read-only evidence, not restart permission, and does not change preparation recommendations or prove restored-session, native-binding or runtime readiness. Native endpoint inspection reads only the bounded loopback target catalog; it does not create a marker, open a debugger WebSocket, evaluate renderer code, focus a window or establish exact window binding. prepare_colab_environment retains its own marker-bound check. An automatic next-operation recommendation requires exactly one valid responding peer, one corroborated app window, consistent verified application evidence and no unhandled blocker for that route. This recommendation rule is intentionally narrower than the existing native preparation path: an observed shared listener in a larger app remains evidence only unless prepare_colab_environment independently establishes the exact marker binding. The same conservative rule reuses the automatic-restart launch-argument allowlist, so an app previously bootstrapped with debug-port and workspace arguments can report startup_arguments_unsupported and suppress an automatic recommendation even while the native target catalog is reachable; that result does not prove native readiness failed. Window labels are shortened to 256 Unicode code points for broker display instead of rejecting a valid long multi-root label. The preflight never returns document contents, terminal commands/arguments/history/environment, authentication state, target titles/URLs, broad process arguments, log paths or filesystem paths. It does not save, close, reload, restart or select a runtime. Results are point-in-time only. The preflight retains no reusable authorization; only an explicit restored-mode preparation owns its bounded durable snapshot and mutation exclusion.

If a terminal blocks preparation, list_colab_terminals({ windowId }) returns opaque references with bounded name, shell PID, shell type, interaction and exit metadata. These fields cannot prove a terminal is idle or establish its ownership. For terminals whose closure is authorized, call close_colab_terminals({ windowId, inventoryId, terminalRefs }) with explicit references from the latest inventory, then inspect the closure result before retrying preparation. Inventories expire after 60 seconds and replacement inventories invalidate earlier references. Closure uses the public Terminal.dispose() API; it can terminate commands and lose scrollback. A closed result confirms removal from the VS Code window, not completion or exit of the shell process. There is no implicit close-all or automatic closure of restored terminals, and the tool never reads terminal arguments, environment, history or commands.

The default preparation route also converts a single folder into an untitled Workspace through VS Code's public updateWorkspaceFolders API, preserving the root URI and giving its display name an owned Colab suffix. This refresh happens before opening the notebook or creating its runtime, so Colab can later add its filesystem without the single-folder-to-workspace refresh. Existing configured Workspaces are reused. Restored notebooks, documents, terminals or debugging block conversion; the tool does not close or save them. The conversion is recorded before dispatch and never replayed if acknowledgement is missing. No internal workspace configuration or trust settings are written. The restored-workspace mode never invokes this conversion. This API behavior was checked against VS Code 1.142.0 and the public reference on 20 October 2026.

VS Code may ask whether to save the untitled Workspace when it is later closed or the app quits. Preparation leaves that normal save decision and the user's close preferences intact.

The receipt survives both restarts and includes preparationId, workspaceUri and expectedWindowLabel; after conversion, folderUri identifies the original root and workspaceUri identifies the untitled Workspace. Reconnect to the same MCP endpoint, re-list windows with list_notebooks, then call get_colab_environment_preparation({ preparationId, windowId }) on the current owner. Polling does not dispatch workspace changes. environmentReady: true requires the exact trusted workspace, original root/name, a fresh native connection and, for a generated folder, its owned marker; it is not Colab runtime readiness. Continue with the existing open_notebooks({ uris, windowId }) or create_notebook({ title, windowId }), then the explicit Colab setup sequence below. No second notebook-opening tool is introduced.

The helper relaunches the visible editor, enables a loopback debugging port for that app launch and returns it through the app environment. It does not modify argv.json, use Orca or enable the smoke driver. For other supported desktop environments, an existing --remote-debugging-port=<port> launch can still be configured with machine-scoped jupyterMcp.nativeDebugPort or JUPYTER_MCP_NATIVE_DEBUG_PORT. The adapter connects only to 127.0.0.1 and rejects redirected hosts, ports and non-page targets. Reloading an already running window cannot add that listener. Fixed DOM writes recheck marker, widget, input, focus, row and CPU state synchronously. An already verified Colab runtime can be reused without native UI setup.

The workflow reuses an already active runtime only after a fresh Colab check. Without an alias it otherwise selects Colab → New Colab Server → CPU, chooses Standard and Latest if those prompts appear, and supplies a unique alias. To rediscover a prior MCP-created server after a reload, pass its exact receipt value as serverAlias, for example configure_kernel({ notebookRef, runtime: "colab", serverAlias: "m0123abcd" }). That path selects only the exact live server row and never creates a fallback server or enters another alias. If the exact alias already has one matching mounted root and the current kernel verifies against it, no picker is needed. An absent, ambiguous, disabled, or changed alias fails safely.

Both paths then pause at awaiting_kernel_selection. Call list_setup_kernels({ notebookRef, setupId }), inspect the returned choices, and call select_setup_kernel({ notebookRef, setupId, kernelChoiceId }) with one exact returned ID. There is no default Python label or first-item selection. This listing is distinct from list_kernels, which reports already registered controllers. Filtered, incomplete, duplicate-label or changed picker contents fail safely. A new listing invalidates old choice IDs.

After that explicit selection and VS Code's confirmation, MCP executes only an inserted temporary pass cell, removes it after confirmed completion, invokes Colab's server-mount command, selects the same alias if needed, and verifies the kernel against the mounted filesystem. It does not choose GPU/TPU, purchase an upgrade, install packages, mount Google Drive, or delete existing servers. Google's sign-in, consent and MFA remain normal security interactions; an unhandled prompt stops setup with a blocker.

Mounting the first server can convert a single-folder window into a multi-folder workspace and restart its extension host. Keep the receipt's serverAlias: the MCP connection, window/notebook handles and in-memory setupId can be replaced during that refresh. Reconnect, call list_notebooks, and use the new notebook reference with configure_kernel({ notebookRef, runtime: "colab", serverAlias }). Follow the new explicit kernel listing/selection if required and require fresh verification. The interrupted receipt does not prove readiness. Notebook-specific picker matching includes the owning folder name in a multi-folder workspace.

Every Colab setup receipt includes its generated serverAlias once the alias exists, even if a later startup or mount step fails. If that exact server was selected successfully and the same notebook now has its active idle Python kernel, mount_colab_server({ notebookRef, serverAlias }) can recover only the missing workspace mount. It accepts only an MCP alias of the form m plus eight lowercase hexadecimal characters. It does not select or start a kernel, execute or edit cells, or create or remove a server. An already mounted folder is reused only when exactly one colab workspace folder has that exact alias. Otherwise the tool invokes the normal mount command, accepts only that alias, requires one newly added matching root, and completes only after a fresh same-kernel nonce check. The operation has its own tracked setupId; poll or cancel it with the normal setup tools.

The request returns a setupId. Poll get_kernel_setup({ notebookRef, setupId, waitMs: 1000 }); its actionRequired field identifies the explicit kernel-selection step. Continue polling after selection until status is ready, failed, or cancelled. Only ready: true with completed verification permits a readiness claim. waitMs is 0–60000 ms (setup/selection default 1000, polling default 0); request expiry does not cancel the five-minute background workflow. The deadline includes time awaiting a choice. cancel_kernel_setup cancels explicitly. Setup records are bounded to sixteen and last only for the extension-window lifetime. A transient unique marker binds actions to the owning window, and a machine-wide lease prevents overlapping setup. MCP notebook mutations are refused while that notebook's setup is active. Only bounded kernel labels and opaque choice IDs are returned by listing; no DOM tree, notebook text or screenshot is exposed.

A cancelled provider assignment may still create a server; MCP does not delete it automatically. A queued/running or edited startup cell is retained and reported, and unsettled commands retain the setup lease until they settle. Inspect failedStage, serverMayRemain, startupCellRetained, and cleanupPending after a failure. Server naming is confirmed by input readback and the following picker displaying the exact generated alias. Picker completion alone never proves readiness. Exact-ID selection requires VS Code's observed boolean confirmation and fails closed if a future version stops supplying it.

Before submitting a server name, MCP also requires the provider's alias prompt to identify Colab CPU. Cancelling setup keeps notebook mutations blocked until pending commands and runtime verification finish. If a command remains stuck after cancellation, use VS Code's normal interrupt control for the owned startup cell, or reload the owning window to recover the extension state; a reload does not prove remote work stopped. A persistent desktop lock after every owning VS Code process has exited needs inspection of the task-specific jupyter-mcp-colab-setup-* temporary directory; do not remove a live owner's lock. These are exceptional recovery paths, not steps in normal initialization.

Colab setup also refuses an unsettled run_cells command, even when an interrupt has made its execution record unavailable or that record has expired. Wait for the underlying VS Code/Jupyter command to settle before recovery. During provider selection, receipts expose providerStep; a failed receipt retains failedProviderStep, distinguishing the initial kernel picker, Colab source, server prompt, optional repeated handoff, and exact existing/new server selection. These bounded step names contain no captured picker text and do not establish a provider or authentication failure by themselves.

check_colab({ notebookRef }) also remains available independently. It requires an active idle Python kernel and one registered Colab filesystem root. Supply colabFileUri: "colab://<server-id>/" if multiple roots exist; this is a root URI without a query or fragment. The check creates one exclusive non-hidden nonce file in /content, validates bounded Linux/Python/module diagnostics, and requires the same nonce through the native Colab filesystem. It removes only its owned probe and confirms absence through both surfaces before returning verified: true. Labels, an installed module, or a mounted folder alone cannot pass. The receipt includes the root, process ID, Python version, timestamp and individual checks. The check reads no user files and edits no notebook cells.

If kernel-side removal succeeds but the filesystem is unavailable, check the current server/root before retrying. If execution or cleanup has not settled, retain that uncertainty. Verification applies only at that moment; check again after kernel changes. Standalone transfer/check request cancellation is not forwarded through cross-window routing; their internal deadlines still apply.

The flow was checked on 2026-10-19 against the installed Colab 0.9.7 bundle, its user guide, a corresponding source snapshot, VS Code's exact-editor command mapping, and native driver. The source snapshot is not a tagged 0.9.7 release. An isolated real VS Code 1.142.0 fixture passed every provider-like prompt, read-only listing, explicit second-kernel selection and alias mounting with no Orca. That fixture is not a real Colab runtime test. Run it explicitly with node src/test/colabNative.live.cjs <absolute-Code-executable> <new-evidence-directory>. Routine setup actions are performed by the MCP adapter; authentication and unsupported UI remain explicit blockers.

File transfer and runtime management

Files above 4 MiB require Colab's registered filesystem provider, which uses Jupyter's Contents API. This is the same file service used by Upload to Colab and Colab's native download command. The provider buffers a whole file, so native transfers have a hard 64 MiB cap even if maxBytes is larger. Smaller files use 256 KiB kernel chunks unless colabFileUri explicitly selects the native service. Each chunk request carries its payload once as a base64 literal, zlib-compressed when that saves at least 10%. IPython keeps the source of every executed request in linecache for the kernel's lifetime, so each transfer request is tagged and removes earlier tagged sources; payload-bearing requests are not retained. A kernel object carries at most 64 MiB of chunk traffic; beyond that, transfers are refused before dispatch with guidance to use the native service or restart the kernel. Kernel-side exceptions are reported by class name only.

For native transfer, open a file from the existing runtime's Colab Contents view. Alternatively, Colab: Mount Server to Workspace is available when Colab's experimental colab.serverMounting setting is enabled. get_kernel_info then lists colabRootCandidates. A transfer creates a random nonce file in the selected kernel's /content and requires the candidate filesystem to read the same nonce before any payload is sent or retrieved. If multiple roots are exposed, supply colabFileUri explicitly; they are not probed in sequence. Alternatively, pass an exact colabFileUri, such as colab://<server-id>/content/data.bin, whose path must equal kernelPath. No runtime is started, selected or guessed. Missing or mismatched native routes fail without falling back to a large kernel transfer.

Uploads use the native service only for a random temporary file, then verify size/SHA-256 and atomically promote it in the original kernel. Downloads verify the returned bytes against an independent kernel-side hash before local promotion. No-overwrite remains the default. Kernel lookups/executions have a 60-second deadline, native payload steps two minutes, nonce reads fifteen seconds, and best-effort remote cleanup five seconds. Only one transfer may use a notebook or kernel at a time; each step rechecks the original active idle kernel. An observed restart invalidates the operation. Pending kernel executions or native payload requests quarantine that kernel until they settle. A timed-out read-only nonce probe does not quarantine the selected kernel, so a stale root cannot block a corrected explicit URI. The filesystem API cannot cancel in-flight I/O: a timeout, busy kernel or lost runtime may leave a random .part or nonce file, and unconfirmed promotion remains uncertain. MCP client cancellation is still not forwarded across window routing. See the transfer diagnosis and regression scope.

restart_kernels and interrupt_kernels request state changes through Jupyter's provider commands. restart_kernels refuses to dispatch when the public Jupyter API explicitly reports no active kernel; it never selects or starts a replacement. Provider command failures are returned as failures. After a command returns, the tool reports the current active-kernel observation and still does not claim completion, because a provider can display confirmation UI or acknowledge before the kernel state changes. Use get_kernel_info before run_cells to verify the intended runtime where available.

The optional Colab VS Code extension contributes controllers through Jupyter. On 2026-09-06, MCP server 0.3.0 with google.colab 0.9.3 passed live checks for targeted execution, nonadjacent output clearing, and a 524,425-byte upload/download round trip with matching SHA-256 hashes in an existing Colab Python session. Initial discovery used the normal Colab picker. This is evidence for that tested integration, not a claim that every Colab feature is MCP-integrated.

On 2026-10-14, Marketplace 0.4.3 with google.colab 0.9.7 passed a live 65,568,700-byte native upload/download round trip and a separate 62,572,000-byte upload. Hashes matched and the same Colab process remained responsive through subsequent notebook execution. See the validation record for timings, scope and remaining limits.

Jupyter's public kernel API returns already-started kernels for open notebooks, without provider identity. Colab's 0.9.7 activation exports no connection API. The 0.5.0 setup flow therefore uses the normal notebook picker and a separate filesystem proof, rather than private provider registries or authentication state.

  1. Call list_notebooks and select a notebookRef.

  2. Read its outline, then request view: "source" for the cells needed. Edit cells and refresh indices or fallback anchors after structural changes.

  3. If a kernel must be chosen, list its registered controllers and select an exact ID. Configure the provider explicitly when necessary. For Colab, use runtime: "colab", list and explicitly select an actual setup kernel when requested, then require a successful nonce verification before workload execution.

  4. Call run_cells. waitMs defaults to 1,000; zero returns immediately, and larger values can wait for long jobs. This is a request wait budget, not an execution time limit. A returned ID or dispatch status does not prove execution started.

  5. If the run is unfinished, call get_execution with that ID. Its waitMs defaults to zero and also supports long waits. A client may impose a shorter request timeout; neither that timeout nor an expired wait budget interrupts execution. Omit the ID to recover the latest tracked run after a lost response.

  6. Read results and save edits or export the notebook. Completed background runs persist file-backed outputs, including remote results; untitled notebooks are not force-saved.

Runs submit cells in order and stop after an observed failure. Only one tracked run is active per notebook, with at most 16 active runs per window and 256 cells per run. The registry retains at most 32 notebooks and eight runs per notebook; terminal records expire after one hour and can be evicted earlier at capacity. Active runs have no one-hour expiry. Returned execution output is capped at 12,000 characters per cell and 48,000 per receipt. run_cells includes output by default; get_execution includes it only with includeOutputs: true. Lookup requires an open notebook, and records do not survive an extension reload. saveBeforeExecute pre-saves dirty notebooks before edit, move, or run; use save_notebooks to persist subsequent edits.

Tracking observes cell identity, source, execution summaries, and restart/interrupt requests made through MCP. Restarts initiated outside those tools are detected only when VS Code exposes corresponding cell or lifecycle changes. An unavailable result means completion could not be confirmed; it does not establish that the kernel stopped.

Execution receipts also report notebookCommandPending: whether any tracked cell command for that notebook has yet to settle in VS Code/Jupyter. This can remain true after completion is observed or tracking is invalidated. Another run and automatic Colab setup are blocked until those commands resolve or reject; pending notebooks continue to count toward the 16-notebook active-work limit independently of retained execution history. This is command-settlement evidence, not proof of remote kernel idleness.

Tracked runs watch the public Jupyter kernel's lifecycle and check its identity before each cell and once per second while waiting. A death, restart, termination, disappearance or replacement ends tracking as unavailable, with a bounded reasonCode and, when observed, kernelStatusAtStop; later cells are not-run. This also covers Colab automatically restarting without completing the old cell's execution summary. Once a kernel is observed, a transient lookup failure or unknown status is tolerated; three consecutive unsuccessful observations end tracking as uncertain. Each lookup is bounded to two seconds, and a new cell waits for a usable observation before dispatch. Initial lookup failure blocks dispatch. None of these observation failures proves a crash or its cause. The monitor runs no heartbeat code and never restarts or interrupts the runtime. It preserves unsettled-command protection, and a return to idle does not clear an earlier confirmed lifecycle failure. Use a fresh check_colab after recovery; neither an old setup receipt nor the new kernel's idle status confirms that the interrupted work completed. If the command never settles, a normal reload of the owning VS Code window clears this in-memory guard and tracking state; it does not prove remote work stopped. Verify the intended runtime before further execution.

Updating clients from 0.3.0

  • Replace inspect_notebooks, read_cells, and read_cell_outputs with the appropriate read_notebook view.

  • Replace run_cells.wait and timeoutMs with the single waitMs budget, then follow executionId with get_execution.

  • Replace file transfer's localPath with hostPath, which names a path on the VS Code extension host.

Obsolete keys and tools are rejected; refresh the client's tool catalog after upgrading.

Multi-window routing

All windows share the configured loopback HTTP URL. The first window binds the broker; others register private loopback peers. If the broker closes, a peer can take over the same port. A URI that is ambiguous across windows fails; use the listed ref. Multi-notebook operations are grouped by owner.

Configuration

Setting

Default

Description

jupyterMcp.enabled

true

Enable the server

jupyterMcp.transport

http

Streamable HTTP on loopback; stdio is scoped to the extension host and has no standalone Node launcher

jupyterMcp.port

51303

Shared machine-scoped broker port

jupyterMcp.saveBeforeExecute

true

Pre-save dirty notebooks before edit, move, or run

jupyterMcp.nativeDebugPort

0

Exact loopback Chromium debugging port; 0 uses the launch environment's JUPYTER_MCP_NATIVE_DEBUG_PORT, otherwise native setup is unavailable

Development and testing

Install dependencies with npm ci, then run npm run typecheck and npm run compile. Press F5 to open an Extension Development Host.

npm test compiles the extension and runs the MCP boundary, Jupyter integration, broker, kernel-file, desktop-adapter and Colab-setup suites. npm run coverage merges their c8 coverage and enforces statements/lines >=75%, branches >=55%, and functions >=85%. The tests use shims without downloading VS Code; live provider checks are separate. CI runs on Ubuntu, Windows, and macOS.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Exposes Jupyter notebook operations as MCP tools over plain HTTP, allowing AI agents to explore, edit, run, and debug notebooks in a live VS Code session using local or remote kernels.
    3
    MIT