@indemn/cli 1.0.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.
Files changed (126) hide show
  1. package/.claude-plugin/plugin.json +13 -0
  2. package/.mcp.json +9 -0
  3. package/README.md +225 -0
  4. package/dist/cli/agents.d.ts +3 -0
  5. package/dist/cli/agents.d.ts.map +1 -0
  6. package/dist/cli/agents.js +187 -0
  7. package/dist/cli/agents.js.map +1 -0
  8. package/dist/cli/auth.d.ts +3 -0
  9. package/dist/cli/auth.d.ts.map +1 -0
  10. package/dist/cli/auth.js +161 -0
  11. package/dist/cli/auth.js.map +1 -0
  12. package/dist/cli/chat.d.ts +3 -0
  13. package/dist/cli/chat.d.ts.map +1 -0
  14. package/dist/cli/chat.js +142 -0
  15. package/dist/cli/chat.js.map +1 -0
  16. package/dist/cli/config.d.ts +3 -0
  17. package/dist/cli/config.d.ts.map +1 -0
  18. package/dist/cli/config.js +119 -0
  19. package/dist/cli/config.js.map +1 -0
  20. package/dist/cli/eval.d.ts +3 -0
  21. package/dist/cli/eval.d.ts.map +1 -0
  22. package/dist/cli/eval.js +717 -0
  23. package/dist/cli/eval.js.map +1 -0
  24. package/dist/cli/functions.d.ts +3 -0
  25. package/dist/cli/functions.d.ts.map +1 -0
  26. package/dist/cli/functions.js +346 -0
  27. package/dist/cli/functions.js.map +1 -0
  28. package/dist/cli/index.d.ts +3 -0
  29. package/dist/cli/index.d.ts.map +1 -0
  30. package/dist/cli/index.js +64 -0
  31. package/dist/cli/index.js.map +1 -0
  32. package/dist/cli/kb.d.ts +3 -0
  33. package/dist/cli/kb.d.ts.map +1 -0
  34. package/dist/cli/kb.js +377 -0
  35. package/dist/cli/kb.js.map +1 -0
  36. package/dist/cli/utils/output.d.ts +6 -0
  37. package/dist/cli/utils/output.d.ts.map +1 -0
  38. package/dist/cli/utils/output.js +36 -0
  39. package/dist/cli/utils/output.js.map +1 -0
  40. package/dist/mcp/server.d.ts +2 -0
  41. package/dist/mcp/server.d.ts.map +1 -0
  42. package/dist/mcp/server.js +15 -0
  43. package/dist/mcp/server.js.map +1 -0
  44. package/dist/mcp/tools.d.ts +4 -0
  45. package/dist/mcp/tools.d.ts.map +1 -0
  46. package/dist/mcp/tools.js +814 -0
  47. package/dist/mcp/tools.js.map +1 -0
  48. package/dist/sdk/agents.d.ts +13 -0
  49. package/dist/sdk/agents.d.ts.map +1 -0
  50. package/dist/sdk/agents.js +27 -0
  51. package/dist/sdk/agents.js.map +1 -0
  52. package/dist/sdk/auth.d.ts +18 -0
  53. package/dist/sdk/auth.d.ts.map +1 -0
  54. package/dist/sdk/auth.js +98 -0
  55. package/dist/sdk/auth.js.map +1 -0
  56. package/dist/sdk/chat.d.ts +17 -0
  57. package/dist/sdk/chat.d.ts.map +1 -0
  58. package/dist/sdk/chat.js +113 -0
  59. package/dist/sdk/chat.js.map +1 -0
  60. package/dist/sdk/client.d.ts +36 -0
  61. package/dist/sdk/client.d.ts.map +1 -0
  62. package/dist/sdk/client.js +219 -0
  63. package/dist/sdk/client.js.map +1 -0
  64. package/dist/sdk/config.d.ts +11 -0
  65. package/dist/sdk/config.d.ts.map +1 -0
  66. package/dist/sdk/config.js +20 -0
  67. package/dist/sdk/config.js.map +1 -0
  68. package/dist/sdk/eval.d.ts +13 -0
  69. package/dist/sdk/eval.d.ts.map +1 -0
  70. package/dist/sdk/eval.js +36 -0
  71. package/dist/sdk/eval.js.map +1 -0
  72. package/dist/sdk/functions.d.ts +30 -0
  73. package/dist/sdk/functions.d.ts.map +1 -0
  74. package/dist/sdk/functions.js +59 -0
  75. package/dist/sdk/functions.js.map +1 -0
  76. package/dist/sdk/index.d.ts +12 -0
  77. package/dist/sdk/index.d.ts.map +1 -0
  78. package/dist/sdk/index.js +12 -0
  79. package/dist/sdk/index.js.map +1 -0
  80. package/dist/sdk/kb.d.ts +30 -0
  81. package/dist/sdk/kb.d.ts.map +1 -0
  82. package/dist/sdk/kb.js +102 -0
  83. package/dist/sdk/kb.js.map +1 -0
  84. package/dist/sdk/rubrics.d.ts +14 -0
  85. package/dist/sdk/rubrics.d.ts.map +1 -0
  86. package/dist/sdk/rubrics.js +29 -0
  87. package/dist/sdk/rubrics.js.map +1 -0
  88. package/dist/sdk/test-sets.d.ts +14 -0
  89. package/dist/sdk/test-sets.d.ts.map +1 -0
  90. package/dist/sdk/test-sets.js +29 -0
  91. package/dist/sdk/test-sets.js.map +1 -0
  92. package/dist/sdk/types.d.ts +305 -0
  93. package/dist/sdk/types.d.ts.map +1 -0
  94. package/dist/sdk/types.js +3 -0
  95. package/dist/sdk/types.js.map +1 -0
  96. package/package.json +40 -0
  97. package/skills/agent-setup/SKILL.md +131 -0
  98. package/skills/eval-orchestration/SKILL.md +257 -0
  99. package/skills/rubric-creation/SKILL.md +163 -0
  100. package/skills/test-set-creation/SKILL.md +250 -0
  101. package/src/cli/agents.ts +206 -0
  102. package/src/cli/auth.ts +170 -0
  103. package/src/cli/chat.ts +161 -0
  104. package/src/cli/config.ts +131 -0
  105. package/src/cli/eval.ts +750 -0
  106. package/src/cli/functions.ts +330 -0
  107. package/src/cli/index.ts +68 -0
  108. package/src/cli/kb.ts +412 -0
  109. package/src/cli/utils/output.ts +42 -0
  110. package/src/mcp/server.ts +17 -0
  111. package/src/mcp/tools.ts +971 -0
  112. package/src/sdk/agents.ts +47 -0
  113. package/src/sdk/auth.ts +132 -0
  114. package/src/sdk/chat.ts +133 -0
  115. package/src/sdk/client.ts +238 -0
  116. package/src/sdk/config.ts +28 -0
  117. package/src/sdk/eval.ts +57 -0
  118. package/src/sdk/functions.ts +103 -0
  119. package/src/sdk/index.ts +11 -0
  120. package/src/sdk/kb.ts +183 -0
  121. package/src/sdk/rubrics.ts +35 -0
  122. package/src/sdk/test-sets.ts +35 -0
  123. package/src/sdk/types.ts +347 -0
  124. package/test/fixtures/test-rubric.json +66 -0
  125. package/test/fixtures/test-testset.json +55 -0
  126. package/tsconfig.json +19 -0
@@ -0,0 +1,971 @@
1
+ import { z } from 'zod/v3';
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import type { IndemnClient } from '../sdk/client.js';
4
+ import { AgentsSDK } from '../sdk/agents.js';
5
+ import { ConfigSDK } from '../sdk/config.js';
6
+ import { FunctionsSDK } from '../sdk/functions.js';
7
+ import { KBSDK } from '../sdk/kb.js';
8
+ import { RubricsSDK } from '../sdk/rubrics.js';
9
+ import { TestSetsSDK } from '../sdk/test-sets.js';
10
+ import { EvalSDK } from '../sdk/eval.js';
11
+ import { ChatSession } from '../sdk/chat.js';
12
+
13
+ // Helper: format a successful MCP tool result
14
+ function success(data: unknown) {
15
+ return { content: [{ type: 'text' as const, text: JSON.stringify(data, null, 2) }] };
16
+ }
17
+
18
+ // Helper: format an error MCP tool result
19
+ function error(err: unknown) {
20
+ const message = err instanceof Error ? err.message : String(err);
21
+ return { content: [{ type: 'text' as const, text: `Error: ${message}` }], isError: true };
22
+ }
23
+
24
+ export function registerTools(server: McpServer, client: IndemnClient): void {
25
+ const agents = new AgentsSDK(client);
26
+ const config = new ConfigSDK(client);
27
+ const functions = new FunctionsSDK(client);
28
+ const kb = new KBSDK(client);
29
+ const rubrics = new RubricsSDK(client);
30
+ const testSets = new TestSetsSDK(client);
31
+ const evals = new EvalSDK(client);
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // Agent tools
35
+ // ---------------------------------------------------------------------------
36
+
37
+ server.tool(
38
+ 'list_agents',
39
+ 'List all agents in the organization',
40
+ { limit: z.number().optional().describe('Maximum number of agents to return') },
41
+ async (args) => {
42
+ try {
43
+ let result = await agents.listAgents();
44
+ if (args.limit) result = result.slice(0, args.limit);
45
+ return success(result);
46
+ } catch (err) { return error(err); }
47
+ },
48
+ );
49
+
50
+ server.tool(
51
+ 'get_agent',
52
+ 'Get details of a specific agent',
53
+ { agent_id: z.string().describe('The agent ID') },
54
+ async (args) => {
55
+ try {
56
+ const result = await agents.getAgent(args.agent_id);
57
+ return success(result);
58
+ } catch (err) { return error(err); }
59
+ },
60
+ );
61
+
62
+ server.tool(
63
+ 'create_agent',
64
+ 'Create a new agent',
65
+ {
66
+ name: z.string().describe('Agent name'),
67
+ channels: z.array(z.string()).optional().describe('Channels (e.g. ["WEB"])'),
68
+ },
69
+ async (args) => {
70
+ try {
71
+ const result = await agents.createAgent({ name: args.name, channels: args.channels });
72
+ return success(result);
73
+ } catch (err) { return error(err); }
74
+ },
75
+ );
76
+
77
+ server.tool(
78
+ 'update_agent',
79
+ 'Update an existing agent',
80
+ {
81
+ agent_id: z.string().describe('The agent ID'),
82
+ name: z.string().optional().describe('New agent name'),
83
+ description: z.string().optional().describe('New agent description'),
84
+ webhook_url: z.string().optional().describe('Webhook URL'),
85
+ },
86
+ async (args) => {
87
+ try {
88
+ const { agent_id, ...updates } = args;
89
+ const input: Record<string, unknown> = {};
90
+ if (updates.name !== undefined) input.name = updates.name;
91
+ if (updates.description !== undefined) input.description = updates.description;
92
+ if (updates.webhook_url !== undefined) input.webhook_url = updates.webhook_url;
93
+ const result = await agents.updateAgent(agent_id, input);
94
+ return success(result);
95
+ } catch (err) { return error(err); }
96
+ },
97
+ );
98
+
99
+ server.tool(
100
+ 'delete_agent',
101
+ 'Delete an agent',
102
+ { agent_id: z.string().describe('The agent ID to delete') },
103
+ async (args) => {
104
+ try {
105
+ await agents.deleteAgent(args.agent_id);
106
+ return success({ deleted: true, agent_id: args.agent_id });
107
+ } catch (err) { return error(err); }
108
+ },
109
+ );
110
+
111
+ server.tool(
112
+ 'clone_agent',
113
+ 'Clone an existing agent',
114
+ { agent_id: z.string().describe('The agent ID to clone') },
115
+ async (args) => {
116
+ try {
117
+ const result = await agents.cloneAgent(args.agent_id);
118
+ return success(result);
119
+ } catch (err) { return error(err); }
120
+ },
121
+ );
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // Config tools
125
+ // ---------------------------------------------------------------------------
126
+
127
+ server.tool(
128
+ 'get_config',
129
+ 'Get the full configuration for an agent',
130
+ { agent_id: z.string().describe('The agent ID') },
131
+ async (args) => {
132
+ try {
133
+ const result = await config.getConfig(args.agent_id);
134
+ return success(result);
135
+ } catch (err) { return error(err); }
136
+ },
137
+ );
138
+
139
+ server.tool(
140
+ 'set_config',
141
+ 'Update configuration for an agent',
142
+ {
143
+ agent_id: z.string().describe('The agent ID'),
144
+ system_prompt: z.string().optional().describe('System prompt text'),
145
+ first_message: z.string().optional().describe('First message text shown to users'),
146
+ },
147
+ async (args) => {
148
+ try {
149
+ const input: Record<string, unknown> = {};
150
+ if (args.system_prompt !== undefined) input.system_prompt = args.system_prompt;
151
+ if (args.first_message !== undefined) input.first_message = { text: args.first_message };
152
+ const result = await config.setConfig(args.agent_id, input);
153
+ return success(result);
154
+ } catch (err) { return error(err); }
155
+ },
156
+ );
157
+
158
+ server.tool(
159
+ 'get_prompt',
160
+ 'Get the system prompt for an agent',
161
+ { agent_id: z.string().describe('The agent ID') },
162
+ async (args) => {
163
+ try {
164
+ const result = await config.getPrompt(args.agent_id);
165
+ return success({ agent_id: args.agent_id, system_prompt: result ?? null });
166
+ } catch (err) { return error(err); }
167
+ },
168
+ );
169
+
170
+ server.tool(
171
+ 'set_prompt',
172
+ 'Set the system prompt for an agent',
173
+ {
174
+ agent_id: z.string().describe('The agent ID'),
175
+ prompt: z.string().describe('The system prompt text'),
176
+ },
177
+ async (args) => {
178
+ try {
179
+ const result = await config.setPrompt(args.agent_id, args.prompt);
180
+ return success(result);
181
+ } catch (err) { return error(err); }
182
+ },
183
+ );
184
+
185
+ // ---------------------------------------------------------------------------
186
+ // Function tools
187
+ // ---------------------------------------------------------------------------
188
+
189
+ server.tool(
190
+ 'list_functions',
191
+ 'List all functions (tools) for an agent',
192
+ { agent_id: z.string().describe('The agent ID') },
193
+ async (args) => {
194
+ try {
195
+ const result = await functions.listFunctions(args.agent_id);
196
+ return success(result);
197
+ } catch (err) { return error(err); }
198
+ },
199
+ );
200
+
201
+ server.tool(
202
+ 'create_function',
203
+ 'Create a new function (tool) for an agent',
204
+ {
205
+ agent_id: z.string().describe('The agent ID'),
206
+ type: z.string().describe('Function type (e.g. "rest_api")'),
207
+ description: z.string().optional().describe('What the function does'),
208
+ name: z.string().optional().describe('Function name'),
209
+ url: z.string().optional().describe('Endpoint URL'),
210
+ method: z.string().optional().describe('HTTP method (GET, POST, etc.)'),
211
+ },
212
+ async (args) => {
213
+ try {
214
+ const { agent_id, ...input } = args;
215
+ const result = await functions.createFunction(agent_id, input);
216
+ return success(result);
217
+ } catch (err) { return error(err); }
218
+ },
219
+ );
220
+
221
+ server.tool(
222
+ 'update_function',
223
+ 'Update an existing function (tool) for an agent',
224
+ {
225
+ agent_id: z.string().describe('The agent ID'),
226
+ function_id: z.string().describe('The function ID'),
227
+ description: z.string().optional().describe('Updated description'),
228
+ name: z.string().optional().describe('Updated function name'),
229
+ url: z.string().optional().describe('Updated endpoint URL'),
230
+ method: z.string().optional().describe('Updated HTTP method'),
231
+ },
232
+ async (args) => {
233
+ try {
234
+ const { agent_id, function_id, ...input } = args;
235
+ const result = await functions.updateFunction(agent_id, function_id, input);
236
+ return success(result);
237
+ } catch (err) { return error(err); }
238
+ },
239
+ );
240
+
241
+ server.tool(
242
+ 'delete_function',
243
+ 'Delete a function (tool) from an agent',
244
+ {
245
+ agent_id: z.string().describe('The agent ID'),
246
+ function_id: z.string().describe('The function ID to delete'),
247
+ },
248
+ async (args) => {
249
+ try {
250
+ await functions.deleteFunction(args.agent_id, args.function_id);
251
+ return success({ deleted: true, agent_id: args.agent_id, function_id: args.function_id });
252
+ } catch (err) { return error(err); }
253
+ },
254
+ );
255
+
256
+ server.tool(
257
+ 'test_function',
258
+ 'Test a function (tool) with optional parameters',
259
+ {
260
+ agent_id: z.string().describe('The agent ID'),
261
+ function_id: z.string().describe('The function ID to test'),
262
+ params: z.record(z.unknown()).optional().describe('Test parameters to send'),
263
+ },
264
+ async (args) => {
265
+ try {
266
+ const result = await functions.testFunction(
267
+ args.agent_id,
268
+ args.function_id,
269
+ args.params as Record<string, unknown> | undefined,
270
+ );
271
+ return success(result);
272
+ } catch (err) { return error(err); }
273
+ },
274
+ );
275
+
276
+ server.tool(
277
+ 'export_functions',
278
+ 'Export all functions from an agent',
279
+ { agent_id: z.string().describe('The agent ID') },
280
+ async (args) => {
281
+ try {
282
+ const result = await functions.exportFunctions(args.agent_id);
283
+ return success(result);
284
+ } catch (err) { return error(err); }
285
+ },
286
+ );
287
+
288
+ server.tool(
289
+ 'import_functions',
290
+ 'Import functions into an agent from a URL (e.g. OpenAPI spec)',
291
+ {
292
+ agent_id: z.string().describe('The agent ID'),
293
+ url: z.string().describe('URL to import functions from'),
294
+ },
295
+ async (args) => {
296
+ try {
297
+ const result = await functions.importFunctions(args.agent_id, args.url);
298
+ return success(result);
299
+ } catch (err) { return error(err); }
300
+ },
301
+ );
302
+
303
+ server.tool(
304
+ 'list_function_parameters',
305
+ 'List parameters for a function',
306
+ {
307
+ agent_id: z.string().describe('The agent ID'),
308
+ function_id: z.string().describe('The function ID'),
309
+ },
310
+ async (args) => {
311
+ try {
312
+ const result = await functions.listParameters(args.agent_id, args.function_id);
313
+ return success(result);
314
+ } catch (err) { return error(err); }
315
+ },
316
+ );
317
+
318
+ server.tool(
319
+ 'add_function_parameter',
320
+ 'Add a parameter to a function',
321
+ {
322
+ agent_id: z.string().describe('The agent ID'),
323
+ function_id: z.string().describe('The function ID'),
324
+ name: z.string().describe('Parameter name'),
325
+ type: z.string().describe('Parameter type (string, number, boolean)'),
326
+ description: z.string().optional().describe('Parameter description'),
327
+ required: z.boolean().optional().describe('Whether the parameter is required'),
328
+ },
329
+ async (args) => {
330
+ try {
331
+ const { agent_id, function_id, ...param } = args;
332
+ const result = await functions.addParameter(agent_id, function_id, param);
333
+ return success(result);
334
+ } catch (err) { return error(err); }
335
+ },
336
+ );
337
+
338
+ server.tool(
339
+ 'update_function_parameter',
340
+ 'Update a function parameter',
341
+ {
342
+ agent_id: z.string().describe('The agent ID'),
343
+ function_id: z.string().describe('The function ID'),
344
+ parameter_id: z.string().describe('The parameter ID'),
345
+ name: z.string().optional().describe('Updated name'),
346
+ type: z.string().optional().describe('Updated type'),
347
+ description: z.string().optional().describe('Updated description'),
348
+ required: z.boolean().optional().describe('Updated required flag'),
349
+ },
350
+ async (args) => {
351
+ try {
352
+ const { agent_id, function_id, parameter_id, ...param } = args;
353
+ const result = await functions.updateParameter(agent_id, function_id, parameter_id, param);
354
+ return success(result);
355
+ } catch (err) { return error(err); }
356
+ },
357
+ );
358
+
359
+ server.tool(
360
+ 'delete_function_parameter',
361
+ 'Delete a function parameter',
362
+ {
363
+ agent_id: z.string().describe('The agent ID'),
364
+ function_id: z.string().describe('The function ID'),
365
+ parameter_id: z.string().describe('The parameter ID'),
366
+ },
367
+ async (args) => {
368
+ try {
369
+ await functions.deleteParameter(args.agent_id, args.function_id, args.parameter_id);
370
+ return success({ deleted: true });
371
+ } catch (err) { return error(err); }
372
+ },
373
+ );
374
+
375
+ server.tool(
376
+ 'list_master_functions',
377
+ 'List available master function templates',
378
+ {},
379
+ async () => {
380
+ try {
381
+ const result = await functions.listMasterFunctions();
382
+ return success(result);
383
+ } catch (err) { return error(err); }
384
+ },
385
+ );
386
+
387
+ server.tool(
388
+ 'list_llm_models',
389
+ 'List available LLM models',
390
+ {},
391
+ async () => {
392
+ try {
393
+ const result = await functions.listLLMModels();
394
+ return success(result);
395
+ } catch (err) { return error(err); }
396
+ },
397
+ );
398
+
399
+ // ---------------------------------------------------------------------------
400
+ // Knowledge base tools
401
+ // ---------------------------------------------------------------------------
402
+
403
+ server.tool(
404
+ 'list_knowledge_bases',
405
+ 'List all knowledge bases in the organization',
406
+ { limit: z.number().optional().describe('Maximum number of KBs to return') },
407
+ async (args) => {
408
+ try {
409
+ let result = await kb.listKBs();
410
+ if (args.limit) result = result.slice(0, args.limit);
411
+ return success(result);
412
+ } catch (err) { return error(err); }
413
+ },
414
+ );
415
+
416
+ server.tool(
417
+ 'get_knowledge_base',
418
+ 'Get details of a specific knowledge base',
419
+ { kb_id: z.string().describe('The knowledge base ID') },
420
+ async (args) => {
421
+ try {
422
+ const result = await kb.getKB(args.kb_id);
423
+ return success(result);
424
+ } catch (err) { return error(err); }
425
+ },
426
+ );
427
+
428
+ server.tool(
429
+ 'create_knowledge_base',
430
+ 'Create a new knowledge base',
431
+ {
432
+ name: z.string().describe('Knowledge base name'),
433
+ type: z.string().describe('Knowledge base type'),
434
+ },
435
+ async (args) => {
436
+ try {
437
+ const result = await kb.createKB({ name: args.name, type: args.type });
438
+ return success(result);
439
+ } catch (err) { return error(err); }
440
+ },
441
+ );
442
+
443
+ server.tool(
444
+ 'update_knowledge_base',
445
+ 'Update an existing knowledge base',
446
+ {
447
+ kb_id: z.string().describe('The knowledge base ID'),
448
+ name: z.string().optional().describe('Updated name'),
449
+ description: z.string().optional().describe('Updated description'),
450
+ },
451
+ async (args) => {
452
+ try {
453
+ const input: Record<string, unknown> = {};
454
+ if (args.name !== undefined) input.name = args.name;
455
+ if (args.description !== undefined) input.description = args.description;
456
+ const result = await kb.updateKB(args.kb_id, input);
457
+ return success(result);
458
+ } catch (err) { return error(err); }
459
+ },
460
+ );
461
+
462
+ server.tool(
463
+ 'delete_knowledge_base',
464
+ 'Delete a knowledge base',
465
+ { kb_id: z.string().describe('The knowledge base ID to delete') },
466
+ async (args) => {
467
+ try {
468
+ await kb.deleteKB(args.kb_id);
469
+ return success({ deleted: true, kb_id: args.kb_id });
470
+ } catch (err) { return error(err); }
471
+ },
472
+ );
473
+
474
+ server.tool(
475
+ 'list_kb_data',
476
+ 'List data sources in a knowledge base',
477
+ {
478
+ kb_id: z.string().describe('The knowledge base ID'),
479
+ page: z.number().optional().describe('Page number'),
480
+ limit: z.number().optional().describe('Items per page'),
481
+ },
482
+ async (args) => {
483
+ try {
484
+ const query: Record<string, string> = {};
485
+ if (args.page !== undefined) query.page = String(args.page);
486
+ if (args.limit !== undefined) query.limit = String(args.limit);
487
+ const result = await kb.listDataSources(args.kb_id, Object.keys(query).length ? query : undefined);
488
+ return success(result);
489
+ } catch (err) { return error(err); }
490
+ },
491
+ );
492
+
493
+ server.tool(
494
+ 'add_kb_data',
495
+ 'Add a data source entry to a knowledge base. For URL scraping use scrape_url instead. For file uploads use upload_kb_file.',
496
+ {
497
+ kb_id: z.string().describe('The knowledge base ID'),
498
+ question: z.string().optional().describe('FAQ question'),
499
+ answer: z.string().optional().describe('FAQ answer'),
500
+ source_url: z.string().optional().describe('Source URL (initiates scraping via import endpoint)'),
501
+ content: z.string().optional().describe('Raw content'),
502
+ },
503
+ async (args) => {
504
+ try {
505
+ const { kb_id, ...input } = args;
506
+ if (input.source_url) {
507
+ const result = await kb.scrapeUrl(kb_id, input.source_url as string);
508
+ return success(result);
509
+ }
510
+ const result = await kb.addDataSource(kb_id, input);
511
+ return success(result);
512
+ } catch (err) { return error(err); }
513
+ },
514
+ );
515
+
516
+ server.tool(
517
+ 'scrape_url',
518
+ 'Scrape a website URL and add it as a data source to a knowledge base. Initiates async crawling.',
519
+ {
520
+ kb_id: z.string().describe('The knowledge base ID'),
521
+ url: z.string().describe('URL to scrape'),
522
+ crawl_mode: z.string().optional().describe('Crawl mode: "full" (entire site) or "selected"'),
523
+ },
524
+ async (args) => {
525
+ try {
526
+ const result = await kb.scrapeUrl(args.kb_id, args.url, args.crawl_mode);
527
+ return success(result);
528
+ } catch (err) { return error(err); }
529
+ },
530
+ );
531
+
532
+ server.tool(
533
+ 'upload_kb_file',
534
+ 'Upload a file to a knowledge base',
535
+ {
536
+ kb_id: z.string().describe('The knowledge base ID'),
537
+ file_path: z.string().describe('Path to the file to upload'),
538
+ },
539
+ async (args) => {
540
+ try {
541
+ const result = await kb.uploadFile(args.kb_id, args.file_path);
542
+ return success(result);
543
+ } catch (err) { return error(err); }
544
+ },
545
+ );
546
+
547
+ server.tool(
548
+ 'update_kb_data',
549
+ 'Update a data source entry in a knowledge base',
550
+ {
551
+ kb_id: z.string().describe('The knowledge base ID'),
552
+ source_id: z.string().describe('The data source ID'),
553
+ question: z.string().optional().describe('Updated question'),
554
+ answer: z.string().optional().describe('Updated answer'),
555
+ source_url: z.string().optional().describe('Updated source URL'),
556
+ content: z.string().optional().describe('Updated content'),
557
+ },
558
+ async (args) => {
559
+ try {
560
+ const { kb_id, source_id, ...input } = args;
561
+ const result = await kb.updateDataSource(kb_id, source_id, input);
562
+ return success(result);
563
+ } catch (err) { return error(err); }
564
+ },
565
+ );
566
+
567
+ server.tool(
568
+ 'delete_kb_data',
569
+ 'Delete a data source entry from a knowledge base',
570
+ {
571
+ kb_id: z.string().describe('The knowledge base ID'),
572
+ source_id: z.string().describe('The data source ID to delete'),
573
+ },
574
+ async (args) => {
575
+ try {
576
+ await kb.deleteDataSource(args.kb_id, args.source_id);
577
+ return success({ deleted: true, kb_id: args.kb_id, source_id: args.source_id });
578
+ } catch (err) { return error(err); }
579
+ },
580
+ );
581
+
582
+ server.tool(
583
+ 'link_kb',
584
+ 'Link knowledge bases to an agent',
585
+ {
586
+ agent_id: z.string().describe('The agent ID'),
587
+ kb_ids: z.array(z.string()).describe('Array of knowledge base IDs to link'),
588
+ },
589
+ async (args) => {
590
+ try {
591
+ const result = await kb.linkKB(args.agent_id, args.kb_ids);
592
+ return success(result);
593
+ } catch (err) { return error(err); }
594
+ },
595
+ );
596
+
597
+ server.tool(
598
+ 'unlink_kb',
599
+ 'Unlink a knowledge base from an agent',
600
+ {
601
+ agent_id: z.string().describe('The agent ID'),
602
+ kb_id: z.string().describe('The knowledge base ID to unlink'),
603
+ },
604
+ async (args) => {
605
+ try {
606
+ await kb.unlinkKB(args.agent_id, args.kb_id);
607
+ return success({ unlinked: true, agent_id: args.agent_id, kb_id: args.kb_id });
608
+ } catch (err) { return error(err); }
609
+ },
610
+ );
611
+
612
+ server.tool(
613
+ 'export_kb',
614
+ 'Export knowledge base data',
615
+ {
616
+ kb_id: z.string().describe('The knowledge base ID'),
617
+ format: z.string().optional().describe('Export format (e.g. "json", "csv")'),
618
+ },
619
+ async (args) => {
620
+ try {
621
+ const result = await kb.exportKB(args.kb_id, args.format || 'json');
622
+ return success(result);
623
+ } catch (err) { return error(err); }
624
+ },
625
+ );
626
+
627
+ server.tool(
628
+ 'import_kb',
629
+ 'Import data into a knowledge base',
630
+ {
631
+ kb_id: z.string().describe('The knowledge base ID'),
632
+ data: z.record(z.unknown()).describe('Data to import'),
633
+ },
634
+ async (args) => {
635
+ try {
636
+ const result = await kb.importKB(args.kb_id, args.data);
637
+ return success(result);
638
+ } catch (err) { return error(err); }
639
+ },
640
+ );
641
+
642
+ // ---------------------------------------------------------------------------
643
+ // Chat tool
644
+ // ---------------------------------------------------------------------------
645
+
646
+ server.tool(
647
+ 'chat_with_agent',
648
+ 'Send a message to an agent and get a response. Not interactive — sends one message and returns the reply.',
649
+ {
650
+ agent_id: z.string().describe('The agent ID (bot type) to chat with'),
651
+ message: z.string().describe('The message to send'),
652
+ session_id: z.string().optional().describe('Optional session ID to continue a conversation'),
653
+ },
654
+ async (args) => {
655
+ try {
656
+ const middlewareUrl = client.getHosts().middleware;
657
+ const chat = new ChatSession(args.agent_id, middlewareUrl, args.session_id);
658
+
659
+ // Track streaming state to distinguish greeting from response
660
+ let receiving = false;
661
+ let messageCount = 0;
662
+
663
+ chat.on('stream', (data: { isFirstChunk?: boolean; streamFinished?: boolean }) => {
664
+ if (data.isFirstChunk) receiving = true;
665
+ if (data.streamFinished) receiving = false;
666
+ });
667
+
668
+ const greetingMessages: string[] = [];
669
+ chat.on('message', (text: string) => {
670
+ greetingMessages.push(text);
671
+ messageCount++;
672
+ });
673
+
674
+ const sessionId = await chat.connect();
675
+
676
+ // Wait for greeting to finish streaming (up to 8s)
677
+ await new Promise<void>((resolve) => {
678
+ const check = setInterval(() => {
679
+ if (!receiving) { clearInterval(check); resolve(); }
680
+ }, 100);
681
+ setTimeout(() => { clearInterval(check); resolve(); }, 8000);
682
+ });
683
+
684
+ // Now swap the listener — next message is the actual response
685
+ const greetingCount = messageCount;
686
+ chat.removeAllListeners('message');
687
+
688
+ const response = await new Promise<string>((resolve, reject) => {
689
+ const timeout = setTimeout(() => {
690
+ chat.disconnect();
691
+ reject(new Error('Agent did not respond within 60 seconds'));
692
+ }, 60000);
693
+
694
+ chat.on('message', (text: string) => {
695
+ clearTimeout(timeout);
696
+ resolve(text);
697
+ });
698
+
699
+ chat.send(args.message);
700
+ });
701
+
702
+ chat.disconnect();
703
+
704
+ return success({ session_id: sessionId, greeting: greetingMessages.slice(0, greetingCount).join('\n'), response });
705
+ } catch (err) { return error(err); }
706
+ },
707
+ );
708
+
709
+ // ---------------------------------------------------------------------------
710
+ // Eval tools
711
+ // ---------------------------------------------------------------------------
712
+
713
+ server.tool(
714
+ 'run_evaluation',
715
+ 'Trigger an evaluation run for a bot against a test set',
716
+ {
717
+ bot_id: z.string().describe('The bot ID to evaluate'),
718
+ test_set_id: z.string().describe('The test set ID'),
719
+ rubric_id: z.string().optional().describe('Optional rubric ID'),
720
+ concurrency: z.number().optional().describe('Number of concurrent evaluations'),
721
+ eval_model: z.string().optional().describe('Model to use for evaluation judging'),
722
+ keep: z.boolean().optional().describe('Keep evaluation data after completion'),
723
+ limit: z.number().optional().describe('Limit items to evaluate'),
724
+ component_scope: z.string().optional().describe('Component scope filter'),
725
+ component_ids: z.array(z.string()).optional().describe('Component ID filter'),
726
+ mode: z.string().optional().describe('Evaluation mode'),
727
+ },
728
+ async (args) => {
729
+ try {
730
+ const input: Record<string, unknown> = {
731
+ bot_id: args.bot_id,
732
+ test_set_id: args.test_set_id,
733
+ };
734
+ if (args.rubric_id !== undefined) input.rubric_id = args.rubric_id;
735
+ if (args.concurrency !== undefined) input.concurrency = args.concurrency;
736
+ if (args.eval_model !== undefined) input.eval_model = args.eval_model;
737
+ if (args.keep !== undefined) input.keep = args.keep;
738
+ if (args.limit !== undefined) input.limit = args.limit;
739
+ if (args.component_scope !== undefined) input.component_scope = args.component_scope;
740
+ if (args.component_ids !== undefined) input.component_ids = args.component_ids;
741
+ if (args.mode !== undefined) input.mode = args.mode;
742
+ const result = await evals.triggerEvaluation(input as any);
743
+ return success(result);
744
+ } catch (err) { return error(err); }
745
+ },
746
+ );
747
+
748
+ server.tool(
749
+ 'get_eval_status',
750
+ 'Get the status of an evaluation run',
751
+ { run_id: z.string().describe('The evaluation run ID') },
752
+ async (args) => {
753
+ try {
754
+ const result = await evals.getRunStatus(args.run_id);
755
+ return success(result);
756
+ } catch (err) { return error(err); }
757
+ },
758
+ );
759
+
760
+ server.tool(
761
+ 'get_eval_results',
762
+ 'Get the detailed results of a completed evaluation run',
763
+ { run_id: z.string().describe('The evaluation run ID') },
764
+ async (args) => {
765
+ try {
766
+ const result = await evals.getRunResults(args.run_id);
767
+ return success(result);
768
+ } catch (err) { return error(err); }
769
+ },
770
+ );
771
+
772
+ server.tool(
773
+ 'get_bot_context',
774
+ 'Get the full context of a bot (prompt, tools, KBs, LLM config)',
775
+ { bot_id: z.string().describe('The bot ID') },
776
+ async (args) => {
777
+ try {
778
+ const result = await evals.getBotContext(args.bot_id);
779
+ return success(result);
780
+ } catch (err) { return error(err); }
781
+ },
782
+ );
783
+
784
+ // ---------------------------------------------------------------------------
785
+ // Rubric tools
786
+ // ---------------------------------------------------------------------------
787
+
788
+ server.tool(
789
+ 'list_rubrics',
790
+ 'List evaluation rubrics, optionally filtered by agent',
791
+ { agent_id: z.string().optional().describe('Filter rubrics by agent ID') },
792
+ async (args) => {
793
+ try {
794
+ const result = await rubrics.listRubrics(args.agent_id);
795
+ return success(result);
796
+ } catch (err) { return error(err); }
797
+ },
798
+ );
799
+
800
+ server.tool(
801
+ 'get_rubric',
802
+ 'Get details of a specific rubric',
803
+ { rubric_id: z.string().describe('The rubric ID') },
804
+ async (args) => {
805
+ try {
806
+ const result = await rubrics.getRubric(args.rubric_id);
807
+ return success(result);
808
+ } catch (err) { return error(err); }
809
+ },
810
+ );
811
+
812
+ server.tool(
813
+ 'create_rubric',
814
+ 'Create a new evaluation rubric',
815
+ {
816
+ name: z.string().describe('Rubric name'),
817
+ rules: z.array(z.record(z.unknown())).describe('Array of rubric rules'),
818
+ agent_id: z.string().optional().describe('Agent ID to associate the rubric with'),
819
+ description: z.string().optional().describe('Rubric description'),
820
+ severity_definitions: z.record(z.string()).optional().describe('Severity level definitions'),
821
+ },
822
+ async (args) => {
823
+ try {
824
+ const input: Record<string, unknown> = { name: args.name, rules: args.rules };
825
+ if (args.agent_id !== undefined) input.agent_id = args.agent_id;
826
+ if (args.description !== undefined) input.description = args.description;
827
+ if (args.severity_definitions !== undefined) input.severity_definitions = args.severity_definitions;
828
+ const result = await rubrics.createRubric(input as any);
829
+ return success(result);
830
+ } catch (err) { return error(err); }
831
+ },
832
+ );
833
+
834
+ server.tool(
835
+ 'update_rubric',
836
+ 'Update an existing rubric',
837
+ {
838
+ rubric_id: z.string().describe('The rubric ID'),
839
+ name: z.string().optional().describe('Updated name'),
840
+ description: z.string().optional().describe('Updated description'),
841
+ rules: z.array(z.record(z.unknown())).optional().describe('Updated rules'),
842
+ },
843
+ async (args) => {
844
+ try {
845
+ const input: Record<string, unknown> = {};
846
+ if (args.name !== undefined) input.name = args.name;
847
+ if (args.description !== undefined) input.description = args.description;
848
+ if (args.rules !== undefined) input.rules = args.rules;
849
+ const result = await rubrics.updateRubric(args.rubric_id, input as any);
850
+ return success(result);
851
+ } catch (err) { return error(err); }
852
+ },
853
+ );
854
+
855
+ server.tool(
856
+ 'delete_rubric',
857
+ 'Delete a rubric',
858
+ { rubric_id: z.string().describe('The rubric ID to delete') },
859
+ async (args) => {
860
+ try {
861
+ await rubrics.deleteRubric(args.rubric_id);
862
+ return success({ deleted: true, rubric_id: args.rubric_id });
863
+ } catch (err) { return error(err); }
864
+ },
865
+ );
866
+
867
+ server.tool(
868
+ 'list_rubric_versions',
869
+ 'List all versions of a rubric',
870
+ { rubric_id: z.string().describe('The rubric ID') },
871
+ async (args) => {
872
+ try {
873
+ const result = await rubrics.listVersions(args.rubric_id);
874
+ return success(result);
875
+ } catch (err) { return error(err); }
876
+ },
877
+ );
878
+
879
+ // ---------------------------------------------------------------------------
880
+ // Test set tools
881
+ // ---------------------------------------------------------------------------
882
+
883
+ server.tool(
884
+ 'list_test_sets',
885
+ 'List test sets, optionally filtered by agent',
886
+ { agent_id: z.string().optional().describe('Filter test sets by agent ID') },
887
+ async (args) => {
888
+ try {
889
+ const result = await testSets.listTestSets(args.agent_id);
890
+ return success(result);
891
+ } catch (err) { return error(err); }
892
+ },
893
+ );
894
+
895
+ server.tool(
896
+ 'get_test_set',
897
+ 'Get details of a specific test set',
898
+ { test_set_id: z.string().describe('The test set ID') },
899
+ async (args) => {
900
+ try {
901
+ const result = await testSets.getTestSet(args.test_set_id);
902
+ return success(result);
903
+ } catch (err) { return error(err); }
904
+ },
905
+ );
906
+
907
+ server.tool(
908
+ 'create_test_set',
909
+ 'Create a new test set',
910
+ {
911
+ name: z.string().describe('Test set name'),
912
+ items: z.array(z.record(z.unknown())).describe('Array of test items'),
913
+ agent_id: z.string().optional().describe('Agent ID to associate the test set with'),
914
+ description: z.string().optional().describe('Test set description'),
915
+ },
916
+ async (args) => {
917
+ try {
918
+ const input: Record<string, unknown> = { name: args.name, items: args.items };
919
+ if (args.agent_id !== undefined) input.agent_id = args.agent_id;
920
+ if (args.description !== undefined) input.description = args.description;
921
+ const result = await testSets.createTestSet(input as any);
922
+ return success(result);
923
+ } catch (err) { return error(err); }
924
+ },
925
+ );
926
+
927
+ server.tool(
928
+ 'update_test_set',
929
+ 'Update an existing test set',
930
+ {
931
+ test_set_id: z.string().describe('The test set ID'),
932
+ name: z.string().optional().describe('Updated name'),
933
+ description: z.string().optional().describe('Updated description'),
934
+ items: z.array(z.record(z.unknown())).optional().describe('Updated test items'),
935
+ },
936
+ async (args) => {
937
+ try {
938
+ const input: Record<string, unknown> = {};
939
+ if (args.name !== undefined) input.name = args.name;
940
+ if (args.description !== undefined) input.description = args.description;
941
+ if (args.items !== undefined) input.items = args.items;
942
+ const result = await testSets.updateTestSet(args.test_set_id, input as any);
943
+ return success(result);
944
+ } catch (err) { return error(err); }
945
+ },
946
+ );
947
+
948
+ server.tool(
949
+ 'delete_test_set',
950
+ 'Delete a test set',
951
+ { test_set_id: z.string().describe('The test set ID to delete') },
952
+ async (args) => {
953
+ try {
954
+ await testSets.deleteTestSet(args.test_set_id);
955
+ return success({ deleted: true, test_set_id: args.test_set_id });
956
+ } catch (err) { return error(err); }
957
+ },
958
+ );
959
+
960
+ server.tool(
961
+ 'list_test_set_versions',
962
+ 'List all versions of a test set',
963
+ { test_set_id: z.string().describe('The test set ID') },
964
+ async (args) => {
965
+ try {
966
+ const result = await testSets.listVersions(args.test_set_id);
967
+ return success(result);
968
+ } catch (err) { return error(err); }
969
+ },
970
+ );
971
+ }