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.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNo
categoryNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It usefully discloses that query mode caps results at 10 and that each mode returns a different shape, but omits what happens when query and category are both supplied, empty-result behavior, and whether query is exact or fuzzy.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One purpose sentence followed by three tightly parallel mode clauses, each earning its place with no filler. The purpose is front-loaded before the mode breakdown.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-value explanation is not required, and the description covers all three call modes for a two-optional-parameter tool. The remaining gap is precedence/conflict handling when both parameters are passed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the two parameters are undocumented structurally, and the description only partly compensates by describing their effect ('top 10 matching examples', 'lists examples in that category'). It never states accepted string formats or matching semantics for query, nor valid category names.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (find) and resource (D3.js examples from the Observable gallery), which is immediately actionable. It does not, however, differentiate itself from the sibling get_example, leaving the agent to guess whether find and get return different things.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells the agent which invocation mode to choose: no arguments for categories, query for matching examples, category for listing. It stops short of stating when to prefer this tool over the sibling get_example or search_docs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_moduleA

Find D3.js modules by keyword search.

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

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It discloses the no-query vs query behavior and result limit (top 5), which is useful. Missing details on permissions, rate limits, or error handling.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with purpose and then conditional behavior. No extraneous text, though could be more structured with bullet points.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter tool with an output schema, the description covers the essentials: what it does, how query affects results, and result count. Lacks guidance on when to use vs siblings.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so description must compensate. It explains the query parameter's behavior (none lists all, query returns top 5), adding meaning beyond the bare schema. However, it doesn't specify search semantics (e.g., case sensitivity, partial matches).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb+resource: 'Find D3.js modules by keyword search' is clear. However, it doesn't distinguish itself from siblings like search_docs or find_example beyond the resource type.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Implies usage via the query condition but offers no explicit when-to-use vs alternatives. Siblings exist for similar searches, yet no routing guidance is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageNo
module_nameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden. For a documentation-fetch tool this is low-risk, but the description says nothing about lookup behavior, whether unknown module names error or return empty, or any fallback behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, the core purpose front-loaded, and each remaining sentence maps to one parameter. No wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With only 2 parameters, an output schema present, and the description covering both the required and optional argument's formats, an agent has enough to invoke this correctly. Only the routing vs. search_docs remains thin.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and it does: it explains module_name accepts a prefixed or bare form ('d3-scale' or 'scale') and gives a concrete page example ('linear'). It omits whether page is optional only with module_name or how the two interact on misses, so it is not a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get D3.js API documentation'), which clearly sets it apart from find_example/get_example siblings. It does not explicitly contrast itself with search_docs, the closest sibling, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives implicit routing guidance by explaining that module_name yields the overview and adding page yields a sub-page, which implies when to supply each. However, it never says when to use this tool versus search_docs or find_module (e.g. when the module name is unknown), leaving the main routing decision unstated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses meaningful behavior beyond the schema: the code comes from Observable notebooks and may contain non-vanilla patterns, with concrete adaptation guidance for `invalidation`, `FileAttachment`, and imported helpers. It does not cover failure behavior for a bad path, but the substantive gotcha disclosure is strong.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core action, then path format, then the discovery alternative, then adaptation notes. The adaptation bullet list is somewhat long but each bullet addresses a real portability pitfall, so it earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and the description instead covers the path format and the Observable-to-vanilla adaptation caveats. Nearly complete; only invalid-path/error behavior is unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and it does: it gives the expected format with a concrete example ('@d3/bar-chart/2') including a version segment. This is far more useful than the bare string type in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get D3.js example source code from an Observable notebook') and clearly distinguishes itself from search_docs/get_docs siblings. The agent can tell immediately this is a retrieval-by-path tool, not a discovery tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes discovery to a named alternative: 'Use find_example() to discover available examples,' which implies you must have a path before calling this. It does not state an explicit when-not-to-use case, but the dependency on a known path is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes
module_nameNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It usefully discloses that the search operates over page content and returns matching sections, implying a read-only, non-destructive operation, but says nothing about result limits, ordering, or whether the search is fuzzy or exact.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the core action, with no filler. Slight redundancy between 'for specific topics or methods' and 'searches page content for matching sections' costs it the top mark.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and the description covers both parameters and the retrieval unit. It remains incomplete only in tool-selection context relative to its four siblings, which would be the last missing piece.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description is the only source of meaning. It clarifies module_name as an optional single-module scoping filter, which the schema's bare anyOf string/null does not convey, but adds nothing about query syntax, matching semantics, or the default null behavior.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource (search D3.js documentation) plus the unit of retrieval (matching sections), which is more than a tautology. It does not explicitly distinguish itself from sibling find_module or get_docs, so an agent must infer the boundary from names alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use or when-not-to-use guidance and no sibling is named as an alternative. The only conditional ('optionally restrict to a single module') is a parameter note, not routing guidance, so the agent must guess whether to reach for search_docs versus get_docs or find_example.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 5 tool updatesv0.1.0
    • First observedfind_example
    • First observedfind_module
    • First observedget_docs
    • First observedget_example
    • First observedsearch_docs

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 issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    D
    maintenance
    Provides AI models with direct access to documentation for over 600 technologies from DevDocs.io, including popular languages, frameworks, and tools. It enables comprehensive searching, content retrieval, and offline access via an intelligent local caching system.
    12
    2
    -
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI assistants to search, list, and retrieve documentation from the Slice.js official GitHub repository. It provides full-text search capabilities and can deliver individual doc pages or a complete documentation bundle for comprehensive LLM context.
    4
    8 npm
    5
    MIT