@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools.js +489 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.3.40",
3
+ "version": "0.3.42",
4
4
  "mcpName": "io.github.ariekogan/ateam-mcp",
5
5
  "description": "A-Team MCP Server — build, validate, and deploy multi-agent solutions from any AI environment",
6
6
  "type": "module",
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`