Skip to main content
Glama
README.md
# echarts-mcp

A modern, open-source **Apache ECharts MCP server**. It exposes type-safe tools that let AI
agents and chat UIs render rich, deterministic charts server-side — no browser. Emits SVG,
PNG, and self-contained interactive HTML (PNG needs the optional `@napi-rs/canvas` dep).

Built against the **Model Context Protocol v2 SDK** (`@modelcontextprotocol/server@2`) and
**Apache ECharts 6.1.0** (pinned), supporting both **stdio** and **Streamable HTTP** transports.

## Highlights

- **Multi-format rendering** — one call returns any combination of `svg` (raw vector
  text, zero native deps), `png` (base64 image, via optional `@napi-rs/canvas`), and
  `html` (a self-contained interactive page). Deterministic by default (`animation: false`).
- **Fully stateless** — every tool result is self-contained (raw SVG/PNG/HTML + echoed
  option). No files, no session state, horizontally scalable over HTTP.
- **Optional chart hosting** — set `MCP_EXPORT_DIR` + `MCP_EXPORT_BASE_URL` and use
  `exportToUrl` to persist an artifact and get a public URL for chat platforms.
- **Type-safe tool surface** — JSON Schema derived from Zod v4 (`Standard Schema`), so
  agents get precise, machine-readable input/output contracts.
- **Passthrough fidelity** — `render_echart` accepts *any* ECharts v6 option; it does not
  restrict chart types (see `list_chart_types` for discoverability only).
- **Hardened by default** — SSRF deny-by-default for remote images, option-depth/complexity
  bounds, render timeout, constant-time auth, and non-root container. See [SECURITY.md](./SECURITY.md).

## Tools

| Tool | Purpose |
| --- | --- |
| `render_echart` | Renders an ECharts option to SVG, PNG, and/or HTML. Returns the artifacts + echoed option. |
| `validate_echart_option` | Dry-run structural + semantic validation (pinned to echarts@6.1.0) without rendering. |
| `list_chart_types` | Lists common series types for agent discoverability (not an allowlist). |

### `render_echart` inputs

| Param | Type | Default | Notes |
| --- | --- | --- | --- |
| `option` | object | *required* | Full ECharts v6 option (passthrough). |
| `formats` | `("svg" \| "png" \| "html")[]` | `["svg"]` | Outputs to produce in one call. |
| `width` / `height` | int | 800 / 500 | Render dimensions in logical pixels (100–4096). |
| `devicePixelRatio` | number | 2 | DPI multiplier for PNG output (1–3). |
| `theme` | string \| object | `"light"` | Built-in name or inline theme JSON object. |
| `background` | string | `"transparent"` | e.g. `"#ffffff"` or `"transparent"`. |
| `animation` | boolean | `false` | Emit animated output when `true`. |
| `exportToUrl` | boolean | `false` | Persist the artifact and return a public URL (needs hosting config). |

The result carries MCP content blocks per requested format — an `image` block for
base64 PNG, a `text` block for raw SVG and/or HTML — plus `structuredContent` with
`formats`, `width`, `height`, `theme`, `background`, `version`, and (conditionally)
`svg`, `pngBytes`, `html`, `url`, and a `warning`.

## Quick start

Requires Node.js 20+ (developed on Node 24) and pnpm.

```bash
pnpm install
pnpm build          # compiles src/ -> dist/
pnpm dev            # run via tsx (stdio)
pnpm start          # run the compiled dist (stdio)
```

Run the server over stdio (the default) from an MCP client by pointing it at `dist/index.js`.

### Install from npm

```bash
npm install -g @ryuk3nd0/echarts-mcp
# then use it from any MCP client:
echarts-mcp
# or ad-hoc without a global install:
npx -y @ryuk3nd0/echarts-mcp
```

### Streamable HTTP

```bash
MCP_TRANSPORT=streamable-http MCP_HTTP_PORT=3030 node dist/index.js
```

The MCP endpoint is then available at `http://localhost:3030/mcp`.

### Configuration

Configuration is resolved in this precedence order (highest first):

1. Environment variables
2. A JSON config file (`MCP_CONFIG` path, or `./config.json` when present)
3. Built-in defaults

| Env var | Config key | Default |
| --- | --- | --- |
| `MCP_TRANSPORT` | `transport` | `stdio` |
| `MCP_HTTP_HOST` | `http.host` | `0.0.0.0` |
| `MCP_HTTP_PORT` | `http.port` | `3030` |
| `MCP_HTTP_PATH` | `http.path` | `/mcp` |
| `MCP_API_TOKEN` | `http.authToken` | `null` (disabled) |
| `MCP_EXPORT_DIR` | `export.outputDir` | `null` (disabled) |
| `MCP_EXPORT_BASE_URL` | `export.baseUrl` | `null` (disabled) |
| `MCP_IMAGE_ALLOW_HOSTS` | `security.imageAllowHosts` | `[]` (deny all remote images) |

See `config.example.json` for the full shape.

### Authentication (Streamable HTTP only)

By default the HTTP endpoint is **open** (intended for trusted localhost). To protect it,
set `MCP_API_TOKEN` (or `http.authToken` in `config.json`). Clients must then send
`Authorization: Bearer <token>`; requests without a matching token receive `401`.

## Development

```bash
pnpm typecheck     # tsc --noEmit
pnpm lint          # eslint
pnpm test          # vitest run
pnpm format        # prettier
```

### Enterprise / security suites

```bash
pnpm test:security     # adversarial: SSRF guard, bounds, HTML injection, allowlist
pnpm test:stress       # concurrency + pathological-input cliffs
pnpm test:leak         # standalone --expose-gc heap-plateau check
pnpm test:enterprise   # security + stress + leak (release gate)
pnpm audit             # dependency audit (release-time, --audit-level=high)
```

## Docker

```bash
docker build -t echarts-mcp .
# stdio
docker run -i --rm echarts-mcp
# streamable HTTP
docker run -p 3030:3030 -e MCP_TRANSPORT=streamable-http echarts-mcp
```

## PNG rendering

PNG output uses the optional `@napi-rs/canvas` dependency (a Rust/Skia binding). It is
installed automatically with `pnpm install`, but if the native binary is unavailable the
SVG and HTML paths still work — requesting `png` without it returns a clear actionable
error. Raster rendering is bounded by an 8K-equivalent pixel budget and a
`maxImageBytes` cap (default 8 MiB); oversized images are suppressed with a `warning`.

## Security

The threat model, implemented controls, and accepted residual risks are documented in
[SECURITY.md](./SECURITY.md). In short: this is a **local-first** server (stdio), with
SSRF denied by default, bounded inputs, and a non-root container. See that file for what is
and isn't covered.

## Roadmap / deferred

- **Geo/map fetches** and custom font registration in the canvas path.
- **Full OAuth 2.1** resource-server auth (optional bearer token today).
- **HTTP-transport hardening** (body/concurrency caps) before any hosted deployment.

## License

Apache-2.0. See [LICENSE](./LICENSE).

## Integrations

Ready-to-paste client config snippets live in `integrations/`:

- `integrations/claude-desktop.json` — Claude Desktop stdio entry.
- `integrations/mcp.json` — generic stdio entry (Claude Code, Cursor, and others).
- `integrations/streamable-http.json` — remote Streamable HTTP endpoint for gateways and hosted LLM tool integrations.

A machine-readable `SKILL.md` at the repo root describes the server for Agent Skills systems.
The same guidance is exposed to MCP clients via the server's `instructions` field.

TDQS

A4.3/5.0

Scored across 3 tools

Disambiguation5/5

render_echart, validate_echart_option, and list_chart_types each address a distinct concern: output generation, option validation, and capability discovery. No two tools overlap in purpose, so agent selection should be straightforward.

Naming Consistency4/5

All tool names use snake_case with a verb-first pattern: render_, validate_, list_. The minor inconsistency is the object noun varying between echart, echart_option, and chart_types, but the overall convention is still predictable.

Tool Count5/5

Three tools cover a narrow, well-defined pipeline: discovering chart types, validating options, and rendering output. This is an appropriate scope with no redundant tools and no obvious missing boilerplate steps.

Completeness4/5

The core workflow is well covered: list_chart_types aids discovery, validate_echart_option prevents render failures, and render_echart produces several output formats. However, render_echart's description references an exportToUrl tool for chat platforms that is not actually part of this server, creating a minor gap for agents aiming at Slack/Discord/Telegram.

Maintenance

ActivityMaintained
ResponsivenessNo issues