Skip to main content
Glama
xability

maidr-mcp

Official
by xability

maidr-mcp

An MCP server that shows accessible maidr charts inside ChatGPT and Claude conversations, and lets the conversation's model move the reader through them.

The model calls show_chart with the data, and the chart appears in the conversation as an MCP App. A blind or low-vision reader Tabs into it and explores it the way they explore any maidr chart: arrow keys, screen reader, sonification, braille. Two things then happen through the model:

  • The model can take the reader somewhere. When the reader asks for "the highest bar", the model calls maidr_get_layer_data to find it and maidr_navigate to move there, and maidr announces the point by speech, braille and sound.

  • The model knows where the reader is. As the reader moves, the chart tells the model their position, so "what is this point?" needs no tool call.

Experimental. Checked end to end in the MCP Apps SDK's reference host (below). Not yet checked in claude.ai or ChatGPT.

How it works

  1. show_chart draws the chart on the server with matplotlib and py-maidr, so the model sends numbers rather than an SVG. The SVG reaches the chart view in the tool result's _meta, which is not added to the model's context.

  2. The view loads maidr.js and the MCP Apps SDK from cdn.jsdelivr.net, the only domain its CSP declares, at pinned versions.

  3. maidr.js registers its own WebMCP tools on document.modelContext. No host hands a view's tools to the model yet (ext-apps#797), so the view supplies document.modelContext itself. Each server tool of the same name relays its call to the view that is open:

    • the model calls the server tool;

    • the view long-polls an app-only tool, picks up the call, runs maidr's tool, and posts maidr's answer back;

    • that answer becomes the model's tool result.

    If hosts adopt WebMCP for MCP Apps, as ext-apps#798 proposes, maidr's tools reach the model directly and the relay can go.

  4. On each key the reader presses in the chart, the view sends their position with ui/update-model-context.

Related MCP server: Accessibility MCP Server

Tools

Tool

Called by

Does

show_chart

model

Draws the chart and shows it. Returns the viewId the other tools take.

maidr_list_charts

model

Returns the chart's layers, point counts, and where the reader is. Silent.

maidr_get_layer_data

model

Returns a page of a layer's points, each with the target that maidr_navigate takes. Silent.

maidr_navigate

model

Moves the reader to a point and announces it.

maidr_view_poll, maidr_view_reply, maidr_view_svg

the chart view only

Carry the relay, and the SVG for hosts that drop _meta.

show_chart takes one of these chart types. Each maps onto the maidr layer type shown:

type

Fields

maidr layer

bar

categories, series (one series, or several side by side, or stacked)

bar, dodged_bar, stacked_bar

line

x (numbers or labels), series

line

scatter

x, y

point

histogram

values, bins

hist

box

groups

box

heatmap

x_labels, y_labels, values, z_label

heat

Every type also takes title, x_label and y_label.

Use it

What the server needs depends on how the host reaches it:

Host

How it connects

What the server needs

Claude (web, Desktop, mobile)

a custom connector, which takes a remote MCP server

a public HTTPS address

ChatGPT

a developer-mode app

a public HTTPS address, or Secure MCP Tunnel to your own machine

ChatGPT desktop app

its own MCP servers, started over STDIO

nothing hosted; see below for what is untested

With a public address

uvx --from git+https://github.com/xability/maidr-mcp maidr-mcp --host 0.0.0.0 --port 8000
# or
docker build -t maidr-mcp . && docker run -p 8000:8000 maidr-mcp

The endpoint is /mcp, over Streamable HTTP.

  • Run one instance. The relay keeps each chart's queue in memory.

  • Restarts are tolerated. An open chart registers itself again on its next poll after a restart.

Claude. Add a custom connector: Customize > Connectors > Add custom connector, with https://<your-host>/mcp.

  • Plans: custom connectors work on every plan; Free allows one.

  • Team and Enterprise: an Owner adds it.

  • Where charts render: on web, Desktop, and iOS/Android once the connector is added.

ChatGPT. Turn on developer mode, then create an app for https://<your-host>/mcp. Developer mode is available on the web for Plus, Pro, Business, Enterprise and Education accounts.

ChatGPT without a public address

Your own machine, through Secure MCP Tunnel. ChatGPT calls an app's MCP server from OpenAI's side, so it cannot reach localhost directly. OpenAI's Secure MCP Tunnel connects it to a server that stays off the internet:

  • Server: runs on your machine, either maidr-mcp (HTTP at 127.0.0.1:8000/mcp) or maidr-mcp --stdio.

  • Tunnel client: OpenAI's open-source client runs next to the server and opens only outbound HTTPS to OpenAI. It needs a tunnel_id from the Platform's tunnel settings and a runtime API key.

  • App: you add it in developer mode through the tunnel, as that guide describes.

  • Uptime: the machine has to stay on while the chart is in use.

The ChatGPT desktop app's own MCP servers. The desktop app can start a local server itself. Open Settings > MCP servers > Add server, choose STDIO, and give it this command, which needs uv:

uvx --from git+https://github.com/xability/maidr-mcp maidr-mcp --stdio

The desktop app shares this configuration with the Codex CLI and IDE extension. There are two caveats:

  • Whether the app draws an MCP App's UI for a server added this way is not documented.

  • This route has not been tried with maidr-mcp, so it is not known whether the chart appears.

Try it in the reference host

e2e/run.sh checks the whole loop without a ChatGPT or Claude account. It does three things:

  1. Builds the reference host from ext-apps (examples/basic-host, at tag v2.0.3).

  2. Starts this server.

  3. Drives both with Playwright the way a model and a reader would.

cd e2e && npm install && npx playwright-core install chromium && cd ..
bash e2e/run.sh

It checks:

  • the chart appears;

  • the model's calls are answered by maidr inside the chart;

  • a move made while the reader is in the chat waits, and is announced when they Tab in;

  • the arrow keys announce, and the reader's position reaches the host as model context;

  • a move made while the reader is in the chart is announced at once;

  • there are no console errors or CSP violations.

Limits

  • The model learns the reader's position one turn late. It sees the position on its next turn, not in the middle of one.

  • Moves wait until the reader is in the chart. maidr never moves focus. A move made while the reader is typing to the model is kept, and announced when they Tab back into the chart. maidr_navigate says so (applied: "on-next-focus"), and the model should tell them.

  • Each show_chart call adds a chart. Claude mounts a new view for every call, and earlier views stay.

  • The relay keeps a request open. The view's poll holds a request for up to 20 seconds. Whether a given host limits calls made from a view is not yet known.

  • There is no authentication. Anyone with the server's URL can draw charts. A chart can only be read or driven with its viewId, a random 24-character token.

  • Six chart families so far. The server does not take plotting code.

Network and data

  • The server: receives the data the model sends to draw a chart. It keeps the chart's SVG in memory until the chart has gone 15 minutes without polling, and logs nothing about the data beyond the HTTP access log.

  • The chart view: loads maidr.js and the MCP Apps SDK from cdn.jsdelivr.net. maidr's own AI chat inside the chart works as it does anywhere else, with a key the reader adds.

Development

uv sync
uv run ruff check . && uv run ruff format --check .
uv run pytest
bash e2e/run.sh

License

GPL-3.0-or-later, like the rest of maidr.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables creating interactive data visualizations from natural language queries using DuckDB for local databases or Databricks for enterprise data warehouses. Supports multiple chart types, CSV imports, SQL queries, and automatic statistical analysis through Claude Desktop.
    19
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables interactive Google Analytics 4 visualizations inside Claude conversations, supporting ad-hoc queries with line, bar, pie charts and data tables.
    -
  • A
    license
    B
    quality
    D
    maintenance
    Enables AI agents to render branded charts as inline images and persistent hosted URLs, supporting explicit chart types and automatic chart suggestion from data.
    2
    43 npm
    MIT