Local Development
Prerequisites
- Node.js (for
wranglerdev server) - Cloudflare account with Workers + KV enabled
- Two TTF fonts uploaded to KV (see Configuration page)
Install
git clone https://github.com/lingion/plot-mcp-worker
cd plot-mcp-worker
npm install # installs wrangler, typescript, expr-eval, opentype.js, resvg-wasm
Locked deps in package-lock.json; npm ci also works.
Scripts (from package.json)
| npm script | Command | Purpose |
|---|---|---|
dev | wrangler dev | Local Worker dev server (Miniflare under the hood) |
deploy | wrangler deploy | Push to Cloudflare Workers |
check | tsc --noEmit | TypeScript type-check only (no emit) |
test:smoke | node scripts/smoke.mjs | Hit /healthz and a few tool calls |
probe:force-analysis | node scripts/local-force-analysis-probe.mjs | Generate 8 force-analysis samples locally |
check:force-drift | node scripts/force-analysis-drift-check.mjs | Compare local vs deployed SVG byte hash |
check:force-struct | node scripts/force-analysis-struct-diff.mjs | Diff structure of local vs deployed SVG |
check:force-compact | node scripts/force-analysis-compact-diagnose.mjs | Compact diagnose of force analysis output |
check:force-verify | node scripts/force-analysis-verify.mjs | Full verification of force analysis |
check:bundle-fingerprint | node scripts/deploy-bundle-fingerprint.mjs | Fingerprint the deployed worker bundle |
check:deploy-preflight | node scripts/deploy-preflight.mjs | Pre-deploy sanity checks |
check:deploy-and-verify | node scripts/deploy-and-verify.mjs | Deploy then immediately verify |
check:remote-health | node scripts/remote-health.mjs | Hit deployed /healthz and parse JSON |
Typical workflow
# 1. Type-check
npm run check
# 2. Smoke test against local dev server (needs wrangler dev running in another terminal)
npm run dev # in terminal 1
npm run test:smoke # in terminal 2
# 3. Verify force analysis output before deploying
npm run probe:force-analysis
npm run check:force-drift
# 4. Deploy
npm run deploy
# 5. Verify deployed
npm run check:remote-health
Calling the MCP endpoint from curl
# initialize
curl -s -X POST https://your-worker.workers.dev/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
# tools/list
curl -s -X POST https://your-worker.workers.dev/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# tools/call -> plot
curl -s -X POST https://your-worker.workers.dev/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"plot","arguments":{"expr":"sin(x)","x_min":-6.28,"x_max":6.28,"title":"Sine","render":{"format":"link"}}}}'
Calling web endpoints
The web endpoints (/plot, /png, etc.) take a ?d= query parameter with gzipped base64url JSON. Easiest path: use the MCP tool (which returns the URL), or generate the encoded payload in Node:
import { toCompressedBase64UrlFromJson } from "./src/utils.js";
const d = await toCompressedBase64UrlFromJson({
expr: "sin(x)", x_min: -6.28, x_max: 6.28, points: 1000,
title: "Sine", xlabel: "x", ylabel: "y"
});
console.log("https://your-worker.workers.dev/plot?d=" + d);