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