> plot-mcp-worker-a-serverless-mcp-chart-engine-on-cloudflare-workers

plot-mcp-worker: A Serverless MCP Chart Engine on Cloudflare Workers
// ─────────────────────────────────────────────────────────────
OUTPUT 002 SEED 1276379991 DATE 2026-06-28 CHARS 10,360 TAGS mcp cloudflare-workers typescript visualization algorithms svg wasm

What is an MCP Server?

The Model Context Protocol (MCP) is an open protocol that lets AI models interact with external tools through a standardized JSON-RPC interface. An MCP server exposes tools — functions that an AI agent can call to get things done. plot-mcp-worker exposes 40+ visualization tools covering function plots, bar charts, scatter plots, histograms, box plots, pie charts, force diagrams, circuit schematics, 3D geometry, Venn diagrams, and physics/math teaching templates. 105 commits, 3,471 lines of TypeScript in the worker entry alone, plus 1,520 lines of plotting engine and 1,123 lines of SVG rendering.

The Architecture

AI Agent (Claude / Cursor)
    ↓ MCP JSON-RPC over HTTP (POST /mcp)
Cloudflare Worker (TypeScript)
    ├─ Expression parser (expr-eval with piecewise segment support)
    ├─ Data transform pipeline (normalize → smooth → filter → rolling_avg → downsample)
    ├─ Smart axis engine (nice ticks, auto-PI, asymptote clamping, log scale)
    ├─ SVG generation (axes, grid, labels, legends, error bars, annotations)
    ├─ CJK text-to-path (opentype.js + GB2312 level-1 → embedded SVG paths)
    └─ resvg-wasm rasterization (SVG → PNG in Workers WASM runtime)
           ↓
PNG / SVG / HTML output → 5-min Cloudflare cache → Direct URL or base64 payload

No headless browser. No Puppeteer. No Docker. The entire rendering pipeline — from math expression parsing through font rasterization to PNG output — runs inside the Cloudflare Workers WASM runtime.

1. Expression Parsing and Function Plotting

The plotting engine uses expr-eval to parse mathematical expressions into ASTs, then evaluates them over a sampling grid. The buildSinglePlot() function samples expressions at up to 20,000 points across a user-defined domain, with adaptive clamping for asymptotes.

1.1 Asymptote detection

Functions like tan(x) or 1/x produce infinite spikes at singularities. The engine detects these by watching for sign flips with large delta-y between adjacent sample points. When detected, the path is broken at that point and the extreme value is clamped using IQR (interquartile range) filtering — the spike is truncated to a reasonable visual ceiling rather than shooting off-screen.

1.2 Piecewise functions

Piecewise segments allow different expressions over different x-ranges, each with its own label and color. The engine stitches them together at boundary points, ensuring continuous paths even across segment transitions.

2. Data Transform Pipeline

Before rendering, series data can pass through a sequence of 5 transforms — a composable pipeline that mutates points in order:

  1. Normalize — three methods: minmax (linear rescale to [0,1]), zscore (standardize to mean 0, std 1), maxabs (divide by maximum absolute value). Target axis selectable (x, y, or both).
  2. Smooth — centered moving average with configurable window size. Boundary NaNs are replaced by nearest valid neighbor values.
  3. Filter — point removal by comparison operator (>, >=, <, <=, ==, !=) against a threshold value. Falls back to keeping all points if the filter would empty the series.
  4. Rolling average — identical to smooth but semantically separated for pipeline clarity.
  5. Downsample — two methods: uniform (evenly spaced by index) and minmax (bucket-based, preserving local extremes to maintain the visual shape of noisy data). Minmax assigns points to buckets, selects both min and max per bucket, then sorts by original index and deduplicates.

Each transform emits warnings when skipped (e.g., normalize on histogram/box/pie series, or smooth on non-line series). An optional trace mode returns per-stage input/output point counts for debugging.

3. Smart Axis Intelligence

The axis engine computes tick positions and labels from data ranges with five intelligence layers:

  • Nice ticks: Step selection from {1, 2, 2.5, 5} × 10^n — guarantees human-readable tick values instead of arbitrary decimals
  • Auto-PI mode: When the x-domain is a PI multiple, the axis switches to π-formatted labels (-2π, -π, 0, π, 2π). Detection uses modulus checks against PI with tolerance
  • Trig y-special: sin/cos functions get y-ticks at [-1, -0.5, 0, 0.5, 1] instead of arbitrary decimals
  • 0-symmetric: Math-style function plots default to symmetric y-axis around zero — the axis range is ±max(|ymin|, |ymax|)
  • Log scale: Logarithmic y-axis with grid lines at powers of 10, auto-detecting the appropriate decade range

4. CJK Text-to-Path Pipeline

Cloudflare Workers have no system fonts. Chinese/Japanese/Korean characters render as tofu boxes. The solution: a font pre-processing pipeline that converts CJK glyphs into raw SVG path data embedded directly in the chart SVG.

The pipeline processes the GB2312 level-1 character set — 3,755 characters producing 4,532 glyphs. Each glyph is loaded via opentype.js, its outline extracted as a Path2D object, and serialized to SVG &lt;path d="..."&gt; strings stored in a lookup map. When the SVG renderer encounters Chinese text in labels or titles, it substitutes the character with its pre-computed path. The rendered text is independent of client fonts — it appears identically everywhere.

Font weight was iterated twice: PingFang SC (442 KB bundled) was replaced with Heiti SC Medium (284 KB), a 36% reduction for the same coverage. Font data was eventually migrated to KV storage so the worker bundle stays under Cloudflare's free-tier size limits while the font loads on first use with a 5-minute cache lifetime.

5. SVG → PNG via resvg-wasm

The rendering pipeline generates SVG first — full control over layout, axes, fonts, grid lines, labels, and legends — then rasterizes to PNG using resvg-wasm, a Rust-based SVG renderer compiled to WebAssembly. The WASM module (~1.4 MB) is loaded at worker start and initialized once per cold start.

The SVG output supports 8 preset backgrounds (light, dark, transparent, and 5 color variants) with configurable sizing via DEFAULT_WIDTH (1200px) and DEFAULT_HEIGHT (720px). Output format is selectable: png for direct display, svg for vector editing, link for a cache-friendly URL, html for interactive 3D views.

6. MCP JSON-RPC Protocol Layer

The worker implements the MCP 2024-11-05 specification: a POST /mcp endpoint that accepts JSON-RPC 2.0 requests. The three MCP methods:

  • initialize — returns server capabilities, protocol version, and server info (name: "plot-mcp-worker", version: "0.4.14")
  • tools/list — returns all 40+ tool definitions as JSON Schema objects with parameter constraints and descriptions
  • tools/call — dispatches by tool name, validates parameters against the schema, calls the appropriate plot/render function, returns either a PNG URL, SVG string, or base64 image payload

CORS headers are set on all responses. Health checks at /mcp/health return server status.

7. Diagram Generators (Physics, Circuits, 3D)

Beyond charts, the worker generates specialized diagrams:

  • Force analysis: Free-body diagrams with configurable forces (angle + magnitude), body labels, coordinate axes, component decomposition, and resultant vector. Templates for incline, hanging mass, horizontal surface, pulley, spring, and oscillator configurations.
  • Circuit schematics: SVG circuit diagrams with 21 component types (battery, resistor, capacitor, inductor, diode, transistor, op-amp, relay, etc.) plus stages for series/parallel layout and return paths. Templates for 10 common teaching circuits.
  • 3D geometry: Interactive HTML viewers for cubes, spheres, cylinders, cones, 3D vectors, and parametric surfaces. Up to 6 simultaneous surfaces with individual color scales, contour lines, and opacity controls. Supports surface expressions with configurable sampling density (up to 80 samples per axis).
  • Venn diagrams: 2-set and 3-set diagrams with per-region labels, configurable colors, and probability-notation regions.
  • C memory diagrams: Pointer teaching diagrams with labeled memory blocks, addresses, byte contents, and type annotations for C programming instruction.

8. Resource Limits and Safety

All tool inputs are bounded by strict limits defined in constants:

  • Expression length: max 400 characters
  • Sample points: 10-20,000 (default 1,000)
  • Series count: max 12 per plot
  • Force diagram items: max 16 forces, 8 bodies, 6 surfaces
  • Circuit components: max 24 components, 48 wires
  • 3D surfaces: max 6 surfaces, 80 samples per axis, 8 lines, 32 point markers
  • Title/label text is clamped to 120/80 characters
  • Multi-plot subplot grids: configurable rows × cols with shared axis options and per-cell clip paths

Self-Host

Add to any MCP client config:

{
  "mcpServers": {
    "plot": {
      "url": "https://&lt;your-worker&gt;.&lt;your-subdomain&gt;.workers.dev/mcp"
    }
  }
}

Then ask your AI agent: "Plot sin(x) from -PI to PI" — and get a rendered PNG back.

License: CC BY-NC-SA 4.0. Deploy to your own Cloudflare account in under 5 minutes.

Star on GitHub · Self-host on your own Cloudflare account

See also: plot-mcp Operation Docs — step-by-step user guide for all 40+ tools, endpoints, local dev, and troubleshooting.

← cd .. /