@ateam-ai/mcp 0.3.40 → 0.3.42
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/tools.js +489 -0
package/package.json
CHANGED
package/src/tools.js
CHANGED
|
@@ -499,6 +499,106 @@ export const tools = [
|
|
|
499
499
|
},
|
|
500
500
|
},
|
|
501
501
|
|
|
502
|
+
{
|
|
503
|
+
name: "ateam_show_skill_minimal",
|
|
504
|
+
core: true,
|
|
505
|
+
description:
|
|
506
|
+
"Show the minimal authoring view of a skill — persona + connectors + " +
|
|
507
|
+
"handoff_when + style + policy guardrails only. ~10× smaller than " +
|
|
508
|
+
"ateam_get_solution(view:'skills') for the same skill. Use this when " +
|
|
509
|
+
"you only need the irreducible author content (Phase 9 of the strip).",
|
|
510
|
+
inputSchema: {
|
|
511
|
+
type: "object",
|
|
512
|
+
properties: {
|
|
513
|
+
solution_id: { type: "string", description: "The solution ID" },
|
|
514
|
+
skill_id: { type: "string", description: "The skill ID" },
|
|
515
|
+
},
|
|
516
|
+
required: ["solution_id", "skill_id"],
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
{
|
|
521
|
+
name: "ateam_show_solution_minimal",
|
|
522
|
+
core: true,
|
|
523
|
+
description:
|
|
524
|
+
"Show the minimal authoring view of a solution — name + description + " +
|
|
525
|
+
"style + routing_mode + identity_mode + skill ids + connector ids only. " +
|
|
526
|
+
"Skips deployed metadata, handoffs (auto-generated), grants, ui_plugins, " +
|
|
527
|
+
"validation results. Use this for fast inspection without the verbose " +
|
|
528
|
+
"fields (Phase 9 of the strip).",
|
|
529
|
+
inputSchema: {
|
|
530
|
+
type: "object",
|
|
531
|
+
properties: {
|
|
532
|
+
solution_id: { type: "string", description: "The solution ID" },
|
|
533
|
+
},
|
|
534
|
+
required: ["solution_id"],
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
{
|
|
539
|
+
name: "ateam_create_connector",
|
|
540
|
+
core: true,
|
|
541
|
+
description:
|
|
542
|
+
"Scaffold a new MCP connector with server.js + package.json + README. " +
|
|
543
|
+
"Eliminates ~50% of identical boilerplate (MCP server setup, tool registration, " +
|
|
544
|
+
"stdio transport). You then fill in the tool implementations. " +
|
|
545
|
+
"Set ui_capable=true to include ui.listPlugins / ui.getPlugin stubs " +
|
|
546
|
+
"(plugin source files added separately via ateam_create_plugin). " +
|
|
547
|
+
"After scaffolding, the files are uploaded to Core via the same path " +
|
|
548
|
+
"as ateam_upload_connector.",
|
|
549
|
+
inputSchema: {
|
|
550
|
+
type: "object",
|
|
551
|
+
properties: {
|
|
552
|
+
solution_id: { type: "string", description: "The solution ID" },
|
|
553
|
+
connector_id: {
|
|
554
|
+
type: "string",
|
|
555
|
+
description: "Connector ID (lowercase-with-dashes, no spaces). Becomes the directory name.",
|
|
556
|
+
},
|
|
557
|
+
name: {
|
|
558
|
+
type: "string",
|
|
559
|
+
description: "Human-readable name for the connector (e.g. 'Hue Lights'). Defaults to connector_id.",
|
|
560
|
+
},
|
|
561
|
+
ui_capable: {
|
|
562
|
+
type: "boolean",
|
|
563
|
+
description: "If true, include ui.listPlugins/ui.getPlugin handler stubs. Default: false.",
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
required: ["solution_id", "connector_id"],
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
{
|
|
571
|
+
name: "ateam_create_plugin",
|
|
572
|
+
core: true,
|
|
573
|
+
description:
|
|
574
|
+
"Scaffold a UI plugin (iframe HTML, React Native TSX, or both) inside an existing connector. " +
|
|
575
|
+
"Eliminates ~50% of identical plugin boilerplate (imports, theme/bridge hooks, " +
|
|
576
|
+
"postMessage protocol, default export shape). You then fill in the component body. " +
|
|
577
|
+
"Use kind='iframe' for web-only, 'rn' for mobile-only, 'adaptive' for both. " +
|
|
578
|
+
"Auto-discovery (Phase 5 of the strip) picks up the new plugin at next deploy " +
|
|
579
|
+
"without a manifest declaration.",
|
|
580
|
+
inputSchema: {
|
|
581
|
+
type: "object",
|
|
582
|
+
properties: {
|
|
583
|
+
solution_id: { type: "string", description: "The solution ID" },
|
|
584
|
+
connector_id: {
|
|
585
|
+
type: "string",
|
|
586
|
+
description: "Existing connector to add the plugin into (e.g. 'personal-assistant-ui-mcp')",
|
|
587
|
+
},
|
|
588
|
+
plugin_name: {
|
|
589
|
+
type: "string",
|
|
590
|
+
description: "Plugin name (lowercase-with-dashes). E.g. 'memories-panel'. Becomes the dir name.",
|
|
591
|
+
},
|
|
592
|
+
kind: {
|
|
593
|
+
type: "string",
|
|
594
|
+
enum: ["iframe", "rn", "adaptive"],
|
|
595
|
+
description: "Render mode. 'adaptive' (default) produces both iframe + RN scaffolds.",
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
required: ["solution_id", "connector_id", "plugin_name"],
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
|
|
502
602
|
{
|
|
503
603
|
name: "ateam_upload_connector",
|
|
504
604
|
core: true,
|
|
@@ -1281,6 +1381,264 @@ const TENANT_TOOLS = new Set([
|
|
|
1281
1381
|
/** Small delay helper */
|
|
1282
1382
|
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
1283
1383
|
|
|
1384
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
1385
|
+
// Phase 7 strip: connector + plugin scaffolds
|
|
1386
|
+
// ───────────────────────────────────────────────────────────────────
|
|
1387
|
+
// Pure client-side templates. ateam_create_connector / ateam_create_plugin
|
|
1388
|
+
// produce file contents + push them via the existing /deploy/.../upload
|
|
1389
|
+
// endpoint. The author writes only the unique tool implementations and
|
|
1390
|
+
// component bodies; the ~50% of boilerplate per connector/plugin (MCP
|
|
1391
|
+
// server setup, theme/bridge hooks, postMessage protocol, package.json)
|
|
1392
|
+
// is template-generated.
|
|
1393
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
1394
|
+
|
|
1395
|
+
function _scaffoldConnectorFiles({ connectorId, displayName, uiCapable }) {
|
|
1396
|
+
const safeName = displayName || connectorId;
|
|
1397
|
+
const files = [];
|
|
1398
|
+
|
|
1399
|
+
// server.js — minimal stdio MCP server with one tool stub + ui handlers if ui_capable
|
|
1400
|
+
const serverJs = `#!/usr/bin/env node
|
|
1401
|
+
// ${connectorId} — stdio MCP server. Generated by ateam_create_connector.
|
|
1402
|
+
//
|
|
1403
|
+
// Fill in the tool implementations below. The MCP scaffolding
|
|
1404
|
+
// (server setup, tool registration, error handling, stdio transport)
|
|
1405
|
+
// is template-provided. You write the integration logic.
|
|
1406
|
+
|
|
1407
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1408
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1409
|
+
|
|
1410
|
+
const server = new Server(
|
|
1411
|
+
{ name: "${connectorId}", version: "1.0.0" },
|
|
1412
|
+
{ capabilities: { tools: {}${uiCapable ? ', ui: {}' : ''} } },
|
|
1413
|
+
);
|
|
1414
|
+
|
|
1415
|
+
// ── Tool definitions ────────────────────────────────────────────────
|
|
1416
|
+
const TOOLS = [
|
|
1417
|
+
{
|
|
1418
|
+
name: "${connectorId}.echo",
|
|
1419
|
+
description: "Echo back the input. Replace with your real tools.",
|
|
1420
|
+
inputSchema: {
|
|
1421
|
+
type: "object",
|
|
1422
|
+
properties: { message: { type: "string" } },
|
|
1423
|
+
required: ["message"],
|
|
1424
|
+
},
|
|
1425
|
+
},
|
|
1426
|
+
];
|
|
1427
|
+
|
|
1428
|
+
server.setRequestHandler({ method: "tools/list" }, async () => ({ tools: TOOLS }));
|
|
1429
|
+
|
|
1430
|
+
server.setRequestHandler({ method: "tools/call" }, async (req) => {
|
|
1431
|
+
const { name, arguments: args } = req.params;
|
|
1432
|
+
switch (name) {
|
|
1433
|
+
case "${connectorId}.echo":
|
|
1434
|
+
return { content: [{ type: "text", text: \`Echo: \${args.message}\` }] };
|
|
1435
|
+
default:
|
|
1436
|
+
throw new Error(\`Unknown tool: \${name}\`);
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1439
|
+
${uiCapable ? `
|
|
1440
|
+
// ── UI plugin handlers (ui_capable connector) ──────────────────────
|
|
1441
|
+
server.setRequestHandler({ method: "ui.listPlugins" }, async () => ({
|
|
1442
|
+
plugins: [
|
|
1443
|
+
// List your plugins here. Plugin source files live in
|
|
1444
|
+
// plugins/<plugin-name>/ (RN) and ui-dist/<plugin-name>/ (iframe).
|
|
1445
|
+
// Plugin manifests are auto-discovered at deploy time per Phase 5
|
|
1446
|
+
// of the strip — no need to repeat them here unless you want overrides.
|
|
1447
|
+
],
|
|
1448
|
+
}));
|
|
1449
|
+
|
|
1450
|
+
server.setRequestHandler({ method: "ui.getPlugin" }, async (req) => {
|
|
1451
|
+
const { id } = req.params;
|
|
1452
|
+
// Return plugin manifest by id. Auto-discovery covers the default case.
|
|
1453
|
+
return { ok: false, error: \`Plugin \${id} not found\` };
|
|
1454
|
+
});
|
|
1455
|
+
` : ''}
|
|
1456
|
+
// ── Boot ────────────────────────────────────────────────────────────
|
|
1457
|
+
const transport = new StdioServerTransport();
|
|
1458
|
+
await server.connect(transport);
|
|
1459
|
+
console.error("[${connectorId}] connected via stdio");
|
|
1460
|
+
`;
|
|
1461
|
+
files.push({ path: "server.js", content: serverJs });
|
|
1462
|
+
|
|
1463
|
+
// package.json
|
|
1464
|
+
const pkg = {
|
|
1465
|
+
name: connectorId,
|
|
1466
|
+
version: "1.0.0",
|
|
1467
|
+
type: "module",
|
|
1468
|
+
description: `${safeName} — A-Team MCP connector`,
|
|
1469
|
+
main: "server.js",
|
|
1470
|
+
dependencies: {
|
|
1471
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
1472
|
+
},
|
|
1473
|
+
};
|
|
1474
|
+
files.push({ path: "package.json", content: JSON.stringify(pkg, null, 2) + "\n" });
|
|
1475
|
+
|
|
1476
|
+
// README.md
|
|
1477
|
+
const readme = `# ${safeName}
|
|
1478
|
+
|
|
1479
|
+
Connector ID: \`${connectorId}\`
|
|
1480
|
+
${uiCapable ? "UI-capable: yes" : ""}
|
|
1481
|
+
|
|
1482
|
+
## Adding tools
|
|
1483
|
+
|
|
1484
|
+
Edit \`server.js\`. Add entries to the \`TOOLS\` array, then add a
|
|
1485
|
+
matching case in the \`tools/call\` handler.
|
|
1486
|
+
|
|
1487
|
+
## Adding UI plugins (ui_capable connectors)
|
|
1488
|
+
|
|
1489
|
+
Drop iframe plugins under \`ui-dist/<plugin-name>/index.html\` and/or
|
|
1490
|
+
React Native plugins under \`plugins/<plugin-name>/index.tsx\`.
|
|
1491
|
+
Phase 5 of the strip auto-discovers them at deploy — no manifest
|
|
1492
|
+
declaration needed unless you want overrides (drop a
|
|
1493
|
+
\`manifest.json\` next to the source).
|
|
1494
|
+
|
|
1495
|
+
## Deploy
|
|
1496
|
+
|
|
1497
|
+
Use \`ateam_upload_connector\` to push the latest source to Core
|
|
1498
|
+
without a full skill redeploy.
|
|
1499
|
+
`;
|
|
1500
|
+
files.push({ path: "README.md", content: readme });
|
|
1501
|
+
|
|
1502
|
+
return files;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
function _scaffoldPluginFiles({ connectorId, pluginName, kind }) {
|
|
1506
|
+
const files = [];
|
|
1507
|
+
|
|
1508
|
+
if (kind === "iframe" || kind === "adaptive") {
|
|
1509
|
+
const html = `<!DOCTYPE html>
|
|
1510
|
+
<html lang="en">
|
|
1511
|
+
<head>
|
|
1512
|
+
<meta charset="UTF-8" />
|
|
1513
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1514
|
+
<title>${pluginName}</title>
|
|
1515
|
+
<style>
|
|
1516
|
+
body { font-family: system-ui, sans-serif; padding: 16px; margin: 0; }
|
|
1517
|
+
.card { background: #f5f5f5; padding: 12px; border-radius: 8px; }
|
|
1518
|
+
</style>
|
|
1519
|
+
</head>
|
|
1520
|
+
<body>
|
|
1521
|
+
<div class="card">
|
|
1522
|
+
<h2>${pluginName}</h2>
|
|
1523
|
+
<p>Plugin body — replace with your real UI.</p>
|
|
1524
|
+
<button id="callTool">Call sample tool</button>
|
|
1525
|
+
<pre id="output"></pre>
|
|
1526
|
+
</div>
|
|
1527
|
+
<script type="module">
|
|
1528
|
+
// ── Plugin postMessage protocol scaffold ────────────────────────
|
|
1529
|
+
// 'adas-host' messages come FROM the host shell (web app / mobile).
|
|
1530
|
+
// 'adas-plugin' messages go TO the host.
|
|
1531
|
+
// Use mcpCall(tool, args) to invoke any tool the skill has access to.
|
|
1532
|
+
|
|
1533
|
+
function mcpCall(tool, args = {}, connectorId) {
|
|
1534
|
+
return new Promise((resolve, reject) => {
|
|
1535
|
+
const id = "call_" + Math.random().toString(36).slice(2);
|
|
1536
|
+
const listener = (e) => {
|
|
1537
|
+
if (e?.data?.type !== "adas-host") return;
|
|
1538
|
+
if (e?.data?.requestId !== id) return;
|
|
1539
|
+
window.removeEventListener("message", listener);
|
|
1540
|
+
if (e.data.error) reject(new Error(e.data.error));
|
|
1541
|
+
else resolve(e.data.result);
|
|
1542
|
+
};
|
|
1543
|
+
window.addEventListener("message", listener);
|
|
1544
|
+
window.parent?.postMessage({
|
|
1545
|
+
type: "adas-plugin",
|
|
1546
|
+
action: "mcpCall",
|
|
1547
|
+
requestId: id,
|
|
1548
|
+
tool, args, connectorId,
|
|
1549
|
+
}, "*");
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
document.getElementById("callTool").addEventListener("click", async () => {
|
|
1554
|
+
try {
|
|
1555
|
+
const result = await mcpCall("${connectorId}.echo", { message: "hello" });
|
|
1556
|
+
document.getElementById("output").textContent = JSON.stringify(result, null, 2);
|
|
1557
|
+
} catch (err) {
|
|
1558
|
+
document.getElementById("output").textContent = "Error: " + err.message;
|
|
1559
|
+
}
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1562
|
+
// Tell host we're ready
|
|
1563
|
+
window.parent?.postMessage({ type: "adas-plugin", action: "ready" }, "*");
|
|
1564
|
+
</script>
|
|
1565
|
+
</body>
|
|
1566
|
+
</html>
|
|
1567
|
+
`;
|
|
1568
|
+
files.push({
|
|
1569
|
+
path: `ui-dist/${pluginName}/index.html`,
|
|
1570
|
+
content: html,
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
if (kind === "rn" || kind === "adaptive") {
|
|
1575
|
+
const tsx = `// ${pluginName} — React Native plugin. Generated by ateam_create_plugin.
|
|
1576
|
+
//
|
|
1577
|
+
// Fill in the Component body. Imports, hooks, default export shape are
|
|
1578
|
+
// template-provided — Phase 7 of the strip eliminates this boilerplate.
|
|
1579
|
+
|
|
1580
|
+
import React, { useState } from 'react';
|
|
1581
|
+
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
|
|
1582
|
+
import { useApi } from '../../plugin-sdk';
|
|
1583
|
+
import type { PluginProps } from '../../plugin-sdk/types';
|
|
1584
|
+
|
|
1585
|
+
// Plain object export — NO PluginSDK.register() (pollutes shared registry).
|
|
1586
|
+
export default {
|
|
1587
|
+
id: '${pluginName}',
|
|
1588
|
+
type: 'ui',
|
|
1589
|
+
version: '1.0.0',
|
|
1590
|
+
capabilities: { haptics: true },
|
|
1591
|
+
|
|
1592
|
+
Component({ bridge, native, theme }: PluginProps) {
|
|
1593
|
+
const api = useApi(bridge);
|
|
1594
|
+
const [output, setOutput] = useState<string>('');
|
|
1595
|
+
|
|
1596
|
+
const handlePress = async () => {
|
|
1597
|
+
try {
|
|
1598
|
+
native?.haptics?.selection?.();
|
|
1599
|
+
const result = await api.call('${connectorId}.echo', { message: 'hello' });
|
|
1600
|
+
setOutput(JSON.stringify(result, null, 2));
|
|
1601
|
+
} catch (err: any) {
|
|
1602
|
+
native?.haptics?.error?.();
|
|
1603
|
+
setOutput('Error: ' + err.message);
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
|
|
1607
|
+
const styles = StyleSheet.create({
|
|
1608
|
+
container: { padding: 16, backgroundColor: theme.colors.bg, flex: 1 },
|
|
1609
|
+
card: { backgroundColor: theme.colors.surface, padding: 12, borderRadius: 8 },
|
|
1610
|
+
title: { fontSize: 18, fontWeight: '600', color: theme.colors.text, marginBottom: 8 },
|
|
1611
|
+
button: { backgroundColor: theme.colors.accent, padding: 12, borderRadius: 6, marginTop: 12 },
|
|
1612
|
+
buttonText: { color: '#fff', textAlign: 'center', fontWeight: '600' },
|
|
1613
|
+
output: { color: theme.colors.textMuted, marginTop: 12, fontFamily: 'Menlo' },
|
|
1614
|
+
});
|
|
1615
|
+
|
|
1616
|
+
return (
|
|
1617
|
+
<View style={styles.container}>
|
|
1618
|
+
<View style={styles.card}>
|
|
1619
|
+
<Text style={styles.title}>${pluginName}</Text>
|
|
1620
|
+
<Text style={{ color: theme.colors.textMuted }}>
|
|
1621
|
+
Plugin body — replace with your real UI.
|
|
1622
|
+
</Text>
|
|
1623
|
+
<TouchableOpacity style={styles.button} onPress={handlePress}>
|
|
1624
|
+
<Text style={styles.buttonText}>Call sample tool</Text>
|
|
1625
|
+
</TouchableOpacity>
|
|
1626
|
+
{!!output && <Text style={styles.output}>{output}</Text>}
|
|
1627
|
+
</View>
|
|
1628
|
+
</View>
|
|
1629
|
+
);
|
|
1630
|
+
},
|
|
1631
|
+
};
|
|
1632
|
+
`;
|
|
1633
|
+
files.push({
|
|
1634
|
+
path: `plugins/${pluginName}/index.tsx`,
|
|
1635
|
+
content: tsx,
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
return files;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1284
1642
|
const handlers = {
|
|
1285
1643
|
ateam_bootstrap: async () => ({
|
|
1286
1644
|
platform_positioning: {
|
|
@@ -2408,6 +2766,137 @@ const handlers = {
|
|
|
2408
2766
|
{ timeoutMs: 300_000, retries: 1 },
|
|
2409
2767
|
),
|
|
2410
2768
|
|
|
2769
|
+
// ── Phase 9 strip: focused minimal responses ────────────────────────
|
|
2770
|
+
ateam_show_skill_minimal: async ({ solution_id, skill_id }, sid) => {
|
|
2771
|
+
if (!solution_id) throw new Error("solution_id required");
|
|
2772
|
+
if (!skill_id) throw new Error("skill_id required");
|
|
2773
|
+
const full = await get(`/deploy/solutions/${solution_id}/skills/${skill_id}`, sid);
|
|
2774
|
+
const skill = full?.skill || full;
|
|
2775
|
+
if (!skill) return { ok: false, error: "skill not found" };
|
|
2776
|
+
return {
|
|
2777
|
+
ok: true,
|
|
2778
|
+
id: skill.id,
|
|
2779
|
+
name: skill.name || skill.id,
|
|
2780
|
+
description: skill.description || "",
|
|
2781
|
+
role: { persona: skill.role?.persona || "" },
|
|
2782
|
+
connectors: skill.connectors || [],
|
|
2783
|
+
handoff_when: skill.handoff_when || null,
|
|
2784
|
+
style: skill.style || null,
|
|
2785
|
+
excluded_tools: skill.excluded_tools || [],
|
|
2786
|
+
policy_guardrails: {
|
|
2787
|
+
never: skill.policy?.guardrails?.never || [],
|
|
2788
|
+
always: skill.policy?.guardrails?.always || [],
|
|
2789
|
+
},
|
|
2790
|
+
engine: typeof skill.engine === "string" ? skill.engine : (skill.engine ? "<explicit-object>" : null),
|
|
2791
|
+
_hint: "This is the MINIMAL view (Phase 9 strip). Use ateam_get_solution(view:'skills', skill_id) for the full schema.",
|
|
2792
|
+
};
|
|
2793
|
+
},
|
|
2794
|
+
|
|
2795
|
+
ateam_show_solution_minimal: async ({ solution_id }, sid) => {
|
|
2796
|
+
if (!solution_id) throw new Error("solution_id required");
|
|
2797
|
+
const full = await get(`/deploy/solutions/${solution_id}/definition`, sid);
|
|
2798
|
+
const sol = full?.solution || full;
|
|
2799
|
+
if (!sol) return { ok: false, error: "solution not found" };
|
|
2800
|
+
return {
|
|
2801
|
+
ok: true,
|
|
2802
|
+
id: sol.id,
|
|
2803
|
+
name: sol.name || sol.id,
|
|
2804
|
+
description: sol.description || "",
|
|
2805
|
+
version: sol.version || "1.0.0",
|
|
2806
|
+
style: sol.style || null,
|
|
2807
|
+
routing_mode: sol.routing_mode || "manual",
|
|
2808
|
+
identity_mode: sol.identity_mode || null,
|
|
2809
|
+
identity: sol.identity ? {
|
|
2810
|
+
default_actor_type: sol.identity.default_actor_type,
|
|
2811
|
+
actor_types_count: (sol.identity.actor_types || []).length,
|
|
2812
|
+
} : null,
|
|
2813
|
+
skills: (sol.skills || []).map(s => ({
|
|
2814
|
+
id: s.id,
|
|
2815
|
+
name: s.name || s.id,
|
|
2816
|
+
role: s.role || "worker",
|
|
2817
|
+
})),
|
|
2818
|
+
connectors_count: (sol.platform_connectors || []).length,
|
|
2819
|
+
ui_plugins_count: (sol.ui_plugins || []).length,
|
|
2820
|
+
handoffs_count: (sol.handoffs || []).length,
|
|
2821
|
+
_hint: "This is the MINIMAL view (Phase 9 strip). Use ateam_get_solution(view:'definition') for the full schema.",
|
|
2822
|
+
};
|
|
2823
|
+
},
|
|
2824
|
+
|
|
2825
|
+
// ── Phase 7 strip: scaffold helpers ─────────────────────────────────
|
|
2826
|
+
ateam_create_connector: async ({ solution_id, connector_id, name, ui_capable }, sid) => {
|
|
2827
|
+
if (!solution_id) throw new Error("solution_id required");
|
|
2828
|
+
if (!connector_id) throw new Error("connector_id required");
|
|
2829
|
+
if (!/^[a-z][a-z0-9-]*$/.test(connector_id)) {
|
|
2830
|
+
throw new Error("connector_id must be lowercase letters/digits/dashes only");
|
|
2831
|
+
}
|
|
2832
|
+
const files = _scaffoldConnectorFiles({
|
|
2833
|
+
connectorId: connector_id,
|
|
2834
|
+
displayName: name || connector_id,
|
|
2835
|
+
uiCapable: !!ui_capable,
|
|
2836
|
+
});
|
|
2837
|
+
const result = await post(
|
|
2838
|
+
`/deploy/solutions/${solution_id}/connectors/${connector_id}/upload`,
|
|
2839
|
+
{ files },
|
|
2840
|
+
sid,
|
|
2841
|
+
{ timeoutMs: 120_000, retries: 1 },
|
|
2842
|
+
);
|
|
2843
|
+
return {
|
|
2844
|
+
ok: true,
|
|
2845
|
+
connector_id,
|
|
2846
|
+
files_created: files.map(f => f.path),
|
|
2847
|
+
ui_capable: !!ui_capable,
|
|
2848
|
+
upload_result: result,
|
|
2849
|
+
next_steps: [
|
|
2850
|
+
`Edit server.js to add your real tools (replace the echo stub).`,
|
|
2851
|
+
ui_capable
|
|
2852
|
+
? `Use ateam_create_plugin to scaffold your first UI plugin.`
|
|
2853
|
+
: null,
|
|
2854
|
+
`Use ateam_test_connector or ateam_build_and_run to deploy + test.`,
|
|
2855
|
+
].filter(Boolean),
|
|
2856
|
+
};
|
|
2857
|
+
},
|
|
2858
|
+
|
|
2859
|
+
ateam_create_plugin: async ({ solution_id, connector_id, plugin_name, kind }, sid) => {
|
|
2860
|
+
if (!solution_id) throw new Error("solution_id required");
|
|
2861
|
+
if (!connector_id) throw new Error("connector_id required");
|
|
2862
|
+
if (!plugin_name) throw new Error("plugin_name required");
|
|
2863
|
+
if (!/^[a-z][a-z0-9-]*$/.test(plugin_name)) {
|
|
2864
|
+
throw new Error("plugin_name must be lowercase letters/digits/dashes only");
|
|
2865
|
+
}
|
|
2866
|
+
const k = kind || "adaptive";
|
|
2867
|
+
if (!["iframe", "rn", "adaptive"].includes(k)) {
|
|
2868
|
+
throw new Error(`kind must be one of: iframe, rn, adaptive (got ${k})`);
|
|
2869
|
+
}
|
|
2870
|
+
const files = _scaffoldPluginFiles({
|
|
2871
|
+
connectorId: connector_id,
|
|
2872
|
+
pluginName: plugin_name,
|
|
2873
|
+
kind: k,
|
|
2874
|
+
});
|
|
2875
|
+
const result = await post(
|
|
2876
|
+
`/deploy/solutions/${solution_id}/connectors/${connector_id}/upload`,
|
|
2877
|
+
{ files },
|
|
2878
|
+
sid,
|
|
2879
|
+
{ timeoutMs: 120_000, retries: 1 },
|
|
2880
|
+
);
|
|
2881
|
+
return {
|
|
2882
|
+
ok: true,
|
|
2883
|
+
plugin_id: `mcp:${connector_id}:${plugin_name}`,
|
|
2884
|
+
kind: k,
|
|
2885
|
+
files_created: files.map(f => f.path),
|
|
2886
|
+
upload_result: result,
|
|
2887
|
+
next_steps: [
|
|
2888
|
+
k === "rn" || k === "adaptive"
|
|
2889
|
+
? `Edit plugins/${plugin_name}/index.tsx — fill in the Component body.`
|
|
2890
|
+
: null,
|
|
2891
|
+
k === "iframe" || k === "adaptive"
|
|
2892
|
+
? `Edit ui-dist/${plugin_name}/index.html — replace the placeholder UI.`
|
|
2893
|
+
: null,
|
|
2894
|
+
`Phase 5 auto-discovery will register the plugin at next deploy.`,
|
|
2895
|
+
`Optional: drop a manifest.json next to the source for custom commands/capabilities.`,
|
|
2896
|
+
].filter(Boolean),
|
|
2897
|
+
};
|
|
2898
|
+
},
|
|
2899
|
+
|
|
2411
2900
|
ateam_redeploy: async ({ solution_id, skill_id }, sid) => {
|
|
2412
2901
|
const endpoint = skill_id
|
|
2413
2902
|
? `/deploy/solutions/${solution_id}/skills/${skill_id}/redeploy`
|