callman-mcp 0.6.0 → 0.7.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 +36 -59
- package/README.md +18 -24
- package/dist/client/api.d.ts +18 -40
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/api.js +7 -54
- package/dist/client/api.js.map +1 -1
- package/dist/client/types.d.ts +19 -44
- package/dist/client/types.d.ts.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -1
- package/dist/domain/diagram/build.d.ts +17 -17
- package/dist/domain/diagram/build.d.ts.map +1 -1
- package/dist/domain/diagram/build.js +3 -3
- package/dist/domain/diagram/build.js.map +1 -1
- package/dist/domain/diagram/layout.d.ts +1 -3
- package/dist/domain/diagram/layout.d.ts.map +1 -1
- package/dist/domain/diagram/layout.js +3 -9
- package/dist/domain/diagram/layout.js.map +1 -1
- package/dist/domain/diagram/patch.d.ts +22 -723
- package/dist/domain/diagram/patch.d.ts.map +1 -1
- package/dist/domain/diagram/patch.js +0 -53
- package/dist/domain/diagram/patch.js.map +1 -1
- package/dist/domain/diagram/summarize.d.ts +0 -1
- package/dist/domain/diagram/summarize.d.ts.map +1 -1
- package/dist/domain/diagram/summarize.js +0 -1
- package/dist/domain/diagram/summarize.js.map +1 -1
- package/dist/domain/requestIntegrations.d.ts +13 -468
- package/dist/domain/requestIntegrations.d.ts.map +1 -1
- package/dist/domain/requestIntegrations.js +4 -95
- package/dist/domain/requestIntegrations.js.map +1 -1
- package/dist/domain/requestShape.d.ts +37 -679
- package/dist/domain/requestShape.d.ts.map +1 -1
- package/dist/domain/requestShape.js +2 -2
- package/dist/domain/requestShape.js.map +1 -1
- package/dist/domain/scenario/build.d.ts +6 -6
- package/dist/domain/scenario/build.d.ts.map +1 -1
- package/dist/domain/scenario/build.js +17 -68
- package/dist/domain/scenario/build.js.map +1 -1
- package/dist/domain/scenario/nodeConfig.d.ts.map +1 -1
- package/dist/domain/scenario/nodeConfig.js +16 -0
- package/dist/domain/scenario/nodeConfig.js.map +1 -1
- package/dist/domain/scenario/summarize.d.ts +0 -1
- package/dist/domain/scenario/summarize.d.ts.map +1 -1
- package/dist/domain/scenario/summarize.js +6 -1
- package/dist/domain/scenario/summarize.js.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/resources/docs/collectionScripts.d.ts +2 -0
- package/dist/resources/docs/collectionScripts.d.ts.map +1 -0
- package/dist/resources/docs/collectionScripts.js +51 -0
- package/dist/resources/docs/collectionScripts.js.map +1 -0
- package/dist/resources/docs/diagramSchema.d.ts +1 -2
- package/dist/resources/docs/diagramSchema.d.ts.map +1 -1
- package/dist/resources/docs/diagramSchema.js +4 -63
- package/dist/resources/docs/diagramSchema.js.map +1 -1
- package/dist/resources/docs/overview.d.ts +1 -1
- package/dist/resources/docs/overview.d.ts.map +1 -1
- package/dist/resources/docs/overview.js +13 -34
- package/dist/resources/docs/overview.js.map +1 -1
- package/dist/resources/docs/requestSchema.d.ts +1 -1
- package/dist/resources/docs/requestSchema.d.ts.map +1 -1
- package/dist/resources/docs/requestSchema.js +11 -16
- package/dist/resources/docs/requestSchema.js.map +1 -1
- package/dist/resources/generated/aiPacks.d.ts +1 -1
- package/dist/resources/generated/aiPacks.js +6 -6
- package/dist/resources/generated/aiPacks.js.map +1 -1
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +5 -3
- package/dist/resources/index.js.map +1 -1
- package/dist/run/executeCollection.d.ts.map +1 -1
- package/dist/run/executeCollection.js +19 -3
- package/dist/run/executeCollection.js.map +1 -1
- package/dist/run/loadBundle.d.ts.map +1 -1
- package/dist/run/loadBundle.js +28 -3
- package/dist/run/loadBundle.js.map +1 -1
- package/dist/run/summarize.d.ts +4 -4
- package/dist/run/types.d.ts +5 -1
- package/dist/run/types.d.ts.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/catalog.d.ts.map +1 -1
- package/dist/tools/catalog.js +3 -11
- package/dist/tools/catalog.js.map +1 -1
- package/dist/tools/collections.d.ts.map +1 -1
- package/dist/tools/collections.js +137 -16
- package/dist/tools/collections.js.map +1 -1
- package/dist/tools/contracts.js +2 -2
- package/dist/tools/contracts.js.map +1 -1
- package/dist/tools/diagrams.d.ts.map +1 -1
- package/dist/tools/diagrams.js +42 -331
- package/dist/tools/diagrams.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -3
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/loadTest.d.ts +4 -0
- package/dist/tools/loadTest.d.ts.map +1 -0
- package/dist/tools/loadTest.js +299 -0
- package/dist/tools/loadTest.js.map +1 -0
- package/dist/tools/lookups.d.ts +1 -5
- package/dist/tools/lookups.d.ts.map +1 -1
- package/dist/tools/lookups.js +0 -41
- package/dist/tools/lookups.js.map +1 -1
- package/dist/tools/requests.js +3 -3
- package/dist/tools/requests.js.map +1 -1
- package/dist/tools/run.d.ts +18 -0
- package/dist/tools/run.d.ts.map +1 -1
- package/dist/tools/run.js +14 -5
- package/dist/tools/run.js.map +1 -1
- package/dist/tools/scenarios.d.ts +0 -4
- package/dist/tools/scenarios.d.ts.map +1 -1
- package/dist/tools/scenarios.js +17 -112
- package/dist/tools/scenarios.js.map +1 -1
- package/dist-cjs/client/api.js +7 -54
- package/dist-cjs/config.js +5 -0
- package/dist-cjs/domain/diagram/build.js +3 -3
- package/dist-cjs/domain/diagram/layout.js +3 -10
- package/dist-cjs/domain/diagram/patch.js +1 -54
- package/dist-cjs/domain/diagram/summarize.js +0 -1
- package/dist-cjs/domain/requestIntegrations.js +4 -95
- package/dist-cjs/domain/requestShape.js +2 -2
- package/dist-cjs/domain/scenario/build.js +17 -68
- package/dist-cjs/domain/scenario/nodeConfig.js +16 -0
- package/dist-cjs/domain/scenario/summarize.js +6 -1
- package/dist-cjs/resources/docs/collectionScripts.js +53 -0
- package/dist-cjs/resources/docs/diagramSchema.js +5 -65
- package/dist-cjs/resources/docs/overview.js +13 -34
- package/dist-cjs/resources/docs/requestSchema.js +11 -16
- package/dist-cjs/resources/generated/aiPacks.js +6 -6
- package/dist-cjs/resources/index.js +4 -2
- package/dist-cjs/run/executeCollection.js +18 -2
- package/dist-cjs/run/loadBundle.js +28 -3
- package/dist-cjs/server.js +2 -2
- package/dist-cjs/tools/catalog.js +3 -11
- package/dist-cjs/tools/collections.js +137 -16
- package/dist-cjs/tools/contracts.js +2 -2
- package/dist-cjs/tools/diagrams.js +37 -326
- package/dist-cjs/tools/index.js +4 -3
- package/dist-cjs/tools/loadTest.js +335 -0
- package/dist-cjs/tools/lookups.js +1 -46
- package/dist-cjs/tools/requests.js +3 -3
- package/dist-cjs/tools/run.js +17 -7
- package/dist-cjs/tools/scenarios.js +18 -114
- package/package.json +3 -3
|
@@ -16,7 +16,7 @@ const shared_js_1 = require("./shared.js");
|
|
|
16
16
|
const directionArg = zod_1.z.enum(["DOWN", "RIGHT", "UP", "LEFT"]).optional().describe("Layout flow direction (default DOWN)");
|
|
17
17
|
const sourceArg = zod_1.z
|
|
18
18
|
.object({
|
|
19
|
-
format: zod_1.z.enum(["mermaid", "bpmn"
|
|
19
|
+
format: zod_1.z.enum(["mermaid", "bpmn"]).describe('"mermaid": Mermaid text; "bpmn": a BPMN 2.0 XML file'),
|
|
20
20
|
text: zod_1.z
|
|
21
21
|
.string()
|
|
22
22
|
.min(1)
|
|
@@ -25,7 +25,7 @@ const sourceArg = zod_1.z
|
|
|
25
25
|
})
|
|
26
26
|
.strict()
|
|
27
27
|
.optional()
|
|
28
|
-
.describe("Create the diagram from Mermaid text
|
|
28
|
+
.describe("Create the diagram from Mermaid text or BPMN 2.0 XML instead of nodes/edges");
|
|
29
29
|
// A BPMN diagram keeps its truth in BPMN 2.0 XML: the model is derived from it.
|
|
30
30
|
const bpmnSource = (text) => ({ format: "bpmn", text, diagramType: "bpmn", grammar: callman_core_1.BPMN_GRAMMAR_VERSION });
|
|
31
31
|
// Typed bpmn.* nodes → a BPMN diagram: the model becomes XML, the XML becomes
|
|
@@ -36,83 +36,6 @@ const bpmnDocumentFromModel = (model, unpositionedNodeIds, base) => {
|
|
|
36
36
|
const document = (0, callman_core_1.applyDiagramOps)({ ...base, nodes: [], edges: [], source: undefined }, [{ op: "setSource", source: bpmnSource(xml), resync: true }], { now: now() }).doc;
|
|
37
37
|
return { document, layoutNote: unpositionedNodeIds.length > 0 ? `Written as BPMN 2.0 XML; ${unpositionedNodeIds.length} node(s) placed left to right after their predecessors.` : "Written as BPMN 2.0 XML with the positions given." };
|
|
38
38
|
};
|
|
39
|
-
// A database diagram keeps its truth in the JSON database model.
|
|
40
|
-
const databaseSource = (text) => ({ format: "database", text, diagramType: "database", grammar: callman_core_1.DATABASE_GRAMMAR_VERSION, valid: true });
|
|
41
|
-
const DATABASE_DIALECTS = ["postgres", "mysql", "mssql", "oracle", "mongo", "generic"];
|
|
42
|
-
const toolErrorFromDatabaseEdit = (error) => {
|
|
43
|
-
if (error instanceof callman_core_1.DatabaseEditError)
|
|
44
|
-
throw new errors_js_1.ToolInputError(error.code, error.message);
|
|
45
|
-
throw error;
|
|
46
|
-
};
|
|
47
|
-
// A database model → a database diagram: the model becomes the source, the
|
|
48
|
-
// tables are derived from it and laid out (unrelated tables packed in rows).
|
|
49
|
-
const databaseDocumentFromModel = async (model, base, keepPositions = false) => {
|
|
50
|
-
const applied = (0, callman_core_1.applyDiagramOps)({ ...base, ...(keepPositions ? {} : { nodes: [], edges: [] }), source: undefined }, [{ op: "setSource", source: databaseSource((0, callman_core_1.serializeDatabaseModel)(model)), resync: true }], { now: now() });
|
|
51
|
-
let document = applied.doc;
|
|
52
|
-
if (keepPositions) {
|
|
53
|
-
const fresh = applied.added.nodes.filter((id) => document.nodes.some((node) => node.id === id));
|
|
54
|
-
if (fresh.length > 0)
|
|
55
|
-
document = (0, layout_js_1.placeDatabaseNodes)(document, fresh);
|
|
56
|
-
return { document, layoutNote: fresh.length > 0 ? `${fresh.length} new table(s) placed to the right of the existing ones.` : "Positions kept." };
|
|
57
|
-
}
|
|
58
|
-
if (document.nodes.length === 0)
|
|
59
|
-
return { document, layoutNote: "An empty database diagram." };
|
|
60
|
-
const laid = await (0, layout_js_1.layoutDocument)(document, { request: callman_core_1.DATABASE_LAYOUT_REQUEST });
|
|
61
|
-
return { document: laid.document, layoutNote: laid.warning ?? `${document.nodes.length} table(s) laid out with ${laid.engine} (relationships left to right).` };
|
|
62
|
-
};
|
|
63
|
-
// Tables + relationships as the create tool takes them → a model.
|
|
64
|
-
const databaseModelFromInput = (tables, relationships, dialect) => {
|
|
65
|
-
const edits = [
|
|
66
|
-
...tables.map((table) => ({ kind: "addTable", table: { ...table, ...(table.columns ? { columns: table.columns.map((column) => ({ ...column, defaultValue: column.defaultValue ?? undefined, comment: column.comment ?? undefined })) } : {}) } })),
|
|
67
|
-
...relationships.map((relationship) => ({
|
|
68
|
-
kind: "addRelationship",
|
|
69
|
-
...(relationship.id ? { id: relationship.id } : {}),
|
|
70
|
-
source: relationship.source,
|
|
71
|
-
target: relationship.target,
|
|
72
|
-
...(relationship.cardinality ? { cardinality: relationship.cardinality } : {}),
|
|
73
|
-
...(relationship.kind ? { relationshipKind: relationship.kind } : {}),
|
|
74
|
-
...(relationship.name ? { name: relationship.name } : {}),
|
|
75
|
-
...(relationship.label ? { label: relationship.label } : {}),
|
|
76
|
-
})),
|
|
77
|
-
];
|
|
78
|
-
try {
|
|
79
|
-
return (0, callman_core_1.applyDatabaseEdits)((0, callman_core_1.emptyDatabaseModel)(dialect), edits).model;
|
|
80
|
-
}
|
|
81
|
-
catch (error) {
|
|
82
|
-
return toolErrorFromDatabaseEdit(error);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
// A database connection by id or name, in one workspace or across all.
|
|
86
|
-
const findDatabaseConnection = async (ctx, workspace, ref) => {
|
|
87
|
-
const wanted = ref.trim().toLowerCase();
|
|
88
|
-
const workspaces = await (0, lookups_js_1.workspacesForListing)(ctx, workspace);
|
|
89
|
-
const matches = [];
|
|
90
|
-
for (const ws of workspaces) {
|
|
91
|
-
for (const connection of await ctx.api.listConnections(ws.id, "database")) {
|
|
92
|
-
if (connection.id === ref.trim() || connection.name.trim().toLowerCase() === wanted)
|
|
93
|
-
matches.push({ ws, connection });
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
if (matches.length === 0)
|
|
97
|
-
throw new errors_js_1.ToolInputError("CONNECTION_NOT_FOUND", `No database connection "${ref}"${workspace ? ` in workspace ${workspace}` : " in your workspaces"}. list_connections shows them (type: database).`);
|
|
98
|
-
const exact = matches.filter((match) => match.connection.id === ref.trim());
|
|
99
|
-
if (exact.length === 1)
|
|
100
|
-
return exact[0];
|
|
101
|
-
if (matches.length > 1) {
|
|
102
|
-
throw new errors_js_1.ToolInputError("AMBIGUOUS_CONNECTION", `"${ref}" matches ${matches.length} database connections; pass its id or a \`workspace\`.`, {
|
|
103
|
-
candidates: matches.map((match) => ({ id: match.connection.id, name: match.connection.name, workspace: (0, lookups_js_1.workspaceTag)(match.ws) })),
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
return matches[0];
|
|
107
|
-
};
|
|
108
|
-
const schemaQuery = (options) => ({
|
|
109
|
-
...(options.schemas && options.schemas.length > 0 ? { schemas: options.schemas.join(",") } : {}),
|
|
110
|
-
...(options.tables && options.tables.length > 0 ? { tables: options.tables.join(",") } : {}),
|
|
111
|
-
...(options.includeViews !== undefined ? { includeViews: String(options.includeViews) } : {}),
|
|
112
|
-
...(options.objectsOnly ? { objectsOnly: "true" } : {}),
|
|
113
|
-
...(options.maxTables !== undefined ? { maxTables: options.maxTables } : {}),
|
|
114
|
-
});
|
|
115
|
-
const PROVIDER_LABEL = { postgresql: "PostgreSQL", mysql: "MySQL", mssql: "SQL Server", oracle: "Oracle", mongodb: "MongoDB" };
|
|
116
39
|
// Every document entering a tool goes through the shared gate (legacy fields
|
|
117
40
|
// stripped, ids deduped, parents healed) — the same one the studio applies.
|
|
118
41
|
const loadDocument = (diagram) => {
|
|
@@ -135,21 +58,6 @@ const assertEditableStructure = (doc) => {
|
|
|
135
58
|
// nodes/edges (positions come from layout); anything else is kept as
|
|
136
59
|
// source only (the desktop previews it, tools read it back).
|
|
137
60
|
const documentFromSource = async (title, text, existing, direction, format = "mermaid") => {
|
|
138
|
-
if (format === "database") {
|
|
139
|
-
const parsed = (0, callman_core_1.parseDatabaseModel)(text);
|
|
140
|
-
if (!parsed.ok)
|
|
141
|
-
throw new errors_js_1.ToolInputError("DIAGRAM_SOURCE_INVALID", "The text is not a readable database model.", { errors: parsed.errors.slice(0, 20) });
|
|
142
|
-
const base = existing ?? (0, callman_core_1.createEmptyDocument)(title);
|
|
143
|
-
const keep = Boolean(existing && existing.source?.format === "database");
|
|
144
|
-
let built;
|
|
145
|
-
try {
|
|
146
|
-
built = await databaseDocumentFromModel(parsed.model, keep ? existing : { ...base, nodes: [], edges: [] }, keep);
|
|
147
|
-
}
|
|
148
|
-
catch (error) {
|
|
149
|
-
return (0, build_js_1.toToolError)(error);
|
|
150
|
-
}
|
|
151
|
-
return { document: built.document, layoutNote: built.layoutNote, warnings: parsed.warnings };
|
|
152
|
-
}
|
|
153
61
|
if (format === "bpmn") {
|
|
154
62
|
if (!(0, callman_core_1.looksLikeBpmn)(text))
|
|
155
63
|
throw new errors_js_1.ToolInputError("DIAGRAM_SOURCE_INVALID", "The text is not a BPMN 2.0 document (no <definitions> root).");
|
|
@@ -219,59 +127,39 @@ const isConflict = (error) => error instanceof errors_js_1.CallmanApiError && er
|
|
|
219
127
|
const registerDiagramTools = (server, ctx) => {
|
|
220
128
|
server.registerTool("list_diagrams", {
|
|
221
129
|
title: "List diagrams",
|
|
222
|
-
description: "List your system-design diagrams and the ones shared with you (owner, node/edge counts, whether you can edit)
|
|
223
|
-
inputSchema: {
|
|
224
|
-
query: zod_1.z.string().max(200).optional(),
|
|
225
|
-
folder: zod_1.z.string().max(300).optional().describe('Only the diagrams in this folder of your library (id, name or path such as "Architecture/Payments"); "/" = the top level only'),
|
|
226
|
-
...shared_js_1.cursorArgs,
|
|
227
|
-
},
|
|
130
|
+
description: "List your system-design diagrams and the ones shared with you (owner, node/edge counts, whether you can edit). Diagrams belong to users and are shared into workspaces — not workspace-scoped.",
|
|
131
|
+
inputSchema: { query: zod_1.z.string().max(200).optional(), ...shared_js_1.cursorArgs },
|
|
228
132
|
annotations: shared_js_1.READ,
|
|
229
|
-
}, (0, shared_js_1.guarded)(async ({ query,
|
|
230
|
-
const
|
|
133
|
+
}, (0, shared_js_1.guarded)(async ({ query, cursor, limit }) => {
|
|
134
|
+
const all = await (0, lookups_js_1.loadDiagrams)(ctx);
|
|
231
135
|
const needle = query?.trim().toLowerCase();
|
|
232
|
-
const
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
return false;
|
|
236
|
-
if (scope === undefined)
|
|
237
|
-
return true;
|
|
238
|
-
return (diagram.folderId ?? null) === (scope.item?.id ?? null);
|
|
239
|
-
});
|
|
240
|
-
const page = (0, pagination_js_1.sliceWithCursor)(filtered, cursor, limit, `${needle ?? ""}|${scope?.item?.id ?? (scope ? "/" : "")}`);
|
|
241
|
-
return (0, shared_js_1.ok)({
|
|
242
|
-
diagrams: page.items.map((diagram) => ({ ...(0, summarize_js_1.diagramListView)(diagram), folder: (0, lookups_js_1.diagramFolderPath)(rows, diagram.folderId) })),
|
|
243
|
-
folders: rows.map((row) => ({ id: row.id, name: row.name, parentId: row.parentId, path: row.path })),
|
|
244
|
-
total: page.total,
|
|
245
|
-
nextCursor: page.nextCursor,
|
|
246
|
-
...(scope?.item && scope.annotation ? { resolved: [(0, resolve_js_1.resolvedSummary)(folder, scope)] } : {}),
|
|
247
|
-
});
|
|
136
|
+
const filtered = needle ? all.filter((diagram) => diagram.name.toLowerCase().includes(needle)) : all;
|
|
137
|
+
const page = (0, pagination_js_1.sliceWithCursor)(filtered, cursor, limit, needle ?? "");
|
|
138
|
+
return (0, shared_js_1.ok)({ diagrams: page.items.map(summarize_js_1.diagramListView), total: page.total, nextCursor: page.nextCursor });
|
|
248
139
|
}));
|
|
249
140
|
server.registerTool("get_diagram", {
|
|
250
141
|
title: "Get a diagram",
|
|
251
|
-
description: "Read a diagram: `summary` (counts, owner, sharing), `graph` (default — nodes with type/label/parent/style and edges with semantic/color/routing/sides, in the same shape the write tools accept: enough to describe, restyle or edit it), `mermaid` (the diagram rendered as Mermaid flowchart text), `source` (the Mermaid text
|
|
252
|
-
inputSchema: { diagram: (0, shared_js_1.refArg)("Diagram"), view: zod_1.z.enum(["summary", "graph", "mermaid", "source", "
|
|
142
|
+
description: "Read a diagram: `summary` (counts, owner, sharing), `graph` (default — nodes with type/label/parent/style and edges with semantic/color/routing/sides, in the same shape the write tools accept: enough to describe, restyle or edit it), `mermaid` (the diagram rendered as Mermaid flowchart text), `source` (the Mermaid text or BPMN 2.0 XML a source-backed diagram keeps) or `full` (the complete stored DiagramDocument with positions, sizes and routes, up to 512 KB).",
|
|
143
|
+
inputSchema: { diagram: (0, shared_js_1.refArg)("Diagram"), view: zod_1.z.enum(["summary", "graph", "mermaid", "source", "full"]).default("graph") },
|
|
253
144
|
annotations: shared_js_1.READ,
|
|
254
145
|
}, (0, shared_js_1.guarded)(async ({ diagram, view }) => {
|
|
255
146
|
const resolved = await (0, lookups_js_1.resolveDiagram)(ctx, diagram);
|
|
256
147
|
const doc = loadDocument(resolved.item);
|
|
257
148
|
const summary = (0, summarize_js_1.diagramListView)(resolved.item);
|
|
258
|
-
const summaryWithFolder = { ...summary, folder: (0, lookups_js_1.diagramFolderPath)(await (0, lookups_js_1.diagramFolderRows)(ctx), resolved.item.folderId) };
|
|
259
149
|
const body = view === "summary"
|
|
260
|
-
?
|
|
150
|
+
? summary
|
|
261
151
|
: view === "graph"
|
|
262
152
|
? { ...summary, ...(0, summarize_js_1.diagramGraphView)(doc) }
|
|
263
153
|
: view === "mermaid"
|
|
264
154
|
? { ...summary, ...(0, summarize_js_1.diagramMermaidView)(doc) }
|
|
265
155
|
: view === "source"
|
|
266
156
|
? { ...summary, ...(0, summarize_js_1.diagramSourceView)(doc) }
|
|
267
|
-
:
|
|
268
|
-
? { ...summary, schema: (0, callman_core_1.databaseSchemaView)(doc) }
|
|
269
|
-
: { ...summary, ...(0, summarize_js_1.diagramFullView)(doc) };
|
|
157
|
+
: { ...summary, ...(0, summarize_js_1.diagramFullView)(doc) };
|
|
270
158
|
return (0, shared_js_1.ok)({ diagram: body, ...(resolved.annotation ? { resolved: [(0, resolve_js_1.resolvedSummary)(diagram, resolved)] } : {}) });
|
|
271
159
|
}));
|
|
272
160
|
server.registerTool("create_diagram", {
|
|
273
|
-
title: "Create a system-design
|
|
274
|
-
description: "Create a diagram from typed nodes and edges — or from Mermaid text / BPMN 2.0 XML
|
|
161
|
+
title: "Create a system-design diagram",
|
|
162
|
+
description: "Create a diagram from typed nodes and edges — or from Mermaid text / BPMN 2.0 XML (`source`). A BPMN process (`kind: \"bpmn\"`, or nodes typed bpmn.*) is stored as BPMN 2.0 XML the desktop opens in its BPMN editor. Node `type` must come from callman://diagram/components (e.g. net.service, net.api-gateway, db.postgres, mq.kafka, aws.lambda, flow.process); unknown types are rejected with suggestions unless strictTypes: false. Group nodes inside a container node — frame, pool.horizontal or pool.vertical — with `parent`. Style nodes with `style` (fill / stroke / textColor as CSS colors, strokeWidth, dashed, radius, bold, fontSize, opacity…) and edges with `semantic` (sync default, async, fallback, bus), an optional `color`, `routing` and fixed `sourceSide`/`targetSide`. Layout is automatic when nodes have no positions. Read callman://docs/diagram-schema for every field. Keep it readable: 8–25 nodes, short labels.",
|
|
275
163
|
inputSchema: {
|
|
276
164
|
name: zod_1.z.string().min(1).max(200).describe("Diagram name (library entry)"),
|
|
277
165
|
title: build_js_1.diagramInputSchema.shape.title.optional().describe("Diagram title (defaults to the name)"),
|
|
@@ -279,76 +167,28 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
279
167
|
edges: build_js_1.diagramInputSchema.shape.edges.optional(),
|
|
280
168
|
canvas: build_js_1.diagramInputSchema.shape.canvas,
|
|
281
169
|
source: sourceArg,
|
|
282
|
-
kind: zod_1.z.enum(["canvas", "bpmn"
|
|
283
|
-
tables: zod_1.z.array(patch_js_1.dbTableInputSchema).max(500).optional().describe("Database diagram: the tables (name, schema?, kind?, columns with dataType / primaryKey / nullable / unique / defaultValue / comment)"),
|
|
284
|
-
relationships: zod_1.z.array(patch_js_1.dbRelationshipInputSchema).max(2000).optional().describe('Database diagram: relationships between the tables — source is the child (foreign-key) side as "table.column", target the parent; cardinality source:target (default N:1)'),
|
|
285
|
-
dialect: zod_1.z.enum(DATABASE_DIALECTS).optional().describe("Database diagram: which database the types are written for"),
|
|
286
|
-
fromConnection: zod_1.z
|
|
287
|
-
.object({
|
|
288
|
-
connection: zod_1.z.string().min(1).max(200).describe("A database connection by id or name (list_connections, type: database)"),
|
|
289
|
-
schemas: zod_1.z.array(zod_1.z.string().min(1).max(120)).max(50).optional().describe("Schemas to read (default: every user schema; MySQL: the connection's database; Oracle: the current schema)"),
|
|
290
|
-
tables: zod_1.z.array(zod_1.z.string().min(1).max(240)).max(2000).optional().describe('Only these tables ("schema.table" or "table"); default: all'),
|
|
291
|
-
includeViews: zod_1.z.boolean().optional(),
|
|
292
|
-
})
|
|
293
|
-
.strict()
|
|
294
|
-
.optional()
|
|
295
|
-
.describe("Database diagram: read a real database's schema through one of the workspace's connections and draw it — tables, columns, keys and the relationships the database declares"),
|
|
296
|
-
workspace: shared_js_1.workspaceArg,
|
|
297
|
-
folder: zod_1.z.string().max(300).optional().describe('File it in a folder of your library (id, name or path such as "Architecture/Payments"; create one with create_diagram_folder). Default: the top level'),
|
|
170
|
+
kind: zod_1.z.enum(["canvas", "bpmn"]).optional().describe('"bpmn": model a business process — nodes use bpmn.* types and the diagram is stored as BPMN 2.0 XML (default: canvas; inferred as bpmn when every node type starts with "bpmn.")'),
|
|
298
171
|
autoLayout: zod_1.z.boolean().optional().describe("Force layout even when positions were given"),
|
|
299
172
|
direction: directionArg,
|
|
300
173
|
strictTypes: zod_1.z.boolean().optional(),
|
|
301
174
|
dryRun: shared_js_1.dryRunArg,
|
|
302
175
|
},
|
|
303
176
|
annotations: shared_js_1.WRITE,
|
|
304
|
-
}, (0, shared_js_1.guarded)(async ({ name, title, nodes, edges, canvas, source, kind,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (
|
|
308
|
-
throw new errors_js_1.ToolInputError("
|
|
309
|
-
if (inputs.length > 1)
|
|
310
|
-
throw new errors_js_1.ToolInputError("CONFLICTING_INPUT", `Pass one of nodes, source, tables or fromConnection — not ${inputs.join(" + ")}.`);
|
|
311
|
-
if (relationships && !tables)
|
|
312
|
-
throw new errors_js_1.ToolInputError("CONFLICTING_INPUT", "relationships go with tables.");
|
|
313
|
-
if (kind === "mermaid" && source?.format !== "mermaid") {
|
|
314
|
-
throw new errors_js_1.ToolInputError("MERMAID_NEEDS_SOURCE", 'A Mermaid diagram is created from source: { format: "mermaid", text } — a flowchart becomes nodes, any other Mermaid type is stored as its source.');
|
|
315
|
-
}
|
|
177
|
+
}, (0, shared_js_1.guarded)(async ({ name, title, nodes, edges, canvas, source, kind, autoLayout, direction, strictTypes, dryRun }) => {
|
|
178
|
+
if (!nodes && !source)
|
|
179
|
+
throw new errors_js_1.ToolInputError("NOTHING_TO_CREATE", "Pass nodes (+ edges) or source { format: \"mermaid\" | \"bpmn\", text }.");
|
|
180
|
+
if (nodes && source)
|
|
181
|
+
throw new errors_js_1.ToolInputError("CONFLICTING_INPUT", "Pass either nodes or source, not both.");
|
|
316
182
|
let document;
|
|
317
183
|
let layoutNote;
|
|
318
184
|
let warnings = [];
|
|
319
185
|
const bpmnKind = kind === "bpmn" || (kind === undefined && Boolean(nodes && nodes.length > 0 && nodes.every((node) => node.type.startsWith("bpmn."))));
|
|
320
|
-
const databaseKind = kind === "database" || Boolean(tables) || Boolean(fromConnection) || (kind === undefined && Boolean(nodes && nodes.length > 0 && nodes.every((node) => (0, callman_core_1.isDatabaseType)(node.type))));
|
|
321
|
-
let connectionNote = {};
|
|
322
186
|
if (source) {
|
|
323
187
|
const fromSource = await documentFromSource(title ?? name, source.text, null, direction, source.format);
|
|
324
188
|
document = canvas ? { ...fromSource.document, canvas: { ...fromSource.document.canvas, ...canvas } } : fromSource.document;
|
|
325
189
|
layoutNote = fromSource.layoutNote;
|
|
326
190
|
warnings = fromSource.warnings;
|
|
327
191
|
}
|
|
328
|
-
else if (fromConnection) {
|
|
329
|
-
const found = await findDatabaseConnection(ctx, workspace, fromConnection.connection);
|
|
330
|
-
const schema = await ctx.api.getConnectionSchema(found.ws.id, found.connection.id, schemaQuery(fromConnection));
|
|
331
|
-
const model = (0, callman_core_1.databaseModelFromSchema)(schema, { connection: { connectionId: found.connection.id, name: found.connection.name, provider: found.connection.provider } });
|
|
332
|
-
const built = await databaseDocumentFromModel(model, (0, callman_core_1.createEmptyDocument)(title ?? name));
|
|
333
|
-
document = built.document;
|
|
334
|
-
layoutNote = built.layoutNote;
|
|
335
|
-
warnings = [...schema.warnings, ...(schema.truncated ? ["The database has more tables than the cap; the list was cut."] : [])];
|
|
336
|
-
connectionNote = { connection: { id: found.connection.id, name: found.connection.name, provider: PROVIDER_LABEL[found.connection.provider] ?? found.connection.provider, workspace: (0, lookups_js_1.workspaceTag)(found.ws) }, tables: model.entities.length, relationships: model.relationships.length };
|
|
337
|
-
}
|
|
338
|
-
else if (tables) {
|
|
339
|
-
const model = databaseModelFromInput(tables, relationships ?? [], dialect);
|
|
340
|
-
const built = await databaseDocumentFromModel(model, (0, callman_core_1.createEmptyDocument)(title ?? name));
|
|
341
|
-
document = built.document;
|
|
342
|
-
layoutNote = built.layoutNote;
|
|
343
|
-
}
|
|
344
|
-
else if (databaseKind) {
|
|
345
|
-
const built = (0, build_js_1.buildDiagramDocument)({ title: title ?? name, nodes: nodes ?? [], edges: edges ?? [], canvas }, { strictTypes, kind: "database" });
|
|
346
|
-
const model = (0, callman_core_1.databaseModelFromDocument)({ ...built.document, source: undefined }, dialect);
|
|
347
|
-
const database = await databaseDocumentFromModel(model, (0, callman_core_1.createEmptyDocument)(title ?? name));
|
|
348
|
-
document = database.document;
|
|
349
|
-
layoutNote = database.layoutNote;
|
|
350
|
-
warnings = built.warnings;
|
|
351
|
-
}
|
|
352
192
|
else if (bpmnKind) {
|
|
353
193
|
const built = (0, build_js_1.buildDiagramDocument)({ title: title ?? name, nodes: nodes ?? [], edges: edges ?? [], canvas }, { strictTypes, kind: "bpmn" });
|
|
354
194
|
const bpmn = bpmnDocumentFromModel(built.document, built.unpositionedNodeIds, (0, callman_core_1.createEmptyDocument)(title ?? name));
|
|
@@ -366,26 +206,21 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
366
206
|
layoutNote = laid.warning ?? `Laid out with ${laid.engine}.`;
|
|
367
207
|
}
|
|
368
208
|
}
|
|
369
|
-
if (document.source?.format === "database" && canvas && !source)
|
|
370
|
-
document = { ...document, canvas: { ...document.canvas, ...canvas } };
|
|
371
|
-
const view = document.source?.format === "database" ? { ...(0, summarize_js_1.diagramGraphView)(document), schema: (0, callman_core_1.databaseSchemaView)(document) } : (0, summarize_js_1.diagramGraphView)(document);
|
|
372
209
|
if (dryRun) {
|
|
373
|
-
return (0, shared_js_1.ok)({ dryRun: true, diagram: { name,
|
|
210
|
+
return (0, shared_js_1.ok)({ dryRun: true, diagram: { name, ...(0, summarize_js_1.diagramGraphView)(document) }, warnings, ...(layoutNote ? { layout: layoutNote } : {}) });
|
|
374
211
|
}
|
|
375
|
-
const created = await ctx.api.createDiagram({ name: name.trim(), document
|
|
212
|
+
const created = await ctx.api.createDiagram({ name: name.trim(), document });
|
|
376
213
|
ctx.invalidate("diagrams");
|
|
377
214
|
return (0, shared_js_1.ok)({
|
|
378
|
-
diagram: { ...(0, summarize_js_1.diagramListView)(created),
|
|
215
|
+
diagram: { ...(0, summarize_js_1.diagramListView)(created), ...(0, summarize_js_1.diagramGraphView)(document) },
|
|
379
216
|
warnings,
|
|
380
|
-
...connectionNote,
|
|
381
217
|
...(layoutNote ? { layout: layoutNote } : {}),
|
|
382
|
-
|
|
383
|
-
note: `Saved${targetFolder ? ` in ${targetFolder.item.path}` : ""}. Open it in the desktop app (Diagrams → Refresh). Edit with patch_diagram; share with share_diagram.`,
|
|
218
|
+
note: "Saved. Open it in the desktop app (Diagrams → Refresh). Edit with patch_diagram; share with share_diagram.",
|
|
384
219
|
});
|
|
385
220
|
}));
|
|
386
221
|
server.registerTool("update_diagram", {
|
|
387
222
|
title: "Replace a diagram's content",
|
|
388
|
-
description: "Replace a diagram's whole document (nodes + edges, same fields as create_diagram), replace or attach its Mermaid text
|
|
223
|
+
description: "Replace a diagram's whole document (nodes + edges, same fields as create_diagram), replace or attach its Mermaid text or BPMN 2.0 XML `source`, and/or rename it, retitle it or change its canvas — keeping its identity and sharing. Passing nodes replaces ALL nodes and edges (omitted edges are removed). Passing `source` re-derives nodes and edges from the text (existing ones keep their place; new ones are placed). For small edits — including restyling — prefer patch_diagram.",
|
|
389
224
|
inputSchema: {
|
|
390
225
|
diagram: (0, shared_js_1.refArg)("Diagram"),
|
|
391
226
|
name: zod_1.z.string().min(1).max(200).optional(),
|
|
@@ -394,19 +229,17 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
394
229
|
edges: build_js_1.diagramInputSchema.shape.edges.optional(),
|
|
395
230
|
canvas: build_js_1.diagramInputSchema.shape.canvas,
|
|
396
231
|
source: sourceArg,
|
|
397
|
-
folder: zod_1.z.string().max(300).nullable().optional().describe('Move the diagram into this folder of your library (id, name or path); null = the top level'),
|
|
398
232
|
autoLayout: zod_1.z.boolean().optional(),
|
|
399
233
|
direction: directionArg,
|
|
400
234
|
strictTypes: zod_1.z.boolean().optional(),
|
|
401
235
|
},
|
|
402
236
|
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
403
|
-
}, (0, shared_js_1.guarded)(async ({ diagram, name, title, nodes, edges, canvas, source,
|
|
404
|
-
if (name === undefined && nodes === undefined && title === undefined && canvas === undefined && source === undefined
|
|
405
|
-
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass name, title, canvas, source
|
|
237
|
+
}, (0, shared_js_1.guarded)(async ({ diagram, name, title, nodes, edges, canvas, source, autoLayout, direction, strictTypes }) => {
|
|
238
|
+
if (name === undefined && nodes === undefined && title === undefined && canvas === undefined && source === undefined) {
|
|
239
|
+
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass name, title, canvas, source and/or nodes (+ edges).");
|
|
406
240
|
}
|
|
407
241
|
if (nodes && source)
|
|
408
242
|
throw new errors_js_1.ToolInputError("CONFLICTING_INPUT", "Pass either nodes or source, not both.");
|
|
409
|
-
const targetFolder = folder ? await (0, lookups_js_1.resolveDiagramFolder)(ctx, folder) : null;
|
|
410
243
|
const resolved = await (0, lookups_js_1.resolveDiagram)(ctx, diagram);
|
|
411
244
|
if (!resolved.item.canEdit)
|
|
412
245
|
throw new errors_js_1.ToolInputError("DIAGRAM_READ_ONLY", "You have read-only access to this diagram.");
|
|
@@ -414,19 +247,7 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
414
247
|
let document;
|
|
415
248
|
let layoutNote;
|
|
416
249
|
let warnings = [];
|
|
417
|
-
if (nodes && existing.source?.format === "
|
|
418
|
-
// A database diagram stays one: the tables given become the new model.
|
|
419
|
-
const built = (0, build_js_1.buildDiagramDocument)({ title: title ?? existing.meta.title, nodes, edges: edges ?? [], canvas: canvas ?? existing.canvas }, { strictTypes, existingId: existing.id, createdAt: existing.meta.createdAt, kind: "database" });
|
|
420
|
-
const previous = (0, callman_core_1.parseDatabaseModel)(existing.source.text).model;
|
|
421
|
-
const model = (0, callman_core_1.databaseModelFromDocument)({ ...built.document, source: undefined }, previous.dialect);
|
|
422
|
-
if (previous.connection)
|
|
423
|
-
model.connection = previous.connection;
|
|
424
|
-
const database = await databaseDocumentFromModel(model, existing, true);
|
|
425
|
-
document = database.document;
|
|
426
|
-
layoutNote = database.layoutNote;
|
|
427
|
-
warnings = [...built.warnings, "The database model was rewritten from the tables given; tables not in the input are gone. Prefer patch_diagram (addTable, addColumn, addRelationship …) for edits."];
|
|
428
|
-
}
|
|
429
|
-
else if (nodes && existing.source?.format === "bpmn") {
|
|
250
|
+
if (nodes && existing.source?.format === "bpmn") {
|
|
430
251
|
// A BPMN diagram stays BPMN: the new model is written as fresh XML.
|
|
431
252
|
const built = (0, build_js_1.buildDiagramDocument)({ title: title ?? existing.meta.title, nodes, edges: edges ?? [], canvas: canvas ?? existing.canvas }, { strictTypes, existingId: existing.id, createdAt: existing.meta.createdAt, kind: "bpmn" });
|
|
432
253
|
const bpmn = bpmnDocumentFromModel(built.document, built.unpositionedNodeIds, existing);
|
|
@@ -443,7 +264,7 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
443
264
|
warnings.push(`edges was omitted, so the ${existing.edges.length} existing edge(s) were removed. Pass edges to keep connections, or use patch_diagram for partial edits.`);
|
|
444
265
|
}
|
|
445
266
|
if (existing.source)
|
|
446
|
-
warnings.push(
|
|
267
|
+
warnings.push("The diagram's Mermaid source was detached: nodes and edges were replaced from the input.");
|
|
447
268
|
if (autoLayout || built.unpositionedNodeIds.length > 0) {
|
|
448
269
|
const laid = await (0, layout_js_1.layoutDocument)(document, { direction });
|
|
449
270
|
document = laid.document;
|
|
@@ -473,20 +294,13 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
473
294
|
const updated = await ctx.api.updateDiagram(resolved.item.id, {
|
|
474
295
|
...(name !== undefined ? { name: name.trim() } : {}),
|
|
475
296
|
...(document ? { document, ...(typeof existing.meta.revision === "number" ? { baseRevision: existing.meta.revision } : {}) } : {}),
|
|
476
|
-
...(folder !== undefined ? { folderId: targetFolder?.item.id ?? null } : {}),
|
|
477
297
|
});
|
|
478
298
|
ctx.invalidate("diagrams");
|
|
479
|
-
|
|
480
|
-
return (0, shared_js_1.ok)({
|
|
481
|
-
diagram: { ...(0, summarize_js_1.diagramListView)(updated), folder: (0, lookups_js_1.diagramFolderPath)(rows, updated.folderId), ...(0, summarize_js_1.diagramGraphView)(document ?? existing) },
|
|
482
|
-
warnings,
|
|
483
|
-
...(layoutNote ? { layout: layoutNote } : {}),
|
|
484
|
-
resolved: [(0, resolve_js_1.resolvedSummary)(diagram, resolved), ...(targetFolder?.annotation ? [(0, resolve_js_1.resolvedSummary)(folder, targetFolder)] : [])],
|
|
485
|
-
});
|
|
299
|
+
return (0, shared_js_1.ok)({ diagram: { ...(0, summarize_js_1.diagramListView)(updated), ...(0, summarize_js_1.diagramGraphView)(document ?? existing) }, warnings, ...(layoutNote ? { layout: layoutNote } : {}), resolved: [(0, resolve_js_1.resolvedSummary)(diagram, resolved)] });
|
|
486
300
|
}));
|
|
487
301
|
server.registerTool("patch_diagram", {
|
|
488
302
|
title: "Edit a diagram incrementally",
|
|
489
|
-
description: "Apply operations to a diagram without resending it: addNode, updateNode, removeNode (drops its edges, lifts its children out), addEdge, updateEdge, removeEdge, setTitle, moveNode, reparent, setEdgeRoute (routing, fixed sides, bends), reorder (stacking), setCanvas, group (wrap nodes in a container), ungroup, relayout (a hint: new | all).
|
|
303
|
+
description: "Apply operations to a diagram without resending it: addNode, updateNode, removeNode (drops its edges, lifts its children out), addEdge, updateEdge, removeEdge, setTitle, moveNode, reparent, setEdgeRoute (routing, fixed sides, bends), reorder (stacking), setCanvas, group (wrap nodes in a container), ungroup, relayout (a hint: new | all). updateNode `changes` MERGE: style and data keys you pass change, a key set to null is removed. updateEdge can change semantic, color, label, arrows, sides and ends. Moving a node re-routes its edges. `relayout`: none | new (place only new nodes, default) | all (re-run automatic layout). Call get_diagram (view: graph) first to see node ids and current styles. Ops are documented in callman://docs/diagram-schema.",
|
|
490
304
|
inputSchema: {
|
|
491
305
|
diagram: (0, shared_js_1.refArg)("Diagram"),
|
|
492
306
|
ops: zod_1.z.array(patch_js_1.diagramPatchOpSchema).min(1).max(200),
|
|
@@ -510,21 +324,15 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
510
324
|
const wantsAll = relayout === "all" || patched.relayout?.scope === "all";
|
|
511
325
|
const layoutDirection = direction ?? patched.relayout?.direction;
|
|
512
326
|
const bpmn = document.source?.format === "bpmn";
|
|
513
|
-
const database = document.source?.format === "database";
|
|
514
327
|
if (wantsAll) {
|
|
515
|
-
// A process reads left to right; the BPMN XML follows the moves.
|
|
516
|
-
|
|
517
|
-
const laid = await (0, layout_js_1.layoutDocument)(document, { direction: layoutDirection ?? (bpmn || database ? "RIGHT" : undefined), ...(database ? { request: callman_core_1.DATABASE_LAYOUT_REQUEST } : {}) });
|
|
328
|
+
// A process reads left to right; the BPMN XML follows the moves.
|
|
329
|
+
const laid = await (0, layout_js_1.layoutDocument)(document, { direction: layoutDirection ?? (bpmn ? "RIGHT" : undefined) });
|
|
518
330
|
document = laid.document;
|
|
519
331
|
layoutNote = laid.warning ?? `Laid out with ${laid.engine}.`;
|
|
520
332
|
}
|
|
521
333
|
else if (relayout === "new" && patched.addedNodeIds.length > 0) {
|
|
522
334
|
const ids = patched.addedNodeIds.filter((id) => document.nodes.some((node) => node.id === id));
|
|
523
|
-
if (
|
|
524
|
-
document = (0, layout_js_1.placeDatabaseNodes)(document, ids);
|
|
525
|
-
layoutNote = `Placed ${ids.length} new table(s) to the right of the existing ones.`;
|
|
526
|
-
}
|
|
527
|
-
else if (bpmn) {
|
|
335
|
+
if (bpmn) {
|
|
528
336
|
document = (0, callman_core_1.applyDiagramOps)(document, (0, callman_core_1.placeBpmnNodesOps)(document, ids), { now: now() }).doc;
|
|
529
337
|
layoutNote = `Placed ${ids.length} new node(s) after their predecessors.`;
|
|
530
338
|
}
|
|
@@ -553,7 +361,7 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
553
361
|
ctx.invalidate("diagrams");
|
|
554
362
|
const { updated, document, patched, layoutNote } = outcome;
|
|
555
363
|
return (0, shared_js_1.ok)({
|
|
556
|
-
diagram: { ...(0, summarize_js_1.diagramListView)(updated), ...(0, summarize_js_1.diagramGraphView)(document)
|
|
364
|
+
diagram: { ...(0, summarize_js_1.diagramListView)(updated), ...(0, summarize_js_1.diagramGraphView)(document) },
|
|
557
365
|
applied: ops.length,
|
|
558
366
|
added: patched.addedNodeIds,
|
|
559
367
|
removedNodes: patched.removedNodeIds,
|
|
@@ -565,103 +373,6 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
565
373
|
resolved: [(0, resolve_js_1.resolvedSummary)(diagram, resolved)],
|
|
566
374
|
});
|
|
567
375
|
}));
|
|
568
|
-
server.registerTool("introspect_database", {
|
|
569
|
-
title: "Read a database's schema through a connection",
|
|
570
|
-
description: "Read the database behind one of the workspace's database connections (list_connections, type: database): schemas, tables and views (MongoDB: collections with fields inferred from a sample), columns with types, nullability, defaults and comments, primary and unique keys, foreign keys with referential actions, indexes. Metadata only — never credentials, never data. Use `objectsOnly` to list names first, then read the tables you need; `create_diagram` with `fromConnection` draws the result as a Database Diagram in one step. Runs on the Callman backend, which must be able to reach the database.",
|
|
571
|
-
inputSchema: {
|
|
572
|
-
connection: zod_1.z.string().min(1).max(200).describe("Connection id or name"),
|
|
573
|
-
workspace: shared_js_1.workspaceArg,
|
|
574
|
-
schemas: zod_1.z.array(zod_1.z.string().min(1).max(120)).max(50).optional(),
|
|
575
|
-
tables: zod_1.z.array(zod_1.z.string().min(1).max(240)).max(2000).optional().describe('"schema.table" or "table"'),
|
|
576
|
-
includeViews: zod_1.z.boolean().optional(),
|
|
577
|
-
objectsOnly: zod_1.z.boolean().optional().describe("Names only (no columns / keys) — for a first look at a large database"),
|
|
578
|
-
maxTables: zod_1.z.number().int().min(1).max(20000).optional(),
|
|
579
|
-
},
|
|
580
|
-
annotations: shared_js_1.READ,
|
|
581
|
-
}, (0, shared_js_1.guarded)(async ({ connection, workspace, schemas, tables, includeViews, objectsOnly, maxTables }) => {
|
|
582
|
-
const found = await findDatabaseConnection(ctx, workspace, connection);
|
|
583
|
-
const schema = await ctx.api.getConnectionSchema(found.ws.id, found.connection.id, schemaQuery({ schemas, tables, includeViews, objectsOnly, maxTables }));
|
|
584
|
-
return (0, shared_js_1.ok)({
|
|
585
|
-
connection: { id: found.connection.id, name: found.connection.name, provider: PROVIDER_LABEL[found.connection.provider] ?? found.connection.provider, workspace: (0, lookups_js_1.workspaceTag)(found.ws) },
|
|
586
|
-
schema,
|
|
587
|
-
...(schema.truncated ? { note: "The database has more tables than the cap; pass schemas/tables to narrow the read or raise maxTables." } : {}),
|
|
588
|
-
});
|
|
589
|
-
}));
|
|
590
|
-
// ── library folders (private to the token's owner) ──────────────────
|
|
591
|
-
const folderView = (row) => ({ id: row.id, name: row.name, parentId: row.parentId, path: row.path });
|
|
592
|
-
server.registerTool("create_diagram_folder", {
|
|
593
|
-
title: "Create a diagram folder",
|
|
594
|
-
description: "Create a folder in your diagram library, optionally inside a parent folder (id, name or path). Folders are yours alone (nobody else sees them; a diagram you share shows at the top level for others), nest up to 8 levels, and sibling names are unique — an existing folder with that name is returned instead of a duplicate. File diagrams with create_diagram / update_diagram `folder`.",
|
|
595
|
-
inputSchema: {
|
|
596
|
-
name: zod_1.z.string().min(1).max(120),
|
|
597
|
-
parent: zod_1.z.string().max(300).optional().describe("Parent folder id, name or path; omit for the top level"),
|
|
598
|
-
},
|
|
599
|
-
annotations: shared_js_1.WRITE,
|
|
600
|
-
}, (0, shared_js_1.guarded)(async ({ name, parent }) => {
|
|
601
|
-
const parentFolder = parent ? await (0, lookups_js_1.resolveDiagramFolder)(ctx, parent) : null;
|
|
602
|
-
const rows = await (0, lookups_js_1.diagramFolderRows)(ctx);
|
|
603
|
-
const trimmed = name.trim();
|
|
604
|
-
const sibling = rows.find((row) => row.parentId === (parentFolder?.item.id ?? null) && (0, ids_js_1.normalizeName)(row.name) === (0, ids_js_1.normalizeName)(trimmed));
|
|
605
|
-
if (sibling) {
|
|
606
|
-
return (0, shared_js_1.ok)({ folder: folderView(sibling), existed: true, note: "A folder with that name already exists there — returned instead of creating a duplicate." });
|
|
607
|
-
}
|
|
608
|
-
const created = await ctx.api.createDiagramFolder({ name: trimmed, parentId: parentFolder?.item.id ?? null });
|
|
609
|
-
ctx.invalidate("diagram-folders");
|
|
610
|
-
const path = parentFolder ? `${parentFolder.item.path}/${created.name}` : created.name;
|
|
611
|
-
return (0, shared_js_1.ok)({
|
|
612
|
-
folder: { id: created.id, name: created.name, parentId: created.parentId, path },
|
|
613
|
-
...(parentFolder?.annotation ? { resolved: [(0, resolve_js_1.resolvedSummary)(parent, parentFolder)] } : {}),
|
|
614
|
-
});
|
|
615
|
-
}));
|
|
616
|
-
server.registerTool("update_diagram_folder", {
|
|
617
|
-
title: "Rename or move a diagram folder",
|
|
618
|
-
description: "Rename a folder of your diagram library and/or move it under another folder (parent: null = the top level). A folder cannot be moved into itself or one of its subfolders.",
|
|
619
|
-
inputSchema: {
|
|
620
|
-
folder: (0, shared_js_1.refArg)("Diagram folder", "(or path)"),
|
|
621
|
-
name: zod_1.z.string().min(1).max(120).optional(),
|
|
622
|
-
parent: zod_1.z.string().max(300).nullable().optional().describe("New parent folder (id, name or path); null = the top level"),
|
|
623
|
-
},
|
|
624
|
-
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
625
|
-
}, (0, shared_js_1.guarded)(async ({ folder, name, parent }) => {
|
|
626
|
-
if (name === undefined && parent === undefined)
|
|
627
|
-
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass name and/or parent.");
|
|
628
|
-
const target = await (0, lookups_js_1.resolveDiagramFolder)(ctx, folder);
|
|
629
|
-
const parentFolder = parent ? await (0, lookups_js_1.resolveDiagramFolder)(ctx, parent) : null;
|
|
630
|
-
if (parentFolder && (parentFolder.item.id === target.item.id || parentFolder.item.path.startsWith(`${target.item.path}/`))) {
|
|
631
|
-
throw new errors_js_1.ToolInputError("FOLDER_CYCLE", "A folder cannot be moved into itself or one of its subfolders.");
|
|
632
|
-
}
|
|
633
|
-
const updated = await ctx.api.updateDiagramFolder(target.item.id, {
|
|
634
|
-
...(name !== undefined ? { name: name.trim() } : {}),
|
|
635
|
-
...(parent !== undefined ? { parentId: parentFolder?.item.id ?? null } : {}),
|
|
636
|
-
});
|
|
637
|
-
ctx.invalidate("diagram-folders");
|
|
638
|
-
const rows = await (0, lookups_js_1.diagramFolderRows)(ctx);
|
|
639
|
-
const row = rows.find((entry) => entry.id === updated.id);
|
|
640
|
-
return (0, shared_js_1.ok)({
|
|
641
|
-
folder: row ? folderView(row) : { id: updated.id, name: updated.name, parentId: updated.parentId, path: updated.name },
|
|
642
|
-
resolved: [(0, resolve_js_1.resolvedSummary)(folder, target), ...(parentFolder?.annotation ? [(0, resolve_js_1.resolvedSummary)(parent, parentFolder)] : [])],
|
|
643
|
-
});
|
|
644
|
-
}));
|
|
645
|
-
server.registerTool("delete_diagram_folder", {
|
|
646
|
-
title: "Delete a diagram folder",
|
|
647
|
-
description: "Delete a folder of your diagram library and its subfolders. No diagram is deleted: everything inside moves to the deleted folder's parent. Confirm with the user first.",
|
|
648
|
-
inputSchema: { folder: (0, shared_js_1.refArg)("Diagram folder", "(or path)"), confirm: shared_js_1.confirmArg },
|
|
649
|
-
annotations: shared_js_1.DESTRUCTIVE,
|
|
650
|
-
}, (0, shared_js_1.guarded)(async ({ folder, confirm }) => {
|
|
651
|
-
(0, shared_js_1.requireConfirm)(confirm, "a diagram folder");
|
|
652
|
-
const target = await (0, lookups_js_1.resolveDiagramFolder)(ctx, folder, { exact: true });
|
|
653
|
-
const result = await ctx.api.deleteDiagramFolder(target.item.id);
|
|
654
|
-
ctx.invalidate("diagram-folders");
|
|
655
|
-
ctx.invalidate("diagrams");
|
|
656
|
-
const rows = await (0, lookups_js_1.diagramFolderRows)(ctx);
|
|
657
|
-
return (0, shared_js_1.ok)({
|
|
658
|
-
deleted: folderView(target.item),
|
|
659
|
-
deletedFolderCount: result.deletedFolderIds.length,
|
|
660
|
-
movedDiagramCount: result.movedDiagramCount,
|
|
661
|
-
movedTo: (0, lookups_js_1.diagramFolderPath)(rows, result.movedToFolderId) ?? "the top level",
|
|
662
|
-
resolved: [(0, resolve_js_1.resolvedSummary)(folder, target)],
|
|
663
|
-
});
|
|
664
|
-
}));
|
|
665
376
|
server.registerTool("delete_diagram", {
|
|
666
377
|
title: "Delete a diagram",
|
|
667
378
|
description: "Delete a diagram you own. Permanent — confirm with the user first.",
|
package/dist-cjs/tools/index.js
CHANGED
|
@@ -7,10 +7,10 @@ const contracts_js_1 = require("./contracts.js");
|
|
|
7
7
|
const diagrams_js_1 = require("./diagrams.js");
|
|
8
8
|
const environments_js_1 = require("./environments.js");
|
|
9
9
|
const import_js_1 = require("./import.js");
|
|
10
|
+
const loadTest_js_1 = require("./loadTest.js");
|
|
10
11
|
const meta_js_1 = require("./meta.js");
|
|
11
12
|
const requests_js_1 = require("./requests.js");
|
|
12
13
|
const run_js_1 = require("./run.js");
|
|
13
|
-
const scenarioAdmin_js_1 = require("./scenarioAdmin.js");
|
|
14
14
|
const scenarios_js_1 = require("./scenarios.js");
|
|
15
15
|
const uiTestAdmin_js_1 = require("./uiTestAdmin.js");
|
|
16
16
|
const uiTestRuns_js_1 = require("./uiTestRuns.js");
|
|
@@ -47,12 +47,13 @@ const registerAllTools = (host, ctx) => {
|
|
|
47
47
|
(0, import_js_1.registerImportTools)(server, ctx);
|
|
48
48
|
(0, environments_js_1.registerEnvironmentTools)(server, ctx);
|
|
49
49
|
(0, scenarios_js_1.registerScenarioTools)(server, ctx);
|
|
50
|
-
(0, scenarioAdmin_js_1.registerScenarioAdminTools)(server, ctx);
|
|
51
50
|
(0, uiTests_js_1.registerUiTestTools)(server, ctx);
|
|
52
51
|
(0, uiTestRuns_js_1.registerUiTestRunTools)(server, ctx);
|
|
53
52
|
(0, uiTestAdmin_js_1.registerUiTestAdminTools)(server, ctx);
|
|
54
53
|
(0, diagrams_js_1.registerDiagramTools)(server, ctx);
|
|
55
|
-
if (ctx.config.allowLocalRun)
|
|
54
|
+
if (ctx.config.allowLocalRun) {
|
|
56
55
|
(0, run_js_1.registerRunTools)(server, ctx);
|
|
56
|
+
(0, loadTest_js_1.registerLoadTestTool)(server, ctx);
|
|
57
|
+
}
|
|
57
58
|
};
|
|
58
59
|
exports.registerAllTools = registerAllTools;
|