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.
Files changed (333) hide show
  1. package/CHANGELOG.md +149 -4
  2. package/LICENSES/Apache-2.0.txt +202 -0
  3. package/README.md +25 -32
  4. package/THIRD_PARTY_NOTICES.md +29 -0
  5. package/assets/fonts/FONT-LICENSES.md +9 -5
  6. package/assets/fonts/Inter-Bold.ttf +0 -0
  7. package/assets/fonts/Inter-Medium.ttf +0 -0
  8. package/assets/fonts/Inter-Regular.ttf +0 -0
  9. package/assets/fonts/Inter-SemiBold.ttf +0 -0
  10. package/dist/agent-core.d.ts +463 -0
  11. package/dist/agent-core.js +397 -0
  12. package/dist/agent.d.ts +81 -1822
  13. package/dist/agent.js +336 -36839
  14. package/dist/agentic-mermaid-mcp.js +7 -37908
  15. package/dist/am.js +33638 -36261
  16. package/dist/chunk-4QECACIA.js +4702 -0
  17. package/dist/chunk-CDPKJ2BQ.js +37 -0
  18. package/dist/chunk-QKDLMOJJ.js +3487 -0
  19. package/dist/chunk-WI35L7IR.js +615 -0
  20. package/dist/chunk-WN3YDOBG.js +149 -0
  21. package/dist/chunk-ZYMMKQ3I.js +62637 -0
  22. package/dist/index-D54dqSdC.d.ts +6777 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +310 -32836
  25. package/dist/mcp-cli-SBQSTJFC.js +17 -0
  26. package/docs/README.md +14 -6
  27. package/docs/schemas/style-spec.schema.json +1221 -30
  28. package/examples/styles/catalog.json +54 -0
  29. package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
  30. package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
  31. package/examples/styles/cupertino-prototype.mmd +10 -0
  32. package/examples/styles/cupertino-prototype.style.json +63 -0
  33. package/examples/styles/mid-century-report.style.json +2 -2
  34. package/examples/styles/star-chart-atlas.style.json +2 -1
  35. package/examples/styles/transit-route-map.style.json +2 -2
  36. package/examples/styles/vercel-inspired-prototype.mmd +6 -0
  37. package/examples/styles/vercel-inspired-prototype.style.json +87 -0
  38. package/package.json +70 -43
  39. package/server.json +41 -0
  40. package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
  41. package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
  42. package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
  43. package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
  44. package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
  45. package/AGENT_NATIVE.md +0 -602
  46. package/Instructions_for_agents.md +0 -83
  47. package/TODO.md +0 -662
  48. package/assets/hero.png +0 -0
  49. package/dist/index-x2Cj1dVv.d.ts +0 -1089
  50. package/docs/agent-api-cookbook.md +0 -308
  51. package/docs/agent-mutation-policy.md +0 -104
  52. package/docs/agent-workflow-examples.md +0 -46
  53. package/docs/api.md +0 -203
  54. package/docs/ascii.md +0 -103
  55. package/docs/assets/improvements/box-start-connector.png +0 -0
  56. package/docs/assets/improvements/comment-dropped-lint.png +0 -0
  57. package/docs/assets/improvements/er-content-loss.png +0 -0
  58. package/docs/assets/improvements/fan-in-trunks.png +0 -0
  59. package/docs/assets/improvements/gantt-family.png +0 -0
  60. package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
  61. package/docs/assets/improvements/gantt-verify-seam.png +0 -0
  62. package/docs/assets/improvements/subgraph-container-edges.png +0 -0
  63. package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
  64. package/docs/assets/improvements/wrapper-init-directive.png +0 -0
  65. package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
  66. package/docs/assets/style-cookbook/mid-century-report.png +0 -0
  67. package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
  68. package/docs/assets/style-cookbook/transit-route-map.png +0 -0
  69. package/docs/comparison.md +0 -94
  70. package/docs/config.md +0 -105
  71. package/docs/contributing/adding-diagram-types.md +0 -144
  72. package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
  73. package/docs/contributing/diagram-family-citizenship.md +0 -82
  74. package/docs/contributing/harvesting-upstream-tests.md +0 -126
  75. package/docs/contributing/lessons-learned.md +0 -276
  76. package/docs/contributing/releasing.md +0 -71
  77. package/docs/contributing/visual-review-evidence.md +0 -53
  78. package/docs/custom-style-cookbook.md +0 -154
  79. package/docs/design/families/README.md +0 -26
  80. package/docs/design/families/architecture-beta.md +0 -48
  81. package/docs/design/families/flowchart-parser-conformance.md +0 -22
  82. package/docs/design/families/gantt-research.md +0 -137
  83. package/docs/design/families/gantt.md +0 -329
  84. package/docs/design/families/journey-agentic-cards-before.png +0 -0
  85. package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
  86. package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
  87. package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
  88. package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
  89. package/docs/design/families/journey-migration-parity.md +0 -433
  90. package/docs/design/families/journey-usage-research.md +0 -423
  91. package/docs/design/families/journey.md +0 -125
  92. package/docs/design/families/xychart.md +0 -85
  93. package/docs/design/system/README.md +0 -45
  94. package/docs/design/system/abstraction-audit.md +0 -224
  95. package/docs/design/system/abstraction-recommendations.md +0 -655
  96. package/docs/design/system/architecture.mmd +0 -29
  97. package/docs/design/system/architecture.svg +0 -137
  98. package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
  99. package/docs/design/system/layout-rubric.md +0 -138
  100. package/docs/design/system/route-contracts.md +0 -848
  101. package/docs/design/system/source-preservation-ladder.md +0 -52
  102. package/docs/design/system/styles-rollout.md +0 -216
  103. package/docs/design/system/ugly-layouts.md +0 -119
  104. package/docs/diagram-families.md +0 -193
  105. package/docs/features.md +0 -147
  106. package/docs/fork-differences.md +0 -130
  107. package/docs/getting-started.md +0 -175
  108. package/docs/issue-derived-test-cases.md +0 -156
  109. package/docs/layout-characterization/README.md +0 -245
  110. package/docs/layout-characterization/contact-sheet-families.md +0 -496
  111. package/docs/layout-characterization/contact-sheet.md +0 -638
  112. package/docs/layout-characterization/properties.md +0 -281
  113. package/docs/layout-characterization/visual-quality.md +0 -160
  114. package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
  115. package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
  116. package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
  117. package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
  118. package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
  119. package/docs/layout-characterization/visual-snapshots/journey.svg +0 -118
  120. package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
  121. package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
  122. package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
  123. package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
  124. package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
  125. package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
  126. package/docs/mcp-code-mode-rationale.md +0 -101
  127. package/docs/mcp-http-transport.md +0 -223
  128. package/docs/mermaid-layout-complaints.md +0 -837
  129. package/docs/mutation-testing.md +0 -206
  130. package/docs/project/divergences.md +0 -98
  131. package/docs/project/dogfooding-docs-strategy.md +0 -66
  132. package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
  133. package/docs/project/lessons-learned.md +0 -1019
  134. package/docs/project/remove-role-styling-plan.md +0 -176
  135. package/docs/quality.md +0 -212
  136. package/docs/react.md +0 -97
  137. package/docs/style-authoring.md +0 -133
  138. package/docs/testing-strategy.md +0 -335
  139. package/docs/theming.md +0 -162
  140. package/examples/agent-improve-auth-flow.ts +0 -206
  141. package/examples/agent-loop.ts +0 -53
  142. package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
  143. package/llms.txt +0 -104
  144. package/skill-evals/README.md +0 -77
  145. package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
  146. package/skill-evals/fixtures/class-payment/input.mmd +0 -4
  147. package/skill-evals/fixtures/er-orders/input.mmd +0 -10
  148. package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
  149. package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
  150. package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
  151. package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
  152. package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
  153. package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
  154. package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
  155. package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
  156. package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
  157. package/skill-evals/shared-benchmark.json +0 -510
  158. package/src/agent/analyze.ts +0 -157
  159. package/src/agent/apply.ts +0 -146
  160. package/src/agent/architecture-body.ts +0 -458
  161. package/src/agent/certificates.ts +0 -110
  162. package/src/agent/class-body.ts +0 -319
  163. package/src/agent/core.ts +0 -207
  164. package/src/agent/create.ts +0 -106
  165. package/src/agent/describe.ts +0 -364
  166. package/src/agent/er-body.ts +0 -224
  167. package/src/agent/facts.ts +0 -337
  168. package/src/agent/families-builtin.ts +0 -934
  169. package/src/agent/families.ts +0 -232
  170. package/src/agent/family-layouts.ts +0 -826
  171. package/src/agent/flowchart-body.ts +0 -372
  172. package/src/agent/flowchart-unsupported.ts +0 -243
  173. package/src/agent/gantt-body.ts +0 -495
  174. package/src/agent/index.ts +0 -10
  175. package/src/agent/journey-body.ts +0 -414
  176. package/src/agent/label-metrics.ts +0 -28
  177. package/src/agent/layout-to-rendered.ts +0 -63
  178. package/src/agent/mutate.ts +0 -97
  179. package/src/agent/mutation-ops.ts +0 -51
  180. package/src/agent/op-schema.ts +0 -439
  181. package/src/agent/parse.ts +0 -223
  182. package/src/agent/pie-body.ts +0 -270
  183. package/src/agent/png.ts +0 -117
  184. package/src/agent/quadrant-body.ts +0 -336
  185. package/src/agent/quality.ts +0 -363
  186. package/src/agent/readability-audit.ts +0 -92
  187. package/src/agent/rendered-route-audit.ts +0 -61
  188. package/src/agent/sequence-body.ts +0 -345
  189. package/src/agent/serialize.ts +0 -214
  190. package/src/agent/state-body.ts +0 -589
  191. package/src/agent/structural-count.ts +0 -150
  192. package/src/agent/timeline-body.ts +0 -280
  193. package/src/agent/trace-log.ts +0 -43
  194. package/src/agent/types.ts +0 -979
  195. package/src/agent/verify.ts +0 -571
  196. package/src/agent/xychart-body.ts +0 -472
  197. package/src/architecture/config.ts +0 -282
  198. package/src/architecture/layout.ts +0 -496
  199. package/src/architecture/parser.ts +0 -375
  200. package/src/architecture/renderer.ts +0 -611
  201. package/src/architecture/types.ts +0 -113
  202. package/src/ascii/ansi.ts +0 -423
  203. package/src/ascii/architecture.ts +0 -210
  204. package/src/ascii/canvas.ts +0 -443
  205. package/src/ascii/class-diagram.ts +0 -697
  206. package/src/ascii/converter.ts +0 -475
  207. package/src/ascii/draw.ts +0 -1693
  208. package/src/ascii/edge-bundling.ts +0 -328
  209. package/src/ascii/edge-routing.ts +0 -330
  210. package/src/ascii/er-diagram.ts +0 -439
  211. package/src/ascii/gantt.ts +0 -276
  212. package/src/ascii/grid.ts +0 -747
  213. package/src/ascii/index.ts +0 -196
  214. package/src/ascii/journey.ts +0 -189
  215. package/src/ascii/meta.ts +0 -244
  216. package/src/ascii/multiline-utils.ts +0 -80
  217. package/src/ascii/pathfinder.ts +0 -301
  218. package/src/ascii/pie.ts +0 -67
  219. package/src/ascii/quadrant.ts +0 -158
  220. package/src/ascii/reverse.ts +0 -216
  221. package/src/ascii/sequence.ts +0 -499
  222. package/src/ascii/shapes/circle.ts +0 -27
  223. package/src/ascii/shapes/corners.ts +0 -143
  224. package/src/ascii/shapes/diamond.ts +0 -27
  225. package/src/ascii/shapes/hexagon.ts +0 -27
  226. package/src/ascii/shapes/index.ts +0 -106
  227. package/src/ascii/shapes/rectangle.ts +0 -168
  228. package/src/ascii/shapes/rounded.ts +0 -27
  229. package/src/ascii/shapes/special.ts +0 -332
  230. package/src/ascii/shapes/stadium.ts +0 -110
  231. package/src/ascii/shapes/state.ts +0 -192
  232. package/src/ascii/shapes/types.ts +0 -73
  233. package/src/ascii/timeline.ts +0 -128
  234. package/src/ascii/types.ts +0 -312
  235. package/src/ascii/validate.ts +0 -120
  236. package/src/ascii/width.ts +0 -81
  237. package/src/ascii/xychart.ts +0 -919
  238. package/src/browser.ts +0 -29
  239. package/src/class/layout.ts +0 -240
  240. package/src/class/parser.ts +0 -332
  241. package/src/class/renderer.ts +0 -638
  242. package/src/class/types.ts +0 -129
  243. package/src/cli/agent-instructions.ts +0 -1
  244. package/src/cli/am-bin.ts +0 -6
  245. package/src/cli/exit-codes.ts +0 -18
  246. package/src/cli/index.ts +0 -1117
  247. package/src/cli/init-agent.ts +0 -147
  248. package/src/color-resolver.ts +0 -152
  249. package/src/elk-instance.ts +0 -114
  250. package/src/er/layout.ts +0 -187
  251. package/src/er/parser.ts +0 -226
  252. package/src/er/renderer.ts +0 -792
  253. package/src/er/types.ts +0 -99
  254. package/src/font-manifest.ts +0 -27
  255. package/src/gantt/layout.ts +0 -365
  256. package/src/gantt/parser.ts +0 -282
  257. package/src/gantt/pipeline.ts +0 -27
  258. package/src/gantt/renderer.ts +0 -414
  259. package/src/gantt/schedule.ts +0 -513
  260. package/src/gantt/types.ts +0 -255
  261. package/src/index.ts +0 -368
  262. package/src/journey/layout.ts +0 -520
  263. package/src/journey/parser.ts +0 -218
  264. package/src/journey/renderer.ts +0 -811
  265. package/src/journey/types.ts +0 -144
  266. package/src/layout/geometry.ts +0 -182
  267. package/src/layout/pass.ts +0 -142
  268. package/src/layout/passes/index.ts +0 -3916
  269. package/src/layout-engine.ts +0 -1565
  270. package/src/layout-rubric.ts +0 -659
  271. package/src/mcp/artifacts.ts +0 -137
  272. package/src/mcp/dynamic-harness.ts +0 -31
  273. package/src/mcp/dynamic-modules.d.ts +0 -7
  274. package/src/mcp/facade.ts +0 -608
  275. package/src/mcp/harness-runtime.ts +0 -203
  276. package/src/mcp/hosted-server.ts +0 -468
  277. package/src/mcp/mcp-bin.ts +0 -3
  278. package/src/mcp/mcp-cli.ts +0 -82
  279. package/src/mcp/protocol.ts +0 -13
  280. package/src/mcp/sandbox.ts +0 -195
  281. package/src/mcp/sdk-decl.ts +0 -387
  282. package/src/mcp/server.ts +0 -362
  283. package/src/mcp/tool-surface.ts +0 -146
  284. package/src/mermaid-source.ts +0 -655
  285. package/src/multiline-utils.ts +0 -228
  286. package/src/parser.ts +0 -1031
  287. package/src/pie/layout.ts +0 -168
  288. package/src/pie/parser.ts +0 -136
  289. package/src/pie/renderer.ts +0 -234
  290. package/src/pie/types.ts +0 -82
  291. package/src/quadrant/layout.ts +0 -191
  292. package/src/quadrant/parser.ts +0 -183
  293. package/src/quadrant/renderer.ts +0 -322
  294. package/src/quadrant/types.ts +0 -113
  295. package/src/render-family-hooks.ts +0 -244
  296. package/src/renderer.ts +0 -1099
  297. package/src/route-contracts.ts +0 -2723
  298. package/src/scene/backend.ts +0 -76
  299. package/src/scene/fidelity.ts +0 -183
  300. package/src/scene/hybrid-backend.ts +0 -174
  301. package/src/scene/ir.ts +0 -181
  302. package/src/scene/marks.ts +0 -127
  303. package/src/scene/rough-backend.ts +0 -378
  304. package/src/scene/seed.ts +0 -46
  305. package/src/scene/style-registry.ts +0 -556
  306. package/src/sequence/layout.ts +0 -456
  307. package/src/sequence/parser.ts +0 -255
  308. package/src/sequence/renderer.ts +0 -604
  309. package/src/sequence/types.ts +0 -154
  310. package/src/shape-clipping.ts +0 -351
  311. package/src/shared/accessibility-directives.ts +0 -22
  312. package/src/shared/batched.ts +0 -61
  313. package/src/shared/color-math.ts +0 -72
  314. package/src/shared/svg-a11y.ts +0 -29
  315. package/src/shared/syntax-error.ts +0 -17
  316. package/src/shared/unicode-ranges.ts +0 -135
  317. package/src/styles.ts +0 -318
  318. package/src/svg-paths.ts +0 -28
  319. package/src/text-metrics.ts +0 -248
  320. package/src/theme.ts +0 -639
  321. package/src/timeline/layout.ts +0 -381
  322. package/src/timeline/parser.ts +0 -215
  323. package/src/timeline/renderer.ts +0 -538
  324. package/src/timeline/types.ts +0 -105
  325. package/src/types.ts +0 -454
  326. package/src/website-worker-modules.d.ts +0 -18
  327. package/src/xychart/axis-utils.ts +0 -109
  328. package/src/xychart/colors.ts +0 -124
  329. package/src/xychart/config.ts +0 -88
  330. package/src/xychart/layout.ts +0 -656
  331. package/src/xychart/parser.ts +0 -504
  332. package/src/xychart/renderer.ts +0 -640
  333. package/src/xychart/types.ts +0 -282
package/src/mcp/server.ts DELETED
@@ -1,362 +0,0 @@
1
- // agentic-mermaid Code Mode MCP server. Primary tool: execute.
2
- // Transports: stdio newline-delimited JSON-RPC and HTTP/SSE.
3
-
4
- import { randomUUID } from 'node:crypto'
5
- import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http'
6
- import { tmpdir } from 'node:os'
7
- import { join } from 'node:path'
8
- import { URL } from 'node:url'
9
- import { executeInSandbox } from './sandbox.ts'
10
- import { reply, rpcError as error, type JsonRpcRequest, type JsonRpcResponse } from './protocol.ts'
11
- import { SDK_DECLARATION } from './sdk-decl.ts'
12
- import { createDescribeTool, createExecuteTool, createRenderPngTool, dispatchMcpRequest, type McpServerSurface } from './tool-surface.ts'
13
- import { createArtifactStore, type ArtifactRecord, type ArtifactStore } from './artifacts.ts'
14
- import { renderMermaidPNG } from '../agent/png.ts'
15
- import { describeMermaidSource, describeMermaid } from '../agent/describe.ts'
16
- import { describeMermaidFacts } from '../agent/facts.ts'
17
- import { parseMermaid } from '../agent/parse.ts'
18
- import { BUILTIN_FAMILY_METADATA } from '../agent/families.ts'
19
-
20
- export type { JsonRpcRequest, JsonRpcResponse } from './protocol.ts'
21
-
22
- export interface McpRequestContext {
23
- artifactStore?: ArtifactStore
24
- maxSandboxTimeoutMs?: number
25
- }
26
-
27
- export interface HttpMcpOptions {
28
- host?: string
29
- port?: number
30
- artifactDir?: string
31
- publicUrl?: string
32
- maxArtifactBytes?: number
33
- artifactTtlMs?: number
34
- maxRpcBodyBytes?: number
35
- authToken?: string
36
- maxSandboxTimeoutMs?: number
37
- }
38
-
39
- export interface HttpMcpServer {
40
- server: Server
41
- url: string
42
- artifactStore: ArtifactStore
43
- close(): Promise<void>
44
- }
45
-
46
- const PROTOCOL_VERSION = '2024-11-05'
47
- const MAX_RPC_BODY_BYTES = 1024 * 1024
48
- const MAX_SANDBOX_TIMEOUT_MS = 30_000
49
-
50
- class HttpStatusError extends Error {
51
- constructor(readonly status: number, message: string) { super(message) }
52
- }
53
-
54
- export const LOCAL_TOOLS = [
55
- createExecuteTool({ sdkDeclaration: SDK_DECLARATION }),
56
- createRenderPngTool('local'),
57
- createDescribeTool(),
58
- ]
59
-
60
- let defaultArtifactStore: ArtifactStore | undefined
61
- const MCP_NARROWERS = BUILTIN_FAMILY_METADATA.map(f => f.narrower).join('/')
62
-
63
- const LOCAL_INSTRUCTIONS = `agentic-mermaid Code Mode server. Primary tool execute runs synchronous JavaScript against the typed mermaid.* SDK in a sandbox; async/await and Promise jobs are not supported. render_png and describe are narrow helpers. render_png can return base64, managed file paths, or managed URLs when the transport config provides an artifact store. mutate is overloaded by family; narrow via ${MCP_NARROWERS}. Every built-in renderable family ships a typed path when the body narrows; only opaque fallback bodies are source-level only. Layout is deterministic; there is no layout seed (the optional style seed only re-rolls ink of styled looks, never geometry).`
64
-
65
- const LOCAL_SURFACE: McpServerSurface<McpRequestContext> = {
66
- protocolVersion: PROTOCOL_VERSION,
67
- tools: LOCAL_TOOLS,
68
- instructions: LOCAL_INSTRUCTIONS,
69
- handleToolCall,
70
- }
71
-
72
- export async function handleRequest(req: JsonRpcRequest, context: McpRequestContext = {}): Promise<JsonRpcResponse | null> {
73
- return dispatchMcpRequest(req, context, LOCAL_SURFACE)
74
- }
75
-
76
-
77
- async function handleToolCall(id: number | string | null, params: unknown, context: McpRequestContext): Promise<JsonRpcResponse> {
78
- const p = params as { name?: string; arguments?: Record<string, unknown> } | undefined
79
- const name = p?.name
80
- const args = p?.arguments ?? {}
81
- if (name === 'execute') {
82
- const code = (args as { code?: string }).code
83
- const requestedTimeoutMs = (args as { timeoutMs?: number }).timeoutMs
84
- const timeoutMs = typeof requestedTimeoutMs === 'number' && Number.isFinite(requestedTimeoutMs)
85
- ? Math.max(1, Math.min(requestedTimeoutMs, context.maxSandboxTimeoutMs ?? MAX_SANDBOX_TIMEOUT_MS))
86
- : undefined
87
- if (typeof code !== 'string') return error(id, -32602, 'execute requires `code` (string)')
88
- const r = await executeInSandbox(code, { timeoutMs })
89
- return reply(id, { content: [{ type: 'text', text: JSON.stringify(r) }], isError: !r.ok })
90
- }
91
- if (name === 'render_png') return handleRenderPng(id, args, context)
92
- if (name === 'describe') {
93
- const source = (args as { source?: string }).source
94
- if (typeof source !== 'string') return error(id, -32602, 'describe requires `source` (string)')
95
- try {
96
- const payload = describePayload(source, args)
97
- return reply(id, { content: [{ type: 'text', text: JSON.stringify(payload) }], isError: !payload.ok })
98
- } catch (e) {
99
- const msg = e instanceof Error ? e.message : String(e)
100
- const payload = { ok: false as const, error: { code: 'DESCRIBE_FAILED', message: msg } }
101
- return reply(id, { content: [{ type: 'text', text: JSON.stringify(payload) }], isError: true })
102
- }
103
- }
104
- return error(id, -32602, `Unknown tool: ${name ?? '<none>'}`)
105
- }
106
-
107
- function describeFormat(args: Record<string, unknown>): 'text' | 'json' | 'facts' {
108
- const format = args.format ?? 'text'
109
- if (format === 'text' || format === 'json' || format === 'facts') return format
110
- throw new Error('describe format must be one of: text, json, facts')
111
- }
112
-
113
- function describePayload(source: string, args: Record<string, unknown>): { ok: boolean } & Record<string, unknown> {
114
- const format = describeFormat(args)
115
- if (format === 'text') return { ok: true as const, text: describeMermaidSource(source) }
116
- const parsed = parseMermaid(source)
117
- if (!parsed.ok) return { ok: false as const, errors: parsed.error }
118
- if (format === 'facts') return { ok: true as const, facts: describeMermaidFacts(parsed.value) }
119
- return { ok: true as const, tree: JSON.parse(describeMermaid(parsed.value, { format: 'json' })) }
120
- }
121
-
122
- function handleRenderPng(id: number | string | null, args: Record<string, unknown>, context: McpRequestContext): JsonRpcResponse {
123
- const source = (args as { source?: string }).source
124
- const scale = (args as { scale?: number }).scale
125
- const background = (args as { background?: string }).background
126
- const style = (args as { style?: import('../scene/style-registry.ts').StyleInput | import('../scene/style-registry.ts').StyleInput[] }).style
127
- const seed = (args as { seed?: number }).seed
128
- const output = String((args as { output?: string; outputMode?: string }).output ?? (args as { outputMode?: string }).outputMode ?? 'base64')
129
- if (typeof source !== 'string') return error(id, -32602, 'render_png requires `source` (string)')
130
- if (!['base64', 'file', 'url'].includes(output)) return error(id, -32602, 'render_png output must be one of: base64, file, url')
131
- try {
132
- const png = renderMermaidPNG(source, { scale, background, style, seed })
133
- if (output === 'base64') {
134
- const png_base64 = Buffer.from(png).toString('base64')
135
- const payload = { ok: true as const, png_base64 }
136
- return reply(id, { content: [{ type: 'text', text: JSON.stringify(payload) }], isError: false })
137
- }
138
- const store = context.artifactStore ?? getDefaultArtifactStore()
139
- if (output === 'url' && !store.hasBaseUrl()) return error(id, -32602, 'render_png output=url requires an HTTP/SSE artifact base URL')
140
- const artifact = store.write(png, { extension: '.png', mimeType: 'image/png' })
141
- const payload = { ok: true as const, artifact: artifactPayload(artifact, output as 'file' | 'url') }
142
- return reply(id, { content: [{ type: 'text', text: JSON.stringify(payload) }], isError: false })
143
- } catch (e) {
144
- const msg = e instanceof Error ? e.message : String(e)
145
- const payload = { ok: false as const, error: { code: 'PNG_RENDER_FAILED', message: msg } }
146
- return reply(id, { content: [{ type: 'text', text: JSON.stringify(payload) }], isError: true })
147
- }
148
- }
149
-
150
- function artifactPayload(artifact: ArtifactRecord, output: 'file' | 'url'): Record<string, unknown> {
151
- const base = { mimeType: artifact.mimeType, bytes: artifact.bytes, sha256: artifact.sha256 }
152
- if (output === 'url') return { ...base, url: artifact.url }
153
- return { ...base, path: artifact.path }
154
- }
155
-
156
- function getDefaultArtifactStore(): ArtifactStore {
157
- defaultArtifactStore ??= createArtifactStore({ dir: join(tmpdir(), 'agentic-mermaid-mcp-artifacts') })
158
- return defaultArtifactStore
159
- }
160
-
161
- // Force the native resvg (`@resvg/resvg-js`) addon to load NOW, before the
162
- // server starts handling requests. On Bun, the addon's first `dlopen` — which
163
- // is deferred until the first `new Resvg()` — panics the runtime
164
- // (`panic: unreachable`) if it happens *after* a `node:vm` context has run.
165
- // Code Mode `execute` runs agent code in exactly such a sandbox, so a normal
166
- // `execute` then `render_png` session would otherwise crash the whole process.
167
- // Warming here lands the dlopen up front. Guarded so a host without the binding
168
- // still boots (render_png then reports the failure per-call instead of at start).
169
- function warmUpPngRenderer(): void {
170
- try {
171
- renderMermaidPNG('flowchart LR\n A --> B')
172
- } catch {
173
- // Binding unavailable in this environment; render_png will surface the error.
174
- }
175
- }
176
-
177
- export async function runStdio(options: { artifactDir?: string; maxArtifactBytes?: number; artifactTtlMs?: number } = {}): Promise<void> {
178
- warmUpPngRenderer()
179
- const artifactStore = createArtifactStore({ dir: options.artifactDir, maxBytes: options.maxArtifactBytes, ttlMs: options.artifactTtlMs })
180
- process.stdin.setEncoding('utf8')
181
- let buf = ''
182
- process.stdin.on('data', async (chunk: string) => {
183
- buf += chunk
184
- let nl: number
185
- while ((nl = buf.indexOf('\n')) >= 0) {
186
- const line = buf.slice(0, nl).trim()
187
- buf = buf.slice(nl + 1)
188
- if (!line) continue
189
- try {
190
- const res = await handleRequest(JSON.parse(line) as JsonRpcRequest, { artifactStore })
191
- if (res) process.stdout.write(JSON.stringify(res) + '\n')
192
- } catch (e) {
193
- process.stdout.write(JSON.stringify(error(null, -32700, `parse error: ${(e as Error).message}`)) + '\n')
194
- }
195
- }
196
- })
197
- return new Promise<void>(resolve => {
198
- process.stdin.on('end', () => resolve())
199
- process.stdin.on('close', () => resolve())
200
- })
201
- }
202
-
203
- export async function startHttpServer(options: HttpMcpOptions = {}): Promise<HttpMcpServer> {
204
- warmUpPngRenderer()
205
- const host = options.host ?? '127.0.0.1'
206
- const port = options.port ?? 3000
207
- if (!isLoopbackHost(host) && !options.authToken) throw new Error('HTTP MCP remote bind requires --auth-token')
208
- const sessions = new Map<string, ServerResponse>()
209
- let baseUrl = ''
210
- const artifactStore = createArtifactStore({
211
- dir: options.artifactDir,
212
- maxBytes: options.maxArtifactBytes,
213
- ttlMs: options.artifactTtlMs,
214
- })
215
- const maxRpcBodyBytes = options.maxRpcBodyBytes ?? MAX_RPC_BODY_BYTES
216
- const context = { artifactStore, maxSandboxTimeoutMs: options.maxSandboxTimeoutMs }
217
-
218
- const server = createServer(async (req, res) => {
219
- try {
220
- const u = new URL(req.url ?? '/', baseUrl || `http://${host}:${port || 0}`)
221
- if (req.method === 'GET' && u.pathname === '/health') return sendJson(res, 200, { ok: true })
222
- if (req.method === 'GET' && u.pathname === '/sse') return openSse(req, res, sessions, baseUrl)
223
- if (req.method === 'POST' && u.pathname === '/message') {
224
- if (!authorizeHttpRpc(req, res, baseUrl, options.authToken)) return
225
- return await postSseMessage(req, res, sessions, context, maxRpcBodyBytes)
226
- }
227
- if (req.method === 'POST' && u.pathname === '/rpc') {
228
- if (!authorizeHttpRpc(req, res, baseUrl, options.authToken)) return
229
- return await postRpc(req, res, context, maxRpcBodyBytes)
230
- }
231
- if (req.method === 'GET' && u.pathname.startsWith('/artifacts/')) return serveArtifact(res, artifactStore, decodeURIComponent(u.pathname.slice('/artifacts/'.length)))
232
- return sendJson(res, 404, { ok: false, error: 'not found' })
233
- } catch (e) {
234
- if (e instanceof HttpStatusError) return sendJson(res, e.status, { ok: false, error: e.message })
235
- return sendJson(res, 500, { ok: false, error: e instanceof Error ? e.message : String(e) })
236
- }
237
- })
238
-
239
- await new Promise<void>((resolve, reject) => {
240
- server.once('error', reject)
241
- server.listen(port, host, () => {
242
- server.off('error', reject)
243
- resolve()
244
- })
245
- })
246
- const address = server.address()
247
- if (!address || typeof address === 'string') throw new Error('HTTP MCP server did not expose a TCP address')
248
- baseUrl = `http://${host}:${address.port}`
249
- artifactStore.setBaseUrl(options.publicUrl ?? `${baseUrl}/artifacts`)
250
- return {
251
- server,
252
- url: baseUrl,
253
- artifactStore,
254
- close: () => new Promise<void>((resolve, reject) => {
255
- for (const sse of sessions.values()) sse.end()
256
- sessions.clear()
257
- server.close(err => err ? reject(err) : resolve())
258
- }),
259
- }
260
- }
261
-
262
- export async function runHttp(options: HttpMcpOptions = {}): Promise<void> {
263
- const started = await startHttpServer(options)
264
- process.stderr.write(`agentic-mermaid-mcp HTTP/SSE listening at ${started.url} (SSE: ${started.url}/sse, plain JSON-RPC: ${started.url}/rpc)\n`)
265
- return new Promise<void>(resolve => {
266
- const shutdown = () => { started.close().finally(resolve) }
267
- process.once('SIGINT', shutdown)
268
- process.once('SIGTERM', shutdown)
269
- })
270
- }
271
-
272
- function isLoopbackHost(host: string): boolean {
273
- return host === '127.0.0.1' || host === 'localhost' || host === '::1'
274
- }
275
-
276
- function authorizeHttpRpc(req: IncomingMessage, res: ServerResponse, baseUrl: string, authToken?: string): boolean {
277
- const contentType = String(req.headers['content-type'] ?? '').toLowerCase()
278
- if (!contentType.startsWith('application/json')) {
279
- sendJson(res, 415, { ok: false, error: 'HTTP MCP JSON-RPC requires content-type application/json' })
280
- return false
281
- }
282
- const origin = req.headers.origin
283
- if (origin && origin !== baseUrl) {
284
- sendJson(res, 403, { ok: false, error: 'origin not allowed' })
285
- return false
286
- }
287
- if (authToken && req.headers.authorization !== `Bearer ${authToken}`) {
288
- sendJson(res, 401, { ok: false, error: 'missing or invalid bearer token' })
289
- return false
290
- }
291
- return true
292
- }
293
-
294
- function openSse(req: IncomingMessage, res: ServerResponse, sessions: Map<string, ServerResponse>, baseUrl: string): void {
295
- const sessionId = randomUUID()
296
- sessions.set(sessionId, res)
297
- res.writeHead(200, {
298
- 'Content-Type': 'text/event-stream; charset=utf-8',
299
- 'Cache-Control': 'no-cache, no-transform',
300
- Connection: 'keep-alive',
301
- 'X-Accel-Buffering': 'no',
302
- })
303
- res.write(`event: endpoint\ndata: ${baseUrl}/message?sessionId=${encodeURIComponent(sessionId)}\n\n`)
304
- const heartbeat = setInterval(() => res.write(': keepalive\n\n'), 25_000)
305
- req.on('close', () => {
306
- clearInterval(heartbeat)
307
- sessions.delete(sessionId)
308
- })
309
- }
310
-
311
- async function postSseMessage(req: IncomingMessage, res: ServerResponse, sessions: Map<string, ServerResponse>, context: McpRequestContext, maxBytes: number): Promise<void> {
312
- const u = new URL(req.url ?? '/', 'http://localhost')
313
- const sessionId = u.searchParams.get('sessionId') ?? ''
314
- const sse = sessions.get(sessionId)
315
- if (!sse) return sendJson(res, 404, { ok: false, error: 'unknown sessionId' })
316
- const body = await readRequestBody(req, maxBytes)
317
- let parsed: JsonRpcRequest
318
- try { parsed = JSON.parse(body) as JsonRpcRequest } catch { throw new HttpStatusError(400, 'invalid JSON-RPC body') }
319
- const response = await handleRequest(parsed, context)
320
- if (response) sse.write(`event: message\ndata: ${JSON.stringify(response)}\n\n`)
321
- sendJson(res, 202, { ok: true })
322
- }
323
-
324
- async function postRpc(req: IncomingMessage, res: ServerResponse, context: McpRequestContext, maxBytes: number): Promise<void> {
325
- const body = await readRequestBody(req, maxBytes)
326
- let parsed: JsonRpcRequest
327
- try { parsed = JSON.parse(body) as JsonRpcRequest } catch { throw new HttpStatusError(400, 'invalid JSON-RPC body') }
328
- const response = await handleRequest(parsed, context)
329
- if (response === null) return sendJson(res, 202, { ok: true })
330
- sendJson(res, 200, response)
331
- }
332
-
333
- function serveArtifact(res: ServerResponse, store: ArtifactStore, name: string): void {
334
- const artifact = store.read(name)
335
- if (!artifact) return sendJson(res, 404, { ok: false, error: 'artifact not found' })
336
- res.writeHead(200, {
337
- 'Content-Type': artifact.mimeType,
338
- 'Content-Length': artifact.bytes.length,
339
- 'Cache-Control': 'private, max-age=3600',
340
- })
341
- res.end(artifact.bytes)
342
- }
343
-
344
- function sendJson(res: ServerResponse, status: number, payload: unknown): void {
345
- const body = JSON.stringify(payload)
346
- res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': Buffer.byteLength(body) })
347
- res.end(body)
348
- }
349
-
350
- async function readRequestBody(req: IncomingMessage, maxBytes = MAX_RPC_BODY_BYTES): Promise<string> {
351
- const declared = Number(req.headers['content-length'] ?? 0)
352
- if (Number.isFinite(declared) && declared > maxBytes) throw new HttpStatusError(413, `request body exceeds ${maxBytes} bytes`)
353
- let body = ''
354
- let bytes = 0
355
- for await (const chunk of req) {
356
- const s = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk)
357
- bytes += Buffer.byteLength(s)
358
- if (bytes > maxBytes) throw new HttpStatusError(413, `request body exceeds ${maxBytes} bytes`)
359
- body += s
360
- }
361
- return body
362
- }
@@ -1,146 +0,0 @@
1
- import { reply, rpcError, type JsonRpcRequest, type JsonRpcResponse } from './protocol.ts'
2
- import pkg from '../../package.json'
3
-
4
- export interface McpToolDefinition {
5
- name: string
6
- description: string
7
- inputSchema: Record<string, unknown>
8
- annotations?: {
9
- readOnlyHint?: boolean
10
- destructiveHint?: boolean
11
- idempotentHint?: boolean
12
- openWorldHint?: boolean
13
- }
14
- }
15
-
16
- export interface McpServerSurface<Context> {
17
- protocolVersion: string | ((params: unknown) => string)
18
- tools: McpToolDefinition[]
19
- instructions: string
20
- handleToolCall(id: number | string | null, params: unknown, context: Context): JsonRpcResponse | Promise<JsonRpcResponse>
21
- }
22
-
23
- export const MCP_SERVER_NAME = 'agentic-mermaid-mcp'
24
- // Derived from package.json so every MCP handshake reports the same package
25
- // version as the published npm artifact.
26
- export const MCP_SERVER_VERSION = pkg.version
27
- export const PURE_COMPUTE_ANNOTATIONS = {
28
- readOnlyHint: true,
29
- destructiveHint: false,
30
- idempotentHint: true,
31
- openWorldHint: false,
32
- } as const
33
- const SANDBOX_EXECUTE_ANNOTATIONS = {
34
- readOnlyHint: true,
35
- destructiveHint: false,
36
- idempotentHint: false,
37
- openWorldHint: false,
38
- } as const
39
-
40
- export async function dispatchMcpRequest<Context>(req: JsonRpcRequest, context: Context, surface: McpServerSurface<Context>): Promise<JsonRpcResponse | null> {
41
- const id = req.id ?? null
42
- switch (req.method) {
43
- case 'initialize': {
44
- const protocolVersion = typeof surface.protocolVersion === 'function'
45
- ? surface.protocolVersion(req.params)
46
- : surface.protocolVersion
47
- return reply(id, {
48
- protocolVersion,
49
- serverInfo: { name: MCP_SERVER_NAME, version: MCP_SERVER_VERSION },
50
- capabilities: { tools: {} },
51
- instructions: surface.instructions,
52
- })
53
- }
54
- case 'notifications/initialized': return null
55
- case 'ping': return reply(id, {})
56
- case 'tools/list': return reply(id, { tools: surface.tools })
57
- case 'tools/call': return await surface.handleToolCall(id, req.params, context)
58
- case 'prompts/list': return reply(id, { prompts: [] })
59
- case 'resources/list': return reply(id, { resources: [] })
60
- default: return rpcError(id, -32601, `Method not found: ${req.method}`)
61
- }
62
- }
63
-
64
- export function createExecuteTool(options: { sdkDeclaration: string; hosted?: boolean }): McpToolDefinition {
65
- const hostedNote = options.hosted
66
- ? `Hosted note: execute runs in an on-demand isolate and costs more than the direct
67
- render_svg/render_ascii/render_png/verify/describe tools — prefer those for plain
68
- render/verify calls. For straightforward structured edits, prefer the declarative
69
- mutate/build tools; reserve execute for logic the ops don't express.
70
-
71
- `
72
- : ''
73
- const timeoutDescription = options.hosted
74
- ? 'Optional CPU-time budget (default 5000ms, max 30000ms).'
75
- : 'Optional hard timeout (default 5000ms).'
76
- const runtime = options.hosted ? 'an isolated sandbox' : 'a sandboxed node:vm context'
77
- return {
78
- name: 'execute',
79
- description: `Run synchronous JavaScript against the mermaid SDK in ${runtime}.
80
- Code runs as an expression or statement body — return the final value. Promise jobs,
81
- async/await, and dynamic import are not supported.
82
- Multi-step diagram edits should be one execute() call. The SDK declaration is
83
- TypeScript-shaped for guidance; the sandbox does not transpile type annotations.
84
- ${hostedNote}SDK declaration:
85
- ${options.sdkDeclaration}`,
86
- inputSchema: {
87
- type: 'object',
88
- properties: {
89
- code: { type: 'string', description: 'JavaScript to execute; mermaid.* SDK is global.' },
90
- timeoutMs: { type: 'number', description: timeoutDescription },
91
- },
92
- required: ['code'],
93
- },
94
- annotations: SANDBOX_EXECUTE_ANNOTATIONS,
95
- }
96
- }
97
-
98
- export function createRenderPngTool(mode: 'local' | 'hosted'): McpToolDefinition {
99
- const hosted = mode === 'hosted'
100
- return {
101
- name: 'render_png',
102
- description: hosted
103
- ? `Rasterize a Mermaid source string to PNG. Returns { ok, png_base64 }.
104
- Hosted rendering uses resvg-wasm with bundled fonts; bytes may differ from the
105
- local napi renderer, so hosted PNG is a convenience surface, not part of the
106
- byte-determinism contract. For file/URL artifacts use the local stdio server.`
107
- : `Rasterize a Mermaid source string to PNG. By default returns base64-encoded PNG bytes.
108
- Set output to "file" or "url" to write a managed artifact instead; artifact responses include
109
- {path?, url?, mimeType, bytes, sha256}. File/URL artifacts are generated under the MCP server's
110
- artifact directory with safe names, size limits, and TTL cleanup.
111
- Uses bundled resvg + DejaVu Sans for same-machine cross-runtime determinism where verified.
112
- Agentic Mermaid outputs SVG, PNG, ASCII, Unicode, and JSON layout. For non-PNG output, use execute() with mermaid.renderMermaidSVG, mermaid.renderMermaidASCII (useAscii true for ASCII, false for Unicode), or verifyMermaid(...).layout — those are streaming text/data and don't need a dedicated tool.`,
113
- inputSchema: {
114
- type: 'object',
115
- properties: {
116
- source: { type: 'string', description: 'Mermaid source.' },
117
- scale: { type: 'number', description: hosted ? 'Output scale multiplier (default 2 — retina; clamped to 0.1–8).' : 'Output scale multiplier (default 2 — retina).' },
118
- background: { type: 'string', description: "CSS color string (default 'white')." },
119
- style: { description: hosted ? 'Style name | record | stack (same as render_svg). Hosted rasterization bundles the built-in style faces; custom unbundled fonts fall back to DejaVu.' : 'Style: a name (hand-drawn, watercolor, …, or any theme name), an inline style record, or an array stack merged left → right.' },
120
- seed: { type: 'number', description: hosted ? 'Ink seed for styled looks.' : 'Re-rolls ink wobble of styled looks; never moves layout.' },
121
- ...(hosted ? {} : { output: { type: 'string', enum: ['base64', 'file', 'url'], description: 'PNG return mode (default base64).' } }),
122
- },
123
- required: ['source'],
124
- },
125
- annotations: PURE_COMPUTE_ANNOTATIONS,
126
- }
127
- }
128
-
129
- export function createDescribeTool(): McpToolDefinition {
130
- return {
131
- name: 'describe',
132
- description: `Describe a Mermaid diagram. format=text returns { ok, text } with
133
- one or two summary sentences; format=json returns { ok, tree } with the AX tree;
134
- format=facts returns { ok, facts } with deterministic semantic fact lines for
135
- machine checking (for example edge A -> B : label, member Duck +quack()).`,
136
- inputSchema: {
137
- type: 'object',
138
- properties: {
139
- source: { type: 'string', description: 'Mermaid source.' },
140
- format: { type: 'string', enum: ['text', 'json', 'facts'], description: 'text (default), json AX tree, or facts semantic read-back.' },
141
- },
142
- required: ['source'],
143
- },
144
- annotations: PURE_COMPUTE_ANNOTATIONS,
145
- }
146
- }