callman-mcp 0.1.0 → 0.2.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 +82 -0
- package/README.md +45 -16
- package/dist/client/api.d.ts +64 -3
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/api.js +67 -2
- package/dist/client/api.js.map +1 -1
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +33 -7
- package/dist/client/errors.js.map +1 -1
- package/dist/client/types.d.ts +91 -2
- package/dist/client/types.d.ts.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +7 -2
- package/dist/context.js.map +1 -1
- package/dist/domain/contracts.d.ts +893 -0
- package/dist/domain/contracts.d.ts.map +1 -0
- package/dist/domain/contracts.js +90 -0
- package/dist/domain/contracts.js.map +1 -0
- package/dist/domain/diagram/build.d.ts +520 -18
- package/dist/domain/diagram/build.d.ts.map +1 -1
- package/dist/domain/diagram/build.js +65 -19
- package/dist/domain/diagram/build.js.map +1 -1
- package/dist/domain/diagram/patch.d.ts +624 -51
- package/dist/domain/diagram/patch.d.ts.map +1 -1
- package/dist/domain/diagram/patch.js +180 -40
- package/dist/domain/diagram/patch.js.map +1 -1
- package/dist/domain/diagram/style.d.ts +315 -0
- package/dist/domain/diagram/style.d.ts.map +1 -0
- package/dist/domain/diagram/style.js +124 -0
- package/dist/domain/diagram/style.js.map +1 -0
- package/dist/domain/diagram/summarize.d.ts +8 -0
- package/dist/domain/diagram/summarize.d.ts.map +1 -1
- package/dist/domain/diagram/summarize.js +28 -13
- package/dist/domain/diagram/summarize.js.map +1 -1
- package/dist/domain/requestIntegrations.d.ts +719 -0
- package/dist/domain/requestIntegrations.d.ts.map +1 -0
- package/dist/domain/requestIntegrations.js +222 -0
- package/dist/domain/requestIntegrations.js.map +1 -0
- package/dist/domain/requestShape.d.ts +1350 -152
- package/dist/domain/requestShape.d.ts.map +1 -1
- package/dist/domain/requestShape.js +336 -121
- package/dist/domain/requestShape.js.map +1 -1
- package/dist/domain/resolve.d.ts +2 -1
- package/dist/domain/resolve.d.ts.map +1 -1
- package/dist/domain/resolve.js +5 -1
- package/dist/domain/resolve.js.map +1 -1
- package/dist/domain/scenario/build.d.ts +82 -26
- package/dist/domain/scenario/build.d.ts.map +1 -1
- package/dist/domain/scenario/build.js +196 -65
- package/dist/domain/scenario/build.js.map +1 -1
- package/dist/domain/scenario/nodeConfig.d.ts +11 -0
- package/dist/domain/scenario/nodeConfig.d.ts.map +1 -0
- package/dist/domain/scenario/nodeConfig.js +251 -0
- package/dist/domain/scenario/nodeConfig.js.map +1 -0
- package/dist/domain/scenario/summarize.d.ts +145 -25
- package/dist/domain/scenario/summarize.d.ts.map +1 -1
- package/dist/domain/scenario/summarize.js +84 -29
- package/dist/domain/scenario/summarize.js.map +1 -1
- package/dist/lib/mask.d.ts +0 -18
- package/dist/lib/mask.d.ts.map +1 -1
- package/dist/lib/mask.js +2 -44
- package/dist/lib/mask.js.map +1 -1
- package/dist/lib/pagination.d.ts +3 -2
- package/dist/lib/pagination.d.ts.map +1 -1
- package/dist/lib/pagination.js +13 -8
- package/dist/lib/pagination.js.map +1 -1
- package/dist/lib/redact.d.ts +12 -0
- package/dist/lib/redact.d.ts.map +1 -0
- package/dist/lib/redact.js +50 -0
- package/dist/lib/redact.js.map +1 -0
- package/dist/lib/strict.d.ts +4 -0
- package/dist/lib/strict.d.ts.map +1 -0
- package/dist/lib/strict.js +14 -0
- package/dist/lib/strict.js.map +1 -0
- package/dist/prompts/index.js +7 -7
- package/dist/prompts/index.js.map +1 -1
- package/dist/resources/docs/contracts.d.ts +2 -0
- package/dist/resources/docs/contracts.d.ts.map +1 -0
- package/dist/resources/docs/contracts.js +74 -0
- package/dist/resources/docs/contracts.js.map +1 -0
- package/dist/resources/docs/diagramSchema.d.ts +1 -1
- package/dist/resources/docs/diagramSchema.d.ts.map +1 -1
- package/dist/resources/docs/diagramSchema.js +95 -28
- 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 +29 -7
- 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 +85 -16
- package/dist/resources/docs/requestSchema.js.map +1 -1
- package/dist/resources/generated/aiPacks.d.ts +1 -1
- package/dist/resources/generated/aiPacks.d.ts.map +1 -1
- package/dist/resources/generated/aiPacks.js +3 -2
- package/dist/resources/generated/aiPacks.js.map +1 -1
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +19 -19
- package/dist/resources/index.js.map +1 -1
- package/dist/run/executeCollection.d.ts +3 -1
- package/dist/run/executeCollection.d.ts.map +1 -1
- package/dist/run/executeCollection.js +16 -4
- package/dist/run/executeCollection.js.map +1 -1
- package/dist/run/summarize.d.ts +17 -6
- package/dist/run/summarize.d.ts.map +1 -1
- package/dist/run/summarize.js +48 -41
- package/dist/run/summarize.js.map +1 -1
- package/dist/run/types.d.ts +1 -0
- 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 +1 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/catalog.d.ts +1 -1
- package/dist/tools/catalog.d.ts.map +1 -1
- package/dist/tools/catalog.js +18 -0
- package/dist/tools/catalog.js.map +1 -1
- package/dist/tools/collections.d.ts.map +1 -1
- package/dist/tools/collections.js +53 -41
- package/dist/tools/collections.js.map +1 -1
- package/dist/tools/contracts.d.ts +4 -0
- package/dist/tools/contracts.d.ts.map +1 -0
- package/dist/tools/contracts.js +201 -0
- package/dist/tools/contracts.js.map +1 -0
- package/dist/tools/diagrams.d.ts.map +1 -1
- package/dist/tools/diagrams.js +19 -10
- package/dist/tools/diagrams.js.map +1 -1
- package/dist/tools/environments.d.ts.map +1 -1
- package/dist/tools/environments.js +184 -64
- package/dist/tools/environments.js.map +1 -1
- package/dist/tools/import.d.ts.map +1 -1
- package/dist/tools/import.js +111 -20
- package/dist/tools/import.js.map +1 -1
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +19 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/lookups.d.ts +38 -7
- package/dist/tools/lookups.d.ts.map +1 -1
- package/dist/tools/lookups.js +113 -10
- package/dist/tools/lookups.js.map +1 -1
- package/dist/tools/meta.d.ts.map +1 -1
- package/dist/tools/meta.js +9 -4
- package/dist/tools/meta.js.map +1 -1
- package/dist/tools/requests.d.ts.map +1 -1
- package/dist/tools/requests.js +138 -50
- package/dist/tools/requests.js.map +1 -1
- package/dist/tools/run.d.ts.map +1 -1
- package/dist/tools/run.js +84 -28
- package/dist/tools/run.js.map +1 -1
- package/dist/tools/scenarios.d.ts.map +1 -1
- package/dist/tools/scenarios.js +238 -75
- package/dist/tools/scenarios.js.map +1 -1
- package/dist/tools/shared.d.ts +1 -0
- package/dist/tools/shared.d.ts.map +1 -1
- package/dist/tools/shared.js +9 -1
- package/dist/tools/shared.js.map +1 -1
- package/dist/tools/workspaces.d.ts.map +1 -1
- package/dist/tools/workspaces.js +44 -7
- package/dist/tools/workspaces.js.map +1 -1
- package/dist-cjs/client/api.js +67 -2
- package/dist-cjs/client/errors.js +33 -7
- package/dist-cjs/context.js +7 -2
- package/dist-cjs/domain/contracts.js +93 -0
- package/dist-cjs/domain/diagram/build.js +71 -20
- package/dist-cjs/domain/diagram/patch.js +179 -39
- package/dist-cjs/domain/diagram/style.js +130 -0
- package/dist-cjs/domain/diagram/summarize.js +28 -13
- package/dist-cjs/domain/requestIntegrations.js +228 -0
- package/dist-cjs/domain/requestShape.js +342 -121
- package/dist-cjs/domain/resolve.js +5 -1
- package/dist-cjs/domain/scenario/build.js +197 -65
- package/dist-cjs/domain/scenario/nodeConfig.js +260 -0
- package/dist-cjs/domain/scenario/summarize.js +86 -29
- package/dist-cjs/lib/mask.js +3 -48
- package/dist-cjs/lib/pagination.js +13 -8
- package/dist-cjs/lib/redact.js +53 -0
- package/dist-cjs/lib/strict.js +18 -0
- package/dist-cjs/prompts/index.js +7 -7
- package/dist-cjs/resources/docs/contracts.js +76 -0
- package/dist-cjs/resources/docs/diagramSchema.js +95 -28
- package/dist-cjs/resources/docs/overview.js +29 -7
- package/dist-cjs/resources/docs/requestSchema.js +85 -16
- package/dist-cjs/resources/generated/aiPacks.js +3 -2
- package/dist-cjs/resources/index.js +17 -17
- package/dist-cjs/run/executeCollection.js +16 -4
- package/dist-cjs/run/summarize.js +48 -41
- package/dist-cjs/server.js +1 -1
- package/dist-cjs/tools/catalog.js +18 -0
- package/dist-cjs/tools/collections.js +52 -40
- package/dist-cjs/tools/contracts.js +204 -0
- package/dist-cjs/tools/diagrams.js +19 -10
- package/dist-cjs/tools/environments.js +182 -62
- package/dist-cjs/tools/import.js +107 -16
- package/dist-cjs/tools/index.js +21 -2
- package/dist-cjs/tools/lookups.js +123 -11
- package/dist-cjs/tools/meta.js +9 -4
- package/dist-cjs/tools/requests.js +134 -46
- package/dist-cjs/tools/run.js +82 -26
- package/dist-cjs/tools/scenarios.js +235 -72
- package/dist-cjs/tools/shared.js +11 -2
- package/dist-cjs/tools/workspaces.js +43 -6
- package/package.json +2 -2
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerContractTools = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const errors_js_1 = require("../client/errors.js");
|
|
6
|
+
const contracts_js_1 = require("../domain/contracts.js");
|
|
7
|
+
const requestShape_js_1 = require("../domain/requestShape.js");
|
|
8
|
+
const lookups_js_1 = require("./lookups.js");
|
|
9
|
+
const shared_js_1 = require("./shared.js");
|
|
10
|
+
// Contract checks on a saved request (the desktop's Contracts tab): field
|
|
11
|
+
// rules and the data-driven contract. Every runner evaluates them after the
|
|
12
|
+
// response — run_request / run_collection report them under `contracts`.
|
|
13
|
+
// Read callman://docs/contracts for the rule vocabulary and path grammar.
|
|
14
|
+
const requestArgs = {
|
|
15
|
+
workspace: shared_js_1.workspaceArg,
|
|
16
|
+
request: (0, shared_js_1.refArg)("Request", "(name, \"METHOD url\" or id)"),
|
|
17
|
+
collection: zod_1.z.string().max(200).optional().describe("Narrow a request name lookup to one collection"),
|
|
18
|
+
};
|
|
19
|
+
const ruleView = (rule) => ({ id: rule.id, path: rule.path, enabled: rule.enabled, rules: rule.rules });
|
|
20
|
+
const registerContractTools = (server, ctx) => {
|
|
21
|
+
server.registerTool("get_contract_checks", {
|
|
22
|
+
title: "Get a request's contract checks",
|
|
23
|
+
description: "Read a saved request's contract checks: field rules ({ id, path, rules, enabled }) and its data-driven contract (columns + rows, each row with expectedStatus and assertions). These run after every response of the request (desktop, CLI, run_request, run_collection).",
|
|
24
|
+
inputSchema: requestArgs,
|
|
25
|
+
annotations: shared_js_1.READ,
|
|
26
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection }) => {
|
|
27
|
+
const { ws, value, note } = await (0, lookups_js_1.findRequest)(ctx, workspace, request, collection);
|
|
28
|
+
const { request: found, annotation } = value;
|
|
29
|
+
const [rules, dataContract] = await Promise.all([ctx.api.listContractRules(ws.id, found.id), ctx.api.getDataContract(ws.id, found.id)]);
|
|
30
|
+
return (0, shared_js_1.ok)({
|
|
31
|
+
request: (0, requestShape_js_1.requestSummaryView)(found),
|
|
32
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
33
|
+
rules: rules.items.map(ruleView),
|
|
34
|
+
dataContract: dataContract.rows.length > 0 || dataContract.columns.length > 0 ? { columns: dataContract.columns, rows: dataContract.rows } : null,
|
|
35
|
+
...(annotation || note ? { resolved: [{ input: request, matched: found.name, id: found.id, note: [annotation, note].filter(Boolean).join("; ") }] } : {}),
|
|
36
|
+
});
|
|
37
|
+
}));
|
|
38
|
+
server.registerTool("add_contract_rules", {
|
|
39
|
+
title: "Add contract rules to a request",
|
|
40
|
+
description: "Add field rules to a saved request's contract (one call, up to 200): each is { path, rules, enabled? } — e.g. { path: \"users[].id\", rules: { required: true, type: \"string\", format: \"uuid\" } } or { path: \"response.status\", rules: { equals: 200 } }. replace: true removes the request's existing rules first (needs requests:delete). Try them against a sample response with test_contract_rules. Rule vocabulary: callman://docs/contracts.",
|
|
41
|
+
inputSchema: {
|
|
42
|
+
...requestArgs,
|
|
43
|
+
rules: zod_1.z.array(contracts_js_1.contractRuleInputSchema).min(1).max(200),
|
|
44
|
+
replace: zod_1.z.boolean().optional().describe("Delete the request's existing rules first"),
|
|
45
|
+
dryRun: shared_js_1.dryRunArg,
|
|
46
|
+
},
|
|
47
|
+
annotations: shared_js_1.WRITE,
|
|
48
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection, rules, replace, dryRun }) => {
|
|
49
|
+
const { ws, value } = await (0, lookups_js_1.findRequest)(ctx, workspace, request, collection);
|
|
50
|
+
const { request: found } = value;
|
|
51
|
+
const existing = (await ctx.api.listContractRules(ws.id, found.id)).items;
|
|
52
|
+
const duplicates = replace ? [] : rules.filter((rule) => existing.some((entry) => entry.path === rule.path)).map((rule) => rule.path);
|
|
53
|
+
const warnings = duplicates.length > 0 ? [`These paths already had rules and now have another one each: ${duplicates.join(", ")} — use update_contract_rule to change an existing rule instead.`] : [];
|
|
54
|
+
if (dryRun) {
|
|
55
|
+
return (0, shared_js_1.ok)({ dryRun: true, request: (0, requestShape_js_1.requestSummaryView)(found),
|
|
56
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws), adding: rules.length, ...(replace ? { removing: existing.length } : {}), warnings });
|
|
57
|
+
}
|
|
58
|
+
if (replace) {
|
|
59
|
+
for (const rule of existing)
|
|
60
|
+
await ctx.api.deleteContractRule(ws.id, rule.id);
|
|
61
|
+
}
|
|
62
|
+
const created = await ctx.api.createContractRules(ws.id, { requestId: found.id, rules: rules.map((rule) => ({ ...rule, enabled: rule.enabled ?? true })) });
|
|
63
|
+
return (0, shared_js_1.ok)({
|
|
64
|
+
request: (0, requestShape_js_1.requestSummaryView)(found),
|
|
65
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
66
|
+
added: created.items.map(ruleView),
|
|
67
|
+
...(replace ? { removed: existing.length } : {}),
|
|
68
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
69
|
+
});
|
|
70
|
+
}));
|
|
71
|
+
server.registerTool("update_contract_rule", {
|
|
72
|
+
title: "Change one contract rule",
|
|
73
|
+
description: "Change one contract rule by id (from get_contract_checks): its path, its rules (the whole rules object is replaced — send every check you want to keep) and/or enabled.",
|
|
74
|
+
inputSchema: {
|
|
75
|
+
workspace: shared_js_1.workspaceArg,
|
|
76
|
+
rule: zod_1.z.string().min(1).max(64).describe("Rule id from get_contract_checks"),
|
|
77
|
+
path: contracts_js_1.contractPathSchema.optional(),
|
|
78
|
+
rules: contracts_js_1.contractRulesSchema.optional(),
|
|
79
|
+
enabled: zod_1.z.boolean().optional(),
|
|
80
|
+
},
|
|
81
|
+
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
82
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, rule, path, rules, enabled }) => {
|
|
83
|
+
if (path === undefined && rules === undefined && enabled === undefined)
|
|
84
|
+
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass path, rules and/or enabled.");
|
|
85
|
+
const body = { ...(path !== undefined ? { path } : {}), ...(rules !== undefined ? { rules } : {}), ...(enabled !== undefined ? { enabled } : {}) };
|
|
86
|
+
// A rule id lives in exactly one workspace; the others answer 404.
|
|
87
|
+
const { ws, value: updated } = await (0, lookups_js_1.findInWorkspaces)(ctx, workspace, "contract rule", rule, (candidate) => ctx.api.updateContractRule(candidate.id, rule, body), { matchOf: () => "id" });
|
|
88
|
+
return (0, shared_js_1.ok)({ rule: ruleView(updated), workspace: (0, lookups_js_1.workspaceTag)(ws) });
|
|
89
|
+
}));
|
|
90
|
+
server.registerTool("delete_contract_rules", {
|
|
91
|
+
title: "Delete contract rules",
|
|
92
|
+
description: "Delete contract rules of a request — the listed rule ids, or all of them when `rules` is omitted. Permanent — confirm with the user first.",
|
|
93
|
+
inputSchema: {
|
|
94
|
+
...requestArgs,
|
|
95
|
+
rules: zod_1.z.array(zod_1.z.string().min(1).max(64)).max(200).optional().describe("Rule ids; omit to delete every rule of the request"),
|
|
96
|
+
confirm: shared_js_1.confirmArg,
|
|
97
|
+
},
|
|
98
|
+
annotations: shared_js_1.DESTRUCTIVE,
|
|
99
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection, rules, confirm }) => {
|
|
100
|
+
(0, shared_js_1.requireConfirm)(confirm, "contract rules");
|
|
101
|
+
const { ws, value } = await (0, lookups_js_1.findRequest)(ctx, workspace, request, collection, { exact: true });
|
|
102
|
+
const { request: found } = value;
|
|
103
|
+
const existing = (await ctx.api.listContractRules(ws.id, found.id)).items;
|
|
104
|
+
const ids = rules ?? existing.map((rule) => rule.id);
|
|
105
|
+
const unknown = ids.filter((id) => !existing.some((rule) => rule.id === id));
|
|
106
|
+
if (unknown.length > 0) {
|
|
107
|
+
throw new errors_js_1.ToolInputError("CONTRACT_RULE_NOT_FOUND", `Not rules of "${found.name}": ${unknown.join(", ")}.`, { rules: existing.map(ruleView) });
|
|
108
|
+
}
|
|
109
|
+
const deleted = [];
|
|
110
|
+
const failed = [];
|
|
111
|
+
for (const id of ids) {
|
|
112
|
+
try {
|
|
113
|
+
await ctx.api.deleteContractRule(ws.id, id);
|
|
114
|
+
deleted.push(id);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
failed.push({ rule: id, error: (0, shared_js_1.failureOf)(error) });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return (0, shared_js_1.ok)({ request: (0, requestShape_js_1.requestSummaryView)(found),
|
|
121
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws), deleted, failed });
|
|
122
|
+
}));
|
|
123
|
+
server.registerTool("test_contract_rules", {
|
|
124
|
+
title: "Test contract rules against a response body",
|
|
125
|
+
description: "Evaluate a request's ENABLED contract rules against a response body you provide (e.g. a sample from the API docs or a previous run) — on the server, nothing is sent to the API and nothing is saved. Returns pass/fail per rule. `response.*` rules (status, headers) need a real run: use run_request.",
|
|
126
|
+
inputSchema: {
|
|
127
|
+
...requestArgs,
|
|
128
|
+
responseBody: zod_1.z.unknown().describe("The response body (JSON value) to check"),
|
|
129
|
+
},
|
|
130
|
+
annotations: shared_js_1.READ,
|
|
131
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection, responseBody }) => {
|
|
132
|
+
const { ws, value } = await (0, lookups_js_1.findRequest)(ctx, workspace, request, collection);
|
|
133
|
+
const { request: found } = value;
|
|
134
|
+
const result = await ctx.api.validateContractRules(ws.id, { requestId: found.id, responseBody: responseBody ?? null });
|
|
135
|
+
return (0, shared_js_1.ok)({ request: (0, requestShape_js_1.requestSummaryView)(found),
|
|
136
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws), summary: result.summary, results: result.results });
|
|
137
|
+
}));
|
|
138
|
+
server.registerTool("set_data_contract", {
|
|
139
|
+
title: "Set a request's data-driven contract",
|
|
140
|
+
description: `Replace a request's data-driven contract: the request runs once per row with the row's values as {{data.<column>}}, and each row checks its own expectedStatus and assertions (same rule vocabulary as contract rules). Limits: ${contracts_js_1.DATA_CONTRACT_LIMITS.columns} columns, ${contracts_js_1.DATA_CONTRACT_LIMITS.rows} rows, ${contracts_js_1.DATA_CONTRACT_LIMITS.assertions} assertions per row. Put {{data.<column>}} into the request's url/body/headers with update_request so the rows are used.`,
|
|
141
|
+
inputSchema: {
|
|
142
|
+
...requestArgs,
|
|
143
|
+
columns: zod_1.z.array(zod_1.z.string().trim().min(1).max(200)).max(contracts_js_1.DATA_CONTRACT_LIMITS.columns).describe("Column names — referenced as {{data.<column>}}"),
|
|
144
|
+
rows: zod_1.z.array(contracts_js_1.dataContractRowSchema).min(1).max(contracts_js_1.DATA_CONTRACT_LIMITS.rows),
|
|
145
|
+
},
|
|
146
|
+
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
147
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection, columns, rows }) => {
|
|
148
|
+
const undeclared = (0, contracts_js_1.undeclaredDataColumns)(columns, rows);
|
|
149
|
+
if (undeclared.length > 0) {
|
|
150
|
+
throw new errors_js_1.ToolInputError("DATA_COLUMN_UNDECLARED", `Rows use column(s) not in columns: ${undeclared.join(", ")}.`);
|
|
151
|
+
}
|
|
152
|
+
const { ws, value } = await (0, lookups_js_1.findRequest)(ctx, workspace, request, collection);
|
|
153
|
+
const { request: found } = value;
|
|
154
|
+
const saved = await ctx.api.putDataContract(ws.id, { requestId: found.id, columns, rows });
|
|
155
|
+
const text = JSON.stringify([found.url, found.headers, found.body, found.query]);
|
|
156
|
+
const unused = columns.filter((column) => !text.includes(`{{data.${column}}}`));
|
|
157
|
+
return (0, shared_js_1.ok)({
|
|
158
|
+
request: (0, requestShape_js_1.requestSummaryView)(found),
|
|
159
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
160
|
+
dataContract: { columns: saved.columns, rows: saved.rows.length },
|
|
161
|
+
...(unused.length > 0 ? { warnings: [`The request does not reference {{data.${unused.join("}}, {{data.")}}} yet — add them to its url, query, headers or body with update_request.`] } : {}),
|
|
162
|
+
});
|
|
163
|
+
}));
|
|
164
|
+
server.registerTool("delete_data_contract", {
|
|
165
|
+
title: "Delete a request's data-driven contract",
|
|
166
|
+
description: "Remove a request's data-driven contract (all rows). Permanent — confirm with the user first.",
|
|
167
|
+
inputSchema: { ...requestArgs, confirm: shared_js_1.confirmArg },
|
|
168
|
+
annotations: shared_js_1.DESTRUCTIVE,
|
|
169
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection, confirm }) => {
|
|
170
|
+
(0, shared_js_1.requireConfirm)(confirm, "a data contract");
|
|
171
|
+
const { ws, value } = await (0, lookups_js_1.findRequest)(ctx, workspace, request, collection, { exact: true });
|
|
172
|
+
const { request: found } = value;
|
|
173
|
+
await ctx.api.deleteDataContract(ws.id, found.id);
|
|
174
|
+
return (0, shared_js_1.ok)({ request: (0, requestShape_js_1.requestSummaryView)(found),
|
|
175
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws), deleted: true });
|
|
176
|
+
}));
|
|
177
|
+
server.registerTool("list_connections", {
|
|
178
|
+
title: "List workspace connections",
|
|
179
|
+
description: "List database / Redis / Kafka / notification / AI connections (id, name, type, provider, workspace — never credentials) across all your workspaces, or one `workspace`. A connection belongs to one workspace: use one from the same workspace as the request or scenario. Request database/Kafka/Redis steps and scenario db/redis/kafka/notification/ai nodes reference a connection by this id (or name). Connections are created and tested in the desktop app.",
|
|
180
|
+
inputSchema: {
|
|
181
|
+
workspace: shared_js_1.workspaceArg,
|
|
182
|
+
type: zod_1.z.enum(["database", "redis", "kafka", "notification", "ai"]).optional(),
|
|
183
|
+
},
|
|
184
|
+
annotations: shared_js_1.READ,
|
|
185
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, type }) => {
|
|
186
|
+
const workspaces = await (0, lookups_js_1.workspacesForListing)(ctx, workspace);
|
|
187
|
+
const perWorkspace = await Promise.all(workspaces.map(async (ws) => (await ctx.api.listConnections(ws.id, type)).map((connection) => ({ ws, connection }))));
|
|
188
|
+
return (0, shared_js_1.ok)({
|
|
189
|
+
...(workspaces.length === 1 ? { workspace: (0, lookups_js_1.workspaceTag)(workspaces[0]) } : { workspaces: workspaces.map(lookups_js_1.workspaceTag) }),
|
|
190
|
+
connections: perWorkspace.flat().map(({ ws, connection }) => ({
|
|
191
|
+
id: connection.id,
|
|
192
|
+
name: connection.name,
|
|
193
|
+
...(workspaces.length > 1 ? { workspace: (0, lookups_js_1.workspaceTag)(ws) } : {}),
|
|
194
|
+
type: connection.type,
|
|
195
|
+
provider: connection.provider,
|
|
196
|
+
...(connection.description ? { description: connection.description } : {}),
|
|
197
|
+
...(connection.isActive === false ? { active: false } : {}),
|
|
198
|
+
...(connection.managed ? { managed: true } : {}),
|
|
199
|
+
...(connection.lastTestStatus ? { lastTest: connection.lastTestStatus } : {}),
|
|
200
|
+
})),
|
|
201
|
+
});
|
|
202
|
+
}));
|
|
203
|
+
};
|
|
204
|
+
exports.registerContractTools = registerContractTools;
|
|
@@ -31,12 +31,12 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
31
31
|
const all = await (0, lookups_js_1.loadDiagrams)(ctx);
|
|
32
32
|
const needle = query?.trim().toLowerCase();
|
|
33
33
|
const filtered = needle ? all.filter((diagram) => diagram.name.toLowerCase().includes(needle)) : all;
|
|
34
|
-
const page = (0, pagination_js_1.sliceWithCursor)(filtered, cursor, limit);
|
|
34
|
+
const page = (0, pagination_js_1.sliceWithCursor)(filtered, cursor, limit, needle ?? "");
|
|
35
35
|
return (0, shared_js_1.ok)({ diagrams: page.items.map(summarize_js_1.diagramListView), total: page.total, nextCursor: page.nextCursor });
|
|
36
36
|
}));
|
|
37
37
|
server.registerTool("get_diagram", {
|
|
38
38
|
title: "Get a diagram",
|
|
39
|
-
description: "Read a diagram: `graph` (nodes with type/label/parent and edges with
|
|
39
|
+
description: "Read a diagram: `summary` (counts, owner, sharing), `graph` (default — nodes with type/label/parent/style and edges with semantic/color, in the same shape the write tools accept: enough to describe, restyle or edit it) or `full` (the complete stored DiagramDocument with positions, sizes and routes, up to 512 KB).",
|
|
40
40
|
inputSchema: { diagram: (0, shared_js_1.refArg)("Diagram"), view: zod_1.z.enum(["summary", "graph", "full"]).default("graph") },
|
|
41
41
|
annotations: shared_js_1.READ,
|
|
42
42
|
}, (0, shared_js_1.guarded)(async ({ diagram, view }) => {
|
|
@@ -48,7 +48,7 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
48
48
|
}));
|
|
49
49
|
server.registerTool("create_diagram", {
|
|
50
50
|
title: "Create a system-design diagram",
|
|
51
|
-
description: "Create a diagram from typed nodes and edges. Node `type` must come from callman://diagram/components (e.g. net.service, net.gateway, db.postgres, mq.kafka, aws.lambda, flow.process); unknown types are rejected with suggestions unless strictTypes: false. Group nodes
|
|
51
|
+
description: "Create a diagram from typed nodes and edges. 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) plus an optional `color`. Layout is automatic when nodes have no positions. Read callman://docs/diagram-schema for every field. Keep it readable: 8–25 nodes, short labels.",
|
|
52
52
|
inputSchema: {
|
|
53
53
|
name: zod_1.z.string().min(1).max(200).describe("Diagram name (library entry)"),
|
|
54
54
|
...build_js_1.diagramInputSchema.shape,
|
|
@@ -81,7 +81,7 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
81
81
|
}));
|
|
82
82
|
server.registerTool("update_diagram", {
|
|
83
83
|
title: "Replace a diagram's content",
|
|
84
|
-
description: "Replace a diagram's whole document (nodes + edges) and/or rename it, keeping its identity and sharing. For small edits prefer patch_diagram.",
|
|
84
|
+
description: "Replace a diagram's whole document (nodes + edges, same fields as create_diagram) 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). For small edits — including restyling — prefer patch_diagram.",
|
|
85
85
|
inputSchema: {
|
|
86
86
|
diagram: (0, shared_js_1.refArg)("Diagram"),
|
|
87
87
|
name: zod_1.z.string().min(1).max(200).optional(),
|
|
@@ -95,8 +95,9 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
95
95
|
},
|
|
96
96
|
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
97
97
|
}, (0, shared_js_1.guarded)(async ({ diagram, name, title, nodes, edges, canvas, autoLayout, direction, strictTypes }) => {
|
|
98
|
-
if (name === undefined && nodes === undefined)
|
|
99
|
-
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass name and/or nodes (+ edges).");
|
|
98
|
+
if (name === undefined && nodes === undefined && title === undefined && canvas === undefined) {
|
|
99
|
+
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass name, title, canvas and/or nodes (+ edges).");
|
|
100
|
+
}
|
|
100
101
|
const resolved = await (0, lookups_js_1.resolveDiagram)(ctx, diagram);
|
|
101
102
|
if (!resolved.item.canEdit)
|
|
102
103
|
throw new errors_js_1.ToolInputError("DIAGRAM_READ_ONLY", "You have read-only access to this diagram.");
|
|
@@ -108,14 +109,21 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
108
109
|
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 });
|
|
109
110
|
document = built.document;
|
|
110
111
|
warnings = built.warnings;
|
|
112
|
+
if (edges === undefined && existing.edges.length > 0) {
|
|
113
|
+
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.`);
|
|
114
|
+
}
|
|
111
115
|
if (autoLayout || built.unpositionedNodeIds.length > 0) {
|
|
112
116
|
const laid = await (0, layout_js_1.layoutDocument)(document, { direction });
|
|
113
117
|
document = laid.document;
|
|
114
118
|
layoutNote = laid.warning ?? `Laid out with ${laid.engine}.`;
|
|
115
119
|
}
|
|
116
120
|
}
|
|
117
|
-
else if (title !== undefined) {
|
|
118
|
-
document = {
|
|
121
|
+
else if (title !== undefined || canvas !== undefined) {
|
|
122
|
+
document = {
|
|
123
|
+
...existing,
|
|
124
|
+
...(canvas !== undefined ? { canvas: { ...existing.canvas, ...canvas } } : {}),
|
|
125
|
+
meta: { ...existing.meta, ...(title !== undefined ? { title } : {}), updatedAt: new Date().toISOString() },
|
|
126
|
+
};
|
|
119
127
|
}
|
|
120
128
|
const updated = await ctx.api.updateDiagram(resolved.item.id, { ...(name !== undefined ? { name: name.trim() } : {}), ...(document ? { document } : {}) });
|
|
121
129
|
ctx.invalidate("diagrams");
|
|
@@ -123,7 +131,7 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
123
131
|
}));
|
|
124
132
|
server.registerTool("patch_diagram", {
|
|
125
133
|
title: "Edit a diagram incrementally",
|
|
126
|
-
description: "Apply operations to a diagram without resending it: addNode, updateNode, removeNode (drops its edges), addEdge, updateEdge, removeEdge, setTitle. `relayout`: none | new (place only new nodes, default) | all (re-run automatic layout).",
|
|
134
|
+
description: "Apply operations to a diagram without resending it: addNode, updateNode, removeNode (drops its edges), addEdge, updateEdge, removeEdge, setTitle. updateNode `changes` MERGE: style and data keys you pass change, a key set to null is removed. updateEdge can change semantic, color, label, arrows and ends. Moving a node (position/size/parent) 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.",
|
|
127
135
|
inputSchema: {
|
|
128
136
|
diagram: (0, shared_js_1.refArg)("Diagram"),
|
|
129
137
|
ops: zod_1.z.array(patch_js_1.diagramPatchOpSchema).min(1).max(200),
|
|
@@ -157,6 +165,7 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
157
165
|
added: patched.addedNodeIds,
|
|
158
166
|
removedNodes: patched.removedNodeIds,
|
|
159
167
|
removedEdges: patched.removedEdgeIds,
|
|
168
|
+
...(patched.reroutedEdgeIds.length > 0 ? { reroutedEdges: patched.reroutedEdgeIds } : {}),
|
|
160
169
|
warnings: patched.warnings,
|
|
161
170
|
...(layoutNote ? { layout: layoutNote } : {}),
|
|
162
171
|
resolved: [(0, resolve_js_1.resolvedSummary)(diagram, resolved)],
|
|
@@ -169,7 +178,7 @@ const registerDiagramTools = (server, ctx) => {
|
|
|
169
178
|
annotations: shared_js_1.DESTRUCTIVE,
|
|
170
179
|
}, (0, shared_js_1.guarded)(async ({ diagram, confirm }) => {
|
|
171
180
|
(0, shared_js_1.requireConfirm)(confirm, "a diagram");
|
|
172
|
-
const resolved = await (0, lookups_js_1.resolveDiagram)(ctx, diagram);
|
|
181
|
+
const resolved = await (0, lookups_js_1.resolveDiagram)(ctx, diagram, { exact: true });
|
|
173
182
|
await ctx.api.deleteDiagram(resolved.item.id);
|
|
174
183
|
ctx.invalidate("diagrams");
|
|
175
184
|
return (0, shared_js_1.ok)({ deleted: { id: resolved.item.id, name: resolved.item.name }, resolved: [(0, resolve_js_1.resolvedSummary)(diagram, resolved)] });
|