Observable Notebook Kit Debug MCP Server
Enables AI assistants to inspect values, view errors, and capture canvas output from Observable Notebook Kit notebooks running in a web browser.
README
mcp-observable-notebook-kit-debug
MCP server for debugging Observable Notebook Kit notebooks.
Enables AI assistants to inspect values, view errors, and capture canvas output from notebooks running in a web browser. For a fully working example, see ./example.
Why?
The Observable Desktop is really cool, but it had some limitations. For one, okay, so I paid Anthropic some money for Claude Code, but it's not the right Anthropic integration to be able to configure an API key in the app, so I'm out of luck. And I was trying to get some WebGPU experiments running, but the app didn't have access to a WebGPU context. So one thing led to another, and I wrote a quick MCP server to exfiltrate values from notebooks running in the browser.
Setup
1. Install the package
npm install @rreusser/mcp-observable-notebook-kit-debug
2. Add the Vite plugin
In your vite.config.js:
import { defineConfig } from "vite";
import { observable, config } from "@observablehq/notebook-kit/vite";
import { debugNotebook } from "@rreusser/mcp-observable-notebook-kit-debug";
export default defineConfig({
...config(),
plugins: [debugNotebook(), observable()],
});
3. Run the dev server
vite -c vite.config.js
4. Configure the MCP server
Add to your .mcp.json:
{
"mcpServers": {
"Notebook": {
"command": "mcp-notebook-kit-debug",
"args": []
}
}
}
Note that you might need an absolute path to the mcp-notebook-kit-debug bin, and you might be using opencode or some other tool, so this step could vary a bit.
5. Go!
You can now use an agent like Claude Code to poke and prod at notebooks running in a web browser, inspecting values and even capturing canvas output as images.
MCP Tools
Observable Runtime Tools
These tools interact directly with the Observable runtime's reactive graph.
| Tool | Description |
|---|---|
ListValues |
List all named values in the Observable runtime's reactive graph |
GetValue |
Get a value from the runtime by name; returns images for Canvas/SVG elements |
GetValues |
Get multiple values at once (snapshot of runtime state) |
GetValueMetadata |
Get metadata: state, type, dependencies (inputs), and dependents (outputs) |
GetDependencyGraph |
Get the dependency graph showing how values depend on each other |
SetInputValue |
Set an input widget's .value property and trigger reactive updates |
RuntimeEval |
Evaluate an expression with access to all notebook variables |
Browser Tools
These tools interact with the browser/DOM context, outside the Observable runtime.
| Tool | Description |
|---|---|
BrowserEval |
Execute JavaScript in the browser (has DOM access but NOT Observable runtime) |
GetElementContent |
Get content from a DOM element by CSS selector; captures canvas/SVG as images |
MouseClick |
Simulate a mouse click at a position or on an element |
MouseDrag |
Simulate a mouse drag from start to end position |
MouseHover |
Simulate mouse hover at a position, triggering hover states and tooltips |
MouseWheel |
Simulate a mouse wheel scroll at a position |
SendKeys |
Simulate keyboard input to an element |
Session Tools
These tools manage notebook connections and debug sessions.
| Tool | Description |
|---|---|
ListNotebooks |
List all connected notebooks (use when multiple notebooks are open) |
FocusNotebook |
Set a default notebook for subsequent commands (when multiple are connected) |
Refresh |
Refresh the page and wait for notebook initialization |
Navigate |
Navigate a notebook to a different URL (e.g., switch from notebook-a to notebook-b) |
GetConsoleMessages |
View console logs from the current session |
GetErrors |
Get all errors (DOM-reported and values in rejected state) |
Multi-Notebook Support
When multiple notebooks are open in different browser tabs, you can target a specific notebook using the notebook parameter on any tool:
# By path (without .html extension)
notebook: "index"
notebook: "second-notebook"
# By index
notebook: "0"
notebook: "1"
# By URL
notebook: "http://localhost:5173/"
If multiple notebooks are connected and you don't specify which one, the tool will return an error listing the available notebooks.
Use ListNotebooks to see all connected notebooks with their URLs and indices.
Navigating Between Notebooks
The Navigate tool allows you to navigate an open notebook to a different URL, or open a URL in your default browser if no notebooks are connected:
// If no notebooks are connected, opens URL in default browser
Navigate({ url: "http://localhost:5173/index.html" })
// Navigate to a different notebook (relative URL)
Navigate({ url: "/second-notebook.html" })
// Navigate using absolute URL
Navigate({ url: "http://localhost:5173/second-notebook.html" })
// Navigate a specific notebook (when multiple are open)
Navigate({ url: "/second-notebook.html", notebook: "index" })
// Skip waiting for page load (useful if target page doesn't have debug plugin)
Navigate({ url: "/some-page.html", wait_for_completion: false })
Behavior:
- No notebooks connected: Opens the URL in your OS default browser
- One notebook connected: Navigates that notebook's browser window to the new URL
- Multiple notebooks connected:
- Navigates the focused notebook (if you've used
FocusNotebook) - Navigates the specified notebook (if
notebookparameter provided) - Returns an error listing all notebooks (if no focus is set and no notebook specified)
- Navigates the focused notebook (if you've used
The tool waits for the new page to load and initialize, similar to Refresh, and reports any errors that occur during initialization.
Value States
Values in an Observable notebook can be in one of three states:
- fulfilled: The value has been computed successfully
- pending: The value is still being computed (e.g., async/Promise)
- rejected: The computation threw an error
The GetValue and GetValues tools return state information along with the value or error.
Image Support
GetValue automatically returns images inline for:
- Canvas elements: Captured as PNG
- SVG elements: Rendered to canvas and captured as PNG
No need to save to files - images are returned directly in the MCP response.
Runtime Evaluation
Use RuntimeEval to evaluate expressions in the Observable runtime context with access to all notebook variables. The body must use a return statement.
// Compute derived values from notebook variables
RuntimeEval({ body: "return number * 2 + rangeValue" })
// Filter or transform data
RuntimeEval({ body: "return data.filter(d => d.value > 0)" })
// Multi-statement expressions
RuntimeEval({
body: `
const doubled = number * 2;
const added = doubled + rangeValue;
return { doubled, added };
`
})
// Persist the result as a named variable for later retrieval
RuntimeEval({
name: "myResult",
body: "return number * 2"
})
Dependencies are auto-detected from the expression. If name is provided, the result persists in the runtime and can be retrieved with GetValue. If name starts with _tmp_, it is automatically deleted after the value resolves.
Setting Input Values
Use SetInputValue to programmatically change the value of interactive input widgets created with Inputs.* (e.g., Inputs.range, Inputs.select, Inputs.text). This sets the widget's .value property and dispatches an input event, triggering reactive updates to dependent values.
// If the notebook has: slider = Inputs.range([0, 100])
SetInputValue({ name: "slider", value: 50 })
// If the notebook has: dropdown = Inputs.select(["A", "B", "C"])
SetInputValue({ name: "dropdown", value: "B" })
Mouse Interaction
Simulate mouse events for testing interactive visualizations:
MouseClick: Click at coordinates or on an element (supports left/middle/right buttons)MouseDrag: Drag from start to end position with configurable durationMouseHover: Hover at a position, dispatching mouseenter/mouseover/mousemove eventsMouseWheel: Scroll at a position with deltaX/deltaY
All mouse tools accept an optional selector parameter to target a specific element, with coordinates relative to that element.
Keyboard Interaction
Use SendKeys to simulate keyboard input:
// Type plain text
SendKeys({ keys: "hello world" })
// Use special keys with braces
SendKeys({ keys: "{Enter}" })
SendKeys({ keys: "{Tab}" })
SendKeys({ keys: "{ArrowDown}" })
// Modifier combinations
SendKeys({ keys: "{Ctrl+a}" }) // Select all
SendKeys({ keys: "{Ctrl+c}" }) // Copy
SendKeys({ keys: "{Shift+Tab}" }) // Reverse tab
// Target a specific element
SendKeys({ selector: "#my-input", keys: "typed text{Enter}" })
// Hold modifiers for all keys
SendKeys({ keys: "abc", modifiers: { shiftKey: true } }) // Types "ABC"
Supported special keys: {Enter}, {Tab}, {Escape}, {Esc}, {Backspace}, {Delete}, {Insert}, {Space}, {ArrowUp}, {ArrowDown}, {ArrowLeft}, {ArrowRight}, {Home}, {End}, {PageUp}, {PageDown}, {F1}-{F12}.
License
© 2026 Ricky Reusser. MIT License.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。