@ateam-ai/mcp 0.3.10 → 0.3.11
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/package.json +1 -1
- package/src/http.js +18 -11
- package/src/tools.js +125 -23
package/package.json
CHANGED
package/src/http.js
CHANGED
|
@@ -60,21 +60,28 @@ export function startHttpServer(port = 3100) {
|
|
|
60
60
|
app.use(express.json());
|
|
61
61
|
|
|
62
62
|
// ─── Fix Accept header for MCP endpoints ──────────────────────────
|
|
63
|
-
//
|
|
64
|
-
//
|
|
63
|
+
// The MCP SDK requires Accept to include BOTH application/json and
|
|
64
|
+
// text/event-stream. Different clients send different combinations:
|
|
65
|
+
// - Claude.ai web: may omit text/event-stream
|
|
66
|
+
// - Claude.ai mobile: may send only text/event-stream
|
|
67
|
+
// - ChatGPT: may send only application/json
|
|
68
|
+
// We normalize to always include both to satisfy the SDK.
|
|
65
69
|
// Must patch both parsed headers AND rawHeaders since @hono/node-server
|
|
66
70
|
// reads from rawHeaders when converting to Web Standard Request.
|
|
67
71
|
for (const path of MCP_PATHS) {
|
|
68
72
|
app.use(path, (req, _res, next) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
req.rawHeaders
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
if (req.method === "POST") {
|
|
74
|
+
const accept = req.headers.accept || "";
|
|
75
|
+
const needsFix = !accept.includes("text/event-stream") || !accept.includes("application/json");
|
|
76
|
+
if (needsFix) {
|
|
77
|
+
const fixed = "application/json, text/event-stream";
|
|
78
|
+
req.headers.accept = fixed;
|
|
79
|
+
const idx = req.rawHeaders.findIndex((h) => h.toLowerCase() === "accept");
|
|
80
|
+
if (idx !== -1) {
|
|
81
|
+
req.rawHeaders[idx + 1] = fixed;
|
|
82
|
+
} else {
|
|
83
|
+
req.rawHeaders.push("Accept", fixed);
|
|
84
|
+
}
|
|
78
85
|
}
|
|
79
86
|
}
|
|
80
87
|
next();
|
package/src/tools.js
CHANGED
|
@@ -187,10 +187,47 @@ export const tools = [
|
|
|
187
187
|
description:
|
|
188
188
|
"If true (default), wait for completion. If false, return job_id immediately for polling via ateam_test_status.",
|
|
189
189
|
},
|
|
190
|
+
actor_id: {
|
|
191
|
+
type: "string",
|
|
192
|
+
description:
|
|
193
|
+
"Optional actor ID for conversation continuity. Pass the actor_id from a previous test response to continue the conversation. Omit to auto-generate a test actor (test_<timestamp>_<random>, auto-expires in 24h).",
|
|
194
|
+
},
|
|
190
195
|
},
|
|
191
196
|
required: ["solution_id", "skill_id", "message"],
|
|
192
197
|
},
|
|
193
198
|
},
|
|
199
|
+
{
|
|
200
|
+
name: "ateam_conversation",
|
|
201
|
+
core: true,
|
|
202
|
+
description:
|
|
203
|
+
"Send a message to a deployed solution and get the result. No skill_id needed — the system auto-routes to the right skill. Supports multi-turn conversations: pass the actor_id from a previous response to continue the thread (e.g., reply to a confirmation prompt). Each call creates a new job but the same actor_id maintains conversation context.",
|
|
204
|
+
inputSchema: {
|
|
205
|
+
type: "object",
|
|
206
|
+
properties: {
|
|
207
|
+
solution_id: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "The solution ID",
|
|
210
|
+
},
|
|
211
|
+
message: {
|
|
212
|
+
type: "string",
|
|
213
|
+
description: "The message to send (e.g., 'send email to X' or 'I confirm')",
|
|
214
|
+
},
|
|
215
|
+
actor_id: {
|
|
216
|
+
type: "string",
|
|
217
|
+
description: "Optional: actor ID from a previous response to continue the conversation. Omit for a new conversation.",
|
|
218
|
+
},
|
|
219
|
+
wait: {
|
|
220
|
+
type: "boolean",
|
|
221
|
+
description: "If true (default), wait for completion. If false, return job_id immediately for polling.",
|
|
222
|
+
},
|
|
223
|
+
timeout_ms: {
|
|
224
|
+
type: "number",
|
|
225
|
+
description: "Optional: max wait time in ms (default: 60000, max: 300000).",
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
required: ["solution_id", "message"],
|
|
229
|
+
},
|
|
230
|
+
},
|
|
194
231
|
{
|
|
195
232
|
name: "ateam_test_pipeline",
|
|
196
233
|
core: true,
|
|
@@ -628,9 +665,9 @@ export const tools = [
|
|
|
628
665
|
},
|
|
629
666
|
{
|
|
630
667
|
name: "ateam_test_status",
|
|
631
|
-
core:
|
|
668
|
+
core: true,
|
|
632
669
|
description:
|
|
633
|
-
"Poll the progress of an async skill test. Returns iteration count, tool call steps, status
|
|
670
|
+
"Poll the progress of an async skill test. Returns iteration count, tool call steps, status (running/completed/failed), and result when done. (Advanced — use ateam_test_skill with wait=true for synchronous testing.)",
|
|
634
671
|
inputSchema: {
|
|
635
672
|
type: "object",
|
|
636
673
|
properties: {
|
|
@@ -652,7 +689,7 @@ export const tools = [
|
|
|
652
689
|
},
|
|
653
690
|
{
|
|
654
691
|
name: "ateam_test_abort",
|
|
655
|
-
core:
|
|
692
|
+
core: true,
|
|
656
693
|
description:
|
|
657
694
|
"Abort a running skill test. Stops the job execution at the next iteration boundary. (Advanced.)",
|
|
658
695
|
inputSchema: {
|
|
@@ -1095,6 +1132,7 @@ const TENANT_TOOLS = new Set([
|
|
|
1095
1132
|
"ateam_list_solutions",
|
|
1096
1133
|
"ateam_get_solution",
|
|
1097
1134
|
"ateam_get_execution_logs",
|
|
1135
|
+
"ateam_conversation",
|
|
1098
1136
|
"ateam_test_skill",
|
|
1099
1137
|
"ateam_test_pipeline",
|
|
1100
1138
|
"ateam_test_voice",
|
|
@@ -1147,7 +1185,7 @@ const handlers = {
|
|
|
1147
1185
|
{ step: 2, action: "Build & Run", description: "Define your solution + skills + connector code, then validate, deploy, and health-check in one call. Include mcp_store with connector source code on the first deploy.", tools: ["ateam_build_and_run"] },
|
|
1148
1186
|
{ step: 3, action: "Version", description: "Every deploy auto-pushes to main on GitHub. The repo (tenant--solution-id) is the source of truth for connector code.", tools: ["ateam_github_status", "ateam_github_log"] },
|
|
1149
1187
|
{ step: 4, action: "Iterate", description: "Edit connector code ONE FILE AT A TIME via ateam_github_patch, then redeploy with ateam_build_and_run (auto-pulls from GitHub). NEVER re-pass all connector code inline after first deploy. For skill definitions, use ateam_patch.", tools: ["ateam_github_patch", "ateam_build_and_run", "ateam_patch"] },
|
|
1150
|
-
{ step: 5, action: "Test & Debug", description: "Test
|
|
1188
|
+
{ step: 5, action: "Test & Debug", description: "Test with ateam_conversation (auto-routes, supports multi-turn with actor_id for confirmations). Use ateam_test_pipeline for intent debugging, ateam_test_voice for voice. Diagnose with logs and metrics.", tools: ["ateam_conversation", "ateam_test_pipeline", "ateam_test_skill", "ateam_test_voice", "ateam_get_execution_logs", "ateam_get_metrics"] },
|
|
1151
1189
|
{ step: 6, action: "Checkpoint", description: "When solution is in a good state, create a checkpoint (safe point). You can rollback to any checkpoint if something breaks.", tools: ["ateam_github_promote", "ateam_github_list_versions"] },
|
|
1152
1190
|
],
|
|
1153
1191
|
},
|
|
@@ -1455,20 +1493,53 @@ const handlers = {
|
|
|
1455
1493
|
// Phase 2: Deploy
|
|
1456
1494
|
let deploy;
|
|
1457
1495
|
try {
|
|
1496
|
+
// Try sync first (fast for small solutions)
|
|
1458
1497
|
deploy = await post("/deploy/solution", {
|
|
1459
1498
|
solution, skills: effectiveSkills, connectors, mcp_store: effectiveMcpStore,
|
|
1460
1499
|
...(github && { skip_github_push: true }),
|
|
1461
|
-
}, sid, { timeoutMs:
|
|
1500
|
+
}, sid, { timeoutMs: 120_000 });
|
|
1462
1501
|
phases.push({ phase: "deploy", status: deploy.ok ? "done" : "failed" });
|
|
1463
1502
|
} catch (err) {
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
phase: "deployment",
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1503
|
+
const isTimeout = /524|502|503|timeout|ETIMEDOUT/i.test(err.message);
|
|
1504
|
+
if (!isTimeout) {
|
|
1505
|
+
return { ok: false, phase: "deployment", phases, error: err.message, validation_warnings: validation.warnings || [] };
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
// Timeout → retry with async mode + polling
|
|
1509
|
+
phases.push({ phase: "deploy", status: "async_retry" });
|
|
1510
|
+
try {
|
|
1511
|
+
const asyncResult = await post("/deploy/solution", {
|
|
1512
|
+
solution, skills: effectiveSkills, connectors, mcp_store: effectiveMcpStore,
|
|
1513
|
+
...(github && { skip_github_push: true }),
|
|
1514
|
+
async: true,
|
|
1515
|
+
}, sid, { timeoutMs: 15_000 });
|
|
1516
|
+
|
|
1517
|
+
if (asyncResult.job_id) {
|
|
1518
|
+
// Poll for completion (up to 10 min)
|
|
1519
|
+
const jobId = asyncResult.job_id;
|
|
1520
|
+
const maxWait = 600_000;
|
|
1521
|
+
const pollInterval = 5_000;
|
|
1522
|
+
const start = Date.now();
|
|
1523
|
+
while (Date.now() - start < maxWait) {
|
|
1524
|
+
await new Promise(r => setTimeout(r, pollInterval));
|
|
1525
|
+
try {
|
|
1526
|
+
const job = await get(`/deploy/jobs/${jobId}`, sid);
|
|
1527
|
+
if (job.status === 'done' || job.status === 'failed') {
|
|
1528
|
+
deploy = job;
|
|
1529
|
+
phases.push({ phase: "deploy", status: job.status });
|
|
1530
|
+
break;
|
|
1531
|
+
}
|
|
1532
|
+
} catch { /* keep polling */ }
|
|
1533
|
+
}
|
|
1534
|
+
if (!deploy) {
|
|
1535
|
+
return { ok: false, phase: "deployment", phases, error: "Async deploy timed out after 10 minutes", validation_warnings: validation.warnings || [],
|
|
1536
|
+
hint: "Deploy is too large even for async mode. Use incremental tools instead: ateam_patch(solution_id, target:'skill', skill_id, updates) for skill changes, ateam_upload_connector(solution_id, connector_id, github:true) for connector code changes." };
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
} catch (asyncErr) {
|
|
1540
|
+
return { ok: false, phase: "deployment", phases, error: `Sync timed out, async fallback failed: ${asyncErr.message}`, validation_warnings: validation.warnings || [],
|
|
1541
|
+
hint: "Deploy timed out. Use incremental tools: ateam_patch for skill changes, ateam_upload_connector for connector changes. These deploy one component at a time and never timeout." };
|
|
1542
|
+
}
|
|
1472
1543
|
}
|
|
1473
1544
|
|
|
1474
1545
|
if (!deploy.ok) {
|
|
@@ -1537,17 +1608,16 @@ const handlers = {
|
|
|
1537
1608
|
}
|
|
1538
1609
|
}
|
|
1539
1610
|
|
|
1540
|
-
// Phase 5: GitHub push
|
|
1611
|
+
// Phase 5: GitHub push — only when NOT deployed from GitHub
|
|
1541
1612
|
let github_result;
|
|
1542
1613
|
if (github) {
|
|
1543
|
-
|
|
1544
|
-
github_result = { skipped: true, reason: 'Deployed from GitHub — skipping push-back to avoid overwriting source of truth.' };
|
|
1614
|
+
github_result = { skipped: true, reason: 'Deployed from GitHub — push-back skipped.' };
|
|
1545
1615
|
phases.push({ phase: "github", status: "skipped", reason: "pulled_from_github" });
|
|
1546
1616
|
} else {
|
|
1547
1617
|
try {
|
|
1548
1618
|
github_result = await post(
|
|
1549
1619
|
`/deploy/solutions/${solutionId}/github/push`,
|
|
1550
|
-
{ message: `Deploy: ${solution.name || solutionId}` },
|
|
1620
|
+
{ push_to_github: true, message: `Deploy: ${solution.name || solutionId}` },
|
|
1551
1621
|
sid,
|
|
1552
1622
|
{ timeoutMs: 60_000 },
|
|
1553
1623
|
);
|
|
@@ -1599,11 +1669,15 @@ const handlers = {
|
|
|
1599
1669
|
}
|
|
1600
1670
|
phases.push({ phase: "update", status: "done" });
|
|
1601
1671
|
} catch (err) {
|
|
1672
|
+
const notFound = /not found|404|ENOENT/i.test(err.message);
|
|
1602
1673
|
return {
|
|
1603
1674
|
ok: false,
|
|
1604
1675
|
phase: "update",
|
|
1605
1676
|
error: err.message,
|
|
1606
1677
|
message: "Patch failed. Check your updates payload format.",
|
|
1678
|
+
...(notFound && {
|
|
1679
|
+
hint: "Skill not found in Builder storage. Use ateam_github_patch to edit the skill JSON directly on GitHub (path: skills/<skill-id>/skill.json), then ateam_redeploy(solution_id, skill_id) to deploy the updated skill.",
|
|
1680
|
+
}),
|
|
1607
1681
|
};
|
|
1608
1682
|
}
|
|
1609
1683
|
|
|
@@ -1650,7 +1724,7 @@ const handlers = {
|
|
|
1650
1724
|
try {
|
|
1651
1725
|
github_result = await post(
|
|
1652
1726
|
`/deploy/solutions/${solution_id}/github/push`,
|
|
1653
|
-
{ message: `Patch: ${target}${skill_id ? ` ${skill_id}` : ''} — ${Object.keys(updates || {}).join(', ')}` },
|
|
1727
|
+
{ push_to_github: true, message: `Patch: ${target}${skill_id ? ` ${skill_id}` : ''} — ${Object.keys(updates || {}).join(', ')}` },
|
|
1654
1728
|
sid,
|
|
1655
1729
|
{ timeoutMs: 60_000 },
|
|
1656
1730
|
);
|
|
@@ -1754,9 +1828,21 @@ const handlers = {
|
|
|
1754
1828
|
return get(`/deploy/solutions/${solution_id}/logs${qsStr}`, sid);
|
|
1755
1829
|
},
|
|
1756
1830
|
|
|
1757
|
-
|
|
1831
|
+
ateam_conversation: async ({ solution_id, message, actor_id, wait, timeout_ms }, sid) => {
|
|
1832
|
+
const asyncMode = wait === false;
|
|
1833
|
+
const body = {
|
|
1834
|
+
message,
|
|
1835
|
+
...(actor_id ? { actor_id } : {}),
|
|
1836
|
+
...(asyncMode ? { async: true } : {}),
|
|
1837
|
+
...(timeout_ms ? { timeout_ms } : {}),
|
|
1838
|
+
};
|
|
1839
|
+
const timeoutMs = asyncMode ? 15_000 : Math.min((timeout_ms || 60_000) + 30_000, 330_000);
|
|
1840
|
+
return post(`/deploy/solutions/${solution_id}/test`, body, sid, { timeoutMs });
|
|
1841
|
+
},
|
|
1842
|
+
|
|
1843
|
+
ateam_test_skill: async ({ solution_id, skill_id, message, wait, actor_id }, sid) => {
|
|
1758
1844
|
const asyncMode = wait === false;
|
|
1759
|
-
const body = { message, ...(asyncMode ? { async: true } : {}) };
|
|
1845
|
+
const body = { message, ...(asyncMode ? { async: true } : {}), ...(actor_id ? { actor_id } : {}) };
|
|
1760
1846
|
const timeoutMs = asyncMode ? 15_000 : 90_000;
|
|
1761
1847
|
return post(`/deploy/solutions/${solution_id}/skills/${skill_id}/test`, body, sid, { timeoutMs });
|
|
1762
1848
|
},
|
|
@@ -1800,7 +1886,7 @@ const handlers = {
|
|
|
1800
1886
|
// ─── GitHub tools ──────────────────────────────────────────────────
|
|
1801
1887
|
|
|
1802
1888
|
ateam_github_push: async ({ solution_id, message }, sid) =>
|
|
1803
|
-
post(`/deploy/solutions/${solution_id}/github/push`, { message }, sid, { timeoutMs: 60_000 }),
|
|
1889
|
+
post(`/deploy/solutions/${solution_id}/github/push`, { push_to_github: true, message }, sid, { timeoutMs: 60_000 }),
|
|
1804
1890
|
|
|
1805
1891
|
ateam_github_pull: async ({ solution_id }, sid) =>
|
|
1806
1892
|
post(`/deploy/solutions/${solution_id}/github/pull`, {}, sid, { timeoutMs: 300_000, retries: 2 }),
|
|
@@ -1849,7 +1935,23 @@ const handlers = {
|
|
|
1849
1935
|
const endpoint = skill_id
|
|
1850
1936
|
? `/deploy/solutions/${solution_id}/skills/${skill_id}/redeploy`
|
|
1851
1937
|
: `/deploy/solutions/${solution_id}/redeploy`;
|
|
1852
|
-
|
|
1938
|
+
let result;
|
|
1939
|
+
try {
|
|
1940
|
+
result = await post(endpoint, {}, sid, { timeoutMs: 300_000, retries: 2 });
|
|
1941
|
+
} catch (err) {
|
|
1942
|
+
const notFound = /not found|404|ENOENT/i.test(err.message);
|
|
1943
|
+
const isTimeout = /524|502|503|timeout|ETIMEDOUT/i.test(err.message);
|
|
1944
|
+
return {
|
|
1945
|
+
ok: false,
|
|
1946
|
+
error: err.message,
|
|
1947
|
+
...(notFound && {
|
|
1948
|
+
hint: "Skill not found in Builder storage. Edit the skill on GitHub with ateam_github_patch(solution_id, path: 'skills/<skill-id>/skill.json', search: '...', replace: '...'), then use ateam_build_and_run(solution_id, github: true) or ask the platform operator to deploy the single skill.",
|
|
1949
|
+
}),
|
|
1950
|
+
...(isTimeout && {
|
|
1951
|
+
hint: "Redeploy timed out. For large solutions, redeploy one skill at a time: ateam_redeploy(solution_id, skill_id: '<specific-skill>').",
|
|
1952
|
+
}),
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1853
1955
|
return {
|
|
1854
1956
|
ok: result.ok,
|
|
1855
1957
|
solution_id,
|
|
@@ -1917,7 +2019,7 @@ const handlers = {
|
|
|
1917
2019
|
// Push: Builder FS → GitHub
|
|
1918
2020
|
if (!pull_only) {
|
|
1919
2021
|
try {
|
|
1920
|
-
const pushResult = await post(`/deploy/solutions/${sol.id}/github/push`, {}, sid);
|
|
2022
|
+
const pushResult = await post(`/deploy/solutions/${sol.id}/github/push`, { push_to_github: true }, sid);
|
|
1921
2023
|
entry.push = { ok: true, commit: pushResult.commitSha?.slice(0, 8), files: pushResult.filesCommitted };
|
|
1922
2024
|
} catch (err) {
|
|
1923
2025
|
entry.push = { ok: false, error: err.message.slice(0, 100) };
|