|
| 1 | +# Notebook MCP Server - Architectural Design Rationale |
| 2 | + |
| 3 | +This document outlines the design necessities, modes of operation, crucial architectural decisions, and limitations of the Notebook MCP server. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Overview & Purpose |
| 8 | + |
| 9 | +The Notebook MCP (Model Context Protocol) server acts as a bridge between Agentic AI capabilities and Jupyter Notebook operations (read, write, execute). Its primary goal is to allow AI agents to safely inspect, edit, and execute code within notebooks, whether running in a standalone local environment or deeply integrated with an active IDE session. |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +## Design Necessity: Zero-Config Static Distribution |
| 14 | + |
| 15 | +One of the major necessities of this design is to ensure we can have a **simple, static `mcpserver` configuration** that we can ship directly with our Agent CLI plugins. |
| 16 | + |
| 17 | +We plan to support plugins for various CLI agents, and in those plugins, the CLI agents only support static `mcpserver` configurations. Automating environment setups for end users requires that they do not need to clone repositories manually or configure absolute system paths. The desired configuration looks like this: |
| 18 | + |
| 19 | +```json |
| 20 | +"notebook-tools": { |
| 21 | + "command": "npx", |
| 22 | + "args": [ |
| 23 | + "-y", |
| 24 | + "github:gemini-cli-extensions/data-cloud-extension" |
| 25 | + ] |
| 26 | +} |
| 27 | +``` |
| 28 | + |
| 29 | +### How Our Solution Solves This |
| 30 | +To support this static configuration running via `npx` (which downloads the code to a random sandbox cache directory), we: |
| 31 | +1. **Bundled the Proxy:** We placed the compiled `mcp_proxy_bundle.cjs` directly inside the repository (`notebook_mcp/bin/`). |
| 32 | +2. **Relative Path Resolution:** The server computes the path to the proxy dynamically using `import.meta.url` (ESM mode). |
| 33 | +This allows the server to find its own resources inside the random `npx` cache without any absolute path strings being passed from the host machine. |
| 34 | + |
| 35 | +--- |
| 36 | + |
| 37 | +## Modes of Operation |
| 38 | + |
| 39 | +The server supports two distinct modes depending on how the CLI agent is being executed: |
| 40 | + |
| 41 | +### 1. CLI Agent running in a Standalone Terminal |
| 42 | +* **Context:** The user opens a normal terminal and runs the agent. No IDE extension is wrapping the execution. |
| 43 | +* **Tools Served:** The server serves **Standalone Tools**. These tools directly read and write `.ipynb` files on the disk using Node filesystem APIs. |
| 44 | +* **Scope:** Can only target saved files. Cannot see or manipulate unsaved memory buffers in an IDE tab. |
| 45 | + |
| 46 | +### 2. CLI Agent running in an IDE's Terminal |
| 47 | +* **Context:** The user triggers the agent from within an extension tab or a terminal spawned by the IDE (like VS Code, Antigravity, or Jetski). |
| 48 | +* **Tools Served:** The server serves **Proxied Tools**. It acts as a client to a domain socket/named pipe opened by the IDE extension, forwarding all tool calls to the IDE. |
| 49 | +* **Scope:** Powerful. Can see unsaved changes in active tabs, trigger executions in the IDE kernel, and interact with visual elements. |
| 50 | + |
| 51 | +--- |
| 52 | + |
| 53 | +## Key Features & Tools |
| 54 | + |
| 55 | +The server exposes a rich set of tools for notebook manipulation: |
| 56 | +* **Cell CRUD Operations:** Add, update, delete, and list cells in a notebook. |
| 57 | +* **Content Inspection:** Retrieve the full content of specific cells or the entire notebook. |
| 58 | +* **Execution (IDE Mode only):** Trigger execution of cells and capture results via the IDE's active kernel. |
| 59 | + |
| 60 | +--- |
| 61 | + |
| 62 | +## Resilience Standalone Fallback |
| 63 | + |
| 64 | +To ensure the agent always has access to tools, the server implements a fallback mechanism: |
| 65 | +* When triggered in IDE mode, it attempts to connect to the IDE's domain socket. |
| 66 | +* It retries **5 times** with a **1-second delay**. |
| 67 | +* If the connection fails (e.g., extension not yet loaded or IDE not running), it **automatically falls back** to serving the Standalone Tools over stdio. |
| 68 | +* This guarantees a zero-freeze experience for the user. |
| 69 | + |
| 70 | +--- |
| 71 | + |
| 72 | +## 🔒 Limitations of Standalone Mode |
| 73 | + |
| 74 | +Users and developers must be aware of the strict boundaries of the Standalone Mode: |
| 75 | +* **No IDE API Access:** Standalone tools run purely at the filesystem child-process level. They have **no access to VS Code or IDE platform APIs**. |
| 76 | +* **Saved Files Only:** They cannot read or write to unsaved memory buffers in the user's editor. They only process the **last saved copy** found on disk. Unsaved edits in the IDE will be invisible to the standalone tools until the user saves them. |
| 77 | + |
| 78 | +--- |
| 79 | + |
| 80 | +## Crucial Design Decisions |
| 81 | + |
| 82 | +### Decision 1: Target Detection (Process Tree vs Env Vars) |
| 83 | + |
| 84 | +To decide whether to connect to the socket (IDE mode) or fall back to standalone file manipulation. |
| 85 | + |
| 86 | +* **Option A: Process Tree Inspection** |
| 87 | + * **Pros:** Theoretically zero-config; doesn't rely on terminal environment inheritance. |
| 88 | + * **Cons:** Fragile. Deeply complex to match random wrapper shell names. Fails if intermediate tools obscure the process ancestry. |
| 89 | +* **Option B: Environment Variables** |
| 90 | + * **Pros:** 100% accurate. Simple. Zero overhead. |
| 91 | + * **Cons:** Relies on the host IDE extension putting the environment variables (like `DATA_CLOUD_CURR_IDE_NAME`) into the spawned terminal session. |
| 92 | +* **Choice:** **Environment Variables**. Since our IDE extension natively spawns the agent terminals, it easily feeds the required indicator tag. We fell back to Standalone Mode if the variable was missing. |
| 93 | + |
| 94 | +### Decision 2: Location of the Proxy Bundle |
| 95 | + |
| 96 | +How should the server locate the `mcp_proxy_bundle` script needed to connect to the socket? |
| 97 | + |
| 98 | +* **Option A: Read from the local machine path (passed by env var)** |
| 99 | + * **Pros:** Ensures the proxy matches exactly the version of the extension installed on the machine. |
| 100 | + * **Cons:** Breaks zero-config `npx` setups. Requires the extension to find its own installation path and pass it down. |
| 101 | +* **Option B: Bundle the Proxy in the Repository** |
| 102 | + * **Pros:** Completely self-contained. Works immediately with `npx` retrieval. |
| 103 | + * **Cons:** Requires engineering to maintain synchronization between the extension changes and repository commits. |
| 104 | +* **Choice:** **Bundle in the Repository**. The benefit of instant zero-configuration execution via `npx` outweighed the burden of occasional synchronization maintenance. |
| 105 | + |
| 106 | +--- |
| 107 | + |
| 108 | +## Security Constraints |
| 109 | + |
| 110 | +* **No Process Scanning:** The server does not scan the host machine's process tree for forensics, protecting system privacy and avoiding environment permission blocks. |
| 111 | +* **Isolated Env Tags:** It strictly obeys the isolated environment variables explicitly declared and passed to it by the IDE. |
| 112 | + |
| 113 | +### Security of `execute_cell` Tool |
| 114 | + |
| 115 | +The `execute_cell` tool involves running code on the user's compute resources, requiring a strict security paradigm: |
| 116 | + |
| 117 | +* **IDE Exclusive:** The standalone MCP server does **not** offer this tool. It is exclusively provided by the MCP server running in the IDE through the Data Cloud extension. |
| 118 | +* **Server-Side Enforcement:** Operations obey all security checks put in place on the server side by the Data Cloud extension. |
| 119 | +* **Opt-In by Default:** The tool is **disabled by default** and not served by the MCP server to its clients until the user manually enables it in the extension setting panel. |
| 120 | +* **Runtime Verification:** The server checks for this enablement setting even when running the tool! If a tool is disabled after being enabled (and the user did not refresh the window for the tool to be formally removed from the served registry), the tool will actively refuse execution. |
| 121 | +* **Explicit Elicitation (User Consent):** |
| 122 | + * We support **elicitation** for the `execute_cell` tool on a best-effort basis (depending on MCP client compatibility). |
| 123 | + * **Client Compatibility:** If the third-party MCP client does not support prompt/consent elicitation bridges, the tool will **not work**. |
| 124 | + * **Forced Consent Prompts:** If the client supports elicitation, the system will ask for **explicit user consent** each time it is called by the agent (even if the agent is operating in autonomous "YOLO" mode). |
| 125 | + * **Override Toggle:** There is a secondary toggle setting in the extension that can be used to disable these forced execution elicitations. However, elicitations are strictly **on by default**. |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +## Development & Maintenance Guide |
| 130 | + |
| 131 | +### Building the Bundle |
| 132 | +The project uses `esbuild` to compile `server.ts` into a single executable file at `dist/index.js`. |
| 133 | +```bash |
| 134 | +npm run bundle |
| 135 | +``` |
| 136 | + |
| 137 | +### ESM vs CommonJS |
| 138 | +* The server uses **ESM (ES Modules)** for cleaner modern execution chains and deterministic mapping via `import.meta.url`. |
| 139 | +* The bundled proxy (`mcp_proxy_bundle.cjs`) retains **CommonJS** mapping using Node's `.cjs` instruction override to prevent evaluation collisions in Node's module system. |
0 commit comments