agentic-mermaid 0.1.1 → 0.2.0
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/AGENT_NATIVE.md +155 -91
- package/CHANGELOG.md +163 -4
- package/Instructions_for_agents.md +10 -10
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +31 -38
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/assets/hero.png +0 -0
- package/dist/agent-core.d.ts +463 -0
- package/dist/agent-core.js +397 -0
- package/dist/agent.d.ts +73 -1868
- package/dist/agent.js +336 -37325
- package/dist/agentic-mermaid-mcp.js +7 -38399
- package/dist/am.js +33626 -40446
- package/dist/chunk-2P5FKUQC.js +3485 -0
- package/dist/chunk-2S3VSS6I.js +149 -0
- package/dist/chunk-2SILJDW2.js +37 -0
- package/dist/chunk-BJYMBT4I.js +61963 -0
- package/dist/chunk-FDM43XU7.js +615 -0
- package/dist/chunk-MYPDVZCO.js +4702 -0
- package/dist/index-DWPa2MJs.d.ts +6780 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +310 -33014
- package/dist/mcp-cli-5J47WEY3.js +17 -0
- package/docs/README.md +39 -31
- package/docs/agent-api-cookbook.md +44 -35
- package/docs/agent-mutation-policy.md +1 -1
- package/docs/agent-workflow-examples.md +2 -2
- package/docs/api.md +284 -77
- package/docs/ascii.md +26 -16
- package/docs/code-mode-impact.md +214 -0
- package/docs/comparison.md +23 -11
- package/docs/computational-aesthetics-prototype-plan.md +252 -0
- package/docs/config.md +26 -10
- package/docs/custom-fonts.md +265 -0
- package/docs/custom-style-cookbook.md +130 -9
- package/docs/diagram-families.md +48 -23
- package/docs/features.md +59 -52
- package/docs/fork-differences.md +6 -3
- package/docs/getting-started.md +23 -16
- package/docs/issue-derived-test-cases.md +29 -22
- package/docs/mcp-code-mode-rationale.md +13 -6
- package/docs/mcp-http-transport.md +20 -13
- package/docs/mermaid-layout-complaints.md +66 -52
- package/docs/mutation-testing.md +145 -66
- package/docs/quality.md +23 -14
- package/docs/react.md +2 -2
- package/docs/schemas/style-spec.schema.json +1221 -30
- package/docs/style-authoring.md +241 -38
- package/docs/svg-semantic-contract.md +49 -0
- package/docs/testing-strategy.md +166 -58
- package/docs/theming.md +19 -32
- 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 +85 -45
- package/server.json +2 -2
- 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/TODO.md +0 -684
- package/dist/index-wjRjbsP-.d.ts +0 -1102
- 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/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 -82
- package/docs/contributing/visual-review-evidence.md +0 -53
- 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-experience-curve-implementation.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 -439
- package/docs/design/families/journey-section-overlap-after.png +0 -0
- package/docs/design/families/journey-section-overlap-before.png +0 -0
- package/docs/design/families/journey-usage-research.md +0 -423
- package/docs/design/families/journey.md +0 -153
- 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/layout-characterization/README.md +0 -245
- package/docs/layout-characterization/contact-sheet-families.md +0 -498
- 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 -98
- 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/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/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 -368
- package/src/agent/er-body.ts +0 -224
- package/src/agent/facts.ts +0 -360
- package/src/agent/families-builtin.ts +0 -920
- package/src/agent/families.ts +0 -232
- package/src/agent/family-layouts.ts +0 -832
- package/src/agent/flowchart-body.ts +0 -372
- package/src/agent/flowchart-unsupported.ts +0 -243
- package/src/agent/font-coverage.ts +0 -288
- package/src/agent/gantt-body.ts +0 -495
- package/src/agent/index.ts +0 -10
- package/src/agent/journey-body.ts +0 -374
- 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 -443
- package/src/agent/parse.ts +0 -223
- package/src/agent/pie-body.ts +0 -270
- package/src/agent/png.ts +0 -171
- 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 -995
- package/src/agent/verify.ts +0 -603
- 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 -169
- 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 -141
- package/src/ascii/types.ts +0 -312
- package/src/ascii/validate.ts +0 -120
- package/src/ascii/width.ts +0 -81
- package/src/ascii/wrap.ts +0 -131
- 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 -1132
- package/src/cli/init-agent.ts +0 -147
- package/src/cli/run-entrypoint.ts +0 -10
- 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/family-rubric.ts +0 -425
- package/src/font-manifest.ts +0 -31
- 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 -554
- package/src/journey/parse-core.ts +0 -251
- package/src/journey/parser.ts +0 -101
- package/src/journey/renderer.ts +0 -917
- 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 -391
- 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 -93
- 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 -710
- 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 -462
- 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,98 +0,0 @@
|
|
|
1
|
-
# Divergences
|
|
2
|
-
|
|
3
|
-
Current implementation decisions that differ from, or materially narrow, the broader `AGENT_NATIVE.md` architecture. This file is **not** a backlog; active work lives only in `TODO.md`. Historical loop logs were removed because they had become process noise. Use Git history for archaeology.
|
|
4
|
-
|
|
5
|
-
## What still matters
|
|
6
|
-
|
|
7
|
-
### Determinism is structural, not seeded
|
|
8
|
-
|
|
9
|
-
- Removed the old `LayoutContext` / `SeededRNG` / `Clock` / `withSeededRandom` / font-metrics apparatus.
|
|
10
|
-
- `RenderedLayout` has no `seed` field.
|
|
11
|
-
- `VerifyOptions` is intentionally small: `{ suppress?, labelCharCap? }`.
|
|
12
|
-
- Claim scope: same-input structural layout determinism is tested for the supported runtime/version matrix. Cross-machine or cross-architecture float equality is **not** claimed.
|
|
13
|
-
|
|
14
|
-
### Package and compatibility choices
|
|
15
|
-
|
|
16
|
-
- Product/docs, npm package name, and repository path are **Agentic Mermaid** / `agentic-mermaid` / `adewale/agentic-mermaid`. The agent surface ships as the `./agent` subpath.
|
|
17
|
-
- `MermaidGraph` and `renderMermaidSVGAsync` remain for compatibility with existing renderer/tests/consumers.
|
|
18
|
-
- ~~`state` diagrams currently share the flowchart body (`body.kind: 'flowchart'`)…~~ **Superseded by BUILD-19.** State diagrams now own a dedicated `StateBody` IR (`body.kind: 'state'`) with state-shaped ops and a real `asState` narrower. `asFlowchart` returns `null` on a state diagram (a breaking change within the unreleased agent surface). Verify still gets full Tier 1 + Tier 2 geometric coverage: the `StateBody` projects to a `MermaidGraph` via the legacy parser (`stateBodyToGraph`) and runs the identical flowchart `verifyGraph`. The modeled subset is simple states/transitions/`[*]` pseudostates/composites/`direction`; unmodeled syntax (`<<fork>>`/`<<choice>>`/`<<join>>`, history, concurrency `--`, notes, `classDef`/`class`/`:::`) falls back to a lossless opaque body. Corpus round-trip for state jumped 5% → 100%.
|
|
19
|
-
|
|
20
|
-
### Mutation surface is intentionally narrower than Mermaid syntax
|
|
21
|
-
|
|
22
|
-
Structured mutation is exposed for every built-in renderable family, but only when that body's parser/IR/serializer/verifier can preserve the modeled semantics:
|
|
23
|
-
|
|
24
|
-
- flowchart/state;
|
|
25
|
-
- sequence (BUILD-18 — segment-preserving: participant/message ops stay live while Note/alt/loop/par/activate/autonumber/title ride along verbatim as opaque-block segments; only un-segmentable input such as an unbalanced `end` falls back to whole-body opaque);
|
|
26
|
-
- timeline;
|
|
27
|
-
- class;
|
|
28
|
-
- ER;
|
|
29
|
-
- journey (BUILD-15 pilot);
|
|
30
|
-
- architecture (BUILD-17 — the modeled subset of groups/services/junctions/edges);
|
|
31
|
-
- xychart (BUILD-16 — the modeled subset of title/axes/series);
|
|
32
|
-
- pie;
|
|
33
|
-
- quadrant;
|
|
34
|
-
- gantt.
|
|
35
|
-
|
|
36
|
-
Opaque/source-level bodies:
|
|
37
|
-
|
|
38
|
-
- any known-family diagram that falls back to opaque because it contains unmodeled syntax (e.g. architecture `{group}` boundary modifiers, accTitle/accDescr, malformed pie entries, out-of-range quadrant coordinates, or un-segmentable sequence/gantt syntax).
|
|
39
|
-
|
|
40
|
-
For source-level bodies, agents may render, verify, describe, and round-trip preserved source. They do **not** get typed mutation ops. `am mutate` returns `UNSUPPORTED_FAMILY` for those bodies.
|
|
41
|
-
|
|
42
|
-
### Structured-or-opaque is load-bearing
|
|
43
|
-
|
|
44
|
-
Known-family input must never be partially parsed and then re-emitted with unknown constructs dropped. If the structured parser cannot preserve a construct, the body stays opaque/source-preserved and serializes from `body.source`.
|
|
45
|
-
|
|
46
|
-
BUILD-18 refines "opaque/source-preserved" for sequence into a finer grain: a sequence body now interleaves structured statements with **opaque-block segments** holding unmodeled lines verbatim, so the structured ops survive instead of being forfeited at the first unmodeled line. The never-lossy invariant is unchanged — the segment lines are byte-for-byte preserved — and whole-body opaque is still the fallback for un-segmentable input. Class/ER/timeline can adopt the same segment-preserving body as follow-up work.
|
|
47
|
-
|
|
48
|
-
This applies even when the diagram renders successfully. Render support is not the same as structured editing support.
|
|
49
|
-
|
|
50
|
-
### Code Mode is local, synchronous, and lineage-checked
|
|
51
|
-
|
|
52
|
-
- The MCP server exposes a Code Mode-style `execute(code)` tool backed by local `node:vm`; it is not Cloudflare Codemode and not a container/OS security boundary.
|
|
53
|
-
- Code Mode is synchronous: no `async`/`await`, Promise jobs, dynamic import, finalizers, or blocking/realm-creating globals.
|
|
54
|
-
- SDK-returned diagrams are read-only. Structured edits must go through `mermaid.mutate(...)` and must use trusted diagram lineage from parse/mutate/narrower outputs.
|
|
55
|
-
|
|
56
|
-
### Runtime surfaces are part of the contract
|
|
57
|
-
|
|
58
|
-
Keep these in sync when changing agent behavior:
|
|
59
|
-
|
|
60
|
-
- `src/agent/*` public API;
|
|
61
|
-
- `am capabilities` and CLI help;
|
|
62
|
-
- MCP SDK declaration and server instructions;
|
|
63
|
-
- `Instructions_for_agents.md` / `am --agent-instructions`;
|
|
64
|
-
- `skills/agentic-mermaid-diagram-workflow/*`;
|
|
65
|
-
- `skills/agentic-mermaid-live-editor/SKILL.md`;
|
|
66
|
-
- `llms.txt`;
|
|
67
|
-
- `docs/agent-api-cookbook.md`.
|
|
68
|
-
|
|
69
|
-
Doc-sync tests exist because agents consume docs and capability JSON as runtime surface, not just prose.
|
|
70
|
-
|
|
71
|
-
## Important fixed issues worth remembering
|
|
72
|
-
|
|
73
|
-
These are retained only because they explain guardrails that may otherwise look excessive:
|
|
74
|
-
|
|
75
|
-
- **Opaque fidelity:** opaque bodies serialize from preserved `body.source`, not normalized `canonicalSource`, so indentation/comments/source text survive parse → serialize.
|
|
76
|
-
- **`am parse | am serialize`:** JSON payloads preserve flowchart styling maps and defensively normalize loose SDK-shaped subgraphs.
|
|
77
|
-
- **Subgraph payload hardening:** `synthesizeFromGraph` tolerates missing `children`, null entries, cyclic references, non-tuple map arrays, and non-integer linkStyle keys.
|
|
78
|
-
- **Timeline mutation:** timeline edits update `canonicalSource`, validate mutation text, allocate unique IDs, and fall back to opaque for header-suffix/lossy syntax.
|
|
79
|
-
- **Shared routing cleanup:** SVG, ASCII, and agent parsing use shared diagram-family detection from `src/mermaid-source.ts`; agent parsing adds only a loose known-family path so malformed known headers can round-trip as opaque.
|
|
80
|
-
- **Source-based `LABEL_OVERFLOW`:** verification checks labels for opaque/source-level families via family label extractors, so fallback does not mean "unverified."
|
|
81
|
-
|
|
82
|
-
## Evidence and limits
|
|
83
|
-
|
|
84
|
-
What the current suite demonstrates:
|
|
85
|
-
|
|
86
|
-
- typed mutation works for the exposed structured families;
|
|
87
|
-
- unsupported/opaque/source-level bodies refuse structured mutation instead of losing source;
|
|
88
|
-
- verify-before-serialize and Code Mode trace linting catch key agent anti-patterns;
|
|
89
|
-
- corpus gates cover Mermaid docs examples and MermaidSeqBench round-trip stability;
|
|
90
|
-
- docs/capabilities/CLI/MCP declarations are checked for consistency.
|
|
91
|
-
|
|
92
|
-
What it does **not** yet prove:
|
|
93
|
-
|
|
94
|
-
- market demand;
|
|
95
|
-
- that a real external consumer likes the API;
|
|
96
|
-
- that a selected release model follows the intended loop from docs alone.
|
|
97
|
-
|
|
98
|
-
Those validation items remain in `TODO.md`.
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# Dogfooding our docs: render and pin our own documentation with our own tools
|
|
2
|
-
|
|
3
|
-
Status: strategy. Drafted 2026-06-20.
|
|
4
|
-
|
|
5
|
-
Agentic Mermaid is a deterministic diagram engine with a typed, verifiable agent surface. Our
|
|
6
|
-
documentation should meet the same bar as our output: **generated by the system it documents, and
|
|
7
|
-
pinned by a test so it cannot silently drift.** An architecture diagram that rots, or a "supported
|
|
8
|
-
families" list that lags the registry, is worse than no doc — it actively misleads.
|
|
9
|
-
|
|
10
|
-
## Principle
|
|
11
|
-
|
|
12
|
-
If a piece of documentation can be *derived* from the code, derive it — then add a test that
|
|
13
|
-
regenerates it from the source of truth and fails when the committed doc disagrees. This is the
|
|
14
|
-
documentation analogue of the determinism and round-trip guarantees the engine already enforces
|
|
15
|
-
([`AGENT_NATIVE.md`](../../AGENT_NATIVE.md) §1, §3): the same inputs must produce the same artifact,
|
|
16
|
-
and the artifact is checked, not trusted.
|
|
17
|
-
|
|
18
|
-
Two regeneration shapes, both gated by `UPDATE_GOLDEN=1 bun test <file>`:
|
|
19
|
-
|
|
20
|
-
| Shape | Source of truth | Artifact | Test asserts |
|
|
21
|
-
|---|---|---|---|
|
|
22
|
-
| **Rendered figure** | a committed `.mmd` | a committed `.svg`, rendered by `renderMermaidSVG` | deterministic re-render + byte-match (normalized) |
|
|
23
|
-
| **Projected table/list** | a code registry (e.g. `BUILTIN_FAMILY_METADATA`) | a marked region in a `.md` | rebuild from the registry + region match |
|
|
24
|
-
|
|
25
|
-
## Instances in the repo
|
|
26
|
-
|
|
27
|
-
1. **The architecture figure** — `docs/design/system/architecture.{mmd,svg}`, pinned by
|
|
28
|
-
`src/__tests__/docs-architecture-diagram.test.ts`. The three-stacks diagram is authored as
|
|
29
|
-
Mermaid and rendered by our own engine; the test asserts the render is deterministic across
|
|
30
|
-
calls and matches the committed SVG. Docs *about* the system, produced *by* the system.
|
|
31
|
-
2. **The family roster table** — the built-in-families table in
|
|
32
|
-
`docs/design/system/abstraction-audit.md` §2, pinned by `src/__tests__/audit-family-table.test.ts`.
|
|
33
|
-
It is generated from `BUILTIN_FAMILY_METADATA`; add or rename a family and the test fails until
|
|
34
|
-
the audit is regenerated.
|
|
35
|
-
|
|
36
|
-
These join the repo's existing doc-sync guarantees, which already follow the same philosophy:
|
|
37
|
-
|
|
38
|
-
- `Instructions_for_agents.md` is byte-identical to `am --agent-instructions` (doc-sync test).
|
|
39
|
-
- `package.json` keywords, `llms.txt`, editor examples, and CLI capabilities are all projected from
|
|
40
|
-
`BUILTIN_FAMILY_METADATA` and checked (`doc-sync.test.ts`, `diagram-family-citizenship.test.ts`).
|
|
41
|
-
- Every `LayoutWarning` code and `MutationOp` kind must appear in `AGENT_NATIVE.md` (doc-sync).
|
|
42
|
-
|
|
43
|
-
## How to add a dogfooded doc
|
|
44
|
-
|
|
45
|
-
**A rendered figure:**
|
|
46
|
-
1. Author the source as `docs/.../<name>.mmd`.
|
|
47
|
-
2. Add a snapshot test modeled on `docs-architecture-diagram.test.ts` that renders the `.mmd` with
|
|
48
|
-
`renderMermaidSVG` and compares to `<name>.svg`, with an `UPDATE_GOLDEN` write path.
|
|
49
|
-
3. Run `UPDATE_GOLDEN=1 bun test <test>` to materialize the artifact; commit both.
|
|
50
|
-
|
|
51
|
-
**A projected table/list:**
|
|
52
|
-
1. Wrap the region in the doc with `<!-- NAME:start -->` / `<!-- NAME:end -->` markers.
|
|
53
|
-
2. Add a test modeled on `audit-family-table.test.ts` that rebuilds the region from the code
|
|
54
|
-
registry and compares, with an `UPDATE_GOLDEN` write path.
|
|
55
|
-
3. Regenerate and commit.
|
|
56
|
-
|
|
57
|
-
## Boundaries
|
|
58
|
-
|
|
59
|
-
- **Pin what is *derivable*.** Prose judgement — the audit's analysis, the recommendations — is not
|
|
60
|
-
mechanically generated; it is reviewed like code. Only the derivable parts (the figure, the
|
|
61
|
-
roster) are auto-pinned.
|
|
62
|
-
- **Rendered-SVG snapshots carry the "within one ELK version" determinism assumption** of the
|
|
63
|
-
existing `*-svg-snapshot` goldens (`AGENT_NATIVE.md` §1). They guard drift within the CI
|
|
64
|
-
environment, not cross-version byte-equality.
|
|
65
|
-
- **Keep generated regions small and clearly marked** so a reader — and `git diff` — can see what
|
|
66
|
-
is machine-owned versus hand-written.
|
|
@@ -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.
|