@archform/mcp 0.2.0 → 0.2.1
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/README.md +4 -2
- package/dist/index.js +49 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ npx -y @archform/mcp --http --port 3000
|
|
|
86
86
|
By default the server binds to `127.0.0.1` and rejects requests whose `Host` or `Origin` is not localhost (DNS rebinding protection). To expose it publicly, for example for ChatGPT developer mode, bind to all interfaces and name your public hostname:
|
|
87
87
|
|
|
88
88
|
```sh
|
|
89
|
-
HOST=0.0.0.0 ALLOWED_HOSTS=mcp.example.com TRUST_PROXY=1 PORT=8080 npx -y @archform/mcp --http
|
|
89
|
+
HOST=0.0.0.0 ALLOWED_HOSTS=mcp.example.com TRUST_PROXY=1 RATE_LIMIT_PER_CLIENT=0 PORT=8080 npx -y @archform/mcp --http
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
Then add `https://mcp.example.com/mcp` as a connector in ChatGPT (Settings → Connectors, developer mode) with no authentication. Serve it behind HTTPS.
|
|
@@ -96,9 +96,11 @@ Then add `https://mcp.example.com/mcp` as a connector in ChatGPT (Settings → C
|
|
|
96
96
|
The HTTP endpoint limits request bodies to 512 KB and counts **tool calls**, not HTTP requests. A session's `initialize` and `tools/list` requests are not counted.
|
|
97
97
|
|
|
98
98
|
- **Overall:** `RATE_LIMIT` (default 120 per minute) caps tool calls from all clients combined. Size it to what your Kroki backend can handle. The default is polite to the public kroki.io; raise it for a self-hosted Kroki.
|
|
99
|
-
- **Per client:** `RATE_LIMIT_PER_CLIENT` (default 60 per minute) stops one client from using the whole budget. ChatGPT calls your server from OpenAI's servers, so **all ChatGPT users share a handful of IPs**, and this limit applies to them together.
|
|
99
|
+
- **Per client:** `RATE_LIMIT_PER_CLIENT` (default 60 per minute) stops one client from using the whole budget. ChatGPT calls your server from OpenAI's servers, so **all ChatGPT users share a handful of IPs**, and this limit applies to them together. On a deployment that mainly serves ChatGPT, set `RATE_LIMIT_PER_CLIENT=0` (as in the example above) or make it equal to `RATE_LIMIT`, so ChatGPT users can use the whole overall budget.
|
|
100
100
|
- **Behind a proxy:** most hosts (Render, Fly.io, Railway, Cloud Run) put a proxy in front of your app, so every request seems to come from the proxy's address. Set `TRUST_PROXY=1` (the number of proxies in front of the server) so the limiter uses the address your proxy added to `X-Forwarded-For`. Leave it unset when clients connect directly. Clients can write anything into that header, so it is ignored unless `TRUST_PROXY` is set.
|
|
101
101
|
|
|
102
|
+
When a single tool call is over a limit, the model gets a normal tool result with `isError: true` and a message like "Rate limit reached: … Retry in 42s.", so it can tell the user to wait. An HTTP 429 would show up only as a generic connector failure. Batched or malformed requests over the limit get `429` with `Retry-After`.
|
|
103
|
+
|
|
102
104
|
The counters are in memory, so each instance enforces its own limits.
|
|
103
105
|
|
|
104
106
|
## Configuration
|
package/dist/index.js
CHANGED
|
@@ -42,10 +42,10 @@ function createToolCallLimiter(options) {
|
|
|
42
42
|
}
|
|
43
43
|
const client = options.perClient > 0 ? clientWindow(clientIp(c, trustProxy), now) : void 0;
|
|
44
44
|
if (client && client.count + calls > options.perClient) {
|
|
45
|
-
return tooManyRequests(c, client.resetAt - now, "Too many tool calls from this client.");
|
|
45
|
+
return tooManyRequests(c, next, client.resetAt - now, "Too many tool calls from this client.");
|
|
46
46
|
}
|
|
47
47
|
if (options.global > 0 && global.count + calls > options.global) {
|
|
48
|
-
return tooManyRequests(c, global.resetAt - now, "The server is at capacity.");
|
|
48
|
+
return tooManyRequests(c, next, global.resetAt - now, "The server is at capacity.");
|
|
49
49
|
}
|
|
50
50
|
if (client) client.count += calls;
|
|
51
51
|
global.count += calls;
|
|
@@ -76,10 +76,22 @@ function clientIp(c, trustProxy) {
|
|
|
76
76
|
const incoming = c.env?.incoming;
|
|
77
77
|
return incoming?.socket?.remoteAddress ?? "unknown";
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
var RATE_LIMITED = "archformRateLimited";
|
|
80
|
+
async function tooManyRequests(c, next, retryMs, message) {
|
|
80
81
|
const seconds = Math.max(1, Math.ceil(retryMs / 1e3));
|
|
82
|
+
const text = `Rate limit reached: ${message} Retry in ${seconds}s.`;
|
|
83
|
+
if (isSingleToolCall(c.get("parsedBody"))) {
|
|
84
|
+
c.set(RATE_LIMITED, text);
|
|
85
|
+
await next();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
81
88
|
c.header("Retry-After", String(seconds));
|
|
82
|
-
return c.json({ error:
|
|
89
|
+
return c.json({ error: text }, 429);
|
|
90
|
+
}
|
|
91
|
+
function isSingleToolCall(body) {
|
|
92
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) return false;
|
|
93
|
+
const { method, id } = body;
|
|
94
|
+
return method === "tools/call" && (typeof id === "string" || typeof id === "number");
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
// src/server.ts
|
|
@@ -273,7 +285,7 @@ var formatEntry = z2.object({
|
|
|
273
285
|
docsUrl: z2.string(),
|
|
274
286
|
example: z2.string()
|
|
275
287
|
});
|
|
276
|
-
function registerListFormats(server) {
|
|
288
|
+
function registerListFormats(server, unavailable) {
|
|
277
289
|
server.registerTool(
|
|
278
290
|
"list_formats",
|
|
279
291
|
{
|
|
@@ -283,6 +295,7 @@ function registerListFormats(server) {
|
|
|
283
295
|
annotations: READ_ONLY_ANNOTATIONS
|
|
284
296
|
},
|
|
285
297
|
async () => {
|
|
298
|
+
if (unavailable) return toolError(unavailable);
|
|
286
299
|
const formats = FORMATS.map((f) => ({ ...f, aliases: [...f.aliases], outputs: [...f.outputs] }));
|
|
287
300
|
return {
|
|
288
301
|
content: [{ type: "text", text: JSON.stringify({ formats }, null, 2) }],
|
|
@@ -295,7 +308,7 @@ function registerListFormats(server) {
|
|
|
295
308
|
// src/tools/render-diagram.ts
|
|
296
309
|
import { createHash } from "crypto";
|
|
297
310
|
import * as z3 from "zod/v4";
|
|
298
|
-
function registerRenderDiagram(server, renderer) {
|
|
311
|
+
function registerRenderDiagram(server, renderer, unavailable) {
|
|
299
312
|
server.registerTool(
|
|
300
313
|
"render_diagram",
|
|
301
314
|
{
|
|
@@ -309,6 +322,7 @@ function registerRenderDiagram(server, renderer) {
|
|
|
309
322
|
annotations: READ_ONLY_ANNOTATIONS
|
|
310
323
|
},
|
|
311
324
|
async ({ format: name, source, output }) => {
|
|
325
|
+
if (unavailable) return toolError(unavailable);
|
|
312
326
|
const format = resolveFormat(name);
|
|
313
327
|
if (!format) return toolError(`Unknown format "${name}". Call list_formats to see the supported formats.`);
|
|
314
328
|
if (!format.outputs.includes(output)) {
|
|
@@ -361,7 +375,7 @@ var outputSchema = z4.object({
|
|
|
361
375
|
valid: z4.boolean(),
|
|
362
376
|
errors: z4.array(z4.object({ line: z4.number().int().optional(), message: z4.string() }))
|
|
363
377
|
});
|
|
364
|
-
function registerValidateDiagram(server, renderer) {
|
|
378
|
+
function registerValidateDiagram(server, renderer, unavailable) {
|
|
365
379
|
server.registerTool(
|
|
366
380
|
"validate_diagram",
|
|
367
381
|
{
|
|
@@ -372,6 +386,7 @@ function registerValidateDiagram(server, renderer) {
|
|
|
372
386
|
annotations: READ_ONLY_ANNOTATIONS
|
|
373
387
|
},
|
|
374
388
|
async ({ format: name, source }) => {
|
|
389
|
+
if (unavailable) return toolError(unavailable);
|
|
375
390
|
const format = resolveFormat(name);
|
|
376
391
|
if (!format) return toolError(`Unknown format "${name}". Call list_formats to see the supported formats.`);
|
|
377
392
|
let errors = [];
|
|
@@ -395,7 +410,7 @@ function registerValidateDiagram(server, renderer) {
|
|
|
395
410
|
// package.json
|
|
396
411
|
var package_default = {
|
|
397
412
|
name: "@archform/mcp",
|
|
398
|
-
version: "0.2.
|
|
413
|
+
version: "0.2.1",
|
|
399
414
|
description: "MCP server that renders and validates diagrams as code: Mermaid, PlantUML, C4, Graphviz, D2, Structurizr, BPMN and Excalidraw.",
|
|
400
415
|
mcpName: "io.github.himanshu-mamgain/archform",
|
|
401
416
|
license: "MIT",
|
|
@@ -468,17 +483,24 @@ function createServer(options = {}) {
|
|
|
468
483
|
instructions: "Archform renders and validates diagrams as code. Call list_formats to see supported languages and examples, validate_diagram to check syntax, and render_diagram to produce an image and a shareable link."
|
|
469
484
|
}
|
|
470
485
|
);
|
|
471
|
-
registerListFormats(server);
|
|
472
|
-
registerRenderDiagram(server, renderer);
|
|
473
|
-
registerValidateDiagram(server, renderer);
|
|
486
|
+
registerListFormats(server, options.unavailable);
|
|
487
|
+
registerRenderDiagram(server, renderer, options.unavailable);
|
|
488
|
+
registerValidateDiagram(server, renderer, options.unavailable);
|
|
474
489
|
return server;
|
|
475
490
|
}
|
|
476
491
|
|
|
477
492
|
// src/http.ts
|
|
478
493
|
var MAX_REQUEST_BODY_BYTES = 512 * 1024;
|
|
494
|
+
var RATE_LIMIT_HEADER = "x-archform-rate-limited";
|
|
479
495
|
var DEFAULT_RATE_LIMIT = { global: 120, perClient: 60 };
|
|
480
496
|
function createHttpApp(options = {}) {
|
|
481
|
-
const handler = createMcpHandler(
|
|
497
|
+
const handler = createMcpHandler(
|
|
498
|
+
({ requestInfo }) => {
|
|
499
|
+
const limited = requestInfo?.headers.get(RATE_LIMIT_HEADER);
|
|
500
|
+
return createServer(limited ? { ...options, unavailable: decodeURIComponent(limited) } : options);
|
|
501
|
+
},
|
|
502
|
+
{ maxRequestBodySize: MAX_REQUEST_BODY_BYTES }
|
|
503
|
+
);
|
|
482
504
|
const app = createMcpHonoApp({
|
|
483
505
|
host: options.host ?? "127.0.0.1",
|
|
484
506
|
...options.allowedHosts?.length ? { allowedHosts: options.allowedHosts } : {},
|
|
@@ -487,7 +509,21 @@ function createHttpApp(options = {}) {
|
|
|
487
509
|
app.get("/health", (c) => c.json({ status: "ok", name: "archform", version: VERSION }));
|
|
488
510
|
const limiter = createToolCallLimiter({ ...DEFAULT_RATE_LIMIT, ...options.rateLimit });
|
|
489
511
|
app.use("/mcp", limiter.middleware);
|
|
490
|
-
app.post("/mcp", (c) =>
|
|
512
|
+
app.post("/mcp", (c) => {
|
|
513
|
+
const parsedBody = c.get("parsedBody");
|
|
514
|
+
const limited = c.get(RATE_LIMITED);
|
|
515
|
+
if (!limited && !c.req.raw.headers.has(RATE_LIMIT_HEADER)) return handler.fetch(c.req.raw, { parsedBody });
|
|
516
|
+
const headers = new Headers(c.req.raw.headers);
|
|
517
|
+
headers.delete(RATE_LIMIT_HEADER);
|
|
518
|
+
if (limited) headers.set(RATE_LIMIT_HEADER, encodeURIComponent(limited));
|
|
519
|
+
const request = new Request(c.req.url, {
|
|
520
|
+
method: "POST",
|
|
521
|
+
headers,
|
|
522
|
+
body: parsedBody === void 0 ? c.req.raw.body : JSON.stringify(parsedBody),
|
|
523
|
+
...parsedBody === void 0 ? { duplex: "half" } : {}
|
|
524
|
+
});
|
|
525
|
+
return handler.fetch(request, { parsedBody });
|
|
526
|
+
});
|
|
491
527
|
app.on(["GET", "DELETE"], "/mcp", (c) => c.json({ error: "Method not allowed. Use POST /mcp." }, 405));
|
|
492
528
|
return { app, handler, limiter };
|
|
493
529
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/http.ts","../src/rate-limit.ts","../src/server.ts","../src/renderers/kroki.ts","../src/renderers/kroki-errors.ts","../src/renderers/renderer.ts","../src/tools/list-formats.ts","../src/formats.ts","../src/tools/shared.ts","../src/tools/render-diagram.ts","../src/tools/validate-diagram.ts","../package.json","../src/version.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from 'node:util';\nimport { serve } from '@hono/node-server';\nimport { serveStdio } from '@modelcontextprotocol/server/stdio';\nimport { createHttpApp, DEFAULT_RATE_LIMIT } from './http.js';\nimport { createServer } from './server.js';\nimport { VERSION } from './version.js';\n\n// stdout carries the MCP protocol in stdio mode: every log goes to stderr.\nconst log = (message: string) => console.error(`[archform] ${message}`);\n\nconst HELP = `archform-mcp ${VERSION}\n\nUsage:\n archform-mcp Serve MCP over stdio (default)\n archform-mcp --http Serve MCP over Streamable HTTP (POST /mcp, GET /health)\n\nOptions:\n --http Use Streamable HTTP instead of stdio\n --port <n> HTTP port (default: $PORT or 3000)\n --host <addr> HTTP bind address (default: $HOST or 127.0.0.1)\n -v, --version Print the version\n -h, --help Print this help\n\nEnvironment:\n KROKI_URL Kroki server (default: https://kroki.io)\n ALLOWED_HOSTS Comma-separated Host header values accepted when binding publicly\n RATE_LIMIT Tool calls per minute across all clients (default: 120, 0 disables)\n RATE_LIMIT_PER_CLIENT Tool calls per minute per client IP (default: 60, 0 disables)\n TRUST_PROXY Reverse proxies in front of the server whose X-Forwarded-For is trusted\n (default: 0 = ignore the header; \"true\" = 1)\n`;\n\nfunction main(): void {\n let values: ReturnType<typeof parse>['values'];\n try {\n values = parse().values;\n } catch (error) {\n log(error instanceof Error ? error.message : String(error));\n console.error(HELP);\n process.exit(2);\n }\n\n if (values.help) {\n console.error(HELP);\n return;\n }\n if (values.version) {\n console.error(VERSION);\n return;\n }\n if (values.http) {\n startHttp(values.port, values.host);\n return;\n }\n\n const handle = serveStdio(() => createServer());\n const shutdown = () => void handle.close().finally(() => process.exit(0));\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n log(`v${VERSION} running on stdio (Kroki: ${process.env.KROKI_URL ?? 'https://kroki.io'})`);\n}\n\nfunction parse() {\n return parseArgs({\n options: {\n http: { type: 'boolean', default: false },\n port: { type: 'string' },\n host: { type: 'string' },\n version: { type: 'boolean', short: 'v' },\n help: { type: 'boolean', short: 'h' },\n },\n strict: true,\n });\n}\n\nfunction startHttp(portArg: string | undefined, hostArg: string | undefined): void {\n const port = Number(portArg ?? process.env.PORT ?? 3000);\n if (!Number.isInteger(port) || port < 1 || port > 65_535) {\n log(`Invalid port \"${portArg ?? process.env.PORT}\".`);\n process.exit(2);\n }\n const host = hostArg ?? process.env.HOST ?? '127.0.0.1';\n const allowedHosts = (process.env.ALLOWED_HOSTS ?? '')\n .split(',')\n .map((h) => h.trim())\n .filter(Boolean);\n const rateLimit = {\n global: envCount('RATE_LIMIT', DEFAULT_RATE_LIMIT.global),\n perClient: envCount('RATE_LIMIT_PER_CLIENT', DEFAULT_RATE_LIMIT.perClient),\n trustProxy: envCount('TRUST_PROXY', 0),\n };\n\n const { app, handler } = createHttpApp({ host, allowedHosts, rateLimit });\n const server = serve({ fetch: app.fetch, port, hostname: host }, () => {\n log(`v${VERSION} listening on http://${host.includes(':') ? `[${host}]` : host}:${port}/mcp`);\n if (!['127.0.0.1', 'localhost', '::1'].includes(host) && allowedHosts.length === 0) {\n log('Bound to a public interface without ALLOWED_HOSTS: Host header validation is off.');\n }\n });\n\n const shutdown = () => {\n void handler.close().finally(() => server.close(() => process.exit(0)));\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\n/** Reads a non-negative integer env var; \"true\"/\"false\" map to 1/0. Exits on garbage. */\nfunction envCount(name: string, fallback: number): number {\n const raw = process.env[name]?.trim().toLowerCase();\n if (!raw) return fallback;\n if (raw === 'true') return 1;\n if (raw === 'false') return 0;\n const value = Number(raw);\n if (!Number.isInteger(value) || value < 0) {\n log(`Invalid ${name}=\"${process.env[name]}\": expected a non-negative integer.`);\n process.exit(2);\n }\n return value;\n}\n\nmain();\n","import { createMcpHonoApp } from '@modelcontextprotocol/hono';\nimport { createMcpHandler, type McpHttpHandler } from '@modelcontextprotocol/server';\nimport type { Context, Hono } from 'hono';\nimport { createToolCallLimiter, type ToolCallLimiter, type ToolCallLimitOptions } from './rate-limit.js';\nimport { type CreateServerOptions, createServer } from './server.js';\nimport { VERSION } from './version.js';\n\n// A 100 KB source can roughly double once JSON-escaped; leave room for the envelope.\nexport const MAX_REQUEST_BODY_BYTES = 512 * 1024;\n\nexport interface HttpAppOptions extends CreateServerOptions {\n /** Interface the server binds to; drives the SDK's DNS rebinding protection. */\n host?: string;\n /** Hostnames accepted in the Host header when binding to a public interface. */\n allowedHosts?: string[];\n /** Tool-call limits on /mcp. Defaults: 120 per minute overall, 60 per client, X-Forwarded-For ignored. */\n rateLimit?: Partial<ToolCallLimitOptions>;\n}\n\nexport const DEFAULT_RATE_LIMIT = { global: 120, perClient: 60 } as const;\n\nexport interface HttpApp {\n app: Hono;\n handler: McpHttpHandler;\n limiter: ToolCallLimiter;\n}\n\nexport function createHttpApp(options: HttpAppOptions = {}): HttpApp {\n const handler = createMcpHandler(() => createServer(options), { maxRequestBodySize: MAX_REQUEST_BODY_BYTES });\n\n const app = createMcpHonoApp({\n host: options.host ?? '127.0.0.1',\n ...(options.allowedHosts?.length ? { allowedHosts: options.allowedHosts } : {}),\n maxRequestBodySize: MAX_REQUEST_BODY_BYTES,\n });\n\n app.get('/health', (c) => c.json({ status: 'ok', name: 'archform', version: VERSION }));\n\n const limiter = createToolCallLimiter({ ...DEFAULT_RATE_LIMIT, ...options.rateLimit });\n app.use('/mcp', limiter.middleware);\n\n app.post('/mcp', (c: Context) => handler.fetch(c.req.raw, { parsedBody: c.get('parsedBody') }));\n // Stateless server: no SSE stream to resume and no session to delete.\n app.on(['GET', 'DELETE'], '/mcp', (c) => c.json({ error: 'Method not allowed. Use POST /mcp.' }, 405));\n\n return { app, handler, limiter };\n}\n","import type { Context, MiddlewareHandler } from 'hono';\n\nexport interface ToolCallLimitOptions {\n /** Tool calls per window across all clients: size it to what the Kroki backend can take. 0 disables. */\n global: number;\n /** Tool calls per window per client IP. 0 disables. */\n perClient: number;\n windowMs?: number;\n /**\n * Number of reverse proxies in front of the server. With 0, X-Forwarded-For is\n * ignored (clients can set it to anything) and the socket address is used.\n * With N, the client is the entry the outermost trusted proxy appended:\n * the Nth from the end.\n */\n trustProxy?: number;\n /** Upper bound on tracked client buckets, so rotating addresses cannot grow memory. */\n maxClients?: number;\n}\n\nexport interface ToolCallLimiter {\n middleware: MiddlewareHandler;\n /** Number of client buckets currently held in memory. */\n readonly trackedClients: number;\n}\n\ninterface Window {\n count: number;\n resetAt: number;\n}\n\n/**\n * Fixed-window, in-memory limiter that counts `tools/call` requests (the calls\n * that reach Kroki), not HTTP requests: a session's initialize and tools/list\n * are free. Good for a single instance; use a shared store when running several.\n */\nexport function createToolCallLimiter(options: ToolCallLimitOptions): ToolCallLimiter {\n const windowMs = options.windowMs ?? 60_000;\n const trustProxy = options.trustProxy ?? 0;\n const maxClients = options.maxClients ?? 10_000;\n const global: Window = { count: 0, resetAt: 0 };\n const clients = new Map<string, Window>();\n\n const clientWindow = (key: string, now: number): Window => {\n let entry = clients.get(key);\n if (entry && entry.resetAt > now) return entry;\n if (entry) clients.delete(key);\n if (clients.size >= maxClients) {\n for (const [k, w] of clients) if (w.resetAt <= now) clients.delete(k);\n // Still full of live buckets: evict the oldest (Map keeps insertion order).\n while (clients.size >= maxClients) {\n const oldest = clients.keys().next().value;\n if (oldest === undefined) break;\n clients.delete(oldest);\n }\n }\n entry = { count: 0, resetAt: now + windowMs };\n clients.set(key, entry);\n return entry;\n };\n\n const middleware: MiddlewareHandler = async (c, next) => {\n const calls = countToolCalls(c);\n if (calls === 0) return next();\n\n const now = Date.now();\n if (global.resetAt <= now) {\n global.count = 0;\n global.resetAt = now + windowMs;\n }\n const client = options.perClient > 0 ? clientWindow(clientIp(c, trustProxy), now) : undefined;\n\n if (client && client.count + calls > options.perClient) {\n return tooManyRequests(c, client.resetAt - now, 'Too many tool calls from this client.');\n }\n if (options.global > 0 && global.count + calls > options.global) {\n return tooManyRequests(c, global.resetAt - now, 'The server is at capacity.');\n }\n if (client) client.count += calls;\n global.count += calls;\n return next();\n };\n\n return {\n middleware,\n get trackedClients() {\n return clients.size;\n },\n };\n}\n\n/**\n * Counts JSON-RPC `tools/call` messages in the body parsed by the MCP Hono app.\n * A POST whose body was not parsed as JSON counts as one call, so changing the\n * Content-Type cannot slip past the limit.\n */\nfunction countToolCalls(c: Context): number {\n if (c.req.method !== 'POST') return 0;\n const body: unknown = c.get('parsedBody');\n if (body === undefined) return 1;\n const messages = Array.isArray(body) ? body : [body];\n return messages.filter(\n (m) => typeof m === 'object' && m !== null && (m as { method?: unknown }).method === 'tools/call',\n ).length;\n}\n\nexport function clientIp(c: Context, trustProxy: number): string {\n if (trustProxy > 0) {\n const hops = (c.req.header('x-forwarded-for') ?? '')\n .split(',')\n .map((h) => h.trim())\n .filter(Boolean);\n const ip = hops[Math.max(0, hops.length - trustProxy)];\n if (ip) return ip;\n }\n const incoming = (c.env as { incoming?: { socket?: { remoteAddress?: string } } } | undefined)?.incoming;\n return incoming?.socket?.remoteAddress ?? 'unknown';\n}\n\nfunction tooManyRequests(c: Context, retryMs: number, message: string): Response {\n const seconds = Math.max(1, Math.ceil(retryMs / 1000));\n c.header('Retry-After', String(seconds));\n return c.json({ error: `${message} Retry in ${seconds}s.` }, 429);\n}\n","import { McpServer } from '@modelcontextprotocol/server';\nimport { KrokiRenderer } from './renderers/kroki.js';\nimport type { Renderer } from './renderers/renderer.js';\nimport { registerListFormats } from './tools/list-formats.js';\nimport { registerRenderDiagram } from './tools/render-diagram.js';\nimport { registerValidateDiagram } from './tools/validate-diagram.js';\nimport { VERSION } from './version.js';\n\nexport interface CreateServerOptions {\n renderer?: Renderer;\n}\n\nexport function createServer(options: CreateServerOptions = {}): McpServer {\n const renderer = options.renderer ?? new KrokiRenderer();\n const server = new McpServer(\n { name: 'archform', title: 'Archform', version: VERSION },\n {\n instructions:\n 'Archform renders and validates diagrams as code. Call list_formats to see supported languages and examples, ' +\n 'validate_diagram to check syntax, and render_diagram to produce an image and a shareable link.',\n },\n );\n registerListFormats(server);\n registerRenderDiagram(server, renderer);\n registerValidateDiagram(server, renderer);\n return server;\n}\n","import { deflateSync } from 'node:zlib';\nimport type { OutputFormat } from '../formats.js';\nimport { parseKrokiError } from './kroki-errors.js';\nimport { DiagramSyntaxError, type Renderer, RenderServiceError } from './renderer.js';\n\nexport const DEFAULT_KROKI_URL = 'https://kroki.io';\nconst TIMEOUT_MS = 15_000;\n\nexport interface KrokiRendererOptions {\n /** Base URL of the Kroki server. Defaults to `KROKI_URL` or https://kroki.io. */\n baseUrl?: string;\n /** Request timeout in milliseconds. Defaults to 15 000. */\n timeoutMs?: number;\n}\n\n/** Deflate (zlib) then base64url, the encoding Kroki's GET API expects. */\nexport function encodeKrokiSource(source: string): string {\n return deflateSync(Buffer.from(source, 'utf8'), { level: 9 }).toString('base64url');\n}\n\nexport class KrokiRenderer implements Renderer {\n readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(options: KrokiRendererOptions = {}) {\n const base = options.baseUrl ?? process.env.KROKI_URL ?? DEFAULT_KROKI_URL;\n this.baseUrl = base.replace(/\\/+$/, '');\n this.timeoutMs = options.timeoutMs ?? TIMEOUT_MS;\n }\n\n async render(format: string, source: string, output: OutputFormat): Promise<Buffer> {\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/${format}/${output}`, {\n method: 'POST',\n headers: { 'Content-Type': 'text/plain; charset=utf-8', Accept: contentType(output) },\n body: source,\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n } catch (error) {\n if (error instanceof DOMException && (error.name === 'TimeoutError' || error.name === 'AbortError')) {\n throw new RenderServiceError(\n `Kroki at ${this.baseUrl} did not respond within ${this.timeoutMs / 1000}s. Try again, or simplify the diagram.`,\n );\n }\n const reason = error instanceof Error ? (error.cause instanceof Error ? error.cause.message : error.message) : '';\n throw new RenderServiceError(`Could not reach Kroki at ${this.baseUrl}${reason ? `: ${reason}` : ''}.`);\n }\n\n if (res.ok) return Buffer.from(await res.arrayBuffer());\n\n const body = await res.text().catch(() => '');\n if (res.status === 400) {\n throw new DiagramSyntaxError([parseKrokiError(format, body)]);\n }\n throw new RenderServiceError(\n `Kroki at ${this.baseUrl} returned HTTP ${res.status}${\n res.status >= 500 ? ' (a server-side failure, not necessarily a problem with the diagram)' : ''\n }. Try again later or use a self-hosted Kroki via KROKI_URL.`,\n );\n }\n\n shareUrl(format: string, source: string): string {\n return `${this.baseUrl}/${format}/svg/${encodeKrokiSource(source)}`;\n }\n}\n\nfunction contentType(output: OutputFormat): string {\n return output === 'png' ? 'image/png' : 'image/svg+xml';\n}\n","import type { DiagramError } from './renderer.js';\n\n// Formats whose parser reports 0-based line numbers (PlantUML counts from @startuml as line 0).\nconst ZERO_BASED_LINES = new Set(['plantuml', 'c4plantuml']);\n\n// Patterns seen in Kroki 400 bodies, tried in order:\n// PlantUML: \"Syntax Error? (Assumed diagram type: sequence) (line: 4)\"\n// Graphviz: \"Error: <stdin>: syntax error in line 4 near '}'\"\n// D2: \"err: failed to compile -: -:2:4: maps must be terminated with }\"\n// Structurizr: \"Unexpected tokens (...) at line 3: u = persn \\\"User\\\".\"\n// Mermaid: \"Parse error on line 3:\"\n// JSON: \"... in JSON at position 1 (line 1 column 2)\"\nconst LINE_PATTERNS = [/\\(line:\\s*(\\d+)\\)/i, /-:(\\d+):\\d+:/, /\\bline\\s+(\\d+)/i];\n\n/**\n * Turns a Kroki error body into a diagram error the model can act on:\n * strips the \"Error 400:\" prefix, stack frames and process noise, and\n * extracts a 1-based line number when the message carries one.\n */\nexport function parseKrokiError(format: string, body: string): DiagramError {\n const lines = body\n .replace(/^\\s*Error \\d{3}:\\s*/, '')\n .split(/\\r?\\n/)\n .map((l) => l.trimEnd())\n .filter((l) => l.trim() !== '' && !/^\\s+at\\s/.test(l) && !/^\\(exit code \\d+\\)$/.test(l.trim()));\n\n // Kroki often repeats the first line verbatim after a JS stack trace; keep one copy.\n const unique = lines.filter((l, i) => lines.indexOf(l) === i);\n let message = unique.join('\\n').trim() || 'The diagram could not be rendered.';\n if (message.length > 2000) message = `${message.slice(0, 2000)}…`;\n\n for (const pattern of LINE_PATTERNS) {\n const match = pattern.exec(message);\n if (match?.[1]) {\n const raw = Number.parseInt(match[1], 10);\n const line = ZERO_BASED_LINES.has(format) ? raw + 1 : raw;\n return { line: Math.max(line, 1), message };\n }\n }\n return { message };\n}\n","import type { OutputFormat } from '../formats.js';\n\nexport interface DiagramError {\n line?: number;\n message: string;\n}\n\nexport interface Renderer {\n /**\n * Renders `source` in the given canonical format.\n * Rejects with `DiagramSyntaxError` when the diagram is invalid and with\n * `RenderServiceError` when the renderer itself fails.\n */\n render(format: string, source: string, output: OutputFormat): Promise<Buffer>;\n /** A URL that renders the diagram as SVG when opened in a browser, if the renderer has one. */\n shareUrl?(format: string, source: string): string;\n}\n\n/** The diagram source is invalid. The message comes from the parser. */\nexport class DiagramSyntaxError extends Error {\n override readonly name = 'DiagramSyntaxError';\n constructor(readonly errors: DiagramError[]) {\n super(errors.map(formatDiagramError).join('\\n'));\n }\n}\n\n/** The renderer could not be reached, timed out, or failed for reasons unrelated to the source. */\nexport class RenderServiceError extends Error {\n override readonly name = 'RenderServiceError';\n}\n\nexport function formatDiagramError(error: DiagramError): string {\n return error.line === undefined ? error.message : `Line ${error.line}: ${error.message}`;\n}\n","import type { McpServer } from '@modelcontextprotocol/server';\nimport * as z from 'zod/v4';\nimport { FORMATS } from '../formats.js';\nimport { READ_ONLY_ANNOTATIONS } from './shared.js';\n\nconst formatEntry = z.object({\n id: z.string(),\n aliases: z.array(z.string()),\n outputs: z.array(z.enum(['svg', 'png'])),\n description: z.string(),\n docsUrl: z.string(),\n example: z.string(),\n});\n\nexport function registerListFormats(server: McpServer): void {\n server.registerTool(\n 'list_formats',\n {\n title: 'List diagram formats',\n description:\n 'List the diagram languages this server can render (Mermaid, PlantUML, C4-PlantUML, Graphviz, D2, Structurizr, BPMN, Excalidraw), ' +\n \"with each one's aliases, supported outputs (svg, png), syntax docs link and a minimal valid example. \" +\n 'Call this before writing a diagram in a format you are unsure of, or to check whether PNG output is available.',\n outputSchema: z.object({ formats: z.array(formatEntry) }),\n annotations: READ_ONLY_ANNOTATIONS,\n },\n async () => {\n const formats = FORMATS.map((f) => ({ ...f, aliases: [...f.aliases], outputs: [...f.outputs] }));\n return {\n content: [{ type: 'text', text: JSON.stringify({ formats }, null, 2) }],\n structuredContent: { formats },\n };\n },\n );\n}\n","export type OutputFormat = 'svg' | 'png';\n\nexport interface DiagramFormat {\n /** Canonical id, also the Kroki diagram type. */\n id: string;\n aliases: readonly string[];\n /** Outputs Kroki can produce for this format. */\n outputs: readonly OutputFormat[];\n description: string;\n docsUrl: string;\n /** A minimal diagram that renders without errors. */\n example: string;\n}\n\n// Output support checked against https://kroki.io/#support.\nexport const FORMATS = [\n {\n id: 'mermaid',\n aliases: [],\n outputs: ['svg', 'png'],\n description: 'Flowcharts, sequence, class, state, ER, Gantt and other diagrams in Markdown-like syntax.',\n docsUrl: 'https://mermaid.js.org/intro/syntax-reference.html',\n example: 'flowchart LR\\n A[Client] --> B[API]\\n B --> C[(Database)]\\n',\n },\n {\n id: 'plantuml',\n aliases: [],\n outputs: ['svg', 'png'],\n description: 'UML diagrams (sequence, class, component, activity, state, deployment) and more.',\n docsUrl: 'https://plantuml.com/',\n example: '@startuml\\nAlice -> Bob: Request\\nBob --> Alice: Response\\n@enduml\\n',\n },\n {\n id: 'c4plantuml',\n aliases: [],\n outputs: ['svg', 'png'],\n description: 'C4 model architecture diagrams (context, container, component) on top of PlantUML.',\n docsUrl: 'https://github.com/plantuml-stdlib/C4-PlantUML',\n example:\n '@startuml\\n!include <C4/C4_Context>\\nPerson(user, \"User\")\\nSystem(app, \"Web App\")\\nRel(user, app, \"Uses\")\\n@enduml\\n',\n },\n {\n id: 'graphviz',\n aliases: ['dot'],\n outputs: ['svg', 'png'],\n description: 'Graphs and networks in the Graphviz DOT language.',\n docsUrl: 'https://graphviz.org/doc/info/lang.html',\n example: 'digraph G {\\n rankdir=LR;\\n client -> api -> db;\\n}\\n',\n },\n {\n id: 'd2',\n aliases: [],\n outputs: ['svg'],\n description: 'Declarative diagrams with automatic layout, well suited to software architecture.',\n docsUrl: 'https://d2lang.com/tour/intro',\n example: 'client -> api: HTTPS\\napi -> db: SQL\\ndb: {shape: cylinder}\\n',\n },\n {\n id: 'structurizr',\n aliases: [],\n outputs: ['svg', 'png'],\n description: 'C4 model workspaces in the Structurizr DSL (model plus views).',\n docsUrl: 'https://docs.structurizr.com/dsl/language',\n example:\n 'workspace {\\n model {\\n user = person \"User\"\\n app = softwareSystem \"App\"\\n user -> app \"Uses\"\\n }\\n views {\\n systemContext app {\\n include *\\n autolayout lr\\n }\\n }\\n}\\n',\n },\n {\n id: 'bpmn',\n aliases: [],\n outputs: ['svg'],\n description: 'Business process diagrams in BPMN 2.0 XML (including diagram interchange coordinates).',\n docsUrl: 'https://www.omg.org/spec/BPMN/2.0/',\n example:\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<bpmn:definitions xmlns:bpmn=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\" xmlns:dc=\"http://www.omg.org/spec/DD/20100524/DC\" xmlns:di=\"http://www.omg.org/spec/DD/20100524/DI\" id=\"defs\" targetNamespace=\"http://bpmn.io/schema/bpmn\">\\n <bpmn:process id=\"process\" isExecutable=\"false\">\\n <bpmn:startEvent id=\"start\"><bpmn:outgoing>flow</bpmn:outgoing></bpmn:startEvent>\\n <bpmn:endEvent id=\"end\"><bpmn:incoming>flow</bpmn:incoming></bpmn:endEvent>\\n <bpmn:sequenceFlow id=\"flow\" sourceRef=\"start\" targetRef=\"end\" />\\n </bpmn:process>\\n <bpmndi:BPMNDiagram id=\"diagram\">\\n <bpmndi:BPMNPlane id=\"plane\" bpmnElement=\"process\">\\n <bpmndi:BPMNShape id=\"start_di\" bpmnElement=\"start\"><dc:Bounds x=\"100\" y=\"100\" width=\"36\" height=\"36\" /></bpmndi:BPMNShape>\\n <bpmndi:BPMNShape id=\"end_di\" bpmnElement=\"end\"><dc:Bounds x=\"250\" y=\"100\" width=\"36\" height=\"36\" /></bpmndi:BPMNShape>\\n <bpmndi:BPMNEdge id=\"flow_di\" bpmnElement=\"flow\"><di:waypoint x=\"136\" y=\"118\" /><di:waypoint x=\"250\" y=\"118\" /></bpmndi:BPMNEdge>\\n </bpmndi:BPMNPlane>\\n </bpmndi:BPMNDiagram>\\n</bpmn:definitions>\\n',\n },\n {\n id: 'excalidraw',\n aliases: [],\n outputs: ['svg'],\n description: 'Hand-drawn style sketches from an Excalidraw JSON scene.',\n docsUrl: 'https://docs.excalidraw.com/docs/codebase/json-schema',\n example:\n '{\\n \"type\": \"excalidraw\",\\n \"version\": 2,\\n \"source\": \"https://excalidraw.com\",\\n \"elements\": [\\n {\\n \"id\": \"box\",\\n \"type\": \"rectangle\",\\n \"x\": 0,\\n \"y\": 0,\\n \"width\": 160,\\n \"height\": 80,\\n \"angle\": 0,\\n \"strokeColor\": \"#1e1e1e\",\\n \"backgroundColor\": \"transparent\",\\n \"fillStyle\": \"solid\",\\n \"strokeWidth\": 2,\\n \"strokeStyle\": \"solid\",\\n \"roughness\": 1,\\n \"opacity\": 100,\\n \"groupIds\": [],\\n \"seed\": 1,\\n \"version\": 1,\\n \"versionNonce\": 1,\\n \"isDeleted\": false,\\n \"boundElements\": null,\\n \"updated\": 1,\\n \"link\": null,\\n \"locked\": false\\n }\\n ],\\n \"appState\": { \"viewBackgroundColor\": \"#ffffff\" },\\n \"files\": {}\\n}\\n',\n },\n] as const satisfies readonly DiagramFormat[];\n\nexport type FormatId = (typeof FORMATS)[number]['id'];\n\n/** Every accepted `format` value: canonical ids followed by aliases. */\nexport const FORMAT_NAMES = [...FORMATS.map((f) => f.id), ...FORMATS.flatMap((f) => f.aliases)] as [\n string,\n ...string[],\n];\n\nconst byName = new Map<string, DiagramFormat>();\nfor (const format of FORMATS) {\n byName.set(format.id, format);\n for (const alias of format.aliases) byName.set(alias, format);\n}\n\n/** Resolves an id or alias (case-insensitive) to its format, or undefined. */\nexport function resolveFormat(name: string): DiagramFormat | undefined {\n return byName.get(name.trim().toLowerCase());\n}\n","import type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/server';\nimport * as z from 'zod/v4';\nimport { FORMAT_NAMES } from '../formats.js';\n\nexport const MAX_SOURCE_BYTES = 100 * 1024;\n\n/** Every tool only reads input and calls an external renderer. */\nexport const READ_ONLY_ANNOTATIONS: ToolAnnotations = { readOnlyHint: true, openWorldHint: true };\n\nexport const formatSchema = z\n .enum(FORMAT_NAMES)\n .describe(`Diagram language. One of: ${FORMAT_NAMES.join(', ')} (\"dot\" is an alias for graphviz).`);\n\nexport const sourceSchema = z\n .string()\n .min(1, 'source is empty')\n .refine((s) => Buffer.byteLength(s, 'utf8') <= MAX_SOURCE_BYTES, {\n message: `source must be at most ${MAX_SOURCE_BYTES / 1024} KB`,\n })\n .describe('The diagram source text, exactly as it would appear in a file of that format.');\n\nexport function toolError(text: string): CallToolResult {\n return { content: [{ type: 'text', text }], isError: true };\n}\n","import { createHash } from 'node:crypto';\nimport type { CallToolResult, McpServer } from '@modelcontextprotocol/server';\nimport * as z from 'zod/v4';\nimport { resolveFormat } from '../formats.js';\nimport { DiagramSyntaxError, type Renderer, RenderServiceError } from '../renderers/renderer.js';\nimport { formatSchema, READ_ONLY_ANNOTATIONS, sourceSchema, toolError } from './shared.js';\n\nexport function registerRenderDiagram(server: McpServer, renderer: Renderer): void {\n server.registerTool(\n 'render_diagram',\n {\n title: 'Render diagram',\n description:\n 'Render diagram source (Mermaid, PlantUML, C4-PlantUML, Graphviz/DOT, D2, Structurizr, BPMN, Excalidraw) to an SVG or PNG image, ' +\n 'and get a shareable link that opens the diagram in a browser. Use it when the user wants to see a diagram. ' +\n 'If the source is invalid the result is an error with the parser message and line number: fix the source and retry. ' +\n 'Call validate_diagram to check syntax without rendering, or list_formats for examples and which formats support PNG.',\n inputSchema: z.object({\n format: formatSchema,\n source: sourceSchema,\n output: z\n .enum(['svg', 'png'])\n .default('svg')\n .describe('Image type. Defaults to svg. d2, bpmn and excalidraw support svg only.'),\n }),\n annotations: READ_ONLY_ANNOTATIONS,\n },\n async ({ format: name, source, output }): Promise<CallToolResult> => {\n const format = resolveFormat(name);\n if (!format) return toolError(`Unknown format \"${name}\". Call list_formats to see the supported formats.`);\n if (!format.outputs.includes(output)) {\n return toolError(\n `${format.id} does not support ${output} output. Supported: ${format.outputs.join(', ')}. Retry with output \"svg\".`,\n );\n }\n\n let image: Buffer;\n try {\n image = await renderer.render(format.id, source, output);\n } catch (error) {\n if (error instanceof DiagramSyntaxError) {\n return toolError(\n `The ${format.id} diagram is invalid:\\n${error.message}\\n\\nFix the source and call render_diagram again. ` +\n `list_formats has a valid ${format.id} example.`,\n );\n }\n if (error instanceof RenderServiceError) return toolError(error.message);\n throw error;\n }\n\n const url = renderer.shareUrl?.(format.id, source);\n const content: CallToolResult['content'] = [];\n if (output === 'png') {\n content.push({ type: 'image', data: image.toString('base64'), mimeType: 'image/png' });\n } else {\n const hash = createHash('sha256').update(source).digest('hex').slice(0, 16);\n content.push({\n type: 'resource',\n resource: {\n uri: `archform://diagram/${format.id}/${hash}.svg`,\n mimeType: 'image/svg+xml',\n text: image.toString('utf8'),\n },\n });\n }\n content.push({\n type: 'text',\n text: url\n ? `Rendered ${format.id} diagram as ${output}. Shareable link (opens the SVG in a browser): ${url}`\n : `Rendered ${format.id} diagram as ${output}.`,\n });\n return { content };\n },\n );\n}\n","import type { CallToolResult, McpServer } from '@modelcontextprotocol/server';\nimport * as z from 'zod/v4';\nimport { resolveFormat } from '../formats.js';\nimport { type DiagramError, DiagramSyntaxError, type Renderer, RenderServiceError } from '../renderers/renderer.js';\nimport { formatSchema, READ_ONLY_ANNOTATIONS, sourceSchema, toolError } from './shared.js';\n\nconst outputSchema = z.object({\n valid: z.boolean(),\n errors: z.array(z.object({ line: z.number().int().optional(), message: z.string() })),\n});\n\nexport function registerValidateDiagram(server: McpServer, renderer: Renderer): void {\n server.registerTool(\n 'validate_diagram',\n {\n title: 'Validate diagram',\n description:\n 'Check whether diagram source (Mermaid, PlantUML, C4-PlantUML, Graphviz/DOT, D2, Structurizr, BPMN, Excalidraw) is valid, ' +\n 'without returning an image. Returns { valid, errors: [{ line?, message }] }. ' +\n 'Use it to check syntax you are unsure of before rendering or before handing diagram code to the user. ' +\n 'Call list_formats for a valid example of each format.',\n inputSchema: z.object({ format: formatSchema, source: sourceSchema }),\n outputSchema,\n annotations: READ_ONLY_ANNOTATIONS,\n },\n async ({ format: name, source }): Promise<CallToolResult> => {\n const format = resolveFormat(name);\n if (!format) return toolError(`Unknown format \"${name}\". Call list_formats to see the supported formats.`);\n\n let errors: DiagramError[] = [];\n try {\n // Every format supports SVG, and rendering is the only check Kroki offers.\n await renderer.render(format.id, source, 'svg');\n } catch (error) {\n if (error instanceof DiagramSyntaxError) errors = error.errors;\n else if (error instanceof RenderServiceError) {\n return toolError(`Could not validate the diagram: ${error.message}`);\n } else throw error;\n }\n\n const result = { valid: errors.length === 0, errors };\n return {\n content: [{ type: 'text', text: JSON.stringify(result) }],\n structuredContent: result,\n };\n },\n );\n}\n","{\n \"name\": \"@archform/mcp\",\n \"version\": \"0.2.0\",\n \"description\": \"MCP server that renders and validates diagrams as code: Mermaid, PlantUML, C4, Graphviz, D2, Structurizr, BPMN and Excalidraw.\",\n \"mcpName\": \"io.github.himanshu-mamgain/archform\",\n \"license\": \"MIT\",\n \"author\": \"Himanshu Mamgain\",\n \"homepage\": \"https://github.com/himanshu-mamgain/archform/tree/main/packages/mcp#readme\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/himanshu-mamgain/archform.git\",\n \"directory\": \"packages/mcp\"\n },\n \"bugs\": \"https://github.com/himanshu-mamgain/archform/issues\",\n \"type\": \"module\",\n \"bin\": {\n \"archform-mcp\": \"dist/index.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"engines\": {\n \"node\": \">=22\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"keywords\": [\n \"mcp\",\n \"model-context-protocol\",\n \"diagrams-as-code\",\n \"mermaid\",\n \"plantuml\",\n \"graphviz\",\n \"d2\",\n \"c4\",\n \"architecture\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node dist/index.js\",\n \"typecheck\": \"tsc -p tsconfig.json\",\n \"test\": \"vitest run\",\n \"test:live\": \"cross-env KROKI_LIVE=1 vitest run test/live.test.ts\",\n \"prepublishOnly\": \"tsup\"\n },\n \"devDependencies\": {\n \"@modelcontextprotocol/client\": \"2.3.1\",\n \"@types/node\": \"22.20.5\",\n \"cross-env\": \"^10.1.0\",\n \"tsup\": \"8.5.1\",\n \"vitest\": \"4.1.12\"\n },\n \"dependencies\": {\n \"@hono/node-server\": \"2.1.5\",\n \"@modelcontextprotocol/hono\": \"2.0.2\",\n \"@modelcontextprotocol/server\": \"2.3.1\",\n \"hono\": \"4.13.13\",\n \"zod\": \"4.6.7\"\n }\n}\n","import pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n"],"mappings":"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","names":["z","z","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/http.ts","../src/rate-limit.ts","../src/server.ts","../src/renderers/kroki.ts","../src/renderers/kroki-errors.ts","../src/renderers/renderer.ts","../src/tools/list-formats.ts","../src/formats.ts","../src/tools/shared.ts","../src/tools/render-diagram.ts","../src/tools/validate-diagram.ts","../package.json","../src/version.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from 'node:util';\nimport { serve } from '@hono/node-server';\nimport { serveStdio } from '@modelcontextprotocol/server/stdio';\nimport { createHttpApp, DEFAULT_RATE_LIMIT } from './http.js';\nimport { createServer } from './server.js';\nimport { VERSION } from './version.js';\n\n// stdout carries the MCP protocol in stdio mode: every log goes to stderr.\nconst log = (message: string) => console.error(`[archform] ${message}`);\n\nconst HELP = `archform-mcp ${VERSION}\n\nUsage:\n archform-mcp Serve MCP over stdio (default)\n archform-mcp --http Serve MCP over Streamable HTTP (POST /mcp, GET /health)\n\nOptions:\n --http Use Streamable HTTP instead of stdio\n --port <n> HTTP port (default: $PORT or 3000)\n --host <addr> HTTP bind address (default: $HOST or 127.0.0.1)\n -v, --version Print the version\n -h, --help Print this help\n\nEnvironment:\n KROKI_URL Kroki server (default: https://kroki.io)\n ALLOWED_HOSTS Comma-separated Host header values accepted when binding publicly\n RATE_LIMIT Tool calls per minute across all clients (default: 120, 0 disables)\n RATE_LIMIT_PER_CLIENT Tool calls per minute per client IP (default: 60, 0 disables)\n TRUST_PROXY Reverse proxies in front of the server whose X-Forwarded-For is trusted\n (default: 0 = ignore the header; \"true\" = 1)\n`;\n\nfunction main(): void {\n let values: ReturnType<typeof parse>['values'];\n try {\n values = parse().values;\n } catch (error) {\n log(error instanceof Error ? error.message : String(error));\n console.error(HELP);\n process.exit(2);\n }\n\n if (values.help) {\n console.error(HELP);\n return;\n }\n if (values.version) {\n console.error(VERSION);\n return;\n }\n if (values.http) {\n startHttp(values.port, values.host);\n return;\n }\n\n const handle = serveStdio(() => createServer());\n const shutdown = () => void handle.close().finally(() => process.exit(0));\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n log(`v${VERSION} running on stdio (Kroki: ${process.env.KROKI_URL ?? 'https://kroki.io'})`);\n}\n\nfunction parse() {\n return parseArgs({\n options: {\n http: { type: 'boolean', default: false },\n port: { type: 'string' },\n host: { type: 'string' },\n version: { type: 'boolean', short: 'v' },\n help: { type: 'boolean', short: 'h' },\n },\n strict: true,\n });\n}\n\nfunction startHttp(portArg: string | undefined, hostArg: string | undefined): void {\n const port = Number(portArg ?? process.env.PORT ?? 3000);\n if (!Number.isInteger(port) || port < 1 || port > 65_535) {\n log(`Invalid port \"${portArg ?? process.env.PORT}\".`);\n process.exit(2);\n }\n const host = hostArg ?? process.env.HOST ?? '127.0.0.1';\n const allowedHosts = (process.env.ALLOWED_HOSTS ?? '')\n .split(',')\n .map((h) => h.trim())\n .filter(Boolean);\n const rateLimit = {\n global: envCount('RATE_LIMIT', DEFAULT_RATE_LIMIT.global),\n perClient: envCount('RATE_LIMIT_PER_CLIENT', DEFAULT_RATE_LIMIT.perClient),\n trustProxy: envCount('TRUST_PROXY', 0),\n };\n\n const { app, handler } = createHttpApp({ host, allowedHosts, rateLimit });\n const server = serve({ fetch: app.fetch, port, hostname: host }, () => {\n log(`v${VERSION} listening on http://${host.includes(':') ? `[${host}]` : host}:${port}/mcp`);\n if (!['127.0.0.1', 'localhost', '::1'].includes(host) && allowedHosts.length === 0) {\n log('Bound to a public interface without ALLOWED_HOSTS: Host header validation is off.');\n }\n });\n\n const shutdown = () => {\n void handler.close().finally(() => server.close(() => process.exit(0)));\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\n/** Reads a non-negative integer env var; \"true\"/\"false\" map to 1/0. Exits on garbage. */\nfunction envCount(name: string, fallback: number): number {\n const raw = process.env[name]?.trim().toLowerCase();\n if (!raw) return fallback;\n if (raw === 'true') return 1;\n if (raw === 'false') return 0;\n const value = Number(raw);\n if (!Number.isInteger(value) || value < 0) {\n log(`Invalid ${name}=\"${process.env[name]}\": expected a non-negative integer.`);\n process.exit(2);\n }\n return value;\n}\n\nmain();\n","import { createMcpHonoApp } from '@modelcontextprotocol/hono';\nimport { createMcpHandler, type McpHttpHandler } from '@modelcontextprotocol/server';\nimport type { Context, Hono } from 'hono';\nimport { createToolCallLimiter, RATE_LIMITED, type ToolCallLimiter, type ToolCallLimitOptions } from './rate-limit.js';\nimport { type CreateServerOptions, createServer } from './server.js';\nimport { VERSION } from './version.js';\n\n// A 100 KB source can roughly double once JSON-escaped; leave room for the envelope.\nexport const MAX_REQUEST_BODY_BYTES = 512 * 1024;\n\n// Carries the limiter's verdict from the route into the per-request server\n// factory. Always stripped from the client's request before being set.\nconst RATE_LIMIT_HEADER = 'x-archform-rate-limited';\n\nexport interface HttpAppOptions extends CreateServerOptions {\n /** Interface the server binds to; drives the SDK's DNS rebinding protection. */\n host?: string;\n /** Hostnames accepted in the Host header when binding to a public interface. */\n allowedHosts?: string[];\n /** Tool-call limits on /mcp. Defaults: 120 per minute overall, 60 per client, X-Forwarded-For ignored. */\n rateLimit?: Partial<ToolCallLimitOptions>;\n}\n\nexport const DEFAULT_RATE_LIMIT = { global: 120, perClient: 60 } as const;\n\nexport interface HttpApp {\n app: Hono;\n handler: McpHttpHandler;\n limiter: ToolCallLimiter;\n}\n\nexport function createHttpApp(options: HttpAppOptions = {}): HttpApp {\n const handler = createMcpHandler(\n ({ requestInfo }) => {\n const limited = requestInfo?.headers.get(RATE_LIMIT_HEADER);\n return createServer(limited ? { ...options, unavailable: decodeURIComponent(limited) } : options);\n },\n { maxRequestBodySize: MAX_REQUEST_BODY_BYTES },\n );\n\n const app = createMcpHonoApp({\n host: options.host ?? '127.0.0.1',\n ...(options.allowedHosts?.length ? { allowedHosts: options.allowedHosts } : {}),\n maxRequestBodySize: MAX_REQUEST_BODY_BYTES,\n });\n\n app.get('/health', (c) => c.json({ status: 'ok', name: 'archform', version: VERSION }));\n\n const limiter = createToolCallLimiter({ ...DEFAULT_RATE_LIMIT, ...options.rateLimit });\n app.use('/mcp', limiter.middleware);\n\n app.post('/mcp', (c: Context) => {\n const parsedBody: unknown = c.get('parsedBody');\n const limited: string | undefined = c.get(RATE_LIMITED);\n if (!limited && !c.req.raw.headers.has(RATE_LIMIT_HEADER)) return handler.fetch(c.req.raw, { parsedBody });\n\n const headers = new Headers(c.req.raw.headers);\n headers.delete(RATE_LIMIT_HEADER);\n if (limited) headers.set(RATE_LIMIT_HEADER, encodeURIComponent(limited));\n const request = new Request(c.req.url, {\n method: 'POST',\n headers,\n body: parsedBody === undefined ? c.req.raw.body : JSON.stringify(parsedBody),\n ...(parsedBody === undefined ? { duplex: 'half' } : {}),\n } as RequestInit);\n return handler.fetch(request, { parsedBody });\n });\n // Stateless server: no SSE stream to resume and no session to delete.\n app.on(['GET', 'DELETE'], '/mcp', (c) => c.json({ error: 'Method not allowed. Use POST /mcp.' }, 405));\n\n return { app, handler, limiter };\n}\n","import type { Context, MiddlewareHandler, Next } from 'hono';\n\nexport interface ToolCallLimitOptions {\n /** Tool calls per window across all clients: size it to what the Kroki backend can take. 0 disables. */\n global: number;\n /** Tool calls per window per client IP. 0 disables. */\n perClient: number;\n windowMs?: number;\n /**\n * Number of reverse proxies in front of the server. With 0, X-Forwarded-For is\n * ignored (clients can set it to anything) and the socket address is used.\n * With N, the client is the entry the outermost trusted proxy appended:\n * the Nth from the end.\n */\n trustProxy?: number;\n /** Upper bound on tracked client buckets, so rotating addresses cannot grow memory. */\n maxClients?: number;\n}\n\nexport interface ToolCallLimiter {\n middleware: MiddlewareHandler;\n /** Number of client buckets currently held in memory. */\n readonly trackedClients: number;\n}\n\ninterface Window {\n count: number;\n resetAt: number;\n}\n\n/**\n * Fixed-window, in-memory limiter that counts `tools/call` requests (the calls\n * that reach Kroki), not HTTP requests: a session's initialize and tools/list\n * are free. Good for a single instance; use a shared store when running several.\n */\nexport function createToolCallLimiter(options: ToolCallLimitOptions): ToolCallLimiter {\n const windowMs = options.windowMs ?? 60_000;\n const trustProxy = options.trustProxy ?? 0;\n const maxClients = options.maxClients ?? 10_000;\n const global: Window = { count: 0, resetAt: 0 };\n const clients = new Map<string, Window>();\n\n const clientWindow = (key: string, now: number): Window => {\n let entry = clients.get(key);\n if (entry && entry.resetAt > now) return entry;\n if (entry) clients.delete(key);\n if (clients.size >= maxClients) {\n for (const [k, w] of clients) if (w.resetAt <= now) clients.delete(k);\n // Still full of live buckets: evict the oldest (Map keeps insertion order).\n while (clients.size >= maxClients) {\n const oldest = clients.keys().next().value;\n if (oldest === undefined) break;\n clients.delete(oldest);\n }\n }\n entry = { count: 0, resetAt: now + windowMs };\n clients.set(key, entry);\n return entry;\n };\n\n const middleware: MiddlewareHandler = async (c, next) => {\n const calls = countToolCalls(c);\n if (calls === 0) return next();\n\n const now = Date.now();\n if (global.resetAt <= now) {\n global.count = 0;\n global.resetAt = now + windowMs;\n }\n const client = options.perClient > 0 ? clientWindow(clientIp(c, trustProxy), now) : undefined;\n\n if (client && client.count + calls > options.perClient) {\n return tooManyRequests(c, next, client.resetAt - now, 'Too many tool calls from this client.');\n }\n if (options.global > 0 && global.count + calls > options.global) {\n return tooManyRequests(c, next, global.resetAt - now, 'The server is at capacity.');\n }\n if (client) client.count += calls;\n global.count += calls;\n return next();\n };\n\n return {\n middleware,\n get trackedClients() {\n return clients.size;\n },\n };\n}\n\n/**\n * Counts JSON-RPC `tools/call` messages in the body parsed by the MCP Hono app.\n * A POST whose body was not parsed as JSON counts as one call, so changing the\n * Content-Type cannot slip past the limit.\n */\nfunction countToolCalls(c: Context): number {\n if (c.req.method !== 'POST') return 0;\n const body: unknown = c.get('parsedBody');\n if (body === undefined) return 1;\n const messages = Array.isArray(body) ? body : [body];\n return messages.filter(\n (m) => typeof m === 'object' && m !== null && (m as { method?: unknown }).method === 'tools/call',\n ).length;\n}\n\nexport function clientIp(c: Context, trustProxy: number): string {\n if (trustProxy > 0) {\n const hops = (c.req.header('x-forwarded-for') ?? '')\n .split(',')\n .map((h) => h.trim())\n .filter(Boolean);\n const ip = hops[Math.max(0, hops.length - trustProxy)];\n if (ip) return ip;\n }\n const incoming = (c.env as { incoming?: { socket?: { remoteAddress?: string } } } | undefined)?.incoming;\n return incoming?.socket?.remoteAddress ?? 'unknown';\n}\n\n/** Context key under which the limiter leaves its message for a rate-limited single tool call. */\nexport const RATE_LIMITED = 'archformRateLimited';\n\n/**\n * A single `tools/call` is let through with a message under `RATE_LIMITED`; the\n * route turns it into a tool result with `isError: true` (through the SDK, so it\n * is valid on every protocol revision) and the model can tell the user to wait.\n * An HTTP 429 would surface as an opaque transport failure. Batches and\n * unparsed bodies still get 429.\n */\nasync function tooManyRequests(\n c: Context,\n next: Next,\n retryMs: number,\n message: string,\n): Promise<Response | undefined> {\n const seconds = Math.max(1, Math.ceil(retryMs / 1000));\n const text = `Rate limit reached: ${message} Retry in ${seconds}s.`;\n if (isSingleToolCall(c.get('parsedBody'))) {\n c.set(RATE_LIMITED, text);\n await next();\n return;\n }\n c.header('Retry-After', String(seconds));\n return c.json({ error: text }, 429);\n}\n\nfunction isSingleToolCall(body: unknown): boolean {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return false;\n const { method, id } = body as { method?: unknown; id?: unknown };\n return method === 'tools/call' && (typeof id === 'string' || typeof id === 'number');\n}\n","import { McpServer } from '@modelcontextprotocol/server';\nimport { KrokiRenderer } from './renderers/kroki.js';\nimport type { Renderer } from './renderers/renderer.js';\nimport { registerListFormats } from './tools/list-formats.js';\nimport { registerRenderDiagram } from './tools/render-diagram.js';\nimport { registerValidateDiagram } from './tools/validate-diagram.js';\nimport { VERSION } from './version.js';\n\nexport interface CreateServerOptions {\n renderer?: Renderer;\n /** When set, every tool returns this message as a tool error without doing any work (used for rate limiting). */\n unavailable?: string;\n}\n\nexport function createServer(options: CreateServerOptions = {}): McpServer {\n const renderer = options.renderer ?? new KrokiRenderer();\n const server = new McpServer(\n { name: 'archform', title: 'Archform', version: VERSION },\n {\n instructions:\n 'Archform renders and validates diagrams as code. Call list_formats to see supported languages and examples, ' +\n 'validate_diagram to check syntax, and render_diagram to produce an image and a shareable link.',\n },\n );\n registerListFormats(server, options.unavailable);\n registerRenderDiagram(server, renderer, options.unavailable);\n registerValidateDiagram(server, renderer, options.unavailable);\n return server;\n}\n","import { deflateSync } from 'node:zlib';\nimport type { OutputFormat } from '../formats.js';\nimport { parseKrokiError } from './kroki-errors.js';\nimport { DiagramSyntaxError, type Renderer, RenderServiceError } from './renderer.js';\n\nexport const DEFAULT_KROKI_URL = 'https://kroki.io';\nconst TIMEOUT_MS = 15_000;\n\nexport interface KrokiRendererOptions {\n /** Base URL of the Kroki server. Defaults to `KROKI_URL` or https://kroki.io. */\n baseUrl?: string;\n /** Request timeout in milliseconds. Defaults to 15 000. */\n timeoutMs?: number;\n}\n\n/** Deflate (zlib) then base64url, the encoding Kroki's GET API expects. */\nexport function encodeKrokiSource(source: string): string {\n return deflateSync(Buffer.from(source, 'utf8'), { level: 9 }).toString('base64url');\n}\n\nexport class KrokiRenderer implements Renderer {\n readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(options: KrokiRendererOptions = {}) {\n const base = options.baseUrl ?? process.env.KROKI_URL ?? DEFAULT_KROKI_URL;\n this.baseUrl = base.replace(/\\/+$/, '');\n this.timeoutMs = options.timeoutMs ?? TIMEOUT_MS;\n }\n\n async render(format: string, source: string, output: OutputFormat): Promise<Buffer> {\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/${format}/${output}`, {\n method: 'POST',\n headers: { 'Content-Type': 'text/plain; charset=utf-8', Accept: contentType(output) },\n body: source,\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n } catch (error) {\n if (error instanceof DOMException && (error.name === 'TimeoutError' || error.name === 'AbortError')) {\n throw new RenderServiceError(\n `Kroki at ${this.baseUrl} did not respond within ${this.timeoutMs / 1000}s. Try again, or simplify the diagram.`,\n );\n }\n const reason = error instanceof Error ? (error.cause instanceof Error ? error.cause.message : error.message) : '';\n throw new RenderServiceError(`Could not reach Kroki at ${this.baseUrl}${reason ? `: ${reason}` : ''}.`);\n }\n\n if (res.ok) return Buffer.from(await res.arrayBuffer());\n\n const body = await res.text().catch(() => '');\n if (res.status === 400) {\n throw new DiagramSyntaxError([parseKrokiError(format, body)]);\n }\n throw new RenderServiceError(\n `Kroki at ${this.baseUrl} returned HTTP ${res.status}${\n res.status >= 500 ? ' (a server-side failure, not necessarily a problem with the diagram)' : ''\n }. Try again later or use a self-hosted Kroki via KROKI_URL.`,\n );\n }\n\n shareUrl(format: string, source: string): string {\n return `${this.baseUrl}/${format}/svg/${encodeKrokiSource(source)}`;\n }\n}\n\nfunction contentType(output: OutputFormat): string {\n return output === 'png' ? 'image/png' : 'image/svg+xml';\n}\n","import type { DiagramError } from './renderer.js';\n\n// Formats whose parser reports 0-based line numbers (PlantUML counts from @startuml as line 0).\nconst ZERO_BASED_LINES = new Set(['plantuml', 'c4plantuml']);\n\n// Patterns seen in Kroki 400 bodies, tried in order:\n// PlantUML: \"Syntax Error? (Assumed diagram type: sequence) (line: 4)\"\n// Graphviz: \"Error: <stdin>: syntax error in line 4 near '}'\"\n// D2: \"err: failed to compile -: -:2:4: maps must be terminated with }\"\n// Structurizr: \"Unexpected tokens (...) at line 3: u = persn \\\"User\\\".\"\n// Mermaid: \"Parse error on line 3:\"\n// JSON: \"... in JSON at position 1 (line 1 column 2)\"\nconst LINE_PATTERNS = [/\\(line:\\s*(\\d+)\\)/i, /-:(\\d+):\\d+:/, /\\bline\\s+(\\d+)/i];\n\n/**\n * Turns a Kroki error body into a diagram error the model can act on:\n * strips the \"Error 400:\" prefix, stack frames and process noise, and\n * extracts a 1-based line number when the message carries one.\n */\nexport function parseKrokiError(format: string, body: string): DiagramError {\n const lines = body\n .replace(/^\\s*Error \\d{3}:\\s*/, '')\n .split(/\\r?\\n/)\n .map((l) => l.trimEnd())\n .filter((l) => l.trim() !== '' && !/^\\s+at\\s/.test(l) && !/^\\(exit code \\d+\\)$/.test(l.trim()));\n\n // Kroki often repeats the first line verbatim after a JS stack trace; keep one copy.\n const unique = lines.filter((l, i) => lines.indexOf(l) === i);\n let message = unique.join('\\n').trim() || 'The diagram could not be rendered.';\n if (message.length > 2000) message = `${message.slice(0, 2000)}…`;\n\n for (const pattern of LINE_PATTERNS) {\n const match = pattern.exec(message);\n if (match?.[1]) {\n const raw = Number.parseInt(match[1], 10);\n const line = ZERO_BASED_LINES.has(format) ? raw + 1 : raw;\n return { line: Math.max(line, 1), message };\n }\n }\n return { message };\n}\n","import type { OutputFormat } from '../formats.js';\n\nexport interface DiagramError {\n line?: number;\n message: string;\n}\n\nexport interface Renderer {\n /**\n * Renders `source` in the given canonical format.\n * Rejects with `DiagramSyntaxError` when the diagram is invalid and with\n * `RenderServiceError` when the renderer itself fails.\n */\n render(format: string, source: string, output: OutputFormat): Promise<Buffer>;\n /** A URL that renders the diagram as SVG when opened in a browser, if the renderer has one. */\n shareUrl?(format: string, source: string): string;\n}\n\n/** The diagram source is invalid. The message comes from the parser. */\nexport class DiagramSyntaxError extends Error {\n override readonly name = 'DiagramSyntaxError';\n constructor(readonly errors: DiagramError[]) {\n super(errors.map(formatDiagramError).join('\\n'));\n }\n}\n\n/** The renderer could not be reached, timed out, or failed for reasons unrelated to the source. */\nexport class RenderServiceError extends Error {\n override readonly name = 'RenderServiceError';\n}\n\nexport function formatDiagramError(error: DiagramError): string {\n return error.line === undefined ? error.message : `Line ${error.line}: ${error.message}`;\n}\n","import type { McpServer } from '@modelcontextprotocol/server';\nimport * as z from 'zod/v4';\nimport { FORMATS } from '../formats.js';\nimport { READ_ONLY_ANNOTATIONS, toolError } from './shared.js';\n\nconst formatEntry = z.object({\n id: z.string(),\n aliases: z.array(z.string()),\n outputs: z.array(z.enum(['svg', 'png'])),\n description: z.string(),\n docsUrl: z.string(),\n example: z.string(),\n});\n\nexport function registerListFormats(server: McpServer, unavailable?: string): void {\n server.registerTool(\n 'list_formats',\n {\n title: 'List diagram formats',\n description:\n 'List the diagram languages this server can render (Mermaid, PlantUML, C4-PlantUML, Graphviz, D2, Structurizr, BPMN, Excalidraw), ' +\n \"with each one's aliases, supported outputs (svg, png), syntax docs link and a minimal valid example. \" +\n 'Call this before writing a diagram in a format you are unsure of, or to check whether PNG output is available.',\n outputSchema: z.object({ formats: z.array(formatEntry) }),\n annotations: READ_ONLY_ANNOTATIONS,\n },\n async () => {\n if (unavailable) return toolError(unavailable);\n const formats = FORMATS.map((f) => ({ ...f, aliases: [...f.aliases], outputs: [...f.outputs] }));\n return {\n content: [{ type: 'text', text: JSON.stringify({ formats }, null, 2) }],\n structuredContent: { formats },\n };\n },\n );\n}\n","export type OutputFormat = 'svg' | 'png';\n\nexport interface DiagramFormat {\n /** Canonical id, also the Kroki diagram type. */\n id: string;\n aliases: readonly string[];\n /** Outputs Kroki can produce for this format. */\n outputs: readonly OutputFormat[];\n description: string;\n docsUrl: string;\n /** A minimal diagram that renders without errors. */\n example: string;\n}\n\n// Output support checked against https://kroki.io/#support.\nexport const FORMATS = [\n {\n id: 'mermaid',\n aliases: [],\n outputs: ['svg', 'png'],\n description: 'Flowcharts, sequence, class, state, ER, Gantt and other diagrams in Markdown-like syntax.',\n docsUrl: 'https://mermaid.js.org/intro/syntax-reference.html',\n example: 'flowchart LR\\n A[Client] --> B[API]\\n B --> C[(Database)]\\n',\n },\n {\n id: 'plantuml',\n aliases: [],\n outputs: ['svg', 'png'],\n description: 'UML diagrams (sequence, class, component, activity, state, deployment) and more.',\n docsUrl: 'https://plantuml.com/',\n example: '@startuml\\nAlice -> Bob: Request\\nBob --> Alice: Response\\n@enduml\\n',\n },\n {\n id: 'c4plantuml',\n aliases: [],\n outputs: ['svg', 'png'],\n description: 'C4 model architecture diagrams (context, container, component) on top of PlantUML.',\n docsUrl: 'https://github.com/plantuml-stdlib/C4-PlantUML',\n example:\n '@startuml\\n!include <C4/C4_Context>\\nPerson(user, \"User\")\\nSystem(app, \"Web App\")\\nRel(user, app, \"Uses\")\\n@enduml\\n',\n },\n {\n id: 'graphviz',\n aliases: ['dot'],\n outputs: ['svg', 'png'],\n description: 'Graphs and networks in the Graphviz DOT language.',\n docsUrl: 'https://graphviz.org/doc/info/lang.html',\n example: 'digraph G {\\n rankdir=LR;\\n client -> api -> db;\\n}\\n',\n },\n {\n id: 'd2',\n aliases: [],\n outputs: ['svg'],\n description: 'Declarative diagrams with automatic layout, well suited to software architecture.',\n docsUrl: 'https://d2lang.com/tour/intro',\n example: 'client -> api: HTTPS\\napi -> db: SQL\\ndb: {shape: cylinder}\\n',\n },\n {\n id: 'structurizr',\n aliases: [],\n outputs: ['svg', 'png'],\n description: 'C4 model workspaces in the Structurizr DSL (model plus views).',\n docsUrl: 'https://docs.structurizr.com/dsl/language',\n example:\n 'workspace {\\n model {\\n user = person \"User\"\\n app = softwareSystem \"App\"\\n user -> app \"Uses\"\\n }\\n views {\\n systemContext app {\\n include *\\n autolayout lr\\n }\\n }\\n}\\n',\n },\n {\n id: 'bpmn',\n aliases: [],\n outputs: ['svg'],\n description: 'Business process diagrams in BPMN 2.0 XML (including diagram interchange coordinates).',\n docsUrl: 'https://www.omg.org/spec/BPMN/2.0/',\n example:\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<bpmn:definitions xmlns:bpmn=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\" xmlns:dc=\"http://www.omg.org/spec/DD/20100524/DC\" xmlns:di=\"http://www.omg.org/spec/DD/20100524/DI\" id=\"defs\" targetNamespace=\"http://bpmn.io/schema/bpmn\">\\n <bpmn:process id=\"process\" isExecutable=\"false\">\\n <bpmn:startEvent id=\"start\"><bpmn:outgoing>flow</bpmn:outgoing></bpmn:startEvent>\\n <bpmn:endEvent id=\"end\"><bpmn:incoming>flow</bpmn:incoming></bpmn:endEvent>\\n <bpmn:sequenceFlow id=\"flow\" sourceRef=\"start\" targetRef=\"end\" />\\n </bpmn:process>\\n <bpmndi:BPMNDiagram id=\"diagram\">\\n <bpmndi:BPMNPlane id=\"plane\" bpmnElement=\"process\">\\n <bpmndi:BPMNShape id=\"start_di\" bpmnElement=\"start\"><dc:Bounds x=\"100\" y=\"100\" width=\"36\" height=\"36\" /></bpmndi:BPMNShape>\\n <bpmndi:BPMNShape id=\"end_di\" bpmnElement=\"end\"><dc:Bounds x=\"250\" y=\"100\" width=\"36\" height=\"36\" /></bpmndi:BPMNShape>\\n <bpmndi:BPMNEdge id=\"flow_di\" bpmnElement=\"flow\"><di:waypoint x=\"136\" y=\"118\" /><di:waypoint x=\"250\" y=\"118\" /></bpmndi:BPMNEdge>\\n </bpmndi:BPMNPlane>\\n </bpmndi:BPMNDiagram>\\n</bpmn:definitions>\\n',\n },\n {\n id: 'excalidraw',\n aliases: [],\n outputs: ['svg'],\n description: 'Hand-drawn style sketches from an Excalidraw JSON scene.',\n docsUrl: 'https://docs.excalidraw.com/docs/codebase/json-schema',\n example:\n '{\\n \"type\": \"excalidraw\",\\n \"version\": 2,\\n \"source\": \"https://excalidraw.com\",\\n \"elements\": [\\n {\\n \"id\": \"box\",\\n \"type\": \"rectangle\",\\n \"x\": 0,\\n \"y\": 0,\\n \"width\": 160,\\n \"height\": 80,\\n \"angle\": 0,\\n \"strokeColor\": \"#1e1e1e\",\\n \"backgroundColor\": \"transparent\",\\n \"fillStyle\": \"solid\",\\n \"strokeWidth\": 2,\\n \"strokeStyle\": \"solid\",\\n \"roughness\": 1,\\n \"opacity\": 100,\\n \"groupIds\": [],\\n \"seed\": 1,\\n \"version\": 1,\\n \"versionNonce\": 1,\\n \"isDeleted\": false,\\n \"boundElements\": null,\\n \"updated\": 1,\\n \"link\": null,\\n \"locked\": false\\n }\\n ],\\n \"appState\": { \"viewBackgroundColor\": \"#ffffff\" },\\n \"files\": {}\\n}\\n',\n },\n] as const satisfies readonly DiagramFormat[];\n\nexport type FormatId = (typeof FORMATS)[number]['id'];\n\n/** Every accepted `format` value: canonical ids followed by aliases. */\nexport const FORMAT_NAMES = [...FORMATS.map((f) => f.id), ...FORMATS.flatMap((f) => f.aliases)] as [\n string,\n ...string[],\n];\n\nconst byName = new Map<string, DiagramFormat>();\nfor (const format of FORMATS) {\n byName.set(format.id, format);\n for (const alias of format.aliases) byName.set(alias, format);\n}\n\n/** Resolves an id or alias (case-insensitive) to its format, or undefined. */\nexport function resolveFormat(name: string): DiagramFormat | undefined {\n return byName.get(name.trim().toLowerCase());\n}\n","import type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/server';\nimport * as z from 'zod/v4';\nimport { FORMAT_NAMES } from '../formats.js';\n\nexport const MAX_SOURCE_BYTES = 100 * 1024;\n\n/** Every tool only reads input and calls an external renderer. */\nexport const READ_ONLY_ANNOTATIONS: ToolAnnotations = { readOnlyHint: true, openWorldHint: true };\n\nexport const formatSchema = z\n .enum(FORMAT_NAMES)\n .describe(`Diagram language. One of: ${FORMAT_NAMES.join(', ')} (\"dot\" is an alias for graphviz).`);\n\nexport const sourceSchema = z\n .string()\n .min(1, 'source is empty')\n .refine((s) => Buffer.byteLength(s, 'utf8') <= MAX_SOURCE_BYTES, {\n message: `source must be at most ${MAX_SOURCE_BYTES / 1024} KB`,\n })\n .describe('The diagram source text, exactly as it would appear in a file of that format.');\n\nexport function toolError(text: string): CallToolResult {\n return { content: [{ type: 'text', text }], isError: true };\n}\n","import { createHash } from 'node:crypto';\nimport type { CallToolResult, McpServer } from '@modelcontextprotocol/server';\nimport * as z from 'zod/v4';\nimport { resolveFormat } from '../formats.js';\nimport { DiagramSyntaxError, type Renderer, RenderServiceError } from '../renderers/renderer.js';\nimport { formatSchema, READ_ONLY_ANNOTATIONS, sourceSchema, toolError } from './shared.js';\n\nexport function registerRenderDiagram(server: McpServer, renderer: Renderer, unavailable?: string): void {\n server.registerTool(\n 'render_diagram',\n {\n title: 'Render diagram',\n description:\n 'Render diagram source (Mermaid, PlantUML, C4-PlantUML, Graphviz/DOT, D2, Structurizr, BPMN, Excalidraw) to an SVG or PNG image, ' +\n 'and get a shareable link that opens the diagram in a browser. Use it when the user wants to see a diagram. ' +\n 'If the source is invalid the result is an error with the parser message and line number: fix the source and retry. ' +\n 'Call validate_diagram to check syntax without rendering, or list_formats for examples and which formats support PNG.',\n inputSchema: z.object({\n format: formatSchema,\n source: sourceSchema,\n output: z\n .enum(['svg', 'png'])\n .default('svg')\n .describe('Image type. Defaults to svg. d2, bpmn and excalidraw support svg only.'),\n }),\n annotations: READ_ONLY_ANNOTATIONS,\n },\n async ({ format: name, source, output }): Promise<CallToolResult> => {\n if (unavailable) return toolError(unavailable);\n const format = resolveFormat(name);\n if (!format) return toolError(`Unknown format \"${name}\". Call list_formats to see the supported formats.`);\n if (!format.outputs.includes(output)) {\n return toolError(\n `${format.id} does not support ${output} output. Supported: ${format.outputs.join(', ')}. Retry with output \"svg\".`,\n );\n }\n\n let image: Buffer;\n try {\n image = await renderer.render(format.id, source, output);\n } catch (error) {\n if (error instanceof DiagramSyntaxError) {\n return toolError(\n `The ${format.id} diagram is invalid:\\n${error.message}\\n\\nFix the source and call render_diagram again. ` +\n `list_formats has a valid ${format.id} example.`,\n );\n }\n if (error instanceof RenderServiceError) return toolError(error.message);\n throw error;\n }\n\n const url = renderer.shareUrl?.(format.id, source);\n const content: CallToolResult['content'] = [];\n if (output === 'png') {\n content.push({ type: 'image', data: image.toString('base64'), mimeType: 'image/png' });\n } else {\n const hash = createHash('sha256').update(source).digest('hex').slice(0, 16);\n content.push({\n type: 'resource',\n resource: {\n uri: `archform://diagram/${format.id}/${hash}.svg`,\n mimeType: 'image/svg+xml',\n text: image.toString('utf8'),\n },\n });\n }\n content.push({\n type: 'text',\n text: url\n ? `Rendered ${format.id} diagram as ${output}. Shareable link (opens the SVG in a browser): ${url}`\n : `Rendered ${format.id} diagram as ${output}.`,\n });\n return { content };\n },\n );\n}\n","import type { CallToolResult, McpServer } from '@modelcontextprotocol/server';\nimport * as z from 'zod/v4';\nimport { resolveFormat } from '../formats.js';\nimport { type DiagramError, DiagramSyntaxError, type Renderer, RenderServiceError } from '../renderers/renderer.js';\nimport { formatSchema, READ_ONLY_ANNOTATIONS, sourceSchema, toolError } from './shared.js';\n\nconst outputSchema = z.object({\n valid: z.boolean(),\n errors: z.array(z.object({ line: z.number().int().optional(), message: z.string() })),\n});\n\nexport function registerValidateDiagram(server: McpServer, renderer: Renderer, unavailable?: string): void {\n server.registerTool(\n 'validate_diagram',\n {\n title: 'Validate diagram',\n description:\n 'Check whether diagram source (Mermaid, PlantUML, C4-PlantUML, Graphviz/DOT, D2, Structurizr, BPMN, Excalidraw) is valid, ' +\n 'without returning an image. Returns { valid, errors: [{ line?, message }] }. ' +\n 'Use it to check syntax you are unsure of before rendering or before handing diagram code to the user. ' +\n 'Call list_formats for a valid example of each format.',\n inputSchema: z.object({ format: formatSchema, source: sourceSchema }),\n outputSchema,\n annotations: READ_ONLY_ANNOTATIONS,\n },\n async ({ format: name, source }): Promise<CallToolResult> => {\n if (unavailable) return toolError(unavailable);\n const format = resolveFormat(name);\n if (!format) return toolError(`Unknown format \"${name}\". Call list_formats to see the supported formats.`);\n\n let errors: DiagramError[] = [];\n try {\n // Every format supports SVG, and rendering is the only check Kroki offers.\n await renderer.render(format.id, source, 'svg');\n } catch (error) {\n if (error instanceof DiagramSyntaxError) errors = error.errors;\n else if (error instanceof RenderServiceError) {\n return toolError(`Could not validate the diagram: ${error.message}`);\n } else throw error;\n }\n\n const result = { valid: errors.length === 0, errors };\n return {\n content: [{ type: 'text', text: JSON.stringify(result) }],\n structuredContent: result,\n };\n },\n );\n}\n","{\n \"name\": \"@archform/mcp\",\n \"version\": \"0.2.1\",\n \"description\": \"MCP server that renders and validates diagrams as code: Mermaid, PlantUML, C4, Graphviz, D2, Structurizr, BPMN and Excalidraw.\",\n \"mcpName\": \"io.github.himanshu-mamgain/archform\",\n \"license\": \"MIT\",\n \"author\": \"Himanshu Mamgain\",\n \"homepage\": \"https://github.com/himanshu-mamgain/archform/tree/main/packages/mcp#readme\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/himanshu-mamgain/archform.git\",\n \"directory\": \"packages/mcp\"\n },\n \"bugs\": \"https://github.com/himanshu-mamgain/archform/issues\",\n \"type\": \"module\",\n \"bin\": {\n \"archform-mcp\": \"dist/index.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"engines\": {\n \"node\": \">=22\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"keywords\": [\n \"mcp\",\n \"model-context-protocol\",\n \"diagrams-as-code\",\n \"mermaid\",\n \"plantuml\",\n \"graphviz\",\n \"d2\",\n \"c4\",\n \"architecture\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node dist/index.js\",\n \"typecheck\": \"tsc -p tsconfig.json\",\n \"test\": \"vitest run\",\n \"test:live\": \"cross-env KROKI_LIVE=1 vitest run test/live.test.ts\",\n \"prepublishOnly\": \"tsup\"\n },\n \"devDependencies\": {\n \"@modelcontextprotocol/client\": \"2.3.1\",\n \"@types/node\": \"22.20.5\",\n \"cross-env\": \"^10.1.0\",\n \"tsup\": \"8.5.1\",\n \"vitest\": \"4.1.12\"\n },\n \"dependencies\": {\n \"@hono/node-server\": \"2.1.5\",\n \"@modelcontextprotocol/hono\": \"2.0.2\",\n \"@modelcontextprotocol/server\": \"2.3.1\",\n \"hono\": \"4.13.13\",\n \"zod\": \"4.6.7\"\n }\n}\n","import pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n"],"mappings":"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","names":["z","z","z"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archform/mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "MCP server that renders and validates diagrams as code: Mermaid, PlantUML, C4, Graphviz, D2, Structurizr, BPMN and Excalidraw.",
|
|
5
5
|
"mcpName": "io.github.himanshu-mamgain/archform",
|
|
6
6
|
"license": "MIT",
|