apteva 0.4.57 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +216 -54
  2. package/cli.js +35 -0
  3. package/install.js +92 -0
  4. package/package.json +12 -79
  5. package/LICENSE +0 -63
  6. package/bin/apteva.js +0 -196
  7. package/dist/ActivityPage.kxzzb4yc.js +0 -3
  8. package/dist/ApiDocsPage.zq998hbm.js +0 -4
  9. package/dist/App.55rea8mn.js +0 -61
  10. package/dist/App.5ywb23z4.js +0 -53
  11. package/dist/App.6thds120.js +0 -4
  12. package/dist/App.9tctxzqm.js +0 -8
  13. package/dist/App.a8r8ttaz.js +0 -4
  14. package/dist/App.agsv5bje.js +0 -4
  15. package/dist/App.cepapqmx.js +0 -4
  16. package/dist/App.dp041gb3.js +0 -221
  17. package/dist/App.fds72zb5.js +0 -4
  18. package/dist/App.fg9qj2dq.js +0 -4
  19. package/dist/App.ndfejbm9.js +0 -4
  20. package/dist/App.nxmfmq1h.js +0 -13
  21. package/dist/App.qdfyt8ba.js +0 -4
  22. package/dist/App.x2d0ygt6.js +0 -4
  23. package/dist/App.yt9p4nr3.js +0 -20
  24. package/dist/App.zn4mw16t.js +0 -1
  25. package/dist/ConnectionsPage.8r96ryw7.js +0 -3
  26. package/dist/McpPage.3cwh0gnd.js +0 -3
  27. package/dist/SettingsPage.ykgdh5ev.js +0 -3
  28. package/dist/SkillsPage.4np1s65b.js +0 -3
  29. package/dist/TasksPage.4g08t7p6.js +0 -3
  30. package/dist/TelemetryPage.72w9pwcp.js +0 -3
  31. package/dist/TestsPage.z4fk3r7r.js +0 -3
  32. package/dist/ThreadsPage.63tcajeh.js +0 -3
  33. package/dist/apteva-kit.css +0 -1
  34. package/dist/icon.png +0 -0
  35. package/dist/index.html +0 -16
  36. package/dist/styles.css +0 -1
  37. package/scripts/postinstall.mjs +0 -102
  38. package/src/auth/index.ts +0 -394
  39. package/src/auth/middleware.ts +0 -213
  40. package/src/binary.ts +0 -536
  41. package/src/channels/index.ts +0 -40
  42. package/src/channels/telegram.ts +0 -311
  43. package/src/crypto.ts +0 -301
  44. package/src/db-tests.ts +0 -174
  45. package/src/db.ts +0 -3133
  46. package/src/integrations/agentdojo.ts +0 -559
  47. package/src/integrations/composio.ts +0 -437
  48. package/src/integrations/index.ts +0 -87
  49. package/src/integrations/skillsmp.ts +0 -318
  50. package/src/mcp-client.ts +0 -605
  51. package/src/mcp-handler.ts +0 -394
  52. package/src/mcp-platform.ts +0 -2403
  53. package/src/openapi.ts +0 -2410
  54. package/src/providers.ts +0 -597
  55. package/src/routes/api/agent-utils.ts +0 -890
  56. package/src/routes/api/agents.ts +0 -916
  57. package/src/routes/api/api-keys.ts +0 -95
  58. package/src/routes/api/channels.ts +0 -182
  59. package/src/routes/api/helpers.ts +0 -12
  60. package/src/routes/api/integrations.ts +0 -639
  61. package/src/routes/api/mcp.ts +0 -574
  62. package/src/routes/api/meta-agent.ts +0 -195
  63. package/src/routes/api/projects.ts +0 -112
  64. package/src/routes/api/providers.ts +0 -424
  65. package/src/routes/api/skills.ts +0 -537
  66. package/src/routes/api/system.ts +0 -333
  67. package/src/routes/api/telemetry.ts +0 -203
  68. package/src/routes/api/tests.ts +0 -148
  69. package/src/routes/api/triggers.ts +0 -518
  70. package/src/routes/api/users.ts +0 -148
  71. package/src/routes/api/webhooks.ts +0 -171
  72. package/src/routes/api.ts +0 -53
  73. package/src/routes/auth.ts +0 -251
  74. package/src/routes/share.ts +0 -86
  75. package/src/routes/static.ts +0 -131
  76. package/src/server.ts +0 -642
  77. package/src/test-runner.ts +0 -598
  78. package/src/triggers/agentdojo.ts +0 -253
  79. package/src/triggers/composio.ts +0 -264
  80. package/src/triggers/index.ts +0 -71
  81. package/src/tui/AgentList.tsx +0 -145
  82. package/src/tui/App.tsx +0 -102
  83. package/src/tui/Login.tsx +0 -104
  84. package/src/tui/api.ts +0 -72
  85. package/src/tui/index.tsx +0 -7
  86. package/src/web/App.tsx +0 -455
  87. package/src/web/components/activity/ActivityPage.tsx +0 -314
  88. package/src/web/components/activity/index.ts +0 -1
  89. package/src/web/components/agents/AgentCard.tsx +0 -189
  90. package/src/web/components/agents/AgentPanel.tsx +0 -2244
  91. package/src/web/components/agents/AgentsView.tsx +0 -180
  92. package/src/web/components/agents/CreateAgentModal.tsx +0 -475
  93. package/src/web/components/agents/index.ts +0 -4
  94. package/src/web/components/api/ApiDocsPage.tsx +0 -842
  95. package/src/web/components/auth/CreateAccountStep.tsx +0 -176
  96. package/src/web/components/auth/LoginPage.tsx +0 -91
  97. package/src/web/components/auth/index.ts +0 -2
  98. package/src/web/components/common/Icons.tsx +0 -250
  99. package/src/web/components/common/LoadingSpinner.tsx +0 -44
  100. package/src/web/components/common/Modal.tsx +0 -199
  101. package/src/web/components/common/Select.tsx +0 -97
  102. package/src/web/components/common/index.ts +0 -20
  103. package/src/web/components/connections/ConnectionsPage.tsx +0 -54
  104. package/src/web/components/connections/IntegrationsTab.tsx +0 -170
  105. package/src/web/components/connections/OverviewTab.tsx +0 -137
  106. package/src/web/components/connections/TriggersTab.tsx +0 -1346
  107. package/src/web/components/dashboard/Dashboard.tsx +0 -572
  108. package/src/web/components/dashboard/index.ts +0 -1
  109. package/src/web/components/index.ts +0 -21
  110. package/src/web/components/layout/ErrorBanner.tsx +0 -18
  111. package/src/web/components/layout/Header.tsx +0 -332
  112. package/src/web/components/layout/Sidebar.tsx +0 -231
  113. package/src/web/components/layout/index.ts +0 -3
  114. package/src/web/components/mcp/IntegrationsPanel.tsx +0 -857
  115. package/src/web/components/mcp/McpPage.tsx +0 -2515
  116. package/src/web/components/mcp/index.ts +0 -1
  117. package/src/web/components/meta-agent/MetaAgent.tsx +0 -245
  118. package/src/web/components/onboarding/OnboardingWizard.tsx +0 -404
  119. package/src/web/components/onboarding/index.ts +0 -1
  120. package/src/web/components/settings/SettingsPage.tsx +0 -2776
  121. package/src/web/components/settings/index.ts +0 -1
  122. package/src/web/components/skills/SkillsPage.tsx +0 -1200
  123. package/src/web/components/tasks/TasksPage.tsx +0 -1116
  124. package/src/web/components/tasks/index.ts +0 -1
  125. package/src/web/components/telemetry/TelemetryPage.tsx +0 -1129
  126. package/src/web/components/tests/TestsPage.tsx +0 -594
  127. package/src/web/components/threads/ThreadsPage.tsx +0 -315
  128. package/src/web/context/AuthContext.tsx +0 -242
  129. package/src/web/context/ProjectContext.tsx +0 -214
  130. package/src/web/context/TelemetryContext.tsx +0 -299
  131. package/src/web/context/ThemeContext.tsx +0 -90
  132. package/src/web/context/UIModeContext.tsx +0 -49
  133. package/src/web/context/index.ts +0 -12
  134. package/src/web/hooks/index.ts +0 -3
  135. package/src/web/hooks/useAgents.ts +0 -115
  136. package/src/web/hooks/useOnboarding.ts +0 -20
  137. package/src/web/hooks/useProviders.ts +0 -75
  138. package/src/web/icon.png +0 -0
  139. package/src/web/index.html +0 -16
  140. package/src/web/styles.css +0 -118
  141. package/src/web/themes.ts +0 -162
  142. package/src/web/types.ts +0 -298
@@ -1,639 +0,0 @@
1
- import { json } from "./helpers";
2
- import { McpServerDB, generateId } from "../../db";
3
- import { ProviderKeys } from "../../providers";
4
- import { getProvider, getProviderIds, registerProvider } from "../../integrations";
5
- import { ComposioProvider } from "../../integrations/composio";
6
- import {
7
- AgentDojoProvider,
8
- listServers as listAgentDojoServers,
9
- createServer as createAgentDojoServer,
10
- getServer as getAgentDojoServer,
11
- deleteServer as deleteAgentDojoServer,
12
- } from "../../integrations/agentdojo";
13
- import type { AuthContext } from "../../auth/middleware";
14
-
15
- // Register integration providers on module load
16
- registerProvider(ComposioProvider);
17
- registerProvider(AgentDojoProvider);
18
-
19
- export async function handleIntegrationRoutes(
20
- req: Request,
21
- path: string,
22
- method: string,
23
- authContext?: AuthContext,
24
- ): Promise<Response | null> {
25
- const user = authContext?.user;
26
-
27
- // ============ Generic Integration Providers ============
28
-
29
- // GET /api/integrations/providers - List available integration providers
30
- if (path === "/api/integrations/providers" && method === "GET") {
31
- const providerIds = getProviderIds();
32
- const providers = providerIds.map(id => {
33
- const provider = getProvider(id);
34
- const hasKey = !!ProviderKeys.getDecrypted(id);
35
- return {
36
- id,
37
- name: provider?.name || id,
38
- connected: hasKey,
39
- };
40
- });
41
- return json({ providers });
42
- }
43
-
44
- // GET /api/integrations/:provider/apps - List available apps from a provider
45
- const appsMatch = path.match(/^\/api\/integrations\/([^/]+)\/apps$/);
46
- if (appsMatch && method === "GET") {
47
- const providerId = appsMatch[1];
48
- const provider = getProvider(providerId);
49
- if (!provider) {
50
- return json({ error: `Unknown provider: ${providerId}` }, 404);
51
- }
52
-
53
- const url = new URL(req.url);
54
- const projectId = url.searchParams.get("project_id") || null;
55
- const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
56
- if (!apiKey) {
57
- return json({ error: `${provider.name} API key not configured`, apps: [] }, 200);
58
- }
59
-
60
- try {
61
- const apps = await provider.listApps(apiKey);
62
- return json({ apps });
63
- } catch (e) {
64
- console.error(`Failed to list apps from ${providerId}:`, e);
65
- return json({ error: "Failed to fetch apps" }, 500);
66
- }
67
- }
68
-
69
- // GET /api/integrations/:provider/connected - List user's connected accounts
70
- const connectedMatch = path.match(/^\/api\/integrations\/([^/]+)\/connected$/);
71
- if (connectedMatch && method === "GET") {
72
- const providerId = connectedMatch[1];
73
- const provider = getProvider(providerId);
74
- if (!provider) {
75
- return json({ error: `Unknown provider: ${providerId}` }, 404);
76
- }
77
-
78
- const url = new URL(req.url);
79
- const projectId = url.searchParams.get("project_id") || null;
80
- console.log(`[integrations/connected] provider=${providerId}, projectId=${projectId}`);
81
- const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
82
- console.log(`[integrations/connected] apiKey found: ${!!apiKey}, length: ${apiKey?.length || 0}`);
83
- if (!apiKey) {
84
- console.log(`[integrations/connected] NO API KEY for ${providerId}`);
85
- return json({ error: `${provider.name} API key not configured`, accounts: [] }, 200);
86
- }
87
-
88
- // Use Apteva user ID as the entity ID for the provider
89
- if (!user?.id) {
90
- return json({ error: "Authentication required" }, 401);
91
- }
92
-
93
- try {
94
- const accounts = await provider.listConnectedAccounts(apiKey, user.id);
95
- console.log(`[integrations/connected] Got ${accounts.length} accounts from ${providerId}`);
96
- return json({ accounts });
97
- } catch (e) {
98
- console.error(`[integrations/connected] Failed from ${providerId}:`, e);
99
- return json({ error: "Failed to fetch connected accounts" }, 500);
100
- }
101
- }
102
-
103
- // POST /api/integrations/:provider/connect - Initiate connection (OAuth or API Key)
104
- const connectMatch = path.match(/^\/api\/integrations\/([^/]+)\/connect$/);
105
- if (connectMatch && method === "POST") {
106
- const providerId = connectMatch[1];
107
- const provider = getProvider(providerId);
108
- if (!provider) {
109
- return json({ error: `Unknown provider: ${providerId}` }, 404);
110
- }
111
-
112
- try {
113
- const body = await req.json();
114
- const { appSlug, redirectUrl, credentials } = body;
115
- // Read project_id from query param (frontend sends it there), fall back to body
116
- const url = new URL(req.url);
117
- const projectId = url.searchParams.get("project_id") || body.project_id || null;
118
- const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
119
- if (!apiKey) {
120
- return json({ error: `${provider.name} API key not configured` }, 401);
121
- }
122
-
123
- if (!appSlug) {
124
- return json({ error: "appSlug is required" }, 400);
125
- }
126
-
127
- // Use Apteva user ID as the entity ID
128
- if (!user?.id) {
129
- return json({ error: "Authentication required" }, 401);
130
- }
131
-
132
- // Default redirect URL back to our integrations page
133
- const origin = req.headers.get("origin") || process.env.INSTANCE_URL || `http://localhost:${process.env.PORT || 4280}`;
134
- const callbackUrl = redirectUrl || `${origin}/mcp?tab=hosted&connected=${appSlug}`;
135
-
136
- const result = await provider.initiateConnection(apiKey, user.id, appSlug, callbackUrl, credentials);
137
- return json(result);
138
- } catch (e) {
139
- console.error(`Failed to initiate connection for ${providerId}:`, e);
140
- return json({ error: `Failed to initiate connection: ${e}` }, 500);
141
- }
142
- }
143
-
144
- // GET /api/integrations/:provider/connection/:id - Check connection status
145
- const connectionStatusMatch = path.match(/^\/api\/integrations\/([^/]+)\/connection\/([^/]+)$/);
146
- if (connectionStatusMatch && method === "GET") {
147
- const providerId = connectionStatusMatch[1];
148
- const connectionId = connectionStatusMatch[2];
149
- const provider = getProvider(providerId);
150
- if (!provider) {
151
- return json({ error: `Unknown provider: ${providerId}` }, 404);
152
- }
153
-
154
- const url = new URL(req.url);
155
- const projectId = url.searchParams.get("project_id") || null;
156
- const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
157
- if (!apiKey) {
158
- return json({ error: `${provider.name} API key not configured` }, 401);
159
- }
160
-
161
- try {
162
- const connection = await provider.getConnectionStatus(apiKey, connectionId);
163
- if (!connection) {
164
- return json({ error: "Connection not found" }, 404);
165
- }
166
- return json({ connection });
167
- } catch (e) {
168
- console.error(`Failed to get connection status:`, e);
169
- return json({ error: "Failed to get connection status" }, 500);
170
- }
171
- }
172
-
173
- // DELETE /api/integrations/:provider/connection/:id - Disconnect/revoke
174
- if (connectionStatusMatch && method === "DELETE") {
175
- const providerId = connectionStatusMatch[1];
176
- const connectionId = connectionStatusMatch[2];
177
- const provider = getProvider(providerId);
178
- if (!provider) {
179
- return json({ error: `Unknown provider: ${providerId}` }, 404);
180
- }
181
-
182
- const url = new URL(req.url);
183
- const projectId = url.searchParams.get("project_id") || null;
184
- const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
185
- if (!apiKey) {
186
- return json({ error: `${provider.name} API key not configured` }, 401);
187
- }
188
-
189
- try {
190
- const success = await provider.disconnect(apiKey, connectionId);
191
- return json({ success });
192
- } catch (e) {
193
- console.error(`Failed to disconnect:`, e);
194
- return json({ error: "Failed to disconnect" }, 500);
195
- }
196
- }
197
-
198
- // ============ Composio-Specific Routes ============
199
-
200
- // GET /api/integrations/composio/configs - List Composio MCP configs
201
- if (path === "/api/integrations/composio/configs" && method === "GET") {
202
- const url = new URL(req.url);
203
- const projectId = url.searchParams.get("project_id") || null;
204
- const apiKey = ProviderKeys.getDecryptedForProject("composio", projectId);
205
- if (!apiKey) {
206
- return json({ error: "Composio API key not configured", configs: [] }, 200);
207
- }
208
-
209
- try {
210
- const res = await fetch("https://backend.composio.dev/api/v3/mcp/servers?limit=50", {
211
- headers: {
212
- "x-api-key": apiKey,
213
- "Content-Type": "application/json",
214
- },
215
- });
216
-
217
- if (!res.ok) {
218
- const text = await res.text();
219
- console.error("Composio API error:", res.status, text);
220
- return json({ error: "Failed to fetch Composio configs" }, 500);
221
- }
222
-
223
- const data = await res.json();
224
-
225
- // Transform to our format
226
- const configs = (data.items || data.servers || []).map((item: any) => ({
227
- id: item.id,
228
- name: item.name || item.id,
229
- toolkits: item.toolkits || item.apps || [],
230
- toolsCount: item.toolsCount || item.tools?.length || 0,
231
- createdAt: item.createdAt || item.created_at,
232
- }));
233
-
234
- return json({ configs });
235
- } catch (e) {
236
- console.error("Composio fetch error:", e);
237
- return json({ error: "Failed to connect to Composio" }, 500);
238
- }
239
- }
240
-
241
- // GET /api/integrations/composio/configs/:id - Get single Composio config details
242
- const composioConfigMatch = path.match(/^\/api\/integrations\/composio\/configs\/([^/]+)$/);
243
- if (composioConfigMatch && method === "GET") {
244
- const configId = composioConfigMatch[1];
245
- const url = new URL(req.url);
246
- const projectId = url.searchParams.get("project_id") || null;
247
- const apiKey = ProviderKeys.getDecryptedForProject("composio", projectId);
248
- if (!apiKey) {
249
- return json({ error: "Composio API key not configured" }, 401);
250
- }
251
-
252
- try {
253
- const res = await fetch(`https://backend.composio.dev/api/v3/mcp/${configId}`, {
254
- headers: {
255
- "x-api-key": apiKey,
256
- "Content-Type": "application/json",
257
- },
258
- });
259
-
260
- if (!res.ok) {
261
- return json({ error: "Config not found" }, 404);
262
- }
263
-
264
- const data = await res.json();
265
- return json({
266
- config: {
267
- id: data.id,
268
- name: data.name || data.id,
269
- toolkits: data.toolkits || data.apps || [],
270
- tools: data.tools || [],
271
- },
272
- });
273
- } catch (e) {
274
- return json({ error: "Failed to fetch config" }, 500);
275
- }
276
- }
277
-
278
- // POST /api/integrations/composio/configs/:id/add - Add a Composio config as an MCP server
279
- const composioAddMatch = path.match(/^\/api\/integrations\/composio\/configs\/([^/]+)\/add$/);
280
- if (composioAddMatch && method === "POST") {
281
- const configId = composioAddMatch[1];
282
- const url = new URL(req.url);
283
- const projectId = url.searchParams.get("project_id") || null;
284
- const apiKey = ProviderKeys.getDecryptedForProject("composio", projectId);
285
- if (!apiKey) {
286
- return json({ error: "Composio API key not configured" }, 401);
287
- }
288
-
289
- try {
290
- // Fetch config details from Composio to get the name and mcp_url
291
- const res = await fetch(`https://backend.composio.dev/api/v3/mcp/${configId}`, {
292
- headers: {
293
- "x-api-key": apiKey,
294
- "Content-Type": "application/json",
295
- },
296
- });
297
-
298
- if (!res.ok) {
299
- const errText = await res.text();
300
- console.error("Failed to fetch Composio MCP config:", errText);
301
- return json({ error: "Failed to fetch MCP config from Composio" }, 400);
302
- }
303
-
304
- const data = await res.json();
305
- const configName = data.name || `composio-${configId.slice(0, 8)}`;
306
- const mcpUrl = data.mcp_url;
307
- const authConfigIds = data.auth_config_ids || [];
308
- const serverInstanceCount = data.server_instance_count || 0;
309
-
310
- if (!mcpUrl) {
311
- return json({ error: "MCP config does not have a URL" }, 400);
312
- }
313
-
314
- // Get user_id from connected accounts for this auth config
315
- const { createMcpServerInstance, getUserIdForAuthConfig } = await import("../../integrations/composio");
316
- let userId: string | null = null;
317
-
318
- if (authConfigIds.length > 0) {
319
- userId = await getUserIdForAuthConfig(apiKey, authConfigIds[0]);
320
-
321
- // Create server instance if none exists
322
- if (serverInstanceCount === 0 && userId) {
323
- const instance = await createMcpServerInstance(apiKey, configId, userId);
324
- if (instance) {
325
- console.log(`Created server instance for user ${userId} on server ${configId}`);
326
- }
327
- }
328
- }
329
-
330
- // Append user_id to mcp_url for authentication
331
- const mcpUrlWithUser = userId
332
- ? `${mcpUrl}?user_id=${encodeURIComponent(userId)}`
333
- : mcpUrl;
334
-
335
- // Check if already exists (match by config ID in URL)
336
- // Use Light variant to skip expensive decryption of env/headers
337
- const existing = McpServerDB.findAllLight().find(
338
- s => s.source === "composio" && s.url?.includes(configId)
339
- );
340
- if (existing) {
341
- return json({ server: existing, message: "Server already exists" });
342
- }
343
-
344
- // Create the MCP server entry with user_id in URL
345
- const server = McpServerDB.create({
346
- id: generateId(),
347
- name: configName,
348
- type: "http",
349
- package: null,
350
- command: null,
351
- args: null,
352
- pip_module: null,
353
- env: {},
354
- url: mcpUrlWithUser,
355
- headers: { "x-api-key": apiKey },
356
- source: "composio",
357
- project_id: projectId,
358
- });
359
-
360
- return json({ server, message: "Server added successfully" });
361
- } catch (e) {
362
- console.error("Failed to add Composio config:", e);
363
- return json({ error: "Failed to add Composio config" }, 500);
364
- }
365
- }
366
-
367
- // POST /api/integrations/composio/configs - Create a new MCP config from connected app
368
- if (path === "/api/integrations/composio/configs" && method === "POST") {
369
- const url = new URL(req.url);
370
- const projectId = url.searchParams.get("project_id") || null;
371
- const apiKey = ProviderKeys.getDecryptedForProject("composio", projectId);
372
- if (!apiKey) {
373
- return json({ error: "Composio API key not configured" }, 401);
374
- }
375
-
376
- try {
377
- const body = await req.json();
378
- const { name, toolkitSlug, authConfigId } = body;
379
-
380
- if (!name || !toolkitSlug) {
381
- return json({ error: "name and toolkitSlug are required" }, 400);
382
- }
383
-
384
- // If authConfigId not provided, find it from the toolkit
385
- let configId = authConfigId;
386
- if (!configId) {
387
- const { getAuthConfigForToolkit } = await import("../../integrations/composio");
388
- configId = await getAuthConfigForToolkit(apiKey, toolkitSlug);
389
- if (!configId) {
390
- return json({ error: `No auth config found for ${toolkitSlug}. Make sure you have connected this app first.` }, 400);
391
- }
392
- }
393
-
394
- // Create MCP server in Composio
395
- const { createMcpServer, createMcpServerInstance, getUserIdForAuthConfig } = await import("../../integrations/composio");
396
- const mcpServer = await createMcpServer(apiKey, name, [configId]);
397
-
398
- if (!mcpServer) {
399
- return json({ error: "Failed to create MCP config" }, 500);
400
- }
401
-
402
- // Create server instance for the user who has the connected account
403
- const userId = await getUserIdForAuthConfig(apiKey, configId);
404
- if (userId) {
405
- const instance = await createMcpServerInstance(apiKey, mcpServer.id, userId);
406
- if (!instance) {
407
- console.warn(`Created MCP server but failed to create instance for user ${userId}`);
408
- }
409
- }
410
-
411
- // Append user_id to mcp_url for authentication
412
- const mcpUrlWithUser = userId
413
- ? `${mcpServer.mcpUrl}?user_id=${encodeURIComponent(userId)}`
414
- : mcpServer.mcpUrl;
415
-
416
- return json({
417
- config: {
418
- id: mcpServer.id,
419
- name: mcpServer.name,
420
- toolkits: mcpServer.toolkits,
421
- mcpUrl: mcpUrlWithUser,
422
- allowedTools: mcpServer.allowedTools,
423
- userId,
424
- },
425
- }, 201);
426
- } catch (e: any) {
427
- console.error("Failed to create Composio MCP config:", e);
428
- return json({ error: e.message || "Failed to create MCP config" }, 500);
429
- }
430
- }
431
-
432
- // DELETE /api/integrations/composio/configs/:id - Delete a Composio MCP config
433
- if (composioConfigMatch && method === "DELETE") {
434
- const configId = composioConfigMatch[1];
435
- const url = new URL(req.url);
436
- const projectId = url.searchParams.get("project_id") || null;
437
- const apiKey = ProviderKeys.getDecryptedForProject("composio", projectId);
438
- if (!apiKey) {
439
- return json({ error: "Composio API key not configured" }, 401);
440
- }
441
-
442
- try {
443
- const { deleteMcpServer } = await import("../../integrations/composio");
444
- const success = await deleteMcpServer(apiKey, configId);
445
- if (!success) {
446
- return json({ error: "Failed to delete MCP config" }, 500);
447
- }
448
- return json({ success: true });
449
- } catch (e) {
450
- console.error("Failed to delete Composio config:", e);
451
- return json({ error: "Failed to delete MCP config" }, 500);
452
- }
453
- }
454
-
455
- // ============ AgentDojo-Specific Routes ============
456
-
457
- // GET /api/integrations/agentdojo/configs - List AgentDojo MCP servers (configs)
458
- if (path === "/api/integrations/agentdojo/configs" && method === "GET") {
459
- const url = new URL(req.url);
460
- const projectId = url.searchParams.get("project_id") || null;
461
- const apiKey = ProviderKeys.getDecryptedForProject("agentdojo", projectId);
462
- if (!apiKey) {
463
- return json({ error: "AgentDojo API key not configured", configs: [] }, 200);
464
- }
465
-
466
- try {
467
- const servers = await listAgentDojoServers(apiKey, true);
468
- const configs = servers.map(s => ({
469
- id: s.id,
470
- name: s.name,
471
- slug: s.slug,
472
- toolkits: [], // Could be extracted from tools
473
- toolsCount: s.tools?.length || 0,
474
- mcpUrl: s.url,
475
- createdAt: s.createdAt,
476
- }));
477
- return json({ configs });
478
- } catch (e) {
479
- console.error("AgentDojo fetch error:", e);
480
- return json({ error: "Failed to connect to AgentDojo" }, 500);
481
- }
482
- }
483
-
484
- // GET /api/integrations/agentdojo/configs/:id - Get single AgentDojo config details
485
- const agentdojoConfigMatch = path.match(/^\/api\/integrations\/agentdojo\/configs\/([^/]+)$/);
486
- if (agentdojoConfigMatch && method === "GET") {
487
- const configId = agentdojoConfigMatch[1];
488
- const url = new URL(req.url);
489
- const projectId = url.searchParams.get("project_id") || null;
490
- const apiKey = ProviderKeys.getDecryptedForProject("agentdojo", projectId);
491
- if (!apiKey) {
492
- return json({ error: "AgentDojo API key not configured" }, 401);
493
- }
494
-
495
- try {
496
- const server = await getAgentDojoServer(apiKey, configId);
497
- if (!server) {
498
- return json({ error: "Config not found" }, 404);
499
- }
500
- return json({
501
- config: {
502
- id: server.id,
503
- name: server.name,
504
- slug: server.slug,
505
- mcpUrl: server.url,
506
- tools: server.tools || [],
507
- },
508
- });
509
- } catch (e) {
510
- return json({ error: "Failed to fetch config" }, 500);
511
- }
512
- }
513
-
514
- // POST /api/integrations/agentdojo/configs/:id/add - Add an AgentDojo config as a local MCP server
515
- const agentdojoAddMatch = path.match(/^\/api\/integrations\/agentdojo\/configs\/([^/]+)\/add$/);
516
- if (agentdojoAddMatch && method === "POST") {
517
- const configId = agentdojoAddMatch[1];
518
- const url = new URL(req.url);
519
- const projectId = url.searchParams.get("project_id") || null;
520
- const apiKey = ProviderKeys.getDecryptedForProject("agentdojo", projectId);
521
- if (!apiKey) {
522
- return json({ error: "AgentDojo API key not configured" }, 401);
523
- }
524
-
525
- try {
526
- console.log(`[agentdojo:add] configId=${configId} projectId=${projectId}`);
527
- const server = await getAgentDojoServer(apiKey, configId);
528
- if (!server) {
529
- console.log(`[agentdojo:add] server not found from AgentDojo API for configId=${configId}`);
530
- return json({ error: "Config not found" }, 404);
531
- }
532
- console.log(`[agentdojo:add] fetched server: slug=${server.slug} name=${server.name} url=${server.url?.substring(0, 80)}`);
533
-
534
- // Check if already exists — match by slug in URL, scoped to same project
535
- // Use Light variant to skip expensive decryption of env/headers
536
- const effectiveProjectId = projectId && projectId !== "unassigned" ? projectId : null;
537
- const allServers = McpServerDB.findAllLight();
538
- const agentdojoServers = allServers.filter(s => s.source === "agentdojo");
539
- console.log(`[agentdojo:add] total servers=${allServers.length} agentdojo servers=${agentdojoServers.length}`);
540
- const existing = agentdojoServers.find(
541
- s => s.project_id === effectiveProjectId && s.url?.endsWith(`/${server.slug}`)
542
- );
543
- if (existing) {
544
- console.log(`[agentdojo:add] ALREADY EXISTS: id=${existing.id} project_id=${existing.project_id} slug=${server.slug}`);
545
- return json({ server: existing, message: "Server already exists" });
546
- }
547
-
548
- // Create the MCP server entry
549
- console.log(`[agentdojo:add] creating new server with effectiveProjectId=${effectiveProjectId}`);
550
- const mcpServer = McpServerDB.create({
551
- id: generateId(),
552
- name: server.name,
553
- type: "http",
554
- package: null,
555
- command: null,
556
- args: null,
557
- pip_module: null,
558
- env: {},
559
- url: server.url,
560
- headers: { "X-API-Key": apiKey },
561
- source: "agentdojo",
562
- project_id: effectiveProjectId,
563
- });
564
- console.log(`[agentdojo:add] created server: id=${mcpServer.id} project_id=${mcpServer.project_id}`);
565
-
566
- return json({ server: mcpServer, message: "Server added successfully" });
567
- } catch (e) {
568
- console.error("[agentdojo:add] Failed to add AgentDojo config:", e);
569
- return json({ error: "Failed to add AgentDojo config" }, 500);
570
- }
571
- }
572
-
573
- // POST /api/integrations/agentdojo/configs - Create a new MCP server from toolkit
574
- if (path === "/api/integrations/agentdojo/configs" && method === "POST") {
575
- const url = new URL(req.url);
576
- const projectId = url.searchParams.get("project_id") || null;
577
- const apiKey = ProviderKeys.getDecryptedForProject("agentdojo", projectId);
578
- if (!apiKey) {
579
- return json({ error: "AgentDojo API key not configured" }, 401);
580
- }
581
-
582
- try {
583
- const body = await req.json();
584
- const { name, toolkitSlug, toolkits } = body;
585
-
586
- if (!name) {
587
- return json({ error: "name is required" }, 400);
588
- }
589
-
590
- // Accept either toolkitSlug (single) or toolkits (array)
591
- const toolkitList = toolkits || (toolkitSlug ? [toolkitSlug] : []);
592
- if (toolkitList.length === 0) {
593
- return json({ error: "toolkitSlug or toolkits is required" }, 400);
594
- }
595
-
596
- const server = await createAgentDojoServer(apiKey, name, toolkitList);
597
- if (!server) {
598
- return json({ error: "Failed to create MCP config" }, 500);
599
- }
600
-
601
- return json({
602
- config: {
603
- id: server.id,
604
- name: server.name,
605
- slug: server.slug,
606
- mcpUrl: server.url,
607
- tools: server.tools || [],
608
- },
609
- }, 201);
610
- } catch (e: any) {
611
- console.error("Failed to create AgentDojo MCP config:", e);
612
- return json({ error: e.message || "Failed to create MCP config" }, 500);
613
- }
614
- }
615
-
616
- // DELETE /api/integrations/agentdojo/configs/:id - Delete an AgentDojo MCP config
617
- if (agentdojoConfigMatch && method === "DELETE") {
618
- const configId = agentdojoConfigMatch[1];
619
- const url = new URL(req.url);
620
- const projectId = url.searchParams.get("project_id") || null;
621
- const apiKey = ProviderKeys.getDecryptedForProject("agentdojo", projectId);
622
- if (!apiKey) {
623
- return json({ error: "AgentDojo API key not configured" }, 401);
624
- }
625
-
626
- try {
627
- const success = await deleteAgentDojoServer(apiKey, configId);
628
- if (!success) {
629
- return json({ error: "Failed to delete MCP config" }, 500);
630
- }
631
- return json({ success: true });
632
- } catch (e) {
633
- console.error("Failed to delete AgentDojo config:", e);
634
- return json({ error: "Failed to delete MCP config" }, 500);
635
- }
636
- }
637
-
638
- return null;
639
- }