agentic-mermaid 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +119 -0
- package/dist/agent-core.d.ts +2 -2
- package/dist/agent-core.js +2 -2
- package/dist/agent.d.ts +2 -2
- package/dist/agent.js +4 -4
- package/dist/agentic-mermaid-mcp.js +5 -5
- package/dist/am.js +6 -6
- package/dist/browser-lazy/chunks/{architecture-3CGEU363.js → architecture-NUJTDJJ7.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-VDTSULBU.js → chunk-2W3BS3M3.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-HZ4ADQMK.js → chunk-4RLZTVJP.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-CTVVJPMX.js → chunk-4ZURJQXD.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-RVO6J7S3.js → chunk-55F5M5XA.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-MMWVZP6M.js → chunk-5QI4P3EJ.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-DPSSG7LR.js → chunk-6LBHOBXT.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-JX5ZZY3T.js → chunk-ASPYKC6M.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-FXBBGAMJ.js → chunk-DQHK4JCG.js} +2 -2
- package/dist/browser-lazy/chunks/{chunk-6UT2VTFI.js → chunk-EKHEVTE2.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-4IUBLBJJ.js → chunk-HO5GDIPN.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-4U2TVB2S.js → chunk-N2K2BPQT.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-2U4EUPSI.js → chunk-OLQF6DTZ.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-7D24ZLOV.js → chunk-TCPK32QH.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-KTTWXSIX.js → chunk-UO3RFEMI.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-PXRUFH7U.js → chunk-ZMPTJUXS.js} +1 -1
- package/dist/browser-lazy/chunks/{class-B3AVLSIF.js → class-ABIOBAFL.js} +1 -1
- package/dist/browser-lazy/chunks/{er-AF4BKKKC.js → er-CX3NGEC2.js} +1 -1
- package/dist/browser-lazy/chunks/{flowchart-BE6BQZLZ.js → flowchart-ML54IJ7F.js} +1 -1
- package/dist/browser-lazy/chunks/{gantt-U4BMMSEL.js → gantt-ZMOMOTF6.js} +1 -1
- package/dist/browser-lazy/chunks/{gitgraph-UPSWNFRP.js → gitgraph-OBTCRWCA.js} +1 -1
- package/dist/browser-lazy/chunks/{journey-PDZ7FDJR.js → journey-RJBHWLRG.js} +1 -1
- package/dist/browser-lazy/chunks/{mindmap-BFEUURLS.js → mindmap-YXHLCHOO.js} +1 -1
- package/dist/browser-lazy/chunks/{pie-ZFYW5JZO.js → pie-TN2QOLSL.js} +1 -1
- package/dist/browser-lazy/chunks/{quadrant-XJVEYYXK.js → quadrant-QE5JZ4H2.js} +1 -1
- package/dist/browser-lazy/chunks/{radar-OVXFNRNC.js → radar-ZPV6O27I.js} +1 -1
- package/dist/browser-lazy/chunks/{render-core-R4ZGGEFS.js → render-core-VPMAH3HT.js} +2 -2
- package/dist/browser-lazy/chunks/{sequence-AWY6JCSB.js → sequence-4AERT525.js} +1 -1
- package/dist/browser-lazy/chunks/{state-TCE4DXKR.js → state-67TZ7USF.js} +1 -1
- package/dist/browser-lazy/chunks/{timeline-W4ZVT3FF.js → timeline-AHOAT7ZL.js} +1 -1
- package/dist/browser-lazy/chunks/{xychart-K3QQACKS.js → xychart-YUY3CPT2.js} +1 -1
- package/dist/browser-lazy/index.js +1 -1
- package/dist/browser.global.js +6 -6
- package/dist/{chunk-BSVM3VMO.js → chunk-M3VTTD3Q.js} +2 -2
- package/dist/{chunk-XVY324NY.js → chunk-PG4PTPJL.js} +10 -8
- package/dist/{chunk-KQMJ37OY.js → chunk-QWCD67AZ.js} +4 -4
- package/dist/{chunk-JJ7JRYZV.js → chunk-SDJYD4OP.js} +2 -2
- package/dist/{chunk-MBIR6S3B.js → chunk-UKLKEZBJ.js} +2 -2
- package/dist/{chunk-FGO3J7RA.js → chunk-WMFFVZ4Q.js} +677 -48
- package/dist/{index-Do8z5rPq.d.ts → index-BoqfBufn.d.ts} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp-cli-YOAFIAVD.js +17 -0
- package/docs/mcp-http-transport.md +2 -0
- package/docs/style-authoring.md +1 -1
- package/package.json +16 -4
- package/server.json +2 -2
- package/skills/agentic-mermaid-diagram-workflow/SKILL.md +64 -1
- package/skills/agentic-mermaid-diagram-workflow/references/hosted-mcp.md +110 -0
- package/dist/mcp-cli-7FLGTEG3.js +0 -17
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
projectKnownRenderErrorDiagnostic,
|
|
3
3
|
projectRenderErrorDiagnostic
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SDJYD4OP.js";
|
|
5
5
|
import {
|
|
6
6
|
renderMermaidPNG,
|
|
7
7
|
renderMermaidPNGWithReceipt
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-UKLKEZBJ.js";
|
|
9
9
|
import {
|
|
10
10
|
analyzeMermaid,
|
|
11
11
|
analyzeMermaidSource,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
renderMermaidSVGWithReceipt,
|
|
32
32
|
verifyMermaid,
|
|
33
33
|
verifySummary
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-M3VTTD3Q.js";
|
|
35
35
|
import {
|
|
36
36
|
BUILTIN_FAMILY_METADATA,
|
|
37
37
|
PACKAGE_VERSION,
|
|
@@ -71,13 +71,11 @@ import {
|
|
|
71
71
|
styleSpecTypeScriptDeclaration,
|
|
72
72
|
validateJsonConfigAdmission,
|
|
73
73
|
validateSerializableRenderOptions
|
|
74
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-PG4PTPJL.js";
|
|
75
75
|
|
|
76
76
|
// src/mcp/server.ts
|
|
77
77
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
78
78
|
import { createServer } from "http";
|
|
79
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
80
|
-
import { join as join2 } from "path";
|
|
81
79
|
import { URL } from "url";
|
|
82
80
|
|
|
83
81
|
// src/mcp/sandbox.ts
|
|
@@ -1214,7 +1212,7 @@ function preserveExactJsonRpcIds(body) {
|
|
|
1214
1212
|
cursor++;
|
|
1215
1213
|
while (/\s/.test(body[cursor] ?? "")) cursor++;
|
|
1216
1214
|
const number = body.slice(cursor).match(/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/)?.[0];
|
|
1217
|
-
if (number && !isSafelyRoundTrippableInteger(number)) {
|
|
1215
|
+
if (number && isIntegerNumberToken(number) && !isSafelyRoundTrippableInteger(number)) {
|
|
1218
1216
|
let sentinel = `__agentic_mermaid_exact_id_${replacements.length}__`;
|
|
1219
1217
|
while (body.includes(sentinel)) sentinel += "_";
|
|
1220
1218
|
replacements.push({ sentinel, raw: number, start: cursor, end: cursor + number.length });
|
|
@@ -1233,6 +1231,10 @@ function preserveExactJsonRpcIds(body) {
|
|
|
1233
1231
|
}
|
|
1234
1232
|
return { body: protectedBody, ids: replacements.map(({ sentinel, raw }) => ({ sentinel, raw })) };
|
|
1235
1233
|
}
|
|
1234
|
+
function isIntegerNumberToken(token) {
|
|
1235
|
+
const value = Number(token);
|
|
1236
|
+
return Number.isFinite(value) && Number.isInteger(value);
|
|
1237
|
+
}
|
|
1236
1238
|
function isSafelyRoundTrippableInteger(token) {
|
|
1237
1239
|
if (!/^-?(?:0|[1-9]\d*)$/.test(token)) return false;
|
|
1238
1240
|
if (token === "-0") return false;
|
|
@@ -1259,8 +1261,335 @@ function stringifyJsonRpc(payload, exactIds = []) {
|
|
|
1259
1261
|
function reply(id, result) {
|
|
1260
1262
|
return { jsonrpc: "2.0", id, result };
|
|
1261
1263
|
}
|
|
1262
|
-
function rpcError(id, code, message) {
|
|
1263
|
-
return { jsonrpc: "2.0", id, error: { code, message } };
|
|
1264
|
+
function rpcError(id, code, message, data) {
|
|
1265
|
+
return { jsonrpc: "2.0", id, error: data === void 0 ? { code, message } : { code, message, data } };
|
|
1266
|
+
}
|
|
1267
|
+
function toolResult(id, payload, isError) {
|
|
1268
|
+
return reply(id, { content: [{ type: "text", text: JSON.stringify(payload) }], isError });
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// src/mcp/protocol-versions.ts
|
|
1272
|
+
var LEGACY_PROTOCOL_VERSIONS = ["2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25"];
|
|
1273
|
+
var MODERN_PROTOCOL_VERSIONS = ["2026-07-28"];
|
|
1274
|
+
var META_PROTOCOL_VERSION = "io.modelcontextprotocol/protocolVersion";
|
|
1275
|
+
var META_CLIENT_INFO = "io.modelcontextprotocol/clientInfo";
|
|
1276
|
+
var META_CLIENT_CAPABILITIES = "io.modelcontextprotocol/clientCapabilities";
|
|
1277
|
+
var META_LOG_LEVEL = "io.modelcontextprotocol/logLevel";
|
|
1278
|
+
var META_SERVER_INFO = "io.modelcontextprotocol/serverInfo";
|
|
1279
|
+
var HEADER_MISMATCH = -32020;
|
|
1280
|
+
var UNSUPPORTED_PROTOCOL_VERSION = -32022;
|
|
1281
|
+
var TOOL_ERROR_VALIDATION_SINCE = "2025-11-25";
|
|
1282
|
+
function isModernProtocolVersion(version) {
|
|
1283
|
+
return typeof version === "string" && MODERN_PROTOCOL_VERSIONS.includes(version);
|
|
1284
|
+
}
|
|
1285
|
+
function isLegacyProtocolVersion(version) {
|
|
1286
|
+
return typeof version === "string" && LEGACY_PROTOCOL_VERSIONS.includes(version);
|
|
1287
|
+
}
|
|
1288
|
+
function usesToolErrorForInvalidArguments(version) {
|
|
1289
|
+
return typeof version === "string" && version >= TOOL_ERROR_VALIDATION_SINCE;
|
|
1290
|
+
}
|
|
1291
|
+
function paramsMeta(req) {
|
|
1292
|
+
const params = req.params;
|
|
1293
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) return void 0;
|
|
1294
|
+
const meta = params._meta;
|
|
1295
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return void 0;
|
|
1296
|
+
return meta;
|
|
1297
|
+
}
|
|
1298
|
+
function requestMetaProtocolVersion(req) {
|
|
1299
|
+
const claim = requestMetaProtocolVersionClaim(req);
|
|
1300
|
+
const value = claim.present ? claim.value : void 0;
|
|
1301
|
+
return typeof value === "string" ? value : void 0;
|
|
1302
|
+
}
|
|
1303
|
+
function requestMetaProtocolVersionClaim(req) {
|
|
1304
|
+
const meta = paramsMeta(req);
|
|
1305
|
+
if (!meta || !Object.prototype.hasOwnProperty.call(meta, META_PROTOCOL_VERSION)) return { present: false };
|
|
1306
|
+
return { present: true, value: meta[META_PROTOCOL_VERSION] };
|
|
1307
|
+
}
|
|
1308
|
+
function mcpImplementationProblems(value, path) {
|
|
1309
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1310
|
+
return [`${path} is required and must be an object with name and version`];
|
|
1311
|
+
}
|
|
1312
|
+
const problems = [];
|
|
1313
|
+
const info = value;
|
|
1314
|
+
if (typeof info.name !== "string") problems.push(`${path}.name is required and must be a string`);
|
|
1315
|
+
if (typeof info.version !== "string") problems.push(`${path}.version is required and must be a string`);
|
|
1316
|
+
for (const key of ["title", "description", "websiteUrl"]) {
|
|
1317
|
+
if (info[key] !== void 0 && typeof info[key] !== "string") {
|
|
1318
|
+
problems.push(`${path}.${key} must be a string when present`);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
if (info.icons !== void 0) {
|
|
1322
|
+
if (!Array.isArray(info.icons)) {
|
|
1323
|
+
problems.push(`${path}.icons must be an array when present`);
|
|
1324
|
+
} else {
|
|
1325
|
+
for (const [index, icon] of info.icons.entries()) {
|
|
1326
|
+
if (!icon || typeof icon !== "object" || Array.isArray(icon)) {
|
|
1327
|
+
problems.push(`${path}.icons[${index}] must be an object`);
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1330
|
+
const record = icon;
|
|
1331
|
+
if (typeof record.src !== "string") problems.push(`${path}.icons[${index}].src is required and must be a string`);
|
|
1332
|
+
if (record.mimeType !== void 0 && typeof record.mimeType !== "string") problems.push(`${path}.icons[${index}].mimeType must be a string when present`);
|
|
1333
|
+
if (record.sizes !== void 0 && (!Array.isArray(record.sizes) || record.sizes.some((size) => typeof size !== "string"))) {
|
|
1334
|
+
problems.push(`${path}.icons[${index}].sizes must be an array of strings when present`);
|
|
1335
|
+
}
|
|
1336
|
+
if (record.theme !== void 0 && record.theme !== "light" && record.theme !== "dark") {
|
|
1337
|
+
problems.push(`${path}.icons[${index}].theme must be light or dark when present`);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
return problems;
|
|
1343
|
+
}
|
|
1344
|
+
function mcpClientCapabilitiesProblems(value, path) {
|
|
1345
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1346
|
+
return [`${path} is required and must be an object (an empty object declares no optional capabilities)`];
|
|
1347
|
+
}
|
|
1348
|
+
const problems = [];
|
|
1349
|
+
const record = value;
|
|
1350
|
+
const objectMember = (key) => {
|
|
1351
|
+
const member = record[key];
|
|
1352
|
+
if (member === void 0) return void 0;
|
|
1353
|
+
if (typeof member !== "object" || member === null || Array.isArray(member)) {
|
|
1354
|
+
problems.push(`${path}.${key} must be an object when present`);
|
|
1355
|
+
return void 0;
|
|
1356
|
+
}
|
|
1357
|
+
return member;
|
|
1358
|
+
};
|
|
1359
|
+
const roots = objectMember("roots");
|
|
1360
|
+
if (roots?.listChanged !== void 0 && typeof roots.listChanged !== "boolean") {
|
|
1361
|
+
problems.push(`${path}.roots.listChanged must be a boolean when present`);
|
|
1362
|
+
}
|
|
1363
|
+
const sampling = objectMember("sampling");
|
|
1364
|
+
for (const key of ["context", "tools"]) {
|
|
1365
|
+
const member = sampling?.[key];
|
|
1366
|
+
if (member !== void 0 && (typeof member !== "object" || member === null || Array.isArray(member))) {
|
|
1367
|
+
problems.push(`${path}.sampling.${key} must be an object when present`);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
const elicitation = objectMember("elicitation");
|
|
1371
|
+
for (const key of ["form", "url"]) {
|
|
1372
|
+
const member = elicitation?.[key];
|
|
1373
|
+
if (member !== void 0 && (typeof member !== "object" || member === null || Array.isArray(member))) {
|
|
1374
|
+
problems.push(`${path}.elicitation.${key} must be an object when present`);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
for (const key of ["experimental", "extensions"]) {
|
|
1378
|
+
const entries = objectMember(key);
|
|
1379
|
+
if (!entries) continue;
|
|
1380
|
+
for (const [name, member] of Object.entries(entries)) {
|
|
1381
|
+
if (key === "extensions" && !/^[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?\/.+/.test(name)) {
|
|
1382
|
+
problems.push(`${path}.extensions.${name} must use a namespaced key with a prefix`);
|
|
1383
|
+
}
|
|
1384
|
+
if (typeof member !== "object" || member === null || Array.isArray(member)) {
|
|
1385
|
+
problems.push(`${path}.${key}.${name} must be an object`);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
return problems;
|
|
1390
|
+
}
|
|
1391
|
+
function modernRequestMetaProblems(req) {
|
|
1392
|
+
const meta = paramsMeta(req);
|
|
1393
|
+
if (!meta) return [`params._meta is required and must carry ${META_PROTOCOL_VERSION} and ${META_CLIENT_CAPABILITIES}`];
|
|
1394
|
+
const problems = [];
|
|
1395
|
+
const clientInfo = meta[META_CLIENT_INFO];
|
|
1396
|
+
if (clientInfo !== void 0) {
|
|
1397
|
+
problems.push(...mcpImplementationProblems(clientInfo, `params._meta.${META_CLIENT_INFO}`));
|
|
1398
|
+
}
|
|
1399
|
+
const logLevel = meta[META_LOG_LEVEL];
|
|
1400
|
+
if (logLevel !== void 0 && (typeof logLevel !== "string" || !["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"].includes(logLevel))) {
|
|
1401
|
+
problems.push(`params._meta.${META_LOG_LEVEL} must be a valid logging level when present`);
|
|
1402
|
+
}
|
|
1403
|
+
const capabilities = meta[META_CLIENT_CAPABILITIES];
|
|
1404
|
+
problems.push(...mcpClientCapabilitiesProblems(capabilities, `params._meta.${META_CLIENT_CAPABILITIES}`));
|
|
1405
|
+
return problems;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// src/mcp/admission.ts
|
|
1409
|
+
var admittedMcpMessage = /* @__PURE__ */ Symbol("AdmittedMcpMessage");
|
|
1410
|
+
function responseId(raw, validId) {
|
|
1411
|
+
const value = raw.id;
|
|
1412
|
+
return validId && value !== void 0 ? value : null;
|
|
1413
|
+
}
|
|
1414
|
+
function rejection(kind, notification, response, protocol, requestedEra) {
|
|
1415
|
+
return {
|
|
1416
|
+
ok: false,
|
|
1417
|
+
kind,
|
|
1418
|
+
response: notification ? null : response,
|
|
1419
|
+
...protocol ? { protocol } : {},
|
|
1420
|
+
...requestedEra ? { requestedEra } : {}
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
function acceptance(request, id, notification, protocol) {
|
|
1424
|
+
const base = { request, id, protocol, [admittedMcpMessage]: true };
|
|
1425
|
+
return notification ? { ok: true, message: { ...base, notification: true } } : { ok: true, message: { ...base, notification: false } };
|
|
1426
|
+
}
|
|
1427
|
+
function admitMcpMessage(rawMessage, options) {
|
|
1428
|
+
const raw = rawMessage;
|
|
1429
|
+
const hasId = Boolean(raw && Object.prototype.hasOwnProperty.call(raw, "id"));
|
|
1430
|
+
const rawId = raw?.id;
|
|
1431
|
+
const validId = rawId === void 0 || typeof rawId === "string" || typeof rawId === "number" && Number.isSafeInteger(rawId);
|
|
1432
|
+
const validEnvelope = Boolean(raw && !Array.isArray(raw) && raw.jsonrpc === "2.0" && typeof raw.method === "string" && validId);
|
|
1433
|
+
if (!validEnvelope) {
|
|
1434
|
+
return { ok: false, kind: "invalid-request", response: rpcError(null, -32600, "invalid JSON-RPC request") };
|
|
1435
|
+
}
|
|
1436
|
+
const request = rawMessage;
|
|
1437
|
+
const id = responseId(raw, validId);
|
|
1438
|
+
const notification = !hasId;
|
|
1439
|
+
const versionClaim = requestMetaProtocolVersionClaim(request);
|
|
1440
|
+
const declaredVersion = requestMetaProtocolVersion(request);
|
|
1441
|
+
const requestedEra = versionClaim.present ? "modern" : "legacy";
|
|
1442
|
+
const supportedVersions = options.protocolEra === "legacy" ? options.supportedVersions.filter(isLegacyProtocolVersion) : options.protocolEra === "modern" ? options.supportedVersions.filter(isModernProtocolVersion) : [...options.supportedVersions];
|
|
1443
|
+
const effectiveTransportVersion = options.negotiatedVersion ?? options.headerVersion ?? void 0;
|
|
1444
|
+
const provisionalProtocol = isModernProtocolVersion(declaredVersion) ? { era: "modern", version: declaredVersion, supportedVersions } : { era: "legacy", version: declaredVersion ?? effectiveTransportVersion, supportedVersions };
|
|
1445
|
+
if (versionClaim.present && typeof versionClaim.value !== "string") {
|
|
1446
|
+
return rejection(
|
|
1447
|
+
"invalid-modern-metadata",
|
|
1448
|
+
notification,
|
|
1449
|
+
rpcError(id, -32602, `Invalid params: params._meta.${META_PROTOCOL_VERSION} is required and must be a string`),
|
|
1450
|
+
void 0,
|
|
1451
|
+
"modern"
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
if (options.headerVersion != null && !supportedVersions.includes(options.headerVersion)) {
|
|
1455
|
+
return rejection(
|
|
1456
|
+
"unsupported-version",
|
|
1457
|
+
notification,
|
|
1458
|
+
rpcError(
|
|
1459
|
+
id,
|
|
1460
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
1461
|
+
`Unsupported protocol version: ${options.headerVersion}`,
|
|
1462
|
+
{ supported: [...supportedVersions], requested: options.headerVersion }
|
|
1463
|
+
),
|
|
1464
|
+
provisionalProtocol,
|
|
1465
|
+
requestedEra
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
if (options.negotiatedVersion != null && !supportedVersions.includes(options.negotiatedVersion)) {
|
|
1469
|
+
return rejection(
|
|
1470
|
+
"unsupported-version",
|
|
1471
|
+
notification,
|
|
1472
|
+
rpcError(
|
|
1473
|
+
id,
|
|
1474
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
1475
|
+
`Unsupported protocol version: ${options.negotiatedVersion}`,
|
|
1476
|
+
{ supported: [...supportedVersions], requested: options.negotiatedVersion }
|
|
1477
|
+
),
|
|
1478
|
+
provisionalProtocol,
|
|
1479
|
+
requestedEra
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1482
|
+
if (declaredVersion !== void 0 && options.headerVersion != null && declaredVersion !== options.headerVersion) {
|
|
1483
|
+
return rejection(
|
|
1484
|
+
"header-mismatch",
|
|
1485
|
+
notification,
|
|
1486
|
+
rpcError(
|
|
1487
|
+
id,
|
|
1488
|
+
HEADER_MISMATCH,
|
|
1489
|
+
`Header mismatch: MCP-Protocol-Version header value '${options.headerVersion}' does not match body value '${declaredVersion}'`
|
|
1490
|
+
),
|
|
1491
|
+
provisionalProtocol,
|
|
1492
|
+
requestedEra
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
if (options.protocolEra !== void 0 && requestedEra !== options.protocolEra) {
|
|
1496
|
+
const requested = declaredVersion ?? effectiveTransportVersion ?? "unversioned";
|
|
1497
|
+
return rejection(
|
|
1498
|
+
"unsupported-version",
|
|
1499
|
+
notification,
|
|
1500
|
+
rpcError(
|
|
1501
|
+
id,
|
|
1502
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
1503
|
+
`Unsupported protocol version: ${requested}`,
|
|
1504
|
+
{ supported: [...supportedVersions], requested }
|
|
1505
|
+
),
|
|
1506
|
+
provisionalProtocol,
|
|
1507
|
+
requestedEra
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
if (requestedEra === "modern" && isLegacyProtocolVersion(declaredVersion) && (!options.requireModernVersionHeader || options.headerVersion != null)) {
|
|
1511
|
+
const modernSupportedVersions = supportedVersions.filter(isModernProtocolVersion);
|
|
1512
|
+
return rejection(
|
|
1513
|
+
"unsupported-version",
|
|
1514
|
+
notification,
|
|
1515
|
+
rpcError(
|
|
1516
|
+
id,
|
|
1517
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
1518
|
+
`Unsupported protocol version: ${declaredVersion}`,
|
|
1519
|
+
{ supported: modernSupportedVersions, requested: declaredVersion }
|
|
1520
|
+
),
|
|
1521
|
+
provisionalProtocol,
|
|
1522
|
+
requestedEra
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
if (declaredVersion !== void 0 && !supportedVersions.includes(declaredVersion)) {
|
|
1526
|
+
return rejection(
|
|
1527
|
+
"unsupported-version",
|
|
1528
|
+
notification,
|
|
1529
|
+
rpcError(
|
|
1530
|
+
id,
|
|
1531
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
1532
|
+
`Unsupported protocol version: ${declaredVersion}`,
|
|
1533
|
+
{ supported: [...supportedVersions], requested: declaredVersion }
|
|
1534
|
+
),
|
|
1535
|
+
provisionalProtocol,
|
|
1536
|
+
requestedEra
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
const era = options.protocolEra ?? (isModernProtocolVersion(options.headerVersion) || isModernProtocolVersion(options.negotiatedVersion) ? "modern" : requestedEra);
|
|
1540
|
+
if (era === "modern") {
|
|
1541
|
+
if (!isModernProtocolVersion(declaredVersion)) {
|
|
1542
|
+
const problem = `params._meta.${META_PROTOCOL_VERSION} is required and must match the MCP-Protocol-Version header (${options.headerVersion})`;
|
|
1543
|
+
return rejection(
|
|
1544
|
+
"invalid-modern-metadata",
|
|
1545
|
+
notification,
|
|
1546
|
+
rpcError(id, -32602, `Invalid params: ${problem}`),
|
|
1547
|
+
provisionalProtocol,
|
|
1548
|
+
requestedEra
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1551
|
+
const protocol2 = { era: "modern", version: declaredVersion, supportedVersions };
|
|
1552
|
+
const metaProblems = modernRequestMetaProblems(request);
|
|
1553
|
+
if (metaProblems.length > 0) {
|
|
1554
|
+
return rejection(
|
|
1555
|
+
"invalid-modern-metadata",
|
|
1556
|
+
notification,
|
|
1557
|
+
rpcError(id, -32602, `Invalid params: ${metaProblems.join("; ")}`),
|
|
1558
|
+
protocol2,
|
|
1559
|
+
requestedEra
|
|
1560
|
+
);
|
|
1561
|
+
}
|
|
1562
|
+
if (options.requireModernVersionHeader && options.headerVersion == null) {
|
|
1563
|
+
return rejection(
|
|
1564
|
+
"header-mismatch",
|
|
1565
|
+
notification,
|
|
1566
|
+
rpcError(
|
|
1567
|
+
id,
|
|
1568
|
+
HEADER_MISMATCH,
|
|
1569
|
+
`Header mismatch: MCP-Protocol-Version header is required for protocol version ${declaredVersion}`
|
|
1570
|
+
),
|
|
1571
|
+
protocol2,
|
|
1572
|
+
requestedEra
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
const transportProblem = options.modernTransportProblem?.(request) ?? null;
|
|
1576
|
+
if (transportProblem !== null) {
|
|
1577
|
+
return rejection(
|
|
1578
|
+
"header-mismatch",
|
|
1579
|
+
notification,
|
|
1580
|
+
rpcError(id, HEADER_MISMATCH, `Header mismatch: ${transportProblem}`),
|
|
1581
|
+
protocol2,
|
|
1582
|
+
requestedEra
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
return acceptance(request, id, notification, protocol2);
|
|
1586
|
+
}
|
|
1587
|
+
const protocol = {
|
|
1588
|
+
era: "legacy",
|
|
1589
|
+
version: declaredVersion ?? effectiveTransportVersion,
|
|
1590
|
+
supportedVersions
|
|
1591
|
+
};
|
|
1592
|
+
return acceptance(request, id, notification, protocol);
|
|
1264
1593
|
}
|
|
1265
1594
|
|
|
1266
1595
|
// src/mcp/tool-surface.ts
|
|
@@ -1269,6 +1598,7 @@ function withClosedMcpInputSchema(tool) {
|
|
|
1269
1598
|
}
|
|
1270
1599
|
var MCP_SERVER_NAME = "agentic-mermaid-mcp";
|
|
1271
1600
|
var MCP_SERVER_VERSION = PACKAGE_VERSION;
|
|
1601
|
+
var SERVER_CAPABILITIES = { tools: {} };
|
|
1272
1602
|
var PURE_COMPUTE_ANNOTATIONS = {
|
|
1273
1603
|
readOnlyHint: true,
|
|
1274
1604
|
destructiveHint: false,
|
|
@@ -1476,32 +1806,66 @@ function validateMcpToolArguments(tool, value) {
|
|
|
1476
1806
|
const problems = validateSchemaValue(value, tool.inputSchema, tool.inputSchema, [], /* @__PURE__ */ new Set()).map((problem) => `${formatSchemaPath(problem.path)} ${problem.message}`);
|
|
1477
1807
|
return limitJsonConfigDiagnostics(problems, "arguments");
|
|
1478
1808
|
}
|
|
1479
|
-
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1809
|
+
function surfaceSupportedVersions(surface, options = {}) {
|
|
1810
|
+
const surfaceVersions = surface.supportedVersions ?? (typeof surface.protocolVersion === "string" ? [surface.protocolVersion] : []);
|
|
1811
|
+
if (!options.supportedVersions) return surfaceVersions;
|
|
1812
|
+
return surfaceVersions.filter((version) => options.supportedVersions.includes(version));
|
|
1813
|
+
}
|
|
1814
|
+
function discoverResult(surface, supportedVersions) {
|
|
1815
|
+
return {
|
|
1816
|
+
supportedVersions: [...supportedVersions],
|
|
1817
|
+
capabilities: SERVER_CAPABILITIES,
|
|
1818
|
+
instructions: surface.instructions
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
function initializeParamsProblems(params) {
|
|
1822
|
+
if (!plainJsonObject(params)) return ["params is required and must be an object"];
|
|
1823
|
+
const problems = [];
|
|
1824
|
+
if (typeof params.protocolVersion !== "string") {
|
|
1825
|
+
problems.push("params.protocolVersion is required and must be a string");
|
|
1826
|
+
}
|
|
1827
|
+
problems.push(...mcpClientCapabilitiesProblems(params.capabilities, "params.capabilities"));
|
|
1828
|
+
problems.push(...mcpImplementationProblems(params.clientInfo, "params.clientInfo"));
|
|
1829
|
+
return problems;
|
|
1830
|
+
}
|
|
1831
|
+
async function dispatchAdmittedMcpRequest(message, context, surface) {
|
|
1832
|
+
const { request: req, id, notification, protocol } = message;
|
|
1833
|
+
const { era, version, supportedVersions: servedVersions } = protocol;
|
|
1488
1834
|
let response;
|
|
1489
1835
|
switch (req.method) {
|
|
1836
|
+
// initialize / notifications/initialized / ping are REMOVED in the modern
|
|
1837
|
+
// era. Under a modern request they are simply unknown methods; a legacy
|
|
1838
|
+
// request keeps the handshake it depends on.
|
|
1490
1839
|
case "initialize": {
|
|
1491
|
-
|
|
1840
|
+
if (era === "modern") {
|
|
1841
|
+
response = unknownMethod(id, req.method);
|
|
1842
|
+
break;
|
|
1843
|
+
}
|
|
1844
|
+
const problems = initializeParamsProblems(req.params);
|
|
1845
|
+
if (problems.length > 0) {
|
|
1846
|
+
response = rpcError(id, -32602, `Invalid params: ${problems.join("; ")}`);
|
|
1847
|
+
break;
|
|
1848
|
+
}
|
|
1849
|
+
const offered = req.params?.protocolVersion;
|
|
1850
|
+
const legacyServedVersions = servedVersions.filter(isLegacyProtocolVersion);
|
|
1851
|
+
const fallbackLegacyVersion = legacyServedVersions.at(-1) ?? (typeof surface.protocolVersion === "function" ? surface.protocolVersion(req.params) : surface.protocolVersion);
|
|
1852
|
+
const protocolVersion = isLegacyProtocolVersion(offered) && legacyServedVersions.includes(offered) ? offered : fallbackLegacyVersion;
|
|
1492
1853
|
response = reply(id, {
|
|
1493
1854
|
protocolVersion,
|
|
1494
1855
|
serverInfo: { name: surface.serverName ?? MCP_SERVER_NAME, version: MCP_SERVER_VERSION },
|
|
1495
|
-
capabilities:
|
|
1856
|
+
capabilities: SERVER_CAPABILITIES,
|
|
1496
1857
|
instructions: surface.instructions
|
|
1497
1858
|
});
|
|
1498
1859
|
break;
|
|
1499
1860
|
}
|
|
1500
1861
|
case "notifications/initialized":
|
|
1501
|
-
response = null;
|
|
1862
|
+
response = era === "modern" ? unknownMethod(id, req.method) : null;
|
|
1502
1863
|
break;
|
|
1503
1864
|
case "ping":
|
|
1504
|
-
response = reply(id, {});
|
|
1865
|
+
response = era === "modern" ? unknownMethod(id, req.method) : reply(id, {});
|
|
1866
|
+
break;
|
|
1867
|
+
case "server/discover":
|
|
1868
|
+
response = era === "modern" ? reply(id, discoverResult(surface, servedVersions)) : unknownMethod(id, req.method);
|
|
1505
1869
|
break;
|
|
1506
1870
|
case "tools/list":
|
|
1507
1871
|
response = reply(id, { tools: surface.tools });
|
|
@@ -1524,22 +1888,39 @@ async function dispatchMcpRequest(req, context, surface) {
|
|
|
1524
1888
|
const args = req.params.arguments ?? {};
|
|
1525
1889
|
const problems = validateMcpToolArguments(tool, args);
|
|
1526
1890
|
if (problems.length > 0) {
|
|
1527
|
-
|
|
1891
|
+
const message2 = `Invalid arguments for ${name}: ${problems.join("; ")}`;
|
|
1892
|
+
response = usesToolErrorForInvalidArguments(version) ? toolResult(id, { ok: false, error: { code: "INVALID_ARGUMENTS", message: message2 } }, true) : rpcError(id, -32602, message2);
|
|
1528
1893
|
break;
|
|
1529
1894
|
}
|
|
1530
1895
|
response = await surface.handleToolCall(id, { ...req.params, arguments: args }, context);
|
|
1531
1896
|
break;
|
|
1532
1897
|
}
|
|
1533
|
-
case "prompts/list":
|
|
1534
|
-
response = reply(id, { prompts: [] });
|
|
1535
|
-
break;
|
|
1536
|
-
case "resources/list":
|
|
1537
|
-
response = reply(id, { resources: [] });
|
|
1538
|
-
break;
|
|
1539
1898
|
default:
|
|
1540
|
-
response =
|
|
1899
|
+
response = unknownMethod(id, req.method);
|
|
1900
|
+
}
|
|
1901
|
+
return notification ? null : decorateMcpResult(response, req.method, era, surface.serverName ?? MCP_SERVER_NAME);
|
|
1902
|
+
}
|
|
1903
|
+
var LIST_RESULT_TTL_MS = 3e5;
|
|
1904
|
+
var CACHEABLE_METHODS = /* @__PURE__ */ new Set(["server/discover", "tools/list"]);
|
|
1905
|
+
function decorateMcpResult(response, method, era, serverName) {
|
|
1906
|
+
if (era !== "modern" || !response || !plainJsonObject(response.result)) return response;
|
|
1907
|
+
const existingMeta = plainJsonObject(response.result._meta) ? response.result._meta : {};
|
|
1908
|
+
const result = {
|
|
1909
|
+
resultType: "complete",
|
|
1910
|
+
...response.result,
|
|
1911
|
+
_meta: {
|
|
1912
|
+
...existingMeta,
|
|
1913
|
+
[META_SERVER_INFO]: { name: serverName, version: MCP_SERVER_VERSION }
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
if (CACHEABLE_METHODS.has(method)) {
|
|
1917
|
+
result.ttlMs = LIST_RESULT_TTL_MS;
|
|
1918
|
+
result.cacheScope = "public";
|
|
1541
1919
|
}
|
|
1542
|
-
return
|
|
1920
|
+
return { ...response, result };
|
|
1921
|
+
}
|
|
1922
|
+
function unknownMethod(id, method) {
|
|
1923
|
+
return rpcError(id, -32601, `Method not found: ${method}`);
|
|
1543
1924
|
}
|
|
1544
1925
|
function createExecuteTool(options) {
|
|
1545
1926
|
const hostedNote = options.hosted ? `Hosted note: execute runs in an on-demand isolate and costs more than the direct
|
|
@@ -2656,6 +3037,7 @@ import {
|
|
|
2656
3037
|
readFileSync,
|
|
2657
3038
|
readdirSync,
|
|
2658
3039
|
renameSync,
|
|
3040
|
+
rmdirSync,
|
|
2659
3041
|
unlinkSync,
|
|
2660
3042
|
writeFileSync,
|
|
2661
3043
|
writeSync
|
|
@@ -2669,7 +3051,12 @@ var DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
|
2669
3051
|
var MANIFEST_NAME = ".agentic-mermaid-artifacts-v1.json";
|
|
2670
3052
|
var LOCK_NAME = ".agentic-mermaid-artifacts-v1.lock";
|
|
2671
3053
|
var MAX_MANIFEST_BYTES = 1024 * 1024;
|
|
3054
|
+
var MAX_LOCK_BYTES = 4096;
|
|
2672
3055
|
var MANAGED_NAME = /^[0-9a-z]+-[0-9a-f-]{36}\.[a-z0-9_-]+$/;
|
|
3056
|
+
var IMPLICIT_NAMESPACE = /^store-[1-9][0-9]*-[0-9a-f-]{36}$/;
|
|
3057
|
+
var LEGACY_IMPLICIT_NAMESPACE = /^agentic-mermaid-mcp-artifacts-[1-9][0-9]*-[0-9a-f-]{36}$/;
|
|
3058
|
+
var IMPLICIT_REAP_GRACE_MS = 3e4;
|
|
3059
|
+
var DEFAULT_ARTIFACT_ROOT = join(tmpdir(), "agentic-mermaid-mcp-artifacts");
|
|
2673
3060
|
function positiveSafeInteger(value, field) {
|
|
2674
3061
|
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`artifact ${field} must be a positive safe integer`);
|
|
2675
3062
|
return value;
|
|
@@ -2689,7 +3076,9 @@ var ArtifactStore = class {
|
|
|
2689
3076
|
lockFd = -1;
|
|
2690
3077
|
closed = false;
|
|
2691
3078
|
constructor(opts = {}) {
|
|
2692
|
-
|
|
3079
|
+
if (opts.dir !== void 0 && opts.namespaceRoot !== void 0) {
|
|
3080
|
+
throw new Error("artifact dir and namespaceRoot are mutually exclusive");
|
|
3081
|
+
}
|
|
2693
3082
|
this.baseUrl = normalizeBaseUrl(opts.baseUrl);
|
|
2694
3083
|
this.maxBytes = positiveSafeInteger(opts.maxBytes ?? DEFAULT_MAX_BYTES, "maxBytes");
|
|
2695
3084
|
this.maxTotalBytes = positiveSafeInteger(opts.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES, "maxTotalBytes");
|
|
@@ -2697,7 +3086,23 @@ var ArtifactStore = class {
|
|
|
2697
3086
|
this.ttlMs = positiveSafeInteger(opts.ttlMs ?? DEFAULT_TTL_MS, "ttlMs");
|
|
2698
3087
|
if (this.maxBytes > this.maxTotalBytes) throw new Error("artifact maxBytes must not exceed maxTotalBytes");
|
|
2699
3088
|
this.now = opts.now ?? (() => Date.now());
|
|
2700
|
-
|
|
3089
|
+
if (opts.dir !== void 0) {
|
|
3090
|
+
this.dir = resolve(opts.dir);
|
|
3091
|
+
mkdirSync(this.dir, { recursive: true, mode: 448 });
|
|
3092
|
+
} else {
|
|
3093
|
+
const root = resolve(opts.namespaceRoot ?? DEFAULT_ARTIFACT_ROOT);
|
|
3094
|
+
mkdirSync(root, { recursive: true, mode: 448 });
|
|
3095
|
+
const rootStat = lstatSync(root);
|
|
3096
|
+
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
|
|
3097
|
+
throw new Error("artifact namespace root must be a real directory");
|
|
3098
|
+
}
|
|
3099
|
+
if (opts.namespaceRoot === void 0) {
|
|
3100
|
+
reapImplicitArtifactNamespaces(resolve(tmpdir()), this.now(), LEGACY_IMPLICIT_NAMESPACE);
|
|
3101
|
+
}
|
|
3102
|
+
reapImplicitArtifactNamespaces(root, this.now());
|
|
3103
|
+
this.dir = join(root, `store-${process.pid}-${randomUUID()}`);
|
|
3104
|
+
mkdirSync(this.dir, { mode: 448 });
|
|
3105
|
+
}
|
|
2701
3106
|
this.manifestPath = join(this.dir, MANIFEST_NAME);
|
|
2702
3107
|
this.lockPath = join(this.dir, LOCK_NAME);
|
|
2703
3108
|
this.acquireOwnership();
|
|
@@ -2900,7 +3305,7 @@ var ArtifactStore = class {
|
|
|
2900
3305
|
}
|
|
2901
3306
|
let totalBytes = 0;
|
|
2902
3307
|
for (const candidate of manifest.records) {
|
|
2903
|
-
if (!
|
|
3308
|
+
if (!isManifestRecord(candidate)) {
|
|
2904
3309
|
throw new Error("artifact manifest contains an invalid record");
|
|
2905
3310
|
}
|
|
2906
3311
|
const path = safePath(this.dir, candidate.name);
|
|
@@ -2941,6 +3346,113 @@ var ArtifactStore = class {
|
|
|
2941
3346
|
if (persist) this.persistManifest();
|
|
2942
3347
|
}
|
|
2943
3348
|
};
|
|
3349
|
+
function reapImplicitArtifactNamespaces(root, now, namespacePattern = IMPLICIT_NAMESPACE) {
|
|
3350
|
+
let entries;
|
|
3351
|
+
try {
|
|
3352
|
+
entries = readdirSync(root);
|
|
3353
|
+
} catch {
|
|
3354
|
+
return;
|
|
3355
|
+
}
|
|
3356
|
+
for (const name of entries) {
|
|
3357
|
+
if (!namespacePattern.test(name)) continue;
|
|
3358
|
+
const dir = safePath(root, name);
|
|
3359
|
+
try {
|
|
3360
|
+
const namespace = lstatSync(dir);
|
|
3361
|
+
if (!namespace.isDirectory() || namespace.isSymbolicLink()) continue;
|
|
3362
|
+
if (now - namespace.mtimeMs < IMPLICIT_REAP_GRACE_MS) continue;
|
|
3363
|
+
const lockPath = join(dir, LOCK_NAME);
|
|
3364
|
+
if (existsSync(lockPath)) {
|
|
3365
|
+
const lock = lstatSync(lockPath);
|
|
3366
|
+
if (!lock.isFile() || lock.isSymbolicLink() || lock.size > MAX_LOCK_BYTES) continue;
|
|
3367
|
+
const marker = JSON.parse(readFileSync(lockPath, "utf8"));
|
|
3368
|
+
if (marker.host !== hostname() || !Number.isSafeInteger(marker.pid) || marker.pid <= 0) continue;
|
|
3369
|
+
if (processIsAlive(marker.pid)) continue;
|
|
3370
|
+
const current = lstatSync(lockPath);
|
|
3371
|
+
if (current.dev !== lock.dev || current.ino !== lock.ino) continue;
|
|
3372
|
+
unlinkSync(lockPath);
|
|
3373
|
+
}
|
|
3374
|
+
reapClosedNamespace(dir, now);
|
|
3375
|
+
} catch {
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
function processIsAlive(pid) {
|
|
3380
|
+
try {
|
|
3381
|
+
process.kill(pid, 0);
|
|
3382
|
+
return true;
|
|
3383
|
+
} catch (error) {
|
|
3384
|
+
return error.code !== "ESRCH";
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
function reapClosedNamespace(dir, now) {
|
|
3388
|
+
const manifestPath = join(dir, MANIFEST_NAME);
|
|
3389
|
+
let records = [];
|
|
3390
|
+
if (existsSync(manifestPath)) {
|
|
3391
|
+
const stat = lstatSync(manifestPath);
|
|
3392
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_MANIFEST_BYTES) return;
|
|
3393
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
3394
|
+
if (manifest?.schemaVersion !== 1 || !Array.isArray(manifest.records) || manifest.records.some((record) => !isManifestRecord(record))) return;
|
|
3395
|
+
records = manifest.records;
|
|
3396
|
+
}
|
|
3397
|
+
const live = [];
|
|
3398
|
+
for (const record of records) {
|
|
3399
|
+
const path = safePath(dir, record.name);
|
|
3400
|
+
if (record.expiresAt <= now) {
|
|
3401
|
+
try {
|
|
3402
|
+
unlinkSync(path);
|
|
3403
|
+
} catch {
|
|
3404
|
+
}
|
|
3405
|
+
continue;
|
|
3406
|
+
}
|
|
3407
|
+
try {
|
|
3408
|
+
const file = lstatSync(path);
|
|
3409
|
+
if (file.isFile() && !file.isSymbolicLink() && file.size === record.bytes) live.push(record);
|
|
3410
|
+
else if (file.isFile() || file.isSymbolicLink()) unlinkSync(path);
|
|
3411
|
+
} catch {
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
const liveNames = new Set(live.map((record) => record.name));
|
|
3415
|
+
for (const name of readdirSync(dir)) {
|
|
3416
|
+
if (!MANAGED_NAME.test(name) || liveNames.has(name)) continue;
|
|
3417
|
+
const path = safePath(dir, name);
|
|
3418
|
+
try {
|
|
3419
|
+
const file = lstatSync(path);
|
|
3420
|
+
if (file.isFile() || file.isSymbolicLink()) unlinkSync(path);
|
|
3421
|
+
} catch {
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
if (live.length > 0) {
|
|
3425
|
+
if (live.length !== records.length) persistManifestRecords(dir, live);
|
|
3426
|
+
return;
|
|
3427
|
+
}
|
|
3428
|
+
try {
|
|
3429
|
+
const manifest = lstatSync(manifestPath);
|
|
3430
|
+
if (manifest.isFile() && !manifest.isSymbolicLink()) unlinkSync(manifestPath);
|
|
3431
|
+
} catch {
|
|
3432
|
+
}
|
|
3433
|
+
try {
|
|
3434
|
+
rmdirSync(dir);
|
|
3435
|
+
} catch {
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
function persistManifestRecords(dir, records) {
|
|
3439
|
+
const path = join(dir, MANIFEST_NAME);
|
|
3440
|
+
const temp = join(dir, `.${MANIFEST_NAME}.${process.pid}.${randomUUID()}.tmp`);
|
|
3441
|
+
try {
|
|
3442
|
+
writeFileSync(temp, JSON.stringify({ schemaVersion: 1, records }), { mode: 384 });
|
|
3443
|
+
renameSync(temp, path);
|
|
3444
|
+
} finally {
|
|
3445
|
+
try {
|
|
3446
|
+
unlinkSync(temp);
|
|
3447
|
+
} catch {
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
function isManifestRecord(candidate) {
|
|
3452
|
+
if (!candidate || typeof candidate !== "object") return false;
|
|
3453
|
+
const record = candidate;
|
|
3454
|
+
return typeof record.name === "string" && MANAGED_NAME.test(record.name) && typeof record.mimeType === "string" && record.mimeType.length > 0 && Number.isSafeInteger(record.bytes) && record.bytes >= 0 && typeof record.sha256 === "string" && /^[a-f0-9]{64}$/.test(record.sha256) && Number.isSafeInteger(record.createdAt) && Number.isSafeInteger(record.expiresAt) && record.expiresAt > record.createdAt;
|
|
3455
|
+
}
|
|
2944
3456
|
function createArtifactStore(opts = {}) {
|
|
2945
3457
|
return new ArtifactStore(opts);
|
|
2946
3458
|
}
|
|
@@ -2967,6 +3479,9 @@ function safePath(root, name) {
|
|
|
2967
3479
|
|
|
2968
3480
|
// src/mcp/server.ts
|
|
2969
3481
|
var PROTOCOL_VERSION = "2024-11-05";
|
|
3482
|
+
var STDIO_PROTOCOL_VERSIONS = ["2024-11-05", "2025-06-18", "2025-11-25", "2026-07-28"];
|
|
3483
|
+
var HTTP_SSE_PROTOCOL_VERSIONS = ["2024-11-05"];
|
|
3484
|
+
var HTTP_SSE_DISPATCH = { supportedVersions: HTTP_SSE_PROTOCOL_VERSIONS };
|
|
2970
3485
|
var MAX_RPC_BODY_BYTES = 1024 * 1024;
|
|
2971
3486
|
var MAX_SANDBOX_TIMEOUT_MS = 3e4;
|
|
2972
3487
|
var MAX_SSE_SESSIONS = 32;
|
|
@@ -2989,12 +3504,21 @@ var MCP_NARROWERS = BUILTIN_FAMILY_METADATA.map((f) => f.narrower).join("/");
|
|
|
2989
3504
|
var 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. describe_sdk progressively discloses one family's version-matched mutation schema; call it before authoring unfamiliar ops. 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. There is no mutate tool on this server: structured edits go through the SDK's mermaid.mutate(...) inside execute; 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).`;
|
|
2990
3505
|
var LOCAL_SURFACE = {
|
|
2991
3506
|
protocolVersion: PROTOCOL_VERSION,
|
|
3507
|
+
supportedVersions: STDIO_PROTOCOL_VERSIONS,
|
|
2992
3508
|
tools: LOCAL_TOOLS,
|
|
2993
3509
|
instructions: LOCAL_INSTRUCTIONS,
|
|
2994
3510
|
handleToolCall
|
|
2995
3511
|
};
|
|
2996
|
-
|
|
2997
|
-
return
|
|
3512
|
+
function admitLocalRequest(req, options = {}) {
|
|
3513
|
+
return admitMcpMessage(req, {
|
|
3514
|
+
supportedVersions: surfaceSupportedVersions(LOCAL_SURFACE, options),
|
|
3515
|
+
negotiatedVersion: options.protocolVersion,
|
|
3516
|
+
protocolEra: options.protocolEra
|
|
3517
|
+
});
|
|
3518
|
+
}
|
|
3519
|
+
async function handleRequest(req, context = {}, options = {}) {
|
|
3520
|
+
const admission = admitLocalRequest(req, options);
|
|
3521
|
+
return admission.ok ? dispatchAdmittedMcpRequest(admission.message, context, LOCAL_SURFACE) : admission.response;
|
|
2998
3522
|
}
|
|
2999
3523
|
async function handleToolCall(id, params, context) {
|
|
3000
3524
|
const p = params;
|
|
@@ -3075,7 +3599,7 @@ function artifactPayload(artifact, output) {
|
|
|
3075
3599
|
return { ...base, path: artifact.path };
|
|
3076
3600
|
}
|
|
3077
3601
|
function getDefaultArtifactStore() {
|
|
3078
|
-
defaultArtifactStore ??= createArtifactStore(
|
|
3602
|
+
defaultArtifactStore ??= createArtifactStore();
|
|
3079
3603
|
if (!defaultArtifactStoreExitHook) {
|
|
3080
3604
|
defaultArtifactStoreExitHook = true;
|
|
3081
3605
|
process.once("exit", () => defaultArtifactStore?.close());
|
|
@@ -3088,6 +3612,112 @@ function warmUpPngRenderer() {
|
|
|
3088
3612
|
} catch {
|
|
3089
3613
|
}
|
|
3090
3614
|
}
|
|
3615
|
+
function stdioCancellationTarget(value) {
|
|
3616
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3617
|
+
const request = value;
|
|
3618
|
+
if (request.jsonrpc !== "2.0" || request.method !== "notifications/cancelled" || Object.prototype.hasOwnProperty.call(request, "id")) return void 0;
|
|
3619
|
+
const params = request.params;
|
|
3620
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) return void 0;
|
|
3621
|
+
const requestId = params.requestId;
|
|
3622
|
+
return typeof requestId === "string" || typeof requestId === "number" && Number.isSafeInteger(requestId) ? requestId : void 0;
|
|
3623
|
+
}
|
|
3624
|
+
function stdioRequestId(value) {
|
|
3625
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3626
|
+
const request = value;
|
|
3627
|
+
if (request.jsonrpc !== "2.0" || typeof request.method !== "string" || !Object.prototype.hasOwnProperty.call(request, "id")) return void 0;
|
|
3628
|
+
const requestId = request.id;
|
|
3629
|
+
return typeof requestId === "string" || typeof requestId === "number" && Number.isSafeInteger(requestId) ? requestId : void 0;
|
|
3630
|
+
}
|
|
3631
|
+
function stdioRequestKey(id) {
|
|
3632
|
+
return `${typeof id}:${String(id)}`;
|
|
3633
|
+
}
|
|
3634
|
+
function createStdioMessageProcessor(context, write, dispatch = dispatchAdmittedMcpRequest) {
|
|
3635
|
+
let negotiatedProtocolVersion = null;
|
|
3636
|
+
let negotiatedEra = null;
|
|
3637
|
+
let admissionGate = Promise.resolve();
|
|
3638
|
+
const tasks = /* @__PURE__ */ new Set();
|
|
3639
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
3640
|
+
const register = (id, controller) => {
|
|
3641
|
+
if (id === void 0) return;
|
|
3642
|
+
const key = stdioRequestKey(id);
|
|
3643
|
+
const controllers = inFlight.get(key) ?? /* @__PURE__ */ new Set();
|
|
3644
|
+
controllers.add(controller);
|
|
3645
|
+
inFlight.set(key, controllers);
|
|
3646
|
+
};
|
|
3647
|
+
const unregister = (id, controller) => {
|
|
3648
|
+
if (id === void 0) return;
|
|
3649
|
+
const key = stdioRequestKey(id);
|
|
3650
|
+
const controllers = inFlight.get(key);
|
|
3651
|
+
controllers?.delete(controller);
|
|
3652
|
+
if (controllers?.size === 0) inFlight.delete(key);
|
|
3653
|
+
};
|
|
3654
|
+
const emit = (response, exactIds, signal) => {
|
|
3655
|
+
if (response && !signal?.aborted) write(stringifyJsonRpc(response, exactIds) + "\n");
|
|
3656
|
+
};
|
|
3657
|
+
const accept = (line) => {
|
|
3658
|
+
let exact;
|
|
3659
|
+
let request;
|
|
3660
|
+
try {
|
|
3661
|
+
exact = preserveExactJsonRpcIds(line);
|
|
3662
|
+
request = JSON.parse(exact.body);
|
|
3663
|
+
} catch (cause) {
|
|
3664
|
+
write(JSON.stringify(rpcError(null, -32700, `parse error: ${cause instanceof Error ? cause.message : String(cause)}`)) + "\n");
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
const cancellationTarget = stdioCancellationTarget(request);
|
|
3668
|
+
if (cancellationTarget !== void 0) {
|
|
3669
|
+
for (const controller2 of inFlight.get(stdioRequestKey(cancellationTarget)) ?? []) controller2.abort();
|
|
3670
|
+
return;
|
|
3671
|
+
}
|
|
3672
|
+
const requestId = stdioRequestId(request);
|
|
3673
|
+
const controller = new AbortController();
|
|
3674
|
+
register(requestId, controller);
|
|
3675
|
+
const preparation = admissionGate.then(async () => {
|
|
3676
|
+
const dispatchOptions = negotiatedEra === "legacy" ? { protocolEra: "legacy", protocolVersion: negotiatedProtocolVersion, supportedVersions: negotiatedProtocolVersion ? [negotiatedProtocolVersion] : void 0 } : negotiatedEra === "modern" ? { protocolEra: "modern", supportedVersions: STDIO_PROTOCOL_VERSIONS } : {};
|
|
3677
|
+
const admission = admitLocalRequest(request, dispatchOptions);
|
|
3678
|
+
if (admission.ok && negotiatedEra === null && request.method === "initialize") {
|
|
3679
|
+
const response = controller.signal.aborted ? null : await dispatch(admission.message, { ...context, signal: controller.signal }, LOCAL_SURFACE);
|
|
3680
|
+
if (!controller.signal.aborted && response?.result && typeof response.result === "object") {
|
|
3681
|
+
const selected = response.result.protocolVersion;
|
|
3682
|
+
if (typeof selected === "string" && STDIO_PROTOCOL_VERSIONS.includes(selected)) {
|
|
3683
|
+
negotiatedProtocolVersion = selected;
|
|
3684
|
+
negotiatedEra = "legacy";
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
emit(response, exact.ids, controller.signal);
|
|
3688
|
+
return { handled: true, admission };
|
|
3689
|
+
}
|
|
3690
|
+
if (admission.ok && negotiatedEra === null && admission.message.protocol.era === "modern") {
|
|
3691
|
+
negotiatedEra = "modern";
|
|
3692
|
+
}
|
|
3693
|
+
return { handled: false, admission };
|
|
3694
|
+
});
|
|
3695
|
+
admissionGate = preparation.then(() => void 0, () => void 0);
|
|
3696
|
+
let task;
|
|
3697
|
+
task = preparation.then(async (prepared) => {
|
|
3698
|
+
if (prepared.handled) return;
|
|
3699
|
+
const response = prepared.admission.ok ? controller.signal.aborted ? null : await dispatch(prepared.admission.message, { ...context, signal: controller.signal }, LOCAL_SURFACE) : prepared.admission.response;
|
|
3700
|
+
emit(response, exact.ids, controller.signal);
|
|
3701
|
+
}).catch((cause) => {
|
|
3702
|
+
emit(
|
|
3703
|
+
rpcError(requestId ?? null, -32603, `internal error: ${cause instanceof Error ? cause.message : String(cause)}`),
|
|
3704
|
+
exact.ids,
|
|
3705
|
+
controller.signal
|
|
3706
|
+
);
|
|
3707
|
+
}).finally(() => {
|
|
3708
|
+
unregister(requestId, controller);
|
|
3709
|
+
tasks.delete(task);
|
|
3710
|
+
});
|
|
3711
|
+
tasks.add(task);
|
|
3712
|
+
};
|
|
3713
|
+
return {
|
|
3714
|
+
accept,
|
|
3715
|
+
async drain() {
|
|
3716
|
+
await admissionGate;
|
|
3717
|
+
while (tasks.size > 0) await Promise.allSettled([...tasks]);
|
|
3718
|
+
}
|
|
3719
|
+
};
|
|
3720
|
+
}
|
|
3091
3721
|
async function runStdio(options = {}) {
|
|
3092
3722
|
warmUpPngRenderer();
|
|
3093
3723
|
const artifactStore = createArtifactStore({
|
|
@@ -3097,22 +3727,20 @@ async function runStdio(options = {}) {
|
|
|
3097
3727
|
maxArtifacts: options.maxArtifacts,
|
|
3098
3728
|
ttlMs: options.artifactTtlMs
|
|
3099
3729
|
});
|
|
3730
|
+
const processor = createStdioMessageProcessor(
|
|
3731
|
+
{ artifactStore, maxSandboxTimeoutMs: options.maxSandboxTimeoutMs },
|
|
3732
|
+
(line) => process.stdout.write(line)
|
|
3733
|
+
);
|
|
3100
3734
|
process.stdin.setEncoding("utf8");
|
|
3101
3735
|
let buf = "";
|
|
3102
|
-
process.stdin.on("data",
|
|
3736
|
+
process.stdin.on("data", (chunk) => {
|
|
3103
3737
|
buf += chunk;
|
|
3104
3738
|
let nl;
|
|
3105
3739
|
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
3106
3740
|
const line = buf.slice(0, nl).trim();
|
|
3107
3741
|
buf = buf.slice(nl + 1);
|
|
3108
3742
|
if (!line) continue;
|
|
3109
|
-
|
|
3110
|
-
const exact = preserveExactJsonRpcIds(line);
|
|
3111
|
-
const res = await handleRequest(JSON.parse(exact.body), { artifactStore, maxSandboxTimeoutMs: options.maxSandboxTimeoutMs });
|
|
3112
|
-
if (res) process.stdout.write(stringifyJsonRpc(res, exact.ids) + "\n");
|
|
3113
|
-
} catch (e) {
|
|
3114
|
-
process.stdout.write(JSON.stringify(rpcError(null, -32700, `parse error: ${e.message}`)) + "\n");
|
|
3115
|
-
}
|
|
3743
|
+
processor.accept(line);
|
|
3116
3744
|
}
|
|
3117
3745
|
});
|
|
3118
3746
|
try {
|
|
@@ -3120,6 +3748,7 @@ async function runStdio(options = {}) {
|
|
|
3120
3748
|
process.stdin.on("end", () => resolve2());
|
|
3121
3749
|
process.stdin.on("close", () => resolve2());
|
|
3122
3750
|
});
|
|
3751
|
+
await processor.drain();
|
|
3123
3752
|
} finally {
|
|
3124
3753
|
artifactStore.close();
|
|
3125
3754
|
}
|
|
@@ -3288,7 +3917,7 @@ async function postSseMessage(req, res, sessions, context, maxBytes) {
|
|
|
3288
3917
|
} catch {
|
|
3289
3918
|
throw new HttpStatusError(400, "invalid JSON-RPC body");
|
|
3290
3919
|
}
|
|
3291
|
-
const response = await handleRequest(parsed, context);
|
|
3920
|
+
const response = await handleRequest(parsed, context, HTTP_SSE_DISPATCH);
|
|
3292
3921
|
if (response) {
|
|
3293
3922
|
sse.write(`event: message
|
|
3294
3923
|
data: ${stringifyJsonRpc(response, exact.ids)}
|
|
@@ -3308,7 +3937,7 @@ async function postRpc(req, res, context, maxBytes) {
|
|
|
3308
3937
|
} catch {
|
|
3309
3938
|
throw new HttpStatusError(400, "invalid JSON-RPC body");
|
|
3310
3939
|
}
|
|
3311
|
-
const response = await handleRequest(parsed, context);
|
|
3940
|
+
const response = await handleRequest(parsed, context, HTTP_SSE_DISPATCH);
|
|
3312
3941
|
if (response === null) {
|
|
3313
3942
|
res.writeHead(202);
|
|
3314
3943
|
res.end();
|
|
@@ -3482,4 +4111,4 @@ export {
|
|
|
3482
4111
|
parseMcpCliOptions,
|
|
3483
4112
|
runMcpCli
|
|
3484
4113
|
};
|
|
3485
|
-
//# sourceMappingURL=chunk-
|
|
4114
|
+
//# sourceMappingURL=chunk-WMFFVZ4Q.js.map
|