agentic-mermaid 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +149 -4
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +25 -32
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/assets/fonts/FONT-LICENSES.md +9 -5
- package/assets/fonts/Inter-Bold.ttf +0 -0
- package/assets/fonts/Inter-Medium.ttf +0 -0
- package/assets/fonts/Inter-Regular.ttf +0 -0
- package/assets/fonts/Inter-SemiBold.ttf +0 -0
- package/dist/agent-core.d.ts +463 -0
- package/dist/agent-core.js +397 -0
- package/dist/agent.d.ts +81 -1822
- package/dist/agent.js +336 -36839
- package/dist/agentic-mermaid-mcp.js +7 -37908
- package/dist/am.js +33638 -36261
- package/dist/chunk-4QECACIA.js +4702 -0
- package/dist/chunk-CDPKJ2BQ.js +37 -0
- package/dist/chunk-QKDLMOJJ.js +3487 -0
- package/dist/chunk-WI35L7IR.js +615 -0
- package/dist/chunk-WN3YDOBG.js +149 -0
- package/dist/chunk-ZYMMKQ3I.js +62637 -0
- package/dist/index-D54dqSdC.d.ts +6777 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +310 -32836
- package/dist/mcp-cli-SBQSTJFC.js +17 -0
- package/docs/README.md +14 -6
- package/docs/schemas/style-spec.schema.json +1221 -30
- package/examples/styles/catalog.json +54 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
- package/examples/styles/cupertino-prototype.mmd +10 -0
- package/examples/styles/cupertino-prototype.style.json +63 -0
- package/examples/styles/mid-century-report.style.json +2 -2
- package/examples/styles/star-chart-atlas.style.json +2 -1
- package/examples/styles/transit-route-map.style.json +2 -2
- package/examples/styles/vercel-inspired-prototype.mmd +6 -0
- package/examples/styles/vercel-inspired-prototype.style.json +87 -0
- package/package.json +70 -43
- package/server.json +41 -0
- package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
- package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
- package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
- package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
- package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
- package/AGENT_NATIVE.md +0 -602
- package/Instructions_for_agents.md +0 -83
- package/TODO.md +0 -662
- package/assets/hero.png +0 -0
- package/dist/index-x2Cj1dVv.d.ts +0 -1089
- package/docs/agent-api-cookbook.md +0 -308
- package/docs/agent-mutation-policy.md +0 -104
- package/docs/agent-workflow-examples.md +0 -46
- package/docs/api.md +0 -203
- package/docs/ascii.md +0 -103
- package/docs/assets/improvements/box-start-connector.png +0 -0
- package/docs/assets/improvements/comment-dropped-lint.png +0 -0
- package/docs/assets/improvements/er-content-loss.png +0 -0
- package/docs/assets/improvements/fan-in-trunks.png +0 -0
- package/docs/assets/improvements/gantt-family.png +0 -0
- package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
- package/docs/assets/improvements/gantt-verify-seam.png +0 -0
- package/docs/assets/improvements/subgraph-container-edges.png +0 -0
- package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
- package/docs/assets/improvements/wrapper-init-directive.png +0 -0
- package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
- package/docs/assets/style-cookbook/mid-century-report.png +0 -0
- package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
- package/docs/assets/style-cookbook/transit-route-map.png +0 -0
- package/docs/comparison.md +0 -94
- package/docs/config.md +0 -105
- package/docs/contributing/adding-diagram-types.md +0 -144
- package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
- package/docs/contributing/diagram-family-citizenship.md +0 -82
- package/docs/contributing/harvesting-upstream-tests.md +0 -126
- package/docs/contributing/lessons-learned.md +0 -276
- package/docs/contributing/releasing.md +0 -71
- package/docs/contributing/visual-review-evidence.md +0 -53
- package/docs/custom-style-cookbook.md +0 -154
- package/docs/design/families/README.md +0 -26
- package/docs/design/families/architecture-beta.md +0 -48
- package/docs/design/families/flowchart-parser-conformance.md +0 -22
- package/docs/design/families/gantt-research.md +0 -137
- package/docs/design/families/gantt.md +0 -329
- package/docs/design/families/journey-agentic-cards-before.png +0 -0
- package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
- package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
- package/docs/design/families/journey-migration-parity.md +0 -433
- package/docs/design/families/journey-usage-research.md +0 -423
- package/docs/design/families/journey.md +0 -125
- package/docs/design/families/xychart.md +0 -85
- package/docs/design/system/README.md +0 -45
- package/docs/design/system/abstraction-audit.md +0 -224
- package/docs/design/system/abstraction-recommendations.md +0 -655
- package/docs/design/system/architecture.mmd +0 -29
- package/docs/design/system/architecture.svg +0 -137
- package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
- package/docs/design/system/layout-rubric.md +0 -138
- package/docs/design/system/route-contracts.md +0 -848
- package/docs/design/system/source-preservation-ladder.md +0 -52
- package/docs/design/system/styles-rollout.md +0 -216
- package/docs/design/system/ugly-layouts.md +0 -119
- package/docs/diagram-families.md +0 -193
- package/docs/features.md +0 -147
- package/docs/fork-differences.md +0 -130
- package/docs/getting-started.md +0 -175
- package/docs/issue-derived-test-cases.md +0 -156
- package/docs/layout-characterization/README.md +0 -245
- package/docs/layout-characterization/contact-sheet-families.md +0 -496
- package/docs/layout-characterization/contact-sheet.md +0 -638
- package/docs/layout-characterization/properties.md +0 -281
- package/docs/layout-characterization/visual-quality.md +0 -160
- package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
- package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
- package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
- package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
- package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/journey.svg +0 -118
- package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
- package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
- package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
- package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
- package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
- package/docs/mcp-code-mode-rationale.md +0 -101
- package/docs/mcp-http-transport.md +0 -223
- package/docs/mermaid-layout-complaints.md +0 -837
- package/docs/mutation-testing.md +0 -206
- package/docs/project/divergences.md +0 -98
- package/docs/project/dogfooding-docs-strategy.md +0 -66
- package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
- package/docs/project/lessons-learned.md +0 -1019
- package/docs/project/remove-role-styling-plan.md +0 -176
- package/docs/quality.md +0 -212
- package/docs/react.md +0 -97
- package/docs/style-authoring.md +0 -133
- package/docs/testing-strategy.md +0 -335
- package/docs/theming.md +0 -162
- package/examples/agent-improve-auth-flow.ts +0 -206
- package/examples/agent-loop.ts +0 -53
- package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
- package/llms.txt +0 -104
- package/skill-evals/README.md +0 -77
- package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
- package/skill-evals/fixtures/class-payment/input.mmd +0 -4
- package/skill-evals/fixtures/er-orders/input.mmd +0 -10
- package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
- package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
- package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
- package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
- package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
- package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
- package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
- package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
- package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
- package/skill-evals/shared-benchmark.json +0 -510
- package/src/agent/analyze.ts +0 -157
- package/src/agent/apply.ts +0 -146
- package/src/agent/architecture-body.ts +0 -458
- package/src/agent/certificates.ts +0 -110
- package/src/agent/class-body.ts +0 -319
- package/src/agent/core.ts +0 -207
- package/src/agent/create.ts +0 -106
- package/src/agent/describe.ts +0 -364
- package/src/agent/er-body.ts +0 -224
- package/src/agent/facts.ts +0 -337
- package/src/agent/families-builtin.ts +0 -934
- package/src/agent/families.ts +0 -232
- package/src/agent/family-layouts.ts +0 -826
- package/src/agent/flowchart-body.ts +0 -372
- package/src/agent/flowchart-unsupported.ts +0 -243
- package/src/agent/gantt-body.ts +0 -495
- package/src/agent/index.ts +0 -10
- package/src/agent/journey-body.ts +0 -414
- package/src/agent/label-metrics.ts +0 -28
- package/src/agent/layout-to-rendered.ts +0 -63
- package/src/agent/mutate.ts +0 -97
- package/src/agent/mutation-ops.ts +0 -51
- package/src/agent/op-schema.ts +0 -439
- package/src/agent/parse.ts +0 -223
- package/src/agent/pie-body.ts +0 -270
- package/src/agent/png.ts +0 -117
- package/src/agent/quadrant-body.ts +0 -336
- package/src/agent/quality.ts +0 -363
- package/src/agent/readability-audit.ts +0 -92
- package/src/agent/rendered-route-audit.ts +0 -61
- package/src/agent/sequence-body.ts +0 -345
- package/src/agent/serialize.ts +0 -214
- package/src/agent/state-body.ts +0 -589
- package/src/agent/structural-count.ts +0 -150
- package/src/agent/timeline-body.ts +0 -280
- package/src/agent/trace-log.ts +0 -43
- package/src/agent/types.ts +0 -979
- package/src/agent/verify.ts +0 -571
- package/src/agent/xychart-body.ts +0 -472
- package/src/architecture/config.ts +0 -282
- package/src/architecture/layout.ts +0 -496
- package/src/architecture/parser.ts +0 -375
- package/src/architecture/renderer.ts +0 -611
- package/src/architecture/types.ts +0 -113
- package/src/ascii/ansi.ts +0 -423
- package/src/ascii/architecture.ts +0 -210
- package/src/ascii/canvas.ts +0 -443
- package/src/ascii/class-diagram.ts +0 -697
- package/src/ascii/converter.ts +0 -475
- package/src/ascii/draw.ts +0 -1693
- package/src/ascii/edge-bundling.ts +0 -328
- package/src/ascii/edge-routing.ts +0 -330
- package/src/ascii/er-diagram.ts +0 -439
- package/src/ascii/gantt.ts +0 -276
- package/src/ascii/grid.ts +0 -747
- package/src/ascii/index.ts +0 -196
- package/src/ascii/journey.ts +0 -189
- package/src/ascii/meta.ts +0 -244
- package/src/ascii/multiline-utils.ts +0 -80
- package/src/ascii/pathfinder.ts +0 -301
- package/src/ascii/pie.ts +0 -67
- package/src/ascii/quadrant.ts +0 -158
- package/src/ascii/reverse.ts +0 -216
- package/src/ascii/sequence.ts +0 -499
- package/src/ascii/shapes/circle.ts +0 -27
- package/src/ascii/shapes/corners.ts +0 -143
- package/src/ascii/shapes/diamond.ts +0 -27
- package/src/ascii/shapes/hexagon.ts +0 -27
- package/src/ascii/shapes/index.ts +0 -106
- package/src/ascii/shapes/rectangle.ts +0 -168
- package/src/ascii/shapes/rounded.ts +0 -27
- package/src/ascii/shapes/special.ts +0 -332
- package/src/ascii/shapes/stadium.ts +0 -110
- package/src/ascii/shapes/state.ts +0 -192
- package/src/ascii/shapes/types.ts +0 -73
- package/src/ascii/timeline.ts +0 -128
- package/src/ascii/types.ts +0 -312
- package/src/ascii/validate.ts +0 -120
- package/src/ascii/width.ts +0 -81
- package/src/ascii/xychart.ts +0 -919
- package/src/browser.ts +0 -29
- package/src/class/layout.ts +0 -240
- package/src/class/parser.ts +0 -332
- package/src/class/renderer.ts +0 -638
- package/src/class/types.ts +0 -129
- package/src/cli/agent-instructions.ts +0 -1
- package/src/cli/am-bin.ts +0 -6
- package/src/cli/exit-codes.ts +0 -18
- package/src/cli/index.ts +0 -1117
- package/src/cli/init-agent.ts +0 -147
- package/src/color-resolver.ts +0 -152
- package/src/elk-instance.ts +0 -114
- package/src/er/layout.ts +0 -187
- package/src/er/parser.ts +0 -226
- package/src/er/renderer.ts +0 -792
- package/src/er/types.ts +0 -99
- package/src/font-manifest.ts +0 -27
- package/src/gantt/layout.ts +0 -365
- package/src/gantt/parser.ts +0 -282
- package/src/gantt/pipeline.ts +0 -27
- package/src/gantt/renderer.ts +0 -414
- package/src/gantt/schedule.ts +0 -513
- package/src/gantt/types.ts +0 -255
- package/src/index.ts +0 -368
- package/src/journey/layout.ts +0 -520
- package/src/journey/parser.ts +0 -218
- package/src/journey/renderer.ts +0 -811
- package/src/journey/types.ts +0 -144
- package/src/layout/geometry.ts +0 -182
- package/src/layout/pass.ts +0 -142
- package/src/layout/passes/index.ts +0 -3916
- package/src/layout-engine.ts +0 -1565
- package/src/layout-rubric.ts +0 -659
- package/src/mcp/artifacts.ts +0 -137
- package/src/mcp/dynamic-harness.ts +0 -31
- package/src/mcp/dynamic-modules.d.ts +0 -7
- package/src/mcp/facade.ts +0 -608
- package/src/mcp/harness-runtime.ts +0 -203
- package/src/mcp/hosted-server.ts +0 -468
- package/src/mcp/mcp-bin.ts +0 -3
- package/src/mcp/mcp-cli.ts +0 -82
- package/src/mcp/protocol.ts +0 -13
- package/src/mcp/sandbox.ts +0 -195
- package/src/mcp/sdk-decl.ts +0 -387
- package/src/mcp/server.ts +0 -362
- package/src/mcp/tool-surface.ts +0 -146
- package/src/mermaid-source.ts +0 -655
- package/src/multiline-utils.ts +0 -228
- package/src/parser.ts +0 -1031
- package/src/pie/layout.ts +0 -168
- package/src/pie/parser.ts +0 -136
- package/src/pie/renderer.ts +0 -234
- package/src/pie/types.ts +0 -82
- package/src/quadrant/layout.ts +0 -191
- package/src/quadrant/parser.ts +0 -183
- package/src/quadrant/renderer.ts +0 -322
- package/src/quadrant/types.ts +0 -113
- package/src/render-family-hooks.ts +0 -244
- package/src/renderer.ts +0 -1099
- package/src/route-contracts.ts +0 -2723
- package/src/scene/backend.ts +0 -76
- package/src/scene/fidelity.ts +0 -183
- package/src/scene/hybrid-backend.ts +0 -174
- package/src/scene/ir.ts +0 -181
- package/src/scene/marks.ts +0 -127
- package/src/scene/rough-backend.ts +0 -378
- package/src/scene/seed.ts +0 -46
- package/src/scene/style-registry.ts +0 -556
- package/src/sequence/layout.ts +0 -456
- package/src/sequence/parser.ts +0 -255
- package/src/sequence/renderer.ts +0 -604
- package/src/sequence/types.ts +0 -154
- package/src/shape-clipping.ts +0 -351
- package/src/shared/accessibility-directives.ts +0 -22
- package/src/shared/batched.ts +0 -61
- package/src/shared/color-math.ts +0 -72
- package/src/shared/svg-a11y.ts +0 -29
- package/src/shared/syntax-error.ts +0 -17
- package/src/shared/unicode-ranges.ts +0 -135
- package/src/styles.ts +0 -318
- package/src/svg-paths.ts +0 -28
- package/src/text-metrics.ts +0 -248
- package/src/theme.ts +0 -639
- package/src/timeline/layout.ts +0 -381
- package/src/timeline/parser.ts +0 -215
- package/src/timeline/renderer.ts +0 -538
- package/src/timeline/types.ts +0 -105
- package/src/types.ts +0 -454
- package/src/website-worker-modules.d.ts +0 -18
- package/src/xychart/axis-utils.ts +0 -109
- package/src/xychart/colors.ts +0 -124
- package/src/xychart/config.ts +0 -88
- package/src/xychart/layout.ts +0 -656
- package/src/xychart/parser.ts +0 -504
- package/src/xychart/renderer.ts +0 -640
- package/src/xychart/types.ts +0 -282
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
# Hosted MCP on Cloudflare: plan
|
|
2
|
-
|
|
3
|
-
> **Status: shipped (PR #94).** This began as a design plan and is now the
|
|
4
|
-
> as-built record of the hosted MCP endpoint live at
|
|
5
|
-
> `https://agentic-mermaid.dev/mcp`. The "Where we start" section below is
|
|
6
|
-
> preserved as the pre-build starting point; the "Post-review hardening"
|
|
7
|
-
> sections (rounds 1–5) track the audit findings applied after the initial
|
|
8
|
-
> build.
|
|
9
|
-
|
|
10
|
-
Plan for turning the website's `/mcp` 501 placeholder into a real hosted MCP
|
|
11
|
-
endpoint at `https://agentic-mermaid.dev/mcp`. The account is on the **Workers
|
|
12
|
-
Paid plan with Dynamic Workers** (<https://developers.cloudflare.com/dynamic-workers/>),
|
|
13
|
-
which changes the earlier free-tier plan in one fundamental way: hosted Code
|
|
14
|
-
Mode (`execute`) is now viable — agent-supplied JavaScript runs in an isolated
|
|
15
|
-
dynamic Worker instead of being local-only.
|
|
16
|
-
|
|
17
|
-
## Where we start
|
|
18
|
-
|
|
19
|
-
- The website is a Cloudflare Worker with Static Assets
|
|
20
|
-
(`website/wrangler.jsonc`, `website/src/worker.js` at the time — now
|
|
21
|
-
`worker.ts`); `/mcp` returned an honest 501 before this work.
|
|
22
|
-
- The MCP server (`src/mcp/server.ts`) has a transport-agnostic JSON-RPC core,
|
|
23
|
-
`handleRequest(req, context)`, plus stdio and node HTTP/SSE transports.
|
|
24
|
-
- Tools today: `execute` (Code Mode in a hardened `node:vm` sandbox),
|
|
25
|
-
`render_png` (native `@resvg/resvg-js`), `describe`.
|
|
26
|
-
- **Viability probe (done):** the pure SDK surface — parse, mutate, verify,
|
|
27
|
-
analyze, serialize, describe, `renderMermaidSVG`, `renderMermaidASCII`, the
|
|
28
|
-
Code Mode facade — bundles for the workerd target at 2.2 MB raw / 657 KB
|
|
29
|
-
gzip with zero Node dependencies, and renders correctly from that bundle.
|
|
30
|
-
The only native/VM dependencies in the whole graph are `png.ts` (napi resvg)
|
|
31
|
-
and `sandbox.ts` (`node:vm`), both replaced per the table below. elkjs is
|
|
32
|
-
the bundled pure-JS build driven synchronously (`src/elk-instance.ts`) and
|
|
33
|
-
needs nothing from Node.
|
|
34
|
-
|
|
35
|
-
## Hosted tool surface
|
|
36
|
-
|
|
37
|
-
| Tool | Local implementation | Hosted implementation |
|
|
38
|
-
|---|---|---|
|
|
39
|
-
| `execute` | `node:vm` hardened sandbox | **Dynamic Worker** per code hash: harness module (SDK bundle + the same hardened facade) + agent code as a module, `globalOutbound: null`, `limits: { cpuMs, subRequests: 0 }` |
|
|
40
|
-
| `render_png` | native `@resvg/resvg-js` | `@resvg/resvg-wasm` + bundled DejaVu Sans and built-in style faces (convenience surface; wasm output is not covered by the byte-determinism contract) |
|
|
41
|
-
| `describe` | pure | same code, unchanged |
|
|
42
|
-
| `render_svg` | — (use execute) | pure, hosted-only |
|
|
43
|
-
| `render_ascii` | — (use execute) | pure, hosted-only |
|
|
44
|
-
| `verify` | — (use execute) | pure, hosted-only |
|
|
45
|
-
| `mutate` | — (use execute or CLI `am mutate`) | hosted-only declarative structured edit of an existing `source`; applies an op list, verifies, emits `{ ok, family, source, verify }` only on success |
|
|
46
|
-
| `build` | — (use execute or CLI/library `buildMermaid`) | hosted-only declarative authoring from a `family` + op list; same verified envelope as `mutate` |
|
|
47
|
-
|
|
48
|
-
The three hosted-only pure render/verify tools exist for cost, not philosophy:
|
|
49
|
-
locally, `execute` is free, so Code Mode stays the single entry point
|
|
50
|
-
(`docs/mcp-code-mode-rationale.md` still holds). Hosted, every `execute`
|
|
51
|
-
spins/bills a dynamic Worker, so the common render/verify paths get direct
|
|
52
|
-
tools that cost one ordinary Worker invocation and are edge-cacheable. The
|
|
53
|
-
`mutate`/`build` tools are the deliberate structured-edit exception: they give
|
|
54
|
-
weak clients a verified op-list path without asking them to write JavaScript,
|
|
55
|
-
while still funneling through the same typed mutation core. The implementations
|
|
56
|
-
live in the shared hosted core (`src/mcp/hosted-server.ts`), not the website, so
|
|
57
|
-
there is exactly one implementation of each.
|
|
58
|
-
|
|
59
|
-
## Hosted `execute` via Dynamic Workers
|
|
60
|
-
|
|
61
|
-
Design (API per <https://developers.cloudflare.com/dynamic-workers/api-reference/>):
|
|
62
|
-
|
|
63
|
-
- The parent Worker holds a `worker_loaders` binding (`LOADER`). On
|
|
64
|
-
`execute`, it calls `env.LOADER.get(id, async () => workerCode)` where:
|
|
65
|
-
- `id` = `am-exec-<pkg.version>-<sha256(code)>` — identical code reuses a
|
|
66
|
-
warm isolate; the callback only runs on cold load.
|
|
67
|
-
- `workerCode.modules` = the prebuilt **harness** (SDK bundle + Code Mode
|
|
68
|
-
facade + result plumbing, built by `website/build.ts` and imported into
|
|
69
|
-
the parent as a text module) plus the agent code wrapped as ONE module
|
|
70
|
-
per isolate. workerd compiles every registry module eagerly at isolate
|
|
71
|
-
startup (discovered in the wrangler e2e — a lazy `import()` fallback
|
|
72
|
-
inside one isolate is impossible), so the expression-first choice
|
|
73
|
-
`sandbox.ts` makes is decided by the loader itself: the parent starts the
|
|
74
|
-
expression-form isolate first and falls back to a statement-form isolate
|
|
75
|
-
when startup fails with a SyntaxError. Statement-form code costs one
|
|
76
|
-
failed isolate attempt; identical repeats never reach the loader thanks
|
|
77
|
-
to the response cache.
|
|
78
|
-
- `globalOutbound: null` — the isolate cannot `fetch()` or `connect()`
|
|
79
|
-
at all; `env` is empty, so there is nothing to reach or leak.
|
|
80
|
-
- `getEntrypoint(null, { limits: { cpuMs: min(timeoutMs, 30_000),
|
|
81
|
-
subRequests: 0 } })` enforces the compute budget; hitting it throws,
|
|
82
|
-
which maps to the same `{ ok: false, error }` shape as a local timeout.
|
|
83
|
-
- The harness passes the user function the **same hardened `mermaid` facade**
|
|
84
|
-
the vm sandbox uses (`createTracingMermaid`, extracted to a runtime-neutral
|
|
85
|
-
`src/mcp/facade.ts`), so read-only results, trusted-diagram checks, and
|
|
86
|
-
error messages match local behavior. Sync-only enforcement
|
|
87
|
-
(`unsupportedCodeReason`) runs parent-side before any isolate is created —
|
|
88
|
-
rejected code costs nothing.
|
|
89
|
-
- Result contract is byte-identical in shape to local: `{ ok, value, logs,
|
|
90
|
-
error }`, with the same undefined→null promotion and Map/Set JSON
|
|
91
|
-
normalization.
|
|
92
|
-
|
|
93
|
-
Known, documented divergences from local `execute`:
|
|
94
|
-
|
|
95
|
-
1. `timeoutMs` is enforced as **CPU** milliseconds (`cpuMs`), not wall-clock;
|
|
96
|
-
for pure synchronous compute these are close but not identical.
|
|
97
|
-
2. A warm isolate can serve repeated identical code, so module-level global
|
|
98
|
-
mutation (`globalThis.x = ...`) may be visible across calls with the same
|
|
99
|
-
code — locally every call gets a fresh vm context. Response caching (below)
|
|
100
|
-
makes identical requests return the first result anyway; the divergence is
|
|
101
|
-
only reachable when the edge cache misses but the isolate is warm.
|
|
102
|
-
3. Isolation authority differs: locally the proxy facade is the security
|
|
103
|
-
boundary; hosted, the isolate + `globalOutbound: null` is, and the facade
|
|
104
|
-
is kept for behavioral parity.
|
|
105
|
-
4. Hosted output is bounded at the source: console logs cap at 1,000
|
|
106
|
-
entries / 256 KB (a truncation marker is appended) and serialized results
|
|
107
|
-
at 2 MB, with the parent's capped stream-read of the isolate response as
|
|
108
|
-
the backstop. The vm sandbox has no output caps.
|
|
109
|
-
|
|
110
|
-
Post-review hardening (external audit, round 1): Worker Loader isolate IDs
|
|
111
|
-
carry a `deployTag` — package version **plus a hash of the harness bundle** —
|
|
112
|
-
because the Worker Loader contract is that one ID always maps to the same
|
|
113
|
-
WorkerCode; without the hash, a harness/SDK change that ships without a
|
|
114
|
-
version bump could keep serving stale warm isolates. Request bodies and
|
|
115
|
-
isolate responses are stream-read with hard byte caps (no buffering past the
|
|
116
|
-
limit), `render_png` clamps `scale` to 0.1–8 and enforces a ~16.7 MP
|
|
117
|
-
output-pixel budget, and the e2e probe pins workerd's codegen bans (`eval`,
|
|
118
|
-
`Function` constructor) plus the log cap against a live isolate.
|
|
119
|
-
|
|
120
|
-
Post-review hardening (external audit, round 2):
|
|
121
|
-
|
|
122
|
-
- **Full-deploy cache version.** The harness hash covers Code Mode isolate
|
|
123
|
-
IDs, but the /mcp *response* cache also stores `render_svg` / `verify` /
|
|
124
|
-
`describe` / `render_png` results, whose implementations live in
|
|
125
|
-
`hosted-server.ts`, `mcp-handler.ts`, `png-wasm.ts`, and the wasm/fonts —
|
|
126
|
-
none of which are in the harness bundle. `cacheVersion` is now a
|
|
127
|
-
`DEPLOY_VERSION` computed at build time (`website/src/deploy-hash.ts`,
|
|
128
|
-
emitted to `generated/deploy-version.ts`): a length-prefixed SHA-256 over
|
|
129
|
-
the **bundled worker JS closure + harness + wasm + fonts + the main
|
|
130
|
-
worker's `compatibility_date`**. Any change to any hosted tool, the
|
|
131
|
-
transport, the PNG path, an asset, or the worker's runtime semantics moves
|
|
132
|
-
it and invalidates cached results, version bump or not. Isolate IDs keep the
|
|
133
|
-
narrower harness hash (they *are* just harness + user module).
|
|
134
|
-
- **Cache keys from normalized effective arguments.** The key was a hash of
|
|
135
|
-
raw `req.params`; handlers drop unknown args and clamp `scale`/`timeoutMs`,
|
|
136
|
-
so a caller could add `{ nonce }` or vary an out-of-range `scale` to force
|
|
137
|
-
recompute and defeat the cost control. `cacheKeyFor` (in `hosted-server.ts`,
|
|
138
|
-
the single source of truth the handlers also use for clamping) now derives
|
|
139
|
-
the key from only the output-affecting arguments, normalized: unknown keys
|
|
140
|
-
dropped, `scale` clamped and defaulted to its resolved value (omitted and
|
|
141
|
-
explicit `scale: 2` share one entry), `timeoutMs` excluded from `execute`
|
|
142
|
-
(it is a budget, not an input). Calls it deems uncacheable (unknown tool,
|
|
143
|
-
missing required arg, non-base64 `render_png` output) bypass the cache and
|
|
144
|
-
run directly — they error, and errors were never cached.
|
|
145
|
-
|
|
146
|
-
**Scope of the cost-control guarantee (external audit, round 3 — adversarial
|
|
147
|
-
multi-agent verification found no correctness/poisoning bug; these are the
|
|
148
|
-
low-severity refinements it surfaced):**
|
|
149
|
-
|
|
150
|
-
- The normalization covers **arguments**, not the `source`/`code` payload,
|
|
151
|
-
which is keyed **verbatim by design**. Keying on the raw payload is what
|
|
152
|
-
makes a cached response provably correspond to what that exact input
|
|
153
|
-
renders; canonicalizing the payload (e.g. stripping Mermaid comments) is
|
|
154
|
-
deliberately avoided because two payloads that canonicalize alike are not
|
|
155
|
-
guaranteed to render byte-identically — a wrong cached result is far worse
|
|
156
|
-
than a missed dedup. A caller can therefore still force recompute by varying
|
|
157
|
-
insignificant payload bytes (comments/trailing whitespace). That residual is
|
|
158
|
-
bounded by the **WAF rate limit** (the actual abuse backstop; see
|
|
159
|
-
`website/README.md`), not by the cache.
|
|
160
|
-
- `execute` is cached on `code` alone. Code Mode is intended for deterministic
|
|
161
|
-
SDK workflows; a non-deterministic body (`Date`/`Math.random`) has its first
|
|
162
|
-
result frozen for the cache TTL. This is pre-existing — `execute` results
|
|
163
|
-
were always cached — and inherent to caching arbitrary code; it is not a
|
|
164
|
-
cross-caller integrity issue (identical `code` → identical key by definition).
|
|
165
|
-
- The response cache is a **cost optimization for legitimate repeat traffic**,
|
|
166
|
-
not the abuse control. The WAF rate-limiting rule on `POST /mcp` is the
|
|
167
|
-
primary defense against a determined attacker and remains a launch
|
|
168
|
-
requirement.
|
|
169
|
-
|
|
170
|
-
**Post-review hardening (external audit, round 4 — full-PR multi-agent audit,
|
|
171
|
-
5 lenses):** one P1 (wrapper-breakout containment) and a set of low-severity
|
|
172
|
-
test-coverage and transport refinements. The verified fixes:
|
|
173
|
-
|
|
174
|
-
- **Wrapper-breakout containment, stated honestly.** Agent code is concatenated
|
|
175
|
-
into an ES module, so code that closes the harness function early with `}`
|
|
176
|
-
reaches module scope. Two layers close what the isolate boundary already
|
|
177
|
-
bounds: (1) the harness wraps the user function in **parentheses** (an
|
|
178
|
-
expression position), which makes an injected top-level `import`/`export`/`;`
|
|
179
|
-
a **SyntaxError** — so a `import ... from 'cloudflare:sockets'` breakout fails
|
|
180
|
-
the isolate start rather than running; and (2) `hardenIsolateGlobals()` runs
|
|
181
|
-
at isolate startup, **before** `user.js` is dynamically imported, and strips
|
|
182
|
-
capability globals (`fetch`, `caches`, `crypto`, `connect`, …) plus
|
|
183
|
-
`Error.prepareStackTrace`. A comma+IIFE tail can still *run* at eval time, but
|
|
184
|
-
only against stripped capabilities. These are **defense in depth** — the
|
|
185
|
-
guaranteed boundary remains the isolate config (`globalOutbound: null`, empty
|
|
186
|
-
env, no bindings, `cpuMs`). `NEUTRALIZED_ISOLATE_GLOBALS` is restricted to
|
|
187
|
-
globals provably unused by the harness and the pure SDK at run time (verified
|
|
188
|
-
by the wrangler e2e, which still renders after hardening).
|
|
189
|
-
- **Test-coverage gaps closed.** The Promise.race wall-clock backstop, the
|
|
190
|
-
empty/non-JSON isolate-body malformed guard, `readCapped`'s byte-exact cap
|
|
191
|
-
(boundary + cross-chunk + UTF-8 straddle), multi-chunk base64 encoding, and
|
|
192
|
-
CORS headers on HTTP-level error responses now have discriminating tests
|
|
193
|
-
(each fails when its mechanism is removed).
|
|
194
|
-
- **Transport nits.** The Promise.race timer is cleared in a `finally` so a
|
|
195
|
-
winning fetch leaves no dangling timer, and an empty/non-JSON isolate body
|
|
196
|
-
degrades to `sandbox returned a malformed result` instead of leaking a raw
|
|
197
|
-
`JSON.parse` error.
|
|
198
|
-
|
|
199
|
-
Residual honesty: `website/e2e-mcp.sh` is a **manual** probe run against a live
|
|
200
|
-
`wrangler dev` (it needs the Worker Loader), **not** a CI gate; the CI gate is
|
|
201
|
-
`bun test src/__tests__/`. The isolate-level containment (globals stripped on
|
|
202
|
-
the real `globalThis`, breakout rejected) is what the e2e pins; the unit suite
|
|
203
|
-
pins the pure, runtime-neutral pieces (`neutralizeGlobalsOn` on a throwaway
|
|
204
|
-
target, `userModuleSources` rejection, the log cap).
|
|
205
|
-
|
|
206
|
-
**Post-review hardening (external audit, round 5 — transport abuse/conformance):**
|
|
207
|
-
|
|
208
|
-
- **Batch fan-out cap.** A JSON-RPC array ran every item under `Promise.all`,
|
|
209
|
-
so one 128 KB body could pack many `tools/call` items that each spin a
|
|
210
|
-
billable isolate/render — a single request amplifying past the per-IP WAF
|
|
211
|
-
limit. `MAX_BATCH_ITEMS` (20) now bounds a batch, refused with 400 before any
|
|
212
|
-
item runs.
|
|
213
|
-
- **`MCP-Protocol-Version` validation + 2025-06-18 batch rule.** The transport
|
|
214
|
-
now rejects an explicit unsupported version header with 400, and — because
|
|
215
|
-
MCP 2025-06-18 *removed* JSON-RPC batching — refuses an array body when the
|
|
216
|
-
header pins that revision. Older negotiated versions (2024-11-05 / 2025-03-26,
|
|
217
|
-
or no header) may still batch, so existing clients are unaffected.
|
|
218
|
-
- **CORS Origin validation.** Wildcard `Access-Control-Allow-Origin: *` on a
|
|
219
|
-
public, credential-less endpoint is not itself an access-control hole — CORS
|
|
220
|
-
gates only browser reads and never the agent/server clients that ignore it —
|
|
221
|
-
but `*` does let an arbitrary site silently drive a *visitor's* browser
|
|
222
|
-
against the endpoint (distributed across visitor IPs, diluting the per-IP WAF
|
|
223
|
-
limit). CORS is now **reflective with Origin validation**: a no-Origin client
|
|
224
|
-
(agent/server) still gets `*`; a browser Origin is echoed only when it is
|
|
225
|
-
same-origin / localhost / allowlisted (`agentic-mermaid.dev`); a disallowed
|
|
226
|
-
browser Origin gets no `Access-Control-Allow-Origin` and a 403. This follows
|
|
227
|
-
the MCP Streamable HTTP Origin-validation guidance without breaking any
|
|
228
|
-
non-browser client (the website itself does not call `/mcp` from the browser).
|
|
229
|
-
|
|
230
|
-
## Transport: stateless Streamable HTTP
|
|
231
|
-
|
|
232
|
-
Unchanged from the original plan and still the cheapest correct choice — the
|
|
233
|
-
tools are pure functions of their inputs:
|
|
234
|
-
|
|
235
|
-
- Single `POST /mcp` accepting JSON-RPC, responding `application/json`.
|
|
236
|
-
No sessions, no SSE stream, `GET /mcp` → 405, notifications → 202.
|
|
237
|
-
- No Durable Objects, agents SDK, KV, R2, or Queues.
|
|
238
|
-
- Protocol version negotiated from a known-good list (the core currently pins
|
|
239
|
-
`2024-11-05`; Streamable HTTP clients offer `2025-03-26`+).
|
|
240
|
-
- **Response caching:** layout is deterministic, so `tools/call` responses
|
|
241
|
-
(except nothing — all eight tools are deterministic) are cached in the Workers
|
|
242
|
-
Cache API keyed on SHA-256 of `(tool, canonicalized arguments)`. Repeat
|
|
243
|
-
requests skip compute entirely; for `execute` they also skip the dynamic
|
|
244
|
-
Worker, which is the biggest cost lever.
|
|
245
|
-
- Hygiene: 128 KB body cap, 64 KB Mermaid-source/code caps with structured
|
|
246
|
-
errors pointing at the local CLI, `content-type` enforcement, Origin-validated
|
|
247
|
-
CORS (no-Origin agent/server clients keep `*`; allowed browser origins are
|
|
248
|
-
echoed; disallowed browser origins get 403), one WAF rate-limiting rule
|
|
249
|
-
(e.g. 60 req/min/IP on `POST /mcp`, configured in the dashboard and
|
|
250
|
-
recorded in `website/README.md`).
|
|
251
|
-
|
|
252
|
-
## Cost model (Workers Paid)
|
|
253
|
-
|
|
254
|
-
| Meter | Included | Overage | Our exposure |
|
|
255
|
-
|---|---|---|---|
|
|
256
|
-
| Worker requests | 10 M/month | $0.30/M | every `/mcp` call + one internal request per uncached `execute` |
|
|
257
|
-
| CPU time | 30 M ms/month | $0.02/M ms | renders are ms-scale; `execute` capped at 30 s by `cpuMs` |
|
|
258
|
-
| Unique dynamic Workers | 1,000/month | $0.002/Worker/day | one per **unique** `execute` code string per day (hash-keyed + edge-cached; retries and repeats dedupe) |
|
|
259
|
-
|
|
260
|
-
Levers that keep this near the plan's $5/month floor: edge caching of
|
|
261
|
-
deterministic responses, code-hash isolate reuse, parent-side rejection of
|
|
262
|
-
unsupported code, `subRequests: 0`, size caps, and the WAF rate limit as the
|
|
263
|
-
abuse backstop. The pure tools keep the high-volume paths (render/verify) off
|
|
264
|
-
the dynamic-Worker meter entirely. No other billable primitives are used.
|
|
265
|
-
|
|
266
|
-
## Verification strategy
|
|
267
|
-
|
|
268
|
-
Two external toolkits gate this work, per the repo owner's direction:
|
|
269
|
-
|
|
270
|
-
- **testing-best-practices** (<https://github.com/adewale/testing-best-practices>):
|
|
271
|
-
- Red-green for new behavior: hosted-core tests written to fail without the
|
|
272
|
-
implementation; red/green evidence stated in the PR.
|
|
273
|
-
- **Differential testing** (`references/differential-testing.md`): the local
|
|
274
|
-
`node:vm` sandbox is the reference implementation; a corpus of Code Mode
|
|
275
|
-
snippets (happy paths, SDK misuse, mutation attempts, sync violations,
|
|
276
|
-
serialization edges) runs through both `executeInSandbox` and the harness
|
|
277
|
-
execution semantics, asserting matching `{ ok, value, logs, error }`.
|
|
278
|
-
- Error-path tests, not just invalid input: loader failures, cpu-limit
|
|
279
|
-
exceptions, oversized bodies, wrong content types, malformed JSON-RPC.
|
|
280
|
-
- Real objects over mocks: transport tests drive the actual `fetch` handler
|
|
281
|
-
with real `Request`/`Response`; the only faked seam is the Worker Loader
|
|
282
|
-
binding (a purpose-built fake implementing `get/getEntrypoint`, since
|
|
283
|
-
workerd is not available inside `bun test`), and a `wrangler dev` e2e
|
|
284
|
-
covers the real binding.
|
|
285
|
-
- **cfdoctor** (<https://github.com/adewale/cfdoctor>): static scan
|
|
286
|
-
(`cfdoctor_static_scan.py`) plus the skill's audit playbook run against
|
|
287
|
-
`website/` before shipping; findings triaged with evidence and fixed or
|
|
288
|
-
explicitly accepted in the audit output.
|
|
289
|
-
|
|
290
|
-
Plus the repo's own gates: `bun test src/__tests__/` (existing MCP tests must
|
|
291
|
-
stay green through the refactor), `bunx tsc --noEmit`, `bun run
|
|
292
|
-
website:check`, and a `wrangler dev` end-to-end pass (`website/e2e-mcp.sh`:
|
|
293
|
-
initialize → tools/list → each tool, including a real dynamic-Worker
|
|
294
|
-
`execute`). Known local-dev limitation found in that pass: wrangler dev does
|
|
295
|
-
not enforce dynamic-worker `cpuMs`, so an unbounded sync loop wedges local
|
|
296
|
-
workerd; production enforces `cpuMs` at the runtime and the parent races a
|
|
297
|
-
wall-clock backstop so `/mcp` always answers. The e2e also flushed out an
|
|
298
|
-
ELK instantiation failure under workerd (fixed in `src/elk-instance.ts` by
|
|
299
|
-
flipping elk-worker's environment sniff with a temporary `document` global
|
|
300
|
-
instead of deleting the undeletable `self`).
|
|
301
|
-
|
|
302
|
-
## Implementation steps
|
|
303
|
-
|
|
304
|
-
1. **Extract runtime-neutral modules.** `src/mcp/protocol.ts` (JSON-RPC
|
|
305
|
-
types + reply/error helpers), `src/mcp/facade.ts` (`createTracingMermaid`,
|
|
306
|
-
`unsupportedCodeReason`, wrapping helpers) importing the pure agent
|
|
307
|
-
surface, not the barrel (the barrel exports `png.ts`, which drags native
|
|
308
|
-
resvg into every graph). `server.ts`/`sandbox.ts` keep their exports and
|
|
309
|
-
behavior; existing tests untouched.
|
|
310
|
-
2. **Hosted core** `src/mcp/hosted-server.ts`: `handleHostedRequest(req,
|
|
311
|
-
context)` with the eight-tool surface; `execute` and `render_png` accept
|
|
312
|
-
injected implementations so the core stays runtime-neutral and testable in
|
|
313
|
-
`bun test`.
|
|
314
|
-
3. **Harness** `src/mcp/dynamic-harness.ts` (entry compiled to a single text
|
|
315
|
-
asset by `website/build.ts`): fetch handler that builds the facade, imports
|
|
316
|
-
the user module (expression-first with `SyntaxError` fallback), executes,
|
|
317
|
-
serializes to the `ExecuteResult` contract.
|
|
318
|
-
4. **Website worker** `website/src/worker.ts`: existing static/redirect logic
|
|
319
|
-
unchanged; `/mcp` branch implements the streamable-HTTP handler, cache
|
|
320
|
-
layer, loader-backed `execute`, wasm `render_png`. `wrangler.jsonc` gains
|
|
321
|
-
`worker_loaders`, text/data module rules for the harness and font, and
|
|
322
|
-
keeps Static Assets.
|
|
323
|
-
5. **Tests** as described under verification; `src/__tests__/website-build.test.ts`
|
|
324
|
-
now asserts the live hosted endpoint (the 501 assertion it carried is gone).
|
|
325
|
-
6. **cfdoctor audit + wrangler dev e2e**, fix findings.
|
|
326
|
-
7. **Docs**: `website/README.md`, `docs/mcp-http-transport.md` cross-link, a
|
|
327
|
-
hosted-MCP section documenting the endpoint, tool surface, limits,
|
|
328
|
-
divergences, and the dashboard-side rate-limit rule.
|
|
329
|
-
|
|
330
|
-
## Explicit non-goals
|
|
331
|
-
|
|
332
|
-
- No REST render API — MCP only.
|
|
333
|
-
- No accounts or tokens; abuse control is caps + rate limiting.
|
|
334
|
-
- No hosted artifact storage; `render_png` returns base64 only (`output:
|
|
335
|
-
"file"/"url"` stay local-server features).
|
|
336
|
-
- Durable Object facets, dynamic Workflows, and egress-controlled outbound
|
|
337
|
-
fetch (Dynamic Workers features) are all unused — the isolates need no
|
|
338
|
-
state and no network.
|