callman-mcp 0.1.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 +28 -0
- package/LICENSE +21 -0
- package/README.md +203 -0
- package/dist/client/api.d.ts +109 -0
- package/dist/client/api.d.ts.map +1 -0
- package/dist/client/api.js +180 -0
- package/dist/client/api.js.map +1 -0
- package/dist/client/errors.d.ts +27 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/errors.js +98 -0
- package/dist/client/errors.js.map +1 -0
- package/dist/client/http.d.ts +34 -0
- package/dist/client/http.d.ts.map +1 -0
- package/dist/client/http.js +121 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/types.d.ts +327 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +5 -0
- package/dist/client/types.js.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +69 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +35 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +91 -0
- package/dist/context.js.map +1 -0
- package/dist/domain/diagram/build.d.ts +260 -0
- package/dist/domain/diagram/build.d.ts.map +1 -0
- package/dist/domain/diagram/build.js +160 -0
- package/dist/domain/diagram/build.js.map +1 -0
- package/dist/domain/diagram/layout.d.ts +17 -0
- package/dist/domain/diagram/layout.d.ts.map +1 -0
- package/dist/domain/diagram/layout.js +61 -0
- package/dist/domain/diagram/layout.js.map +1 -0
- package/dist/domain/diagram/patch.d.ts +330 -0
- package/dist/domain/diagram/patch.d.ts.map +1 -0
- package/dist/domain/diagram/patch.js +176 -0
- package/dist/domain/diagram/patch.js.map +1 -0
- package/dist/domain/diagram/summarize.d.ts +41 -0
- package/dist/domain/diagram/summarize.d.ts.map +1 -0
- package/dist/domain/diagram/summarize.js +47 -0
- package/dist/domain/diagram/summarize.js.map +1 -0
- package/dist/domain/importDetect.d.ts +6 -0
- package/dist/domain/importDetect.d.ts.map +1 -0
- package/dist/domain/importDetect.js +32 -0
- package/dist/domain/importDetect.js.map +1 -0
- package/dist/domain/requestShape.d.ts +796 -0
- package/dist/domain/requestShape.d.ts.map +1 -0
- package/dist/domain/requestShape.js +250 -0
- package/dist/domain/requestShape.js.map +1 -0
- package/dist/domain/resolve.d.ts +30 -0
- package/dist/domain/resolve.d.ts.map +1 -0
- package/dist/domain/resolve.js +56 -0
- package/dist/domain/resolve.js.map +1 -0
- package/dist/domain/scenario/build.d.ts +270 -0
- package/dist/domain/scenario/build.d.ts.map +1 -0
- package/dist/domain/scenario/build.js +135 -0
- package/dist/domain/scenario/build.js.map +1 -0
- package/dist/domain/scenario/summarize.d.ts +206 -0
- package/dist/domain/scenario/summarize.d.ts.map +1 -0
- package/dist/domain/scenario/summarize.js +138 -0
- package/dist/domain/scenario/summarize.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +142 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cache.d.ts +12 -0
- package/dist/lib/cache.d.ts.map +1 -0
- package/dist/lib/cache.js +40 -0
- package/dist/lib/cache.js.map +1 -0
- package/dist/lib/ids.d.ts +5 -0
- package/dist/lib/ids.d.ts.map +1 -0
- package/dist/lib/ids.js +19 -0
- package/dist/lib/ids.js.map +1 -0
- package/dist/lib/log.d.ts +10 -0
- package/dist/lib/log.d.ts.map +1 -0
- package/dist/lib/log.js +25 -0
- package/dist/lib/log.js.map +1 -0
- package/dist/lib/mask.d.ts +24 -0
- package/dist/lib/mask.d.ts.map +1 -0
- package/dist/lib/mask.js +63 -0
- package/dist/lib/mask.js.map +1 -0
- package/dist/lib/pagination.d.ts +21 -0
- package/dist/lib/pagination.d.ts.map +1 -0
- package/dist/lib/pagination.js +40 -0
- package/dist/lib/pagination.js.map +1 -0
- package/dist/lib/truncate.d.ts +25 -0
- package/dist/lib/truncate.d.ts.map +1 -0
- package/dist/lib/truncate.js +45 -0
- package/dist/lib/truncate.js.map +1 -0
- package/dist/main.d.ts +9 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +12 -0
- package/dist/main.js.map +1 -0
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +72 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/resources/docs/diagramSchema.d.ts +2 -0
- package/dist/resources/docs/diagramSchema.d.ts.map +1 -0
- package/dist/resources/docs/diagramSchema.js +65 -0
- package/dist/resources/docs/diagramSchema.js.map +1 -0
- package/dist/resources/docs/overview.d.ts +2 -0
- package/dist/resources/docs/overview.d.ts.map +1 -0
- package/dist/resources/docs/overview.js +46 -0
- package/dist/resources/docs/overview.js.map +1 -0
- package/dist/resources/docs/requestSchema.d.ts +2 -0
- package/dist/resources/docs/requestSchema.d.ts.map +1 -0
- package/dist/resources/docs/requestSchema.js +70 -0
- package/dist/resources/docs/requestSchema.js.map +1 -0
- package/dist/resources/generated/aiPacks.d.ts +3 -0
- package/dist/resources/generated/aiPacks.d.ts.map +1 -0
- package/dist/resources/generated/aiPacks.js +11 -0
- package/dist/resources/generated/aiPacks.js.map +1 -0
- package/dist/resources/index.d.ts +4 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +114 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/run/executeCollection.d.ts +19 -0
- package/dist/run/executeCollection.d.ts.map +1 -0
- package/dist/run/executeCollection.js +89 -0
- package/dist/run/executeCollection.js.map +1 -0
- package/dist/run/loadBundle.d.ts +9 -0
- package/dist/run/loadBundle.d.ts.map +1 -0
- package/dist/run/loadBundle.js +242 -0
- package/dist/run/loadBundle.js.map +1 -0
- package/dist/run/resolveTemplate.d.ts +2 -0
- package/dist/run/resolveTemplate.d.ts.map +1 -0
- package/dist/run/resolveTemplate.js +12 -0
- package/dist/run/resolveTemplate.js.map +1 -0
- package/dist/run/summarize.d.ts +73 -0
- package/dist/run/summarize.d.ts.map +1 -0
- package/dist/run/summarize.js +69 -0
- package/dist/run/summarize.js.map +1 -0
- package/dist/run/types.d.ts +46 -0
- package/dist/run/types.d.ts.map +1 -0
- package/dist/run/types.js +2 -0
- package/dist/run/types.js.map +1 -0
- package/dist/server.d.ts +23 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +25 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/catalog.d.ts +18 -0
- package/dist/tools/catalog.d.ts.map +1 -0
- package/dist/tools/catalog.js +66 -0
- package/dist/tools/catalog.js.map +1 -0
- package/dist/tools/collections.d.ts +4 -0
- package/dist/tools/collections.d.ts.map +1 -0
- package/dist/tools/collections.js +201 -0
- package/dist/tools/collections.js.map +1 -0
- package/dist/tools/diagrams.d.ts +4 -0
- package/dist/tools/diagrams.d.ts.map +1 -0
- package/dist/tools/diagrams.js +226 -0
- package/dist/tools/diagrams.js.map +1 -0
- package/dist/tools/environments.d.ts +4 -0
- package/dist/tools/environments.d.ts.map +1 -0
- package/dist/tools/environments.js +150 -0
- package/dist/tools/environments.js.map +1 -0
- package/dist/tools/import.d.ts +6 -0
- package/dist/tools/import.d.ts.map +1 -0
- package/dist/tools/import.js +144 -0
- package/dist/tools/import.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +26 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/lookups.d.ts +33 -0
- package/dist/tools/lookups.d.ts.map +1 -0
- package/dist/tools/lookups.js +132 -0
- package/dist/tools/lookups.js.map +1 -0
- package/dist/tools/meta.d.ts +4 -0
- package/dist/tools/meta.d.ts.map +1 -0
- package/dist/tools/meta.js +78 -0
- package/dist/tools/meta.js.map +1 -0
- package/dist/tools/requests.d.ts +4 -0
- package/dist/tools/requests.d.ts.map +1 -0
- package/dist/tools/requests.js +247 -0
- package/dist/tools/requests.js.map +1 -0
- package/dist/tools/run.d.ts +4 -0
- package/dist/tools/run.d.ts.map +1 -0
- package/dist/tools/run.js +167 -0
- package/dist/tools/run.js.map +1 -0
- package/dist/tools/scenarios.d.ts +4 -0
- package/dist/tools/scenarios.d.ts.map +1 -0
- package/dist/tools/scenarios.js +242 -0
- package/dist/tools/scenarios.js.map +1 -0
- package/dist/tools/shared.d.ts +20 -0
- package/dist/tools/shared.d.ts.map +1 -0
- package/dist/tools/shared.js +74 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/workspaces.d.ts +4 -0
- package/dist/tools/workspaces.d.ts.map +1 -0
- package/dist/tools/workspaces.js +80 -0
- package/dist/tools/workspaces.js.map +1 -0
- package/dist-cjs/client/api.js +183 -0
- package/dist-cjs/client/errors.js +104 -0
- package/dist-cjs/client/http.js +124 -0
- package/dist-cjs/client/types.js +5 -0
- package/dist-cjs/config.js +73 -0
- package/dist-cjs/context.js +94 -0
- package/dist-cjs/domain/diagram/build.js +164 -0
- package/dist-cjs/domain/diagram/layout.js +98 -0
- package/dist-cjs/domain/diagram/patch.js +179 -0
- package/dist-cjs/domain/diagram/summarize.js +52 -0
- package/dist-cjs/domain/importDetect.js +40 -0
- package/dist-cjs/domain/requestShape.js +260 -0
- package/dist-cjs/domain/resolve.js +63 -0
- package/dist-cjs/domain/scenario/build.js +140 -0
- package/dist-cjs/domain/scenario/summarize.js +144 -0
- package/dist-cjs/lib/cache.js +43 -0
- package/dist-cjs/lib/ids.js +24 -0
- package/dist-cjs/lib/log.js +28 -0
- package/dist-cjs/lib/mask.js +72 -0
- package/dist-cjs/lib/pagination.js +48 -0
- package/dist-cjs/lib/truncate.js +52 -0
- package/dist-cjs/main.js +31 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/prompts/index.js +75 -0
- package/dist-cjs/resources/docs/diagramSchema.js +67 -0
- package/dist-cjs/resources/docs/overview.js +48 -0
- package/dist-cjs/resources/docs/requestSchema.js +72 -0
- package/dist-cjs/resources/generated/aiPacks.js +13 -0
- package/dist-cjs/resources/index.js +117 -0
- package/dist-cjs/run/executeCollection.js +93 -0
- package/dist-cjs/run/loadBundle.js +246 -0
- package/dist-cjs/run/resolveTemplate.js +15 -0
- package/dist-cjs/run/summarize.js +73 -0
- package/dist-cjs/run/types.js +2 -0
- package/dist-cjs/server.js +28 -0
- package/dist-cjs/tools/catalog.js +69 -0
- package/dist-cjs/tools/collections.js +204 -0
- package/dist-cjs/tools/diagrams.js +229 -0
- package/dist-cjs/tools/environments.js +153 -0
- package/dist-cjs/tools/import.js +147 -0
- package/dist-cjs/tools/index.js +31 -0
- package/dist-cjs/tools/lookups.js +149 -0
- package/dist-cjs/tools/meta.js +81 -0
- package/dist-cjs/tools/requests.js +250 -0
- package/dist-cjs/tools/run.js +203 -0
- package/dist-cjs/tools/scenarios.js +245 -0
- package/dist-cjs/tools/shared.js +81 -0
- package/dist-cjs/tools/workspaces.js +83 -0
- package/package.json +80 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerEnvironmentTools = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const errors_js_1 = require("../client/errors.js");
|
|
6
|
+
const requestShape_js_1 = require("../domain/requestShape.js");
|
|
7
|
+
const resolve_js_1 = require("../domain/resolve.js");
|
|
8
|
+
const mask_js_1 = require("../lib/mask.js");
|
|
9
|
+
const truncate_js_1 = require("../lib/truncate.js");
|
|
10
|
+
const lookups_js_1 = require("./lookups.js");
|
|
11
|
+
const shared_js_1 = require("./shared.js");
|
|
12
|
+
// Environments hold the values requests reference as {{key}}. Values come
|
|
13
|
+
// back MASKED for a personal token (••••ab) unless the token holds
|
|
14
|
+
// environments:reveal AND the call asks for reveal — the server enforces
|
|
15
|
+
// both; this side only forwards the flag and refuses to write a masked
|
|
16
|
+
// value back.
|
|
17
|
+
const registerEnvironmentTools = (server, ctx) => {
|
|
18
|
+
server.registerTool("list_environments", {
|
|
19
|
+
title: "List environments",
|
|
20
|
+
description: "List a workspace's environments with their variable KEYS (values are not included). Global variables (shared by every environment) are listed too.",
|
|
21
|
+
inputSchema: { workspace: shared_js_1.workspaceArg },
|
|
22
|
+
annotations: shared_js_1.READ,
|
|
23
|
+
}, (0, shared_js_1.guarded)(async ({ workspace }) => {
|
|
24
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
25
|
+
const [environments, globals] = await Promise.all([
|
|
26
|
+
(0, lookups_js_1.loadEnvironments)(ctx, ws.id),
|
|
27
|
+
ctx.api.listGlobalVariables(ws.id).catch(() => []),
|
|
28
|
+
]);
|
|
29
|
+
return (0, shared_js_1.ok)({
|
|
30
|
+
workspace: { id: ws.id, name: ws.name },
|
|
31
|
+
environments: environments.map((environment) => ({
|
|
32
|
+
id: environment.id,
|
|
33
|
+
name: environment.name,
|
|
34
|
+
variableCount: environment.variables.length,
|
|
35
|
+
variableKeys: environment.variables.map((variable) => variable.key).slice(0, 200),
|
|
36
|
+
})),
|
|
37
|
+
globalVariableKeys: globals.map((variable) => variable.key),
|
|
38
|
+
});
|
|
39
|
+
}));
|
|
40
|
+
server.registerTool("get_environment", {
|
|
41
|
+
title: "Get an environment",
|
|
42
|
+
description: "Read one environment's variables. Values are masked (••••ab) unless reveal: true AND the token holds environments:reveal. Global variables are returned alongside.",
|
|
43
|
+
inputSchema: { workspace: shared_js_1.workspaceArg, environment: (0, shared_js_1.refArg)("Environment"), reveal: zod_1.z.boolean().optional() },
|
|
44
|
+
annotations: shared_js_1.READ,
|
|
45
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, environment, reveal }) => {
|
|
46
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
47
|
+
const resolved = await (0, lookups_js_1.resolveEnvironment)(ctx, ws.id, environment);
|
|
48
|
+
const [fresh, globals] = await Promise.all([
|
|
49
|
+
ctx.api.getEnvironment(ws.id, resolved.item.id, reveal ?? false),
|
|
50
|
+
ctx.api.listGlobalVariables(ws.id, reveal ?? false).catch(() => []),
|
|
51
|
+
]);
|
|
52
|
+
const masked = fresh.variables.some((variable) => variable.valueMasked);
|
|
53
|
+
return (0, shared_js_1.ok)({
|
|
54
|
+
environment: {
|
|
55
|
+
id: fresh.id,
|
|
56
|
+
name: fresh.name,
|
|
57
|
+
variables: fresh.variables.map((variable) => ({ key: variable.key, value: variable.value, ...(variable.enabled === false ? { enabled: false } : {}) })),
|
|
58
|
+
},
|
|
59
|
+
globalVariables: globals.map((variable) => ({ key: variable.key, value: variable.value })),
|
|
60
|
+
valuesMasked: masked,
|
|
61
|
+
...(masked && reveal ? { note: "Values stayed masked: the token lacks environments:reveal." } : {}),
|
|
62
|
+
resolved: [(0, resolve_js_1.resolvedSummary)(environment, resolved)],
|
|
63
|
+
});
|
|
64
|
+
}));
|
|
65
|
+
server.registerTool("create_environment", {
|
|
66
|
+
title: "Create an environment",
|
|
67
|
+
description: "Create an environment with optional variables (key/value). Use it for hosts ({{baseUrl}}) and credentials so requests never hard-code them.",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
workspace: shared_js_1.workspaceArg,
|
|
70
|
+
name: zod_1.z.string().min(1).max(truncate_js_1.LIMITS.NAME),
|
|
71
|
+
variables: zod_1.z.array(requestShape_js_1.kvSchema).max(500).optional(),
|
|
72
|
+
},
|
|
73
|
+
annotations: shared_js_1.WRITE,
|
|
74
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, name, variables }) => {
|
|
75
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
76
|
+
const rows = (0, requestShape_js_1.toRows)(variables);
|
|
77
|
+
const maskedKeys = rows.filter((row) => (0, mask_js_1.isMasked)(row.value)).map((row) => row.key);
|
|
78
|
+
if (maskedKeys.length > 0)
|
|
79
|
+
throw new errors_js_1.ToolInputError("MASKED_VALUE", `Masked values cannot be stored (keys: ${maskedKeys.join(", ")}). Provide the real values.`);
|
|
80
|
+
const created = await ctx.api.createEnvironment(ws.id, { name: name.trim(), variables: rows });
|
|
81
|
+
ctx.invalidate(`environments:${ws.id}`);
|
|
82
|
+
return (0, shared_js_1.ok)({ environment: { id: created.id, name: created.name, variableKeys: created.variables.map((variable) => variable.key) } });
|
|
83
|
+
}));
|
|
84
|
+
server.registerTool("set_environment_variables", {
|
|
85
|
+
title: "Set / unset environment variables",
|
|
86
|
+
description: "Upsert variables (`set`) and/or remove keys (`unset`) in an environment. Existing keys are updated in place, new keys appended, other keys untouched. A masked value (starting with ••••) is refused — read it with reveal or supply a real value.",
|
|
87
|
+
inputSchema: {
|
|
88
|
+
workspace: shared_js_1.workspaceArg,
|
|
89
|
+
environment: (0, shared_js_1.refArg)("Environment"),
|
|
90
|
+
set: zod_1.z.array(requestShape_js_1.kvSchema).max(500).optional(),
|
|
91
|
+
unset: zod_1.z.array(zod_1.z.string().min(1).max(100)).max(500).optional(),
|
|
92
|
+
},
|
|
93
|
+
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
94
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, environment, set, unset }) => {
|
|
95
|
+
const rows = (0, requestShape_js_1.toRows)(set);
|
|
96
|
+
const removals = (unset ?? []).map((key) => key.trim()).filter(Boolean);
|
|
97
|
+
if (rows.length === 0 && removals.length === 0)
|
|
98
|
+
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass set and/or unset.");
|
|
99
|
+
const maskedKeys = rows.filter((row) => (0, mask_js_1.isMasked)(row.value)).map((row) => row.key);
|
|
100
|
+
if (maskedKeys.length > 0)
|
|
101
|
+
throw new errors_js_1.ToolInputError("MASKED_VALUE", `Masked values cannot be written back (keys: ${maskedKeys.join(", ")}).`);
|
|
102
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
103
|
+
const resolved = await (0, lookups_js_1.resolveEnvironment)(ctx, ws.id, environment);
|
|
104
|
+
const existingKeys = new Set(resolved.item.variables.map((variable) => variable.key));
|
|
105
|
+
const updated = [];
|
|
106
|
+
const added = [];
|
|
107
|
+
const removed = [];
|
|
108
|
+
const failed = [];
|
|
109
|
+
for (const row of rows) {
|
|
110
|
+
try {
|
|
111
|
+
if (existingKeys.has(row.key)) {
|
|
112
|
+
await ctx.api.updateEnvironmentVariable(ws.id, resolved.item.id, row.key, { value: row.value });
|
|
113
|
+
updated.push(row.key);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
await ctx.api.addEnvironmentVariable(ws.id, resolved.item.id, { key: row.key, value: row.value });
|
|
117
|
+
added.push(row.key);
|
|
118
|
+
existingKeys.add(row.key);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
failed.push({ key: row.key, error: error instanceof Error ? error.message : String(error) });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (const key of removals) {
|
|
126
|
+
try {
|
|
127
|
+
if (!existingKeys.has(key))
|
|
128
|
+
continue;
|
|
129
|
+
await ctx.api.deleteEnvironmentVariable(ws.id, resolved.item.id, key);
|
|
130
|
+
removed.push(key);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
failed.push({ key, error: error instanceof Error ? error.message : String(error) });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
ctx.invalidate(`environments:${ws.id}`);
|
|
137
|
+
return (0, shared_js_1.ok)({ environment: { id: resolved.item.id, name: resolved.item.name }, updated, added, removed, failed, resolved: [(0, resolve_js_1.resolvedSummary)(environment, resolved)] });
|
|
138
|
+
}));
|
|
139
|
+
server.registerTool("delete_environment", {
|
|
140
|
+
title: "Delete an environment",
|
|
141
|
+
description: "Delete an environment and all its variables. Permanent — confirm with the user first.",
|
|
142
|
+
inputSchema: { workspace: shared_js_1.workspaceArg, environment: (0, shared_js_1.refArg)("Environment"), confirm: shared_js_1.confirmArg },
|
|
143
|
+
annotations: shared_js_1.DESTRUCTIVE,
|
|
144
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, environment, confirm }) => {
|
|
145
|
+
(0, shared_js_1.requireConfirm)(confirm, "an environment");
|
|
146
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
147
|
+
const resolved = await (0, lookups_js_1.resolveEnvironment)(ctx, ws.id, environment);
|
|
148
|
+
await ctx.api.deleteEnvironment(ws.id, resolved.item.id);
|
|
149
|
+
ctx.invalidate(`environments:${ws.id}`);
|
|
150
|
+
return (0, shared_js_1.ok)({ deleted: { id: resolved.item.id, name: resolved.item.name }, resolved: [(0, resolve_js_1.resolvedSummary)(environment, resolved)] });
|
|
151
|
+
}));
|
|
152
|
+
};
|
|
153
|
+
exports.registerEnvironmentTools = registerEnvironmentTools;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requestRef = exports.registerImportTools = void 0;
|
|
4
|
+
const callman_core_1 = require("callman-core");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const errors_js_1 = require("../client/errors.js");
|
|
7
|
+
const importDetect_js_1 = require("../domain/importDetect.js");
|
|
8
|
+
const resolve_js_1 = require("../domain/resolve.js");
|
|
9
|
+
const truncate_js_1 = require("../lib/truncate.js");
|
|
10
|
+
const lookups_js_1 = require("./lookups.js");
|
|
11
|
+
const shared_js_1 = require("./shared.js");
|
|
12
|
+
const CONTENT_MAX = 8 * 1024 * 1024;
|
|
13
|
+
const IMPORT_FORMATS = ["auto", "postman", "openapi", "insomnia", "bruno", "internal", "curl", "http"];
|
|
14
|
+
const registerImportTools = (server, ctx) => {
|
|
15
|
+
server.registerTool("import_content", {
|
|
16
|
+
title: "Import a file's content",
|
|
17
|
+
description: "Import API definitions from file CONTENT you read: Postman collection, OpenAPI/Swagger (JSON or YAML), Insomnia, Bruno, a Callman export, a cURL script, or a .http/.rest file. The format is detected (`format: auto`). Document formats always create a new collection; cURL/.http create a new collection (`collectionName`) or add to an existing one (`targetCollection` + optional `folder`). Use dryRun: true to see what was detected before writing. For endpoints found in source code, read the routes yourself and call create_requests_bulk instead.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
workspace: shared_js_1.workspaceArg,
|
|
20
|
+
content: zod_1.z.string().min(1).max(CONTENT_MAX).describe("The raw file content"),
|
|
21
|
+
fileName: zod_1.z.string().max(300).optional().describe("Original file name — helps detection (.bru, .http, .yaml …)"),
|
|
22
|
+
format: zod_1.z.enum(IMPORT_FORMATS).default("auto"),
|
|
23
|
+
collectionName: zod_1.z.string().max(truncate_js_1.LIMITS.NAME).optional().describe("Name for the new collection (documents: fallback when the file carries no name)"),
|
|
24
|
+
targetCollection: zod_1.z.string().max(200).optional().describe("cURL/.http only: add to this existing collection instead of creating one"),
|
|
25
|
+
folder: zod_1.z.string().max(300).optional().describe("cURL/.http + targetCollection: folder to put the requests in"),
|
|
26
|
+
dryRun: shared_js_1.dryRunArg,
|
|
27
|
+
},
|
|
28
|
+
annotations: shared_js_1.WRITE,
|
|
29
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, content, fileName, format, collectionName, targetCollection, folder, dryRun }) => {
|
|
30
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
31
|
+
const detection = (0, importDetect_js_1.detectImport)(fileName, content);
|
|
32
|
+
const resolvedFormat = format === "auto" ? detection.format : format;
|
|
33
|
+
if (resolvedFormat === "unknown" || resolvedFormat === "scenario" || resolvedFormat === "environment") {
|
|
34
|
+
throw new errors_js_1.ToolInputError("IMPORT_FORMAT_UNSUPPORTED", resolvedFormat === "scenario"
|
|
35
|
+
? "This is a Callman scenario export — use import_scenario."
|
|
36
|
+
: resolvedFormat === "environment"
|
|
37
|
+
? "This is an environment export — create it with create_environment / set_environment_variables."
|
|
38
|
+
: "Could not detect the format. Pass `format` explicitly (postman, openapi, insomnia, bruno, internal, curl, http).", { detected: detection });
|
|
39
|
+
}
|
|
40
|
+
if (resolvedFormat === "curl" || resolvedFormat === "http") {
|
|
41
|
+
const name = fileName ?? (resolvedFormat === "curl" ? "requests.sh" : "requests.http");
|
|
42
|
+
let payload;
|
|
43
|
+
try {
|
|
44
|
+
payload = resolvedFormat === "curl" ? (0, callman_core_1.parseCurlFile)(name, content) : (0, callman_core_1.parseHttpFile)(name, content);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw new errors_js_1.ToolInputError("IMPORT_PARSE_FAILED", error instanceof Error ? error.message : String(error));
|
|
48
|
+
}
|
|
49
|
+
if (collectionName)
|
|
50
|
+
payload = { ...payload, collectionName: collectionName.trim() };
|
|
51
|
+
const sample = payload.requests.slice(0, 5).map((request) => `${request.method} ${request.name}`);
|
|
52
|
+
if (targetCollection) {
|
|
53
|
+
const resolved = await (0, lookups_js_1.resolveCollection)(ctx, ws.id, targetCollection);
|
|
54
|
+
const targetFolder = await (0, lookups_js_1.resolveFolder)(ctx, ws.id, resolved.item.id, folder);
|
|
55
|
+
if (dryRun) {
|
|
56
|
+
return (0, shared_js_1.ok)({ dryRun: true, detected: detection, format: resolvedFormat, mode: "existing-collection", collection: { id: resolved.item.id, name: resolved.item.name }, requestCount: payload.requests.length, sample });
|
|
57
|
+
}
|
|
58
|
+
const created = [];
|
|
59
|
+
const failed = [];
|
|
60
|
+
for (const request of payload.requests) {
|
|
61
|
+
try {
|
|
62
|
+
const row = await ctx.api.createRequest(ws.id, { ...request, collectionId: resolved.item.id, folderId: targetFolder?.item.id ?? null });
|
|
63
|
+
created.push({ id: row.id, name: row.name, method: row.method });
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
failed.push({ name: request.name, error: error instanceof errors_js_1.CallmanApiError ? `${error.code}: ${error.message}` : String(error) });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
ctx.invalidate(`tree:${ws.id}:${resolved.item.id}`);
|
|
70
|
+
ctx.invalidate(`collections:${ws.id}`);
|
|
71
|
+
return (0, shared_js_1.ok)({ format: resolvedFormat, collection: { id: resolved.item.id, name: resolved.item.name }, created: created.length, failed, requests: created, resolved: [(0, resolve_js_1.resolvedSummary)(targetCollection, resolved)] });
|
|
72
|
+
}
|
|
73
|
+
const existing = (await (0, lookups_js_1.loadCollections)(ctx, ws.id)).find((entry) => (0, lookups_js_1.sameName)(entry.name, payload.collectionName));
|
|
74
|
+
if (existing) {
|
|
75
|
+
throw new errors_js_1.ToolInputError("COLLECTION_NAME_EXISTS", `A collection named "${existing.name}" already exists. Pass collectionName for a new name, or targetCollection: "${existing.id}" to add to it.`, {
|
|
76
|
+
existing: { id: existing.id, name: existing.name },
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (dryRun) {
|
|
80
|
+
return (0, shared_js_1.ok)({ dryRun: true, detected: detection, format: resolvedFormat, mode: "new-collection", collectionName: payload.collectionName, requestCount: payload.requests.length, sample });
|
|
81
|
+
}
|
|
82
|
+
const result = await ctx.api.importJson(ws.id, { format: "requests", payload });
|
|
83
|
+
ctx.invalidate(`collections:${ws.id}`);
|
|
84
|
+
return (0, shared_js_1.ok)({ format: resolvedFormat, collection: { id: result.collection.id, name: result.collection.name }, created: result.importedCounts });
|
|
85
|
+
}
|
|
86
|
+
if (!importDetect_js_1.SERVER_DOCUMENT_FORMATS.has(resolvedFormat)) {
|
|
87
|
+
throw new errors_js_1.ToolInputError("IMPORT_FORMAT_UNSUPPORTED", `Format "${resolvedFormat}" is not importable here.`);
|
|
88
|
+
}
|
|
89
|
+
const payload = (0, importDetect_js_1.documentPayload)(resolvedFormat, content);
|
|
90
|
+
if (dryRun) {
|
|
91
|
+
return (0, shared_js_1.ok)({ dryRun: true, detected: detection, format: resolvedFormat, mode: "new-collection", note: "The server parses this document; request counts are known after the import." });
|
|
92
|
+
}
|
|
93
|
+
const result = await ctx.api.importJson(ws.id, { format: resolvedFormat, payload, ...(collectionName ? { fallbackCollectionName: collectionName.trim() } : {}) });
|
|
94
|
+
ctx.invalidate(`collections:${ws.id}`);
|
|
95
|
+
return (0, shared_js_1.ok)({ format: resolvedFormat, collection: { id: result.collection.id, name: result.collection.name }, created: result.importedCounts });
|
|
96
|
+
}));
|
|
97
|
+
server.registerTool("import_scenario", {
|
|
98
|
+
title: "Import a scenario export",
|
|
99
|
+
description: "Import a Callman scenario export (a `callman.scenario` envelope — the JSON of a .callman.scenario file, as text or object). Connections referenced by the export that have no counterpart here are skipped (the nodes import without a connection; the result lists warnings).",
|
|
100
|
+
inputSchema: {
|
|
101
|
+
workspace: shared_js_1.workspaceArg,
|
|
102
|
+
envelope: zod_1.z.union([zod_1.z.string().max(CONTENT_MAX), zod_1.z.record(zod_1.z.unknown())]).describe("The export envelope (JSON text or object)"),
|
|
103
|
+
name: zod_1.z.string().max(200).optional().describe("Override the imported scenario's name"),
|
|
104
|
+
folder: zod_1.z.string().max(200).optional().describe("Scenario folder id or name"),
|
|
105
|
+
dryRun: shared_js_1.dryRunArg,
|
|
106
|
+
},
|
|
107
|
+
annotations: shared_js_1.WRITE,
|
|
108
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, envelope, name, folder, dryRun }) => {
|
|
109
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
110
|
+
let parsed = envelope;
|
|
111
|
+
if (typeof envelope === "string") {
|
|
112
|
+
try {
|
|
113
|
+
parsed = JSON.parse(envelope);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
throw new errors_js_1.ToolInputError("IMPORT_INVALID_JSON", "The envelope is not valid JSON.");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const record = parsed;
|
|
120
|
+
if (record?.kind !== "callman.scenario") {
|
|
121
|
+
throw new errors_js_1.ToolInputError("IMPORT_NOT_SCENARIO", "This is not a Callman scenario export (expected kind: \"callman.scenario\").");
|
|
122
|
+
}
|
|
123
|
+
const targetFolder = folder ? await (0, lookups_js_1.resolveScenarioFolder)(ctx, ws.id, folder) : null;
|
|
124
|
+
const connections = record.connections ?? [];
|
|
125
|
+
const connectionMap = Object.fromEntries(connections.filter((connection) => connection.refId).map((connection) => [connection.refId, { action: "skip" }]));
|
|
126
|
+
if (dryRun) {
|
|
127
|
+
return (0, shared_js_1.ok)({
|
|
128
|
+
dryRun: true,
|
|
129
|
+
scenario: { name: name ?? record.scenario?.name ?? null, nodeCount: Array.isArray(record.scenario?.nodes) ? record.scenario?.nodes.length : null },
|
|
130
|
+
connectionsSkipped: connections.map((connection) => `${connection.name ?? connection.refId} (${connection.type ?? "?"})`),
|
|
131
|
+
folder: targetFolder ? { id: targetFolder.item.id, name: targetFolder.item.name } : null,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
const result = await ctx.api.importScenario(ws.id, { envelope: parsed, ...(name ? { name } : {}), folderId: targetFolder?.item.id ?? null, connectionMap });
|
|
135
|
+
ctx.invalidate("scenarios:");
|
|
136
|
+
const scenario = result.scenario;
|
|
137
|
+
return (0, shared_js_1.ok)({
|
|
138
|
+
scenario: { id: scenario.id, name: scenario.name, nodeCount: scenario.nodes?.length ?? null, reviewStatus: scenario.reviewStatus ?? null },
|
|
139
|
+
warnings: result.warnings ?? [],
|
|
140
|
+
...(connections.length > 0 ? { note: "Connections from the export were skipped — attach the right workspace connections to the DB/Kafka/Redis nodes in the desktop app." } : {}),
|
|
141
|
+
...(targetFolder ? { resolved: [(0, resolve_js_1.resolvedSummary)(folder, targetFolder)] } : {}),
|
|
142
|
+
});
|
|
143
|
+
}));
|
|
144
|
+
};
|
|
145
|
+
exports.registerImportTools = registerImportTools;
|
|
146
|
+
// Re-exported for the run/collection tools' folder resolution symmetry.
|
|
147
|
+
exports.requestRef = shared_js_1.refArg;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerAllTools = exports.toolAvailability = exports.TOOL_CATALOG = void 0;
|
|
4
|
+
const collections_js_1 = require("./collections.js");
|
|
5
|
+
const diagrams_js_1 = require("./diagrams.js");
|
|
6
|
+
const environments_js_1 = require("./environments.js");
|
|
7
|
+
const import_js_1 = require("./import.js");
|
|
8
|
+
const meta_js_1 = require("./meta.js");
|
|
9
|
+
const requests_js_1 = require("./requests.js");
|
|
10
|
+
const run_js_1 = require("./run.js");
|
|
11
|
+
const scenarios_js_1 = require("./scenarios.js");
|
|
12
|
+
const workspaces_js_1 = require("./workspaces.js");
|
|
13
|
+
var catalog_js_1 = require("./catalog.js");
|
|
14
|
+
Object.defineProperty(exports, "TOOL_CATALOG", { enumerable: true, get: function () { return catalog_js_1.TOOL_CATALOG; } });
|
|
15
|
+
Object.defineProperty(exports, "toolAvailability", { enumerable: true, get: function () { return catalog_js_1.toolAvailability; } });
|
|
16
|
+
// Registration order = the order clients list tools in. Run tools are only
|
|
17
|
+
// registered when local execution is allowed (a backend-hosted server passes
|
|
18
|
+
// allowLocalRun: false and they disappear from tools/list).
|
|
19
|
+
const registerAllTools = (server, ctx) => {
|
|
20
|
+
(0, meta_js_1.registerMetaTools)(server, ctx);
|
|
21
|
+
(0, workspaces_js_1.registerWorkspaceTools)(server, ctx);
|
|
22
|
+
(0, collections_js_1.registerCollectionTools)(server, ctx);
|
|
23
|
+
(0, requests_js_1.registerRequestTools)(server, ctx);
|
|
24
|
+
(0, import_js_1.registerImportTools)(server, ctx);
|
|
25
|
+
(0, environments_js_1.registerEnvironmentTools)(server, ctx);
|
|
26
|
+
(0, scenarios_js_1.registerScenarioTools)(server, ctx);
|
|
27
|
+
(0, diagrams_js_1.registerDiagramTools)(server, ctx);
|
|
28
|
+
if (ctx.config.allowLocalRun)
|
|
29
|
+
(0, run_js_1.registerRunTools)(server, ctx);
|
|
30
|
+
};
|
|
31
|
+
exports.registerAllTools = registerAllTools;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sameName = exports.resolveDiagram = exports.loadDiagrams = exports.resolveScenarioFolder = exports.resolveScenario = exports.loadScenarioCatalog = exports.resolveEnvironment = exports.loadEnvironments = exports.resolveRequest = exports.searchRequests = exports.requestIdsInFolder = exports.resolveFolder = exports.loadTree = exports.resolveCollection = exports.loadCollections = void 0;
|
|
4
|
+
const resolve_js_1 = require("../domain/resolve.js");
|
|
5
|
+
const ids_js_1 = require("../lib/ids.js");
|
|
6
|
+
const truncate_js_1 = require("../lib/truncate.js");
|
|
7
|
+
// Cached lookups + reference resolution shared by the tool modules. Reads go
|
|
8
|
+
// through the context cache (15 s TTL); every write tool invalidates the
|
|
9
|
+
// prefixes it touches so the next read is fresh.
|
|
10
|
+
const loadCollections = (ctx, ws) => ctx.cache.getOrSet(`collections:${ws}`, () => ctx.api.listAllCollections(ws));
|
|
11
|
+
exports.loadCollections = loadCollections;
|
|
12
|
+
const resolveCollection = async (ctx, ws, ref) => {
|
|
13
|
+
const collections = await (0, exports.loadCollections)(ctx, ws);
|
|
14
|
+
if ((0, ids_js_1.isObjectId)(ref) && !collections.some((collection) => collection.id === ref)) {
|
|
15
|
+
// Not in the cached list (just created?) — let the server decide.
|
|
16
|
+
try {
|
|
17
|
+
const fresh = await ctx.api.getCollection(ws, ref);
|
|
18
|
+
return { item: fresh, matched: "id" };
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// fall through to the resolver's error with candidates
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return (0, resolve_js_1.resolveRef)("collection", ref, collections, {
|
|
25
|
+
describe: (collection) => ({ requestCount: collection.requestCount ?? null }),
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
exports.resolveCollection = resolveCollection;
|
|
29
|
+
const loadTree = (ctx, ws, collectionId) => ctx.cache.getOrSet(`tree:${ws}:${collectionId}`, async () => {
|
|
30
|
+
const [folderTree, requests] = await Promise.all([ctx.api.folderTree(ws, collectionId), ctx.api.requestTree(ws, collectionId)]);
|
|
31
|
+
return { folders: (0, resolve_js_1.flattenFolderTree)(folderTree.tree ?? []), requests };
|
|
32
|
+
});
|
|
33
|
+
exports.loadTree = loadTree;
|
|
34
|
+
const resolveFolder = async (ctx, ws, collectionId, ref) => {
|
|
35
|
+
if (!ref || !ref.trim())
|
|
36
|
+
return null;
|
|
37
|
+
const tree = await (0, exports.loadTree)(ctx, ws, collectionId);
|
|
38
|
+
return (0, resolve_js_1.resolveFolderRef)(ref, tree.folders);
|
|
39
|
+
};
|
|
40
|
+
exports.resolveFolder = resolveFolder;
|
|
41
|
+
// Request ids inside a folder, subfolders included.
|
|
42
|
+
const requestIdsInFolder = (tree, folderId) => {
|
|
43
|
+
const childrenOf = new Map();
|
|
44
|
+
for (const folder of tree.folders) {
|
|
45
|
+
const list = childrenOf.get(folder.parentId) ?? [];
|
|
46
|
+
list.push(folder.id);
|
|
47
|
+
childrenOf.set(folder.parentId, list);
|
|
48
|
+
}
|
|
49
|
+
const folderIds = new Set([folderId]);
|
|
50
|
+
const queue = [folderId];
|
|
51
|
+
while (queue.length > 0) {
|
|
52
|
+
const current = queue.shift();
|
|
53
|
+
for (const child of childrenOf.get(current) ?? []) {
|
|
54
|
+
if (!folderIds.has(child)) {
|
|
55
|
+
folderIds.add(child);
|
|
56
|
+
queue.push(child);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return tree.requests.filter((request) => request.folderId && folderIds.has(request.folderId)).map((request) => request.id);
|
|
61
|
+
};
|
|
62
|
+
exports.requestIdsInFolder = requestIdsInFolder;
|
|
63
|
+
// Search requests across the workspace (or one collection): every query token
|
|
64
|
+
// must appear in "name method url" (case-insensitive) — the desktop
|
|
65
|
+
// assistant's rule.
|
|
66
|
+
const searchRequests = async (ctx, ws, query, collectionId, limit = truncate_js_1.LIMITS.SEARCH_RESULTS) => {
|
|
67
|
+
const tokens = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
68
|
+
const collections = await (0, exports.loadCollections)(ctx, ws);
|
|
69
|
+
const targets = collectionId ? collections.filter((collection) => collection.id === collectionId) : collections;
|
|
70
|
+
const matches = [];
|
|
71
|
+
for (const collection of targets) {
|
|
72
|
+
if (matches.length >= limit)
|
|
73
|
+
break;
|
|
74
|
+
const tree = await (0, exports.loadTree)(ctx, ws, collection.id);
|
|
75
|
+
for (const request of tree.requests) {
|
|
76
|
+
if (matches.length >= limit)
|
|
77
|
+
break;
|
|
78
|
+
const haystack = `${request.name} ${request.method} ${request.url}`.toLowerCase();
|
|
79
|
+
if (tokens.every((token) => haystack.includes(token)))
|
|
80
|
+
matches.push({ ...request, collectionName: collection.name });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return matches;
|
|
84
|
+
};
|
|
85
|
+
exports.searchRequests = searchRequests;
|
|
86
|
+
// A request reference: an id (fetched directly) or a name — unique across the
|
|
87
|
+
// given collection, or across the workspace when no collection is given.
|
|
88
|
+
const resolveRequest = async (ctx, ws, ref, collectionRef) => {
|
|
89
|
+
if ((0, ids_js_1.isObjectId)(ref)) {
|
|
90
|
+
return { request: await ctx.api.getRequest(ws, ref) };
|
|
91
|
+
}
|
|
92
|
+
const collectionId = collectionRef ? (await (0, exports.resolveCollection)(ctx, ws, collectionRef)).item.id : null;
|
|
93
|
+
const collections = await (0, exports.loadCollections)(ctx, ws);
|
|
94
|
+
const targets = collectionId ? collections.filter((collection) => collection.id === collectionId) : collections;
|
|
95
|
+
const candidates = [];
|
|
96
|
+
for (const collection of targets) {
|
|
97
|
+
const tree = await (0, exports.loadTree)(ctx, ws, collection.id);
|
|
98
|
+
for (const request of tree.requests)
|
|
99
|
+
candidates.push({ ...request, collectionName: collection.name });
|
|
100
|
+
}
|
|
101
|
+
const resolved = (0, resolve_js_1.resolveRef)("request", ref, candidates, {
|
|
102
|
+
describe: (request) => ({ method: request.method, url: request.url, collection: request.collectionName }),
|
|
103
|
+
aliases: (request) => [`${request.method} ${request.url}`, request.url],
|
|
104
|
+
});
|
|
105
|
+
return { request: await ctx.api.getRequest(ws, resolved.item.id), annotation: resolved.annotation };
|
|
106
|
+
};
|
|
107
|
+
exports.resolveRequest = resolveRequest;
|
|
108
|
+
const loadEnvironments = (ctx, ws) => ctx.cache.getOrSet(`environments:${ws}`, async () => (await ctx.api.listEnvironments(ws)).items);
|
|
109
|
+
exports.loadEnvironments = loadEnvironments;
|
|
110
|
+
const resolveEnvironment = async (ctx, ws, ref) => (0, resolve_js_1.resolveRef)("environment", ref, await (0, exports.loadEnvironments)(ctx, ws), {
|
|
111
|
+
describe: (environment) => ({ variables: environment.variables.length }),
|
|
112
|
+
});
|
|
113
|
+
exports.resolveEnvironment = resolveEnvironment;
|
|
114
|
+
const loadScenarioCatalog = (ctx, ws) => ctx.cache.getOrSet(`scenarios:${ws ?? "-"}`, async () => {
|
|
115
|
+
const [scenarios, foldersRaw] = await Promise.all([
|
|
116
|
+
ctx.api.listScenarios(ws),
|
|
117
|
+
ctx.api.listScenarioFolders(ws).catch(() => []),
|
|
118
|
+
]);
|
|
119
|
+
const folders = Array.isArray(foldersRaw) ? foldersRaw : (foldersRaw.items ?? []);
|
|
120
|
+
return { scenarios, folders };
|
|
121
|
+
});
|
|
122
|
+
exports.loadScenarioCatalog = loadScenarioCatalog;
|
|
123
|
+
const resolveScenario = async (ctx, ws, ref) => {
|
|
124
|
+
if ((0, ids_js_1.isObjectId)(ref)) {
|
|
125
|
+
return { item: await ctx.api.getScenario(ws, ref), matched: "id" };
|
|
126
|
+
}
|
|
127
|
+
const { scenarios, folders } = await (0, exports.loadScenarioCatalog)(ctx, ws);
|
|
128
|
+
const folderName = new Map(folders.map((folder) => [folder.id, folder.name]));
|
|
129
|
+
return (0, resolve_js_1.resolveRef)("scenario", ref, scenarios, {
|
|
130
|
+
describe: (scenario) => ({ folder: scenario.folderId ? (folderName.get(scenario.folderId) ?? null) : null, reviewStatus: scenario.reviewStatus }),
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
exports.resolveScenario = resolveScenario;
|
|
134
|
+
const resolveScenarioFolder = async (ctx, ws, ref) => (0, resolve_js_1.resolveRef)("scenario folder", ref, (await (0, exports.loadScenarioCatalog)(ctx, ws)).folders);
|
|
135
|
+
exports.resolveScenarioFolder = resolveScenarioFolder;
|
|
136
|
+
const loadDiagrams = (ctx) => ctx.cache.getOrSet("diagrams", () => ctx.api.listDiagrams());
|
|
137
|
+
exports.loadDiagrams = loadDiagrams;
|
|
138
|
+
const resolveDiagram = async (ctx, ref) => {
|
|
139
|
+
if ((0, ids_js_1.isObjectId)(ref))
|
|
140
|
+
return { item: await ctx.api.getDiagram(ref), matched: "id" };
|
|
141
|
+
const resolved = (0, resolve_js_1.resolveRef)("diagram", ref, await (0, exports.loadDiagrams)(ctx), {
|
|
142
|
+
describe: (diagram) => ({ canEdit: diagram.canEdit }),
|
|
143
|
+
});
|
|
144
|
+
// The list carries the document too, but fetch by id for the freshest copy.
|
|
145
|
+
return { ...resolved, item: await ctx.api.getDiagram(resolved.item.id) };
|
|
146
|
+
};
|
|
147
|
+
exports.resolveDiagram = resolveDiagram;
|
|
148
|
+
const sameName = (a, b) => (0, ids_js_1.normalizeName)(a) === (0, ids_js_1.normalizeName)(b);
|
|
149
|
+
exports.sameName = sameName;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerMetaTools = void 0;
|
|
4
|
+
const errors_js_1 = require("../client/errors.js");
|
|
5
|
+
const catalog_js_1 = require("./catalog.js");
|
|
6
|
+
const shared_js_1 = require("./shared.js");
|
|
7
|
+
// whoami — the first call a model should make: proves the connection, tells
|
|
8
|
+
// it who it acts as, which workspaces exist, which features are on and which
|
|
9
|
+
// tools the token can actually use. Never throws for a missing/invalid token:
|
|
10
|
+
// the answer explains how to fix the setup.
|
|
11
|
+
const registerMetaTools = (server, ctx) => {
|
|
12
|
+
server.registerTool("whoami", {
|
|
13
|
+
title: "Who am I (connection check)",
|
|
14
|
+
description: "Check the Callman connection: server URL, the user the token acts as, token permissions (scopes), the workspaces you can use (and which one is the default), enabled features and which tools are available with this token. Call this first in a session, or when a tool reports a permission problem.",
|
|
15
|
+
inputSchema: {},
|
|
16
|
+
annotations: shared_js_1.READ,
|
|
17
|
+
}, (0, shared_js_1.guarded)(async () => {
|
|
18
|
+
if (!ctx.config.token) {
|
|
19
|
+
return (0, shared_js_1.ok)({
|
|
20
|
+
apiUrl: ctx.config.apiUrl,
|
|
21
|
+
connected: false,
|
|
22
|
+
error: {
|
|
23
|
+
code: "TOKEN_MISSING",
|
|
24
|
+
message: "No token is configured.",
|
|
25
|
+
hint: "Create a personal token in the Callman desktop app (Settings → AI tools → New token) and set CALLMAN_TOKEN (or CALLMAN_TOKEN_FILE) in this MCP server's config, then restart the AI tool.",
|
|
26
|
+
},
|
|
27
|
+
tools: (0, catalog_js_1.toolAvailability)([], ctx.config.allowLocalRun),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const [meResult, workspacesResult, featuresResult] = await Promise.allSettled([
|
|
31
|
+
ctx.api.me(),
|
|
32
|
+
ctx.listWorkspaces(),
|
|
33
|
+
ctx.api.features(),
|
|
34
|
+
]);
|
|
35
|
+
if (meResult.status === "rejected") {
|
|
36
|
+
const error = meResult.reason;
|
|
37
|
+
return (0, shared_js_1.ok)({
|
|
38
|
+
apiUrl: ctx.config.apiUrl,
|
|
39
|
+
connected: false,
|
|
40
|
+
error: error instanceof errors_js_1.CallmanApiError
|
|
41
|
+
? { code: error.code, status: error.status, message: error.message, hint: (0, errors_js_1.hintFor)(error) }
|
|
42
|
+
: { code: "NETWORK_UNREACHABLE", message: error instanceof Error ? error.message : String(error) },
|
|
43
|
+
tools: (0, catalog_js_1.toolAvailability)([], ctx.config.allowLocalRun),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const me = meResult.value;
|
|
47
|
+
const workspaces = workspacesResult.status === "fulfilled" ? workspacesResult.value : [];
|
|
48
|
+
const features = featuresResult.status === "fulfilled" ? featuresResult.value.features : {};
|
|
49
|
+
const defaultWorkspace = ctx.config.defaultWorkspaceId
|
|
50
|
+
? (workspaces.find((workspace) => workspace.id === ctx.config.defaultWorkspaceId) ?? null)
|
|
51
|
+
: workspaces.length === 1
|
|
52
|
+
? workspaces[0]
|
|
53
|
+
: null;
|
|
54
|
+
return (0, shared_js_1.ok)({
|
|
55
|
+
apiUrl: ctx.config.apiUrl,
|
|
56
|
+
connected: true,
|
|
57
|
+
serverVersion: ctx.version,
|
|
58
|
+
user: me.user,
|
|
59
|
+
token: {
|
|
60
|
+
name: me.token.name,
|
|
61
|
+
scopes: me.token.scopes,
|
|
62
|
+
expiresAt: me.token.expiresAt,
|
|
63
|
+
lastUsedAt: me.token.lastUsedAt,
|
|
64
|
+
},
|
|
65
|
+
workspaces: workspaces.map((workspace) => ({
|
|
66
|
+
id: workspace.id,
|
|
67
|
+
name: workspace.name,
|
|
68
|
+
role: workspace.role,
|
|
69
|
+
isDefault: workspace.id === defaultWorkspace?.id,
|
|
70
|
+
})),
|
|
71
|
+
defaultWorkspaceId: defaultWorkspace?.id ?? null,
|
|
72
|
+
...(workspaces.length > 1 && !defaultWorkspace
|
|
73
|
+
? { note: "Several workspaces and no default: pass `workspace` to tools or set CALLMAN_WORKSPACE_ID." }
|
|
74
|
+
: {}),
|
|
75
|
+
features,
|
|
76
|
+
localRun: ctx.config.allowLocalRun,
|
|
77
|
+
tools: (0, catalog_js_1.toolAvailability)(me.token.scopes, ctx.config.allowLocalRun),
|
|
78
|
+
});
|
|
79
|
+
}));
|
|
80
|
+
};
|
|
81
|
+
exports.registerMetaTools = registerMetaTools;
|