@agentlify/mcp-server 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts ADDED
@@ -0,0 +1,946 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Agentlify MCP Server
5
+ * Provides AI assistants with access to Agentlify features via Model Context Protocol
6
+ */
7
+
8
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
9
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
+ import {
11
+ CallToolRequestSchema,
12
+ ListResourcesRequestSchema,
13
+ ListToolsRequestSchema,
14
+ ReadResourceRequestSchema,
15
+ } from '@modelcontextprotocol/sdk/types.js';
16
+ import { readFile } from 'fs/promises';
17
+ import { join, dirname } from 'path';
18
+ import { fileURLToPath } from 'url';
19
+
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const __dirname = dirname(__filename);
22
+
23
+ // Agentlify API base URL
24
+ const API_BASE_URL = 'https://agentlify.co/api/mcp';
25
+ const AGENT_API_URL = 'https://agentlify.co/api/agents';
26
+ const HTTP_TIMEOUT_MS = 60000;
27
+
28
+ // Get API key from environment
29
+ const API_KEY = process.env.AGENTLIFY_API_KEY;
30
+
31
+ if (!API_KEY) {
32
+ console.error('Error: AGENTLIFY_API_KEY environment variable is required');
33
+ process.exit(1);
34
+ }
35
+
36
+ /**
37
+ * Call an HTTP endpoint for MCP tools
38
+ */
39
+ async function callHttpEndpoint(endpointName: string, data: any): Promise<any> {
40
+ // Convert function name to API endpoint URL
41
+ // "mcpGetBalanceHttp" -> "get-balance"
42
+ const apiEndpoint = endpointName
43
+ .replace('Http', '') // Remove "Http" suffix
44
+ .replace(/^mcp/, '') // Remove "mcp" prefix
45
+ .replace(/([A-Z])/g, '-$1') // Add dashes before capitals
46
+ .toLowerCase() // Convert to lowercase
47
+ .replace(/^-/, ''); // Remove leading dash
48
+
49
+ const response = await fetch(`${API_BASE_URL}/${apiEndpoint}`, {
50
+ method: 'POST',
51
+ headers: {
52
+ 'Content-Type': 'application/json',
53
+ Authorization: `Bearer ${API_KEY}`,
54
+ },
55
+ body: JSON.stringify(data && typeof data === 'object' ? data : {}),
56
+ signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
57
+ });
58
+
59
+ if (!response.ok) {
60
+ const error = await response.text();
61
+ throw new Error(`HTTP call failed: ${error}`);
62
+ }
63
+
64
+ try {
65
+ return (await response.json()) as any;
66
+ } catch {
67
+ throw new Error(`HTTP call failed: invalid JSON response from ${apiEndpoint}`);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Read documentation file
73
+ */
74
+ async function readDoc(filename: string): Promise<string> {
75
+ const docPath = join(__dirname, '..', 'docs', filename);
76
+ return await readFile(docPath, 'utf-8');
77
+ }
78
+
79
+ /**
80
+ * Initialize MCP Server
81
+ */
82
+ const server = new Server(
83
+ {
84
+ name: 'agentlify',
85
+ version: '2.0.0',
86
+ },
87
+ {
88
+ capabilities: {
89
+ tools: {},
90
+ resources: {},
91
+ },
92
+ },
93
+ );
94
+
95
+ /**
96
+ * List available tools
97
+ */
98
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
99
+ return {
100
+ tools: [
101
+ {
102
+ name: 'create_router',
103
+ description:
104
+ 'Create a new AI model router with custom configuration. Returns router ID and sample code.',
105
+ inputSchema: {
106
+ type: 'object',
107
+ properties: {
108
+ name: {
109
+ type: 'string',
110
+ description: 'Router name (e.g., "my-chatbot")',
111
+ },
112
+ cost_weight: {
113
+ type: 'number',
114
+ description:
115
+ 'Weight for cost optimization (0-1, higher = prioritize cost savings)',
116
+ default: 0.4,
117
+ },
118
+ latency_weight: {
119
+ type: 'number',
120
+ description:
121
+ 'Weight for speed optimization (0-1, higher = prioritize low latency)',
122
+ default: 0.3,
123
+ },
124
+ quality_weight: {
125
+ type: 'number',
126
+ description:
127
+ 'Weight for quality optimization (0-1, higher = prioritize output quality)',
128
+ default: 0.2,
129
+ },
130
+ carbon_weight: {
131
+ type: 'number',
132
+ description:
133
+ 'Weight for carbon footprint optimization (0-1, higher = prioritize low carbon)',
134
+ default: 0.1,
135
+ },
136
+ models: {
137
+ type: 'array',
138
+ items: { type: 'string' },
139
+ description:
140
+ 'List of model IDs to include (empty array means all available models)',
141
+ default: [],
142
+ },
143
+ fallback_enabled: {
144
+ type: 'boolean',
145
+ description:
146
+ 'Enable automatic fallbacks if primary model fails (recommended: true)',
147
+ default: true,
148
+ },
149
+ budget_limit: {
150
+ type: 'number',
151
+ description: 'Optional maximum cost per token in USD',
152
+ },
153
+ enable_caching: {
154
+ type: 'boolean',
155
+ description:
156
+ 'Enable response caching to improve speed and reduce costs',
157
+ default: false,
158
+ },
159
+ enable_rate_limit: {
160
+ type: 'boolean',
161
+ description:
162
+ 'Enable rate limiting to protect against excessive usage',
163
+ default: false,
164
+ },
165
+ timeout: {
166
+ type: 'number',
167
+ description: 'Request timeout in seconds (5-300)',
168
+ default: 30,
169
+ },
170
+ rate_limit: {
171
+ type: 'number',
172
+ description:
173
+ 'Maximum requests per minute when rate limiting is enabled',
174
+ default: 100,
175
+ },
176
+ },
177
+ required: ['name'],
178
+ },
179
+ },
180
+ {
181
+ name: 'test_request',
182
+ description:
183
+ 'Test a router with a real request. Returns response, cost breakdown, latency, and model used.',
184
+ inputSchema: {
185
+ type: 'object',
186
+ properties: {
187
+ router_id: {
188
+ type: 'string',
189
+ description: 'Router ID to test',
190
+ },
191
+ messages: {
192
+ type: 'array',
193
+ items: {
194
+ type: 'object',
195
+ properties: {
196
+ role: {
197
+ type: 'string',
198
+ enum: ['system', 'user', 'assistant'],
199
+ },
200
+ content: { type: 'string' },
201
+ },
202
+ required: ['role', 'content'],
203
+ },
204
+ description: 'Chat messages to send',
205
+ },
206
+ parameters: {
207
+ type: 'object',
208
+ description:
209
+ 'Optional parameters (temperature, max_tokens, etc.)',
210
+ },
211
+ },
212
+ required: ['router_id', 'messages'],
213
+ },
214
+ },
215
+ {
216
+ name: 'get_usage_summary',
217
+ description:
218
+ 'Get usage statistics and costs for a time range. Returns total cost, requests, top models, and daily breakdown.',
219
+ inputSchema: {
220
+ type: 'object',
221
+ properties: {
222
+ time_range: {
223
+ type: 'string',
224
+ enum: ['24h', '7d', '30d'],
225
+ description: 'Time range for usage summary (default: 7d)',
226
+ },
227
+ },
228
+ },
229
+ },
230
+ {
231
+ name: 'list_models',
232
+ description:
233
+ 'List all available models with their pricing, capabilities, and providers.',
234
+ inputSchema: {
235
+ type: 'object',
236
+ properties: {},
237
+ },
238
+ },
239
+ {
240
+ name: 'list_routers',
241
+ description:
242
+ 'List all routers created by the user with their configuration and status.',
243
+ inputSchema: {
244
+ type: 'object',
245
+ properties: {},
246
+ },
247
+ },
248
+ {
249
+ name: 'get_balance',
250
+ description:
251
+ 'Check current credit balance, subscription status, and monthly usage.',
252
+ inputSchema: {
253
+ type: 'object',
254
+ properties: {},
255
+ },
256
+ },
257
+ {
258
+ name: 'list_agents',
259
+ description:
260
+ 'List all agents created by the user. Returns agent IDs, names, descriptions, and configuration summary.',
261
+ inputSchema: {
262
+ type: 'object',
263
+ properties: {
264
+ include_inactive: {
265
+ type: 'boolean',
266
+ description: 'Include disabled agents (default: false)',
267
+ default: false,
268
+ },
269
+ limit: {
270
+ type: 'number',
271
+ description: 'Maximum number of agents to return (default: 50, max: 100)',
272
+ default: 50,
273
+ },
274
+ start_after: {
275
+ type: 'string',
276
+ description:
277
+ 'Optional pagination cursor from a prior list_agents response',
278
+ },
279
+ },
280
+ },
281
+ },
282
+ {
283
+ name: 'get_agent',
284
+ description:
285
+ 'Get detailed configuration of a specific agent, including steps, skills, tools, and settings.',
286
+ inputSchema: {
287
+ type: 'object',
288
+ properties: {
289
+ agent_id: {
290
+ type: 'string',
291
+ description: 'The agent ID to retrieve',
292
+ },
293
+ },
294
+ required: ['agent_id'],
295
+ },
296
+ },
297
+ {
298
+ name: 'create_agent',
299
+ description:
300
+ 'Create a new agent with optional steps, skills, tools, and router assignment. Use display_name for human-friendly naming; name is optional slug override.',
301
+ inputSchema: {
302
+ type: 'object',
303
+ properties: {
304
+ name: {
305
+ type: 'string',
306
+ description:
307
+ 'Optional slug-style ID (e.g., "customer-support-agent"). If omitted, generated from display_name.',
308
+ },
309
+ display_name: {
310
+ type: 'string',
311
+ description: 'Human-readable name for the agent',
312
+ },
313
+ description: {
314
+ type: 'string',
315
+ description: 'Optional description of what the agent does',
316
+ },
317
+ router_id: {
318
+ type: 'string',
319
+ description:
320
+ 'Optional router ID to use for agent execution. If omitted, default routing is used.',
321
+ },
322
+ steps: {
323
+ type: 'array',
324
+ items: { type: 'object' },
325
+ description:
326
+ 'Optional workflow step definitions. If omitted, a default single step is created.',
327
+ },
328
+ skills: {
329
+ type: 'array',
330
+ items: { type: 'string' },
331
+ description: 'Optional list of skill IDs enabled for this agent',
332
+ },
333
+ builtin_tools: {
334
+ type: 'array',
335
+ items: { type: 'string' },
336
+ description:
337
+ 'Optional built-in tool IDs (e.g. web search, calculator, scraper)',
338
+ },
339
+ tools: {
340
+ type: 'array',
341
+ items: { type: 'object' },
342
+ description:
343
+ 'Optional custom tools in OpenAI function tool format with optional webhook metadata',
344
+ },
345
+ settings: {
346
+ type: 'object',
347
+ description:
348
+ 'Optional execution settings (planning_mode, timeout, tool backend, optimization flags)',
349
+ },
350
+ is_active: {
351
+ type: 'boolean',
352
+ description: 'Whether the agent should be active immediately',
353
+ default: true,
354
+ },
355
+ },
356
+ anyOf: [{ required: ['display_name'] }, { required: ['name'] }],
357
+ },
358
+ },
359
+ {
360
+ name: 'update_agent',
361
+ description:
362
+ 'Update an existing agent. Pass a partial updates object for safe patching, or provide top-level helper fields.',
363
+ inputSchema: {
364
+ type: 'object',
365
+ properties: {
366
+ agent_id: {
367
+ type: 'string',
368
+ description: 'Agent ID to update (accepts "agent:<id>" or plain ID)',
369
+ },
370
+ updates: {
371
+ type: 'object',
372
+ description:
373
+ 'Preferred: partial update payload (display_name, description, router_id, steps, skills, builtin_tools, tools, settings, is_active).',
374
+ },
375
+ expected_version: {
376
+ type: 'number',
377
+ description:
378
+ 'Optional optimistic-lock version. Update fails if current version differs.',
379
+ },
380
+ display_name: {
381
+ type: 'string',
382
+ description:
383
+ 'Optional shorthand when updates object is not provided',
384
+ },
385
+ description: {
386
+ type: 'string',
387
+ description:
388
+ 'Optional shorthand when updates object is not provided',
389
+ },
390
+ router_id: {
391
+ type: 'string',
392
+ description:
393
+ 'Optional shorthand when updates object is not provided',
394
+ },
395
+ steps: {
396
+ type: 'array',
397
+ items: { type: 'object' },
398
+ description:
399
+ 'Optional shorthand when updates object is not provided',
400
+ },
401
+ skills: {
402
+ type: 'array',
403
+ items: { type: 'string' },
404
+ description:
405
+ 'Optional shorthand when updates object is not provided',
406
+ },
407
+ builtin_tools: {
408
+ type: 'array',
409
+ items: { type: 'string' },
410
+ description:
411
+ 'Optional shorthand when updates object is not provided',
412
+ },
413
+ tools: {
414
+ type: 'array',
415
+ items: { type: 'object' },
416
+ description:
417
+ 'Optional shorthand when updates object is not provided',
418
+ },
419
+ settings: {
420
+ type: 'object',
421
+ description:
422
+ 'Optional shorthand when updates object is not provided',
423
+ },
424
+ is_active: {
425
+ type: 'boolean',
426
+ description:
427
+ 'Optional shorthand when updates object is not provided',
428
+ },
429
+ },
430
+ required: ['agent_id'],
431
+ },
432
+ },
433
+ {
434
+ name: 'delete_agent',
435
+ description:
436
+ 'Delete an agent permanently. Use carefully; this cannot be undone.',
437
+ inputSchema: {
438
+ type: 'object',
439
+ properties: {
440
+ agent_id: {
441
+ type: 'string',
442
+ description: 'Agent ID to delete (accepts "agent:<id>" or plain ID)',
443
+ },
444
+ },
445
+ required: ['agent_id'],
446
+ },
447
+ },
448
+ {
449
+ name: 'toggle_agent',
450
+ description:
451
+ 'Enable or disable an agent without deleting it.',
452
+ inputSchema: {
453
+ type: 'object',
454
+ properties: {
455
+ agent_id: {
456
+ type: 'string',
457
+ description: 'Agent ID to toggle (accepts "agent:<id>" or plain ID)',
458
+ },
459
+ is_active: {
460
+ type: 'boolean',
461
+ description: 'Target active state',
462
+ },
463
+ },
464
+ required: ['agent_id', 'is_active'],
465
+ },
466
+ },
467
+ {
468
+ name: 'duplicate_agent',
469
+ description:
470
+ 'Clone an existing agent, including steps, skills, and tools.',
471
+ inputSchema: {
472
+ type: 'object',
473
+ properties: {
474
+ agent_id: {
475
+ type: 'string',
476
+ description:
477
+ 'Source agent ID to duplicate (accepts "agent:<id>" or plain ID)',
478
+ },
479
+ new_display_name: {
480
+ type: 'string',
481
+ description:
482
+ 'Optional new display name for the copy. Default appends "(Copy)".',
483
+ },
484
+ },
485
+ required: ['agent_id'],
486
+ },
487
+ },
488
+ {
489
+ name: 'get_agent_usage',
490
+ description:
491
+ 'Get current agent usage count, plan limit, remaining slots, and plan name.',
492
+ inputSchema: {
493
+ type: 'object',
494
+ properties: {},
495
+ },
496
+ },
497
+ {
498
+ name: 'list_agent_templates',
499
+ description:
500
+ 'List built-in starter agent templates that can be forked into your account.',
501
+ inputSchema: {
502
+ type: 'object',
503
+ properties: {
504
+ category: {
505
+ type: 'string',
506
+ description: 'Optional template category filter',
507
+ },
508
+ limit: {
509
+ type: 'number',
510
+ description: 'Maximum templates to return (default: 50, max: 100)',
511
+ default: 50,
512
+ },
513
+ },
514
+ },
515
+ },
516
+ {
517
+ name: 'fork_agent_template',
518
+ description:
519
+ 'Create a new personal agent from a built-in template.',
520
+ inputSchema: {
521
+ type: 'object',
522
+ properties: {
523
+ template_id: {
524
+ type: 'string',
525
+ description: 'Template ID to fork',
526
+ },
527
+ new_display_name: {
528
+ type: 'string',
529
+ description: 'Optional display name override for the new agent',
530
+ },
531
+ },
532
+ required: ['template_id'],
533
+ },
534
+ },
535
+ {
536
+ name: 'run_agent',
537
+ description:
538
+ 'Execute an Agentlify agent with a message. The agent uses intelligent model routing, multi-step reasoning, and built-in skills. Returns the response, cost breakdown, and execution metadata.',
539
+ inputSchema: {
540
+ type: 'object',
541
+ properties: {
542
+ agent_id: {
543
+ type: 'string',
544
+ description:
545
+ 'The agent ID to run (e.g., "my-agent" or "agent:my-agent")',
546
+ },
547
+ messages: {
548
+ type: 'array',
549
+ items: {
550
+ type: 'object',
551
+ properties: {
552
+ role: {
553
+ type: 'string',
554
+ enum: ['system', 'user', 'assistant', 'tool'],
555
+ },
556
+ content: { type: 'string' },
557
+ tool_call_id: {
558
+ type: 'string',
559
+ description:
560
+ 'Required when role is "tool": tool call ID being resolved',
561
+ },
562
+ tool_calls: {
563
+ type: 'array',
564
+ description:
565
+ 'Assistant tool calls to preserve during tool-call continuation',
566
+ },
567
+ },
568
+ required: ['role', 'content'],
569
+ },
570
+ description: 'Chat messages to send to the agent',
571
+ },
572
+ tools: {
573
+ type: 'array',
574
+ items: { type: 'object' },
575
+ description:
576
+ 'Optional tools in OpenAI function calling format. If the agent needs to use these, the response will include tool_calls.',
577
+ },
578
+ },
579
+ required: ['agent_id', 'messages'],
580
+ },
581
+ },
582
+ {
583
+ name: 'list_agent_runs',
584
+ description:
585
+ 'Get execution history for a specific agent. Returns cost, latency, tokens, and status per run.',
586
+ inputSchema: {
587
+ type: 'object',
588
+ properties: {
589
+ agent_id: {
590
+ type: 'string',
591
+ description: 'The agent ID to get runs for',
592
+ },
593
+ limit: {
594
+ type: 'number',
595
+ description:
596
+ 'Maximum number of runs to return (default: 20, max: 100)',
597
+ default: 20,
598
+ },
599
+ status: {
600
+ type: 'string',
601
+ description:
602
+ 'Optional run status filter (e.g., "completed", "failed", "running")',
603
+ },
604
+ start_date: {
605
+ type: 'string',
606
+ description:
607
+ 'Optional ISO start date filter. Only runs started after this date are included.',
608
+ },
609
+ end_date: {
610
+ type: 'string',
611
+ description:
612
+ 'Optional ISO end date filter. Only runs started before this date are included.',
613
+ },
614
+ start_after: {
615
+ type: 'string',
616
+ description:
617
+ 'Optional pagination cursor (execution ID) to fetch runs after a previous page.',
618
+ },
619
+ },
620
+ required: ['agent_id'],
621
+ },
622
+ },
623
+ {
624
+ name: 'get_agent_run_detail',
625
+ description:
626
+ 'Get full details for a specific agent run, including step timeline, final output, and metrics.',
627
+ inputSchema: {
628
+ type: 'object',
629
+ properties: {
630
+ execution_id: {
631
+ type: 'string',
632
+ description: 'Execution ID returned from list_agent_runs',
633
+ },
634
+ },
635
+ required: ['execution_id'],
636
+ },
637
+ },
638
+ {
639
+ name: 'export_agent_runs',
640
+ description:
641
+ 'Export agent runs for analytics or debugging. Supports json, jsonl, and csv formats.',
642
+ inputSchema: {
643
+ type: 'object',
644
+ properties: {
645
+ execution_ids: {
646
+ type: 'array',
647
+ items: { type: 'string' },
648
+ description:
649
+ 'Optional list of specific run IDs to export. If omitted, exports by filters.',
650
+ },
651
+ format: {
652
+ type: 'string',
653
+ enum: ['json', 'jsonl', 'csv'],
654
+ description: 'Export format (default: json)',
655
+ default: 'json',
656
+ },
657
+ include_steps: {
658
+ type: 'boolean',
659
+ description:
660
+ 'Whether to include full step-level details in export payloads',
661
+ default: true,
662
+ },
663
+ agent_id: {
664
+ type: 'string',
665
+ description:
666
+ 'Optional agent filter when exporting by query rather than explicit IDs',
667
+ },
668
+ limit: {
669
+ type: 'number',
670
+ description:
671
+ 'Optional maximum records when exporting by query (default backend behavior applies)',
672
+ },
673
+ },
674
+ },
675
+ },
676
+ ],
677
+ };
678
+ });
679
+
680
+ /**
681
+ * Handle MCP Tool calls
682
+ */
683
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
684
+ const { name, arguments: args } = request.params;
685
+
686
+ try {
687
+ let result;
688
+
689
+ switch (name) {
690
+ case 'create_router':
691
+ result = await callHttpEndpoint('mcpCreateRouterHttp', args);
692
+ break;
693
+ case 'test_request':
694
+ result = await callHttpEndpoint('mcpTestRequestHttp', args);
695
+ break;
696
+ case 'get_usage_summary':
697
+ result = await callHttpEndpoint('mcpGetUsageSummaryHttp', args);
698
+ break;
699
+ case 'list_models':
700
+ result = await callHttpEndpoint('mcpListModelsHttp', args);
701
+ break;
702
+ case 'list_routers':
703
+ result = await callHttpEndpoint('mcpListRoutersHttp', args);
704
+ break;
705
+ case 'get_balance':
706
+ result = await callHttpEndpoint('mcpGetBalanceHttp', args);
707
+ break;
708
+ case 'list_agents':
709
+ result = await callHttpEndpoint('mcpListAgentsHttp', args);
710
+ break;
711
+ case 'get_agent':
712
+ result = await callHttpEndpoint('mcpGetAgentHttp', args);
713
+ break;
714
+ case 'create_agent':
715
+ result = await callHttpEndpoint('mcpCreateAgentHttp', args);
716
+ break;
717
+ case 'update_agent':
718
+ result = await callHttpEndpoint('mcpUpdateAgentHttp', args);
719
+ break;
720
+ case 'delete_agent':
721
+ result = await callHttpEndpoint('mcpDeleteAgentHttp', args);
722
+ break;
723
+ case 'toggle_agent':
724
+ result = await callHttpEndpoint('mcpToggleAgentHttp', args);
725
+ break;
726
+ case 'duplicate_agent':
727
+ result = await callHttpEndpoint('mcpDuplicateAgentHttp', args);
728
+ break;
729
+ case 'get_agent_usage':
730
+ result = await callHttpEndpoint('mcpGetAgentUsageHttp', args);
731
+ break;
732
+ case 'list_agent_templates':
733
+ result = await callHttpEndpoint('mcpListAgentTemplatesHttp', args);
734
+ break;
735
+ case 'fork_agent_template':
736
+ result = await callHttpEndpoint('mcpForkAgentTemplateHttp', args);
737
+ break;
738
+ case 'run_agent': {
739
+ const {
740
+ agent_id: agentId,
741
+ messages: agentMessages,
742
+ tools: agentTools,
743
+ } = args as any;
744
+ const agentStartTime = Date.now();
745
+
746
+ const agentResponse = await fetch(AGENT_API_URL, {
747
+ method: 'POST',
748
+ headers: {
749
+ 'Content-Type': 'application/json',
750
+ Authorization: `Bearer ${API_KEY}`,
751
+ },
752
+ body: JSON.stringify({
753
+ agentId:
754
+ typeof agentId === 'string' && agentId.startsWith('agent:')
755
+ ? agentId.slice(6)
756
+ : agentId,
757
+ messages: agentMessages,
758
+ ...(agentTools ? { tools: agentTools } : {}),
759
+ }),
760
+ signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
761
+ });
762
+
763
+ if (!agentResponse.ok) {
764
+ const errorText = await agentResponse.text();
765
+ throw new Error(`Agent execution failed: ${errorText}`);
766
+ }
767
+
768
+ const agentData = (await agentResponse.json()) as any;
769
+ const agentLatency = Date.now() - agentStartTime;
770
+
771
+ result = {
772
+ success: true,
773
+ response: agentData.choices?.[0]?.message?.content || '',
774
+ model: agentData.model,
775
+ requires_tool_execution:
776
+ agentData.agent_metadata?.requires_tool_execution || false,
777
+ tool_calls: agentData.choices?.[0]?.message?.tool_calls || null,
778
+ cost: agentData.cost || 0,
779
+ cost_breakdown: agentData.costBreakdown || null,
780
+ usage: agentData.usage || null,
781
+ agent_metadata: agentData.agent_metadata || null,
782
+ latency_ms: agentLatency,
783
+ };
784
+ break;
785
+ }
786
+ case 'list_agent_runs':
787
+ result = await callHttpEndpoint('mcpListAgentRunsHttp', args);
788
+ break;
789
+ case 'get_agent_run_detail':
790
+ result = await callHttpEndpoint('mcpGetAgentRunDetailHttp', args);
791
+ break;
792
+ case 'export_agent_runs':
793
+ result = await callHttpEndpoint('mcpExportAgentRunsHttp', args);
794
+ break;
795
+ default:
796
+ throw new Error(`Unknown tool: ${name}`);
797
+ }
798
+
799
+ return {
800
+ content: [
801
+ {
802
+ type: 'text',
803
+ text: JSON.stringify(result, null, 2),
804
+ },
805
+ ],
806
+ };
807
+ } catch (error) {
808
+ return {
809
+ content: [
810
+ {
811
+ type: 'text',
812
+ text: `Error: ${(error as Error).message}`,
813
+ },
814
+ ],
815
+ isError: true,
816
+ };
817
+ }
818
+ });
819
+
820
+ /**
821
+ * List available resources (documentation)
822
+ */
823
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
824
+ return {
825
+ resources: [
826
+ {
827
+ uri: 'agentlify://quickstart',
828
+ name: '5-Minute Quickstart',
829
+ mimeType: 'text/markdown',
830
+ description: 'Get started with Agentlify in 5 minutes',
831
+ },
832
+ {
833
+ uri: 'agentlify://migration/openai',
834
+ name: 'OpenAI Migration Guide',
835
+ mimeType: 'text/markdown',
836
+ description: 'Complete guide for migrating from OpenAI to Agentlify',
837
+ },
838
+ {
839
+ uri: 'agentlify://migration/anthropic',
840
+ name: 'Anthropic Migration Guide',
841
+ mimeType: 'text/markdown',
842
+ description: 'Complete guide for migrating from Anthropic to Agentlify',
843
+ },
844
+ {
845
+ uri: 'agentlify://router-configuration',
846
+ name: 'Router Configuration Guide',
847
+ mimeType: 'text/markdown',
848
+ description: 'How to configure routers for optimal performance',
849
+ },
850
+ {
851
+ uri: 'agentlify://sdk/python',
852
+ name: 'Python SDK Reference',
853
+ mimeType: 'text/markdown',
854
+ description: 'Complete Python SDK documentation',
855
+ },
856
+ {
857
+ uri: 'agentlify://sdk/javascript',
858
+ name: 'JavaScript/TypeScript SDK',
859
+ mimeType: 'text/markdown',
860
+ description: 'Complete JavaScript SDK documentation',
861
+ },
862
+ {
863
+ uri: 'agentlify://routing-strategies',
864
+ name: 'Routing Strategies Explained',
865
+ mimeType: 'text/markdown',
866
+ description: 'Understanding cost, quality, and speed optimization',
867
+ },
868
+ {
869
+ uri: 'agentlify://cost-optimization',
870
+ name: 'Cost Optimization Guide',
871
+ mimeType: 'text/markdown',
872
+ description: 'Top strategies for reducing AI costs',
873
+ },
874
+ {
875
+ uri: 'agentlify://examples/chatbot',
876
+ name: 'Chatbot Example',
877
+ mimeType: 'text/markdown',
878
+ description: 'Complete production chatbot implementation',
879
+ },
880
+ {
881
+ uri: 'agentlify://pricing',
882
+ name: 'Pricing & Billing',
883
+ mimeType: 'text/markdown',
884
+ description: 'Understanding Agentlify pricing',
885
+ },
886
+ ],
887
+ };
888
+ });
889
+
890
+ /**
891
+ * Read resource content
892
+ */
893
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
894
+ const { uri } = request.params;
895
+
896
+ const docMap: Record<string, string> = {
897
+ 'agentlify://quickstart': 'quickstart.md',
898
+ 'agentlify://migration/openai': 'migration-openai.md',
899
+ 'agentlify://migration/anthropic': 'migration-anthropic.md',
900
+ 'agentlify://router-configuration': 'router-configuration.md',
901
+ 'agentlify://sdk/python': 'sdk-python.md',
902
+ 'agentlify://sdk/javascript': 'sdk-javascript.md',
903
+ 'agentlify://routing-strategies': 'routing-strategies.md',
904
+ 'agentlify://cost-optimization': 'cost-optimization.md',
905
+ 'agentlify://examples/chatbot': 'example-chatbot.md',
906
+ 'agentlify://pricing': 'pricing.md',
907
+ };
908
+
909
+ const filename = docMap[uri];
910
+ if (!filename) {
911
+ throw new Error(`Unknown resource: ${uri}`);
912
+ }
913
+
914
+ try {
915
+ const content = await readDoc(filename);
916
+ return {
917
+ contents: [
918
+ {
919
+ uri,
920
+ mimeType: 'text/markdown',
921
+ text: content,
922
+ },
923
+ ],
924
+ };
925
+ } catch (error) {
926
+ throw new Error(
927
+ `Failed to read resource: ${
928
+ error instanceof Error ? error.message : String(error)
929
+ }`,
930
+ );
931
+ }
932
+ });
933
+
934
+ /**
935
+ * Start server
936
+ */
937
+ async function main() {
938
+ const transport = new StdioServerTransport();
939
+ await server.connect(transport);
940
+ console.error('Agentlify MCP server running on stdio');
941
+ }
942
+
943
+ main().catch((error) => {
944
+ console.error('Fatal error:', error);
945
+ process.exit(1);
946
+ });