apteva 0.4.32 → 0.4.44
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/ActivityPage.c48n83h2.js +3 -0
- package/dist/ApiDocsPage.yzcxx5ax.js +4 -0
- package/dist/App.09yb8t0b.js +1 -0
- package/dist/App.152mbs1r.js +4 -0
- package/dist/App.3a67nx9w.js +4 -0
- package/dist/App.9epx6785.js +4 -0
- package/dist/App.d8955awp.js +4 -0
- package/dist/App.drwb57jq.js +4 -0
- package/dist/App.gssbmajb.js +4 -0
- package/dist/App.qw70pc29.js +53 -0
- package/dist/App.qzbx5wtj.js +4 -0
- package/dist/App.r5serxkt.js +8 -0
- package/dist/App.tpmp9020.js +20 -0
- package/dist/App.v2wb4d7d.js +61 -0
- package/dist/App.vxmaaj0m.js +13 -0
- package/dist/App.w4p2tda9.js +4 -0
- package/dist/App.wv2ng55q.js +221 -0
- package/dist/App.yncnrn0f.js +4 -0
- package/dist/ConnectionsPage.k6cspyqq.js +3 -0
- package/dist/McpPage.cdxm48xj.js +3 -0
- package/dist/SettingsPage.evpv7c2y.js +3 -0
- package/dist/SkillsPage.pvzp6c1a.js +3 -0
- package/dist/TasksPage.6jnvbpsy.js +3 -0
- package/dist/TelemetryPage.t7vk24zc.js +3 -0
- package/dist/TestsPage.5x6658aa.js +3 -0
- package/dist/ThreadsPage.3fvhtevh.js +3 -0
- package/dist/apteva-kit.css +1 -1
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +10 -9
- package/src/crypto.ts +4 -3
- package/src/db.ts +171 -36
- package/src/integrations/agentdojo.ts +95 -12
- package/src/integrations/index.ts +7 -0
- package/src/mcp-platform.ts +870 -142
- package/src/openapi.ts +96 -0
- package/src/providers.ts +60 -34
- package/src/routes/api/agent-utils.ts +59 -47
- package/src/routes/api/agents.ts +71 -2
- package/src/routes/api/integrations.ts +11 -5
- package/src/routes/api/mcp.ts +5 -4
- package/src/routes/api/meta-agent.ts +37 -1
- package/src/routes/api/projects.ts +3 -3
- package/src/routes/api/providers.ts +121 -30
- package/src/routes/api/skills.ts +2 -3
- package/src/routes/api/system.ts +98 -14
- package/src/routes/api/telemetry.ts +19 -1
- package/src/routes/share.ts +85 -0
- package/src/server.ts +43 -32
- package/src/triggers/agentdojo.ts +2 -2
- package/src/web/App.tsx +107 -21
- package/src/web/components/activity/ActivityPage.tsx +242 -389
- package/src/web/components/agents/AgentCard.tsx +19 -27
- package/src/web/components/agents/AgentPanel.tsx +358 -198
- package/src/web/components/agents/AgentsView.tsx +4 -4
- package/src/web/components/agents/CreateAgentModal.tsx +21 -79
- package/src/web/components/api/ApiDocsPage.tsx +66 -66
- package/src/web/components/auth/CreateAccountStep.tsx +16 -16
- package/src/web/components/auth/LoginPage.tsx +10 -10
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/common/LoadingSpinner.tsx +2 -2
- package/src/web/components/common/Modal.tsx +8 -8
- package/src/web/components/common/Select.tsx +11 -10
- package/src/web/components/connections/ConnectionsPage.tsx +4 -4
- package/src/web/components/connections/IntegrationsTab.tsx +18 -18
- package/src/web/components/connections/OverviewTab.tsx +13 -13
- package/src/web/components/connections/TriggersTab.tsx +99 -99
- package/src/web/components/dashboard/Dashboard.tsx +177 -52
- package/src/web/components/index.ts +1 -1
- package/src/web/components/layout/Header.tsx +50 -34
- package/src/web/components/layout/Sidebar.tsx +41 -16
- package/src/web/components/mcp/IntegrationsPanel.tsx +160 -69
- package/src/web/components/mcp/McpPage.tsx +218 -209
- package/src/web/components/meta-agent/MetaAgent.tsx +15 -11
- package/src/web/components/onboarding/OnboardingWizard.tsx +25 -25
- package/src/web/components/settings/SettingsPage.tsx +389 -221
- package/src/web/components/skills/SkillsPage.tsx +88 -88
- package/src/web/components/tasks/TasksPage.tsx +385 -68
- package/src/web/components/telemetry/TelemetryPage.tsx +294 -39
- package/src/web/components/tests/TestsPage.tsx +50 -50
- package/src/web/components/threads/ThreadsPage.tsx +315 -0
- package/src/web/context/AuthContext.tsx +3 -3
- package/src/web/context/ProjectContext.tsx +8 -3
- package/src/web/context/TelemetryContext.tsx +24 -6
- package/src/web/context/ThemeContext.tsx +69 -0
- package/src/web/context/index.ts +3 -1
- package/src/web/styles.css +25 -7
- package/src/web/themes.ts +99 -0
- package/src/web/types.ts +4 -7
- package/dist/ActivityPage.41nbye4r.js +0 -3
- package/dist/ApiDocsPage.4smnt8m3.js +0 -4
- package/dist/App.0sbax9et.js +0 -4
- package/dist/App.0ws427h8.js +0 -4
- package/dist/App.6q6bar8b.js +0 -4
- package/dist/App.80301vdb.js +0 -4
- package/dist/App.af2wg84v.js +0 -267
- package/dist/App.ca1rz1ph.js +0 -4
- package/dist/App.ensa6z0r.js +0 -4
- package/dist/App.f8g7tych.js +0 -13
- package/dist/App.mvtqv6qc.js +0 -20
- package/dist/App.ncgc9cxy.js +0 -4
- package/dist/App.p02f4ret.js +0 -1
- package/dist/App.p0fb1pds.js +0 -4
- package/dist/App.pmaq48sj.js +0 -4
- package/dist/App.yv87t9m5.js +0 -4
- package/dist/App.zjmfm8p6.js +0 -4
- package/dist/ConnectionsPage.anb3rv9a.js +0 -3
- package/dist/McpPage.y396h6fy.js +0 -3
- package/dist/SettingsPage.p1hc60gk.js +0 -3
- package/dist/SkillsPage.yj3xdsay.js +0 -3
- package/dist/TasksPage.sjv0khtv.js +0 -3
- package/dist/TelemetryPage.2qm4w16r.js +0 -3
- package/dist/TestsPage.zzs4qfj8.js +0 -3
package/src/mcp-platform.ts
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
// Built-in MCP server that exposes the Apteva platform API as MCP tools
|
|
2
2
|
// This allows the meta agent (Apteva Assistant) to control the platform
|
|
3
3
|
|
|
4
|
-
import { AgentDB, ProjectDB, McpServerDB, SkillDB, TelemetryDB, SubscriptionDB, SettingsDB, generateId } from "./db";
|
|
4
|
+
import { AgentDB, ProjectDB, McpServerDB, McpServerToolDB, SkillDB, TelemetryDB, SubscriptionDB, SettingsDB, generateId } from "./db";
|
|
5
5
|
import { TestCaseDB, TestRunDB } from "./db-tests";
|
|
6
6
|
import { runTest, runAll } from "./test-runner";
|
|
7
7
|
import { getProvidersWithStatus, PROVIDERS, ProviderKeys } from "./providers";
|
|
8
|
-
import { startAgentProcess, setAgentStatus, toApiAgent, META_AGENT_ID, agentFetch } from "./routes/api/agent-utils";
|
|
8
|
+
import { startAgentProcess, setAgentStatus, toApiAgent, META_AGENT_ID, agentFetch, fetchFromAgent } from "./routes/api/agent-utils";
|
|
9
9
|
import { agentProcesses } from "./server";
|
|
10
10
|
import { getTriggerProvider, getTriggerProviderIds, registerTriggerProvider } from "./triggers";
|
|
11
11
|
import { ComposioTriggerProvider } from "./triggers/composio";
|
|
12
12
|
import { AgentDojoTriggerProvider } from "./triggers/agentdojo";
|
|
13
|
-
import { getProvider, registerProvider } from "./integrations";
|
|
13
|
+
import { getProvider, getProviderIds, registerProvider } from "./integrations";
|
|
14
14
|
import { ComposioProvider } from "./integrations/composio";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
AgentDojoProvider,
|
|
17
|
+
listServers as listAgentDojoServers,
|
|
18
|
+
createServer as createAgentDojoServer,
|
|
19
|
+
getServer as getAgentDojoServer,
|
|
20
|
+
} from "./integrations/agentdojo";
|
|
21
|
+
import {
|
|
22
|
+
listMcpServers as listComposioServers,
|
|
23
|
+
createMcpServer as createComposioServer,
|
|
24
|
+
getAuthConfigForToolkit as getComposioAuthConfig,
|
|
25
|
+
getUserIdForAuthConfig as getComposioUserForAuth,
|
|
26
|
+
createMcpServerInstance as createComposioInstance,
|
|
27
|
+
} from "./integrations/composio";
|
|
16
28
|
|
|
17
29
|
// Register trigger + integration providers on module load
|
|
18
30
|
registerTriggerProvider(ComposioTriggerProvider);
|
|
@@ -30,6 +42,7 @@ interface JsonRpcRequest {
|
|
|
30
42
|
params?: any;
|
|
31
43
|
}
|
|
32
44
|
|
|
45
|
+
|
|
33
46
|
interface JsonRpcResponse {
|
|
34
47
|
jsonrpc: "2.0";
|
|
35
48
|
id: number;
|
|
@@ -65,7 +78,7 @@ const PLATFORM_TOOLS = [
|
|
|
65
78
|
description: `Create a new AI agent. The provider must have an API key configured — use list_providers first to check.
|
|
66
79
|
|
|
67
80
|
PROVIDERS & MODELS (use list_providers to see which have keys):
|
|
68
|
-
- anthropic: claude-sonnet-4-
|
|
81
|
+
- anthropic: claude-sonnet-4-6 (recommended), claude-sonnet-4-5, claude-haiku-4-5 (fast/cheap)
|
|
69
82
|
- openai: gpt-4o (recommended), gpt-4o-mini (fast/cheap)
|
|
70
83
|
- groq: llama-3.3-70b-versatile (recommended), llama-3.1-8b-instant (fast)
|
|
71
84
|
- gemini: gemini-3-pro-preview (recommended), gemini-3-flash-preview (fast)
|
|
@@ -111,7 +124,14 @@ TIPS:
|
|
|
111
124
|
},
|
|
112
125
|
{
|
|
113
126
|
name: "update_agent",
|
|
114
|
-
description:
|
|
127
|
+
description: `Update an existing agent's configuration. Only provide fields you want to change. If the agent is running, restart it after updating for changes to take effect.
|
|
128
|
+
|
|
129
|
+
SKILLS & MCP SERVERS:
|
|
130
|
+
- Pass skill_ids or mcp_server_ids to SET the full list (replaces existing).
|
|
131
|
+
- Use add_skills / remove_skills to add/remove individual skills without replacing the whole list.
|
|
132
|
+
- Use add_mcp_servers / remove_mcp_servers to add/remove individual MCP servers.
|
|
133
|
+
- Adding MCP servers automatically enables the mcp feature.
|
|
134
|
+
- Use list_skills and list_mcp_servers to find IDs.`,
|
|
115
135
|
inputSchema: {
|
|
116
136
|
type: "object",
|
|
117
137
|
properties: {
|
|
@@ -132,6 +152,12 @@ TIPS:
|
|
|
132
152
|
files: { type: "boolean" },
|
|
133
153
|
},
|
|
134
154
|
},
|
|
155
|
+
skill_ids: { type: "array", items: { type: "string" }, description: "Set the full list of skill IDs (replaces existing)" },
|
|
156
|
+
add_skills: { type: "array", items: { type: "string" }, description: "Skill IDs to add (keeps existing)" },
|
|
157
|
+
remove_skills: { type: "array", items: { type: "string" }, description: "Skill IDs to remove" },
|
|
158
|
+
mcp_server_ids: { type: "array", items: { type: "string" }, description: "Set the full list of MCP server IDs (replaces existing)" },
|
|
159
|
+
add_mcp_servers: { type: "array", items: { type: "string" }, description: "MCP server IDs to add (keeps existing)" },
|
|
160
|
+
remove_mcp_servers: { type: "array", items: { type: "string" }, description: "MCP server IDs to remove" },
|
|
135
161
|
},
|
|
136
162
|
required: ["agent_id"],
|
|
137
163
|
},
|
|
@@ -190,6 +216,31 @@ TIPS:
|
|
|
190
216
|
required: ["name"],
|
|
191
217
|
},
|
|
192
218
|
},
|
|
219
|
+
{
|
|
220
|
+
name: "update_project",
|
|
221
|
+
description: "Update an existing project's name, description, or color.",
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {
|
|
225
|
+
project_id: { type: "string", description: "The project ID to update" },
|
|
226
|
+
name: { type: "string", description: "New project name" },
|
|
227
|
+
description: { type: "string", description: "New project description" },
|
|
228
|
+
color: { type: "string", description: "New hex color code (e.g. #6366f1)" },
|
|
229
|
+
},
|
|
230
|
+
required: ["project_id"],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: "delete_project",
|
|
235
|
+
description: "Delete a project. Agents, MCP servers, and skills in the project will be unassigned (not deleted).",
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: {
|
|
239
|
+
project_id: { type: "string", description: "The project ID to delete" },
|
|
240
|
+
},
|
|
241
|
+
required: ["project_id"],
|
|
242
|
+
},
|
|
243
|
+
},
|
|
193
244
|
{
|
|
194
245
|
name: "list_providers",
|
|
195
246
|
description: "List all available LLM providers and their configuration status (which have API keys).",
|
|
@@ -224,12 +275,13 @@ TIPS:
|
|
|
224
275
|
description: `Create a new MCP server configuration. MCP servers provide tools that agents can use (web search, file access, APIs, etc).
|
|
225
276
|
|
|
226
277
|
SERVER TYPES:
|
|
278
|
+
- local: Runs inside the apteva server — no external process needed. Add tools with add_tool_to_local_server. Ready to use immediately after adding tools.
|
|
227
279
|
- http: Remote MCP server accessible via URL. Provide url and optional auth headers. Ready to use immediately.
|
|
228
280
|
- npm: Node.js MCP server from npm. Provide package name (e.g. '@modelcontextprotocol/server-filesystem'). Needs to be started.
|
|
229
281
|
- pip: Python MCP server from PyPI. Provide package name. Needs to be started.
|
|
230
282
|
- custom: Custom command. Provide command and args. Needs to be started.
|
|
231
283
|
|
|
232
|
-
After creating, assign to agents with assign_mcp_server_to_agent. HTTP servers work immediately; npm/pip/custom servers need to be started from the MCP page in the UI.`,
|
|
284
|
+
After creating, assign to agents with assign_mcp_server_to_agent. Local and HTTP servers work immediately; npm/pip/custom servers need to be started from the MCP page in the UI.`,
|
|
233
285
|
inputSchema: {
|
|
234
286
|
type: "object",
|
|
235
287
|
properties: {
|
|
@@ -257,27 +309,56 @@ After creating, assign to agents with assign_mcp_server_to_agent. HTTP servers w
|
|
|
257
309
|
},
|
|
258
310
|
},
|
|
259
311
|
{
|
|
260
|
-
name: "
|
|
261
|
-
description:
|
|
312
|
+
name: "add_tool_to_local_server",
|
|
313
|
+
description: `Add a tool to a local MCP server. Only works for servers with type "local".
|
|
314
|
+
|
|
315
|
+
HANDLER TYPES:
|
|
316
|
+
- mock: Returns a static/templated response. Use mock_response with template variables: {{args.field}}, {{uuid()}}, {{now}}, {{timestamp}}, {{random_int(min,max)}}.
|
|
317
|
+
- http: Makes a real HTTP API call. Provide http_config with method, url, headers, and optional body. Templates work in url/headers/body. Use {{credential.KEY}} to reference server env vars for auth.
|
|
318
|
+
- javascript: Runs custom JavaScript code. The code receives args, credentials, and helper functions (uuid, now, timestamp, random_int, random_float). Return a value or JSON object.
|
|
319
|
+
|
|
320
|
+
EXAMPLES:
|
|
321
|
+
- Mock: handler_type="mock", mock_response={"greeting": "Hello {{args.name}}!", "id": "{{uuid()}}"}
|
|
322
|
+
- HTTP: handler_type="http", http_config={"method": "GET", "url": "https://api.example.com/users/{{args.user_id}}", "headers": {"Authorization": "Bearer {{credential.API_KEY}}"}}
|
|
323
|
+
- JavaScript: handler_type="javascript", code="return { sum: args.a + args.b, computed_at: now }"`,
|
|
262
324
|
inputSchema: {
|
|
263
325
|
type: "object",
|
|
264
326
|
properties: {
|
|
265
|
-
|
|
266
|
-
|
|
327
|
+
server_id: { type: "string", description: "The local MCP server ID" },
|
|
328
|
+
name: { type: "string", description: "Tool name (snake_case, e.g. 'get_weather', 'create_ticket')" },
|
|
329
|
+
description: { type: "string", description: "What this tool does (shown to the agent)" },
|
|
330
|
+
input_schema: {
|
|
331
|
+
type: "object",
|
|
332
|
+
description: "JSON Schema for tool parameters. Must have type='object' with properties.",
|
|
333
|
+
},
|
|
334
|
+
handler_type: { type: "string", description: "How the tool executes: mock, http, or javascript" },
|
|
335
|
+
mock_response: { type: "object", description: "For mock handler: the response template (supports {{args.field}}, {{uuid()}}, {{now}})" },
|
|
336
|
+
http_config: {
|
|
337
|
+
type: "object",
|
|
338
|
+
description: "For http handler: { method, url, headers?, body? }. Templates supported in all fields.",
|
|
339
|
+
},
|
|
340
|
+
code: { type: "string", description: "For javascript handler: JS code to execute. Has access to args, credentials, uuid, now, timestamp, random_int, random_float. Must return a value." },
|
|
267
341
|
},
|
|
268
|
-
required: ["
|
|
342
|
+
required: ["server_id", "name", "description", "input_schema", "handler_type"],
|
|
269
343
|
},
|
|
270
344
|
},
|
|
271
345
|
{
|
|
272
|
-
name: "
|
|
273
|
-
description:
|
|
346
|
+
name: "update_tool_on_local_server",
|
|
347
|
+
description: `Update an existing tool on a local MCP server. Only provide fields you want to change. Only works for servers with type "local".`,
|
|
274
348
|
inputSchema: {
|
|
275
349
|
type: "object",
|
|
276
350
|
properties: {
|
|
277
|
-
|
|
278
|
-
|
|
351
|
+
tool_id: { type: "string", description: "The tool ID to update" },
|
|
352
|
+
name: { type: "string", description: "New tool name" },
|
|
353
|
+
description: { type: "string", description: "New description" },
|
|
354
|
+
input_schema: { type: "object", description: "New JSON Schema for tool parameters" },
|
|
355
|
+
handler_type: { type: "string", description: "New handler type: mock, http, or javascript" },
|
|
356
|
+
mock_response: { type: "object", description: "New mock response template" },
|
|
357
|
+
http_config: { type: "object", description: "New HTTP config: { method, url, headers?, body? }" },
|
|
358
|
+
code: { type: "string", description: "New JavaScript code" },
|
|
359
|
+
enabled: { type: "boolean", description: "Enable or disable the tool" },
|
|
279
360
|
},
|
|
280
|
-
required: ["
|
|
361
|
+
required: ["tool_id"],
|
|
281
362
|
},
|
|
282
363
|
},
|
|
283
364
|
{
|
|
@@ -300,14 +381,69 @@ After creating, assign to agents with assign_mcp_server_to_agent. HTTP servers w
|
|
|
300
381
|
required: ["agent_id", "message"],
|
|
301
382
|
},
|
|
302
383
|
},
|
|
384
|
+
// Task management on agents
|
|
385
|
+
{
|
|
386
|
+
name: "list_agent_tasks",
|
|
387
|
+
description: "List tasks on a running agent. Tasks are scheduled work items that agents execute autonomously.",
|
|
388
|
+
inputSchema: {
|
|
389
|
+
type: "object",
|
|
390
|
+
properties: {
|
|
391
|
+
agent_id: { type: "string", description: "The agent ID to list tasks from" },
|
|
392
|
+
status: { type: "string", description: "Filter by status: all, pending, running, completed, failed, cancelled (default: all)" },
|
|
393
|
+
},
|
|
394
|
+
required: ["agent_id"],
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
name: "create_agent_task",
|
|
399
|
+
description: "Create a new task on a running agent. The agent must have the tasks feature enabled.",
|
|
400
|
+
inputSchema: {
|
|
401
|
+
type: "object",
|
|
402
|
+
properties: {
|
|
403
|
+
agent_id: { type: "string", description: "The agent ID to create the task on" },
|
|
404
|
+
title: { type: "string", description: "Task title" },
|
|
405
|
+
description: { type: "string", description: "Task description / instructions for the agent" },
|
|
406
|
+
type: { type: "string", description: "Task type: once (one-time) or recurring (default: once)" },
|
|
407
|
+
priority: { type: "number", description: "Priority 1-10, higher = more important (default: 5)" },
|
|
408
|
+
execute_at: { type: "string", description: "ISO timestamp for scheduled execution (optional, for one-time tasks)" },
|
|
409
|
+
recurrence: { type: "string", description: "Cron expression for recurring tasks (e.g. '*/30 * * * *' = every 30 min)" },
|
|
410
|
+
},
|
|
411
|
+
required: ["agent_id", "title"],
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
name: "delete_agent_task",
|
|
416
|
+
description: "Delete a task from a running agent.",
|
|
417
|
+
inputSchema: {
|
|
418
|
+
type: "object",
|
|
419
|
+
properties: {
|
|
420
|
+
agent_id: { type: "string", description: "The agent ID" },
|
|
421
|
+
task_id: { type: "string", description: "The task ID to delete" },
|
|
422
|
+
},
|
|
423
|
+
required: ["agent_id", "task_id"],
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
name: "execute_agent_task",
|
|
428
|
+
description: "Immediately execute a task on a running agent, regardless of its schedule.",
|
|
429
|
+
inputSchema: {
|
|
430
|
+
type: "object",
|
|
431
|
+
properties: {
|
|
432
|
+
agent_id: { type: "string", description: "The agent ID" },
|
|
433
|
+
task_id: { type: "string", description: "The task ID to execute" },
|
|
434
|
+
},
|
|
435
|
+
required: ["agent_id", "task_id"],
|
|
436
|
+
},
|
|
437
|
+
},
|
|
303
438
|
// Skills management
|
|
304
439
|
{
|
|
305
440
|
name: "list_skills",
|
|
306
|
-
description: "List all installed skills. Skills are reusable instruction sets (like prompt templates with tool permissions) that give agents specialized capabilities. Skills can be installed from the SkillsMP marketplace or created locally.",
|
|
441
|
+
description: "List all installed skills. Skills are reusable instruction sets (like prompt templates with tool permissions) that give agents specialized capabilities. Skills can be installed from the SkillsMP marketplace or created locally. Pass project_id to only see skills scoped to that project (plus global skills).",
|
|
307
442
|
inputSchema: {
|
|
308
443
|
type: "object",
|
|
309
444
|
properties: {
|
|
310
445
|
enabled_only: { type: "boolean", description: "Only return enabled skills (optional, default false)" },
|
|
446
|
+
project_id: { type: "string", description: "Filter by project ID — returns skills scoped to this project plus global skills" },
|
|
311
447
|
},
|
|
312
448
|
},
|
|
313
449
|
},
|
|
@@ -335,27 +471,22 @@ After creating, assign to agents with assign_mcp_server_to_agent. HTTP servers w
|
|
|
335
471
|
},
|
|
336
472
|
},
|
|
337
473
|
{
|
|
338
|
-
name: "
|
|
339
|
-
description: "
|
|
474
|
+
name: "update_skill",
|
|
475
|
+
description: "Update an existing skill. Only provide fields you want to change.",
|
|
340
476
|
inputSchema: {
|
|
341
477
|
type: "object",
|
|
342
478
|
properties: {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
inputSchema: {
|
|
353
|
-
type: "object",
|
|
354
|
-
properties: {
|
|
355
|
-
agent_id: { type: "string", description: "The agent ID" },
|
|
356
|
-
skill_id: { type: "string", description: "The skill ID to remove" },
|
|
479
|
+
skill_id: { type: "string", description: "The skill ID to update" },
|
|
480
|
+
name: { type: "string", description: "New name" },
|
|
481
|
+
description: { type: "string", description: "New description" },
|
|
482
|
+
content: { type: "string", description: "New instructions content (markdown)" },
|
|
483
|
+
allowed_tools: {
|
|
484
|
+
type: "array",
|
|
485
|
+
items: { type: "string" },
|
|
486
|
+
description: "New list of allowed MCP tool names",
|
|
487
|
+
},
|
|
357
488
|
},
|
|
358
|
-
required: ["
|
|
489
|
+
required: ["skill_id"],
|
|
359
490
|
},
|
|
360
491
|
},
|
|
361
492
|
{
|
|
@@ -490,18 +621,6 @@ Use this to find trigger slugs before creating a subscription.`,
|
|
|
490
621
|
required: ["provider"],
|
|
491
622
|
},
|
|
492
623
|
},
|
|
493
|
-
{
|
|
494
|
-
name: "list_connected_accounts",
|
|
495
|
-
description: "List connected accounts (OAuth connections) for a provider. You need a connected_account_id to create a subscription. Each account represents a user's authenticated connection to a service (e.g. GitHub, Slack, Stripe).",
|
|
496
|
-
inputSchema: {
|
|
497
|
-
type: "object",
|
|
498
|
-
properties: {
|
|
499
|
-
provider: { type: "string", description: "Integration provider ID: composio or agentdojo" },
|
|
500
|
-
project_id: { type: "string", description: "Project ID for project-scoped API keys (optional)" },
|
|
501
|
-
},
|
|
502
|
-
required: ["provider"],
|
|
503
|
-
},
|
|
504
|
-
},
|
|
505
624
|
{
|
|
506
625
|
name: "list_subscriptions",
|
|
507
626
|
description: "List local trigger subscriptions. Subscriptions route incoming webhook events to agents. Each subscription maps a trigger (e.g. github:push) to a specific agent.",
|
|
@@ -515,22 +634,21 @@ Use this to find trigger slugs before creating a subscription.`,
|
|
|
515
634
|
},
|
|
516
635
|
{
|
|
517
636
|
name: "create_subscription",
|
|
518
|
-
description: `Create a trigger subscription: registers a webhook with the external service
|
|
637
|
+
description: `Create a trigger subscription: registers a webhook with the external service and routes events to an agent. The webhook URL is auto-configured — you do NOT need to provide a callback URL.
|
|
519
638
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
5. Call create_subscription with all the above
|
|
639
|
+
Just provide all 4 required fields in a single call:
|
|
640
|
+
- provider: "agentdojo" or "composio"
|
|
641
|
+
- trigger_slug: from list_trigger_types
|
|
642
|
+
- connected_account_id: from list_integration_connections
|
|
643
|
+
- agent_id: from list_agents
|
|
526
644
|
|
|
527
|
-
|
|
645
|
+
Some triggers require extra config fields (e.g. GitHub needs "owner" and "repo") — pass them in the config object.`,
|
|
528
646
|
inputSchema: {
|
|
529
647
|
type: "object",
|
|
530
648
|
properties: {
|
|
531
649
|
provider: { type: "string", description: "Trigger provider ID: composio or agentdojo" },
|
|
532
650
|
trigger_slug: { type: "string", description: "Trigger type slug (e.g. 'github:push', 'GITHUB_PUSH_EVENT'). Use list_trigger_types to find slugs." },
|
|
533
|
-
connected_account_id: { type: "string", description: "Connected account ID that owns the integration. Use
|
|
651
|
+
connected_account_id: { type: "string", description: "Connected account ID that owns the integration. Use list_integration_connections to find IDs." },
|
|
534
652
|
agent_id: { type: "string", description: "Agent ID to route trigger events to. Use list_agents to find IDs." },
|
|
535
653
|
project_id: { type: "string", description: "Project ID for scoping (optional)" },
|
|
536
654
|
config: {
|
|
@@ -581,8 +699,164 @@ IMPORTANT: Some triggers require extra config fields (e.g. GitHub triggers need
|
|
|
581
699
|
required: ["subscription_id"],
|
|
582
700
|
},
|
|
583
701
|
},
|
|
702
|
+
// Integration management tools
|
|
703
|
+
{
|
|
704
|
+
name: "list_integration_providers",
|
|
705
|
+
description: "List available integration providers (e.g. agentdojo, composio) and whether they have API keys configured. Integration providers give access to third-party apps, OAuth connections, and MCP server creation.",
|
|
706
|
+
inputSchema: {
|
|
707
|
+
type: "object",
|
|
708
|
+
properties: {
|
|
709
|
+
project_id: { type: "string", description: "Project ID for project-scoped API keys (optional)" },
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
name: "list_integration_apps",
|
|
715
|
+
description: `List available apps/toolkits from an integration provider. Each app represents a service (e.g. GitHub, Slack, Stripe) that can be connected via OAuth or API key.
|
|
716
|
+
|
|
717
|
+
Returns apps with their auth schemes (OAUTH2, API_KEY), connection status, and categories.
|
|
718
|
+
Use this to browse what's available before connecting an app.`,
|
|
719
|
+
inputSchema: {
|
|
720
|
+
type: "object",
|
|
721
|
+
properties: {
|
|
722
|
+
provider: { type: "string", description: "Integration provider ID: agentdojo or composio" },
|
|
723
|
+
project_id: { type: "string", description: "Project ID for project-scoped API keys (optional)" },
|
|
724
|
+
search: { type: "string", description: "Optional search filter for app name/slug" },
|
|
725
|
+
},
|
|
726
|
+
required: ["provider"],
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
name: "connect_integration_app",
|
|
731
|
+
description: `Connect/authenticate an app by storing credentials on the integration provider. This enables the app's tools to be used in MCP servers.
|
|
732
|
+
|
|
733
|
+
NOTE: Only API_KEY auth is supported from the assistant. For OAuth apps, direct the user to the Browse Toolkits UI.
|
|
734
|
+
|
|
735
|
+
Some apps require multiple credential fields (e.g. Pushover needs appToken + userKey). Use list_integration_apps to see credentialFields for each app — if present, pass all required fields in the "credentials" object. If the app has no credentialFields, pass a single "api_key".
|
|
736
|
+
|
|
737
|
+
WORKFLOW:
|
|
738
|
+
1. list_integration_apps to find the app and its credentialFields
|
|
739
|
+
2. connect_integration_app with the app slug and credentials
|
|
740
|
+
3. create_integration_config to create an MCP server from the connected app
|
|
741
|
+
4. add_integration_config_locally to add it as a local MCP server`,
|
|
742
|
+
inputSchema: {
|
|
743
|
+
type: "object",
|
|
744
|
+
properties: {
|
|
745
|
+
provider: { type: "string", description: "Integration provider ID: agentdojo or composio" },
|
|
746
|
+
app_slug: { type: "string", description: "App slug (from list_integration_apps)" },
|
|
747
|
+
api_key: { type: "string", description: "Single API key (for apps with no credentialFields)" },
|
|
748
|
+
credentials: {
|
|
749
|
+
type: "object",
|
|
750
|
+
description: "Credential fields as key-value pairs (e.g. { appToken: '...', userKey: '...' }). Use this for apps with multiple credentialFields. Takes priority over api_key.",
|
|
751
|
+
},
|
|
752
|
+
project_id: { type: "string", description: "Project ID for project-scoped API keys (optional)" },
|
|
753
|
+
},
|
|
754
|
+
required: ["provider", "app_slug"],
|
|
755
|
+
},
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
name: "list_integration_connections",
|
|
759
|
+
description: "List connected accounts (credentials) for an integration provider. Shows which apps have been authenticated and their status.",
|
|
760
|
+
inputSchema: {
|
|
761
|
+
type: "object",
|
|
762
|
+
properties: {
|
|
763
|
+
provider: { type: "string", description: "Integration provider ID: agentdojo or composio" },
|
|
764
|
+
project_id: { type: "string", description: "Project ID for project-scoped API keys (optional)" },
|
|
765
|
+
},
|
|
766
|
+
required: ["provider"],
|
|
767
|
+
},
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
name: "disconnect_integration_app",
|
|
771
|
+
description: "Disconnect/revoke a connected account (credential) from an integration provider.",
|
|
772
|
+
inputSchema: {
|
|
773
|
+
type: "object",
|
|
774
|
+
properties: {
|
|
775
|
+
provider: { type: "string", description: "Integration provider ID: agentdojo or composio" },
|
|
776
|
+
connection_id: { type: "string", description: "Connection/credential ID (from list_integration_connections)" },
|
|
777
|
+
project_id: { type: "string", description: "Project ID for project-scoped API keys (optional)" },
|
|
778
|
+
},
|
|
779
|
+
required: ["provider", "connection_id"],
|
|
780
|
+
},
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
name: "list_integration_configs",
|
|
784
|
+
description: "List MCP server configs on an integration provider. These are remote MCP servers hosted by the provider that can be added locally.",
|
|
785
|
+
inputSchema: {
|
|
786
|
+
type: "object",
|
|
787
|
+
properties: {
|
|
788
|
+
provider: { type: "string", description: "Integration provider ID: agentdojo or composio" },
|
|
789
|
+
project_id: { type: "string", description: "Project ID for project-scoped API keys (optional)" },
|
|
790
|
+
},
|
|
791
|
+
required: ["provider"],
|
|
792
|
+
},
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
name: "create_integration_config",
|
|
796
|
+
description: `Create an MCP server config on an integration provider from a connected app/toolkit. This creates a remote MCP server on the provider that bundles the app's tools.
|
|
797
|
+
|
|
798
|
+
After creation, use add_integration_config_locally to add it as a local MCP server.
|
|
799
|
+
|
|
800
|
+
WORKFLOW:
|
|
801
|
+
1. list_integration_apps → find the app slug
|
|
802
|
+
2. Ensure app is connected (list_integration_connections or connect_integration_app)
|
|
803
|
+
3. create_integration_config → creates remote MCP server
|
|
804
|
+
4. add_integration_config_locally → adds it locally so agents can use it`,
|
|
805
|
+
inputSchema: {
|
|
806
|
+
type: "object",
|
|
807
|
+
properties: {
|
|
808
|
+
provider: { type: "string", description: "Integration provider ID: agentdojo or composio" },
|
|
809
|
+
name: { type: "string", description: "Name for the MCP config (e.g. 'GitHub MCP', 'Slack Tools')" },
|
|
810
|
+
toolkit_slug: { type: "string", description: "Toolkit/app slug to create the config from" },
|
|
811
|
+
project_id: { type: "string", description: "Project ID for project-scoped API keys (optional)" },
|
|
812
|
+
},
|
|
813
|
+
required: ["provider", "name", "toolkit_slug"],
|
|
814
|
+
},
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
name: "add_integration_config_locally",
|
|
818
|
+
description: `Add a remote integration MCP config as a local MCP server. This creates a local MCP server entry that connects to the provider's hosted MCP endpoint, making its tools available to agents.
|
|
819
|
+
|
|
820
|
+
After adding, use assign_mcp_server_to_agent to give an agent access to these tools.`,
|
|
821
|
+
inputSchema: {
|
|
822
|
+
type: "object",
|
|
823
|
+
properties: {
|
|
824
|
+
provider: { type: "string", description: "Integration provider ID: agentdojo or composio" },
|
|
825
|
+
config_id: { type: "string", description: "Config/server ID on the provider (from list_integration_configs or create_integration_config)" },
|
|
826
|
+
project_id: { type: "string", description: "Project ID to scope the local server to (optional)" },
|
|
827
|
+
},
|
|
828
|
+
required: ["provider", "config_id"],
|
|
829
|
+
},
|
|
830
|
+
},
|
|
584
831
|
];
|
|
585
832
|
|
|
833
|
+
// Project-only tools — hidden entirely when PROJECTS_ENABLED is not set
|
|
834
|
+
const PROJECT_ONLY_TOOLS = new Set(["list_projects", "create_project", "update_project", "delete_project"]);
|
|
835
|
+
|
|
836
|
+
// Build tools list — when PROJECTS_ENABLED, add project_id to required for all tools that accept it
|
|
837
|
+
function getPlatformTools() {
|
|
838
|
+
const projectsEnabled = process.env.PROJECTS_ENABLED === "true";
|
|
839
|
+
if (!projectsEnabled) {
|
|
840
|
+
return PLATFORM_TOOLS.filter(tool => !PROJECT_ONLY_TOOLS.has(tool.name));
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
return PLATFORM_TOOLS.map(tool => {
|
|
844
|
+
const props = tool.inputSchema.properties as Record<string, unknown> | undefined;
|
|
845
|
+
if (!props || !("project_id" in props)) return tool;
|
|
846
|
+
|
|
847
|
+
const existing = (tool.inputSchema as any).required || [];
|
|
848
|
+
if (existing.includes("project_id")) return tool;
|
|
849
|
+
|
|
850
|
+
return {
|
|
851
|
+
...tool,
|
|
852
|
+
inputSchema: {
|
|
853
|
+
...tool.inputSchema,
|
|
854
|
+
required: [...existing, "project_id"],
|
|
855
|
+
},
|
|
856
|
+
};
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
|
|
586
860
|
// Tool execution handlers
|
|
587
861
|
async function executeTool(name: string, args: Record<string, any>): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
|
|
588
862
|
try {
|
|
@@ -662,8 +936,46 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
662
936
|
updates.features = { ...agent.features, ...args.features };
|
|
663
937
|
}
|
|
664
938
|
|
|
939
|
+
// Skills: set, add, or remove
|
|
940
|
+
let skills = agent.skills || [];
|
|
941
|
+
if (args.skill_ids !== undefined) {
|
|
942
|
+
skills = args.skill_ids;
|
|
943
|
+
}
|
|
944
|
+
if (args.add_skills) {
|
|
945
|
+
for (const sid of args.add_skills) {
|
|
946
|
+
if (!skills.includes(sid)) skills.push(sid);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
if (args.remove_skills) {
|
|
950
|
+
skills = skills.filter((id: string) => !args.remove_skills.includes(id));
|
|
951
|
+
}
|
|
952
|
+
if (args.skill_ids !== undefined || args.add_skills || args.remove_skills) {
|
|
953
|
+
updates.skills = skills;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// MCP servers: set, add, or remove
|
|
957
|
+
let mcpServers = agent.mcp_servers || [];
|
|
958
|
+
if (args.mcp_server_ids !== undefined) {
|
|
959
|
+
mcpServers = args.mcp_server_ids;
|
|
960
|
+
}
|
|
961
|
+
if (args.add_mcp_servers) {
|
|
962
|
+
for (const sid of args.add_mcp_servers) {
|
|
963
|
+
if (!mcpServers.includes(sid)) mcpServers.push(sid);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
if (args.remove_mcp_servers) {
|
|
967
|
+
mcpServers = mcpServers.filter((id: string) => !args.remove_mcp_servers.includes(id));
|
|
968
|
+
}
|
|
969
|
+
if (args.mcp_server_ids !== undefined || args.add_mcp_servers || args.remove_mcp_servers) {
|
|
970
|
+
updates.mcp_servers = mcpServers;
|
|
971
|
+
// Auto-enable MCP feature if servers are being added
|
|
972
|
+
if (mcpServers.length > 0 && !agent.features.mcp) {
|
|
973
|
+
updates.features = { ...(updates.features || agent.features), mcp: true };
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
665
977
|
const updated = AgentDB.update(args.agent_id, updates);
|
|
666
|
-
return { content: [{ type: "text", text: `Agent updated: ${JSON.stringify({ id: updated?.id, name: updated?.name }, null, 2)}` }] };
|
|
978
|
+
return { content: [{ type: "text", text: `Agent updated: ${JSON.stringify({ id: updated?.id, name: updated?.name, skills: updates.skills !== undefined ? updates.skills.length + " skills" : "unchanged", mcp_servers: updates.mcp_servers !== undefined ? updates.mcp_servers.length + " servers" : "unchanged" }, null, 2)}` }] };
|
|
667
979
|
}
|
|
668
980
|
|
|
669
981
|
case "delete_agent": {
|
|
@@ -740,6 +1052,36 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
740
1052
|
return { content: [{ type: "text", text: `Project created: ${JSON.stringify({ id: project.id, name: project.name, color: project.color }, null, 2)}` }] };
|
|
741
1053
|
}
|
|
742
1054
|
|
|
1055
|
+
case "update_project": {
|
|
1056
|
+
const project = ProjectDB.findById(args.project_id);
|
|
1057
|
+
if (!project) return { content: [{ type: "text", text: `Project not found: ${args.project_id}` }], isError: true };
|
|
1058
|
+
|
|
1059
|
+
const updates: Record<string, unknown> = {};
|
|
1060
|
+
if (args.name !== undefined) updates.name = args.name;
|
|
1061
|
+
if (args.description !== undefined) updates.description = args.description;
|
|
1062
|
+
if (args.color !== undefined) updates.color = args.color;
|
|
1063
|
+
|
|
1064
|
+
if (Object.keys(updates).length === 0) {
|
|
1065
|
+
return { content: [{ type: "text", text: "No updates provided" }], isError: true };
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
const updated = ProjectDB.update(args.project_id, updates);
|
|
1069
|
+
return { content: [{ type: "text", text: `Project updated: ${JSON.stringify({ id: updated!.id, name: updated!.name, description: updated!.description, color: updated!.color }, null, 2)}` }] };
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
case "delete_project": {
|
|
1073
|
+
const project = ProjectDB.findById(args.project_id);
|
|
1074
|
+
if (!project) return { content: [{ type: "text", text: `Project not found: ${args.project_id}` }], isError: true };
|
|
1075
|
+
|
|
1076
|
+
const agentCounts = ProjectDB.getAgentCounts();
|
|
1077
|
+
const agentCount = agentCounts.get(args.project_id) || 0;
|
|
1078
|
+
|
|
1079
|
+
const deleted = ProjectDB.delete(args.project_id);
|
|
1080
|
+
if (!deleted) return { content: [{ type: "text", text: "Failed to delete project" }], isError: true };
|
|
1081
|
+
|
|
1082
|
+
return { content: [{ type: "text", text: `Project "${project.name}" deleted. ${agentCount} agent(s) were unassigned.` }] };
|
|
1083
|
+
}
|
|
1084
|
+
|
|
743
1085
|
case "list_providers": {
|
|
744
1086
|
const providers = getProvidersWithStatus();
|
|
745
1087
|
const llmProviders = providers.filter(p => p.type === "llm");
|
|
@@ -754,8 +1096,8 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
754
1096
|
|
|
755
1097
|
case "list_mcp_servers": {
|
|
756
1098
|
const servers = args.project_id
|
|
757
|
-
? McpServerDB.
|
|
758
|
-
: McpServerDB.
|
|
1099
|
+
? McpServerDB.findByProjectLight(args.project_id)
|
|
1100
|
+
: McpServerDB.findAllLight();
|
|
759
1101
|
const result = servers.map(s => ({
|
|
760
1102
|
id: s.id,
|
|
761
1103
|
name: s.name,
|
|
@@ -773,7 +1115,7 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
773
1115
|
if (!server) {
|
|
774
1116
|
return { content: [{ type: "text", text: `MCP server not found: ${args.server_id}` }], isError: true };
|
|
775
1117
|
}
|
|
776
|
-
|
|
1118
|
+
const serverInfo: Record<string, any> = {
|
|
777
1119
|
id: server.id,
|
|
778
1120
|
name: server.name,
|
|
779
1121
|
type: server.type,
|
|
@@ -785,7 +1127,23 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
785
1127
|
port: server.port,
|
|
786
1128
|
source: server.source,
|
|
787
1129
|
projectId: server.project_id,
|
|
788
|
-
}
|
|
1130
|
+
};
|
|
1131
|
+
// Include tools with full details for local servers
|
|
1132
|
+
if (server.type === "local") {
|
|
1133
|
+
const tools = McpServerToolDB.findByServer(server.id);
|
|
1134
|
+
serverInfo.tools = tools.map(t => ({
|
|
1135
|
+
id: t.id,
|
|
1136
|
+
name: t.name,
|
|
1137
|
+
description: t.description,
|
|
1138
|
+
handler_type: t.handler_type,
|
|
1139
|
+
enabled: t.enabled,
|
|
1140
|
+
input_schema: t.input_schema,
|
|
1141
|
+
mock_response: t.mock_response,
|
|
1142
|
+
http_config: t.http_config,
|
|
1143
|
+
code: t.code,
|
|
1144
|
+
}));
|
|
1145
|
+
}
|
|
1146
|
+
return { content: [{ type: "text", text: JSON.stringify(serverInfo, null, 2) }] };
|
|
789
1147
|
}
|
|
790
1148
|
|
|
791
1149
|
case "create_mcp_server": {
|
|
@@ -819,38 +1177,57 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
819
1177
|
return { content: [{ type: "text", text: `MCP server deleted: ${server.name} (${server.id})` }] };
|
|
820
1178
|
}
|
|
821
1179
|
|
|
822
|
-
case "
|
|
823
|
-
const agent = AgentDB.findById(args.agent_id);
|
|
824
|
-
if (!agent) {
|
|
825
|
-
return { content: [{ type: "text", text: `Agent not found: ${args.agent_id}` }], isError: true };
|
|
826
|
-
}
|
|
1180
|
+
case "add_tool_to_local_server": {
|
|
827
1181
|
const server = McpServerDB.findById(args.server_id);
|
|
828
1182
|
if (!server) {
|
|
829
1183
|
return { content: [{ type: "text", text: `MCP server not found: ${args.server_id}` }], isError: true };
|
|
830
1184
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
return { content: [{ type: "text", text: `Server ${server.name} is already assigned to ${agent.name}` }] };
|
|
1185
|
+
if (server.type !== "local") {
|
|
1186
|
+
return { content: [{ type: "text", text: `Server "${server.name}" is type "${server.type}" — add_tool_to_local_server only works for local servers.` }], isError: true };
|
|
834
1187
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
if (
|
|
838
|
-
|
|
1188
|
+
// Check for duplicate tool name
|
|
1189
|
+
const existing = McpServerToolDB.findByServerAndName(args.server_id, args.name);
|
|
1190
|
+
if (existing) {
|
|
1191
|
+
return { content: [{ type: "text", text: `Tool "${args.name}" already exists on this server (ID: ${existing.id}). Use a different name or delete the existing tool first.` }], isError: true };
|
|
839
1192
|
}
|
|
840
|
-
|
|
1193
|
+
const tool = McpServerToolDB.create({
|
|
1194
|
+
id: generateId(),
|
|
1195
|
+
server_id: args.server_id,
|
|
1196
|
+
name: args.name,
|
|
1197
|
+
description: args.description,
|
|
1198
|
+
input_schema: args.input_schema || { type: "object", properties: {} },
|
|
1199
|
+
handler_type: args.handler_type || "mock",
|
|
1200
|
+
mock_response: args.mock_response || null,
|
|
1201
|
+
http_config: args.http_config || null,
|
|
1202
|
+
code: args.code || null,
|
|
1203
|
+
enabled: true,
|
|
1204
|
+
});
|
|
1205
|
+
return { content: [{ type: "text", text: `Tool added: ${JSON.stringify({ id: tool.id, name: tool.name, handler_type: tool.handler_type, server: server.name }, null, 2)}` }] };
|
|
841
1206
|
}
|
|
842
1207
|
|
|
843
|
-
case "
|
|
844
|
-
const
|
|
845
|
-
if (!
|
|
846
|
-
return { content: [{ type: "text", text: `
|
|
1208
|
+
case "update_tool_on_local_server": {
|
|
1209
|
+
const tool = McpServerToolDB.findById(args.tool_id);
|
|
1210
|
+
if (!tool) {
|
|
1211
|
+
return { content: [{ type: "text", text: `Tool not found: ${args.tool_id}` }], isError: true };
|
|
1212
|
+
}
|
|
1213
|
+
const server = McpServerDB.findById(tool.server_id);
|
|
1214
|
+
if (!server || server.type !== "local") {
|
|
1215
|
+
return { content: [{ type: "text", text: `Tool belongs to a non-local server — update_tool_on_local_server only works for local servers.` }], isError: true };
|
|
847
1216
|
}
|
|
848
|
-
const
|
|
849
|
-
if (
|
|
850
|
-
|
|
1217
|
+
const updates: Record<string, unknown> = {};
|
|
1218
|
+
if (args.name !== undefined) updates.name = args.name;
|
|
1219
|
+
if (args.description !== undefined) updates.description = args.description;
|
|
1220
|
+
if (args.input_schema !== undefined) updates.input_schema = args.input_schema;
|
|
1221
|
+
if (args.handler_type !== undefined) updates.handler_type = args.handler_type;
|
|
1222
|
+
if (args.mock_response !== undefined) updates.mock_response = args.mock_response;
|
|
1223
|
+
if (args.http_config !== undefined) updates.http_config = args.http_config;
|
|
1224
|
+
if (args.code !== undefined) updates.code = args.code;
|
|
1225
|
+
if (args.enabled !== undefined) updates.enabled = args.enabled;
|
|
1226
|
+
const updated = McpServerToolDB.update(args.tool_id, updates);
|
|
1227
|
+
if (!updated) {
|
|
1228
|
+
return { content: [{ type: "text", text: `Failed to update tool ${args.tool_id}` }], isError: true };
|
|
851
1229
|
}
|
|
852
|
-
|
|
853
|
-
return { content: [{ type: "text", text: `Removed MCP server from agent "${agent.name}". Restart the agent for changes to take effect.` }] };
|
|
1230
|
+
return { content: [{ type: "text", text: `Tool updated: ${JSON.stringify({ id: updated.id, name: updated.name, handler_type: updated.handler_type, enabled: updated.enabled, server: server.name }, null, 2)}` }] };
|
|
854
1231
|
}
|
|
855
1232
|
|
|
856
1233
|
case "get_dashboard_stats": {
|
|
@@ -859,7 +1236,7 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
859
1236
|
const projectCount = ProjectDB.count();
|
|
860
1237
|
const providers = getProvidersWithStatus().filter(p => p.type === "llm");
|
|
861
1238
|
const configuredProviders = providers.filter(p => p.hasKey).length;
|
|
862
|
-
const mcpServerCount = McpServerDB.
|
|
1239
|
+
const mcpServerCount = McpServerDB.count();
|
|
863
1240
|
const skillCount = SkillDB.count();
|
|
864
1241
|
|
|
865
1242
|
return {
|
|
@@ -890,7 +1267,7 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
890
1267
|
method: "POST",
|
|
891
1268
|
headers: { "Content-Type": "application/json" },
|
|
892
1269
|
body: JSON.stringify({ message: args.message }),
|
|
893
|
-
signal: AbortSignal.timeout(
|
|
1270
|
+
signal: AbortSignal.timeout(120000),
|
|
894
1271
|
});
|
|
895
1272
|
|
|
896
1273
|
if (!res.ok) {
|
|
@@ -898,16 +1275,129 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
898
1275
|
return { content: [{ type: "text", text: `Agent responded with error: ${err}` }], isError: true };
|
|
899
1276
|
}
|
|
900
1277
|
|
|
901
|
-
|
|
902
|
-
const
|
|
1278
|
+
// Agent returns SSE stream — consume and assemble content chunks
|
|
1279
|
+
const reader = res.body?.getReader();
|
|
1280
|
+
if (!reader) {
|
|
1281
|
+
return { content: [{ type: "text", text: "No response stream from agent" }], isError: true };
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
const decoder = new TextDecoder();
|
|
1285
|
+
const contentChunks: string[] = [];
|
|
1286
|
+
let raw = "";
|
|
1287
|
+
|
|
1288
|
+
while (true) {
|
|
1289
|
+
const { done, value } = await reader.read();
|
|
1290
|
+
if (done) break;
|
|
1291
|
+
raw += decoder.decode(value, { stream: true });
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// Parse SSE lines: "data: {...}"
|
|
1295
|
+
for (const line of raw.split("\n")) {
|
|
1296
|
+
const sseData = line.startsWith("data: ") ? line.slice(6) : line.trim();
|
|
1297
|
+
if (!sseData) continue;
|
|
1298
|
+
try {
|
|
1299
|
+
const evt = JSON.parse(sseData);
|
|
1300
|
+
if (evt.type === "content" && evt.content) {
|
|
1301
|
+
contentChunks.push(evt.content);
|
|
1302
|
+
}
|
|
1303
|
+
} catch {
|
|
1304
|
+
// Not valid JSON — skip
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
const reply = contentChunks.join("") || "(no response)";
|
|
903
1309
|
return { content: [{ type: "text", text: reply }] };
|
|
904
1310
|
} catch (err) {
|
|
905
1311
|
return { content: [{ type: "text", text: `Failed to communicate with agent: ${err}` }], isError: true };
|
|
906
1312
|
}
|
|
907
1313
|
}
|
|
908
1314
|
|
|
1315
|
+
case "list_agent_tasks": {
|
|
1316
|
+
const agent = AgentDB.findById(args.agent_id);
|
|
1317
|
+
if (!agent) return { content: [{ type: "text", text: `Agent not found: ${args.agent_id}` }], isError: true };
|
|
1318
|
+
if (agent.status !== "running" || !agent.port) return { content: [{ type: "text", text: `Agent ${agent.name} is not running` }], isError: true };
|
|
1319
|
+
|
|
1320
|
+
const status = args.status || "all";
|
|
1321
|
+
const data = await fetchFromAgent(agent.id, agent.port, `/tasks?status=${status}`);
|
|
1322
|
+
if (!data) return { content: [{ type: "text", text: "Failed to fetch tasks from agent" }], isError: true };
|
|
1323
|
+
|
|
1324
|
+
const tasks = (data.tasks || []).map((t: any) => ({
|
|
1325
|
+
id: t.id, title: t.title, type: t.type, status: t.status, priority: t.priority,
|
|
1326
|
+
recurrence: t.recurrence, next_run: t.next_run, execute_at: t.execute_at, created_at: t.created_at,
|
|
1327
|
+
}));
|
|
1328
|
+
return { content: [{ type: "text", text: JSON.stringify({ tasks, count: tasks.length }, null, 2) }] };
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
case "create_agent_task": {
|
|
1332
|
+
const agent = AgentDB.findById(args.agent_id);
|
|
1333
|
+
if (!agent) return { content: [{ type: "text", text: `Agent not found: ${args.agent_id}` }], isError: true };
|
|
1334
|
+
if (agent.status !== "running" || !agent.port) return { content: [{ type: "text", text: `Agent ${agent.name} is not running` }], isError: true };
|
|
1335
|
+
|
|
1336
|
+
const body: Record<string, unknown> = {
|
|
1337
|
+
title: args.title,
|
|
1338
|
+
description: args.description,
|
|
1339
|
+
type: args.type || "once",
|
|
1340
|
+
priority: args.priority || 5,
|
|
1341
|
+
};
|
|
1342
|
+
if (args.execute_at) body.execute_at = args.execute_at;
|
|
1343
|
+
if (args.recurrence) body.recurrence = args.recurrence;
|
|
1344
|
+
|
|
1345
|
+
try {
|
|
1346
|
+
const res = await agentFetch(agent.id, agent.port, "/tasks", {
|
|
1347
|
+
method: "POST",
|
|
1348
|
+
headers: { "Content-Type": "application/json" },
|
|
1349
|
+
body: JSON.stringify(body),
|
|
1350
|
+
signal: AbortSignal.timeout(5000),
|
|
1351
|
+
});
|
|
1352
|
+
const data = await res.json();
|
|
1353
|
+
if (!res.ok) return { content: [{ type: "text", text: `Failed to create task: ${data.error || res.status}` }], isError: true };
|
|
1354
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1355
|
+
} catch (err) {
|
|
1356
|
+
return { content: [{ type: "text", text: `Failed to create task: ${err}` }], isError: true };
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
case "delete_agent_task": {
|
|
1361
|
+
const agent = AgentDB.findById(args.agent_id);
|
|
1362
|
+
if (!agent) return { content: [{ type: "text", text: `Agent not found: ${args.agent_id}` }], isError: true };
|
|
1363
|
+
if (agent.status !== "running" || !agent.port) return { content: [{ type: "text", text: `Agent ${agent.name} is not running` }], isError: true };
|
|
1364
|
+
|
|
1365
|
+
try {
|
|
1366
|
+
const res = await agentFetch(agent.id, agent.port, `/tasks/${args.task_id}`, {
|
|
1367
|
+
method: "DELETE",
|
|
1368
|
+
signal: AbortSignal.timeout(5000),
|
|
1369
|
+
});
|
|
1370
|
+
const data = await res.json();
|
|
1371
|
+
if (!res.ok) return { content: [{ type: "text", text: `Failed to delete task: ${data.error || res.status}` }], isError: true };
|
|
1372
|
+
return { content: [{ type: "text", text: `Task ${args.task_id} deleted successfully` }] };
|
|
1373
|
+
} catch (err) {
|
|
1374
|
+
return { content: [{ type: "text", text: `Failed to delete task: ${err}` }], isError: true };
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
case "execute_agent_task": {
|
|
1379
|
+
const agent = AgentDB.findById(args.agent_id);
|
|
1380
|
+
if (!agent) return { content: [{ type: "text", text: `Agent not found: ${args.agent_id}` }], isError: true };
|
|
1381
|
+
if (agent.status !== "running" || !agent.port) return { content: [{ type: "text", text: `Agent ${agent.name} is not running` }], isError: true };
|
|
1382
|
+
|
|
1383
|
+
try {
|
|
1384
|
+
const res = await agentFetch(agent.id, agent.port, `/tasks/${args.task_id}/execute`, {
|
|
1385
|
+
method: "POST",
|
|
1386
|
+
signal: AbortSignal.timeout(5000),
|
|
1387
|
+
});
|
|
1388
|
+
const data = await res.json();
|
|
1389
|
+
if (!res.ok) return { content: [{ type: "text", text: `Failed to execute task: ${data.error || res.status}` }], isError: true };
|
|
1390
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1391
|
+
} catch (err) {
|
|
1392
|
+
return { content: [{ type: "text", text: `Failed to execute task: ${err}` }], isError: true };
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
|
|
909
1396
|
case "list_skills": {
|
|
910
|
-
|
|
1397
|
+
let skills = args.enabled_only ? SkillDB.findEnabled() : SkillDB.findAll();
|
|
1398
|
+
if (args.project_id) {
|
|
1399
|
+
skills = skills.filter(s => !s.project_id || s.project_id === args.project_id);
|
|
1400
|
+
}
|
|
911
1401
|
const result = skills.map(s => ({
|
|
912
1402
|
id: s.id,
|
|
913
1403
|
name: s.name,
|
|
@@ -947,34 +1437,18 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
947
1437
|
return { content: [{ type: "text", text: `Skill "${skill.name}" ${args.enabled ? "enabled" : "disabled"}` }] };
|
|
948
1438
|
}
|
|
949
1439
|
|
|
950
|
-
case "
|
|
951
|
-
const agent = AgentDB.findById(args.agent_id);
|
|
952
|
-
if (!agent) {
|
|
953
|
-
return { content: [{ type: "text", text: `Agent not found: ${args.agent_id}` }], isError: true };
|
|
954
|
-
}
|
|
1440
|
+
case "update_skill": {
|
|
955
1441
|
const skill = SkillDB.findById(args.skill_id);
|
|
956
1442
|
if (!skill) {
|
|
957
1443
|
return { content: [{ type: "text", text: `Skill not found: ${args.skill_id}` }], isError: true };
|
|
958
1444
|
}
|
|
959
|
-
const
|
|
960
|
-
if (
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
case "unassign_skill_from_agent": {
|
|
968
|
-
const agent = AgentDB.findById(args.agent_id);
|
|
969
|
-
if (!agent) {
|
|
970
|
-
return { content: [{ type: "text", text: `Agent not found: ${args.agent_id}` }], isError: true };
|
|
971
|
-
}
|
|
972
|
-
const skills = agent.skills || [];
|
|
973
|
-
if (!skills.includes(args.skill_id)) {
|
|
974
|
-
return { content: [{ type: "text", text: `Skill is not assigned to this agent` }] };
|
|
975
|
-
}
|
|
976
|
-
AgentDB.update(args.agent_id, { skills: skills.filter((id: string) => id !== args.skill_id) });
|
|
977
|
-
return { content: [{ type: "text", text: `Removed skill from agent "${agent.name}". Restart the agent for changes to take effect.` }] };
|
|
1445
|
+
const updates: Record<string, any> = {};
|
|
1446
|
+
if (args.name !== undefined) updates.name = args.name;
|
|
1447
|
+
if (args.description !== undefined) updates.description = args.description;
|
|
1448
|
+
if (args.content !== undefined) updates.content = args.content;
|
|
1449
|
+
if (args.allowed_tools !== undefined) updates.allowed_tools = args.allowed_tools;
|
|
1450
|
+
const updated = SkillDB.update(args.skill_id, updates);
|
|
1451
|
+
return { content: [{ type: "text", text: `Skill "${updated?.name || skill.name}" updated.` }] };
|
|
978
1452
|
}
|
|
979
1453
|
|
|
980
1454
|
case "create_skill": {
|
|
@@ -1138,26 +1612,6 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
1138
1612
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1139
1613
|
}
|
|
1140
1614
|
|
|
1141
|
-
case "list_connected_accounts": {
|
|
1142
|
-
const providerId = args.provider;
|
|
1143
|
-
const projectId = args.project_id || null;
|
|
1144
|
-
const integrationProvider = getProvider(providerId);
|
|
1145
|
-
if (!integrationProvider) {
|
|
1146
|
-
return { content: [{ type: "text", text: `Unknown integration provider: ${providerId}` }], isError: true };
|
|
1147
|
-
}
|
|
1148
|
-
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
1149
|
-
if (!apiKey) {
|
|
1150
|
-
return { content: [{ type: "text", text: `${integrationProvider.name} API key not configured` }], isError: true };
|
|
1151
|
-
}
|
|
1152
|
-
const accounts = await integrationProvider.listConnectedAccounts(apiKey, "platform-agent");
|
|
1153
|
-
const result = accounts.map(a => ({
|
|
1154
|
-
id: a.id,
|
|
1155
|
-
appName: a.appName,
|
|
1156
|
-
status: a.status,
|
|
1157
|
-
createdAt: a.createdAt,
|
|
1158
|
-
}));
|
|
1159
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1160
|
-
}
|
|
1161
1615
|
|
|
1162
1616
|
case "list_subscriptions": {
|
|
1163
1617
|
let subscriptions;
|
|
@@ -1218,9 +1672,11 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
1218
1672
|
}
|
|
1219
1673
|
}
|
|
1220
1674
|
|
|
1221
|
-
// Create remote trigger on the provider
|
|
1675
|
+
// Create remote trigger on the provider — auto-inject callback_url
|
|
1676
|
+
const webhookUrl = SettingsDB.get(`${providerId}_webhook_url`);
|
|
1222
1677
|
const config: Record<string, unknown> = {
|
|
1223
1678
|
agent_id: args.agent_id,
|
|
1679
|
+
...(webhookUrl ? { callback_url: webhookUrl } : {}),
|
|
1224
1680
|
...(args.config || {}),
|
|
1225
1681
|
};
|
|
1226
1682
|
const triggerResult = await triggerProvider.createTrigger(apiKey, args.trigger_slug, args.connected_account_id, config);
|
|
@@ -1313,6 +1769,272 @@ async function executeTool(name: string, args: Record<string, any>): Promise<{ c
|
|
|
1313
1769
|
return { content: [{ type: "text", text: `Subscription "${sub.trigger_slug}" deleted` }] };
|
|
1314
1770
|
}
|
|
1315
1771
|
|
|
1772
|
+
// Integration management tools
|
|
1773
|
+
case "list_integration_providers": {
|
|
1774
|
+
|
|
1775
|
+
const providerIds = getProviderIds();
|
|
1776
|
+
const projectId = args.project_id || null;
|
|
1777
|
+
const result = providerIds.map(id => {
|
|
1778
|
+
const provider = getProvider(id);
|
|
1779
|
+
const hasKey = !!ProviderKeys.getDecryptedForProject(id, projectId);
|
|
1780
|
+
return { id, name: provider?.name || id, hasKey };
|
|
1781
|
+
});
|
|
1782
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
case "list_integration_apps": {
|
|
1786
|
+
|
|
1787
|
+
const providerId = args.provider;
|
|
1788
|
+
const projectId = args.project_id || null;
|
|
1789
|
+
const provider = getProvider(providerId);
|
|
1790
|
+
if (!provider) {
|
|
1791
|
+
return { content: [{ type: "text", text: `Unknown integration provider: ${providerId}. Available: ${getProviderIds().join(", ")}` }], isError: true };
|
|
1792
|
+
}
|
|
1793
|
+
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
1794
|
+
if (!apiKey) {
|
|
1795
|
+
return { content: [{ type: "text", text: `${provider.name} API key not configured` }], isError: true };
|
|
1796
|
+
}
|
|
1797
|
+
let apps = await provider.listApps(apiKey);
|
|
1798
|
+
if (args.search) {
|
|
1799
|
+
const s = args.search.toLowerCase();
|
|
1800
|
+
apps = apps.filter(a =>
|
|
1801
|
+
a.name.toLowerCase().includes(s) ||
|
|
1802
|
+
a.slug.toLowerCase().includes(s) ||
|
|
1803
|
+
a.description?.toLowerCase().includes(s)
|
|
1804
|
+
);
|
|
1805
|
+
}
|
|
1806
|
+
// Also check which are connected
|
|
1807
|
+
let connectedIds = new Set<string>();
|
|
1808
|
+
try {
|
|
1809
|
+
const accounts = await provider.listConnectedAccounts(apiKey, "platform-agent");
|
|
1810
|
+
connectedIds = new Set(accounts.filter(a => a.status === "active").map(a => a.appId));
|
|
1811
|
+
} catch {}
|
|
1812
|
+
const result = apps.slice(0, 50).map(a => ({
|
|
1813
|
+
slug: a.slug,
|
|
1814
|
+
name: a.name,
|
|
1815
|
+
description: a.description,
|
|
1816
|
+
authSchemes: a.authSchemes,
|
|
1817
|
+
categories: a.categories,
|
|
1818
|
+
connected: connectedIds.has(a.slug) || (a.providerSlug ? connectedIds.has(a.providerSlug) : false),
|
|
1819
|
+
credentialFields: a.credentialFields || undefined,
|
|
1820
|
+
}));
|
|
1821
|
+
return { content: [{ type: "text", text: `Found ${apps.length} apps (showing ${result.length}):\n${JSON.stringify(result, null, 2)}` }] };
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
case "connect_integration_app": {
|
|
1825
|
+
const providerId = args.provider;
|
|
1826
|
+
const projectId = args.project_id || null;
|
|
1827
|
+
const provider = getProvider(providerId);
|
|
1828
|
+
if (!provider) {
|
|
1829
|
+
return { content: [{ type: "text", text: `Unknown integration provider: ${providerId}` }], isError: true };
|
|
1830
|
+
}
|
|
1831
|
+
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
1832
|
+
if (!apiKey) {
|
|
1833
|
+
return { content: [{ type: "text", text: `${provider.name} API key not configured` }], isError: true };
|
|
1834
|
+
}
|
|
1835
|
+
if (!args.api_key && !args.credentials) {
|
|
1836
|
+
return { content: [{ type: "text", text: `Either api_key or credentials is required` }], isError: true };
|
|
1837
|
+
}
|
|
1838
|
+
// Build credential object — prefer multi-field credentials over single api_key
|
|
1839
|
+
const creds: any = { authScheme: "API_KEY" as const };
|
|
1840
|
+
if (args.credentials && Object.keys(args.credentials).length > 0) {
|
|
1841
|
+
creds.fields = args.credentials;
|
|
1842
|
+
} else {
|
|
1843
|
+
creds.apiKey = args.api_key;
|
|
1844
|
+
}
|
|
1845
|
+
const connectionResult = await provider.initiateConnection(apiKey, "platform-agent", args.app_slug, "", creds);
|
|
1846
|
+
if (connectionResult.status === "active") {
|
|
1847
|
+
return { content: [{ type: "text", text: `Successfully connected "${args.app_slug}". Connection ID: ${connectionResult.connectionId || "N/A"}` }] };
|
|
1848
|
+
}
|
|
1849
|
+
return { content: [{ type: "text", text: `Connection initiated but status is ${connectionResult.status}. This may require OAuth — direct the user to the Browse Toolkits UI.` }] };
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
case "list_integration_connections": {
|
|
1853
|
+
const providerId = args.provider;
|
|
1854
|
+
const projectId = args.project_id || null;
|
|
1855
|
+
const provider = getProvider(providerId);
|
|
1856
|
+
if (!provider) {
|
|
1857
|
+
return { content: [{ type: "text", text: `Unknown integration provider: ${providerId}` }], isError: true };
|
|
1858
|
+
}
|
|
1859
|
+
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
1860
|
+
if (!apiKey) {
|
|
1861
|
+
return { content: [{ type: "text", text: `${provider.name} API key not configured` }], isError: true };
|
|
1862
|
+
}
|
|
1863
|
+
const accounts = await provider.listConnectedAccounts(apiKey, "platform-agent");
|
|
1864
|
+
const result = accounts.map(a => ({
|
|
1865
|
+
id: a.id,
|
|
1866
|
+
appName: a.appName,
|
|
1867
|
+
appId: a.appId,
|
|
1868
|
+
status: a.status,
|
|
1869
|
+
createdAt: a.createdAt,
|
|
1870
|
+
}));
|
|
1871
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
case "disconnect_integration_app": {
|
|
1875
|
+
const providerId = args.provider;
|
|
1876
|
+
const projectId = args.project_id || null;
|
|
1877
|
+
const provider = getProvider(providerId);
|
|
1878
|
+
if (!provider) {
|
|
1879
|
+
return { content: [{ type: "text", text: `Unknown integration provider: ${providerId}` }], isError: true };
|
|
1880
|
+
}
|
|
1881
|
+
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
1882
|
+
if (!apiKey) {
|
|
1883
|
+
return { content: [{ type: "text", text: `${provider.name} API key not configured` }], isError: true };
|
|
1884
|
+
}
|
|
1885
|
+
const success = await provider.disconnect(apiKey, args.connection_id);
|
|
1886
|
+
return { content: [{ type: "text", text: success ? `Disconnected successfully` : `Failed to disconnect` }], isError: !success };
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
case "list_integration_configs": {
|
|
1890
|
+
const providerId = args.provider;
|
|
1891
|
+
const projectId = args.project_id || null;
|
|
1892
|
+
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
1893
|
+
if (!apiKey) {
|
|
1894
|
+
return { content: [{ type: "text", text: `${providerId} API key not configured` }], isError: true };
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
if (providerId === "agentdojo") {
|
|
1898
|
+
const servers = await listAgentDojoServers(apiKey, true);
|
|
1899
|
+
const result = servers.map(s => ({
|
|
1900
|
+
id: s.id,
|
|
1901
|
+
name: s.name,
|
|
1902
|
+
slug: s.slug,
|
|
1903
|
+
url: s.url,
|
|
1904
|
+
toolsCount: s.tools?.length || 0,
|
|
1905
|
+
}));
|
|
1906
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1907
|
+
} else if (providerId === "composio") {
|
|
1908
|
+
const servers = await listComposioServers(apiKey);
|
|
1909
|
+
const result = servers.map(s => ({
|
|
1910
|
+
id: s.id,
|
|
1911
|
+
name: s.name,
|
|
1912
|
+
url: s.mcpUrl,
|
|
1913
|
+
toolkits: s.toolkits,
|
|
1914
|
+
}));
|
|
1915
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1916
|
+
}
|
|
1917
|
+
return { content: [{ type: "text", text: `Config listing not supported for provider: ${providerId}` }], isError: true };
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
case "create_integration_config": {
|
|
1921
|
+
const providerId = args.provider;
|
|
1922
|
+
const projectId = args.project_id || null;
|
|
1923
|
+
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
1924
|
+
if (!apiKey) {
|
|
1925
|
+
return { content: [{ type: "text", text: `${providerId} API key not configured` }], isError: true };
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
if (providerId === "agentdojo") {
|
|
1929
|
+
const server = await createAgentDojoServer(apiKey, args.name, [args.toolkit_slug]);
|
|
1930
|
+
if (!server) {
|
|
1931
|
+
return { content: [{ type: "text", text: "Failed to create MCP config on AgentDojo" }], isError: true };
|
|
1932
|
+
}
|
|
1933
|
+
return { content: [{ type: "text", text: `MCP config created on AgentDojo:\n${JSON.stringify({ id: server.id, name: server.name, slug: server.slug, url: server.url }, null, 2)}\n\nUse add_integration_config_locally to add it as a local MCP server.` }] };
|
|
1934
|
+
} else if (providerId === "composio") {
|
|
1935
|
+
// For Composio, we need the authConfigId for the toolkit
|
|
1936
|
+
const authConfigId = await getComposioAuthConfig(apiKey, args.toolkit_slug);
|
|
1937
|
+
if (!authConfigId) {
|
|
1938
|
+
return { content: [{ type: "text", text: `No auth config found for toolkit "${args.toolkit_slug}". Make sure the app is connected first.` }], isError: true };
|
|
1939
|
+
}
|
|
1940
|
+
const server = await createComposioServer(apiKey, args.name, [authConfigId]);
|
|
1941
|
+
if (!server) {
|
|
1942
|
+
return { content: [{ type: "text", text: "Failed to create MCP config on Composio" }], isError: true };
|
|
1943
|
+
}
|
|
1944
|
+
// Create server instance for the user
|
|
1945
|
+
const userId = await getComposioUserForAuth(apiKey, authConfigId);
|
|
1946
|
+
if (userId) {
|
|
1947
|
+
await createComposioInstance(apiKey, server.id, userId);
|
|
1948
|
+
}
|
|
1949
|
+
return { content: [{ type: "text", text: `MCP config created on Composio:\n${JSON.stringify({ id: server.id, name: server.name, url: server.mcpUrl }, null, 2)}\n\nUse add_integration_config_locally to add it as a local MCP server.` }] };
|
|
1950
|
+
}
|
|
1951
|
+
return { content: [{ type: "text", text: `Config creation not supported for provider: ${providerId}` }], isError: true };
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
case "add_integration_config_locally": {
|
|
1955
|
+
const providerId = args.provider;
|
|
1956
|
+
const projectId = args.project_id || null;
|
|
1957
|
+
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
1958
|
+
if (!apiKey) {
|
|
1959
|
+
return { content: [{ type: "text", text: `${providerId} API key not configured` }], isError: true };
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
const effectiveProjectId = projectId && projectId !== "unassigned" ? projectId : null;
|
|
1963
|
+
|
|
1964
|
+
if (providerId === "agentdojo") {
|
|
1965
|
+
const server = await getAgentDojoServer(apiKey, args.config_id);
|
|
1966
|
+
if (!server) {
|
|
1967
|
+
return { content: [{ type: "text", text: `Config not found on AgentDojo: ${args.config_id}` }], isError: true };
|
|
1968
|
+
}
|
|
1969
|
+
// Check if already exists locally
|
|
1970
|
+
const existing = McpServerDB.findAllLight().find(
|
|
1971
|
+
s => s.source === "agentdojo" && s.project_id === effectiveProjectId && s.url?.endsWith(`/${server.slug}`)
|
|
1972
|
+
);
|
|
1973
|
+
if (existing) {
|
|
1974
|
+
return { content: [{ type: "text", text: `Server "${server.name}" already exists locally (ID: ${existing.id})` }] };
|
|
1975
|
+
}
|
|
1976
|
+
const localServer = McpServerDB.create({
|
|
1977
|
+
id: generateId(),
|
|
1978
|
+
name: server.name,
|
|
1979
|
+
type: "http",
|
|
1980
|
+
package: null,
|
|
1981
|
+
command: null,
|
|
1982
|
+
args: null,
|
|
1983
|
+
pip_module: null,
|
|
1984
|
+
env: {},
|
|
1985
|
+
url: server.url,
|
|
1986
|
+
headers: { "X-API-Key": apiKey },
|
|
1987
|
+
source: "agentdojo",
|
|
1988
|
+
project_id: effectiveProjectId,
|
|
1989
|
+
});
|
|
1990
|
+
return { content: [{ type: "text", text: `Server "${server.name}" added locally (ID: ${localServer.id}). Use assign_mcp_server_to_agent to give agents access.` }] };
|
|
1991
|
+
} else if (providerId === "composio") {
|
|
1992
|
+
// Fetch config details from Composio
|
|
1993
|
+
const res = await fetch(`https://backend.composio.dev/api/v3/mcp/${args.config_id}`, {
|
|
1994
|
+
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
|
|
1995
|
+
});
|
|
1996
|
+
if (!res.ok) {
|
|
1997
|
+
return { content: [{ type: "text", text: `Config not found on Composio: ${args.config_id}` }], isError: true };
|
|
1998
|
+
}
|
|
1999
|
+
const data = await res.json();
|
|
2000
|
+
const configName = data.name || `composio-${args.config_id.slice(0, 8)}`;
|
|
2001
|
+
const mcpUrl = data.mcp_url;
|
|
2002
|
+
if (!mcpUrl) {
|
|
2003
|
+
return { content: [{ type: "text", text: "Config does not have an MCP URL" }], isError: true };
|
|
2004
|
+
}
|
|
2005
|
+
// Check if already exists locally
|
|
2006
|
+
const existing = McpServerDB.findAllLight().find(
|
|
2007
|
+
s => s.source === "composio" && s.url?.includes(args.config_id)
|
|
2008
|
+
);
|
|
2009
|
+
if (existing) {
|
|
2010
|
+
return { content: [{ type: "text", text: `Server "${configName}" already exists locally (ID: ${existing.id})` }] };
|
|
2011
|
+
}
|
|
2012
|
+
// Get user_id for URL auth
|
|
2013
|
+
const authConfigIds = data.auth_config_ids || [];
|
|
2014
|
+
let userId: string | null = null;
|
|
2015
|
+
if (authConfigIds.length > 0) {
|
|
2016
|
+
userId = await getComposioUserForAuth(apiKey, authConfigIds[0]);
|
|
2017
|
+
}
|
|
2018
|
+
const mcpUrlWithUser = userId ? `${mcpUrl}?user_id=${encodeURIComponent(userId)}` : mcpUrl;
|
|
2019
|
+
const localServer = McpServerDB.create({
|
|
2020
|
+
id: generateId(),
|
|
2021
|
+
name: configName,
|
|
2022
|
+
type: "http",
|
|
2023
|
+
package: null,
|
|
2024
|
+
command: null,
|
|
2025
|
+
args: null,
|
|
2026
|
+
pip_module: null,
|
|
2027
|
+
env: {},
|
|
2028
|
+
url: mcpUrlWithUser,
|
|
2029
|
+
headers: { "x-api-key": apiKey },
|
|
2030
|
+
source: "composio",
|
|
2031
|
+
project_id: effectiveProjectId,
|
|
2032
|
+
});
|
|
2033
|
+
return { content: [{ type: "text", text: `Server "${configName}" added locally (ID: ${localServer.id}). Use assign_mcp_server_to_agent to give agents access.` }] };
|
|
2034
|
+
}
|
|
2035
|
+
return { content: [{ type: "text", text: `Local add not supported for provider: ${providerId}` }], isError: true };
|
|
2036
|
+
}
|
|
2037
|
+
|
|
1316
2038
|
default:
|
|
1317
2039
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
1318
2040
|
}
|
|
@@ -1363,16 +2085,22 @@ export async function handlePlatformMcpRequest(req: Request): Promise<Response>
|
|
|
1363
2085
|
instructions: `This MCP server controls the Apteva AI agent management platform.
|
|
1364
2086
|
|
|
1365
2087
|
You can manage:
|
|
1366
|
-
- AGENTS: Create, configure, start, stop, and delete AI agents. Each agent has a provider (LLM), model, system prompt, and optional features (memory, tasks, vision, MCP tools, files).
|
|
1367
|
-
-
|
|
1368
|
-
-
|
|
1369
|
-
-
|
|
2088
|
+
- AGENTS: Create, configure, start, stop, and delete AI agents. Each agent has a provider (LLM), model, system prompt, and optional features (memory, tasks, vision, MCP tools, files). Use update_agent to assign/remove MCP servers (add_mcp_servers, remove_mcp_servers) and skills (add_skills, remove_skills).
|
|
2089
|
+
- TASKS: List, create, delete, and execute tasks on running agents. Tasks are scheduled work items that agents execute autonomously. Agents must have the tasks feature enabled. Tools: list_agent_tasks, create_agent_task, delete_agent_task, execute_agent_task.
|
|
2090
|
+
- PROJECTS: Organize agents into projects. Tools: list_projects, create_project, update_project, delete_project. Use project tools when the user is viewing All Projects. Deleting a project unassigns its agents (does not delete them).
|
|
2091
|
+
- MCP SERVERS: Tool integrations that give agents capabilities (web search, file access, APIs). Use update_agent with add_mcp_servers to assign servers to agents.
|
|
2092
|
+
- SKILLS: Reusable instruction sets that specialize agent behavior. Use create_skill to create new skills (pass project_id from context to scope to the current project). Use update_agent with add_skills/remove_skills to assign/unassign skills to agents. Tools: list_skills, get_skill, create_skill, update_skill, toggle_skill, delete_skill.
|
|
1370
2093
|
- PROVIDERS: View which LLM providers have API keys configured.
|
|
1371
|
-
- TESTS: Create and run automated tests for agent workflows. Tests send a message to an agent, then an LLM judge evaluates the response against success criteria.
|
|
1372
|
-
- SUBSCRIPTIONS & TRIGGERS: Subscribe agents to external events (webhooks). Supports multiple providers (composio, agentdojo). Use list_trigger_providers → list_trigger_types →
|
|
2094
|
+
- TESTS: Create and run automated tests for agent workflows. Tests send a message to an agent, then an LLM judge evaluates the response against success criteria. Tools: list_tests, create_test, run_test, run_all_tests, get_test_results, delete_test.
|
|
2095
|
+
- SUBSCRIPTIONS & TRIGGERS: Subscribe agents to external events (webhooks). Supports multiple providers (composio, agentdojo). Use list_trigger_providers → list_trigger_types → list_integration_connections → create_subscription. Manage with enable_subscription, disable_subscription, delete_subscription, list_subscriptions.
|
|
2096
|
+
- INTEGRATIONS: Connect third-party apps and create MCP servers from them. Supports agentdojo and composio providers. Use list_integration_providers → list_integration_apps → connect_integration_app (API key) → create_integration_config → add_integration_config_locally → then update_agent with add_mcp_servers to assign to an agent. For OAuth apps, direct the user to the Browse Toolkits UI.
|
|
2097
|
+
|
|
2098
|
+
CRITICAL: ALWAYS pass project_id to every tool call that accepts it. API keys and resources are scoped per project — calls without project_id will fail. The chat context tells you the current project id.
|
|
1373
2099
|
|
|
1374
|
-
Typical workflow: list_providers → create_agent →
|
|
1375
|
-
|
|
2100
|
+
Typical workflow: list_providers → create_agent → update_agent (add_mcp_servers/add_skills) → start_agent.
|
|
2101
|
+
Task workflow: list_agent_tasks → create_agent_task (schedule work) → execute_agent_task (run immediately).
|
|
2102
|
+
Integration workflow: list_integration_providers → list_integration_apps (browse) → connect_integration_app (API key) → create_integration_config → add_integration_config_locally → update_agent (add_mcp_servers).
|
|
2103
|
+
Subscription workflow: list_trigger_providers → list_trigger_types (pick trigger) → list_integration_connections (pick account) → create_subscription (link trigger to agent).
|
|
1376
2104
|
Test workflow: create_test (set agent, message, eval criteria) → run_test → check results.
|
|
1377
2105
|
Always use list_providers first to check which providers have API keys before creating agents.`,
|
|
1378
2106
|
};
|
|
@@ -1386,7 +2114,7 @@ Always use list_providers first to check which providers have API keys before cr
|
|
|
1386
2114
|
}
|
|
1387
2115
|
|
|
1388
2116
|
case "tools/list": {
|
|
1389
|
-
result = { tools:
|
|
2117
|
+
result = { tools: getPlatformTools() };
|
|
1390
2118
|
break;
|
|
1391
2119
|
}
|
|
1392
2120
|
|