sequentum-mcp 1.1.4 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +100 -0
- package/README.md +104 -3
- package/dist/{api-client.d.ts → api/api-client.d.ts} +106 -4
- package/dist/api/api-client.d.ts.map +1 -0
- package/dist/{api-client.js → api/api-client.js} +345 -99
- package/dist/api/api-client.js.map +1 -0
- package/dist/{types.d.ts → api/types.d.ts} +122 -12
- package/dist/api/types.d.ts.map +1 -0
- package/dist/{types.js → api/types.js} +57 -0
- package/dist/api/types.js.map +1 -0
- package/dist/index.js +11 -1737
- package/dist/index.js.map +1 -1
- package/dist/server/cors.d.ts +33 -0
- package/dist/server/cors.d.ts.map +1 -0
- package/dist/server/cors.js +72 -0
- package/dist/server/cors.js.map +1 -0
- package/dist/server/handlers.d.ts +49 -0
- package/dist/server/handlers.d.ts.map +1 -0
- package/dist/server/handlers.js +1031 -0
- package/dist/server/handlers.js.map +1 -0
- package/dist/server/http-server.d.ts +13 -0
- package/dist/server/http-server.d.ts.map +1 -0
- package/dist/server/http-server.js +393 -0
- package/dist/server/http-server.js.map +1 -0
- package/dist/server/policies.d.ts +19 -0
- package/dist/server/policies.d.ts.map +1 -0
- package/dist/server/policies.js +32 -0
- package/dist/server/policies.js.map +1 -0
- package/dist/server/prompts.d.ts +19 -0
- package/dist/server/prompts.d.ts.map +1 -0
- package/dist/server/prompts.js +464 -0
- package/dist/server/prompts.js.map +1 -0
- package/dist/server/resources.d.ts +26 -0
- package/dist/server/resources.d.ts.map +1 -0
- package/dist/server/resources.js +348 -0
- package/dist/server/resources.js.map +1 -0
- package/dist/server/tools.d.ts +9 -0
- package/dist/server/tools.d.ts.map +1 -0
- package/dist/server/tools.js +977 -0
- package/dist/server/tools.js.map +1 -0
- package/dist/utils/oauth-metadata.d.ts.map +1 -0
- package/dist/utils/oauth-metadata.js.map +1 -0
- package/dist/{validation.d.ts → utils/validation.d.ts} +25 -2
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/{validation.js → utils/validation.js} +43 -3
- package/dist/utils/validation.js.map +1 -0
- package/docs/prompts-reference.md +370 -0
- package/docs/resources-reference.md +300 -0
- package/docs/tool-reference.md +244 -2
- package/package.json +4 -3
- package/dist/api-client.d.ts.map +0 -1
- package/dist/api-client.js.map +0 -1
- package/dist/oauth-metadata.d.ts.map +0 -1
- package/dist/oauth-metadata.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js.map +0 -1
- /package/dist/{oauth-metadata.d.ts → utils/oauth-metadata.d.ts} +0 -0
- /package/dist/{oauth-metadata.js → utils/oauth-metadata.js} +0 -0
|
@@ -0,0 +1,1031 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Factory and Tool Handlers
|
|
3
|
+
*
|
|
4
|
+
* Contains input validation helpers, response helpers, and the
|
|
5
|
+
* createMcpServer factory that wires tool definitions to their handlers.
|
|
6
|
+
*/
|
|
7
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
|
+
import { SUFFICIENCY_POLICY } from "./policies.js";
|
|
9
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
import { ApiRequestError, RateLimitError, AuthenticationError } from "../api/types.js";
|
|
11
|
+
import { getDefaultDateRange, validateBoolean, validateDateRange, validateEnum, validateISODate, validateJsonString, validateNumber, validateStartTimeInFuture, validateString, } from "../utils/validation.js";
|
|
12
|
+
import { tools } from "./tools.js";
|
|
13
|
+
import { resources, resourceTemplates, readResource } from "./resources.js";
|
|
14
|
+
import { prompts, getPromptMessages } from "./prompts.js";
|
|
15
|
+
// ==========================================
|
|
16
|
+
// Response Helpers
|
|
17
|
+
// ==========================================
|
|
18
|
+
/**
|
|
19
|
+
* Map RunStatus numeric value to human-readable string
|
|
20
|
+
*/
|
|
21
|
+
function getRunStatusLabel(status) {
|
|
22
|
+
const statusMap = {
|
|
23
|
+
0: "Invalid",
|
|
24
|
+
1: "Running",
|
|
25
|
+
2: "Exporting",
|
|
26
|
+
3: "Starting",
|
|
27
|
+
4: "Queuing",
|
|
28
|
+
5: "Stopping",
|
|
29
|
+
6: "Failure",
|
|
30
|
+
7: "Failed",
|
|
31
|
+
8: "Stopped",
|
|
32
|
+
9: "Completed",
|
|
33
|
+
10: "Success",
|
|
34
|
+
11: "Skipped",
|
|
35
|
+
12: "Waiting",
|
|
36
|
+
};
|
|
37
|
+
if (status === undefined || status === null) {
|
|
38
|
+
return "Never Run";
|
|
39
|
+
}
|
|
40
|
+
return statusMap[status] ?? `Unknown (${status})`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Transform agent list to summary format for display
|
|
44
|
+
*/
|
|
45
|
+
function summarizeAgents(agents) {
|
|
46
|
+
return agents.map((a) => ({
|
|
47
|
+
id: a.id,
|
|
48
|
+
name: a.name,
|
|
49
|
+
description: a.description,
|
|
50
|
+
status: getRunStatusLabel(a.status),
|
|
51
|
+
configType: a.configType,
|
|
52
|
+
version: a.version,
|
|
53
|
+
lastActivity: a.lastActivity,
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Type guard for paginated agent responses from the API.
|
|
58
|
+
*/
|
|
59
|
+
export function isPaginatedResponse(r) {
|
|
60
|
+
return r !== null && typeof r === 'object' && 'agents' in r && Array.isArray(r.agents);
|
|
61
|
+
}
|
|
62
|
+
export function parseScheduleParams(params) {
|
|
63
|
+
return {
|
|
64
|
+
scheduleType: validateNumber(params, "scheduleType", { required: false, min: 1, max: 3, integer: true }),
|
|
65
|
+
startTime: validateString(params, "startTime", false),
|
|
66
|
+
cronExpression: validateString(params, "cronExpression", false),
|
|
67
|
+
runEveryCount: validateNumber(params, "runEveryCount", { required: false, min: 1, integer: true }),
|
|
68
|
+
runEveryPeriod: validateNumber(params, "runEveryPeriod", { required: false, min: 1, max: 5, integer: true }),
|
|
69
|
+
timezone: validateString(params, "timezone", false),
|
|
70
|
+
inputParameters: validateJsonString(params, "inputParameters", false),
|
|
71
|
+
isEnabled: validateBoolean(params, "isEnabled", false),
|
|
72
|
+
parallelism: validateNumber(params, "parallelism", { required: false, min: 1, max: 50, integer: true }),
|
|
73
|
+
parallelMaxConcurrency: validateNumber(params, "parallelMaxConcurrency", { required: false, min: 1, integer: true }),
|
|
74
|
+
parallelExport: validateString(params, "parallelExport", false),
|
|
75
|
+
logLevel: validateString(params, "logLevel", false),
|
|
76
|
+
logMode: validateString(params, "logMode", false),
|
|
77
|
+
isExclusive: validateBoolean(params, "isExclusive", false),
|
|
78
|
+
isWaitOnFailure: validateBoolean(params, "isWaitOnFailure", false),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function validateScheduleStartTime(effectiveScheduleType, startTime) {
|
|
82
|
+
if (effectiveScheduleType === 1 && startTime) {
|
|
83
|
+
validateStartTimeInFuture(startTime, 1);
|
|
84
|
+
}
|
|
85
|
+
if (effectiveScheduleType === 2 && startTime) {
|
|
86
|
+
validateStartTimeInFuture(startTime, 0);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function formatToolError(error) {
|
|
90
|
+
let errorMessage;
|
|
91
|
+
let errorPrefix = "Error";
|
|
92
|
+
if (error instanceof RateLimitError) {
|
|
93
|
+
errorPrefix = "Rate Limited";
|
|
94
|
+
const retryHint = error.retryAfterSeconds
|
|
95
|
+
? ` Try again in ${error.retryAfterSeconds} seconds.`
|
|
96
|
+
: " Please wait a moment before retrying.";
|
|
97
|
+
errorMessage = `The Sequentum API rate limit has been reached.${retryHint}`;
|
|
98
|
+
}
|
|
99
|
+
else if (error instanceof AuthenticationError) {
|
|
100
|
+
errorPrefix = "Authentication Error";
|
|
101
|
+
errorMessage = error.message;
|
|
102
|
+
}
|
|
103
|
+
else if (error instanceof ApiRequestError) {
|
|
104
|
+
if (error.isUnauthorized) {
|
|
105
|
+
errorPrefix = "Authentication Failed";
|
|
106
|
+
errorMessage = "Your API key or OAuth token is invalid or has expired. Please check your credentials.";
|
|
107
|
+
}
|
|
108
|
+
else if (error.isForbidden) {
|
|
109
|
+
errorPrefix = "Access Denied";
|
|
110
|
+
errorMessage = "You don't have permission to perform this action. Check your API key permissions.";
|
|
111
|
+
}
|
|
112
|
+
else if (error.isNotFound) {
|
|
113
|
+
errorPrefix = "Not Found";
|
|
114
|
+
errorMessage = error.message;
|
|
115
|
+
}
|
|
116
|
+
else if (error.isServerError) {
|
|
117
|
+
errorPrefix = "Server Error";
|
|
118
|
+
errorMessage = `The Sequentum API encountered an internal error (${error.statusCode}). This is a server-side issue — please try again later.`;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
errorPrefix = `API Error (${error.statusCode})`;
|
|
122
|
+
errorMessage = error.message;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else if (error instanceof Error) {
|
|
126
|
+
errorMessage = error.message;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
errorMessage = "An unknown error occurred";
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: "text",
|
|
135
|
+
text: `${errorPrefix}: ${errorMessage}`,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
isError: true,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
// ==========================================
|
|
142
|
+
// Server Factory
|
|
143
|
+
// ==========================================
|
|
144
|
+
const DEBUG = process.env.DEBUG === '1';
|
|
145
|
+
function redactDebugArgs(args) {
|
|
146
|
+
if (args === null || typeof args !== "object" || Array.isArray(args)) {
|
|
147
|
+
return args;
|
|
148
|
+
}
|
|
149
|
+
const safeArgs = { ...args };
|
|
150
|
+
const sensitiveFields = [
|
|
151
|
+
"inputParameters",
|
|
152
|
+
"prompt",
|
|
153
|
+
"comments",
|
|
154
|
+
"apiKey",
|
|
155
|
+
"token",
|
|
156
|
+
"accessToken",
|
|
157
|
+
"refreshToken",
|
|
158
|
+
"password",
|
|
159
|
+
"secret",
|
|
160
|
+
"clientSecret",
|
|
161
|
+
];
|
|
162
|
+
for (const field of sensitiveFields) {
|
|
163
|
+
if (field in safeArgs) {
|
|
164
|
+
safeArgs[field] = "[REDACTED]";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return safeArgs;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Create a new MCP Server instance with all handlers registered.
|
|
171
|
+
* Each session in HTTP mode needs its own Server instance.
|
|
172
|
+
*
|
|
173
|
+
* @param apiClient - The API client to use for this server instance
|
|
174
|
+
* @param version - The server version string from package.json
|
|
175
|
+
* @returns Configured MCP Server instance
|
|
176
|
+
*/
|
|
177
|
+
export function createMcpServer(apiClient, version) {
|
|
178
|
+
const server = new Server({
|
|
179
|
+
name: "sequentum-mcp-server",
|
|
180
|
+
version,
|
|
181
|
+
}, {
|
|
182
|
+
capabilities: {
|
|
183
|
+
tools: {},
|
|
184
|
+
resources: {},
|
|
185
|
+
prompts: {},
|
|
186
|
+
},
|
|
187
|
+
// Sent to clients on `initialize`. Canonical text + JSDoc live in
|
|
188
|
+
// policies.ts; reinforced by the start_agent_build PRE-CALL CHECK in
|
|
189
|
+
// tools.ts. Keep these in sync if the policy's name or scope changes.
|
|
190
|
+
instructions: SUFFICIENCY_POLICY,
|
|
191
|
+
});
|
|
192
|
+
// Handle tool listing
|
|
193
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
194
|
+
tools,
|
|
195
|
+
}));
|
|
196
|
+
// Handle tool execution
|
|
197
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
198
|
+
const { name, arguments: args } = request.params;
|
|
199
|
+
if (DEBUG) {
|
|
200
|
+
console.error(`[DEBUG] Tool called: ${name}`);
|
|
201
|
+
console.error(`[DEBUG] Args: ${JSON.stringify(redactDebugArgs(args))}`);
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
// TODO: Consider replacing this switch with a dispatch map (Record<string, HandlerFn>)
|
|
205
|
+
// to improve readability and testability as the tool count grows.
|
|
206
|
+
switch (name) {
|
|
207
|
+
// Agent Tools
|
|
208
|
+
case "list_agents": {
|
|
209
|
+
const params = args;
|
|
210
|
+
const statusNum = validateNumber(params, "status", { required: false, min: 0, max: 12, integer: true });
|
|
211
|
+
const spaceId = validateNumber(params, "spaceId", { required: false, min: 1, integer: true });
|
|
212
|
+
const search = validateString(params, "search", false);
|
|
213
|
+
const configTypeStr = validateString(params, "configType", false);
|
|
214
|
+
const sortColumn = validateString(params, "sortColumn", false);
|
|
215
|
+
const sortOrderStr = validateString(params, "sortOrder", false);
|
|
216
|
+
const pageIndex = validateNumber(params, "pageIndex", { required: false, min: 1, integer: true });
|
|
217
|
+
const recordsPerPage = validateNumber(params, "recordsPerPage", { required: false, min: 1, max: 100, integer: true });
|
|
218
|
+
// Build filters object - ALWAYS include pagination to ensure resource-efficient API calls
|
|
219
|
+
const filters = {
|
|
220
|
+
// Always enforce pagination with defaults (pageIndex is 1-based per API spec)
|
|
221
|
+
pageIndex: pageIndex ?? 1,
|
|
222
|
+
recordsPerPage: recordsPerPage ?? 50,
|
|
223
|
+
};
|
|
224
|
+
// Add other optional filters
|
|
225
|
+
// Status is now the RunStatus enum value (1=Running, 7=Failed, 9=Completed, etc.)
|
|
226
|
+
if (statusNum !== undefined) {
|
|
227
|
+
filters.status = statusNum;
|
|
228
|
+
}
|
|
229
|
+
if (spaceId !== undefined) {
|
|
230
|
+
filters.spaceId = spaceId;
|
|
231
|
+
}
|
|
232
|
+
if (search) {
|
|
233
|
+
filters.search = search;
|
|
234
|
+
}
|
|
235
|
+
if (configTypeStr) {
|
|
236
|
+
filters.configType = configTypeStr;
|
|
237
|
+
}
|
|
238
|
+
if (sortColumn) {
|
|
239
|
+
filters.sortColumn = sortColumn;
|
|
240
|
+
}
|
|
241
|
+
if (sortOrderStr) {
|
|
242
|
+
if (sortOrderStr !== "asc" && sortOrderStr !== "desc") {
|
|
243
|
+
throw new Error(`Invalid parameter 'sortOrder': must be "asc" or "desc", got "${sortOrderStr}"`);
|
|
244
|
+
}
|
|
245
|
+
// Convert "asc"/"desc" to 0/1 as the API expects
|
|
246
|
+
filters.sortOrder = sortOrderStr === "desc" ? 1 : 0;
|
|
247
|
+
}
|
|
248
|
+
const response = await apiClient.getAllAgents(filters);
|
|
249
|
+
// Parse response — either a plain array (no pagination) or a PaginatedAgentsResponse
|
|
250
|
+
let agents;
|
|
251
|
+
let paginationInfo = null;
|
|
252
|
+
if (Array.isArray(response)) {
|
|
253
|
+
agents = response;
|
|
254
|
+
}
|
|
255
|
+
else if (isPaginatedResponse(response)) {
|
|
256
|
+
agents = response.agents;
|
|
257
|
+
paginationInfo = {
|
|
258
|
+
totalRecordCount: response.totalRecordCount,
|
|
259
|
+
pageIndex: filters.pageIndex ?? 1,
|
|
260
|
+
recordsPerPage: filters.recordsPerPage ?? 50,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
throw new Error(`Unexpected response type: ${typeof response}`);
|
|
265
|
+
}
|
|
266
|
+
const summary = summarizeAgents(agents);
|
|
267
|
+
// Include pagination info if available
|
|
268
|
+
const result = paginationInfo ? {
|
|
269
|
+
agents: summary,
|
|
270
|
+
pagination: paginationInfo,
|
|
271
|
+
} : summary;
|
|
272
|
+
return {
|
|
273
|
+
content: [
|
|
274
|
+
{
|
|
275
|
+
type: "text",
|
|
276
|
+
text: JSON.stringify(result, null, 2),
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
case "get_agent": {
|
|
282
|
+
const params = args;
|
|
283
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
284
|
+
const agent = await apiClient.getAgent(agentId);
|
|
285
|
+
return {
|
|
286
|
+
content: [
|
|
287
|
+
{
|
|
288
|
+
type: "text",
|
|
289
|
+
text: JSON.stringify(agent, null, 2),
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
case "search_agents": {
|
|
295
|
+
const params = args;
|
|
296
|
+
const query = validateString(params, "query");
|
|
297
|
+
if (!query.trim()) {
|
|
298
|
+
throw new Error("Search query cannot be empty");
|
|
299
|
+
}
|
|
300
|
+
const maxRecords = validateNumber(params, "maxRecords", { required: false, min: 1, max: 1000, integer: true });
|
|
301
|
+
const agents = await apiClient.searchAgents(query, maxRecords);
|
|
302
|
+
return {
|
|
303
|
+
content: [
|
|
304
|
+
{
|
|
305
|
+
type: "text",
|
|
306
|
+
text: JSON.stringify(summarizeAgents(agents), null, 2),
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
// Run Tools
|
|
312
|
+
case "get_agent_runs": {
|
|
313
|
+
const params = args;
|
|
314
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
315
|
+
const maxRecords = validateNumber(params, "maxRecords", { required: false, min: 1, max: 1000, integer: true });
|
|
316
|
+
const runs = await apiClient.getAgentRuns(agentId, maxRecords);
|
|
317
|
+
return {
|
|
318
|
+
content: [
|
|
319
|
+
{
|
|
320
|
+
type: "text",
|
|
321
|
+
text: JSON.stringify(runs, null, 2),
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
case "get_run_status": {
|
|
327
|
+
const params = args;
|
|
328
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
329
|
+
const runId = validateNumber(params, "runId", { min: 1, integer: true });
|
|
330
|
+
const status = await apiClient.getRunStatus(agentId, runId);
|
|
331
|
+
return {
|
|
332
|
+
content: [
|
|
333
|
+
{
|
|
334
|
+
type: "text",
|
|
335
|
+
text: JSON.stringify(status, null, 2),
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
case "start_agent": {
|
|
341
|
+
const params = args;
|
|
342
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
343
|
+
const inputParameters = validateJsonString(params, "inputParameters", false);
|
|
344
|
+
const isRunSynchronously = validateBoolean(params, "isRunSynchronously", false);
|
|
345
|
+
const timeout = validateNumber(params, "timeout", { required: false, min: 1, max: 3600, integer: true });
|
|
346
|
+
const parallelism = validateNumber(params, "parallelism", { required: false, min: 1, max: 50, integer: true });
|
|
347
|
+
const result = await apiClient.startAgent(agentId, {
|
|
348
|
+
inputParameters,
|
|
349
|
+
isRunSynchronously: isRunSynchronously ?? false,
|
|
350
|
+
timeout: timeout ?? 60,
|
|
351
|
+
parallelism: parallelism ?? 1,
|
|
352
|
+
});
|
|
353
|
+
if (typeof result === "string") {
|
|
354
|
+
// Synchronous run returned data directly
|
|
355
|
+
return {
|
|
356
|
+
content: [
|
|
357
|
+
{
|
|
358
|
+
type: "text",
|
|
359
|
+
text: result,
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
// Asynchronous run returned run info
|
|
366
|
+
return {
|
|
367
|
+
content: [
|
|
368
|
+
{
|
|
369
|
+
type: "text",
|
|
370
|
+
text: `Agent started successfully.\n\n${JSON.stringify(result, null, 2)}`,
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
case "stop_agent": {
|
|
377
|
+
const params = args;
|
|
378
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
379
|
+
const runId = validateNumber(params, "runId", { min: 1, integer: true });
|
|
380
|
+
await apiClient.stopAgent(agentId, runId);
|
|
381
|
+
return {
|
|
382
|
+
content: [
|
|
383
|
+
{
|
|
384
|
+
type: "text",
|
|
385
|
+
text: `Successfully stopped run ${runId} for agent ${agentId}`,
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
case "kill_agent": {
|
|
391
|
+
const params = args;
|
|
392
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
393
|
+
const runId = validateNumber(params, "runId", { min: 1, integer: true });
|
|
394
|
+
await apiClient.killAgent(agentId, runId);
|
|
395
|
+
return {
|
|
396
|
+
content: [
|
|
397
|
+
{
|
|
398
|
+
type: "text",
|
|
399
|
+
text: `Kill command sent for run ${runId} of agent ${agentId}. If the agent was running, it will initiate graceful stop. If already stopping, it will force immediate termination.`,
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
// Destructive Operations
|
|
405
|
+
case "delete_run": {
|
|
406
|
+
const params = args;
|
|
407
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
408
|
+
const runId = validateNumber(params, "runId", { min: 1, integer: true });
|
|
409
|
+
const removeMethod = validateEnum(params, "removeMethod", ["RemoveEntireRun", "RemoveAllFiles", "RemoveAllFilesAndAgentInput"], false);
|
|
410
|
+
await apiClient.deleteRun(agentId, runId, removeMethod);
|
|
411
|
+
const methodDescriptions = {
|
|
412
|
+
RemoveEntireRun: `Successfully deleted run ${runId} and all associated files from agent ${agentId}.`,
|
|
413
|
+
RemoveAllFiles: `Successfully removed all files for run ${runId} from agent ${agentId}. The run record has been preserved.`,
|
|
414
|
+
RemoveAllFilesAndAgentInput: `Successfully removed all files and agent input for run ${runId} from agent ${agentId}. The run record has been preserved.`,
|
|
415
|
+
};
|
|
416
|
+
const description = (removeMethod ? methodDescriptions[removeMethod] : undefined) ??
|
|
417
|
+
`Successfully deleted run ${runId} and all associated files from agent ${agentId}.`;
|
|
418
|
+
return {
|
|
419
|
+
content: [
|
|
420
|
+
{
|
|
421
|
+
type: "text",
|
|
422
|
+
text: description,
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
// File Tools
|
|
428
|
+
case "get_run_files": {
|
|
429
|
+
const params = args;
|
|
430
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
431
|
+
const runId = validateNumber(params, "runId", { min: 1, integer: true });
|
|
432
|
+
const files = await apiClient.getRunFiles(agentId, runId);
|
|
433
|
+
if (files.length === 0) {
|
|
434
|
+
return {
|
|
435
|
+
content: [
|
|
436
|
+
{
|
|
437
|
+
type: "text",
|
|
438
|
+
text: "No files found for this run.",
|
|
439
|
+
},
|
|
440
|
+
],
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
const summary = files.map((f) => ({
|
|
444
|
+
id: f.id,
|
|
445
|
+
name: f.name,
|
|
446
|
+
fileType: f.fileType,
|
|
447
|
+
fileSize: `${((f.fileSize ?? 0) / 1024).toFixed(2)} KB`,
|
|
448
|
+
created: f.created,
|
|
449
|
+
}));
|
|
450
|
+
return {
|
|
451
|
+
content: [
|
|
452
|
+
{
|
|
453
|
+
type: "text",
|
|
454
|
+
text: JSON.stringify(summary, null, 2),
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
case "get_file_download_url": {
|
|
460
|
+
const params = args;
|
|
461
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
462
|
+
const runId = validateNumber(params, "runId", { min: 1, integer: true });
|
|
463
|
+
const fileId = validateNumber(params, "fileId", { min: 1, integer: true });
|
|
464
|
+
const result = await apiClient.downloadRunFile(agentId, runId, fileId);
|
|
465
|
+
return {
|
|
466
|
+
content: [
|
|
467
|
+
{
|
|
468
|
+
type: "text",
|
|
469
|
+
text: `Download URL:\n${result.redirectUrl}\n\nNote: This URL is temporary and will expire.`,
|
|
470
|
+
},
|
|
471
|
+
],
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
// Version Tools
|
|
475
|
+
case "get_agent_versions": {
|
|
476
|
+
const params = args;
|
|
477
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
478
|
+
const versions = await apiClient.getAgentVersions(agentId);
|
|
479
|
+
return {
|
|
480
|
+
content: [
|
|
481
|
+
{
|
|
482
|
+
type: "text",
|
|
483
|
+
text: JSON.stringify(versions, null, 2),
|
|
484
|
+
},
|
|
485
|
+
],
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
case "restore_agent_version": {
|
|
489
|
+
const params = args;
|
|
490
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
491
|
+
const versionNumber = validateNumber(params, "versionNumber", { min: 1, integer: true });
|
|
492
|
+
const comments = validateString(params, "comments");
|
|
493
|
+
await apiClient.restoreAgentVersion(agentId, versionNumber, comments);
|
|
494
|
+
return {
|
|
495
|
+
content: [
|
|
496
|
+
{
|
|
497
|
+
type: "text",
|
|
498
|
+
text: `Successfully restored agent ${agentId} to version ${versionNumber}.\n\nA new version has been created based on version ${versionNumber}.`,
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
// Schedule Tools
|
|
504
|
+
case "list_agent_schedules": {
|
|
505
|
+
const params = args;
|
|
506
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
507
|
+
const schedules = await apiClient.getAgentSchedules(agentId);
|
|
508
|
+
return {
|
|
509
|
+
content: [
|
|
510
|
+
{
|
|
511
|
+
type: "text",
|
|
512
|
+
text: JSON.stringify(schedules, null, 2),
|
|
513
|
+
},
|
|
514
|
+
],
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
case "create_agent_schedule": {
|
|
518
|
+
const params = args;
|
|
519
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
520
|
+
const name = validateString(params, "name");
|
|
521
|
+
const { scheduleType, startTime, cronExpression, runEveryCount, runEveryPeriod, timezone, inputParameters, isEnabled, parallelism, parallelMaxConcurrency, parallelExport, logLevel, logMode, isExclusive, isWaitOnFailure, } = parseScheduleParams(params);
|
|
522
|
+
// Validate schedule type specific parameters
|
|
523
|
+
const effectiveScheduleType = scheduleType ?? 3; // Default to CRON
|
|
524
|
+
// RunOnce (1): startTime is required and must be at least 1 minute in the future
|
|
525
|
+
if (effectiveScheduleType === 1) {
|
|
526
|
+
if (!startTime) {
|
|
527
|
+
throw new Error("startTime is required when scheduleType is 1 (RunOnce)");
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
// RunEvery (2): runEveryCount and runEveryPeriod are required, startTime is optional but must be in the future if provided
|
|
531
|
+
if (effectiveScheduleType === 2) {
|
|
532
|
+
if (runEveryCount === undefined || runEveryPeriod === undefined) {
|
|
533
|
+
throw new Error("runEveryCount and runEveryPeriod are required when scheduleType is 2 (RunEvery)");
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// CRON (3): cronExpression is required, startTime is not used
|
|
537
|
+
if (effectiveScheduleType === 3 && !cronExpression) {
|
|
538
|
+
throw new Error("cronExpression is required when scheduleType is 3 (CRON)");
|
|
539
|
+
}
|
|
540
|
+
validateScheduleStartTime(effectiveScheduleType, startTime);
|
|
541
|
+
const schedule = await apiClient.createAgentSchedule(agentId, {
|
|
542
|
+
name,
|
|
543
|
+
scheduleType: effectiveScheduleType,
|
|
544
|
+
startTime,
|
|
545
|
+
cronExpression,
|
|
546
|
+
runEveryCount,
|
|
547
|
+
runEveryPeriod,
|
|
548
|
+
timezone,
|
|
549
|
+
inputParameters,
|
|
550
|
+
isEnabled: isEnabled ?? true,
|
|
551
|
+
parallelism: parallelism ?? 1,
|
|
552
|
+
parallelMaxConcurrency,
|
|
553
|
+
parallelExport,
|
|
554
|
+
logLevel,
|
|
555
|
+
logMode,
|
|
556
|
+
isExclusive,
|
|
557
|
+
isWaitOnFailure,
|
|
558
|
+
});
|
|
559
|
+
return {
|
|
560
|
+
content: [
|
|
561
|
+
{
|
|
562
|
+
type: "text",
|
|
563
|
+
text: `Schedule created successfully.\n\n${JSON.stringify(schedule, null, 2)}`,
|
|
564
|
+
},
|
|
565
|
+
],
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
case "delete_agent_schedule": {
|
|
569
|
+
const params = args;
|
|
570
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
571
|
+
const scheduleId = validateNumber(params, "scheduleId", { min: 1, integer: true });
|
|
572
|
+
await apiClient.deleteAgentSchedule(agentId, scheduleId);
|
|
573
|
+
return {
|
|
574
|
+
content: [
|
|
575
|
+
{
|
|
576
|
+
type: "text",
|
|
577
|
+
text: `Successfully deleted schedule ${scheduleId} from agent ${agentId}`,
|
|
578
|
+
},
|
|
579
|
+
],
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
case "get_agent_schedule": {
|
|
583
|
+
const params = args;
|
|
584
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
585
|
+
const scheduleId = validateNumber(params, "scheduleId", { min: 1, integer: true });
|
|
586
|
+
const schedule = await apiClient.getAgentSchedule(agentId, scheduleId);
|
|
587
|
+
return {
|
|
588
|
+
content: [
|
|
589
|
+
{
|
|
590
|
+
type: "text",
|
|
591
|
+
text: JSON.stringify(schedule, null, 2),
|
|
592
|
+
},
|
|
593
|
+
],
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
case "update_agent_schedule": {
|
|
597
|
+
const params = args;
|
|
598
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
599
|
+
const scheduleId = validateNumber(params, "scheduleId", { min: 1, integer: true });
|
|
600
|
+
const name = validateString(params, "name");
|
|
601
|
+
const { scheduleType, startTime, cronExpression, runEveryCount, runEveryPeriod, timezone, inputParameters, isEnabled, parallelism, parallelMaxConcurrency, parallelExport, logLevel, logMode, isExclusive, isWaitOnFailure, } = parseScheduleParams(params);
|
|
602
|
+
const hasCronFields = cronExpression !== undefined;
|
|
603
|
+
const hasRunEveryFields = runEveryCount !== undefined || runEveryPeriod !== undefined;
|
|
604
|
+
if (hasCronFields && hasRunEveryFields && scheduleType === undefined) {
|
|
605
|
+
throw new Error("Conflicting schedule fields: both cronExpression and runEveryCount/runEveryPeriod were provided without an explicit scheduleType. " +
|
|
606
|
+
"Specify scheduleType to clarify intent (2=RunEvery, 3=CRON).");
|
|
607
|
+
}
|
|
608
|
+
// Infer scheduleType from provided fields when not explicitly set,
|
|
609
|
+
// so the user doesn't have to redundantly specify it on every update.
|
|
610
|
+
let effectiveScheduleType = scheduleType;
|
|
611
|
+
if (effectiveScheduleType === undefined) {
|
|
612
|
+
if (hasCronFields)
|
|
613
|
+
effectiveScheduleType = 3;
|
|
614
|
+
else if (hasRunEveryFields)
|
|
615
|
+
effectiveScheduleType = 2;
|
|
616
|
+
}
|
|
617
|
+
validateScheduleStartTime(effectiveScheduleType, startTime);
|
|
618
|
+
const updated = await apiClient.updateAgentSchedule(agentId, scheduleId, {
|
|
619
|
+
name,
|
|
620
|
+
scheduleType: effectiveScheduleType,
|
|
621
|
+
startTime,
|
|
622
|
+
cronExpression,
|
|
623
|
+
runEveryCount,
|
|
624
|
+
runEveryPeriod,
|
|
625
|
+
timezone,
|
|
626
|
+
inputParameters,
|
|
627
|
+
isEnabled,
|
|
628
|
+
parallelism,
|
|
629
|
+
parallelMaxConcurrency,
|
|
630
|
+
parallelExport,
|
|
631
|
+
logLevel,
|
|
632
|
+
logMode,
|
|
633
|
+
isExclusive,
|
|
634
|
+
isWaitOnFailure,
|
|
635
|
+
});
|
|
636
|
+
return {
|
|
637
|
+
content: [
|
|
638
|
+
{
|
|
639
|
+
type: "text",
|
|
640
|
+
text: `Schedule updated successfully.\n\n${JSON.stringify(updated, null, 2)}`,
|
|
641
|
+
},
|
|
642
|
+
],
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
case "enable_agent_schedule": {
|
|
646
|
+
const params = args;
|
|
647
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
648
|
+
const scheduleId = validateNumber(params, "scheduleId", { min: 1, integer: true });
|
|
649
|
+
await apiClient.enableAgentSchedule(agentId, scheduleId);
|
|
650
|
+
return {
|
|
651
|
+
content: [
|
|
652
|
+
{
|
|
653
|
+
type: "text",
|
|
654
|
+
text: `Successfully enabled schedule ${scheduleId} for agent ${agentId}. The schedule will now run according to its configuration.`,
|
|
655
|
+
},
|
|
656
|
+
],
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
case "disable_agent_schedule": {
|
|
660
|
+
const params = args;
|
|
661
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
662
|
+
const scheduleId = validateNumber(params, "scheduleId", { min: 1, integer: true });
|
|
663
|
+
await apiClient.disableAgentSchedule(agentId, scheduleId);
|
|
664
|
+
return {
|
|
665
|
+
content: [
|
|
666
|
+
{
|
|
667
|
+
type: "text",
|
|
668
|
+
text: `Successfully disabled schedule ${scheduleId} for agent ${agentId}. The schedule will not run until re-enabled.`,
|
|
669
|
+
},
|
|
670
|
+
],
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
case "get_scheduled_runs": {
|
|
674
|
+
const params = args;
|
|
675
|
+
const startDate = validateString(params, "startDate", false);
|
|
676
|
+
const endDate = validateString(params, "endDate", false);
|
|
677
|
+
const schedules = await apiClient.getUpcomingSchedules(startDate, endDate);
|
|
678
|
+
return {
|
|
679
|
+
content: [
|
|
680
|
+
{
|
|
681
|
+
type: "text",
|
|
682
|
+
text: JSON.stringify(schedules, null, 2),
|
|
683
|
+
},
|
|
684
|
+
],
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
// Billing/Credits Tools
|
|
688
|
+
case "get_credits_balance": {
|
|
689
|
+
const balance = await apiClient.getCreditsBalance();
|
|
690
|
+
return {
|
|
691
|
+
content: [
|
|
692
|
+
{
|
|
693
|
+
type: "text",
|
|
694
|
+
text: JSON.stringify(balance, null, 2),
|
|
695
|
+
},
|
|
696
|
+
],
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
case "get_spending_summary": {
|
|
700
|
+
const params = args;
|
|
701
|
+
const startDate = validateString(params, "startDate", false);
|
|
702
|
+
const endDate = validateString(params, "endDate", false);
|
|
703
|
+
const spending = await apiClient.getSpendingSummary(startDate, endDate);
|
|
704
|
+
return {
|
|
705
|
+
content: [
|
|
706
|
+
{
|
|
707
|
+
type: "text",
|
|
708
|
+
text: JSON.stringify(spending, null, 2),
|
|
709
|
+
},
|
|
710
|
+
],
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
case "get_credit_history": {
|
|
714
|
+
const params = args;
|
|
715
|
+
const pageIndex = validateNumber(params, "pageIndex", { required: false, min: 1, integer: true });
|
|
716
|
+
const recordsPerPage = validateNumber(params, "recordsPerPage", { required: false, min: 1, max: 100, integer: true });
|
|
717
|
+
const history = await apiClient.getCreditHistory(pageIndex, recordsPerPage);
|
|
718
|
+
return {
|
|
719
|
+
content: [
|
|
720
|
+
{
|
|
721
|
+
type: "text",
|
|
722
|
+
text: JSON.stringify(history, null, 2),
|
|
723
|
+
},
|
|
724
|
+
],
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
case "get_agents_usage": {
|
|
728
|
+
const params = args;
|
|
729
|
+
const defaults = getDefaultDateRange();
|
|
730
|
+
const startDate = validateString(params, "startDate", false) ?? defaults.startDate;
|
|
731
|
+
const endDate = validateString(params, "endDate", false) ?? defaults.endDate;
|
|
732
|
+
validateISODate(startDate, "startDate");
|
|
733
|
+
validateISODate(endDate, "endDate");
|
|
734
|
+
validateDateRange(startDate, endDate);
|
|
735
|
+
const pageIndex = validateNumber(params, "pageIndex", { required: false, min: 1, integer: true });
|
|
736
|
+
const recordsPerPage = validateNumber(params, "recordsPerPage", { required: false, min: 1, max: 1000, integer: true });
|
|
737
|
+
const sortColumn = validateString(params, "sortColumn", false);
|
|
738
|
+
const sortOrder = validateNumber(params, "sortOrder", { required: false, min: 0, max: 1, integer: true });
|
|
739
|
+
const name = validateString(params, "name", false);
|
|
740
|
+
const usageTypes = validateString(params, "usageTypes", false);
|
|
741
|
+
const result = await apiClient.getAgentsUsage(startDate, endDate, pageIndex, recordsPerPage, sortColumn, sortOrder, name, usageTypes);
|
|
742
|
+
return {
|
|
743
|
+
content: [
|
|
744
|
+
{
|
|
745
|
+
type: "text",
|
|
746
|
+
text: JSON.stringify(result, null, 2),
|
|
747
|
+
},
|
|
748
|
+
],
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
case "get_agent_cost_breakdown": {
|
|
752
|
+
const params = args;
|
|
753
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
754
|
+
const defaults = getDefaultDateRange();
|
|
755
|
+
const startDate = validateString(params, "startDate", false) ?? defaults.startDate;
|
|
756
|
+
const endDate = validateString(params, "endDate", false) ?? defaults.endDate;
|
|
757
|
+
validateISODate(startDate, "startDate");
|
|
758
|
+
validateISODate(endDate, "endDate");
|
|
759
|
+
validateDateRange(startDate, endDate);
|
|
760
|
+
const timeUnit = validateEnum(params, "timeUnit", ["day", "month"], false);
|
|
761
|
+
const usageTypes = validateString(params, "usageTypes", false);
|
|
762
|
+
const result = await apiClient.getAgentCostBreakdown(agentId, startDate, endDate, timeUnit, usageTypes);
|
|
763
|
+
return {
|
|
764
|
+
content: [
|
|
765
|
+
{
|
|
766
|
+
type: "text",
|
|
767
|
+
text: JSON.stringify(result, null, 2),
|
|
768
|
+
},
|
|
769
|
+
],
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
case "get_agent_runs_cost": {
|
|
773
|
+
const params = args;
|
|
774
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
775
|
+
const defaults = getDefaultDateRange();
|
|
776
|
+
const startDate = validateString(params, "startDate", false) ?? defaults.startDate;
|
|
777
|
+
const endDate = validateString(params, "endDate", false) ?? defaults.endDate;
|
|
778
|
+
validateISODate(startDate, "startDate");
|
|
779
|
+
validateISODate(endDate, "endDate");
|
|
780
|
+
validateDateRange(startDate, endDate);
|
|
781
|
+
const pageIndex = validateNumber(params, "pageIndex", { required: false, min: 1, integer: true });
|
|
782
|
+
const recordsPerPage = validateNumber(params, "recordsPerPage", { required: false, min: 1, max: 1000, integer: true });
|
|
783
|
+
const sortColumn = validateEnum(params, "sortColumn", ["date", "cost", "duration"], false);
|
|
784
|
+
const sortOrder = validateNumber(params, "sortOrder", { required: false, min: 0, max: 1, integer: true });
|
|
785
|
+
const usageTypes = validateString(params, "usageTypes", false);
|
|
786
|
+
const result = await apiClient.getAgentRunsCost(agentId, startDate, endDate, pageIndex, recordsPerPage, sortColumn, sortOrder, usageTypes);
|
|
787
|
+
return {
|
|
788
|
+
content: [
|
|
789
|
+
{
|
|
790
|
+
type: "text",
|
|
791
|
+
text: JSON.stringify(result, null, 2),
|
|
792
|
+
},
|
|
793
|
+
],
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
// Space Tools
|
|
797
|
+
case "list_spaces": {
|
|
798
|
+
const spaces = await apiClient.getAllSpaces();
|
|
799
|
+
return {
|
|
800
|
+
content: [
|
|
801
|
+
{
|
|
802
|
+
type: "text",
|
|
803
|
+
text: JSON.stringify(spaces, null, 2),
|
|
804
|
+
},
|
|
805
|
+
],
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
case "get_space": {
|
|
809
|
+
const params = args;
|
|
810
|
+
const spaceId = validateNumber(params, "spaceId", { min: 1, integer: true });
|
|
811
|
+
const space = await apiClient.getSpace(spaceId);
|
|
812
|
+
return {
|
|
813
|
+
content: [
|
|
814
|
+
{
|
|
815
|
+
type: "text",
|
|
816
|
+
text: JSON.stringify(space, null, 2),
|
|
817
|
+
},
|
|
818
|
+
],
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
case "get_space_agents": {
|
|
822
|
+
const params = args;
|
|
823
|
+
const spaceId = validateNumber(params, "spaceId", { min: 1, integer: true });
|
|
824
|
+
const agents = await apiClient.getSpaceAgents(spaceId);
|
|
825
|
+
return {
|
|
826
|
+
content: [
|
|
827
|
+
{
|
|
828
|
+
type: "text",
|
|
829
|
+
text: JSON.stringify(agents, null, 2),
|
|
830
|
+
},
|
|
831
|
+
],
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
case "search_space_by_name": {
|
|
835
|
+
const params = args;
|
|
836
|
+
const name = validateString(params, "name");
|
|
837
|
+
const space = await apiClient.searchSpaceByName(name);
|
|
838
|
+
return {
|
|
839
|
+
content: [
|
|
840
|
+
{
|
|
841
|
+
type: "text",
|
|
842
|
+
text: JSON.stringify(space, null, 2),
|
|
843
|
+
},
|
|
844
|
+
],
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
case "run_space_agents": {
|
|
848
|
+
const params = args;
|
|
849
|
+
const spaceId = validateNumber(params, "spaceId", { min: 1, integer: true });
|
|
850
|
+
const inputParameters = validateJsonString(params, "inputParameters", false);
|
|
851
|
+
const result = await apiClient.runSpaceAgents(spaceId, inputParameters);
|
|
852
|
+
return {
|
|
853
|
+
content: [
|
|
854
|
+
{
|
|
855
|
+
type: "text",
|
|
856
|
+
text: `Started agents in space.\n\n${JSON.stringify(result, null, 2)}`,
|
|
857
|
+
},
|
|
858
|
+
],
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
// Analytics Tools
|
|
862
|
+
case "get_runs_summary": {
|
|
863
|
+
const params = args;
|
|
864
|
+
const startDate = validateString(params, "startDate", false);
|
|
865
|
+
const endDate = validateString(params, "endDate", false);
|
|
866
|
+
const status = validateString(params, "status", false);
|
|
867
|
+
const includeDetails = validateBoolean(params, "includeDetails", false);
|
|
868
|
+
const summary = await apiClient.getRunsSummary(startDate, endDate, status, includeDetails);
|
|
869
|
+
return {
|
|
870
|
+
content: [
|
|
871
|
+
{
|
|
872
|
+
type: "text",
|
|
873
|
+
text: JSON.stringify(summary, null, 2),
|
|
874
|
+
},
|
|
875
|
+
],
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
case "get_records_summary": {
|
|
879
|
+
const params = args;
|
|
880
|
+
const startDate = validateString(params, "startDate", false);
|
|
881
|
+
const endDate = validateString(params, "endDate", false);
|
|
882
|
+
const agentId = validateNumber(params, "agentId", { required: false, min: 1, integer: true });
|
|
883
|
+
const summary = await apiClient.getRecordsSummary(startDate, endDate, agentId);
|
|
884
|
+
return {
|
|
885
|
+
content: [
|
|
886
|
+
{
|
|
887
|
+
type: "text",
|
|
888
|
+
text: JSON.stringify(summary, null, 2),
|
|
889
|
+
},
|
|
890
|
+
],
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
case "get_run_diagnostics": {
|
|
894
|
+
const params = args;
|
|
895
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
896
|
+
const runId = validateNumber(params, "runId", { min: 1, integer: true });
|
|
897
|
+
const diagnostics = await apiClient.getRunDiagnostics(agentId, runId);
|
|
898
|
+
return {
|
|
899
|
+
content: [
|
|
900
|
+
{
|
|
901
|
+
type: "text",
|
|
902
|
+
text: JSON.stringify(diagnostics, null, 2),
|
|
903
|
+
},
|
|
904
|
+
],
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
case "get_latest_failure": {
|
|
908
|
+
const params = args;
|
|
909
|
+
const agentId = validateNumber(params, "agentId", { min: 1, integer: true });
|
|
910
|
+
const diagnostics = await apiClient.getLatestFailure(agentId);
|
|
911
|
+
return {
|
|
912
|
+
content: [
|
|
913
|
+
{
|
|
914
|
+
type: "text",
|
|
915
|
+
text: JSON.stringify(diagnostics, null, 2),
|
|
916
|
+
},
|
|
917
|
+
],
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
// Agent Builder Tools
|
|
921
|
+
case "start_agent_build": {
|
|
922
|
+
const params = args;
|
|
923
|
+
const prompt = validateString(params, "prompt", {
|
|
924
|
+
required: true,
|
|
925
|
+
minLength: 10,
|
|
926
|
+
maxLength: 5000,
|
|
927
|
+
trim: true,
|
|
928
|
+
});
|
|
929
|
+
const spaceId = validateNumber(params, "spaceId", { required: false, min: 1, integer: true });
|
|
930
|
+
const response = await apiClient.startAgentBuild({ prompt, spaceId });
|
|
931
|
+
return {
|
|
932
|
+
content: [
|
|
933
|
+
{
|
|
934
|
+
type: "text",
|
|
935
|
+
text: JSON.stringify(response, null, 2),
|
|
936
|
+
},
|
|
937
|
+
],
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
case "get_agent_build_status": {
|
|
941
|
+
const params = args;
|
|
942
|
+
// maxLength:256 guards against oversized session IDs being forwarded to the API (#7)
|
|
943
|
+
const sessionId = validateString(params, "sessionId", { required: true, maxLength: 256 });
|
|
944
|
+
const status = await apiClient.getAgentBuildStatus(sessionId);
|
|
945
|
+
// The backend's `error` field is a raw ex.Message passthrough that can contain
|
|
946
|
+
// stack traces, internal endpoint paths, upstream LLM URLs, and similar. Replace
|
|
947
|
+
// it with a fixed user-facing string; log the raw value at DEBUG only (#6).
|
|
948
|
+
if (DEBUG && status.status === "error" && status.error) {
|
|
949
|
+
console.error(`[DEBUG] Agent build session ${sessionId} failed: ${status.error}`);
|
|
950
|
+
}
|
|
951
|
+
const sanitized = {
|
|
952
|
+
...status,
|
|
953
|
+
error: status.status === "error"
|
|
954
|
+
? "Build failed. Please review your prompt and try again."
|
|
955
|
+
: undefined,
|
|
956
|
+
};
|
|
957
|
+
return {
|
|
958
|
+
content: [
|
|
959
|
+
{
|
|
960
|
+
type: "text",
|
|
961
|
+
text: JSON.stringify(sanitized, null, 2),
|
|
962
|
+
},
|
|
963
|
+
],
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
case "stop_agent_build": {
|
|
967
|
+
const params = args;
|
|
968
|
+
// maxLength:256 guards against oversized session IDs (#7)
|
|
969
|
+
const sessionId = validateString(params, "sessionId", { required: true, maxLength: 256 });
|
|
970
|
+
await apiClient.stopAgentBuild(sessionId);
|
|
971
|
+
// Return structured JSON consistent with every other tool handler (#8)
|
|
972
|
+
return {
|
|
973
|
+
content: [
|
|
974
|
+
{
|
|
975
|
+
type: "text",
|
|
976
|
+
text: JSON.stringify({ stopped: true, sessionId }, null, 2),
|
|
977
|
+
},
|
|
978
|
+
],
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
default:
|
|
982
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
catch (error) {
|
|
986
|
+
return formatToolError(error);
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
// ==========================================
|
|
990
|
+
// Resource Handlers
|
|
991
|
+
// ==========================================
|
|
992
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
993
|
+
resources,
|
|
994
|
+
}));
|
|
995
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
996
|
+
resourceTemplates,
|
|
997
|
+
}));
|
|
998
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
999
|
+
const { uri } = request.params;
|
|
1000
|
+
if (DEBUG) {
|
|
1001
|
+
console.error(`[DEBUG] Resource read: ${uri}`);
|
|
1002
|
+
}
|
|
1003
|
+
try {
|
|
1004
|
+
const content = await readResource(uri, apiClient);
|
|
1005
|
+
return {
|
|
1006
|
+
contents: [content],
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
catch (error) {
|
|
1010
|
+
const errorMessage = error instanceof Error ? error.message : "An unknown error occurred";
|
|
1011
|
+
throw new Error(`Failed to read resource ${uri}: ${errorMessage}`);
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
// ==========================================
|
|
1015
|
+
// Prompt Handlers
|
|
1016
|
+
// ==========================================
|
|
1017
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
1018
|
+
prompts,
|
|
1019
|
+
}));
|
|
1020
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
1021
|
+
const { name, arguments: args } = request.params;
|
|
1022
|
+
if (DEBUG) {
|
|
1023
|
+
console.error(`[DEBUG] Prompt requested: ${name}`);
|
|
1024
|
+
console.error(`[DEBUG] Args: ${JSON.stringify(redactDebugArgs(args))}`);
|
|
1025
|
+
}
|
|
1026
|
+
const messages = getPromptMessages(name, args);
|
|
1027
|
+
return { messages };
|
|
1028
|
+
});
|
|
1029
|
+
return server;
|
|
1030
|
+
}
|
|
1031
|
+
//# sourceMappingURL=handlers.js.map
|