Skip to main content
Glama

d3-mcp-server

MCP server that provides D3.js API documentation and example code to AI agents. Fetches docs from d3js.org and examples from the Observable D3 gallery, serving them through searchable tools.

Setup

Requires Python 3.14+ and uv.

Claude Code

claude mcp add -t stdio -s user d3 -- uvx --from git+https://github.com/jakeb-grant/d3-mcp-server d3-mcp-server

Claude Desktop / Cursor / etc.

{
  "mcpServers": {
    "d3": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/jakeb-grant/d3-mcp-server", "d3-mcp-server"]
    }
  }
}

Updating

uvx caches the installed version. To pull the latest, clear the cache and re-run:

uv cache clean d3-mcp-server

MCP Inspector

uv run fastmcp dev inspector d3_mcp_server/server.py --with-editable .

Related MCP server: Vega-Lite MCP Server

Tools

find_module(query?)

Discover D3 modules. Without a query, lists all 30 modules. With a query, returns the top 5 matches ranked by name, tag, and description relevance.

find_module()                → list all modules
find_module("scale")         → modules related to scales
find_module("color")         → d3-color, d3-scale-chromatic, d3-interpolate

get_docs(module_name, page?)

Fetch documentation for a module. Returns the module overview by default, or a specific sub-page.

get_docs("d3-scale")              → scale overview + list of sub-pages
get_docs("scale")                 → same (short names work)
get_docs("d3-scale", "linear")    → linear scales sub-page
get_docs("d3-array", "ticks")     → ticks sub-page

search_docs(query, module_name?)

Search documentation content for specific topics or methods. Optionally scope to a single module.

search_docs("scaleLinear")              → searches across relevant modules
search_docs("domain", "d3-scale")       → searches within d3-scale only

find_example(query?, category?)

Browse and search ~170 D3 examples from the Observable gallery across 14 categories (Bars, Lines, Maps, Hierarchies, etc.).

find_example()                    → list all categories with counts
find_example(query="treemap")     → search examples by keyword
find_example(category="Bars")     → list all examples in a category

get_example(path)

Fetch example source code from an Observable notebook. Extracts clean, standalone D3 code from the notebook format, along with a description and data file URLs.

get_example("@d3/bar-chart/2")           → bar chart source code
get_example("@d3/force-directed-graph/2") → force-directed graph source

Architecture

d3_mcp_server/
├── __init__.py    # Entry point (main)
├── server.py      # FastMCP server, tools, resource template
├── modules.py     # D3Module model + 30-module registry
├── examples.py    # Observable gallery scraping + notebook code extraction
├── cache.py       # File cache (~/.cache/d3-mcp-server/) + HTML→markdown
├── search.py      # Module scoring + markdown section parsing/search
└── sync.py        # Registry drift detection (see below)
tests/
├── test_examples.py # Gallery parsing, scoring, notebook extraction tests
├── test_search.py   # Search, parsing, module resolution tests
├── test_cache.py    # HTML conversion, caching, fetch error handling
└── test_server.py   # Tool integration tests

Doc pages are fetched from d3js.org, stripped to <main class="main"> content, converted to markdown via markdownify, and cached to disk with a 24-hour TTL.

Registry Sync

The module registry in modules.py is hardcoded. If d3js.org adds, removes, or renames modules or pages, the registry will drift. A sync utility detects this.

Check for drift

uv run python -m d3_mcp_server.sync

This scrapes the d3js.org/api sidebar and compares it against the hardcoded D3_MODULES list, reporting:

  • New modules on d3js.org not in the registry

  • Modules in the registry that no longer exist on d3js.org

  • New sub-pages added to existing modules

  • Sub-pages in the registry that no longer exist on d3js.org

Updating the registry

When drift is detected, update d3_mcp_server/modules.py following these rules:

D3_MODULES is the list to edit. Each entry is a D3Module with:

  • name — module name exactly as shown on d3js.org (e.g. "d3-array")

  • description — short summary (copy from the d3js.org sidebar or module index page)

  • tags — lowercase keywords for search scoring (include the short name, key API terms, and related concepts)

  • pages — ordered list of page paths; first entry is always the module index page ("/d3-array"), followed by sub-pages ("/d3-array/ticks")

For new modules, add a D3Module entry to D3_MODULES. Place it alphabetically among existing entries or group it with related modules. Populate tags with the short module name and 3-8 relevant keywords from the module's description and API methods.

For removed modules, delete the entire D3Module entry.

For new pages, append the page path to the module's pages list. The path format is "/{module_name}/{page_slug}" where the slug matches the d3js.org URL.

For removed pages, delete the page path from the module's pages list.

After editing, verify:

uv run python -m d3_mcp_server.sync   # should report no drift
uv run pytest tests/ -v               # all tests should pass
uvx ruff check d3_mcp_server/ tests/  # no lint errors

Development

uv run pytest tests/ -v                              # run tests
uvx ruff check d3_mcp_server/ tests/                 # lint
uvx ruff format d3_mcp_server/ tests/                # format
uv run python -m d3_mcp_server.sync                  # check registry drift
uv run fastmcp dev inspector d3_mcp_server/server.py --with-editable .  # MCP inspector

Available Tools

5 tools
find_exampleA

Find D3.js examples from the Observable gallery.

Without arguments, lists all categories with counts. With query, returns top 10 matching examples. With category, lists examples in that category.

Inputs: query, category. Returns: result

find_moduleA

Find D3.js modules by keyword search.

Without a query, lists all modules. With a query, returns top 5.

Inputs: query. Returns: result

get_docsA

Get D3.js API documentation.

Provide module_name (e.g. "d3-scale" or "scale") for the overview. Add page (e.g. "linear") to get a specific sub-page.

Inputs: module_name (required), page. Returns: result

get_exampleA

Get D3.js example source code from an Observable notebook.

Provide the example path (e.g. "@d3/bar-chart/2"). Use find_example() to discover available examples.

Code is extracted from Observable notebooks and may contain Observable-specific patterns. For vanilla D3, adapt these:

  • invalidation — remove or replace with cleanup logic

  • FileAttachment(...) — use fetch/d3.csv/d3.json with the data URLs listed below the code

  • Imported helpers (e.g. Legend) — source notebook URLs are listed; fetch or inline as needed

Inputs: path (required). Returns: result

search_docsB

Search D3.js documentation for specific topics or methods.

Searches page content for matching sections. Optionally restrict to a single module with module_name.

Inputs: query (required), module_name. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 5 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose: get_docs retrieves module/page documentation, search_docs searches documentation content, find_module discovers modules, find_example lists examples, and get_example fetches example source. No two tools overlap enough to cause misselection.

    Naming Consistency5/5

    All tool names follow a consistent snake_case verb_noun pattern (get_docs, search_docs, find_example, get_example, find_module), with verbs that clearly indicate the action.

    Tool Count5/5

    Five tools are well-scoped for a D3.js documentation and example retrieval server. Each tool covers a distinct discovery or retrieval need, with no redundant or missing core operations.

    Completeness4/5

    The surface covers documentation retrieval/search, module discovery, example discovery, and example source retrieval, which is strong for the stated purpose. A minor gap exists in retrieving module-level metadata such as full method indexes or version info, but agents can work around this via get_docs and search_docs.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers