@chandshantanu/agentbuilder-mcp-server 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +7 -0
- package/AGENT_CREATION_GUIDE.md +724 -0
- package/AGENT_CREATION_TEST_PLAN.md +514 -0
- package/AGENT_DEPLOYMENT_GUIDE.md +509 -0
- package/GETTING_STARTED.md +435 -0
- package/LOCAL_DEVELOPMENT_TESTING_GUIDE.md +358 -0
- package/PRODUCTION_DEPLOYMENT_GUIDE.md +486 -0
- package/README.md +978 -0
- package/claude_desktop_config.example.json +13 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2751 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
- package/setup-production.sh +130 -0
- package/setup.sh +88 -0
- package/src/index.ts +3104 -0
- package/test-connection.js +154 -0
- package/tsconfig.json +21 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,3104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AgentBuilder MCP Server
|
|
5
|
+
* Provides workflow creation and management tools for Claude Code
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import {
|
|
11
|
+
CallToolRequestSchema,
|
|
12
|
+
ListToolsRequestSchema,
|
|
13
|
+
Tool,
|
|
14
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
15
|
+
import axios, { AxiosInstance } from 'axios';
|
|
16
|
+
import * as http from 'http';
|
|
17
|
+
import * as fs from 'fs';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import * as os from 'os';
|
|
20
|
+
|
|
21
|
+
const CREDENTIALS_PATH = path.join(os.homedir(), '.agentbuilder', 'credentials.json');
|
|
22
|
+
const FRONTEND_URL = 'https://www.chatslytics.com';
|
|
23
|
+
|
|
24
|
+
function loadStoredToken(): string | null {
|
|
25
|
+
try {
|
|
26
|
+
if (fs.existsSync(CREDENTIALS_PATH)) {
|
|
27
|
+
const creds = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf-8'));
|
|
28
|
+
if (creds.token && creds.expires_at && Date.now() < creds.expires_at) {
|
|
29
|
+
return creds.token;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} catch {}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function saveToken(token: string, expiresIn: number = 3600) {
|
|
37
|
+
const dir = path.dirname(CREDENTIALS_PATH);
|
|
38
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
39
|
+
fs.writeFileSync(CREDENTIALS_PATH, JSON.stringify({
|
|
40
|
+
token,
|
|
41
|
+
expires_at: Date.now() + (expiresIn * 1000) - 60000, // 1 min buffer
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function promptLogin(): Promise<string> {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
// Start local HTTP server to receive the token callback
|
|
48
|
+
const server = http.createServer((req, res) => {
|
|
49
|
+
// Allow CORS from the frontend
|
|
50
|
+
res.setHeader('Access-Control-Allow-Origin', FRONTEND_URL);
|
|
51
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
52
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
53
|
+
|
|
54
|
+
if (req.method === 'OPTIONS') {
|
|
55
|
+
res.writeHead(204);
|
|
56
|
+
res.end();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (req.method === 'POST' && req.url === '/callback') {
|
|
61
|
+
let body = '';
|
|
62
|
+
req.on('data', chunk => { body += chunk; });
|
|
63
|
+
req.on('end', () => {
|
|
64
|
+
try {
|
|
65
|
+
const { token, expires_in } = JSON.parse(body);
|
|
66
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
67
|
+
res.end(JSON.stringify({ success: true }));
|
|
68
|
+
server.close();
|
|
69
|
+
saveToken(token, expires_in || 3600);
|
|
70
|
+
resolve(token);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
res.writeHead(400);
|
|
73
|
+
res.end('Invalid payload');
|
|
74
|
+
reject(new Error('Invalid token callback payload'));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
res.writeHead(404);
|
|
79
|
+
res.end();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
server.listen(0, '127.0.0.1', async () => {
|
|
84
|
+
const port = (server.address() as any).port;
|
|
85
|
+
const loginUrl = `${FRONTEND_URL}/login?mcp_callback=http://127.0.0.1:${port}/callback`;
|
|
86
|
+
|
|
87
|
+
console.error('\nš AgentBuilder Login Required');
|
|
88
|
+
console.error('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
89
|
+
console.error(`Opening browser to login page...`);
|
|
90
|
+
console.error(`If it doesn't open automatically, visit:\n ${loginUrl}`);
|
|
91
|
+
console.error('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const { default: open } = await import('open');
|
|
95
|
+
await open(loginUrl);
|
|
96
|
+
} catch {}
|
|
97
|
+
|
|
98
|
+
// Timeout after 5 minutes
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
server.close();
|
|
101
|
+
reject(new Error('Login timed out after 5 minutes'));
|
|
102
|
+
}, 5 * 60 * 1000);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
server.on('error', reject);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// AgentBuilder API Client
|
|
110
|
+
class AgentBuilderClient {
|
|
111
|
+
private client: AxiosInstance;
|
|
112
|
+
private apiBaseUrl: string;
|
|
113
|
+
private authToken: string | null = null; // DEPRECATED: Use connectionId instead
|
|
114
|
+
private connectionId: string | null = null; // Production OAuth connection ID
|
|
115
|
+
|
|
116
|
+
constructor() {
|
|
117
|
+
// PRODUCTION ONLY: Always use production API
|
|
118
|
+
this.apiBaseUrl = process.env.AGENTBUILDER_API_URL || 'https://agentsapi.chatslytics.com';
|
|
119
|
+
|
|
120
|
+
// Ensure no localhost connections
|
|
121
|
+
if (this.apiBaseUrl.includes('localhost') || this.apiBaseUrl.includes('127.0.0.1')) {
|
|
122
|
+
console.error('ā Localhost connections are disabled. Only production API is allowed.');
|
|
123
|
+
this.apiBaseUrl = 'https://agentsapi.chatslytics.com';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.client = axios.create({
|
|
127
|
+
baseURL: this.apiBaseUrl,
|
|
128
|
+
timeout: 30000,
|
|
129
|
+
headers: {
|
|
130
|
+
'Content-Type': 'application/json',
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Add request interceptor to inject connection ID header
|
|
135
|
+
this.client.interceptors.request.use((config) => {
|
|
136
|
+
if (this.connectionId) {
|
|
137
|
+
// Production mode: Use connection ID
|
|
138
|
+
config.headers['X-MCP-Connection-ID'] = this.connectionId;
|
|
139
|
+
} else if (this.authToken) {
|
|
140
|
+
// Dev/test mode: Use direct auth token (DEPRECATED)
|
|
141
|
+
console.warn('ā ļø Using deprecated auth token. Please use set_connection for production.');
|
|
142
|
+
config.headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
143
|
+
}
|
|
144
|
+
return config;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Auto-authenticate: env var ā stored credentials file
|
|
148
|
+
const envToken = process.env.AGENTBUILDER_AUTH_TOKEN;
|
|
149
|
+
if (envToken) {
|
|
150
|
+
this.authToken = envToken;
|
|
151
|
+
console.error('[MCP] Authenticated from AGENTBUILDER_AUTH_TOKEN env var');
|
|
152
|
+
} else {
|
|
153
|
+
const storedToken = loadStoredToken();
|
|
154
|
+
if (storedToken) {
|
|
155
|
+
this.authToken = storedToken;
|
|
156
|
+
console.error('[MCP] Authenticated from stored credentials');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.error(`[MCP] Configured for PRODUCTION API: ${this.apiBaseUrl}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
isAuthenticated(): boolean {
|
|
164
|
+
return !!(this.connectionId || this.authToken);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Set MCP connection ID (Production OAuth)
|
|
169
|
+
* Obtains connection ID from dashboard after OAuth flow
|
|
170
|
+
*/
|
|
171
|
+
setConnection(connectionId: string) {
|
|
172
|
+
this.connectionId = connectionId;
|
|
173
|
+
console.log(`[MCP] Connected with connection ID: ${connectionId}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get connection ID from backend to validate connection
|
|
178
|
+
*/
|
|
179
|
+
async validateConnection(connectionId: string): Promise<any> {
|
|
180
|
+
const response = await this.client.get(`/api/v1/mcp/connections/${connectionId}`);
|
|
181
|
+
return response.data;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Set authentication token for API requests
|
|
186
|
+
* @deprecated Use setConnection() for production. This method is for dev/test only.
|
|
187
|
+
*/
|
|
188
|
+
setAuthToken(token: string) {
|
|
189
|
+
console.warn('ā ļø setAuthToken is deprecated for production use. Use setConnection() instead.');
|
|
190
|
+
this.authToken = token;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Create a new workflow
|
|
195
|
+
*/
|
|
196
|
+
async createWorkflow(params: {
|
|
197
|
+
name: string;
|
|
198
|
+
description?: string;
|
|
199
|
+
nodes?: any[];
|
|
200
|
+
connections?: any[];
|
|
201
|
+
triggers?: any[];
|
|
202
|
+
max_execution_time?: number;
|
|
203
|
+
retry_attempts?: number;
|
|
204
|
+
}) {
|
|
205
|
+
const payload: any = {
|
|
206
|
+
name: params.name,
|
|
207
|
+
description: params.description || '',
|
|
208
|
+
nodes: params.nodes || [],
|
|
209
|
+
connections: params.connections || [],
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Add optional fields if provided
|
|
213
|
+
if (params.triggers) {
|
|
214
|
+
payload.triggers = params.triggers;
|
|
215
|
+
}
|
|
216
|
+
if (params.max_execution_time !== undefined) {
|
|
217
|
+
payload.max_execution_time = params.max_execution_time;
|
|
218
|
+
}
|
|
219
|
+
if (params.retry_attempts !== undefined) {
|
|
220
|
+
payload.retry_attempts = params.retry_attempts;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const response = await this.client.post('/api/v1/workflows', payload);
|
|
224
|
+
return response.data;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* List workflows
|
|
229
|
+
*/
|
|
230
|
+
async listWorkflows(params?: {
|
|
231
|
+
limit?: number;
|
|
232
|
+
offset?: number;
|
|
233
|
+
status?: string;
|
|
234
|
+
}) {
|
|
235
|
+
const response = await this.client.get('/api/v1/workflows', {
|
|
236
|
+
params: {
|
|
237
|
+
limit: params?.limit || 20,
|
|
238
|
+
offset: params?.offset || 0,
|
|
239
|
+
status: params?.status,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
return response.data;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get workflow by ID
|
|
247
|
+
*/
|
|
248
|
+
async getWorkflow(workflowId: string) {
|
|
249
|
+
const response = await this.client.get(`/api/v1/workflows/${workflowId}`);
|
|
250
|
+
return response.data;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Update workflow
|
|
255
|
+
*/
|
|
256
|
+
async updateWorkflow(workflowId: string, updates: any) {
|
|
257
|
+
const response = await this.client.put(`/api/v1/workflows/${workflowId}`, updates);
|
|
258
|
+
return response.data;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Execute workflow
|
|
263
|
+
*/
|
|
264
|
+
async executeWorkflow(workflowId: string, input?: any) {
|
|
265
|
+
const response = await this.client.post(`/api/v1/workflows/${workflowId}/execute`, {
|
|
266
|
+
input: input || {},
|
|
267
|
+
});
|
|
268
|
+
return response.data;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Get workflow execution status
|
|
273
|
+
*/
|
|
274
|
+
async getExecutionStatus(executionId: string) {
|
|
275
|
+
const response = await this.client.get(`/api/v1/executions/${executionId}`);
|
|
276
|
+
return response.data;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* List workflow executions
|
|
281
|
+
*/
|
|
282
|
+
async listExecutions(workflowId?: string) {
|
|
283
|
+
const params: any = {};
|
|
284
|
+
if (workflowId) {
|
|
285
|
+
params.workflow_id = workflowId;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const response = await this.client.get('/api/v1/executions', { params });
|
|
289
|
+
return response.data;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Delete workflow
|
|
294
|
+
*/
|
|
295
|
+
async deleteWorkflow(workflowId: string) {
|
|
296
|
+
const response = await this.client.delete(`/api/v1/workflows/${workflowId}`);
|
|
297
|
+
return response.data;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Validate workflow
|
|
302
|
+
*/
|
|
303
|
+
async validateWorkflow(workflow: any) {
|
|
304
|
+
const response = await this.client.post('/api/v1/workflows/validate', workflow);
|
|
305
|
+
return response.data;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Check if workflow is ready for deployment
|
|
310
|
+
*/
|
|
311
|
+
async checkDeploymentReadiness(workflowId: string) {
|
|
312
|
+
const response = await this.client.get(
|
|
313
|
+
`/api/v1/workflows/${workflowId}/deployment-readiness`
|
|
314
|
+
);
|
|
315
|
+
return response.data;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Deploy workflow to production
|
|
320
|
+
*/
|
|
321
|
+
async deployWorkflow(workflowId: string, config: {
|
|
322
|
+
instance_name: string;
|
|
323
|
+
price_per_execution?: number;
|
|
324
|
+
}) {
|
|
325
|
+
const response = await this.client.post(
|
|
326
|
+
`/api/v1/seller/workflows/${workflowId}/deploy`,
|
|
327
|
+
{
|
|
328
|
+
display_name: config.instance_name,
|
|
329
|
+
pricing: {
|
|
330
|
+
model: 'pay_per_use',
|
|
331
|
+
price_per_execution: config.price_per_execution || 0.01,
|
|
332
|
+
currency: 'USD'
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
return response.data;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* List workflows created via MCP
|
|
341
|
+
*/
|
|
342
|
+
async listMCPWorkflows(filter?: string) {
|
|
343
|
+
const response = await this.client.get('/api/v1/workflows', {
|
|
344
|
+
params: {
|
|
345
|
+
source_type: 'mcp_server',
|
|
346
|
+
deployment_filter: filter
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
return response.data;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* List agents
|
|
354
|
+
*/
|
|
355
|
+
async listAgents(params?: {
|
|
356
|
+
page?: number;
|
|
357
|
+
page_size?: number;
|
|
358
|
+
search?: string;
|
|
359
|
+
category?: string;
|
|
360
|
+
agent_type?: string;
|
|
361
|
+
pricing_model?: string;
|
|
362
|
+
agent_status?: string;
|
|
363
|
+
creator_id?: string;
|
|
364
|
+
is_featured?: boolean;
|
|
365
|
+
sort_by?: string;
|
|
366
|
+
sort_order?: string;
|
|
367
|
+
}) {
|
|
368
|
+
const response = await this.client.get('/api/v1/agents', {
|
|
369
|
+
params: {
|
|
370
|
+
page: params?.page || 1,
|
|
371
|
+
page_size: params?.page_size || 50,
|
|
372
|
+
search: params?.search,
|
|
373
|
+
category: params?.category,
|
|
374
|
+
agent_type: params?.agent_type,
|
|
375
|
+
pricing_model: params?.pricing_model,
|
|
376
|
+
agent_status: params?.agent_status,
|
|
377
|
+
creator_id: params?.creator_id,
|
|
378
|
+
is_featured: params?.is_featured,
|
|
379
|
+
sort_by: params?.sort_by || 'created_at',
|
|
380
|
+
sort_order: params?.sort_order || 'desc',
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
return response.data;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Get agent by ID
|
|
388
|
+
*/
|
|
389
|
+
async getAgent(agentId: string) {
|
|
390
|
+
const response = await this.client.get(`/api/v1/agents/${agentId}`);
|
|
391
|
+
return response.data;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Execute an agent
|
|
396
|
+
*/
|
|
397
|
+
async executeAgent(agentId: string, input?: any) {
|
|
398
|
+
const response = await this.client.post(`/api/v1/agents/${agentId}/execute`, {
|
|
399
|
+
input: input || {},
|
|
400
|
+
});
|
|
401
|
+
return response.data;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Deploy an agent to Kubernetes
|
|
406
|
+
*/
|
|
407
|
+
async deployAgent(agentId: string, deploymentConfig?: any) {
|
|
408
|
+
const response = await this.client.post(`/api/v1/agents/${agentId}/deploy`, deploymentConfig || {});
|
|
409
|
+
return response.data;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async redeployAgent(agentId: string, body?: { runtime_image?: string }) {
|
|
413
|
+
const response = await this.client.post(`/api/v1/agents/${agentId}/redeploy`, body || {});
|
|
414
|
+
return response.data;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async getMcpPlanLimits() {
|
|
418
|
+
const response = await this.client.get('/api/v1/agents/mcp/plan-limits');
|
|
419
|
+
return response.data;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Create a new agent
|
|
424
|
+
*/
|
|
425
|
+
async createAgent(params: {
|
|
426
|
+
name: string;
|
|
427
|
+
description: string;
|
|
428
|
+
type: string;
|
|
429
|
+
category: string;
|
|
430
|
+
wizard_configuration?: any;
|
|
431
|
+
graph?: any;
|
|
432
|
+
pricing?: any;
|
|
433
|
+
tags?: string[];
|
|
434
|
+
capabilities?: any[];
|
|
435
|
+
icon_url?: string;
|
|
436
|
+
banner_url?: string;
|
|
437
|
+
screenshots?: string[];
|
|
438
|
+
demo_url?: string;
|
|
439
|
+
video_url?: string;
|
|
440
|
+
detailed_description?: string;
|
|
441
|
+
is_public?: boolean;
|
|
442
|
+
}) {
|
|
443
|
+
const response = await this.client.post('/api/v1/agents', params);
|
|
444
|
+
return response.data;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Update an existing agent
|
|
449
|
+
*/
|
|
450
|
+
async updateAgent(agentId: string, updates: any) {
|
|
451
|
+
const response = await this.client.put(`/api/v1/agents/${agentId}`, updates);
|
|
452
|
+
return response.data;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Set wizard configuration for an agent
|
|
457
|
+
*/
|
|
458
|
+
async setAgentWizardConfig(agentId: string, wizardConfiguration: any) {
|
|
459
|
+
const response = await this.client.put(
|
|
460
|
+
`/api/v1/agents/${agentId}/wizard-configuration`,
|
|
461
|
+
wizardConfiguration
|
|
462
|
+
);
|
|
463
|
+
return response.data;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Set LangGraph workflow definition for an agent
|
|
468
|
+
*/
|
|
469
|
+
async setAgentGraph(agentId: string, graph: any) {
|
|
470
|
+
const response = await this.client.put(`/api/v1/agents/${agentId}/graph`, graph);
|
|
471
|
+
return response.data;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Validate agent configuration before publishing
|
|
476
|
+
*/
|
|
477
|
+
async validateAgentConfiguration(agentId: string) {
|
|
478
|
+
const response = await this.client.get(`/api/v1/agents/${agentId}/validate`);
|
|
479
|
+
return response.data;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Publish agent to marketplace
|
|
484
|
+
*/
|
|
485
|
+
async publishAgent(agentId: string) {
|
|
486
|
+
const response = await this.client.post(`/api/v1/agents/${agentId}/publish`, {});
|
|
487
|
+
return response.data;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ==========================================
|
|
491
|
+
// DASHBOARD TOOLS
|
|
492
|
+
// ==========================================
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Create or update agent dashboard configuration
|
|
496
|
+
*/
|
|
497
|
+
async createAgentDashboard(agentId: string, dashboardConfig: {
|
|
498
|
+
template_type?: string;
|
|
499
|
+
layout?: 'minimal' | 'standard' | 'full';
|
|
500
|
+
navigation?: 'sidebar' | 'topbar';
|
|
501
|
+
widgets?: string[];
|
|
502
|
+
tabs?: Array<{ id: string; label: string; icon?: string; widgets: Array<{ type: string; config?: any }> }>;
|
|
503
|
+
theme?: any;
|
|
504
|
+
branding?: any;
|
|
505
|
+
}) {
|
|
506
|
+
const response = await this.client.post(`/api/v1/agents/${agentId}/dashboard/config`, dashboardConfig);
|
|
507
|
+
return response.data;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Get agent dashboard configuration
|
|
512
|
+
*/
|
|
513
|
+
async getAgentDashboardConfig(agentId: string) {
|
|
514
|
+
const response = await this.client.get(`/api/v1/agents/${agentId}/dashboard/config`);
|
|
515
|
+
return response.data;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Update agent dashboard
|
|
520
|
+
*/
|
|
521
|
+
async updateAgentDashboard(agentId: string, updates: any) {
|
|
522
|
+
const response = await this.client.put(`/api/v1/agents/${agentId}/dashboard/config`, updates);
|
|
523
|
+
return response.data;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Add widget to agent dashboard
|
|
528
|
+
*/
|
|
529
|
+
async addDashboardWidget(agentId: string, widgetConfig: {
|
|
530
|
+
widget_type: 'chart' | 'list' | 'stats' | 'custom';
|
|
531
|
+
title: string;
|
|
532
|
+
position?: { x: number; y: number };
|
|
533
|
+
size?: { width: number; height: number };
|
|
534
|
+
config?: any;
|
|
535
|
+
}) {
|
|
536
|
+
const response = await this.client.post(`/api/v1/agents/${agentId}/dashboard/widgets`, widgetConfig);
|
|
537
|
+
return response.data;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Configure agent page (landing, configuration, dashboard)
|
|
542
|
+
*/
|
|
543
|
+
async configureAgentPage(agentId: string, pageConfig: {
|
|
544
|
+
page_type: 'landing' | 'configuration' | 'dashboard';
|
|
545
|
+
content: any;
|
|
546
|
+
}) {
|
|
547
|
+
const response = await this.client.put(`/api/v1/agents/${agentId}/page/${pageConfig.page_type}`, { content: pageConfig.content });
|
|
548
|
+
return response.data;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// ==========================================
|
|
552
|
+
// AGENT COMPOSITION TOOLS
|
|
553
|
+
// ==========================================
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Create a composed/clubbed agent from multiple sub-agents
|
|
557
|
+
*/
|
|
558
|
+
async composeAgents(params: {
|
|
559
|
+
name: string;
|
|
560
|
+
description: string;
|
|
561
|
+
sub_agent_ids: string[];
|
|
562
|
+
composition_type: 'sequential' | 'parallel' | 'router' | 'map_reduce';
|
|
563
|
+
routing_config?: any;
|
|
564
|
+
state_mapping?: any;
|
|
565
|
+
aggregation_strategy?: string;
|
|
566
|
+
}) {
|
|
567
|
+
const response = await this.client.post('/api/v1/agent-composition/create', params);
|
|
568
|
+
return response.data;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* List composed agents owned by the authenticated user
|
|
573
|
+
*/
|
|
574
|
+
async listComposedAgents(params?: { page?: number; limit?: number }) {
|
|
575
|
+
const response = await this.client.get('/api/v1/agent-composition/list', {
|
|
576
|
+
params: {
|
|
577
|
+
page: params?.page || 1,
|
|
578
|
+
limit: params?.limit || 20,
|
|
579
|
+
},
|
|
580
|
+
});
|
|
581
|
+
return response.data;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Get a specific composed agent by ID
|
|
586
|
+
*/
|
|
587
|
+
async getComposedAgent(agentId: string) {
|
|
588
|
+
const response = await this.client.get(`/api/v1/agent-composition/${agentId}`);
|
|
589
|
+
return response.data;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Preview a composition before creating it (shows pricing & execution flow)
|
|
594
|
+
*/
|
|
595
|
+
async previewComposition(params: {
|
|
596
|
+
sub_agent_ids: string[];
|
|
597
|
+
composition_type: 'sequential' | 'parallel' | 'router' | 'map_reduce';
|
|
598
|
+
}) {
|
|
599
|
+
const response = await this.client.post('/api/v1/agent-composition/preview', params);
|
|
600
|
+
return response.data;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Execute a composed agent with input data
|
|
605
|
+
*/
|
|
606
|
+
async executeComposedAgent(agentId: string, inputData?: any) {
|
|
607
|
+
const response = await this.client.post(`/api/v1/agent-composition/execute/${agentId}`, {
|
|
608
|
+
input_data: inputData || {},
|
|
609
|
+
});
|
|
610
|
+
return response.data;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Delete a composed agent
|
|
615
|
+
*/
|
|
616
|
+
async deleteComposedAgent(agentId: string) {
|
|
617
|
+
const response = await this.client.delete(`/api/v1/agent-composition/${agentId}`);
|
|
618
|
+
return response.data;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* List available agent type templates (instagram_dm_sales, whatsapp_business, etc.)
|
|
623
|
+
*/
|
|
624
|
+
async listAgentTypes() {
|
|
625
|
+
const response = await this.client.get('/api/v1/seller/wizard/agent-types');
|
|
626
|
+
return response.data;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Clone an existing agent as a starting point
|
|
631
|
+
*/
|
|
632
|
+
async cloneAgent(agentId: string, newName: string) {
|
|
633
|
+
const response = await this.client.post(`/api/v1/agents/${agentId}/clone`, {
|
|
634
|
+
new_name: newName,
|
|
635
|
+
});
|
|
636
|
+
return response.data;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// ==========================================
|
|
640
|
+
// WIDGET GENERATION
|
|
641
|
+
// ==========================================
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Compile and store a custom React widget (source code provided by caller)
|
|
645
|
+
*/
|
|
646
|
+
async generateWidget(params: {
|
|
647
|
+
agent_id: string;
|
|
648
|
+
widget_name: string;
|
|
649
|
+
source_code: string;
|
|
650
|
+
description?: string;
|
|
651
|
+
}) {
|
|
652
|
+
const response = await this.client.post('/api/v1/widgets/generate', params);
|
|
653
|
+
return response.data;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* List generated widgets for an agent
|
|
658
|
+
*/
|
|
659
|
+
async listGeneratedWidgets(agentId: string) {
|
|
660
|
+
const response = await this.client.get(`/api/v1/widgets/agent/${agentId}`);
|
|
661
|
+
return response.data;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Regenerate a widget with new source code
|
|
666
|
+
*/
|
|
667
|
+
async regenerateWidget(widgetId: string, agentId: string, params: {
|
|
668
|
+
source_code: string;
|
|
669
|
+
description?: string;
|
|
670
|
+
}) {
|
|
671
|
+
const response = await this.client.post(
|
|
672
|
+
`/api/v1/widgets/${widgetId}/regenerate?agent_id=${agentId}`,
|
|
673
|
+
params,
|
|
674
|
+
);
|
|
675
|
+
return response.data;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Delete a generated widget
|
|
680
|
+
*/
|
|
681
|
+
async deleteGeneratedWidget(widgetId: string, agentId: string) {
|
|
682
|
+
const response = await this.client.delete(
|
|
683
|
+
`/api/v1/widgets/${widgetId}?agent_id=${agentId}`,
|
|
684
|
+
);
|
|
685
|
+
return response.data;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// MCP Server
|
|
691
|
+
class AgentBuilderMCPServer {
|
|
692
|
+
private server: Server;
|
|
693
|
+
private client: AgentBuilderClient;
|
|
694
|
+
|
|
695
|
+
constructor() {
|
|
696
|
+
this.server = new Server(
|
|
697
|
+
{
|
|
698
|
+
name: 'agentbuilder-mcp-server',
|
|
699
|
+
version: '0.0.1',
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
capabilities: {
|
|
703
|
+
tools: {},
|
|
704
|
+
},
|
|
705
|
+
}
|
|
706
|
+
);
|
|
707
|
+
|
|
708
|
+
this.client = new AgentBuilderClient();
|
|
709
|
+
this.setupHandlers();
|
|
710
|
+
this.setupErrorHandling();
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
private setupErrorHandling() {
|
|
714
|
+
this.server.onerror = (error) => {
|
|
715
|
+
console.error('[MCP Error]', error);
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
process.on('SIGINT', async () => {
|
|
719
|
+
await this.server.close();
|
|
720
|
+
process.exit(0);
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
private setupHandlers() {
|
|
725
|
+
// List available tools
|
|
726
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
727
|
+
return {
|
|
728
|
+
tools: this.getTools(),
|
|
729
|
+
};
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
// Handle tool calls
|
|
733
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
734
|
+
const { name, arguments: args } = request.params;
|
|
735
|
+
|
|
736
|
+
try {
|
|
737
|
+
switch (name) {
|
|
738
|
+
case 'create_workflow':
|
|
739
|
+
return await this.handleCreateWorkflow(args);
|
|
740
|
+
|
|
741
|
+
case 'list_workflows':
|
|
742
|
+
return await this.handleListWorkflows(args);
|
|
743
|
+
|
|
744
|
+
case 'get_workflow':
|
|
745
|
+
return await this.handleGetWorkflow(args);
|
|
746
|
+
|
|
747
|
+
case 'update_workflow':
|
|
748
|
+
return await this.handleUpdateWorkflow(args);
|
|
749
|
+
|
|
750
|
+
case 'execute_workflow':
|
|
751
|
+
return await this.handleExecuteWorkflow(args);
|
|
752
|
+
|
|
753
|
+
case 'get_execution_status':
|
|
754
|
+
return await this.handleGetExecutionStatus(args);
|
|
755
|
+
|
|
756
|
+
case 'list_executions':
|
|
757
|
+
return await this.handleListExecutions(args);
|
|
758
|
+
|
|
759
|
+
case 'delete_workflow':
|
|
760
|
+
return await this.handleDeleteWorkflow(args);
|
|
761
|
+
|
|
762
|
+
case 'validate_workflow':
|
|
763
|
+
return await this.handleValidateWorkflow(args);
|
|
764
|
+
|
|
765
|
+
case 'check_deployment_readiness':
|
|
766
|
+
return await this.handleCheckDeploymentReadiness(args);
|
|
767
|
+
|
|
768
|
+
case 'deploy_workflow':
|
|
769
|
+
return await this.handleDeployWorkflow(args);
|
|
770
|
+
|
|
771
|
+
case 'list_mcp_workflows':
|
|
772
|
+
return await this.handleListMCPWorkflows(args);
|
|
773
|
+
|
|
774
|
+
case 'list_agents':
|
|
775
|
+
return await this.handleListAgents(args);
|
|
776
|
+
|
|
777
|
+
case 'get_agent':
|
|
778
|
+
return await this.handleGetAgent(args);
|
|
779
|
+
|
|
780
|
+
case 'execute_agent':
|
|
781
|
+
return await this.handleExecuteAgent(args);
|
|
782
|
+
|
|
783
|
+
case 'deploy_agent':
|
|
784
|
+
return await this.handleDeployAgent(args);
|
|
785
|
+
|
|
786
|
+
case 'redeploy_agent':
|
|
787
|
+
return await this.handleRedeployAgent(args);
|
|
788
|
+
|
|
789
|
+
// Agent Management Tools
|
|
790
|
+
case 'create_agent':
|
|
791
|
+
return await this.handleCreateAgent(args);
|
|
792
|
+
|
|
793
|
+
case 'update_agent':
|
|
794
|
+
return await this.handleUpdateAgent(args);
|
|
795
|
+
|
|
796
|
+
case 'set_agent_wizard_config':
|
|
797
|
+
return await this.handleSetAgentWizardConfig(args);
|
|
798
|
+
|
|
799
|
+
case 'set_agent_graph':
|
|
800
|
+
return await this.handleSetAgentGraph(args);
|
|
801
|
+
|
|
802
|
+
case 'validate_agent_configuration':
|
|
803
|
+
return await this.handleValidateAgentConfiguration(args);
|
|
804
|
+
|
|
805
|
+
case 'publish_agent':
|
|
806
|
+
return await this.handlePublishAgent(args);
|
|
807
|
+
|
|
808
|
+
case 'set_auth_token':
|
|
809
|
+
return await this.handleSetAuthToken(args);
|
|
810
|
+
|
|
811
|
+
case 'set_connection':
|
|
812
|
+
return await this.handleSetConnection(args);
|
|
813
|
+
|
|
814
|
+
// Dashboard Tools
|
|
815
|
+
case 'create_agent_dashboard':
|
|
816
|
+
return await this.handleCreateAgentDashboard(args);
|
|
817
|
+
|
|
818
|
+
case 'get_agent_dashboard_config':
|
|
819
|
+
return await this.handleGetAgentDashboardConfig(args);
|
|
820
|
+
|
|
821
|
+
case 'update_agent_dashboard':
|
|
822
|
+
return await this.handleUpdateAgentDashboard(args);
|
|
823
|
+
|
|
824
|
+
case 'add_dashboard_widget':
|
|
825
|
+
return await this.handleAddDashboardWidget(args);
|
|
826
|
+
|
|
827
|
+
case 'configure_agent_page':
|
|
828
|
+
return await this.handleConfigureAgentPage(args);
|
|
829
|
+
|
|
830
|
+
// Widget Generation Tools
|
|
831
|
+
case 'generate_dashboard_widget':
|
|
832
|
+
return await this.handleGenerateDashboardWidget(args);
|
|
833
|
+
|
|
834
|
+
case 'list_generated_widgets':
|
|
835
|
+
return await this.handleListGeneratedWidgets(args);
|
|
836
|
+
|
|
837
|
+
case 'regenerate_widget':
|
|
838
|
+
return await this.handleRegenerateWidget(args);
|
|
839
|
+
|
|
840
|
+
case 'delete_generated_widget':
|
|
841
|
+
return await this.handleDeleteGeneratedWidget(args);
|
|
842
|
+
|
|
843
|
+
// Agent Composition Tools
|
|
844
|
+
case 'compose_agents':
|
|
845
|
+
return await this.handleComposeAgents(args);
|
|
846
|
+
|
|
847
|
+
case 'list_composed_agents':
|
|
848
|
+
return await this.handleListComposedAgents(args);
|
|
849
|
+
|
|
850
|
+
case 'get_composed_agent':
|
|
851
|
+
return await this.handleGetComposedAgent(args);
|
|
852
|
+
|
|
853
|
+
case 'preview_composition':
|
|
854
|
+
return await this.handlePreviewComposition(args);
|
|
855
|
+
|
|
856
|
+
case 'execute_composed_agent':
|
|
857
|
+
return await this.handleExecuteComposedAgent(args);
|
|
858
|
+
|
|
859
|
+
case 'list_agent_types':
|
|
860
|
+
return await this.handleListAgentTypes(args);
|
|
861
|
+
|
|
862
|
+
case 'clone_agent':
|
|
863
|
+
return await this.handleCloneAgent(args);
|
|
864
|
+
|
|
865
|
+
case 'get_mcp_plan_limits':
|
|
866
|
+
return await this.handleGetMcpPlanLimits();
|
|
867
|
+
|
|
868
|
+
default:
|
|
869
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
870
|
+
}
|
|
871
|
+
} catch (error: any) {
|
|
872
|
+
const errorMessage = this.formatErrorMessage(error);
|
|
873
|
+
|
|
874
|
+
return {
|
|
875
|
+
content: [
|
|
876
|
+
{
|
|
877
|
+
type: 'text',
|
|
878
|
+
text: errorMessage,
|
|
879
|
+
},
|
|
880
|
+
],
|
|
881
|
+
isError: true,
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
private getTools(): Tool[] {
|
|
888
|
+
return [
|
|
889
|
+
{
|
|
890
|
+
name: 'set_connection',
|
|
891
|
+
description: 'Connect to AgentBuilder workspace using OAuth connection ID (PRODUCTION). Obtain connection ID from dashboard after OAuth flow.',
|
|
892
|
+
inputSchema: {
|
|
893
|
+
type: 'object',
|
|
894
|
+
properties: {
|
|
895
|
+
connection_id: {
|
|
896
|
+
type: 'string',
|
|
897
|
+
description: 'MCP connection ID from dashboard (format: mcp_conn_xxx)',
|
|
898
|
+
pattern: '^mcp_conn_[a-zA-Z0-9_-]+$',
|
|
899
|
+
},
|
|
900
|
+
},
|
|
901
|
+
required: ['connection_id'],
|
|
902
|
+
},
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
name: 'set_auth_token',
|
|
906
|
+
description: 'DEPRECATED: Set authentication token for dev/test only. Use set_connection for production.',
|
|
907
|
+
inputSchema: {
|
|
908
|
+
type: 'object',
|
|
909
|
+
properties: {
|
|
910
|
+
token: {
|
|
911
|
+
type: 'string',
|
|
912
|
+
description: 'JWT authentication token from Supabase (dev/test only)',
|
|
913
|
+
},
|
|
914
|
+
},
|
|
915
|
+
required: ['token'],
|
|
916
|
+
},
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
name: 'create_workflow',
|
|
920
|
+
description: 'Create a new custom workflow with nodes and edges. NOTE: This creates a new workflow from scratch. To use pre-built agent templates, use list_agents and execute_agent instead.',
|
|
921
|
+
inputSchema: {
|
|
922
|
+
type: 'object',
|
|
923
|
+
properties: {
|
|
924
|
+
name: {
|
|
925
|
+
type: 'string',
|
|
926
|
+
description: 'Name of the workflow',
|
|
927
|
+
},
|
|
928
|
+
description: {
|
|
929
|
+
type: 'string',
|
|
930
|
+
description: 'Description of what the workflow does',
|
|
931
|
+
},
|
|
932
|
+
nodes: {
|
|
933
|
+
type: 'array',
|
|
934
|
+
description: 'Array of workflow nodes',
|
|
935
|
+
items: {
|
|
936
|
+
type: 'object',
|
|
937
|
+
properties: {
|
|
938
|
+
id: {
|
|
939
|
+
type: 'string',
|
|
940
|
+
description: 'Unique node identifier'
|
|
941
|
+
},
|
|
942
|
+
type: {
|
|
943
|
+
type: 'string',
|
|
944
|
+
description: 'Node type (e.g., start, openai, telegram, slack)'
|
|
945
|
+
},
|
|
946
|
+
title: {
|
|
947
|
+
type: 'string',
|
|
948
|
+
description: 'Display title for the node'
|
|
949
|
+
},
|
|
950
|
+
description: {
|
|
951
|
+
type: 'string',
|
|
952
|
+
description: 'Optional node description'
|
|
953
|
+
},
|
|
954
|
+
position: {
|
|
955
|
+
type: 'object',
|
|
956
|
+
description: 'UI position coordinates',
|
|
957
|
+
properties: {
|
|
958
|
+
x: { type: 'number' },
|
|
959
|
+
y: { type: 'number' }
|
|
960
|
+
}
|
|
961
|
+
},
|
|
962
|
+
config: {
|
|
963
|
+
type: 'object',
|
|
964
|
+
description: 'Node-specific configuration'
|
|
965
|
+
}
|
|
966
|
+
},
|
|
967
|
+
required: ['id', 'type', 'title']
|
|
968
|
+
},
|
|
969
|
+
},
|
|
970
|
+
connections: {
|
|
971
|
+
type: 'array',
|
|
972
|
+
description: 'Array of connections between nodes',
|
|
973
|
+
items: {
|
|
974
|
+
type: 'object',
|
|
975
|
+
properties: {
|
|
976
|
+
id: {
|
|
977
|
+
type: 'string',
|
|
978
|
+
description: 'Unique connection identifier'
|
|
979
|
+
},
|
|
980
|
+
source: {
|
|
981
|
+
type: 'string',
|
|
982
|
+
description: 'Source node ID'
|
|
983
|
+
},
|
|
984
|
+
target: {
|
|
985
|
+
type: 'string',
|
|
986
|
+
description: 'Target node ID'
|
|
987
|
+
},
|
|
988
|
+
source_port: {
|
|
989
|
+
type: 'string',
|
|
990
|
+
description: 'Source output port (default: "output")'
|
|
991
|
+
},
|
|
992
|
+
target_port: {
|
|
993
|
+
type: 'string',
|
|
994
|
+
description: 'Target input port (default: "input")'
|
|
995
|
+
}
|
|
996
|
+
},
|
|
997
|
+
required: ['id', 'source', 'target']
|
|
998
|
+
},
|
|
999
|
+
},
|
|
1000
|
+
triggers: {
|
|
1001
|
+
type: 'array',
|
|
1002
|
+
description: 'Optional workflow triggers (schedule, webhook, etc.)'
|
|
1003
|
+
},
|
|
1004
|
+
max_execution_time: {
|
|
1005
|
+
type: 'number',
|
|
1006
|
+
description: 'Maximum execution time in seconds (default: 3600)'
|
|
1007
|
+
},
|
|
1008
|
+
retry_attempts: {
|
|
1009
|
+
type: 'number',
|
|
1010
|
+
description: 'Number of retry attempts on failure (default: 3)'
|
|
1011
|
+
}
|
|
1012
|
+
},
|
|
1013
|
+
required: ['name'],
|
|
1014
|
+
},
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
name: 'list_workflows',
|
|
1018
|
+
description: 'List custom workflows created by the authenticated user. NOTE: For pre-built agent templates (like Instagram DM Sales Agent), use list_agents instead.',
|
|
1019
|
+
inputSchema: {
|
|
1020
|
+
type: 'object',
|
|
1021
|
+
properties: {
|
|
1022
|
+
limit: {
|
|
1023
|
+
type: 'number',
|
|
1024
|
+
description: 'Number of workflows to return (default: 20)',
|
|
1025
|
+
},
|
|
1026
|
+
offset: {
|
|
1027
|
+
type: 'number',
|
|
1028
|
+
description: 'Pagination offset (default: 0)',
|
|
1029
|
+
},
|
|
1030
|
+
status: {
|
|
1031
|
+
type: 'string',
|
|
1032
|
+
description: 'Filter by status (draft, active, archived)',
|
|
1033
|
+
},
|
|
1034
|
+
},
|
|
1035
|
+
},
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
name: 'get_workflow',
|
|
1039
|
+
description: 'Get details of a specific workflow by ID',
|
|
1040
|
+
inputSchema: {
|
|
1041
|
+
type: 'object',
|
|
1042
|
+
properties: {
|
|
1043
|
+
workflow_id: {
|
|
1044
|
+
type: 'string',
|
|
1045
|
+
description: 'Workflow ID',
|
|
1046
|
+
},
|
|
1047
|
+
},
|
|
1048
|
+
required: ['workflow_id'],
|
|
1049
|
+
},
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
name: 'update_workflow',
|
|
1053
|
+
description: 'Update an existing workflow',
|
|
1054
|
+
inputSchema: {
|
|
1055
|
+
type: 'object',
|
|
1056
|
+
properties: {
|
|
1057
|
+
workflow_id: {
|
|
1058
|
+
type: 'string',
|
|
1059
|
+
description: 'Workflow ID',
|
|
1060
|
+
},
|
|
1061
|
+
updates: {
|
|
1062
|
+
type: 'object',
|
|
1063
|
+
description: 'Fields to update (name, description, nodes, edges, status, etc.)',
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
required: ['workflow_id', 'updates'],
|
|
1067
|
+
},
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
name: 'execute_workflow',
|
|
1071
|
+
description: 'Execute a workflow with optional input data',
|
|
1072
|
+
inputSchema: {
|
|
1073
|
+
type: 'object',
|
|
1074
|
+
properties: {
|
|
1075
|
+
workflow_id: {
|
|
1076
|
+
type: 'string',
|
|
1077
|
+
description: 'Workflow ID',
|
|
1078
|
+
},
|
|
1079
|
+
input: {
|
|
1080
|
+
type: 'object',
|
|
1081
|
+
description: 'Input data for the workflow execution',
|
|
1082
|
+
},
|
|
1083
|
+
},
|
|
1084
|
+
required: ['workflow_id'],
|
|
1085
|
+
},
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
name: 'get_execution_status',
|
|
1089
|
+
description: 'Get the status and results of a workflow execution',
|
|
1090
|
+
inputSchema: {
|
|
1091
|
+
type: 'object',
|
|
1092
|
+
properties: {
|
|
1093
|
+
execution_id: {
|
|
1094
|
+
type: 'string',
|
|
1095
|
+
description: 'Execution ID',
|
|
1096
|
+
},
|
|
1097
|
+
},
|
|
1098
|
+
required: ['execution_id'],
|
|
1099
|
+
},
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
name: 'list_executions',
|
|
1103
|
+
description: 'List workflow executions, optionally filtered by workflow ID',
|
|
1104
|
+
inputSchema: {
|
|
1105
|
+
type: 'object',
|
|
1106
|
+
properties: {
|
|
1107
|
+
workflow_id: {
|
|
1108
|
+
type: 'string',
|
|
1109
|
+
description: 'Filter executions by workflow ID (optional)',
|
|
1110
|
+
},
|
|
1111
|
+
},
|
|
1112
|
+
},
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
name: 'delete_workflow',
|
|
1116
|
+
description: 'Delete a workflow by ID',
|
|
1117
|
+
inputSchema: {
|
|
1118
|
+
type: 'object',
|
|
1119
|
+
properties: {
|
|
1120
|
+
workflow_id: {
|
|
1121
|
+
type: 'string',
|
|
1122
|
+
description: 'Workflow ID to delete',
|
|
1123
|
+
},
|
|
1124
|
+
},
|
|
1125
|
+
required: ['workflow_id'],
|
|
1126
|
+
},
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
name: 'validate_workflow',
|
|
1130
|
+
description: 'Validate a workflow structure before creating/updating',
|
|
1131
|
+
inputSchema: {
|
|
1132
|
+
type: 'object',
|
|
1133
|
+
properties: {
|
|
1134
|
+
workflow: {
|
|
1135
|
+
type: 'object',
|
|
1136
|
+
description: 'Workflow object to validate',
|
|
1137
|
+
},
|
|
1138
|
+
},
|
|
1139
|
+
required: ['workflow'],
|
|
1140
|
+
},
|
|
1141
|
+
},
|
|
1142
|
+
{
|
|
1143
|
+
name: 'check_deployment_readiness',
|
|
1144
|
+
description: 'Check if workflow is ready for deployment and see required credentials',
|
|
1145
|
+
inputSchema: {
|
|
1146
|
+
type: 'object',
|
|
1147
|
+
properties: {
|
|
1148
|
+
workflow_id: {
|
|
1149
|
+
type: 'string',
|
|
1150
|
+
description: 'Workflow ID to check for deployment readiness',
|
|
1151
|
+
},
|
|
1152
|
+
},
|
|
1153
|
+
required: ['workflow_id'],
|
|
1154
|
+
},
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
name: 'deploy_workflow',
|
|
1158
|
+
description: 'Deploy workflow as a production instance on Kubernetes',
|
|
1159
|
+
inputSchema: {
|
|
1160
|
+
type: 'object',
|
|
1161
|
+
properties: {
|
|
1162
|
+
workflow_id: {
|
|
1163
|
+
type: 'string',
|
|
1164
|
+
description: 'Workflow ID to deploy',
|
|
1165
|
+
},
|
|
1166
|
+
instance_name: {
|
|
1167
|
+
type: 'string',
|
|
1168
|
+
description: 'Display name for the deployed instance',
|
|
1169
|
+
},
|
|
1170
|
+
price_per_execution: {
|
|
1171
|
+
type: 'number',
|
|
1172
|
+
description: 'Price to charge per execution in USD (default: 0.01)',
|
|
1173
|
+
},
|
|
1174
|
+
},
|
|
1175
|
+
required: ['workflow_id', 'instance_name'],
|
|
1176
|
+
},
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
name: 'list_mcp_workflows',
|
|
1180
|
+
description: 'List workflows created via MCP with deployment status',
|
|
1181
|
+
inputSchema: {
|
|
1182
|
+
type: 'object',
|
|
1183
|
+
properties: {
|
|
1184
|
+
filter: {
|
|
1185
|
+
type: 'string',
|
|
1186
|
+
enum: ['all', 'deployable', 'pending', 'failed'],
|
|
1187
|
+
description: 'Filter by deployment status',
|
|
1188
|
+
},
|
|
1189
|
+
},
|
|
1190
|
+
},
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
name: 'list_agents',
|
|
1194
|
+
description: 'List all agents with filtering and pagination. Agents are pre-built workflow templates (e.g., Instagram DM Sales Agent, Patient Follow-up WhatsApp Agent) that can be deployed and executed.',
|
|
1195
|
+
inputSchema: {
|
|
1196
|
+
type: 'object',
|
|
1197
|
+
properties: {
|
|
1198
|
+
page: {
|
|
1199
|
+
type: 'number',
|
|
1200
|
+
description: 'Page number (default: 1)',
|
|
1201
|
+
},
|
|
1202
|
+
page_size: {
|
|
1203
|
+
type: 'number',
|
|
1204
|
+
description: 'Number of agents per page (default: 50)',
|
|
1205
|
+
},
|
|
1206
|
+
search: {
|
|
1207
|
+
type: 'string',
|
|
1208
|
+
description: 'Search query for agent name or description',
|
|
1209
|
+
},
|
|
1210
|
+
category: {
|
|
1211
|
+
type: 'string',
|
|
1212
|
+
description: 'Filter by category (e.g., healthcare, sales, customer-support)',
|
|
1213
|
+
},
|
|
1214
|
+
agent_type: {
|
|
1215
|
+
type: 'string',
|
|
1216
|
+
description: 'Filter by agent type (workflow, chatbot, api)',
|
|
1217
|
+
},
|
|
1218
|
+
pricing_model: {
|
|
1219
|
+
type: 'string',
|
|
1220
|
+
description: 'Filter by pricing model (free, subscription, pay_per_use)',
|
|
1221
|
+
},
|
|
1222
|
+
agent_status: {
|
|
1223
|
+
type: 'string',
|
|
1224
|
+
description: 'Filter by status (active, draft, archived)',
|
|
1225
|
+
},
|
|
1226
|
+
creator_id: {
|
|
1227
|
+
type: 'string',
|
|
1228
|
+
description: 'Filter by creator user ID',
|
|
1229
|
+
},
|
|
1230
|
+
is_featured: {
|
|
1231
|
+
type: 'boolean',
|
|
1232
|
+
description: 'Filter featured agents only',
|
|
1233
|
+
},
|
|
1234
|
+
sort_by: {
|
|
1235
|
+
type: 'string',
|
|
1236
|
+
description: 'Sort by field (name, created_at, updated_at, metrics.views, metrics.average_rating)',
|
|
1237
|
+
},
|
|
1238
|
+
sort_order: {
|
|
1239
|
+
type: 'string',
|
|
1240
|
+
description: 'Sort order (asc, desc)',
|
|
1241
|
+
},
|
|
1242
|
+
},
|
|
1243
|
+
},
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
name: 'get_agent',
|
|
1247
|
+
description: 'Get detailed information about a specific agent by ID',
|
|
1248
|
+
inputSchema: {
|
|
1249
|
+
type: 'object',
|
|
1250
|
+
properties: {
|
|
1251
|
+
agent_id: {
|
|
1252
|
+
type: 'string',
|
|
1253
|
+
description: 'Agent ID',
|
|
1254
|
+
},
|
|
1255
|
+
},
|
|
1256
|
+
required: ['agent_id'],
|
|
1257
|
+
},
|
|
1258
|
+
},
|
|
1259
|
+
{
|
|
1260
|
+
name: 'execute_agent',
|
|
1261
|
+
description: 'Execute an agent with input data',
|
|
1262
|
+
inputSchema: {
|
|
1263
|
+
type: 'object',
|
|
1264
|
+
properties: {
|
|
1265
|
+
agent_id: {
|
|
1266
|
+
type: 'string',
|
|
1267
|
+
description: 'Agent ID to execute',
|
|
1268
|
+
},
|
|
1269
|
+
input: {
|
|
1270
|
+
type: 'object',
|
|
1271
|
+
description: 'Input data for the agent execution',
|
|
1272
|
+
},
|
|
1273
|
+
},
|
|
1274
|
+
required: ['agent_id'],
|
|
1275
|
+
},
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
name: 'deploy_agent',
|
|
1279
|
+
description: 'Deploy an agent to Kubernetes production environment. Creates an isolated pod per subscription with agent runtime + dashboard sidecar. Dashboard is accessible at https://{subscription_id}.agents.chatslytics.com. Deployment uses the agent graph and dashboard config created during setup.\n\nARCHITECTURE NOTES:\n- Each agent uses its OWN dedicated Facebook/Meta App ā never share apps across agents. This gives each agent its own webhook callback URL, verify token, and app credentials, providing full isolation.\n- Webhook URL pattern per agent: https://{subscription_id}.agents.chatslytics.com/webhook/{provider} ā registered directly in that agent\'s Meta App settings. The platform is NOT in the webhook path for new agents.\n- AGENT_GRAPH_CONFIG (LangGraph nodes/edges) is baked as a Kubernetes env var at deploy time. Changing graph topology (adding nodes, modifying LLM prompts in graph) requires a redeploy. Credentials and user configuration are managed in platform MongoDB (user_subscriptions.user_configuration) with platform-side Fernet AES encryption ā the pod reads these live at runtime via _enrich_context_from_db() so config changes from the platform UI take effect without pod restart. Use RuntimeConfig (runtime_config_{sub_id}, pod-owned collection) only for agent-operational state such as onboarding completion status.\n- All agent storage is namespaced by subscription_id (e.g. invoices_{sub_id}, messages_{sub_id}) ā one agent\'s data is never accessible to another.\n- Pod reports lifecycle to platform via PlatformClient: /api/v1/runtime/ready, /api/v1/runtime/stopped, /api/v1/runtime/metrics. These are fire-and-forget; platform downtime does not affect the agent.',
|
|
1280
|
+
inputSchema: {
|
|
1281
|
+
type: 'object',
|
|
1282
|
+
properties: {
|
|
1283
|
+
agent_id: {
|
|
1284
|
+
type: 'string',
|
|
1285
|
+
description: 'Agent ID to deploy',
|
|
1286
|
+
},
|
|
1287
|
+
deployment_config: {
|
|
1288
|
+
type: 'object',
|
|
1289
|
+
description: 'Optional deployment configuration',
|
|
1290
|
+
properties: {
|
|
1291
|
+
replicas: {
|
|
1292
|
+
type: 'number',
|
|
1293
|
+
description: 'Number of pod replicas (default: 1)',
|
|
1294
|
+
},
|
|
1295
|
+
cpu_request: {
|
|
1296
|
+
type: 'string',
|
|
1297
|
+
description: 'CPU request per pod (default: "100m")',
|
|
1298
|
+
},
|
|
1299
|
+
cpu_limit: {
|
|
1300
|
+
type: 'string',
|
|
1301
|
+
description: 'CPU limit per pod (default: "500m")',
|
|
1302
|
+
},
|
|
1303
|
+
memory_request: {
|
|
1304
|
+
type: 'string',
|
|
1305
|
+
description: 'Memory request per pod (default: "128Mi")',
|
|
1306
|
+
},
|
|
1307
|
+
memory_limit: {
|
|
1308
|
+
type: 'string',
|
|
1309
|
+
description: 'Memory limit per pod (default: "512Mi")',
|
|
1310
|
+
},
|
|
1311
|
+
image: {
|
|
1312
|
+
type: 'string',
|
|
1313
|
+
description: 'Container image override. If omitted, the agent\'s runtime_image field in MongoDB is used automatically (e.g. acragentbuilder.azurecr.io/agent-runtime:ca-invoice-v1.0.0). Falls back to agent-runtime:latest only if no runtime_image is set on the agent doc.',
|
|
1314
|
+
},
|
|
1315
|
+
},
|
|
1316
|
+
},
|
|
1317
|
+
},
|
|
1318
|
+
required: ['agent_id'],
|
|
1319
|
+
},
|
|
1320
|
+
},
|
|
1321
|
+
{
|
|
1322
|
+
name: 'redeploy_agent',
|
|
1323
|
+
description: 'Redeploy a running agent pod with the latest graph and/or image. Use this after set_agent_graph (graph changes require pod restart) or after pushing a new container image to ACR. Preserves all pod data (conversations, vectors, checkpoints). Triggers rolling restart ā ~60s downtime.\n\nWORKFLOW:\n1. Make code changes in agent repo ā build + push new image to ACR\n2. Call set_agent_graph if graph/node configs changed\n3. Call redeploy_agent ā picks up latest graph from MongoDB + new image\n\nNOTE: Credential/config changes (via platform wizard UI) do NOT need redeploy ā the pod reads them live from DB on every webhook.',
|
|
1324
|
+
inputSchema: {
|
|
1325
|
+
type: 'object',
|
|
1326
|
+
properties: {
|
|
1327
|
+
agent_id: {
|
|
1328
|
+
type: 'string',
|
|
1329
|
+
description: 'Agent ID to redeploy',
|
|
1330
|
+
},
|
|
1331
|
+
runtime_image: {
|
|
1332
|
+
type: 'string',
|
|
1333
|
+
description: 'New container image to deploy (e.g. acragentbuilder.azurecr.io/instagram-dm-agent:v4.12.0). Omit to keep current image and only update the graph.',
|
|
1334
|
+
},
|
|
1335
|
+
},
|
|
1336
|
+
required: ['agent_id'],
|
|
1337
|
+
},
|
|
1338
|
+
},
|
|
1339
|
+
// ==========================================
|
|
1340
|
+
// AGENT MANAGEMENT TOOLS
|
|
1341
|
+
// ==========================================
|
|
1342
|
+
{
|
|
1343
|
+
name: 'create_agent',
|
|
1344
|
+
description: 'Create a new agent with complete configuration. Requires seller role. Agent will be created in draft status.\n\nAGENT ISOLATION MODEL:\n- Each agent is a fully isolated unit: its own K8s pod, its own Facebook/Meta App (for webhook providers), its own MongoDB namespace (collections prefixed with subscription_id), and its own dashboard URL.\n- Never reuse an existing agent\'s Facebook App or webhook credentials for a new agent. Create a fresh Meta App per agent type.\n- Full creation flow: create_agent ā set_agent_wizard_config ā set_agent_graph ā create_agent_dashboard ā validate_agent_configuration ā publish_agent ā deploy_agent.\n- After deploy_agent, the operator manually registers the webhook callback URL (https://{subscription_id}.agents.chatslytics.com/webhook/{provider}) in the new agent\'s Meta App settings.\n\nSHARED PLATFORM SERVICES (do not replicate in agent logic):\n- AUTH: Agent pods call AUTH_SERVICE_URL/verify for dashboard JWT validation. Requests forwarded by the platform carry X-Platform-Proxy: true and should be trusted without re-verification.\n- PAYMENT: All billing, Razorpay subscriptions, and payment lifecycle are handled by the platform (razorpay_service.py, seller_instance_billing_service.py). Agent pods never touch payment logic ā they just run if SUBSCRIPTION_ID is present.\n- SUBSCRIPTION + CONFIGURATION: The platform\'s user_subscriptions.user_configuration is the source of truth for all credentials and wizard-collected config. The platform handles Fernet AES encryption/decryption. Agent pods read this via _enrich_context_from_db() at startup and per-webhook ā this is correct and intentional, not a coupling violation. Config changes made via platform UI propagate to the pod without restart. Do NOT duplicate credentials into pod-owned storage. Use RuntimeConfig (runtime_config_{sub_id}, pod-owned collection) only for agent-operational state like onboarding completion flags.',
|
|
1345
|
+
inputSchema: {
|
|
1346
|
+
type: 'object',
|
|
1347
|
+
properties: {
|
|
1348
|
+
name: {
|
|
1349
|
+
type: 'string',
|
|
1350
|
+
description: 'Agent name (1-100 characters)',
|
|
1351
|
+
},
|
|
1352
|
+
description: {
|
|
1353
|
+
type: 'string',
|
|
1354
|
+
description: 'Short description (max 1000 characters)',
|
|
1355
|
+
},
|
|
1356
|
+
detailed_description: {
|
|
1357
|
+
type: 'string',
|
|
1358
|
+
description: 'Detailed description (max 5000 characters)',
|
|
1359
|
+
},
|
|
1360
|
+
type: {
|
|
1361
|
+
type: 'string',
|
|
1362
|
+
enum: ['conversational', 'workflow', 'analytical', 'creative', 'research', 'automation'],
|
|
1363
|
+
description: 'Agent type',
|
|
1364
|
+
},
|
|
1365
|
+
category: {
|
|
1366
|
+
type: 'string',
|
|
1367
|
+
description: 'Agent category (e.g., Sales, Customer Support, Analytics)',
|
|
1368
|
+
},
|
|
1369
|
+
tags: {
|
|
1370
|
+
type: 'array',
|
|
1371
|
+
items: { type: 'string' },
|
|
1372
|
+
description: 'Tags for discoverability (max 20)',
|
|
1373
|
+
},
|
|
1374
|
+
wizard_configuration: {
|
|
1375
|
+
type: 'object',
|
|
1376
|
+
description: 'Configuration wizard schema for agent onboarding',
|
|
1377
|
+
},
|
|
1378
|
+
graph: {
|
|
1379
|
+
type: 'object',
|
|
1380
|
+
description: 'LangGraph workflow definition with nodes and edges',
|
|
1381
|
+
},
|
|
1382
|
+
pricing: {
|
|
1383
|
+
type: 'object',
|
|
1384
|
+
description: 'Pricing configuration',
|
|
1385
|
+
},
|
|
1386
|
+
icon_url: {
|
|
1387
|
+
type: 'string',
|
|
1388
|
+
description: 'Icon URL',
|
|
1389
|
+
},
|
|
1390
|
+
banner_url: {
|
|
1391
|
+
type: 'string',
|
|
1392
|
+
description: 'Banner URL',
|
|
1393
|
+
},
|
|
1394
|
+
screenshots: {
|
|
1395
|
+
type: 'array',
|
|
1396
|
+
items: { type: 'string' },
|
|
1397
|
+
description: 'Screenshot URLs (max 10)',
|
|
1398
|
+
},
|
|
1399
|
+
demo_url: {
|
|
1400
|
+
type: 'string',
|
|
1401
|
+
description: 'Demo video URL',
|
|
1402
|
+
},
|
|
1403
|
+
video_url: {
|
|
1404
|
+
type: 'string',
|
|
1405
|
+
description: 'Promotional video URL',
|
|
1406
|
+
},
|
|
1407
|
+
is_public: {
|
|
1408
|
+
type: 'boolean',
|
|
1409
|
+
description: 'Whether agent is publicly visible (default: false)',
|
|
1410
|
+
},
|
|
1411
|
+
runtime_image: {
|
|
1412
|
+
type: 'string',
|
|
1413
|
+
description: 'Container image for this agent\'s runtime pods (e.g. acragentbuilder.azurecr.io/instagram-dm-agent:latest). Each agent type should have its own dedicated image built from its own repo. Omit to fall back to the generic agent-runtime:latest.',
|
|
1414
|
+
},
|
|
1415
|
+
},
|
|
1416
|
+
required: ['name', 'description', 'type', 'category'],
|
|
1417
|
+
},
|
|
1418
|
+
},
|
|
1419
|
+
{
|
|
1420
|
+
name: 'update_agent',
|
|
1421
|
+
description: 'Update an existing agent. Requires agent ownership or admin role.',
|
|
1422
|
+
inputSchema: {
|
|
1423
|
+
type: 'object',
|
|
1424
|
+
properties: {
|
|
1425
|
+
agent_id: {
|
|
1426
|
+
type: 'string',
|
|
1427
|
+
description: 'Agent ID',
|
|
1428
|
+
},
|
|
1429
|
+
updates: {
|
|
1430
|
+
type: 'object',
|
|
1431
|
+
description: 'Fields to update (partial update)',
|
|
1432
|
+
},
|
|
1433
|
+
},
|
|
1434
|
+
required: ['agent_id', 'updates'],
|
|
1435
|
+
},
|
|
1436
|
+
},
|
|
1437
|
+
{
|
|
1438
|
+
name: 'set_agent_wizard_config',
|
|
1439
|
+
description: 'Set or update wizard configuration for agent onboarding. Defines OAuth providers, credentials, platform config, and custom wizard steps.\n\nNOTE: For webhook-based agents (WhatsApp, Instagram, Telegram), the wizard should collect the provider app credentials specific to THIS agent\'s dedicated Meta/provider App ā not shared platform credentials. Each agent has its own app, so WHATSAPP_PHONE_NUMBER_ID, WHATSAPP_ACCESS_TOKEN, app secret etc. are all per-agent. These get injected into the pod as env vars at deploy time and are also accessible via RuntimeConfig for hot updates.',
|
|
1440
|
+
inputSchema: {
|
|
1441
|
+
type: 'object',
|
|
1442
|
+
properties: {
|
|
1443
|
+
agent_id: {
|
|
1444
|
+
type: 'string',
|
|
1445
|
+
description: 'Agent ID',
|
|
1446
|
+
},
|
|
1447
|
+
wizard_configuration: {
|
|
1448
|
+
type: 'object',
|
|
1449
|
+
description: 'Complete wizard configuration',
|
|
1450
|
+
properties: {
|
|
1451
|
+
required_oauth_providers: {
|
|
1452
|
+
type: 'array',
|
|
1453
|
+
items: { type: 'string' },
|
|
1454
|
+
description: 'Required OAuth providers (e.g., facebook, instagram, whatsapp)',
|
|
1455
|
+
},
|
|
1456
|
+
required_credentials: {
|
|
1457
|
+
type: 'array',
|
|
1458
|
+
description: 'Required API credentials with field definitions',
|
|
1459
|
+
},
|
|
1460
|
+
required_platform_config: {
|
|
1461
|
+
type: 'array',
|
|
1462
|
+
description: 'Platform-specific configuration fields',
|
|
1463
|
+
},
|
|
1464
|
+
custom_wizard_steps: {
|
|
1465
|
+
type: 'array',
|
|
1466
|
+
description: 'Agent-specific custom wizard steps',
|
|
1467
|
+
},
|
|
1468
|
+
configuration_defaults: {
|
|
1469
|
+
type: 'object',
|
|
1470
|
+
description: 'Default values for optional fields',
|
|
1471
|
+
},
|
|
1472
|
+
},
|
|
1473
|
+
},
|
|
1474
|
+
},
|
|
1475
|
+
required: ['agent_id', 'wizard_configuration'],
|
|
1476
|
+
},
|
|
1477
|
+
},
|
|
1478
|
+
{
|
|
1479
|
+
name: 'set_agent_graph',
|
|
1480
|
+
description: 'Set LangGraph workflow definition for agent. Defines nodes, edges, and entry point for agent execution.\n\nARCHITECTURE NOTES:\n- The graph JSON is injected as AGENT_GRAPH_CONFIG env var into the pod at deploy time. Topology changes require redeploy.\n- Node configs support {user_config.field_name} template variables resolved at runtime from the subscriber\'s user_configuration in MongoDB ā use this for per-user LLM models, thresholds, etc.\n- The entry point node receives the raw webhook payload from the provider (WhatsApp, Instagram, Telegram, etc.).\n- Each new agent should have its own provider app (e.g. its own Facebook App for WhatsApp/Instagram). The webhook entry point in the graph receives events directly from that app\'s callback URL ā no platform proxy involved for new agents.\n- Use node type "whatsapp_media_downloader", "gst_invoice_extractor", "phone_client_resolver", "invoice_storage", "whatsapp_confirmation_sender" for CA invoice flow. For other agents, define custom node types registered in the node handler registry.',
|
|
1481
|
+
inputSchema: {
|
|
1482
|
+
type: 'object',
|
|
1483
|
+
properties: {
|
|
1484
|
+
agent_id: {
|
|
1485
|
+
type: 'string',
|
|
1486
|
+
description: 'Agent ID',
|
|
1487
|
+
},
|
|
1488
|
+
graph: {
|
|
1489
|
+
type: 'object',
|
|
1490
|
+
description: 'LangGraph workflow definition',
|
|
1491
|
+
properties: {
|
|
1492
|
+
nodes: {
|
|
1493
|
+
type: 'array',
|
|
1494
|
+
description: 'Array of workflow nodes',
|
|
1495
|
+
items: {
|
|
1496
|
+
type: 'object',
|
|
1497
|
+
properties: {
|
|
1498
|
+
id: { type: 'string', description: 'Unique node identifier' },
|
|
1499
|
+
type: { type: 'string', description: 'Node type (webhook, llm_classifier, vector_search, etc.)' },
|
|
1500
|
+
config: { type: 'object', description: 'Node-specific configuration' },
|
|
1501
|
+
},
|
|
1502
|
+
required: ['id', 'type'],
|
|
1503
|
+
},
|
|
1504
|
+
},
|
|
1505
|
+
edges: {
|
|
1506
|
+
type: 'array',
|
|
1507
|
+
description: 'Array of connections between nodes',
|
|
1508
|
+
items: {
|
|
1509
|
+
type: 'object',
|
|
1510
|
+
properties: {
|
|
1511
|
+
from: { type: 'string', description: 'Source node ID' },
|
|
1512
|
+
to: { type: 'string', description: 'Target node ID' },
|
|
1513
|
+
condition: { type: 'string', description: 'Optional condition for edge' },
|
|
1514
|
+
},
|
|
1515
|
+
required: ['from', 'to'],
|
|
1516
|
+
},
|
|
1517
|
+
},
|
|
1518
|
+
entry_point: {
|
|
1519
|
+
type: 'string',
|
|
1520
|
+
description: 'Entry point node ID',
|
|
1521
|
+
},
|
|
1522
|
+
},
|
|
1523
|
+
required: ['nodes', 'edges', 'entry_point'],
|
|
1524
|
+
},
|
|
1525
|
+
},
|
|
1526
|
+
required: ['agent_id', 'graph'],
|
|
1527
|
+
},
|
|
1528
|
+
},
|
|
1529
|
+
{
|
|
1530
|
+
name: 'validate_agent_configuration',
|
|
1531
|
+
description: 'Validate agent before publishing. Checks wizard configuration, agent graph, pricing, and metadata completeness.',
|
|
1532
|
+
inputSchema: {
|
|
1533
|
+
type: 'object',
|
|
1534
|
+
properties: {
|
|
1535
|
+
agent_id: {
|
|
1536
|
+
type: 'string',
|
|
1537
|
+
description: 'Agent ID to validate',
|
|
1538
|
+
},
|
|
1539
|
+
},
|
|
1540
|
+
required: ['agent_id'],
|
|
1541
|
+
},
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
name: 'publish_agent',
|
|
1545
|
+
description: 'Publish agent to marketplace. Runs validation and sets status to active (admins) or pending_review (sellers).',
|
|
1546
|
+
inputSchema: {
|
|
1547
|
+
type: 'object',
|
|
1548
|
+
properties: {
|
|
1549
|
+
agent_id: {
|
|
1550
|
+
type: 'string',
|
|
1551
|
+
description: 'Agent ID to publish',
|
|
1552
|
+
},
|
|
1553
|
+
},
|
|
1554
|
+
required: ['agent_id'],
|
|
1555
|
+
},
|
|
1556
|
+
},
|
|
1557
|
+
// ==========================================
|
|
1558
|
+
// DASHBOARD TOOLS
|
|
1559
|
+
// ==========================================
|
|
1560
|
+
{
|
|
1561
|
+
name: 'create_agent_dashboard',
|
|
1562
|
+
description: 'Create or configure a dashboard for an agent. Dashboards deploy as independent pods alongside the agent runtime. Subscribers access dashboards via unique URLs with JWT auth. Supports white-labelling via branding config.',
|
|
1563
|
+
inputSchema: {
|
|
1564
|
+
type: 'object',
|
|
1565
|
+
properties: {
|
|
1566
|
+
agent_id: {
|
|
1567
|
+
type: 'string',
|
|
1568
|
+
description: 'Agent ID to create dashboard for',
|
|
1569
|
+
},
|
|
1570
|
+
template_type: {
|
|
1571
|
+
type: 'string',
|
|
1572
|
+
description: 'Dashboard template type (e.g., instagram-dm-portal, customer-support, analytics)',
|
|
1573
|
+
},
|
|
1574
|
+
layout: {
|
|
1575
|
+
type: 'string',
|
|
1576
|
+
enum: ['minimal', 'standard', 'full'],
|
|
1577
|
+
description: 'Dashboard layout complexity (default: standard)',
|
|
1578
|
+
},
|
|
1579
|
+
navigation: {
|
|
1580
|
+
type: 'string',
|
|
1581
|
+
enum: ['sidebar', 'topbar'],
|
|
1582
|
+
description: 'Navigation style (default: sidebar)',
|
|
1583
|
+
},
|
|
1584
|
+
widgets: {
|
|
1585
|
+
type: 'array',
|
|
1586
|
+
items: { type: 'string' },
|
|
1587
|
+
description: 'Shorthand: list of widget type strings. Auto-wrapped into a single "main" tab. Available types: stats_card, data_table, chart, form, activity_feed, ca_invoice_table, ca_client_management, ca_export_panel, ca_dashboard_overview, ca_whatsapp_status, instagram_conversations, instagram_metrics',
|
|
1588
|
+
},
|
|
1589
|
+
tabs: {
|
|
1590
|
+
type: 'array',
|
|
1591
|
+
items: {
|
|
1592
|
+
type: 'object',
|
|
1593
|
+
properties: {
|
|
1594
|
+
id: { type: 'string', description: 'Unique tab ID' },
|
|
1595
|
+
label: { type: 'string', description: 'Tab display label' },
|
|
1596
|
+
icon: { type: 'string', description: 'Lucide icon name (e.g., file-text, users, settings)' },
|
|
1597
|
+
widgets: {
|
|
1598
|
+
type: 'array',
|
|
1599
|
+
items: {
|
|
1600
|
+
type: 'object',
|
|
1601
|
+
properties: {
|
|
1602
|
+
type: { type: 'string', description: 'Widget type from registry' },
|
|
1603
|
+
config: { type: 'object', description: 'Widget-specific config (e.g., endpoint, columns)' },
|
|
1604
|
+
},
|
|
1605
|
+
required: ['type'],
|
|
1606
|
+
},
|
|
1607
|
+
description: 'Widgets to render in this tab',
|
|
1608
|
+
},
|
|
1609
|
+
},
|
|
1610
|
+
required: ['id', 'label', 'widgets'],
|
|
1611
|
+
},
|
|
1612
|
+
description: 'Full tab structure with widgets per tab. Use this OR widgets (not both).',
|
|
1613
|
+
},
|
|
1614
|
+
theme: {
|
|
1615
|
+
type: 'object',
|
|
1616
|
+
description: 'Custom theme configuration (colors, fonts)',
|
|
1617
|
+
},
|
|
1618
|
+
branding: {
|
|
1619
|
+
type: 'object',
|
|
1620
|
+
description: 'Custom branding: { brand_name, logo_url, favicon_url, primary_color, secondary_color, background_color, text_color, font_family, custom_css, show_powered_by }',
|
|
1621
|
+
},
|
|
1622
|
+
},
|
|
1623
|
+
required: ['agent_id'],
|
|
1624
|
+
},
|
|
1625
|
+
},
|
|
1626
|
+
{
|
|
1627
|
+
name: 'get_agent_dashboard_config',
|
|
1628
|
+
description: 'Get the current dashboard configuration for an agent',
|
|
1629
|
+
inputSchema: {
|
|
1630
|
+
type: 'object',
|
|
1631
|
+
properties: {
|
|
1632
|
+
agent_id: {
|
|
1633
|
+
type: 'string',
|
|
1634
|
+
description: 'Agent ID',
|
|
1635
|
+
},
|
|
1636
|
+
},
|
|
1637
|
+
required: ['agent_id'],
|
|
1638
|
+
},
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
name: 'update_agent_dashboard',
|
|
1642
|
+
description: 'Update dashboard configuration for an agent',
|
|
1643
|
+
inputSchema: {
|
|
1644
|
+
type: 'object',
|
|
1645
|
+
properties: {
|
|
1646
|
+
agent_id: {
|
|
1647
|
+
type: 'string',
|
|
1648
|
+
description: 'Agent ID',
|
|
1649
|
+
},
|
|
1650
|
+
updates: {
|
|
1651
|
+
type: 'object',
|
|
1652
|
+
description: 'Dashboard configuration updates',
|
|
1653
|
+
},
|
|
1654
|
+
},
|
|
1655
|
+
required: ['agent_id', 'updates'],
|
|
1656
|
+
},
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
name: 'add_dashboard_widget',
|
|
1660
|
+
description: 'Add a widget to an agent dashboard',
|
|
1661
|
+
inputSchema: {
|
|
1662
|
+
type: 'object',
|
|
1663
|
+
properties: {
|
|
1664
|
+
agent_id: {
|
|
1665
|
+
type: 'string',
|
|
1666
|
+
description: 'Agent ID',
|
|
1667
|
+
},
|
|
1668
|
+
widget_type: {
|
|
1669
|
+
type: 'string',
|
|
1670
|
+
enum: ['chart', 'list', 'stats', 'custom'],
|
|
1671
|
+
description: 'Type of widget to add',
|
|
1672
|
+
},
|
|
1673
|
+
title: {
|
|
1674
|
+
type: 'string',
|
|
1675
|
+
description: 'Widget title',
|
|
1676
|
+
},
|
|
1677
|
+
position: {
|
|
1678
|
+
type: 'object',
|
|
1679
|
+
properties: {
|
|
1680
|
+
x: { type: 'number' },
|
|
1681
|
+
y: { type: 'number' },
|
|
1682
|
+
},
|
|
1683
|
+
description: 'Widget position on dashboard grid',
|
|
1684
|
+
},
|
|
1685
|
+
size: {
|
|
1686
|
+
type: 'object',
|
|
1687
|
+
properties: {
|
|
1688
|
+
width: { type: 'number' },
|
|
1689
|
+
height: { type: 'number' },
|
|
1690
|
+
},
|
|
1691
|
+
description: 'Widget size',
|
|
1692
|
+
},
|
|
1693
|
+
config: {
|
|
1694
|
+
type: 'object',
|
|
1695
|
+
description: 'Widget-specific configuration',
|
|
1696
|
+
},
|
|
1697
|
+
},
|
|
1698
|
+
required: ['agent_id', 'widget_type', 'title'],
|
|
1699
|
+
},
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
name: 'configure_agent_page',
|
|
1703
|
+
description: 'Configure agent pages (landing page, configuration page, or dashboard page)',
|
|
1704
|
+
inputSchema: {
|
|
1705
|
+
type: 'object',
|
|
1706
|
+
properties: {
|
|
1707
|
+
agent_id: {
|
|
1708
|
+
type: 'string',
|
|
1709
|
+
description: 'Agent ID',
|
|
1710
|
+
},
|
|
1711
|
+
page_type: {
|
|
1712
|
+
type: 'string',
|
|
1713
|
+
enum: ['landing', 'configuration', 'dashboard'],
|
|
1714
|
+
description: 'Type of page to configure',
|
|
1715
|
+
},
|
|
1716
|
+
content: {
|
|
1717
|
+
type: 'object',
|
|
1718
|
+
description: 'Page content and configuration',
|
|
1719
|
+
},
|
|
1720
|
+
},
|
|
1721
|
+
required: ['agent_id', 'page_type', 'content'],
|
|
1722
|
+
},
|
|
1723
|
+
},
|
|
1724
|
+
// ==========================================
|
|
1725
|
+
// WIDGET GENERATION TOOLS
|
|
1726
|
+
// ==========================================
|
|
1727
|
+
{
|
|
1728
|
+
name: 'generate_dashboard_widget',
|
|
1729
|
+
description: `Compile and deploy a custom React widget to an agent's dashboard. YOU (the connected LLM) write the TSX source code and pass it as source_code. The backend validates, compiles to ESM via esbuild, uploads to CDN, and auto-adds it to the dashboard. Use this for any UI the 11 built-in widgets cannot cover (Kanban boards, calendars, maps, custom forms, etc.).
|
|
1730
|
+
|
|
1731
|
+
## Widget Contract ā YOU must generate code following these rules:
|
|
1732
|
+
|
|
1733
|
+
\`\`\`tsx
|
|
1734
|
+
import React from 'react';
|
|
1735
|
+
export default function Widget({ config }: { config: Record<string, unknown> }) { ... }
|
|
1736
|
+
\`\`\`
|
|
1737
|
+
|
|
1738
|
+
**Available imports** (provided by host dashboard ā do NOT use anything else):
|
|
1739
|
+
- \`react\` (useState, useEffect, useMemo, useCallback, useRef)
|
|
1740
|
+
- \`recharts\` (BarChart, LineChart, PieChart, AreaChart, ResponsiveContainer, etc.)
|
|
1741
|
+
- \`lucide-react\` (icons: Search, Plus, Trash, Edit, Check, X, etc.)
|
|
1742
|
+
- \`axios\` (for API calls ā always use relative URLs like \`/api/...\`)
|
|
1743
|
+
|
|
1744
|
+
**Styling**: TailwindCSS utility classes only. Use \`dark:\` variants for dark mode.
|
|
1745
|
+
|
|
1746
|
+
**Rules**:
|
|
1747
|
+
1. Single default-exported function component
|
|
1748
|
+
2. Receives \`{ config }\` prop ā use \`config.data_endpoint\`, \`config.title\`, etc.
|
|
1749
|
+
3. For data: \`axios.get(config.data_endpoint || '/api/data')\` inside useEffect
|
|
1750
|
+
4. Handle loading + error states
|
|
1751
|
+
5. FORBIDDEN: eval(), Function(), document.cookie, localStorage, sessionStorage, <script>
|
|
1752
|
+
6. Keep component self-contained (no external files)`,
|
|
1753
|
+
inputSchema: {
|
|
1754
|
+
type: 'object',
|
|
1755
|
+
properties: {
|
|
1756
|
+
agent_id: {
|
|
1757
|
+
type: 'string',
|
|
1758
|
+
description: 'Agent ID to add the widget to',
|
|
1759
|
+
},
|
|
1760
|
+
widget_name: {
|
|
1761
|
+
type: 'string',
|
|
1762
|
+
description: 'Machine-friendly name for the widget (snake_case, e.g. "kanban_leads_board")',
|
|
1763
|
+
},
|
|
1764
|
+
source_code: {
|
|
1765
|
+
type: 'string',
|
|
1766
|
+
description: 'Complete React TSX source code for the widget. Must follow the widget contract: export default a function component that receives { config } prop. Use only react, recharts, lucide-react, axios imports. TailwindCSS for styling.',
|
|
1767
|
+
},
|
|
1768
|
+
description: {
|
|
1769
|
+
type: 'string',
|
|
1770
|
+
description: 'Human-readable description of what the widget does (stored as metadata)',
|
|
1771
|
+
},
|
|
1772
|
+
},
|
|
1773
|
+
required: ['agent_id', 'widget_name', 'source_code'],
|
|
1774
|
+
},
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1777
|
+
name: 'list_generated_widgets',
|
|
1778
|
+
description: 'List all custom-generated widgets for an agent. Shows widget name, CDN URL, version, build status, and description.',
|
|
1779
|
+
inputSchema: {
|
|
1780
|
+
type: 'object',
|
|
1781
|
+
properties: {
|
|
1782
|
+
agent_id: {
|
|
1783
|
+
type: 'string',
|
|
1784
|
+
description: 'Agent ID to list widgets for',
|
|
1785
|
+
},
|
|
1786
|
+
},
|
|
1787
|
+
required: ['agent_id'],
|
|
1788
|
+
},
|
|
1789
|
+
},
|
|
1790
|
+
{
|
|
1791
|
+
name: 'regenerate_widget',
|
|
1792
|
+
description: 'Replace a widget\'s source code and recompile. Creates a new version (old version retained on CDN). YOU (the connected LLM) write the updated TSX following the same widget contract as generate_dashboard_widget.',
|
|
1793
|
+
inputSchema: {
|
|
1794
|
+
type: 'object',
|
|
1795
|
+
properties: {
|
|
1796
|
+
agent_id: {
|
|
1797
|
+
type: 'string',
|
|
1798
|
+
description: 'Agent ID that owns the widget',
|
|
1799
|
+
},
|
|
1800
|
+
widget_id: {
|
|
1801
|
+
type: 'string',
|
|
1802
|
+
description: 'Widget name/ID to regenerate',
|
|
1803
|
+
},
|
|
1804
|
+
source_code: {
|
|
1805
|
+
type: 'string',
|
|
1806
|
+
description: 'Updated React TSX source code (same contract as generate_dashboard_widget)',
|
|
1807
|
+
},
|
|
1808
|
+
description: {
|
|
1809
|
+
type: 'string',
|
|
1810
|
+
description: 'Updated description (omit to keep existing)',
|
|
1811
|
+
},
|
|
1812
|
+
},
|
|
1813
|
+
required: ['agent_id', 'widget_id', 'source_code'],
|
|
1814
|
+
},
|
|
1815
|
+
},
|
|
1816
|
+
{
|
|
1817
|
+
name: 'delete_generated_widget',
|
|
1818
|
+
description: 'Delete a generated widget from the agent. Removes metadata from database. CDN bundles are retained (immutable).',
|
|
1819
|
+
inputSchema: {
|
|
1820
|
+
type: 'object',
|
|
1821
|
+
properties: {
|
|
1822
|
+
agent_id: {
|
|
1823
|
+
type: 'string',
|
|
1824
|
+
description: 'Agent ID that owns the widget',
|
|
1825
|
+
},
|
|
1826
|
+
widget_id: {
|
|
1827
|
+
type: 'string',
|
|
1828
|
+
description: 'Widget name/ID to delete',
|
|
1829
|
+
},
|
|
1830
|
+
},
|
|
1831
|
+
required: ['agent_id', 'widget_id'],
|
|
1832
|
+
},
|
|
1833
|
+
},
|
|
1834
|
+
// ==========================================
|
|
1835
|
+
// AGENT COMPOSITION TOOLS
|
|
1836
|
+
// ==========================================
|
|
1837
|
+
{
|
|
1838
|
+
name: 'compose_agents',
|
|
1839
|
+
description: 'Combine 2-10 agents into a composite agent using composition patterns (sequential, parallel, router, map_reduce). Sequential runs AāBāC. Parallel runs all simultaneously. Router selects agent based on input. Map-reduce fans out, processes, and aggregates. Returns composed agent with bundle pricing (15% discount).',
|
|
1840
|
+
inputSchema: {
|
|
1841
|
+
type: 'object',
|
|
1842
|
+
properties: {
|
|
1843
|
+
name: {
|
|
1844
|
+
type: 'string',
|
|
1845
|
+
description: 'Name for the composed agent',
|
|
1846
|
+
},
|
|
1847
|
+
description: {
|
|
1848
|
+
type: 'string',
|
|
1849
|
+
description: 'Description of what the composed agent does',
|
|
1850
|
+
},
|
|
1851
|
+
sub_agent_ids: {
|
|
1852
|
+
type: 'array',
|
|
1853
|
+
items: { type: 'string' },
|
|
1854
|
+
description: 'Array of 2-10 agent IDs to compose together',
|
|
1855
|
+
minItems: 2,
|
|
1856
|
+
maxItems: 10,
|
|
1857
|
+
},
|
|
1858
|
+
composition_type: {
|
|
1859
|
+
type: 'string',
|
|
1860
|
+
enum: ['sequential', 'parallel', 'router', 'map_reduce'],
|
|
1861
|
+
description: 'How agents are combined: sequential (AāBāC), parallel (A+B+C), router (A|B|C based on input), map_reduce (fan-outāprocessāaggregate)',
|
|
1862
|
+
},
|
|
1863
|
+
routing_config: {
|
|
1864
|
+
type: 'object',
|
|
1865
|
+
description: 'Required for router type. Maps conditions to agent IDs. Example: {"rules": [{"condition": "intent==product_inquiry", "agent_id": "..."}]}',
|
|
1866
|
+
},
|
|
1867
|
+
state_mapping: {
|
|
1868
|
+
type: 'object',
|
|
1869
|
+
description: 'Optional. Maps output fields from one agent to input fields of the next (for sequential type)',
|
|
1870
|
+
},
|
|
1871
|
+
aggregation_strategy: {
|
|
1872
|
+
type: 'string',
|
|
1873
|
+
description: 'Optional. For parallel/map_reduce: how to combine results (merge, concat, first_success, majority_vote)',
|
|
1874
|
+
},
|
|
1875
|
+
},
|
|
1876
|
+
required: ['name', 'description', 'sub_agent_ids', 'composition_type'],
|
|
1877
|
+
},
|
|
1878
|
+
},
|
|
1879
|
+
{
|
|
1880
|
+
name: 'list_composed_agents',
|
|
1881
|
+
description: 'List all composed/composite agents owned by the authenticated user',
|
|
1882
|
+
inputSchema: {
|
|
1883
|
+
type: 'object',
|
|
1884
|
+
properties: {
|
|
1885
|
+
page: {
|
|
1886
|
+
type: 'number',
|
|
1887
|
+
description: 'Page number (default: 1)',
|
|
1888
|
+
},
|
|
1889
|
+
limit: {
|
|
1890
|
+
type: 'number',
|
|
1891
|
+
description: 'Results per page (default: 20, max: 100)',
|
|
1892
|
+
},
|
|
1893
|
+
},
|
|
1894
|
+
},
|
|
1895
|
+
},
|
|
1896
|
+
{
|
|
1897
|
+
name: 'get_composed_agent',
|
|
1898
|
+
description: 'Get detailed information about a composed agent including its sub-agents, composition type, pricing, and execution flow',
|
|
1899
|
+
inputSchema: {
|
|
1900
|
+
type: 'object',
|
|
1901
|
+
properties: {
|
|
1902
|
+
agent_id: {
|
|
1903
|
+
type: 'string',
|
|
1904
|
+
description: 'Composed agent ID',
|
|
1905
|
+
},
|
|
1906
|
+
},
|
|
1907
|
+
required: ['agent_id'],
|
|
1908
|
+
},
|
|
1909
|
+
},
|
|
1910
|
+
{
|
|
1911
|
+
name: 'preview_composition',
|
|
1912
|
+
description: 'Preview a composition before creating it. Shows pricing breakdown (individual prices, bundle discount, final price) and execution flow diagram. Use this to validate a composition before committing.',
|
|
1913
|
+
inputSchema: {
|
|
1914
|
+
type: 'object',
|
|
1915
|
+
properties: {
|
|
1916
|
+
sub_agent_ids: {
|
|
1917
|
+
type: 'array',
|
|
1918
|
+
items: { type: 'string' },
|
|
1919
|
+
description: 'Array of 2-10 agent IDs to preview',
|
|
1920
|
+
minItems: 2,
|
|
1921
|
+
maxItems: 10,
|
|
1922
|
+
},
|
|
1923
|
+
composition_type: {
|
|
1924
|
+
type: 'string',
|
|
1925
|
+
enum: ['sequential', 'parallel', 'router', 'map_reduce'],
|
|
1926
|
+
description: 'How agents would be combined',
|
|
1927
|
+
},
|
|
1928
|
+
},
|
|
1929
|
+
required: ['sub_agent_ids', 'composition_type'],
|
|
1930
|
+
},
|
|
1931
|
+
},
|
|
1932
|
+
{
|
|
1933
|
+
name: 'execute_composed_agent',
|
|
1934
|
+
description: 'Execute a composed agent with input data. Routes input through the composition pipeline (sequential/parallel/router/map_reduce) and returns aggregated results from all sub-agents.',
|
|
1935
|
+
inputSchema: {
|
|
1936
|
+
type: 'object',
|
|
1937
|
+
properties: {
|
|
1938
|
+
agent_id: {
|
|
1939
|
+
type: 'string',
|
|
1940
|
+
description: 'Composed agent ID to execute',
|
|
1941
|
+
},
|
|
1942
|
+
input_data: {
|
|
1943
|
+
type: 'object',
|
|
1944
|
+
description: 'Input data to pass to the composed agent pipeline',
|
|
1945
|
+
},
|
|
1946
|
+
},
|
|
1947
|
+
required: ['agent_id'],
|
|
1948
|
+
},
|
|
1949
|
+
},
|
|
1950
|
+
{
|
|
1951
|
+
name: 'list_agent_types',
|
|
1952
|
+
description: 'List available agent type templates that serve as starting points for creating agents. Returns pre-seeded templates like instagram_dm_sales, whatsapp_business, email_marketing, telegram_support with their default configurations, pricing, and required integrations.',
|
|
1953
|
+
inputSchema: {
|
|
1954
|
+
type: 'object',
|
|
1955
|
+
properties: {},
|
|
1956
|
+
},
|
|
1957
|
+
},
|
|
1958
|
+
{
|
|
1959
|
+
name: 'get_mcp_plan_limits',
|
|
1960
|
+
description: 'Show your current subscription plan limits for MCP agent operations ā how many agents you can create, how many pods you can deploy, and current usage. Call this first if you are unsure whether an operation will be allowed.',
|
|
1961
|
+
inputSchema: {
|
|
1962
|
+
type: 'object',
|
|
1963
|
+
properties: {},
|
|
1964
|
+
},
|
|
1965
|
+
},
|
|
1966
|
+
{
|
|
1967
|
+
name: 'clone_agent',
|
|
1968
|
+
description: 'Clone an existing agent as a starting point for a new one. Creates a deep copy with graph, wizard_configuration, and pricing copied over. New agent starts in draft status. Useful for creating variations of existing agents.',
|
|
1969
|
+
inputSchema: {
|
|
1970
|
+
type: 'object',
|
|
1971
|
+
properties: {
|
|
1972
|
+
agent_id: {
|
|
1973
|
+
type: 'string',
|
|
1974
|
+
description: 'ID of the agent to clone',
|
|
1975
|
+
},
|
|
1976
|
+
new_name: {
|
|
1977
|
+
type: 'string',
|
|
1978
|
+
description: 'Name for the cloned agent',
|
|
1979
|
+
},
|
|
1980
|
+
},
|
|
1981
|
+
required: ['agent_id', 'new_name'],
|
|
1982
|
+
},
|
|
1983
|
+
},
|
|
1984
|
+
];
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
// Tool Handlers
|
|
1988
|
+
|
|
1989
|
+
/**
|
|
1990
|
+
* Validate workflow structure before sending to API
|
|
1991
|
+
*/
|
|
1992
|
+
private validateWorkflowStructure(workflow: any): string[] {
|
|
1993
|
+
const errors: string[] = [];
|
|
1994
|
+
|
|
1995
|
+
// Validate name
|
|
1996
|
+
if (!workflow.name || workflow.name.trim().length === 0) {
|
|
1997
|
+
errors.push('Workflow name is required');
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
// Validate nodes
|
|
2001
|
+
if (!workflow.nodes || workflow.nodes.length === 0) {
|
|
2002
|
+
errors.push('At least one node is required');
|
|
2003
|
+
} else {
|
|
2004
|
+
workflow.nodes.forEach((node: any, index: number) => {
|
|
2005
|
+
if (!node.id) errors.push(`Node ${index}: missing required field 'id'`);
|
|
2006
|
+
if (!node.type) errors.push(`Node ${index}: missing required field 'type'`);
|
|
2007
|
+
if (!node.title) errors.push(`Node ${index}: missing required field 'title'`);
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
// Validate connections
|
|
2012
|
+
if (workflow.connections && workflow.connections.length > 0) {
|
|
2013
|
+
workflow.connections.forEach((conn: any, index: number) => {
|
|
2014
|
+
if (!conn.id) errors.push(`Connection ${index}: missing required field 'id'`);
|
|
2015
|
+
if (!conn.source) errors.push(`Connection ${index}: missing required field 'source'`);
|
|
2016
|
+
if (!conn.target) errors.push(`Connection ${index}: missing required field 'target'`);
|
|
2017
|
+
|
|
2018
|
+
// Check if source/target nodes exist
|
|
2019
|
+
const sourceExists = workflow.nodes.some((n: any) => n.id === conn.source);
|
|
2020
|
+
const targetExists = workflow.nodes.some((n: any) => n.id === conn.target);
|
|
2021
|
+
|
|
2022
|
+
if (!sourceExists) errors.push(`Connection ${index}: source node '${conn.source}' not found`);
|
|
2023
|
+
if (!targetExists) errors.push(`Connection ${index}: target node '${conn.target}' not found`);
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
return errors;
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
/**
|
|
2031
|
+
* Format error messages for better user experience
|
|
2032
|
+
*/
|
|
2033
|
+
private formatErrorMessage(error: any): string {
|
|
2034
|
+
// Network errors
|
|
2035
|
+
if (error.code === 'ECONNREFUSED') {
|
|
2036
|
+
return `ā Cannot connect to AgentBuilder API at ${this.client['apiBaseUrl']}\n\n` +
|
|
2037
|
+
`Please ensure the backend is running and accessible.`;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
// API errors with response
|
|
2041
|
+
if (error.response) {
|
|
2042
|
+
const status = error.response.status;
|
|
2043
|
+
const message = error.response.data?.error?.message || error.response.data?.detail || error.message;
|
|
2044
|
+
|
|
2045
|
+
if (status === 401) {
|
|
2046
|
+
return `š Authentication failed\n\n${message}\n\n` +
|
|
2047
|
+
`Please run set_connection or set_auth_token first.`;
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
if (status === 400) {
|
|
2051
|
+
return `ā ļø Validation error\n\n${message}\n\n` +
|
|
2052
|
+
`Please check your workflow structure matches the required format.`;
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
if (status === 404) {
|
|
2056
|
+
return `ā Not found\n\n${message}`;
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
return `ā API Error (${status})\n\n${message}`;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
// Generic errors
|
|
2063
|
+
return `ā Error: ${error.message}`;
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
private async handleSetConnection(args: any) {
|
|
2067
|
+
const { connection_id } = args;
|
|
2068
|
+
|
|
2069
|
+
try {
|
|
2070
|
+
// Validate connection with backend
|
|
2071
|
+
const response = await this.client.validateConnection(connection_id);
|
|
2072
|
+
|
|
2073
|
+
if (response.success) {
|
|
2074
|
+
// Set connection ID in client
|
|
2075
|
+
this.client.setConnection(connection_id);
|
|
2076
|
+
|
|
2077
|
+
const workspaceName = response.data.workspace_name;
|
|
2078
|
+
const status = response.data.status;
|
|
2079
|
+
|
|
2080
|
+
return {
|
|
2081
|
+
content: [
|
|
2082
|
+
{
|
|
2083
|
+
type: 'text',
|
|
2084
|
+
text: `ā
Successfully connected to AgentBuilder workspace: "${workspaceName}"\n\n` +
|
|
2085
|
+
`Connection Status: ${status}\n` +
|
|
2086
|
+
`Connection ID: ${connection_id}\n\n` +
|
|
2087
|
+
`You can now create workflows, and they will be saved to this workspace.`,
|
|
2088
|
+
},
|
|
2089
|
+
],
|
|
2090
|
+
};
|
|
2091
|
+
} else {
|
|
2092
|
+
throw new Error(response.message || 'Connection validation failed');
|
|
2093
|
+
}
|
|
2094
|
+
} catch (error: any) {
|
|
2095
|
+
const errorMessage = error.response?.data?.error?.message || error.message;
|
|
2096
|
+
|
|
2097
|
+
return {
|
|
2098
|
+
content: [
|
|
2099
|
+
{
|
|
2100
|
+
type: 'text',
|
|
2101
|
+
text: `ā Failed to connect with connection ID: ${connection_id}\n\n` +
|
|
2102
|
+
`Error: ${errorMessage}\n\n` +
|
|
2103
|
+
`Please ensure:\n` +
|
|
2104
|
+
`1. You've created an MCP connection in the AgentBuilder dashboard\n` +
|
|
2105
|
+
`2. The connection ID is correct (format: mcp_conn_xxx)\n` +
|
|
2106
|
+
`3. The connection is active and hasn't expired or been revoked\n` +
|
|
2107
|
+
`4. The backend API is accessible at ${this.client['apiBaseUrl']}`,
|
|
2108
|
+
},
|
|
2109
|
+
],
|
|
2110
|
+
isError: true,
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
private async handleSetAuthToken(args: any) {
|
|
2116
|
+
this.client.setAuthToken(args.token);
|
|
2117
|
+
return {
|
|
2118
|
+
content: [
|
|
2119
|
+
{
|
|
2120
|
+
type: 'text',
|
|
2121
|
+
text: 'Authentication token set successfully. You can now use workflow tools.',
|
|
2122
|
+
},
|
|
2123
|
+
],
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
private async handleCreateWorkflow(args: any) {
|
|
2128
|
+
// Validate workflow structure
|
|
2129
|
+
const validationErrors = this.validateWorkflowStructure({
|
|
2130
|
+
name: args.name,
|
|
2131
|
+
nodes: args.nodes || [],
|
|
2132
|
+
connections: args.connections || []
|
|
2133
|
+
});
|
|
2134
|
+
|
|
2135
|
+
if (validationErrors.length > 0) {
|
|
2136
|
+
return {
|
|
2137
|
+
content: [
|
|
2138
|
+
{
|
|
2139
|
+
type: 'text',
|
|
2140
|
+
text: `ā ļø Workflow validation failed:\n\n${validationErrors.map(err => `⢠${err}`).join('\n')}\n\n` +
|
|
2141
|
+
`Please fix these issues and try again.`,
|
|
2142
|
+
},
|
|
2143
|
+
],
|
|
2144
|
+
isError: true,
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
const result = await this.client.createWorkflow(args);
|
|
2149
|
+
return {
|
|
2150
|
+
content: [
|
|
2151
|
+
{
|
|
2152
|
+
type: 'text',
|
|
2153
|
+
text: JSON.stringify(result, null, 2),
|
|
2154
|
+
},
|
|
2155
|
+
],
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
private async handleListWorkflows(args: any) {
|
|
2160
|
+
const result = await this.client.listWorkflows(args);
|
|
2161
|
+
return {
|
|
2162
|
+
content: [
|
|
2163
|
+
{
|
|
2164
|
+
type: 'text',
|
|
2165
|
+
text: JSON.stringify(result, null, 2),
|
|
2166
|
+
},
|
|
2167
|
+
],
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
private async handleGetWorkflow(args: any) {
|
|
2172
|
+
const result = await this.client.getWorkflow(args.workflow_id);
|
|
2173
|
+
return {
|
|
2174
|
+
content: [
|
|
2175
|
+
{
|
|
2176
|
+
type: 'text',
|
|
2177
|
+
text: JSON.stringify(result, null, 2),
|
|
2178
|
+
},
|
|
2179
|
+
],
|
|
2180
|
+
};
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
private async handleUpdateWorkflow(args: any) {
|
|
2184
|
+
const result = await this.client.updateWorkflow(args.workflow_id, args.updates);
|
|
2185
|
+
return {
|
|
2186
|
+
content: [
|
|
2187
|
+
{
|
|
2188
|
+
type: 'text',
|
|
2189
|
+
text: JSON.stringify(result, null, 2),
|
|
2190
|
+
},
|
|
2191
|
+
],
|
|
2192
|
+
};
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
private async handleExecuteWorkflow(args: any) {
|
|
2196
|
+
const result = await this.client.executeWorkflow(args.workflow_id, args.input);
|
|
2197
|
+
return {
|
|
2198
|
+
content: [
|
|
2199
|
+
{
|
|
2200
|
+
type: 'text',
|
|
2201
|
+
text: JSON.stringify(result, null, 2),
|
|
2202
|
+
},
|
|
2203
|
+
],
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
private async handleGetExecutionStatus(args: any) {
|
|
2208
|
+
const result = await this.client.getExecutionStatus(args.execution_id);
|
|
2209
|
+
return {
|
|
2210
|
+
content: [
|
|
2211
|
+
{
|
|
2212
|
+
type: 'text',
|
|
2213
|
+
text: JSON.stringify(result, null, 2),
|
|
2214
|
+
},
|
|
2215
|
+
],
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
private async handleListExecutions(args: any) {
|
|
2220
|
+
const result = await this.client.listExecutions(args.workflow_id);
|
|
2221
|
+
return {
|
|
2222
|
+
content: [
|
|
2223
|
+
{
|
|
2224
|
+
type: 'text',
|
|
2225
|
+
text: JSON.stringify(result, null, 2),
|
|
2226
|
+
},
|
|
2227
|
+
],
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
private async handleDeleteWorkflow(args: any) {
|
|
2232
|
+
const result = await this.client.deleteWorkflow(args.workflow_id);
|
|
2233
|
+
return {
|
|
2234
|
+
content: [
|
|
2235
|
+
{
|
|
2236
|
+
type: 'text',
|
|
2237
|
+
text: JSON.stringify(result, null, 2),
|
|
2238
|
+
},
|
|
2239
|
+
],
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
private async handleValidateWorkflow(args: any) {
|
|
2244
|
+
const result = await this.client.validateWorkflow(args.workflow);
|
|
2245
|
+
return {
|
|
2246
|
+
content: [
|
|
2247
|
+
{
|
|
2248
|
+
type: 'text',
|
|
2249
|
+
text: JSON.stringify(result, null, 2),
|
|
2250
|
+
},
|
|
2251
|
+
],
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
private async handleCheckDeploymentReadiness(args: any) {
|
|
2256
|
+
const result = await this.client.checkDeploymentReadiness(args.workflow_id);
|
|
2257
|
+
|
|
2258
|
+
const status = result.data;
|
|
2259
|
+
|
|
2260
|
+
let message = `## Deployment Readiness: ${args.workflow_id}\n\n`;
|
|
2261
|
+
message += `**Status**: ${status.can_deploy ? 'ā
Ready' : 'ā ļø Not Ready'}\n\n`;
|
|
2262
|
+
|
|
2263
|
+
if (status.required_credentials && status.required_credentials.length > 0) {
|
|
2264
|
+
message += `### Required Credentials:\n`;
|
|
2265
|
+
status.required_credentials.forEach((cred: any) => {
|
|
2266
|
+
const icon = cred.is_configured ? 'ā
' : 'ā';
|
|
2267
|
+
message += `${icon} **${cred.display_name}**\n`;
|
|
2268
|
+
if (!cred.is_configured && cred.setup_url) {
|
|
2269
|
+
message += ` Setup: ${status.setup_base_url || ''}${cred.setup_url}\n`;
|
|
2270
|
+
}
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
if (status.missing_credentials && status.missing_credentials.length > 0) {
|
|
2275
|
+
message += `\nā ļø **Missing**: ${status.missing_credentials.map((c: any) => c.display_name).join(', ')}\n`;
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
return {
|
|
2279
|
+
content: [
|
|
2280
|
+
{
|
|
2281
|
+
type: 'text',
|
|
2282
|
+
text: message,
|
|
2283
|
+
},
|
|
2284
|
+
],
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
private async handleDeployWorkflow(args: any) {
|
|
2289
|
+
const result = await this.client.deployWorkflow(args.workflow_id, {
|
|
2290
|
+
instance_name: args.instance_name,
|
|
2291
|
+
price_per_execution: args.price_per_execution,
|
|
2292
|
+
});
|
|
2293
|
+
|
|
2294
|
+
const deployment = result.data;
|
|
2295
|
+
|
|
2296
|
+
let message = `## ā
Deployment Successful!\n\n`;
|
|
2297
|
+
message += `**Instance ID**: ${deployment.instance_id}\n`;
|
|
2298
|
+
message += `**Status**: ${deployment.status}\n`;
|
|
2299
|
+
message += `**Endpoint**: ${deployment.platform_endpoint}\n\n`;
|
|
2300
|
+
|
|
2301
|
+
if (deployment.demo_api_key) {
|
|
2302
|
+
message += `**Demo API Key**: ${deployment.demo_api_key}\n`;
|
|
2303
|
+
message += `Use this key to test your deployment.\n`;
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
return {
|
|
2307
|
+
content: [
|
|
2308
|
+
{
|
|
2309
|
+
type: 'text',
|
|
2310
|
+
text: message,
|
|
2311
|
+
},
|
|
2312
|
+
],
|
|
2313
|
+
};
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
private async handleListMCPWorkflows(args: any) {
|
|
2317
|
+
const result = await this.client.listMCPWorkflows(args.filter);
|
|
2318
|
+
|
|
2319
|
+
let message = `## MCP Workflows\n\n`;
|
|
2320
|
+
|
|
2321
|
+
if (result.data && result.data.workflows && result.data.workflows.length > 0) {
|
|
2322
|
+
result.data.workflows.forEach((workflow: any) => {
|
|
2323
|
+
message += `### ${workflow.name}\n`;
|
|
2324
|
+
message += `- **ID**: ${workflow.id}\n`;
|
|
2325
|
+
message += `- **Status**: ${workflow.status}\n`;
|
|
2326
|
+
message += `- **Deployment**: ${workflow.deployment_status || 'Not deployed'}\n`;
|
|
2327
|
+
if (workflow.required_credentials) {
|
|
2328
|
+
message += `- **Required Credentials**: ${workflow.required_credentials.join(', ')}\n`;
|
|
2329
|
+
}
|
|
2330
|
+
message += `\n`;
|
|
2331
|
+
});
|
|
2332
|
+
} else {
|
|
2333
|
+
message += `No MCP workflows found.\n`;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
return {
|
|
2337
|
+
content: [
|
|
2338
|
+
{
|
|
2339
|
+
type: 'text',
|
|
2340
|
+
text: message,
|
|
2341
|
+
},
|
|
2342
|
+
],
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
private async handleListAgents(args: any) {
|
|
2347
|
+
const result = await this.client.listAgents(args);
|
|
2348
|
+
return {
|
|
2349
|
+
content: [
|
|
2350
|
+
{
|
|
2351
|
+
type: 'text',
|
|
2352
|
+
text: JSON.stringify(result, null, 2),
|
|
2353
|
+
},
|
|
2354
|
+
],
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
private async handleGetAgent(args: any) {
|
|
2359
|
+
const result = await this.client.getAgent(args.agent_id);
|
|
2360
|
+
return {
|
|
2361
|
+
content: [
|
|
2362
|
+
{
|
|
2363
|
+
type: 'text',
|
|
2364
|
+
text: JSON.stringify(result, null, 2),
|
|
2365
|
+
},
|
|
2366
|
+
],
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
private async handleExecuteAgent(args: any) {
|
|
2371
|
+
const result = await this.client.executeAgent(args.agent_id, args.input);
|
|
2372
|
+
return {
|
|
2373
|
+
content: [
|
|
2374
|
+
{
|
|
2375
|
+
type: 'text',
|
|
2376
|
+
text: JSON.stringify(result, null, 2),
|
|
2377
|
+
},
|
|
2378
|
+
],
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
private async handleDeployAgent(args: any) {
|
|
2383
|
+
// Auto-populate runtime_image from the agent's MongoDB record so that new
|
|
2384
|
+
// deployments always use the agent-type-pinned image rather than :latest.
|
|
2385
|
+
// The caller may still pass an explicit image in deployment_config.image to
|
|
2386
|
+
// override (e.g. for testing a canary build).
|
|
2387
|
+
const deploymentConfig = { ...(args.deployment_config || {}) };
|
|
2388
|
+
|
|
2389
|
+
if (!deploymentConfig.image) {
|
|
2390
|
+
try {
|
|
2391
|
+
const agentDoc = await this.client.getAgent(args.agent_id);
|
|
2392
|
+
const pinnedImage = agentDoc?.data?.runtime_image || agentDoc?.runtime_image;
|
|
2393
|
+
if (pinnedImage) {
|
|
2394
|
+
deploymentConfig.image = pinnedImage;
|
|
2395
|
+
}
|
|
2396
|
+
} catch {
|
|
2397
|
+
// Non-fatal: provisioner will fall back to agent-runtime:latest
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
const result = await this.client.deployAgent(args.agent_id, deploymentConfig);
|
|
2402
|
+
|
|
2403
|
+
// Format response for better readability
|
|
2404
|
+
let formattedMessage = `ā
Agent Deployment Initiated\n\n`;
|
|
2405
|
+
formattedMessage += `š Deployment Details:\n`;
|
|
2406
|
+
formattedMessage += ` Agent: ${result.agent_name}\n`;
|
|
2407
|
+
formattedMessage += ` Deployment ID: ${result.deployment_id}\n`;
|
|
2408
|
+
formattedMessage += ` Status: ${result.status}\n`;
|
|
2409
|
+
formattedMessage += ` Namespace: ${result.namespace}\n`;
|
|
2410
|
+
formattedMessage += ` Replicas: ${result.replicas}\n`;
|
|
2411
|
+
formattedMessage += ` Created: ${result.created_at}\n\n`;
|
|
2412
|
+
if (deploymentConfig.image) {
|
|
2413
|
+
formattedMessage += ` Runtime Image: ${deploymentConfig.image}\n\n`;
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
formattedMessage += `ā±ļø ${result.message}\n\n`;
|
|
2417
|
+
|
|
2418
|
+
if (result.endpoint_url) {
|
|
2419
|
+
formattedMessage += `š Endpoint: ${result.endpoint_url}\n\n`;
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
if (result.dashboard_url) {
|
|
2423
|
+
formattedMessage += `š Dashboard: ${result.dashboard_url}\n\n`;
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
formattedMessage += `š Next Steps:\n`;
|
|
2427
|
+
formattedMessage += ` 1. Monitor deployment status in seller/admin dashboard\n`;
|
|
2428
|
+
formattedMessage += ` 2. Wait 2-3 minutes for pods to become ready\n`;
|
|
2429
|
+
formattedMessage += ` 3. Check endpoint health once deployed\n`;
|
|
2430
|
+
formattedMessage += ` 4. Configure custom domain (optional)\n\n`;
|
|
2431
|
+
|
|
2432
|
+
formattedMessage += `š” View deployment in dashboard:\n`;
|
|
2433
|
+
formattedMessage += ` https://agents.chatslytics.com/seller/instances\n\n`;
|
|
2434
|
+
|
|
2435
|
+
formattedMessage += `Raw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2436
|
+
|
|
2437
|
+
return {
|
|
2438
|
+
content: [
|
|
2439
|
+
{
|
|
2440
|
+
type: 'text',
|
|
2441
|
+
text: formattedMessage,
|
|
2442
|
+
},
|
|
2443
|
+
],
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
private async handleRedeployAgent(args: any) {
|
|
2448
|
+
const body = args.runtime_image ? { runtime_image: args.runtime_image } : {};
|
|
2449
|
+
const result = await this.client.redeployAgent(args.agent_id, body);
|
|
2450
|
+
|
|
2451
|
+
let msg = `š Agent Redeployment Triggered\n\n`;
|
|
2452
|
+
msg += `š Details:\n`;
|
|
2453
|
+
msg += ` Agent: ${result.agent_name}\n`;
|
|
2454
|
+
msg += ` Image: ${result.runtime_image || '(unchanged)'}\n`;
|
|
2455
|
+
msg += ` Subscription: ${result.subscription_id}\n`;
|
|
2456
|
+
msg += ` Dashboard: ${result.dashboard_url}\n\n`;
|
|
2457
|
+
msg += `ā±ļø ${result.message}\n\n`;
|
|
2458
|
+
msg += `š” What was updated:\n`;
|
|
2459
|
+
msg += ` ⢠ConfigMap patched with latest agent graph from MongoDB\n`;
|
|
2460
|
+
msg += ` ⢠Pod rolling restart triggered\n`;
|
|
2461
|
+
if (args.runtime_image) {
|
|
2462
|
+
msg += ` ⢠Container image updated to: ${args.runtime_image}\n`;
|
|
2463
|
+
}
|
|
2464
|
+
msg += `\nš Monitor pod status:\n`;
|
|
2465
|
+
msg += ` kubectl get pods -n default | grep ${result.subscription_id?.slice(-8) || 'agent'}\n`;
|
|
2466
|
+
|
|
2467
|
+
return { content: [{ type: 'text', text: msg }] };
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
private async handleGetMcpPlanLimits() {
|
|
2471
|
+
const result = await this.client.getMcpPlanLimits();
|
|
2472
|
+
|
|
2473
|
+
const agentBar = result.agents.limit === 'unlimited'
|
|
2474
|
+
? `${result.agents.used} used (unlimited)`
|
|
2475
|
+
: `${result.agents.used}/${result.agents.limit} used (${result.agents.remaining} remaining)`;
|
|
2476
|
+
|
|
2477
|
+
const podBar = result.deployments.limit === 'unlimited'
|
|
2478
|
+
? `${result.deployments.used} active (unlimited)`
|
|
2479
|
+
: `${result.deployments.used}/${result.deployments.limit} active (${result.deployments.remaining} remaining)`;
|
|
2480
|
+
|
|
2481
|
+
let msg = `š MCP Plan Limits ā ${result.plan} Plan\n\n`;
|
|
2482
|
+
msg += `š¤ Agents: ${agentBar}\n`;
|
|
2483
|
+
msg += `š Active Pods: ${podBar}\n`;
|
|
2484
|
+
msg += `š§ Deploy: ${result.deployments.can_deploy ? 'ā
Allowed' : 'ā Upgrade required'}\n\n`;
|
|
2485
|
+
|
|
2486
|
+
if (!result.deployments.can_deploy) {
|
|
2487
|
+
msg += `ā ļø Deployment requires Basic plan or higher.\n`;
|
|
2488
|
+
msg += ` Upgrade at: https://agents.chatslytics.com/settings/billing\n`;
|
|
2489
|
+
} else if (result.deployments.remaining === 0 && result.deployments.limit !== 'unlimited') {
|
|
2490
|
+
msg += `ā ļø Active pod limit reached. Deprovision an agent or upgrade your plan.\n`;
|
|
2491
|
+
} else if (result.agents.remaining === 0 && result.agents.limit !== 'unlimited') {
|
|
2492
|
+
msg += `ā ļø Agent creation limit reached. Upgrade your plan to create more agents.\n`;
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
return { content: [{ type: 'text', text: msg }] };
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
// ==========================================
|
|
2499
|
+
// AGENT MANAGEMENT HANDLERS
|
|
2500
|
+
// ==========================================
|
|
2501
|
+
|
|
2502
|
+
private async handleCreateAgent(args: any) {
|
|
2503
|
+
const result = await this.client.createAgent(args);
|
|
2504
|
+
|
|
2505
|
+
let message = `ā
Agent Created Successfully\n\n`;
|
|
2506
|
+
message += `š Agent Details:\n`;
|
|
2507
|
+
message += ` Name: ${result.name}\n`;
|
|
2508
|
+
message += ` ID: ${result.id}\n`;
|
|
2509
|
+
message += ` Slug: ${result.slug}\n`;
|
|
2510
|
+
message += ` Type: ${result.type}\n`;
|
|
2511
|
+
message += ` Category: ${result.category}\n`;
|
|
2512
|
+
message += ` Status: ${result.status}\n\n`;
|
|
2513
|
+
|
|
2514
|
+
message += `š Next Steps:\n`;
|
|
2515
|
+
message += ` 1. Set wizard configuration: set_agent_wizard_config\n`;
|
|
2516
|
+
message += ` 2. Define agent graph: set_agent_graph\n`;
|
|
2517
|
+
message += ` 3. Validate agent: validate_agent_configuration\n`;
|
|
2518
|
+
message += ` 4. Publish agent: publish_agent\n\n`;
|
|
2519
|
+
|
|
2520
|
+
message += `Raw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2521
|
+
|
|
2522
|
+
return {
|
|
2523
|
+
content: [{ type: 'text', text: message }],
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
private async handleUpdateAgent(args: any) {
|
|
2528
|
+
const result = await this.client.updateAgent(args.agent_id, args.updates);
|
|
2529
|
+
return {
|
|
2530
|
+
content: [{
|
|
2531
|
+
type: 'text',
|
|
2532
|
+
text: `ā
Agent Updated Successfully\n\n${JSON.stringify(result, null, 2)}`
|
|
2533
|
+
}],
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
private async handleSetAgentWizardConfig(args: any) {
|
|
2538
|
+
const result = await this.client.setAgentWizardConfig(
|
|
2539
|
+
args.agent_id,
|
|
2540
|
+
args.wizard_configuration
|
|
2541
|
+
);
|
|
2542
|
+
|
|
2543
|
+
let message = `ā
Wizard Configuration Set Successfully\n\n`;
|
|
2544
|
+
message += `š Configuration Details:\n`;
|
|
2545
|
+
message += ` Agent ID: ${args.agent_id}\n`;
|
|
2546
|
+
|
|
2547
|
+
const config = args.wizard_configuration;
|
|
2548
|
+
if (config.required_oauth_providers?.length > 0) {
|
|
2549
|
+
message += ` OAuth Providers: ${config.required_oauth_providers.join(', ')}\n`;
|
|
2550
|
+
}
|
|
2551
|
+
if (config.required_credentials?.length > 0) {
|
|
2552
|
+
message += ` Credentials: ${config.required_credentials.map((c: any) => c.key).join(', ')}\n`;
|
|
2553
|
+
}
|
|
2554
|
+
if (config.required_platform_config?.length > 0) {
|
|
2555
|
+
message += ` Platform Config Fields: ${config.required_platform_config.length}\n`;
|
|
2556
|
+
}
|
|
2557
|
+
if (config.custom_wizard_steps?.length > 0) {
|
|
2558
|
+
message += ` Custom Steps: ${config.custom_wizard_steps.length}\n`;
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
message += `\nš Next Step: Define agent graph with set_agent_graph\n\n`;
|
|
2562
|
+
message += `Raw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2563
|
+
|
|
2564
|
+
return {
|
|
2565
|
+
content: [{ type: 'text', text: message }],
|
|
2566
|
+
};
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
private async handleSetAgentGraph(args: any) {
|
|
2570
|
+
const result = await this.client.setAgentGraph(args.agent_id, args.graph);
|
|
2571
|
+
|
|
2572
|
+
let message = `ā
Agent Graph Set Successfully\n\n`;
|
|
2573
|
+
message += `š Graph Details:\n`;
|
|
2574
|
+
message += ` Agent ID: ${args.agent_id}\n`;
|
|
2575
|
+
|
|
2576
|
+
const graph = args.graph;
|
|
2577
|
+
if (graph.nodes) {
|
|
2578
|
+
message += ` Nodes: ${graph.nodes.length}\n`;
|
|
2579
|
+
message += ` Node Types: ${graph.nodes.map((n: any) => n.type).join(', ')}\n`;
|
|
2580
|
+
}
|
|
2581
|
+
if (graph.edges) {
|
|
2582
|
+
message += ` Edges: ${graph.edges.length}\n`;
|
|
2583
|
+
}
|
|
2584
|
+
if (graph.entry_point) {
|
|
2585
|
+
message += ` Entry Point: ${graph.entry_point}\n`;
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
message += `\nš Next Steps:\n`;
|
|
2589
|
+
message += ` 1. Validate agent: validate_agent_configuration\n`;
|
|
2590
|
+
message += ` 2. Publish agent: publish_agent\n\n`;
|
|
2591
|
+
|
|
2592
|
+
message += `Raw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2593
|
+
|
|
2594
|
+
return {
|
|
2595
|
+
content: [{ type: 'text', text: message }],
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
private async handleValidateAgentConfiguration(args: any) {
|
|
2600
|
+
const result = await this.client.validateAgentConfiguration(args.agent_id);
|
|
2601
|
+
|
|
2602
|
+
let message = result.valid
|
|
2603
|
+
? `ā
Agent Validation Passed\n\n`
|
|
2604
|
+
: `ā Agent Validation Failed\n\n`;
|
|
2605
|
+
|
|
2606
|
+
message += `š Validation Checklist:\n`;
|
|
2607
|
+
for (const [check, passed] of Object.entries(result.checklist || {})) {
|
|
2608
|
+
message += ` ${passed ? 'ā
' : 'ā'} ${check}\n`;
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2611
|
+
if (result.errors && result.errors.length > 0) {
|
|
2612
|
+
message += `\nšØ Errors:\n`;
|
|
2613
|
+
result.errors.forEach((error: string) => {
|
|
2614
|
+
message += ` ⢠${error}\n`;
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
2619
|
+
message += `\nā ļø Warnings:\n`;
|
|
2620
|
+
result.warnings.forEach((warning: string) => {
|
|
2621
|
+
message += ` ⢠${warning}\n`;
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
if (result.valid) {
|
|
2626
|
+
message += `\nš Next Step: Publish agent with publish_agent\n`;
|
|
2627
|
+
} else {
|
|
2628
|
+
message += `\nš Fix the errors above and run validation again\n`;
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
message += `\nRaw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2632
|
+
|
|
2633
|
+
return {
|
|
2634
|
+
content: [{ type: 'text', text: message }],
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
private async handlePublishAgent(args: any) {
|
|
2639
|
+
const result = await this.client.publishAgent(args.agent_id);
|
|
2640
|
+
|
|
2641
|
+
let message = `ā
${result.message}\n\n`;
|
|
2642
|
+
message += `š Publication Details:\n`;
|
|
2643
|
+
message += ` Agent: ${result.agent_name}\n`;
|
|
2644
|
+
message += ` Agent ID: ${result.agent_id}\n`;
|
|
2645
|
+
message += ` Status: ${result.status}\n`;
|
|
2646
|
+
|
|
2647
|
+
if (result.marketplace_url) {
|
|
2648
|
+
message += ` Marketplace URL: ${result.marketplace_url}\n`;
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
if (result.status === 'pending_review') {
|
|
2652
|
+
message += `\nā³ Your agent is now pending review by admins.\n`;
|
|
2653
|
+
message += `You'll be notified once it's approved and appears in the marketplace.\n`;
|
|
2654
|
+
} else {
|
|
2655
|
+
message += `\nš Your agent is now live in the marketplace!\n`;
|
|
2656
|
+
message += `Users can now subscribe and deploy your agent.\n`;
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
message += `\nRaw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2660
|
+
|
|
2661
|
+
return {
|
|
2662
|
+
content: [{ type: 'text', text: message }],
|
|
2663
|
+
};
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
// ==========================================
|
|
2667
|
+
// DASHBOARD HANDLERS
|
|
2668
|
+
// ==========================================
|
|
2669
|
+
|
|
2670
|
+
private async handleCreateAgentDashboard(args: any) {
|
|
2671
|
+
const config: any = {};
|
|
2672
|
+
if (args.template_type) config.template_type = args.template_type;
|
|
2673
|
+
if (args.layout) config.layout = args.layout;
|
|
2674
|
+
if (args.navigation) config.navigation = args.navigation;
|
|
2675
|
+
if (args.widgets) config.widgets = args.widgets;
|
|
2676
|
+
if (args.tabs) config.tabs = args.tabs;
|
|
2677
|
+
if (args.theme) config.theme = args.theme;
|
|
2678
|
+
if (args.branding) config.branding = args.branding;
|
|
2679
|
+
|
|
2680
|
+
const result = await this.client.createAgentDashboard(args.agent_id, config);
|
|
2681
|
+
|
|
2682
|
+
let message = `ā
Dashboard Created for Agent: ${args.agent_id}\n\n`;
|
|
2683
|
+
message += `š Configuration:\n`;
|
|
2684
|
+
message += ` Template: ${args.template_type || 'default'}\n`;
|
|
2685
|
+
message += ` Layout: ${args.layout || 'standard'}\n`;
|
|
2686
|
+
message += ` Navigation: ${args.navigation || 'sidebar'}\n`;
|
|
2687
|
+
if (args.tabs) {
|
|
2688
|
+
message += ` Tabs: ${args.tabs.map((t: any) => t.label).join(', ')}\n`;
|
|
2689
|
+
} else {
|
|
2690
|
+
message += ` Widgets: ${args.widgets?.join(', ') || 'default set'}\n`;
|
|
2691
|
+
}
|
|
2692
|
+
if (args.branding?.brand_name) {
|
|
2693
|
+
message += ` Brand: ${args.branding.brand_name}\n`;
|
|
2694
|
+
}
|
|
2695
|
+
message += `\nš Dashboard URL: Will be available after subscription deployment\n`;
|
|
2696
|
+
message += ` Format: https://{subscription_id}.agents.chatslytics.com\n\n`;
|
|
2697
|
+
message += `Raw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2698
|
+
|
|
2699
|
+
return {
|
|
2700
|
+
content: [{ type: 'text', text: message }],
|
|
2701
|
+
};
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
private async handleGetAgentDashboardConfig(args: any) {
|
|
2705
|
+
const result = await this.client.getAgentDashboardConfig(args.agent_id);
|
|
2706
|
+
return {
|
|
2707
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
2708
|
+
};
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
private async handleUpdateAgentDashboard(args: any) {
|
|
2712
|
+
const result = await this.client.updateAgentDashboard(args.agent_id, args.updates);
|
|
2713
|
+
return {
|
|
2714
|
+
content: [{ type: 'text', text: `ā
Dashboard updated successfully\n\n${JSON.stringify(result, null, 2)}` }],
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
private async handleAddDashboardWidget(args: any) {
|
|
2719
|
+
const result = await this.client.addDashboardWidget(args.agent_id, {
|
|
2720
|
+
widget_type: args.widget_type,
|
|
2721
|
+
title: args.title,
|
|
2722
|
+
position: args.position,
|
|
2723
|
+
size: args.size,
|
|
2724
|
+
config: args.config,
|
|
2725
|
+
});
|
|
2726
|
+
|
|
2727
|
+
let message = `ā
Widget Added: ${args.title}\n\n`;
|
|
2728
|
+
message += `š Widget Details:\n`;
|
|
2729
|
+
message += ` Type: ${args.widget_type}\n`;
|
|
2730
|
+
message += ` Position: ${args.position ? `(${args.position.x}, ${args.position.y})` : 'auto'}\n`;
|
|
2731
|
+
message += ` Size: ${args.size ? `${args.size.width}x${args.size.height}` : 'default'}\n\n`;
|
|
2732
|
+
message += `Raw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2733
|
+
|
|
2734
|
+
return {
|
|
2735
|
+
content: [{ type: 'text', text: message }],
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
private async handleConfigureAgentPage(args: any) {
|
|
2740
|
+
const result = await this.client.configureAgentPage(args.agent_id, {
|
|
2741
|
+
page_type: args.page_type,
|
|
2742
|
+
content: args.content,
|
|
2743
|
+
});
|
|
2744
|
+
|
|
2745
|
+
return {
|
|
2746
|
+
content: [
|
|
2747
|
+
{
|
|
2748
|
+
type: 'text',
|
|
2749
|
+
text: `ā
${args.page_type.charAt(0).toUpperCase() + args.page_type.slice(1)} page configured successfully\n\n${JSON.stringify(result, null, 2)}`,
|
|
2750
|
+
},
|
|
2751
|
+
],
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
// ==========================================
|
|
2756
|
+
// WIDGET GENERATION HANDLERS
|
|
2757
|
+
// ==========================================
|
|
2758
|
+
|
|
2759
|
+
private async handleGenerateDashboardWidget(args: any) {
|
|
2760
|
+
const result = await this.client.generateWidget({
|
|
2761
|
+
agent_id: args.agent_id,
|
|
2762
|
+
widget_name: args.widget_name,
|
|
2763
|
+
source_code: args.source_code,
|
|
2764
|
+
description: args.description,
|
|
2765
|
+
});
|
|
2766
|
+
|
|
2767
|
+
// Auto-add the generated widget to the agent's dashboard config
|
|
2768
|
+
const widgetData = result.data || result;
|
|
2769
|
+
if (widgetData.build_status === 'success' && widgetData.cdn_url) {
|
|
2770
|
+
try {
|
|
2771
|
+
await this.client.addDashboardWidget(args.agent_id, {
|
|
2772
|
+
widget_type: 'custom',
|
|
2773
|
+
title: args.widget_name.replace(/_/g, ' ').replace(/\b\w/g, (c: string) => c.toUpperCase()),
|
|
2774
|
+
config: {
|
|
2775
|
+
type: `dynamic:${args.widget_name}`,
|
|
2776
|
+
cdn_url: widgetData.cdn_url,
|
|
2777
|
+
widget_name: args.widget_name,
|
|
2778
|
+
},
|
|
2779
|
+
});
|
|
2780
|
+
} catch (e: any) {
|
|
2781
|
+
// Non-fatal: widget is compiled but dashboard auto-add failed
|
|
2782
|
+
console.warn(`Auto-add to dashboard failed: ${e.message}`);
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
let message = `ā
Custom Widget Compiled & Deployed: ${args.widget_name}\n\n`;
|
|
2787
|
+
message += `š¦ Build Details:\n`;
|
|
2788
|
+
message += ` Status: ${widgetData.build_status}\n`;
|
|
2789
|
+
message += ` CDN URL: ${widgetData.cdn_url || 'N/A'}\n`;
|
|
2790
|
+
message += ` Bundle Size: ${widgetData.bundle_size_bytes ? `${(widgetData.bundle_size_bytes / 1024).toFixed(1)} KB` : 'N/A'}\n`;
|
|
2791
|
+
message += ` Version: ${widgetData.version || 1}\n\n`;
|
|
2792
|
+
message += `š Dashboard Integration:\n`;
|
|
2793
|
+
message += ` Widget type: dynamic:${args.widget_name}\n`;
|
|
2794
|
+
message += ` The widget has been automatically added to the agent's dashboard.\n\n`;
|
|
2795
|
+
message += `Raw Response:\n${JSON.stringify(widgetData, null, 2)}`;
|
|
2796
|
+
|
|
2797
|
+
return {
|
|
2798
|
+
content: [{ type: 'text', text: message }],
|
|
2799
|
+
};
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
private async handleListGeneratedWidgets(args: any) {
|
|
2803
|
+
const result = await this.client.listGeneratedWidgets(args.agent_id);
|
|
2804
|
+
const widgets = result.widgets || result.data?.widgets || [];
|
|
2805
|
+
|
|
2806
|
+
if (widgets.length === 0) {
|
|
2807
|
+
return {
|
|
2808
|
+
content: [{ type: 'text', text: `No generated widgets found for agent ${args.agent_id}` }],
|
|
2809
|
+
};
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
let message = `š¦ Generated Widgets for Agent ${args.agent_id}:\n\n`;
|
|
2813
|
+
for (const w of widgets) {
|
|
2814
|
+
message += ` ⢠${w.widget_id} (v${w.version}) ā ${w.build_status}\n`;
|
|
2815
|
+
message += ` CDN: ${w.cdn_url || 'N/A'}\n`;
|
|
2816
|
+
message += ` Prompt: "${(w.prompt || '').substring(0, 80)}${(w.prompt || '').length > 80 ? '...' : ''}"\n\n`;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
return {
|
|
2820
|
+
content: [{ type: 'text', text: message }],
|
|
2821
|
+
};
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
private async handleRegenerateWidget(args: any) {
|
|
2825
|
+
const result = await this.client.regenerateWidget(args.widget_id, args.agent_id, {
|
|
2826
|
+
source_code: args.source_code,
|
|
2827
|
+
description: args.description,
|
|
2828
|
+
});
|
|
2829
|
+
|
|
2830
|
+
const widgetData = result.data || result;
|
|
2831
|
+
|
|
2832
|
+
let message = `š Widget Regenerated: ${args.widget_id}\n\n`;
|
|
2833
|
+
message += ` New Version: ${widgetData.version}\n`;
|
|
2834
|
+
message += ` CDN URL: ${widgetData.cdn_url || 'N/A'}\n`;
|
|
2835
|
+
message += ` Bundle Size: ${widgetData.bundle_size_bytes ? `${(widgetData.bundle_size_bytes / 1024).toFixed(1)} KB` : 'N/A'}\n`;
|
|
2836
|
+
message += ` Status: ${widgetData.build_status}\n\n`;
|
|
2837
|
+
message += `Raw Response:\n${JSON.stringify(widgetData, null, 2)}`;
|
|
2838
|
+
|
|
2839
|
+
return {
|
|
2840
|
+
content: [{ type: 'text', text: message }],
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
private async handleDeleteGeneratedWidget(args: any) {
|
|
2845
|
+
const result = await this.client.deleteGeneratedWidget(args.widget_id, args.agent_id);
|
|
2846
|
+
|
|
2847
|
+
return {
|
|
2848
|
+
content: [
|
|
2849
|
+
{
|
|
2850
|
+
type: 'text',
|
|
2851
|
+
text: `šļø Widget "${args.widget_id}" deleted successfully.\n\nNote: CDN bundles are retained (immutable). The widget will no longer appear in dashboard configurations.\n\n${JSON.stringify(result, null, 2)}`,
|
|
2852
|
+
},
|
|
2853
|
+
],
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
// ==========================================
|
|
2858
|
+
// AGENT COMPOSITION HANDLERS
|
|
2859
|
+
// ==========================================
|
|
2860
|
+
|
|
2861
|
+
private async handleComposeAgents(args: any) {
|
|
2862
|
+
// Validate sub_agent_ids count
|
|
2863
|
+
const subAgentIds = args.sub_agent_ids || [];
|
|
2864
|
+
if (subAgentIds.length < 2 || subAgentIds.length > 10) {
|
|
2865
|
+
return {
|
|
2866
|
+
content: [{
|
|
2867
|
+
type: 'text',
|
|
2868
|
+
text: 'ā ļø Composition requires between 2 and 10 sub-agents. ' +
|
|
2869
|
+
`You provided ${subAgentIds.length}.`,
|
|
2870
|
+
}],
|
|
2871
|
+
isError: true,
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
const result = await this.client.composeAgents({
|
|
2876
|
+
name: args.name,
|
|
2877
|
+
description: args.description,
|
|
2878
|
+
sub_agent_ids: subAgentIds,
|
|
2879
|
+
composition_type: args.composition_type,
|
|
2880
|
+
routing_config: args.routing_config,
|
|
2881
|
+
state_mapping: args.state_mapping,
|
|
2882
|
+
aggregation_strategy: args.aggregation_strategy,
|
|
2883
|
+
});
|
|
2884
|
+
|
|
2885
|
+
let message = `ā
Composed Agent Created Successfully\n\n`;
|
|
2886
|
+
message += `š Composition Details:\n`;
|
|
2887
|
+
message += ` Name: ${args.name}\n`;
|
|
2888
|
+
message += ` Type: ${args.composition_type}\n`;
|
|
2889
|
+
message += ` Sub-Agents: ${subAgentIds.length}\n`;
|
|
2890
|
+
|
|
2891
|
+
if (result.data?.id) {
|
|
2892
|
+
message += ` Composed Agent ID: ${result.data.id}\n`;
|
|
2893
|
+
}
|
|
2894
|
+
if (result.data?.pricing) {
|
|
2895
|
+
const p = result.data.pricing;
|
|
2896
|
+
message += `\nš° Bundle Pricing:\n`;
|
|
2897
|
+
if (p.base_price !== undefined) message += ` Base Price: ${p.currency || 'INR'} ${p.base_price}\n`;
|
|
2898
|
+
if (p.bundle_discount !== undefined) message += ` Bundle Discount: ${p.bundle_discount}%\n`;
|
|
2899
|
+
if (p.final_price !== undefined) message += ` Final Price: ${p.currency || 'INR'} ${p.final_price}\n`;
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
message += `\nš Next Steps:\n`;
|
|
2903
|
+
message += ` 1. Execute: execute_composed_agent\n`;
|
|
2904
|
+
message += ` 2. Preview: get_composed_agent for full details\n`;
|
|
2905
|
+
message += `\nRaw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2906
|
+
|
|
2907
|
+
return {
|
|
2908
|
+
content: [{ type: 'text', text: message }],
|
|
2909
|
+
};
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
private async handleListComposedAgents(args: any) {
|
|
2913
|
+
const result = await this.client.listComposedAgents({
|
|
2914
|
+
page: args?.page,
|
|
2915
|
+
limit: args?.limit,
|
|
2916
|
+
});
|
|
2917
|
+
|
|
2918
|
+
let message = `## Composed Agents\n\n`;
|
|
2919
|
+
|
|
2920
|
+
const agents = result.data?.agents || result.data || [];
|
|
2921
|
+
if (Array.isArray(agents) && agents.length > 0) {
|
|
2922
|
+
agents.forEach((agent: any) => {
|
|
2923
|
+
message += `### ${agent.name}\n`;
|
|
2924
|
+
message += `- **ID**: ${agent.id || agent._id}\n`;
|
|
2925
|
+
message += `- **Type**: ${agent.composition_type}\n`;
|
|
2926
|
+
message += `- **Sub-Agents**: ${agent.sub_agent_ids?.length || 0}\n`;
|
|
2927
|
+
message += `- **Status**: ${agent.status || 'active'}\n\n`;
|
|
2928
|
+
});
|
|
2929
|
+
} else {
|
|
2930
|
+
message += `No composed agents found.\n`;
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
message += `\nRaw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2934
|
+
|
|
2935
|
+
return {
|
|
2936
|
+
content: [{ type: 'text', text: message }],
|
|
2937
|
+
};
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
private async handleGetComposedAgent(args: any) {
|
|
2941
|
+
const result = await this.client.getComposedAgent(args.agent_id);
|
|
2942
|
+
return {
|
|
2943
|
+
content: [{
|
|
2944
|
+
type: 'text',
|
|
2945
|
+
text: JSON.stringify(result, null, 2),
|
|
2946
|
+
}],
|
|
2947
|
+
};
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
private async handlePreviewComposition(args: any) {
|
|
2951
|
+
const result = await this.client.previewComposition({
|
|
2952
|
+
sub_agent_ids: args.sub_agent_ids,
|
|
2953
|
+
composition_type: args.composition_type,
|
|
2954
|
+
});
|
|
2955
|
+
|
|
2956
|
+
let message = `## Composition Preview\n\n`;
|
|
2957
|
+
message += `**Type**: ${args.composition_type}\n`;
|
|
2958
|
+
message += `**Sub-Agents**: ${args.sub_agent_ids.length}\n\n`;
|
|
2959
|
+
|
|
2960
|
+
const data = result.data || result;
|
|
2961
|
+
|
|
2962
|
+
// Pricing breakdown
|
|
2963
|
+
if (data.pricing) {
|
|
2964
|
+
const p = data.pricing;
|
|
2965
|
+
message += `### š° Pricing Breakdown\n`;
|
|
2966
|
+
if (p.individual_prices && Array.isArray(p.individual_prices)) {
|
|
2967
|
+
p.individual_prices.forEach((ip: any) => {
|
|
2968
|
+
message += `- ${ip.name}: ${ip.currency || 'INR'} ${ip.price}\n`;
|
|
2969
|
+
});
|
|
2970
|
+
}
|
|
2971
|
+
if (p.base_price !== undefined) message += `\n**Base Total**: ${p.currency || 'INR'} ${p.base_price}\n`;
|
|
2972
|
+
if (p.bundle_discount !== undefined) message += `**Bundle Discount**: ${p.bundle_discount}%\n`;
|
|
2973
|
+
if (p.final_price !== undefined) message += `**Final Price**: ${p.currency || 'INR'} ${p.final_price}\n`;
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2976
|
+
// Execution flow
|
|
2977
|
+
if (data.execution_flow) {
|
|
2978
|
+
message += `\n### š Execution Flow\n`;
|
|
2979
|
+
message += `${data.execution_flow}\n`;
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
message += `\nš To create this composition, use compose_agents with the same parameters.\n`;
|
|
2983
|
+
message += `\nRaw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
2984
|
+
|
|
2985
|
+
return {
|
|
2986
|
+
content: [{ type: 'text', text: message }],
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
private async handleExecuteComposedAgent(args: any) {
|
|
2991
|
+
const result = await this.client.executeComposedAgent(args.agent_id, args.input_data);
|
|
2992
|
+
|
|
2993
|
+
let message = `## Composed Agent Execution\n\n`;
|
|
2994
|
+
|
|
2995
|
+
const data = result.data || result;
|
|
2996
|
+
if (data.success !== undefined) {
|
|
2997
|
+
message += `**Status**: ${data.success ? 'ā
Success' : 'ā Failed'}\n`;
|
|
2998
|
+
}
|
|
2999
|
+
if (data.execution_id) {
|
|
3000
|
+
message += `**Execution ID**: ${data.execution_id}\n`;
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
if (data.output) {
|
|
3004
|
+
message += `\n### Output\n\`\`\`json\n${JSON.stringify(data.output, null, 2)}\n\`\`\`\n`;
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
if (data.metadata) {
|
|
3008
|
+
message += `\n### Metadata\n\`\`\`json\n${JSON.stringify(data.metadata, null, 2)}\n\`\`\`\n`;
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
if (data.error) {
|
|
3012
|
+
message += `\n### Error\n${data.error}\n`;
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
message += `\nRaw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
3016
|
+
|
|
3017
|
+
return {
|
|
3018
|
+
content: [{ type: 'text', text: message }],
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
private async handleListAgentTypes(args: any) {
|
|
3023
|
+
const result = await this.client.listAgentTypes();
|
|
3024
|
+
|
|
3025
|
+
let message = `## Available Agent Type Templates\n\n`;
|
|
3026
|
+
message += `These are pre-built templates that can be used as starting points for creating agents.\n\n`;
|
|
3027
|
+
|
|
3028
|
+
const types = result.data?.agent_types || result.agent_types || result.data || [];
|
|
3029
|
+
if (Array.isArray(types) && types.length > 0) {
|
|
3030
|
+
types.forEach((t: any) => {
|
|
3031
|
+
message += `### ${t.display_name || t.name}\n`;
|
|
3032
|
+
message += `- **Slug**: ${t.type_slug || t.slug}\n`;
|
|
3033
|
+
message += `- **Category**: ${t.category}\n`;
|
|
3034
|
+
message += `- **Description**: ${t.description}\n`;
|
|
3035
|
+
if (t.platform_requirements) {
|
|
3036
|
+
message += `- **Platforms**: ${JSON.stringify(t.platform_requirements)}\n`;
|
|
3037
|
+
}
|
|
3038
|
+
if (t.default_pricing) {
|
|
3039
|
+
message += `- **Default Pricing**: ${JSON.stringify(t.default_pricing)}\n`;
|
|
3040
|
+
}
|
|
3041
|
+
message += `\n`;
|
|
3042
|
+
});
|
|
3043
|
+
} else {
|
|
3044
|
+
message += `No agent type templates found.\n`;
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
message += `\nš Use create_agent or clone_agent to start from a template.\n`;
|
|
3048
|
+
message += `\nRaw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
3049
|
+
|
|
3050
|
+
return {
|
|
3051
|
+
content: [{ type: 'text', text: message }],
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
private async handleCloneAgent(args: any) {
|
|
3056
|
+
const result = await this.client.cloneAgent(args.agent_id, args.new_name);
|
|
3057
|
+
|
|
3058
|
+
let message = `ā
Agent Cloned Successfully\n\n`;
|
|
3059
|
+
message += `š Clone Details:\n`;
|
|
3060
|
+
message += ` Original Agent: ${args.agent_id}\n`;
|
|
3061
|
+
message += ` New Name: ${args.new_name}\n`;
|
|
3062
|
+
|
|
3063
|
+
const data = result.data || result;
|
|
3064
|
+
if (data.id) message += ` New Agent ID: ${data.id}\n`;
|
|
3065
|
+
if (data.slug) message += ` Slug: ${data.slug}\n`;
|
|
3066
|
+
message += ` Status: draft\n\n`;
|
|
3067
|
+
|
|
3068
|
+
message += `š Next Steps:\n`;
|
|
3069
|
+
message += ` 1. Customize wizard: set_agent_wizard_config\n`;
|
|
3070
|
+
message += ` 2. Modify graph: set_agent_graph\n`;
|
|
3071
|
+
message += ` 3. Update pricing: update_agent\n`;
|
|
3072
|
+
message += ` 4. Validate: validate_agent_configuration\n`;
|
|
3073
|
+
message += ` 5. Publish: publish_agent\n\n`;
|
|
3074
|
+
|
|
3075
|
+
message += `Raw Response:\n${JSON.stringify(result, null, 2)}`;
|
|
3076
|
+
|
|
3077
|
+
return {
|
|
3078
|
+
content: [{ type: 'text', text: message }],
|
|
3079
|
+
};
|
|
3080
|
+
}
|
|
3081
|
+
|
|
3082
|
+
async run() {
|
|
3083
|
+
// If no auth, prompt browser login before starting
|
|
3084
|
+
if (!this.client.isAuthenticated()) {
|
|
3085
|
+
console.error('[MCP] No credentials found. Starting login flow...');
|
|
3086
|
+
try {
|
|
3087
|
+
const token = await promptLogin();
|
|
3088
|
+
this.client.setAuthToken(token);
|
|
3089
|
+
console.error('[MCP] Login successful. Starting server...');
|
|
3090
|
+
} catch (err: any) {
|
|
3091
|
+
console.error(`[MCP] Login failed: ${err.message}`);
|
|
3092
|
+
process.exit(1);
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
const transport = new StdioServerTransport();
|
|
3097
|
+
await this.server.connect(transport);
|
|
3098
|
+
console.error('AgentBuilder MCP Server running on stdio');
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
|
|
3102
|
+
// Start server
|
|
3103
|
+
const server = new AgentBuilderMCPServer();
|
|
3104
|
+
server.run().catch(console.error);
|