Observable Notebook Kit Debug MCP Server

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.

Category
Visit Server

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 notebook parameter provided)
    • Returns an error listing all notebooks (if no focus is set and no notebook specified)

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 duration
  • MouseHover: Hover at a position, dispatching mouseenter/mouseover/mousemove events
  • MouseWheel: 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.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured