agentic-mermaid 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -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-WMSX5L4P.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-RVO6J7S3.js → chunk-32FWKUUV.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-KTTWXSIX.js → chunk-ANTNEVJC.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-DPSSG7LR.js → chunk-AOCFMYYH.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-2U4EUPSI.js → chunk-CCTZFX43.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-PXRUFH7U.js → chunk-EBPTM2RP.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-FXBBGAMJ.js → chunk-EX6TP7QM.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-MMWVZP6M.js → chunk-QMZ4Y6I7.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-4IUBLBJJ.js → chunk-QYJMRNS3.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-6UT2VTFI.js → chunk-RLS545DI.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-VDTSULBU.js → chunk-TBZAU7KC.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-JX5ZZY3T.js → chunk-TDGTBGV3.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-HZ4ADQMK.js → chunk-TEH2PRKN.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-CTVVJPMX.js → chunk-W6Q4PHPC.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-4U2TVB2S.js → chunk-WN7SFUCZ.js} +1 -1
- package/dist/browser-lazy/chunks/{chunk-7D24ZLOV.js → chunk-ZZECLXC3.js} +1 -1
- package/dist/browser-lazy/chunks/{class-B3AVLSIF.js → class-7JRALQIM.js} +1 -1
- package/dist/browser-lazy/chunks/{er-AF4BKKKC.js → er-UDCGHKBX.js} +1 -1
- package/dist/browser-lazy/chunks/{flowchart-BE6BQZLZ.js → flowchart-5FP7RRIK.js} +1 -1
- package/dist/browser-lazy/chunks/{gantt-U4BMMSEL.js → gantt-NTOIC5CW.js} +1 -1
- package/dist/browser-lazy/chunks/{gitgraph-UPSWNFRP.js → gitgraph-JYAPXUIJ.js} +1 -1
- package/dist/browser-lazy/chunks/{journey-PDZ7FDJR.js → journey-646EFIB6.js} +1 -1
- package/dist/browser-lazy/chunks/{mindmap-BFEUURLS.js → mindmap-2SBTQHQO.js} +1 -1
- package/dist/browser-lazy/chunks/{pie-ZFYW5JZO.js → pie-7FDXZ24K.js} +1 -1
- package/dist/browser-lazy/chunks/{quadrant-XJVEYYXK.js → quadrant-72UT4XAW.js} +1 -1
- package/dist/browser-lazy/chunks/{radar-OVXFNRNC.js → radar-SU7BO56J.js} +1 -1
- package/dist/browser-lazy/chunks/{render-core-R4ZGGEFS.js → render-core-Q2JWXDDL.js} +2 -2
- package/dist/browser-lazy/chunks/{sequence-AWY6JCSB.js → sequence-5O5ET264.js} +1 -1
- package/dist/browser-lazy/chunks/{state-TCE4DXKR.js → state-IWPLFQ6R.js} +1 -1
- package/dist/browser-lazy/chunks/{timeline-W4ZVT3FF.js → timeline-WJWYHH5Y.js} +1 -1
- package/dist/browser-lazy/chunks/{xychart-K3QQACKS.js → xychart-HHQVQA5O.js} +1 -1
- package/dist/browser-lazy/index.js +1 -1
- package/dist/browser.global.js +2 -2
- package/dist/{chunk-XVY324NY.js → chunk-3D4EHVQ4.js} +5 -3
- package/dist/{chunk-BSVM3VMO.js → chunk-GX7UT56Q.js} +2 -2
- package/dist/{chunk-JJ7JRYZV.js → chunk-IVN6KP3W.js} +2 -2
- package/dist/{chunk-FGO3J7RA.js → chunk-NE6JBHSE.js} +680 -42
- package/dist/{chunk-KQMJ37OY.js → chunk-R5U67OIW.js} +4 -4
- package/dist/{chunk-MBIR6S3B.js → chunk-YWKCUBQN.js} +2 -2
- package/dist/{index-Do8z5rPq.d.ts → index-BFg_LoZS.d.ts} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp-cli-XGZP6CMW.js +17 -0
- package/docs/mcp-http-transport.md +2 -0
- package/package.json +5 -4
- package/server.json +2 -2
- 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-IVN6KP3W.js";
|
|
5
5
|
import {
|
|
6
6
|
renderMermaidPNG,
|
|
7
7
|
renderMermaidPNGWithReceipt
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YWKCUBQN.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-GX7UT56Q.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-3D4EHVQ4.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,338 @@ 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 admitMcpHeaderVersion(headerVersion, supportedVersions) {
|
|
1411
|
+
if (headerVersion === null || supportedVersions.includes(headerVersion)) return null;
|
|
1412
|
+
return {
|
|
1413
|
+
ok: false,
|
|
1414
|
+
kind: "unsupported-version",
|
|
1415
|
+
response: rpcError(
|
|
1416
|
+
null,
|
|
1417
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
1418
|
+
`Unsupported protocol version: ${headerVersion}`,
|
|
1419
|
+
{ supported: [...supportedVersions], requested: headerVersion }
|
|
1420
|
+
)
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
function responseId(raw, validId) {
|
|
1424
|
+
const value = raw.id;
|
|
1425
|
+
return validId && value !== void 0 ? value : null;
|
|
1426
|
+
}
|
|
1427
|
+
function rejection(kind, notification, response, protocol, requestedEra) {
|
|
1428
|
+
return {
|
|
1429
|
+
ok: false,
|
|
1430
|
+
kind,
|
|
1431
|
+
response: notification ? null : response,
|
|
1432
|
+
...protocol ? { protocol } : {},
|
|
1433
|
+
...requestedEra ? { requestedEra } : {}
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
function acceptance(request, id, notification, protocol) {
|
|
1437
|
+
const base = { request, id, protocol, [admittedMcpMessage]: true };
|
|
1438
|
+
return notification ? { ok: true, message: { ...base, notification: true } } : { ok: true, message: { ...base, notification: false } };
|
|
1439
|
+
}
|
|
1440
|
+
function admitMcpMessage(rawMessage, options) {
|
|
1441
|
+
const raw = rawMessage;
|
|
1442
|
+
const hasId = Boolean(raw && Object.prototype.hasOwnProperty.call(raw, "id"));
|
|
1443
|
+
const rawId = raw?.id;
|
|
1444
|
+
const validId = rawId === void 0 || typeof rawId === "string" || typeof rawId === "number" && Number.isSafeInteger(rawId);
|
|
1445
|
+
const validEnvelope = Boolean(raw && !Array.isArray(raw) && raw.jsonrpc === "2.0" && typeof raw.method === "string" && validId);
|
|
1446
|
+
if (!validEnvelope) {
|
|
1447
|
+
return { ok: false, kind: "invalid-request", response: rpcError(null, -32600, "invalid JSON-RPC request") };
|
|
1448
|
+
}
|
|
1449
|
+
const request = rawMessage;
|
|
1450
|
+
const id = responseId(raw, validId);
|
|
1451
|
+
const notification = !hasId;
|
|
1452
|
+
const versionClaim = requestMetaProtocolVersionClaim(request);
|
|
1453
|
+
const declaredVersion = requestMetaProtocolVersion(request);
|
|
1454
|
+
const requestedEra = versionClaim.present ? "modern" : "legacy";
|
|
1455
|
+
const supportedVersions = options.protocolEra === "legacy" ? options.supportedVersions.filter(isLegacyProtocolVersion) : options.protocolEra === "modern" ? options.supportedVersions.filter(isModernProtocolVersion) : [...options.supportedVersions];
|
|
1456
|
+
const effectiveTransportVersion = options.negotiatedVersion ?? options.headerVersion ?? void 0;
|
|
1457
|
+
const provisionalProtocol = isModernProtocolVersion(declaredVersion) ? { era: "modern", version: declaredVersion, supportedVersions } : { era: "legacy", version: declaredVersion ?? effectiveTransportVersion, supportedVersions };
|
|
1458
|
+
const headerAdmission = admitMcpHeaderVersion(options.headerVersion ?? null, supportedVersions);
|
|
1459
|
+
if (headerAdmission) {
|
|
1460
|
+
return { ...headerAdmission, protocol: provisionalProtocol, requestedEra };
|
|
1461
|
+
}
|
|
1462
|
+
if (versionClaim.present && typeof versionClaim.value !== "string") {
|
|
1463
|
+
return rejection(
|
|
1464
|
+
"invalid-modern-metadata",
|
|
1465
|
+
notification,
|
|
1466
|
+
rpcError(id, -32602, `Invalid params: params._meta.${META_PROTOCOL_VERSION} is required and must be a string`),
|
|
1467
|
+
void 0,
|
|
1468
|
+
"modern"
|
|
1469
|
+
);
|
|
1470
|
+
}
|
|
1471
|
+
if (options.negotiatedVersion != null && !supportedVersions.includes(options.negotiatedVersion)) {
|
|
1472
|
+
return rejection(
|
|
1473
|
+
"unsupported-version",
|
|
1474
|
+
notification,
|
|
1475
|
+
rpcError(
|
|
1476
|
+
id,
|
|
1477
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
1478
|
+
`Unsupported protocol version: ${options.negotiatedVersion}`,
|
|
1479
|
+
{ supported: [...supportedVersions], requested: options.negotiatedVersion }
|
|
1480
|
+
),
|
|
1481
|
+
provisionalProtocol,
|
|
1482
|
+
requestedEra
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
if (declaredVersion !== void 0 && options.headerVersion != null && declaredVersion !== options.headerVersion) {
|
|
1486
|
+
return rejection(
|
|
1487
|
+
"header-mismatch",
|
|
1488
|
+
notification,
|
|
1489
|
+
rpcError(
|
|
1490
|
+
id,
|
|
1491
|
+
HEADER_MISMATCH,
|
|
1492
|
+
`Header mismatch: MCP-Protocol-Version header value '${options.headerVersion}' does not match body value '${declaredVersion}'`
|
|
1493
|
+
),
|
|
1494
|
+
provisionalProtocol,
|
|
1495
|
+
requestedEra
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
if (options.protocolEra !== void 0 && requestedEra !== options.protocolEra) {
|
|
1499
|
+
const requested = declaredVersion ?? effectiveTransportVersion ?? "unversioned";
|
|
1500
|
+
return rejection(
|
|
1501
|
+
"unsupported-version",
|
|
1502
|
+
notification,
|
|
1503
|
+
rpcError(
|
|
1504
|
+
id,
|
|
1505
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
1506
|
+
`Unsupported protocol version: ${requested}`,
|
|
1507
|
+
{ supported: [...supportedVersions], requested }
|
|
1508
|
+
),
|
|
1509
|
+
provisionalProtocol,
|
|
1510
|
+
requestedEra
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
if (requestedEra === "modern" && isLegacyProtocolVersion(declaredVersion) && (!options.requireModernVersionHeader || options.headerVersion != null)) {
|
|
1514
|
+
const modernSupportedVersions = supportedVersions.filter(isModernProtocolVersion);
|
|
1515
|
+
return rejection(
|
|
1516
|
+
"unsupported-version",
|
|
1517
|
+
notification,
|
|
1518
|
+
rpcError(
|
|
1519
|
+
id,
|
|
1520
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
1521
|
+
`Unsupported protocol version: ${declaredVersion}`,
|
|
1522
|
+
{ supported: modernSupportedVersions, requested: declaredVersion }
|
|
1523
|
+
),
|
|
1524
|
+
provisionalProtocol,
|
|
1525
|
+
requestedEra
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
if (declaredVersion !== void 0 && !supportedVersions.includes(declaredVersion)) {
|
|
1529
|
+
return rejection(
|
|
1530
|
+
"unsupported-version",
|
|
1531
|
+
notification,
|
|
1532
|
+
rpcError(
|
|
1533
|
+
id,
|
|
1534
|
+
UNSUPPORTED_PROTOCOL_VERSION,
|
|
1535
|
+
`Unsupported protocol version: ${declaredVersion}`,
|
|
1536
|
+
{ supported: [...supportedVersions], requested: declaredVersion }
|
|
1537
|
+
),
|
|
1538
|
+
provisionalProtocol,
|
|
1539
|
+
requestedEra
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
const era = options.protocolEra ?? (isModernProtocolVersion(options.headerVersion) || isModernProtocolVersion(options.negotiatedVersion) ? "modern" : requestedEra);
|
|
1543
|
+
if (era === "modern") {
|
|
1544
|
+
if (!isModernProtocolVersion(declaredVersion)) {
|
|
1545
|
+
const problem = `params._meta.${META_PROTOCOL_VERSION} is required and must match the MCP-Protocol-Version header (${options.headerVersion})`;
|
|
1546
|
+
return rejection(
|
|
1547
|
+
"header-mismatch",
|
|
1548
|
+
notification,
|
|
1549
|
+
rpcError(id, HEADER_MISMATCH, `Header mismatch: ${problem}`),
|
|
1550
|
+
provisionalProtocol,
|
|
1551
|
+
requestedEra
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
const protocol2 = { era: "modern", version: declaredVersion, supportedVersions };
|
|
1555
|
+
if (options.requireModernVersionHeader && options.headerVersion == null) {
|
|
1556
|
+
return rejection(
|
|
1557
|
+
"header-mismatch",
|
|
1558
|
+
notification,
|
|
1559
|
+
rpcError(
|
|
1560
|
+
id,
|
|
1561
|
+
HEADER_MISMATCH,
|
|
1562
|
+
`Header mismatch: MCP-Protocol-Version header is required for protocol version ${declaredVersion}`
|
|
1563
|
+
),
|
|
1564
|
+
protocol2,
|
|
1565
|
+
requestedEra
|
|
1566
|
+
);
|
|
1567
|
+
}
|
|
1568
|
+
const transportProblem = options.modernTransportProblem?.(request) ?? null;
|
|
1569
|
+
if (transportProblem !== null) {
|
|
1570
|
+
return rejection(
|
|
1571
|
+
"header-mismatch",
|
|
1572
|
+
notification,
|
|
1573
|
+
rpcError(id, HEADER_MISMATCH, `Header mismatch: ${transportProblem}`),
|
|
1574
|
+
protocol2,
|
|
1575
|
+
requestedEra
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
const metaProblems = modernRequestMetaProblems(request);
|
|
1579
|
+
if (metaProblems.length > 0) {
|
|
1580
|
+
return rejection(
|
|
1581
|
+
"invalid-modern-metadata",
|
|
1582
|
+
notification,
|
|
1583
|
+
rpcError(id, -32602, `Invalid params: ${metaProblems.join("; ")}`),
|
|
1584
|
+
protocol2,
|
|
1585
|
+
requestedEra
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
return acceptance(request, id, notification, protocol2);
|
|
1589
|
+
}
|
|
1590
|
+
const protocol = {
|
|
1591
|
+
era: "legacy",
|
|
1592
|
+
version: declaredVersion ?? effectiveTransportVersion,
|
|
1593
|
+
supportedVersions
|
|
1594
|
+
};
|
|
1595
|
+
return acceptance(request, id, notification, protocol);
|
|
1264
1596
|
}
|
|
1265
1597
|
|
|
1266
1598
|
// src/mcp/tool-surface.ts
|
|
@@ -1269,6 +1601,7 @@ function withClosedMcpInputSchema(tool) {
|
|
|
1269
1601
|
}
|
|
1270
1602
|
var MCP_SERVER_NAME = "agentic-mermaid-mcp";
|
|
1271
1603
|
var MCP_SERVER_VERSION = PACKAGE_VERSION;
|
|
1604
|
+
var SERVER_CAPABILITIES = { tools: {}, prompts: {}, resources: {} };
|
|
1272
1605
|
var PURE_COMPUTE_ANNOTATIONS = {
|
|
1273
1606
|
readOnlyHint: true,
|
|
1274
1607
|
destructiveHint: false,
|
|
@@ -1476,32 +1809,66 @@ function validateMcpToolArguments(tool, value) {
|
|
|
1476
1809
|
const problems = validateSchemaValue(value, tool.inputSchema, tool.inputSchema, [], /* @__PURE__ */ new Set()).map((problem) => `${formatSchemaPath(problem.path)} ${problem.message}`);
|
|
1477
1810
|
return limitJsonConfigDiagnostics(problems, "arguments");
|
|
1478
1811
|
}
|
|
1479
|
-
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1812
|
+
function surfaceSupportedVersions(surface, options = {}) {
|
|
1813
|
+
const surfaceVersions = surface.supportedVersions ?? (typeof surface.protocolVersion === "string" ? [surface.protocolVersion] : []);
|
|
1814
|
+
if (!options.supportedVersions) return surfaceVersions;
|
|
1815
|
+
return surfaceVersions.filter((version) => options.supportedVersions.includes(version));
|
|
1816
|
+
}
|
|
1817
|
+
function discoverResult(surface, supportedVersions) {
|
|
1818
|
+
return {
|
|
1819
|
+
supportedVersions: supportedVersions.filter(isModernProtocolVersion),
|
|
1820
|
+
capabilities: SERVER_CAPABILITIES,
|
|
1821
|
+
instructions: surface.instructions
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
function initializeParamsProblems(params) {
|
|
1825
|
+
if (!plainJsonObject(params)) return ["params is required and must be an object"];
|
|
1826
|
+
const problems = [];
|
|
1827
|
+
if (typeof params.protocolVersion !== "string") {
|
|
1828
|
+
problems.push("params.protocolVersion is required and must be a string");
|
|
1829
|
+
}
|
|
1830
|
+
problems.push(...mcpClientCapabilitiesProblems(params.capabilities, "params.capabilities"));
|
|
1831
|
+
problems.push(...mcpImplementationProblems(params.clientInfo, "params.clientInfo"));
|
|
1832
|
+
return problems;
|
|
1833
|
+
}
|
|
1834
|
+
async function dispatchAdmittedMcpRequest(message, context, surface) {
|
|
1835
|
+
const { request: req, id, notification, protocol } = message;
|
|
1836
|
+
const { era, version, supportedVersions: servedVersions } = protocol;
|
|
1488
1837
|
let response;
|
|
1489
1838
|
switch (req.method) {
|
|
1839
|
+
// initialize / notifications/initialized / ping are REMOVED in the modern
|
|
1840
|
+
// era. Under a modern request they are simply unknown methods; a legacy
|
|
1841
|
+
// request keeps the handshake it depends on.
|
|
1490
1842
|
case "initialize": {
|
|
1491
|
-
|
|
1843
|
+
if (era === "modern") {
|
|
1844
|
+
response = unknownMethod(id, req.method);
|
|
1845
|
+
break;
|
|
1846
|
+
}
|
|
1847
|
+
const problems = initializeParamsProblems(req.params);
|
|
1848
|
+
if (problems.length > 0) {
|
|
1849
|
+
response = rpcError(id, -32602, `Invalid params: ${problems.join("; ")}`);
|
|
1850
|
+
break;
|
|
1851
|
+
}
|
|
1852
|
+
const offered = req.params?.protocolVersion;
|
|
1853
|
+
const legacyServedVersions = servedVersions.filter(isLegacyProtocolVersion);
|
|
1854
|
+
const fallbackLegacyVersion = legacyServedVersions.at(-1) ?? (typeof surface.protocolVersion === "function" ? surface.protocolVersion(req.params) : surface.protocolVersion);
|
|
1855
|
+
const protocolVersion = isLegacyProtocolVersion(offered) && legacyServedVersions.includes(offered) ? offered : fallbackLegacyVersion;
|
|
1492
1856
|
response = reply(id, {
|
|
1493
1857
|
protocolVersion,
|
|
1494
1858
|
serverInfo: { name: surface.serverName ?? MCP_SERVER_NAME, version: MCP_SERVER_VERSION },
|
|
1495
|
-
capabilities:
|
|
1859
|
+
capabilities: SERVER_CAPABILITIES,
|
|
1496
1860
|
instructions: surface.instructions
|
|
1497
1861
|
});
|
|
1498
1862
|
break;
|
|
1499
1863
|
}
|
|
1500
1864
|
case "notifications/initialized":
|
|
1501
|
-
response = null;
|
|
1865
|
+
response = era === "modern" ? unknownMethod(id, req.method) : null;
|
|
1502
1866
|
break;
|
|
1503
1867
|
case "ping":
|
|
1504
|
-
response = reply(id, {});
|
|
1868
|
+
response = era === "modern" ? unknownMethod(id, req.method) : reply(id, {});
|
|
1869
|
+
break;
|
|
1870
|
+
case "server/discover":
|
|
1871
|
+
response = era === "modern" ? reply(id, discoverResult(surface, servedVersions)) : unknownMethod(id, req.method);
|
|
1505
1872
|
break;
|
|
1506
1873
|
case "tools/list":
|
|
1507
1874
|
response = reply(id, { tools: surface.tools });
|
|
@@ -1524,7 +1891,8 @@ async function dispatchMcpRequest(req, context, surface) {
|
|
|
1524
1891
|
const args = req.params.arguments ?? {};
|
|
1525
1892
|
const problems = validateMcpToolArguments(tool, args);
|
|
1526
1893
|
if (problems.length > 0) {
|
|
1527
|
-
|
|
1894
|
+
const message2 = `Invalid arguments for ${name}: ${problems.join("; ")}`;
|
|
1895
|
+
response = usesToolErrorForInvalidArguments(version) ? toolResult(id, { ok: false, error: { code: "INVALID_ARGUMENTS", message: message2 } }, true) : rpcError(id, -32602, message2);
|
|
1528
1896
|
break;
|
|
1529
1897
|
}
|
|
1530
1898
|
response = await surface.handleToolCall(id, { ...req.params, arguments: args }, context);
|
|
@@ -1537,9 +1905,31 @@ async function dispatchMcpRequest(req, context, surface) {
|
|
|
1537
1905
|
response = reply(id, { resources: [] });
|
|
1538
1906
|
break;
|
|
1539
1907
|
default:
|
|
1540
|
-
response =
|
|
1908
|
+
response = unknownMethod(id, req.method);
|
|
1909
|
+
}
|
|
1910
|
+
return notification ? null : decorateMcpResult(response, req.method, era, surface.serverName ?? MCP_SERVER_NAME);
|
|
1911
|
+
}
|
|
1912
|
+
var LIST_RESULT_TTL_MS = 3e5;
|
|
1913
|
+
var CACHEABLE_METHODS = /* @__PURE__ */ new Set(["server/discover", "tools/list", "prompts/list", "resources/list"]);
|
|
1914
|
+
function decorateMcpResult(response, method, era, serverName) {
|
|
1915
|
+
if (era !== "modern" || !response || !plainJsonObject(response.result)) return response;
|
|
1916
|
+
const existingMeta = plainJsonObject(response.result._meta) ? response.result._meta : {};
|
|
1917
|
+
const result = {
|
|
1918
|
+
resultType: "complete",
|
|
1919
|
+
...response.result,
|
|
1920
|
+
_meta: {
|
|
1921
|
+
...existingMeta,
|
|
1922
|
+
[META_SERVER_INFO]: { name: serverName, version: MCP_SERVER_VERSION }
|
|
1923
|
+
}
|
|
1924
|
+
};
|
|
1925
|
+
if (CACHEABLE_METHODS.has(method)) {
|
|
1926
|
+
result.ttlMs = LIST_RESULT_TTL_MS;
|
|
1927
|
+
result.cacheScope = "public";
|
|
1541
1928
|
}
|
|
1542
|
-
return
|
|
1929
|
+
return { ...response, result };
|
|
1930
|
+
}
|
|
1931
|
+
function unknownMethod(id, method) {
|
|
1932
|
+
return rpcError(id, -32601, `Method not found: ${method}`);
|
|
1543
1933
|
}
|
|
1544
1934
|
function createExecuteTool(options) {
|
|
1545
1935
|
const hostedNote = options.hosted ? `Hosted note: execute runs in an on-demand isolate and costs more than the direct
|
|
@@ -2656,6 +3046,7 @@ import {
|
|
|
2656
3046
|
readFileSync,
|
|
2657
3047
|
readdirSync,
|
|
2658
3048
|
renameSync,
|
|
3049
|
+
rmdirSync,
|
|
2659
3050
|
unlinkSync,
|
|
2660
3051
|
writeFileSync,
|
|
2661
3052
|
writeSync
|
|
@@ -2669,7 +3060,12 @@ var DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
|
2669
3060
|
var MANIFEST_NAME = ".agentic-mermaid-artifacts-v1.json";
|
|
2670
3061
|
var LOCK_NAME = ".agentic-mermaid-artifacts-v1.lock";
|
|
2671
3062
|
var MAX_MANIFEST_BYTES = 1024 * 1024;
|
|
3063
|
+
var MAX_LOCK_BYTES = 4096;
|
|
2672
3064
|
var MANAGED_NAME = /^[0-9a-z]+-[0-9a-f-]{36}\.[a-z0-9_-]+$/;
|
|
3065
|
+
var IMPLICIT_NAMESPACE = /^store-[1-9][0-9]*-[0-9a-f-]{36}$/;
|
|
3066
|
+
var LEGACY_IMPLICIT_NAMESPACE = /^agentic-mermaid-mcp-artifacts-[1-9][0-9]*-[0-9a-f-]{36}$/;
|
|
3067
|
+
var IMPLICIT_REAP_GRACE_MS = 3e4;
|
|
3068
|
+
var DEFAULT_ARTIFACT_ROOT = join(tmpdir(), "agentic-mermaid-mcp-artifacts");
|
|
2673
3069
|
function positiveSafeInteger(value, field) {
|
|
2674
3070
|
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`artifact ${field} must be a positive safe integer`);
|
|
2675
3071
|
return value;
|
|
@@ -2689,7 +3085,9 @@ var ArtifactStore = class {
|
|
|
2689
3085
|
lockFd = -1;
|
|
2690
3086
|
closed = false;
|
|
2691
3087
|
constructor(opts = {}) {
|
|
2692
|
-
|
|
3088
|
+
if (opts.dir !== void 0 && opts.namespaceRoot !== void 0) {
|
|
3089
|
+
throw new Error("artifact dir and namespaceRoot are mutually exclusive");
|
|
3090
|
+
}
|
|
2693
3091
|
this.baseUrl = normalizeBaseUrl(opts.baseUrl);
|
|
2694
3092
|
this.maxBytes = positiveSafeInteger(opts.maxBytes ?? DEFAULT_MAX_BYTES, "maxBytes");
|
|
2695
3093
|
this.maxTotalBytes = positiveSafeInteger(opts.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES, "maxTotalBytes");
|
|
@@ -2697,7 +3095,23 @@ var ArtifactStore = class {
|
|
|
2697
3095
|
this.ttlMs = positiveSafeInteger(opts.ttlMs ?? DEFAULT_TTL_MS, "ttlMs");
|
|
2698
3096
|
if (this.maxBytes > this.maxTotalBytes) throw new Error("artifact maxBytes must not exceed maxTotalBytes");
|
|
2699
3097
|
this.now = opts.now ?? (() => Date.now());
|
|
2700
|
-
|
|
3098
|
+
if (opts.dir !== void 0) {
|
|
3099
|
+
this.dir = resolve(opts.dir);
|
|
3100
|
+
mkdirSync(this.dir, { recursive: true, mode: 448 });
|
|
3101
|
+
} else {
|
|
3102
|
+
const root = resolve(opts.namespaceRoot ?? DEFAULT_ARTIFACT_ROOT);
|
|
3103
|
+
mkdirSync(root, { recursive: true, mode: 448 });
|
|
3104
|
+
const rootStat = lstatSync(root);
|
|
3105
|
+
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
|
|
3106
|
+
throw new Error("artifact namespace root must be a real directory");
|
|
3107
|
+
}
|
|
3108
|
+
if (opts.namespaceRoot === void 0) {
|
|
3109
|
+
reapImplicitArtifactNamespaces(resolve(tmpdir()), this.now(), LEGACY_IMPLICIT_NAMESPACE);
|
|
3110
|
+
}
|
|
3111
|
+
reapImplicitArtifactNamespaces(root, this.now());
|
|
3112
|
+
this.dir = join(root, `store-${process.pid}-${randomUUID()}`);
|
|
3113
|
+
mkdirSync(this.dir, { mode: 448 });
|
|
3114
|
+
}
|
|
2701
3115
|
this.manifestPath = join(this.dir, MANIFEST_NAME);
|
|
2702
3116
|
this.lockPath = join(this.dir, LOCK_NAME);
|
|
2703
3117
|
this.acquireOwnership();
|
|
@@ -2900,7 +3314,7 @@ var ArtifactStore = class {
|
|
|
2900
3314
|
}
|
|
2901
3315
|
let totalBytes = 0;
|
|
2902
3316
|
for (const candidate of manifest.records) {
|
|
2903
|
-
if (!
|
|
3317
|
+
if (!isManifestRecord(candidate)) {
|
|
2904
3318
|
throw new Error("artifact manifest contains an invalid record");
|
|
2905
3319
|
}
|
|
2906
3320
|
const path = safePath(this.dir, candidate.name);
|
|
@@ -2941,6 +3355,113 @@ var ArtifactStore = class {
|
|
|
2941
3355
|
if (persist) this.persistManifest();
|
|
2942
3356
|
}
|
|
2943
3357
|
};
|
|
3358
|
+
function reapImplicitArtifactNamespaces(root, now, namespacePattern = IMPLICIT_NAMESPACE) {
|
|
3359
|
+
let entries;
|
|
3360
|
+
try {
|
|
3361
|
+
entries = readdirSync(root);
|
|
3362
|
+
} catch {
|
|
3363
|
+
return;
|
|
3364
|
+
}
|
|
3365
|
+
for (const name of entries) {
|
|
3366
|
+
if (!namespacePattern.test(name)) continue;
|
|
3367
|
+
const dir = safePath(root, name);
|
|
3368
|
+
try {
|
|
3369
|
+
const namespace = lstatSync(dir);
|
|
3370
|
+
if (!namespace.isDirectory() || namespace.isSymbolicLink()) continue;
|
|
3371
|
+
if (now - namespace.mtimeMs < IMPLICIT_REAP_GRACE_MS) continue;
|
|
3372
|
+
const lockPath = join(dir, LOCK_NAME);
|
|
3373
|
+
if (existsSync(lockPath)) {
|
|
3374
|
+
const lock = lstatSync(lockPath);
|
|
3375
|
+
if (!lock.isFile() || lock.isSymbolicLink() || lock.size > MAX_LOCK_BYTES) continue;
|
|
3376
|
+
const marker = JSON.parse(readFileSync(lockPath, "utf8"));
|
|
3377
|
+
if (marker.host !== hostname() || !Number.isSafeInteger(marker.pid) || marker.pid <= 0) continue;
|
|
3378
|
+
if (processIsAlive(marker.pid)) continue;
|
|
3379
|
+
const current = lstatSync(lockPath);
|
|
3380
|
+
if (current.dev !== lock.dev || current.ino !== lock.ino) continue;
|
|
3381
|
+
unlinkSync(lockPath);
|
|
3382
|
+
}
|
|
3383
|
+
reapClosedNamespace(dir, now);
|
|
3384
|
+
} catch {
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
function processIsAlive(pid) {
|
|
3389
|
+
try {
|
|
3390
|
+
process.kill(pid, 0);
|
|
3391
|
+
return true;
|
|
3392
|
+
} catch (error) {
|
|
3393
|
+
return error.code !== "ESRCH";
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
function reapClosedNamespace(dir, now) {
|
|
3397
|
+
const manifestPath = join(dir, MANIFEST_NAME);
|
|
3398
|
+
let records = [];
|
|
3399
|
+
if (existsSync(manifestPath)) {
|
|
3400
|
+
const stat = lstatSync(manifestPath);
|
|
3401
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_MANIFEST_BYTES) return;
|
|
3402
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
3403
|
+
if (manifest?.schemaVersion !== 1 || !Array.isArray(manifest.records) || manifest.records.some((record) => !isManifestRecord(record))) return;
|
|
3404
|
+
records = manifest.records;
|
|
3405
|
+
}
|
|
3406
|
+
const live = [];
|
|
3407
|
+
for (const record of records) {
|
|
3408
|
+
const path = safePath(dir, record.name);
|
|
3409
|
+
if (record.expiresAt <= now) {
|
|
3410
|
+
try {
|
|
3411
|
+
unlinkSync(path);
|
|
3412
|
+
} catch {
|
|
3413
|
+
}
|
|
3414
|
+
continue;
|
|
3415
|
+
}
|
|
3416
|
+
try {
|
|
3417
|
+
const file = lstatSync(path);
|
|
3418
|
+
if (file.isFile() && !file.isSymbolicLink() && file.size === record.bytes) live.push(record);
|
|
3419
|
+
else if (file.isFile() || file.isSymbolicLink()) unlinkSync(path);
|
|
3420
|
+
} catch {
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
const liveNames = new Set(live.map((record) => record.name));
|
|
3424
|
+
for (const name of readdirSync(dir)) {
|
|
3425
|
+
if (!MANAGED_NAME.test(name) || liveNames.has(name)) continue;
|
|
3426
|
+
const path = safePath(dir, name);
|
|
3427
|
+
try {
|
|
3428
|
+
const file = lstatSync(path);
|
|
3429
|
+
if (file.isFile() || file.isSymbolicLink()) unlinkSync(path);
|
|
3430
|
+
} catch {
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
if (live.length > 0) {
|
|
3434
|
+
if (live.length !== records.length) persistManifestRecords(dir, live);
|
|
3435
|
+
return;
|
|
3436
|
+
}
|
|
3437
|
+
try {
|
|
3438
|
+
const manifest = lstatSync(manifestPath);
|
|
3439
|
+
if (manifest.isFile() && !manifest.isSymbolicLink()) unlinkSync(manifestPath);
|
|
3440
|
+
} catch {
|
|
3441
|
+
}
|
|
3442
|
+
try {
|
|
3443
|
+
rmdirSync(dir);
|
|
3444
|
+
} catch {
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
function persistManifestRecords(dir, records) {
|
|
3448
|
+
const path = join(dir, MANIFEST_NAME);
|
|
3449
|
+
const temp = join(dir, `.${MANIFEST_NAME}.${process.pid}.${randomUUID()}.tmp`);
|
|
3450
|
+
try {
|
|
3451
|
+
writeFileSync(temp, JSON.stringify({ schemaVersion: 1, records }), { mode: 384 });
|
|
3452
|
+
renameSync(temp, path);
|
|
3453
|
+
} finally {
|
|
3454
|
+
try {
|
|
3455
|
+
unlinkSync(temp);
|
|
3456
|
+
} catch {
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
function isManifestRecord(candidate) {
|
|
3461
|
+
if (!candidate || typeof candidate !== "object") return false;
|
|
3462
|
+
const record = candidate;
|
|
3463
|
+
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;
|
|
3464
|
+
}
|
|
2944
3465
|
function createArtifactStore(opts = {}) {
|
|
2945
3466
|
return new ArtifactStore(opts);
|
|
2946
3467
|
}
|
|
@@ -2967,6 +3488,9 @@ function safePath(root, name) {
|
|
|
2967
3488
|
|
|
2968
3489
|
// src/mcp/server.ts
|
|
2969
3490
|
var PROTOCOL_VERSION = "2024-11-05";
|
|
3491
|
+
var STDIO_PROTOCOL_VERSIONS = ["2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25", "2026-07-28"];
|
|
3492
|
+
var HTTP_SSE_PROTOCOL_VERSIONS = ["2024-11-05"];
|
|
3493
|
+
var HTTP_SSE_DISPATCH = { supportedVersions: HTTP_SSE_PROTOCOL_VERSIONS };
|
|
2970
3494
|
var MAX_RPC_BODY_BYTES = 1024 * 1024;
|
|
2971
3495
|
var MAX_SANDBOX_TIMEOUT_MS = 3e4;
|
|
2972
3496
|
var MAX_SSE_SESSIONS = 32;
|
|
@@ -2989,12 +3513,21 @@ var MCP_NARROWERS = BUILTIN_FAMILY_METADATA.map((f) => f.narrower).join("/");
|
|
|
2989
3513
|
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
3514
|
var LOCAL_SURFACE = {
|
|
2991
3515
|
protocolVersion: PROTOCOL_VERSION,
|
|
3516
|
+
supportedVersions: STDIO_PROTOCOL_VERSIONS,
|
|
2992
3517
|
tools: LOCAL_TOOLS,
|
|
2993
3518
|
instructions: LOCAL_INSTRUCTIONS,
|
|
2994
3519
|
handleToolCall
|
|
2995
3520
|
};
|
|
2996
|
-
|
|
2997
|
-
return
|
|
3521
|
+
function admitLocalRequest(req, options = {}) {
|
|
3522
|
+
return admitMcpMessage(req, {
|
|
3523
|
+
supportedVersions: surfaceSupportedVersions(LOCAL_SURFACE, options),
|
|
3524
|
+
negotiatedVersion: options.protocolVersion,
|
|
3525
|
+
protocolEra: options.protocolEra
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
3528
|
+
async function handleRequest(req, context = {}, options = {}) {
|
|
3529
|
+
const admission = admitLocalRequest(req, options);
|
|
3530
|
+
return admission.ok ? dispatchAdmittedMcpRequest(admission.message, context, LOCAL_SURFACE) : admission.response;
|
|
2998
3531
|
}
|
|
2999
3532
|
async function handleToolCall(id, params, context) {
|
|
3000
3533
|
const p = params;
|
|
@@ -3075,7 +3608,7 @@ function artifactPayload(artifact, output) {
|
|
|
3075
3608
|
return { ...base, path: artifact.path };
|
|
3076
3609
|
}
|
|
3077
3610
|
function getDefaultArtifactStore() {
|
|
3078
|
-
defaultArtifactStore ??= createArtifactStore(
|
|
3611
|
+
defaultArtifactStore ??= createArtifactStore();
|
|
3079
3612
|
if (!defaultArtifactStoreExitHook) {
|
|
3080
3613
|
defaultArtifactStoreExitHook = true;
|
|
3081
3614
|
process.once("exit", () => defaultArtifactStore?.close());
|
|
@@ -3088,6 +3621,112 @@ function warmUpPngRenderer() {
|
|
|
3088
3621
|
} catch {
|
|
3089
3622
|
}
|
|
3090
3623
|
}
|
|
3624
|
+
function stdioCancellationTarget(value) {
|
|
3625
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3626
|
+
const request = value;
|
|
3627
|
+
if (request.jsonrpc !== "2.0" || request.method !== "notifications/cancelled" || Object.prototype.hasOwnProperty.call(request, "id")) return void 0;
|
|
3628
|
+
const params = request.params;
|
|
3629
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) return void 0;
|
|
3630
|
+
const requestId = params.requestId;
|
|
3631
|
+
return typeof requestId === "string" || typeof requestId === "number" && Number.isSafeInteger(requestId) ? requestId : void 0;
|
|
3632
|
+
}
|
|
3633
|
+
function stdioRequestId(value) {
|
|
3634
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3635
|
+
const request = value;
|
|
3636
|
+
if (request.jsonrpc !== "2.0" || typeof request.method !== "string" || !Object.prototype.hasOwnProperty.call(request, "id")) return void 0;
|
|
3637
|
+
const requestId = request.id;
|
|
3638
|
+
return typeof requestId === "string" || typeof requestId === "number" && Number.isSafeInteger(requestId) ? requestId : void 0;
|
|
3639
|
+
}
|
|
3640
|
+
function stdioRequestKey(id) {
|
|
3641
|
+
return `${typeof id}:${String(id)}`;
|
|
3642
|
+
}
|
|
3643
|
+
function createStdioMessageProcessor(context, write, dispatch = dispatchAdmittedMcpRequest) {
|
|
3644
|
+
let negotiatedProtocolVersion = null;
|
|
3645
|
+
let negotiatedEra = null;
|
|
3646
|
+
let admissionGate = Promise.resolve();
|
|
3647
|
+
const tasks = /* @__PURE__ */ new Set();
|
|
3648
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
3649
|
+
const register = (id, controller) => {
|
|
3650
|
+
if (id === void 0) return;
|
|
3651
|
+
const key = stdioRequestKey(id);
|
|
3652
|
+
const controllers = inFlight.get(key) ?? /* @__PURE__ */ new Set();
|
|
3653
|
+
controllers.add(controller);
|
|
3654
|
+
inFlight.set(key, controllers);
|
|
3655
|
+
};
|
|
3656
|
+
const unregister = (id, controller) => {
|
|
3657
|
+
if (id === void 0) return;
|
|
3658
|
+
const key = stdioRequestKey(id);
|
|
3659
|
+
const controllers = inFlight.get(key);
|
|
3660
|
+
controllers?.delete(controller);
|
|
3661
|
+
if (controllers?.size === 0) inFlight.delete(key);
|
|
3662
|
+
};
|
|
3663
|
+
const emit = (response, exactIds, signal) => {
|
|
3664
|
+
if (response && !signal?.aborted) write(stringifyJsonRpc(response, exactIds) + "\n");
|
|
3665
|
+
};
|
|
3666
|
+
const accept = (line) => {
|
|
3667
|
+
let exact;
|
|
3668
|
+
let request;
|
|
3669
|
+
try {
|
|
3670
|
+
exact = preserveExactJsonRpcIds(line);
|
|
3671
|
+
request = JSON.parse(exact.body);
|
|
3672
|
+
} catch (cause) {
|
|
3673
|
+
write(JSON.stringify(rpcError(null, -32700, `parse error: ${cause instanceof Error ? cause.message : String(cause)}`)) + "\n");
|
|
3674
|
+
return;
|
|
3675
|
+
}
|
|
3676
|
+
const cancellationTarget = stdioCancellationTarget(request);
|
|
3677
|
+
if (cancellationTarget !== void 0) {
|
|
3678
|
+
for (const controller2 of inFlight.get(stdioRequestKey(cancellationTarget)) ?? []) controller2.abort();
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
const requestId = stdioRequestId(request);
|
|
3682
|
+
const controller = new AbortController();
|
|
3683
|
+
register(requestId, controller);
|
|
3684
|
+
const preparation = admissionGate.then(async () => {
|
|
3685
|
+
const dispatchOptions = negotiatedEra === "legacy" ? { protocolEra: "legacy", protocolVersion: negotiatedProtocolVersion, supportedVersions: negotiatedProtocolVersion ? [negotiatedProtocolVersion] : void 0 } : negotiatedEra === "modern" ? { protocolEra: "modern", supportedVersions: STDIO_PROTOCOL_VERSIONS } : {};
|
|
3686
|
+
const admission = admitLocalRequest(request, dispatchOptions);
|
|
3687
|
+
if (admission.ok && negotiatedEra === null && request.method === "initialize") {
|
|
3688
|
+
const response = controller.signal.aborted ? null : await dispatch(admission.message, { ...context, signal: controller.signal }, LOCAL_SURFACE);
|
|
3689
|
+
if (!controller.signal.aborted && response?.result && typeof response.result === "object") {
|
|
3690
|
+
const selected = response.result.protocolVersion;
|
|
3691
|
+
if (typeof selected === "string" && STDIO_PROTOCOL_VERSIONS.includes(selected)) {
|
|
3692
|
+
negotiatedProtocolVersion = selected;
|
|
3693
|
+
negotiatedEra = "legacy";
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
emit(response, exact.ids, controller.signal);
|
|
3697
|
+
return { handled: true, admission };
|
|
3698
|
+
}
|
|
3699
|
+
if (admission.ok && negotiatedEra === null && admission.message.protocol.era === "modern") {
|
|
3700
|
+
negotiatedEra = "modern";
|
|
3701
|
+
}
|
|
3702
|
+
return { handled: false, admission };
|
|
3703
|
+
});
|
|
3704
|
+
admissionGate = preparation.then(() => void 0, () => void 0);
|
|
3705
|
+
let task;
|
|
3706
|
+
task = preparation.then(async (prepared) => {
|
|
3707
|
+
if (prepared.handled) return;
|
|
3708
|
+
const response = prepared.admission.ok ? controller.signal.aborted ? null : await dispatch(prepared.admission.message, { ...context, signal: controller.signal }, LOCAL_SURFACE) : prepared.admission.response;
|
|
3709
|
+
emit(response, exact.ids, controller.signal);
|
|
3710
|
+
}).catch((cause) => {
|
|
3711
|
+
emit(
|
|
3712
|
+
rpcError(requestId ?? null, -32603, `internal error: ${cause instanceof Error ? cause.message : String(cause)}`),
|
|
3713
|
+
exact.ids,
|
|
3714
|
+
controller.signal
|
|
3715
|
+
);
|
|
3716
|
+
}).finally(() => {
|
|
3717
|
+
unregister(requestId, controller);
|
|
3718
|
+
tasks.delete(task);
|
|
3719
|
+
});
|
|
3720
|
+
tasks.add(task);
|
|
3721
|
+
};
|
|
3722
|
+
return {
|
|
3723
|
+
accept,
|
|
3724
|
+
async drain() {
|
|
3725
|
+
await admissionGate;
|
|
3726
|
+
while (tasks.size > 0) await Promise.allSettled([...tasks]);
|
|
3727
|
+
}
|
|
3728
|
+
};
|
|
3729
|
+
}
|
|
3091
3730
|
async function runStdio(options = {}) {
|
|
3092
3731
|
warmUpPngRenderer();
|
|
3093
3732
|
const artifactStore = createArtifactStore({
|
|
@@ -3097,22 +3736,20 @@ async function runStdio(options = {}) {
|
|
|
3097
3736
|
maxArtifacts: options.maxArtifacts,
|
|
3098
3737
|
ttlMs: options.artifactTtlMs
|
|
3099
3738
|
});
|
|
3739
|
+
const processor = createStdioMessageProcessor(
|
|
3740
|
+
{ artifactStore, maxSandboxTimeoutMs: options.maxSandboxTimeoutMs },
|
|
3741
|
+
(line) => process.stdout.write(line)
|
|
3742
|
+
);
|
|
3100
3743
|
process.stdin.setEncoding("utf8");
|
|
3101
3744
|
let buf = "";
|
|
3102
|
-
process.stdin.on("data",
|
|
3745
|
+
process.stdin.on("data", (chunk) => {
|
|
3103
3746
|
buf += chunk;
|
|
3104
3747
|
let nl;
|
|
3105
3748
|
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
3106
3749
|
const line = buf.slice(0, nl).trim();
|
|
3107
3750
|
buf = buf.slice(nl + 1);
|
|
3108
3751
|
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
|
-
}
|
|
3752
|
+
processor.accept(line);
|
|
3116
3753
|
}
|
|
3117
3754
|
});
|
|
3118
3755
|
try {
|
|
@@ -3120,6 +3757,7 @@ async function runStdio(options = {}) {
|
|
|
3120
3757
|
process.stdin.on("end", () => resolve2());
|
|
3121
3758
|
process.stdin.on("close", () => resolve2());
|
|
3122
3759
|
});
|
|
3760
|
+
await processor.drain();
|
|
3123
3761
|
} finally {
|
|
3124
3762
|
artifactStore.close();
|
|
3125
3763
|
}
|
|
@@ -3288,7 +3926,7 @@ async function postSseMessage(req, res, sessions, context, maxBytes) {
|
|
|
3288
3926
|
} catch {
|
|
3289
3927
|
throw new HttpStatusError(400, "invalid JSON-RPC body");
|
|
3290
3928
|
}
|
|
3291
|
-
const response = await handleRequest(parsed, context);
|
|
3929
|
+
const response = await handleRequest(parsed, context, HTTP_SSE_DISPATCH);
|
|
3292
3930
|
if (response) {
|
|
3293
3931
|
sse.write(`event: message
|
|
3294
3932
|
data: ${stringifyJsonRpc(response, exact.ids)}
|
|
@@ -3308,7 +3946,7 @@ async function postRpc(req, res, context, maxBytes) {
|
|
|
3308
3946
|
} catch {
|
|
3309
3947
|
throw new HttpStatusError(400, "invalid JSON-RPC body");
|
|
3310
3948
|
}
|
|
3311
|
-
const response = await handleRequest(parsed, context);
|
|
3949
|
+
const response = await handleRequest(parsed, context, HTTP_SSE_DISPATCH);
|
|
3312
3950
|
if (response === null) {
|
|
3313
3951
|
res.writeHead(202);
|
|
3314
3952
|
res.end();
|
|
@@ -3482,4 +4120,4 @@ export {
|
|
|
3482
4120
|
parseMcpCliOptions,
|
|
3483
4121
|
runMcpCli
|
|
3484
4122
|
};
|
|
3485
|
-
//# sourceMappingURL=chunk-
|
|
4123
|
+
//# sourceMappingURL=chunk-NE6JBHSE.js.map
|