agentic-mermaid 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +149 -4
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +25 -32
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/assets/fonts/FONT-LICENSES.md +9 -5
- package/assets/fonts/Inter-Bold.ttf +0 -0
- package/assets/fonts/Inter-Medium.ttf +0 -0
- package/assets/fonts/Inter-Regular.ttf +0 -0
- package/assets/fonts/Inter-SemiBold.ttf +0 -0
- package/dist/agent-core.d.ts +463 -0
- package/dist/agent-core.js +397 -0
- package/dist/agent.d.ts +81 -1822
- package/dist/agent.js +336 -36839
- package/dist/agentic-mermaid-mcp.js +7 -37908
- package/dist/am.js +33638 -36261
- package/dist/chunk-4QECACIA.js +4702 -0
- package/dist/chunk-CDPKJ2BQ.js +37 -0
- package/dist/chunk-QKDLMOJJ.js +3487 -0
- package/dist/chunk-WI35L7IR.js +615 -0
- package/dist/chunk-WN3YDOBG.js +149 -0
- package/dist/chunk-ZYMMKQ3I.js +62637 -0
- package/dist/index-D54dqSdC.d.ts +6777 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +310 -32836
- package/dist/mcp-cli-SBQSTJFC.js +17 -0
- package/docs/README.md +14 -6
- package/docs/schemas/style-spec.schema.json +1221 -30
- package/examples/styles/catalog.json +54 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
- package/examples/styles/cupertino-prototype.mmd +10 -0
- package/examples/styles/cupertino-prototype.style.json +63 -0
- package/examples/styles/mid-century-report.style.json +2 -2
- package/examples/styles/star-chart-atlas.style.json +2 -1
- package/examples/styles/transit-route-map.style.json +2 -2
- package/examples/styles/vercel-inspired-prototype.mmd +6 -0
- package/examples/styles/vercel-inspired-prototype.style.json +87 -0
- package/package.json +70 -43
- package/server.json +41 -0
- package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
- package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
- package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
- package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
- package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
- package/AGENT_NATIVE.md +0 -602
- package/Instructions_for_agents.md +0 -83
- package/TODO.md +0 -662
- package/assets/hero.png +0 -0
- package/dist/index-x2Cj1dVv.d.ts +0 -1089
- package/docs/agent-api-cookbook.md +0 -308
- package/docs/agent-mutation-policy.md +0 -104
- package/docs/agent-workflow-examples.md +0 -46
- package/docs/api.md +0 -203
- package/docs/ascii.md +0 -103
- package/docs/assets/improvements/box-start-connector.png +0 -0
- package/docs/assets/improvements/comment-dropped-lint.png +0 -0
- package/docs/assets/improvements/er-content-loss.png +0 -0
- package/docs/assets/improvements/fan-in-trunks.png +0 -0
- package/docs/assets/improvements/gantt-family.png +0 -0
- package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
- package/docs/assets/improvements/gantt-verify-seam.png +0 -0
- package/docs/assets/improvements/subgraph-container-edges.png +0 -0
- package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
- package/docs/assets/improvements/wrapper-init-directive.png +0 -0
- package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
- package/docs/assets/style-cookbook/mid-century-report.png +0 -0
- package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
- package/docs/assets/style-cookbook/transit-route-map.png +0 -0
- package/docs/comparison.md +0 -94
- package/docs/config.md +0 -105
- package/docs/contributing/adding-diagram-types.md +0 -144
- package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
- package/docs/contributing/diagram-family-citizenship.md +0 -82
- package/docs/contributing/harvesting-upstream-tests.md +0 -126
- package/docs/contributing/lessons-learned.md +0 -276
- package/docs/contributing/releasing.md +0 -71
- package/docs/contributing/visual-review-evidence.md +0 -53
- package/docs/custom-style-cookbook.md +0 -154
- package/docs/design/families/README.md +0 -26
- package/docs/design/families/architecture-beta.md +0 -48
- package/docs/design/families/flowchart-parser-conformance.md +0 -22
- package/docs/design/families/gantt-research.md +0 -137
- package/docs/design/families/gantt.md +0 -329
- package/docs/design/families/journey-agentic-cards-before.png +0 -0
- package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
- package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
- package/docs/design/families/journey-migration-parity.md +0 -433
- package/docs/design/families/journey-usage-research.md +0 -423
- package/docs/design/families/journey.md +0 -125
- package/docs/design/families/xychart.md +0 -85
- package/docs/design/system/README.md +0 -45
- package/docs/design/system/abstraction-audit.md +0 -224
- package/docs/design/system/abstraction-recommendations.md +0 -655
- package/docs/design/system/architecture.mmd +0 -29
- package/docs/design/system/architecture.svg +0 -137
- package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
- package/docs/design/system/layout-rubric.md +0 -138
- package/docs/design/system/route-contracts.md +0 -848
- package/docs/design/system/source-preservation-ladder.md +0 -52
- package/docs/design/system/styles-rollout.md +0 -216
- package/docs/design/system/ugly-layouts.md +0 -119
- package/docs/diagram-families.md +0 -193
- package/docs/features.md +0 -147
- package/docs/fork-differences.md +0 -130
- package/docs/getting-started.md +0 -175
- package/docs/issue-derived-test-cases.md +0 -156
- package/docs/layout-characterization/README.md +0 -245
- package/docs/layout-characterization/contact-sheet-families.md +0 -496
- package/docs/layout-characterization/contact-sheet.md +0 -638
- package/docs/layout-characterization/properties.md +0 -281
- package/docs/layout-characterization/visual-quality.md +0 -160
- package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
- package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
- package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
- package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
- package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/journey.svg +0 -118
- package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
- package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
- package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
- package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
- package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
- package/docs/mcp-code-mode-rationale.md +0 -101
- package/docs/mcp-http-transport.md +0 -223
- package/docs/mermaid-layout-complaints.md +0 -837
- package/docs/mutation-testing.md +0 -206
- package/docs/project/divergences.md +0 -98
- package/docs/project/dogfooding-docs-strategy.md +0 -66
- package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
- package/docs/project/lessons-learned.md +0 -1019
- package/docs/project/remove-role-styling-plan.md +0 -176
- package/docs/quality.md +0 -212
- package/docs/react.md +0 -97
- package/docs/style-authoring.md +0 -133
- package/docs/testing-strategy.md +0 -335
- package/docs/theming.md +0 -162
- package/examples/agent-improve-auth-flow.ts +0 -206
- package/examples/agent-loop.ts +0 -53
- package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
- package/llms.txt +0 -104
- package/skill-evals/README.md +0 -77
- package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
- package/skill-evals/fixtures/class-payment/input.mmd +0 -4
- package/skill-evals/fixtures/er-orders/input.mmd +0 -10
- package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
- package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
- package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
- package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
- package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
- package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
- package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
- package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
- package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
- package/skill-evals/shared-benchmark.json +0 -510
- package/src/agent/analyze.ts +0 -157
- package/src/agent/apply.ts +0 -146
- package/src/agent/architecture-body.ts +0 -458
- package/src/agent/certificates.ts +0 -110
- package/src/agent/class-body.ts +0 -319
- package/src/agent/core.ts +0 -207
- package/src/agent/create.ts +0 -106
- package/src/agent/describe.ts +0 -364
- package/src/agent/er-body.ts +0 -224
- package/src/agent/facts.ts +0 -337
- package/src/agent/families-builtin.ts +0 -934
- package/src/agent/families.ts +0 -232
- package/src/agent/family-layouts.ts +0 -826
- package/src/agent/flowchart-body.ts +0 -372
- package/src/agent/flowchart-unsupported.ts +0 -243
- package/src/agent/gantt-body.ts +0 -495
- package/src/agent/index.ts +0 -10
- package/src/agent/journey-body.ts +0 -414
- package/src/agent/label-metrics.ts +0 -28
- package/src/agent/layout-to-rendered.ts +0 -63
- package/src/agent/mutate.ts +0 -97
- package/src/agent/mutation-ops.ts +0 -51
- package/src/agent/op-schema.ts +0 -439
- package/src/agent/parse.ts +0 -223
- package/src/agent/pie-body.ts +0 -270
- package/src/agent/png.ts +0 -117
- package/src/agent/quadrant-body.ts +0 -336
- package/src/agent/quality.ts +0 -363
- package/src/agent/readability-audit.ts +0 -92
- package/src/agent/rendered-route-audit.ts +0 -61
- package/src/agent/sequence-body.ts +0 -345
- package/src/agent/serialize.ts +0 -214
- package/src/agent/state-body.ts +0 -589
- package/src/agent/structural-count.ts +0 -150
- package/src/agent/timeline-body.ts +0 -280
- package/src/agent/trace-log.ts +0 -43
- package/src/agent/types.ts +0 -979
- package/src/agent/verify.ts +0 -571
- package/src/agent/xychart-body.ts +0 -472
- package/src/architecture/config.ts +0 -282
- package/src/architecture/layout.ts +0 -496
- package/src/architecture/parser.ts +0 -375
- package/src/architecture/renderer.ts +0 -611
- package/src/architecture/types.ts +0 -113
- package/src/ascii/ansi.ts +0 -423
- package/src/ascii/architecture.ts +0 -210
- package/src/ascii/canvas.ts +0 -443
- package/src/ascii/class-diagram.ts +0 -697
- package/src/ascii/converter.ts +0 -475
- package/src/ascii/draw.ts +0 -1693
- package/src/ascii/edge-bundling.ts +0 -328
- package/src/ascii/edge-routing.ts +0 -330
- package/src/ascii/er-diagram.ts +0 -439
- package/src/ascii/gantt.ts +0 -276
- package/src/ascii/grid.ts +0 -747
- package/src/ascii/index.ts +0 -196
- package/src/ascii/journey.ts +0 -189
- package/src/ascii/meta.ts +0 -244
- package/src/ascii/multiline-utils.ts +0 -80
- package/src/ascii/pathfinder.ts +0 -301
- package/src/ascii/pie.ts +0 -67
- package/src/ascii/quadrant.ts +0 -158
- package/src/ascii/reverse.ts +0 -216
- package/src/ascii/sequence.ts +0 -499
- package/src/ascii/shapes/circle.ts +0 -27
- package/src/ascii/shapes/corners.ts +0 -143
- package/src/ascii/shapes/diamond.ts +0 -27
- package/src/ascii/shapes/hexagon.ts +0 -27
- package/src/ascii/shapes/index.ts +0 -106
- package/src/ascii/shapes/rectangle.ts +0 -168
- package/src/ascii/shapes/rounded.ts +0 -27
- package/src/ascii/shapes/special.ts +0 -332
- package/src/ascii/shapes/stadium.ts +0 -110
- package/src/ascii/shapes/state.ts +0 -192
- package/src/ascii/shapes/types.ts +0 -73
- package/src/ascii/timeline.ts +0 -128
- package/src/ascii/types.ts +0 -312
- package/src/ascii/validate.ts +0 -120
- package/src/ascii/width.ts +0 -81
- package/src/ascii/xychart.ts +0 -919
- package/src/browser.ts +0 -29
- package/src/class/layout.ts +0 -240
- package/src/class/parser.ts +0 -332
- package/src/class/renderer.ts +0 -638
- package/src/class/types.ts +0 -129
- package/src/cli/agent-instructions.ts +0 -1
- package/src/cli/am-bin.ts +0 -6
- package/src/cli/exit-codes.ts +0 -18
- package/src/cli/index.ts +0 -1117
- package/src/cli/init-agent.ts +0 -147
- package/src/color-resolver.ts +0 -152
- package/src/elk-instance.ts +0 -114
- package/src/er/layout.ts +0 -187
- package/src/er/parser.ts +0 -226
- package/src/er/renderer.ts +0 -792
- package/src/er/types.ts +0 -99
- package/src/font-manifest.ts +0 -27
- package/src/gantt/layout.ts +0 -365
- package/src/gantt/parser.ts +0 -282
- package/src/gantt/pipeline.ts +0 -27
- package/src/gantt/renderer.ts +0 -414
- package/src/gantt/schedule.ts +0 -513
- package/src/gantt/types.ts +0 -255
- package/src/index.ts +0 -368
- package/src/journey/layout.ts +0 -520
- package/src/journey/parser.ts +0 -218
- package/src/journey/renderer.ts +0 -811
- package/src/journey/types.ts +0 -144
- package/src/layout/geometry.ts +0 -182
- package/src/layout/pass.ts +0 -142
- package/src/layout/passes/index.ts +0 -3916
- package/src/layout-engine.ts +0 -1565
- package/src/layout-rubric.ts +0 -659
- package/src/mcp/artifacts.ts +0 -137
- package/src/mcp/dynamic-harness.ts +0 -31
- package/src/mcp/dynamic-modules.d.ts +0 -7
- package/src/mcp/facade.ts +0 -608
- package/src/mcp/harness-runtime.ts +0 -203
- package/src/mcp/hosted-server.ts +0 -468
- package/src/mcp/mcp-bin.ts +0 -3
- package/src/mcp/mcp-cli.ts +0 -82
- package/src/mcp/protocol.ts +0 -13
- package/src/mcp/sandbox.ts +0 -195
- package/src/mcp/sdk-decl.ts +0 -387
- package/src/mcp/server.ts +0 -362
- package/src/mcp/tool-surface.ts +0 -146
- package/src/mermaid-source.ts +0 -655
- package/src/multiline-utils.ts +0 -228
- package/src/parser.ts +0 -1031
- package/src/pie/layout.ts +0 -168
- package/src/pie/parser.ts +0 -136
- package/src/pie/renderer.ts +0 -234
- package/src/pie/types.ts +0 -82
- package/src/quadrant/layout.ts +0 -191
- package/src/quadrant/parser.ts +0 -183
- package/src/quadrant/renderer.ts +0 -322
- package/src/quadrant/types.ts +0 -113
- package/src/render-family-hooks.ts +0 -244
- package/src/renderer.ts +0 -1099
- package/src/route-contracts.ts +0 -2723
- package/src/scene/backend.ts +0 -76
- package/src/scene/fidelity.ts +0 -183
- package/src/scene/hybrid-backend.ts +0 -174
- package/src/scene/ir.ts +0 -181
- package/src/scene/marks.ts +0 -127
- package/src/scene/rough-backend.ts +0 -378
- package/src/scene/seed.ts +0 -46
- package/src/scene/style-registry.ts +0 -556
- package/src/sequence/layout.ts +0 -456
- package/src/sequence/parser.ts +0 -255
- package/src/sequence/renderer.ts +0 -604
- package/src/sequence/types.ts +0 -154
- package/src/shape-clipping.ts +0 -351
- package/src/shared/accessibility-directives.ts +0 -22
- package/src/shared/batched.ts +0 -61
- package/src/shared/color-math.ts +0 -72
- package/src/shared/svg-a11y.ts +0 -29
- package/src/shared/syntax-error.ts +0 -17
- package/src/shared/unicode-ranges.ts +0 -135
- package/src/styles.ts +0 -318
- package/src/svg-paths.ts +0 -28
- package/src/text-metrics.ts +0 -248
- package/src/theme.ts +0 -639
- package/src/timeline/layout.ts +0 -381
- package/src/timeline/parser.ts +0 -215
- package/src/timeline/renderer.ts +0 -538
- package/src/timeline/types.ts +0 -105
- package/src/types.ts +0 -454
- package/src/website-worker-modules.d.ts +0 -18
- package/src/xychart/axis-utils.ts +0 -109
- package/src/xychart/colors.ts +0 -124
- package/src/xychart/config.ts +0 -88
- package/src/xychart/layout.ts +0 -656
- package/src/xychart/parser.ts +0 -504
- package/src/xychart/renderer.ts +0 -640
- package/src/xychart/types.ts +0 -282
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
|
-
}
|
package/src/mcp/tool-surface.ts
DELETED
|
@@ -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
|
-
}
|