@ateam-ai/mcp 0.1.12 → 0.2.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.1.12",
3
+ "version": "0.2.1",
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/api.js CHANGED
@@ -107,6 +107,17 @@ export function isAuthenticated(sessionId) {
107
107
  return apiKey.length > 0;
108
108
  }
109
109
 
110
+ /**
111
+ * Check if a session has been explicitly authenticated via ateam_auth.
112
+ * This checks ONLY per-session credentials, ignoring env vars.
113
+ * Used to gate mutating operations — env vars alone are not sufficient
114
+ * to deploy, update, or delete solutions.
115
+ */
116
+ export function isExplicitlyAuthenticated(sessionId) {
117
+ if (!sessionId) return false;
118
+ return sessions.has(sessionId);
119
+ }
120
+
110
121
  /**
111
122
  * Remove session credentials (on disconnect).
112
123
  */
@@ -127,6 +138,7 @@ function headers(sessionId) {
127
138
  */
128
139
  function formatError(method, path, status, body) {
129
140
  const hints = {
141
+ 400: "Bad request — see the error details above for what to fix.",
130
142
  401: "Your API key may be invalid or expired. Get a valid key at https://mcp.ateam-ai.com/get-api-key then call ateam_auth(api_key: \"your_key\").",
131
143
  403: "You don't have permission for this operation. Check your tenant and API key. Get a key at https://mcp.ateam-ai.com/get-api-key",
132
144
  404: "Resource not found. Check the solution_id or skill_id you're using. Use ateam_list_solutions to see available solutions.",
@@ -139,7 +151,7 @@ function formatError(method, path, status, body) {
139
151
  };
140
152
 
141
153
  const hint = hints[status] || "";
142
- const detail = typeof body === "string" && body.length > 0 && body.length < 500 ? body : "";
154
+ const detail = typeof body === "string" && body.length > 0 && body.length < 2000 ? body : "";
143
155
 
144
156
  let msg = `A-Team API error: ${method} ${path} returned ${status}`;
145
157
  if (detail) msg += ` — ${detail}`;
package/src/server.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  CallToolRequestSchema,
11
11
  ListToolsRequestSchema,
12
12
  } from "@modelcontextprotocol/sdk/types.js";
13
- import { tools, handleToolCall } from "./tools.js";
13
+ import { tools, coreTools, handleToolCall } from "./tools.js";
14
14
 
15
15
  /**
16
16
  * @param {string} sessionId — identifier for credential isolation.
@@ -18,7 +18,7 @@ import { tools, handleToolCall } from "./tools.js";
18
18
  */
19
19
  export function createServer(sessionId = "stdio") {
20
20
  const server = new Server(
21
- { name: "ateam-mcp", version: "0.2.0" },
21
+ { name: "ateam-mcp", version: "0.3.0" },
22
22
  {
23
23
  capabilities: { tools: {} },
24
24
  instructions: [
@@ -30,7 +30,9 @@ export function createServer(sessionId = "stdio") {
30
30
  }
31
31
  );
32
32
 
33
- server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
33
+ // Only advertise core tools — advanced tools are still callable but not listed.
34
+ // This reduces cognitive load from 23+ tools to ~11 in the tool surface.
35
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: coreTools }));
34
36
 
35
37
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
36
38
  const { name, arguments: args } = request.params;
package/src/tools.js CHANGED
@@ -1,18 +1,29 @@
1
1
  /**
2
2
  * A-Team MCP tool definitions and handlers.
3
- * 18 tools covering the full A-Team External Agent API + auth + bootstrap.
3
+ *
4
+ * Tools are split into two tiers:
5
+ * - Core tools (core: true) — shown in tools/list, the simplified developer loop
6
+ * - Advanced tools (core: false) — hidden from tools/list, still callable by name
7
+ *
8
+ * Core loop: bootstrap → auth → get_spec/examples → build_and_run → test → patch → test → done
4
9
  */
5
10
 
6
11
  import {
7
12
  get, post, patch, del,
8
- setSessionCredentials, isAuthenticated, getCredentials, parseApiKey,
13
+ setSessionCredentials, isAuthenticated, isExplicitlyAuthenticated,
14
+ getCredentials, parseApiKey,
9
15
  } from "./api.js";
10
16
 
11
17
  // ─── Tool definitions ───────────────────────────────────────────────
12
18
 
13
19
  export const tools = [
20
+ // ═══════════════════════════════════════════════════════════════════
21
+ // CORE TOOLS — the simplified developer loop
22
+ // ═══════════════════════════════════════════════════════════════════
23
+
14
24
  {
15
25
  name: "ateam_bootstrap",
26
+ core: true,
16
27
  description:
17
28
  "REQUIRED onboarding entrypoint for A-Team MCP. MUST be called when user greets, says hi, asks what this is, asks for help, explores capabilities, or when MCP is first connected. Returns platform explanation, example solutions, and assistant behavior instructions. Do NOT improvise an introduction — call this tool instead.",
18
29
  inputSchema: {
@@ -22,8 +33,9 @@ export const tools = [
22
33
  },
23
34
  {
24
35
  name: "ateam_auth",
36
+ core: true,
25
37
  description:
26
- "Authenticate with A-Team. Required before deploying or modifying solutions. The user can get their API key at https://mcp.ateam-ai.com/get-api-key. Read-only operations (spec, examples, validate) work without auth.",
38
+ "Authenticate with A-Team. Required before any tenant-aware operation (reading solutions, deploying, testing, etc.). The user can get their API key at https://mcp.ateam-ai.com/get-api-key. Only global endpoints (spec, examples, validate) work without auth. IMPORTANT: Even if environment variables (ADAS_API_KEY) are configured, you MUST call ateam_auth explicitly — env vars alone are not sufficient.",
27
39
  inputSchema: {
28
40
  type: "object",
29
41
  properties: {
@@ -41,6 +53,7 @@ export const tools = [
41
53
  },
42
54
  {
43
55
  name: "ateam_get_spec",
56
+ core: true,
44
57
  description:
45
58
  "Get the A-Team specification — schemas, validation rules, system tools, agent guides, and templates. Start here after bootstrap to understand how to build skills and solutions.",
46
59
  inputSchema: {
@@ -58,6 +71,7 @@ export const tools = [
58
71
  },
59
72
  {
60
73
  name: "ateam_get_workflows",
74
+ core: true,
61
75
  description:
62
76
  "Get the builder workflows — step-by-step state machines for building skills and solutions. Use this to guide users through the entire build process conversationally. Returns phases, what to ask, what to build, exit criteria, and tips for each stage.",
63
77
  inputSchema: {
@@ -67,6 +81,7 @@ export const tools = [
67
81
  },
68
82
  {
69
83
  name: "ateam_get_examples",
84
+ core: true,
70
85
  description:
71
86
  "Get complete working examples that pass validation. Study these before building your own.",
72
87
  inputSchema: {
@@ -82,10 +97,169 @@ export const tools = [
82
97
  required: ["type"],
83
98
  },
84
99
  },
100
+ {
101
+ name: "ateam_build_and_run",
102
+ core: true,
103
+ description:
104
+ "Build and deploy a governed AI Team solution in one step. Validates, deploys, health-checks, and optionally runs a warm test — all in one call. Use this instead of calling validate, deploy, and health separately. Requires authentication.",
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {
108
+ solution: {
109
+ type: "object",
110
+ description: "Solution architecture — identity, grants, handoffs, routing",
111
+ },
112
+ skills: {
113
+ type: "array",
114
+ items: { type: "object" },
115
+ description: "Array of full skill definitions",
116
+ },
117
+ connectors: {
118
+ type: "array",
119
+ items: { type: "object" },
120
+ description: "Optional: connector metadata (id, name, transport). Entry points auto-detected from mcp_store.",
121
+ },
122
+ mcp_store: {
123
+ type: "object",
124
+ description: "Optional: connector source code files. Key = connector id, value = array of {path, content}.",
125
+ },
126
+ test_message: {
127
+ type: "string",
128
+ description: "Optional: send a test message after deployment to verify the skill works. Returns the full execution result.",
129
+ },
130
+ test_skill_id: {
131
+ type: "string",
132
+ description: "Optional: which skill to test (defaults to the first skill).",
133
+ },
134
+ },
135
+ required: ["solution", "skills"],
136
+ },
137
+ },
138
+ {
139
+ name: "ateam_test_skill",
140
+ core: true,
141
+ description:
142
+ "Send a test message to a deployed skill and get the full execution result. By default waits for completion (up to 60s). Set wait=false for async mode — returns job_id immediately, then poll with ateam_test_status.",
143
+ inputSchema: {
144
+ type: "object",
145
+ properties: {
146
+ solution_id: {
147
+ type: "string",
148
+ description: "The solution ID",
149
+ },
150
+ skill_id: {
151
+ type: "string",
152
+ description: "The skill ID to test (original or internal ID)",
153
+ },
154
+ message: {
155
+ type: "string",
156
+ description: "The test message to send to the skill",
157
+ },
158
+ wait: {
159
+ type: "boolean",
160
+ description:
161
+ "If true (default), wait for completion. If false, return job_id immediately for polling via ateam_test_status.",
162
+ },
163
+ },
164
+ required: ["solution_id", "skill_id", "message"],
165
+ },
166
+ },
167
+ {
168
+ name: "ateam_patch",
169
+ core: true,
170
+ description:
171
+ "Update a deployed skill or solution, redeploy, and optionally re-test — all in one step. Use this instead of calling update + redeploy separately. Requires authentication.",
172
+ inputSchema: {
173
+ type: "object",
174
+ properties: {
175
+ solution_id: {
176
+ type: "string",
177
+ description: "The solution ID",
178
+ },
179
+ target: {
180
+ type: "string",
181
+ enum: ["solution", "skill"],
182
+ description: "What to update: 'solution' or 'skill'",
183
+ },
184
+ skill_id: {
185
+ type: "string",
186
+ description: "Required when target is 'skill'",
187
+ },
188
+ updates: {
189
+ type: "object",
190
+ description:
191
+ "The update payload — use dot notation for scalars (e.g. 'problem.statement'), and tools_push/tools_delete/tools_update for array operations",
192
+ },
193
+ test_message: {
194
+ type: "string",
195
+ description: "Optional: re-test the skill after patching. Requires skill_id.",
196
+ },
197
+ },
198
+ required: ["solution_id", "target", "updates"],
199
+ },
200
+ },
201
+ {
202
+ name: "ateam_get_solution",
203
+ core: true,
204
+ description:
205
+ "Read solution state — definition, skills, health, status, or export. Use this to inspect deployed solutions.",
206
+ inputSchema: {
207
+ type: "object",
208
+ properties: {
209
+ solution_id: {
210
+ type: "string",
211
+ description: "The solution ID",
212
+ },
213
+ view: {
214
+ type: "string",
215
+ enum: ["definition", "skills", "health", "status", "export", "validate", "connectors_health"],
216
+ description:
217
+ "What to read: 'definition' = full solution def, 'skills' = list skills, 'health' = live health check, 'status' = deploy status, 'export' = exportable bundle, 'validate' = re-validate from stored state, 'connectors_health' = connector status",
218
+ },
219
+ skill_id: {
220
+ type: "string",
221
+ description: "Optional: read a specific skill by ID (original or internal)",
222
+ },
223
+ },
224
+ required: ["solution_id", "view"],
225
+ },
226
+ },
227
+ {
228
+ name: "ateam_list_solutions",
229
+ core: true,
230
+ description: "List all solutions deployed in the Skill Builder.",
231
+ inputSchema: {
232
+ type: "object",
233
+ properties: {},
234
+ },
235
+ },
236
+ {
237
+ name: "ateam_delete_solution",
238
+ core: true,
239
+ description:
240
+ "Delete a deployed solution and all its skills from A-Team. Use with caution — this removes the solution from both the Skill Builder and A-Team Core. Useful for cleaning up test solutions or starting fresh.",
241
+ inputSchema: {
242
+ type: "object",
243
+ properties: {
244
+ solution_id: {
245
+ type: "string",
246
+ description: "The solution ID to delete",
247
+ },
248
+ },
249
+ required: ["solution_id"],
250
+ },
251
+ },
252
+
253
+ // ═══════════════════════════════════════════════════════════════════
254
+ // ADVANCED TOOLS — hidden from tools/list, still callable by name
255
+ // Use these for manual lifecycle control, debugging, and diagnostics
256
+ // ═══════════════════════════════════════════════════════════════════
257
+
85
258
  {
86
259
  name: "ateam_validate_skill",
260
+ core: false,
87
261
  description:
88
- "Validate a skill definition through the 5-stage A-Team validation pipeline. Part of building a governed AI Team solution. Returns errors and suggestions to fix. Always validate before deploying.",
262
+ "Validate a skill definition through the 5-stage A-Team validation pipeline. Returns errors and suggestions to fix. (Advanced ateam_build_and_run validates automatically.)",
89
263
  inputSchema: {
90
264
  type: "object",
91
265
  properties: {
@@ -99,8 +273,9 @@ export const tools = [
99
273
  },
100
274
  {
101
275
  name: "ateam_validate_solution",
276
+ core: false,
102
277
  description:
103
- "Validate a governed AI Team solution — cross-skill contracts, grant economy, handoffs, and LLM quality scoring. Part of building a governed AI Team solution. Always validate before deploying.",
278
+ "Validate a governed AI Team solution — cross-skill contracts, grant economy, handoffs, and LLM quality scoring. (Advanced ateam_build_and_run validates automatically.)",
104
279
  inputSchema: {
105
280
  type: "object",
106
281
  properties: {
@@ -119,8 +294,9 @@ export const tools = [
119
294
  },
120
295
  {
121
296
  name: "ateam_deploy_solution",
297
+ core: false,
122
298
  description:
123
- "Deploy a governed AI Team solution to A-Team Core — identity, connectors, skills. The Skill Builder auto-generates MCP servers from tool definitions. For connectors with UI or Node.js code, send SOURCE files only via mcp_store the server runs npm install + npm run build automatically (like Vercel/Netlify). Never send dist/ or node_modules/. Always validate first using ateam_validate_solution. Requires authentication (call ateam_auth first if not using env vars).",
299
+ "Deploy a governed AI Team solution to A-Team Core. (Advancedprefer ateam_build_and_run which validates + deploys + health-checks in one step.)",
124
300
  inputSchema: {
125
301
  type: "object",
126
302
  properties: {
@@ -136,12 +312,12 @@ export const tools = [
136
312
  connectors: {
137
313
  type: "array",
138
314
  items: { type: "object" },
139
- description: "Array of connector metadata (id, name, transport). command and args are OPTIONAL when mcp_store provides the code — the system auto-detects the entry point (server.js, index.js, package.json main, server.py) and runtime (node, python3). You can still provide explicit command/args to override auto-detection.",
315
+ description: "Array of connector metadata (id, name, transport). command and args are OPTIONAL when mcp_store provides the code — the system auto-detects the entry point.",
140
316
  },
141
317
  mcp_store: {
142
318
  type: "object",
143
319
  description:
144
- "Optional: connector source code files. Key = connector id, value = array of {path, content}. Send SOURCE files only (server.js, package.json, src/*.jsx, etc.) — the server runs npm install + npm run build automatically. Never include node_modules/ or dist/ folders. Entry point is auto-detected from the uploaded files.",
320
+ "Optional: connector source code files. Key = connector id, value = array of {path, content}.",
145
321
  },
146
322
  },
147
323
  required: ["solution", "skills"],
@@ -149,7 +325,8 @@ export const tools = [
149
325
  },
150
326
  {
151
327
  name: "ateam_deploy_skill",
152
- description: "Deploy a single skill into an existing solution. Requires authentication.",
328
+ core: false,
329
+ description: "Deploy a single skill into an existing solution. (Advanced — use ateam_build_and_run for new solutions.)",
153
330
  inputSchema: {
154
331
  type: "object",
155
332
  properties: {
@@ -167,7 +344,8 @@ export const tools = [
167
344
  },
168
345
  {
169
346
  name: "ateam_deploy_connector",
170
- description: "Deploy a connector — registers in the Skill Builder catalog and connects in A-Team Core. Requires authentication.",
347
+ core: false,
348
+ description: "Deploy a connector — registers in the Skill Builder catalog and connects in A-Team Core. (Advanced.)",
171
349
  inputSchema: {
172
350
  type: "object",
173
351
  properties: {
@@ -181,8 +359,9 @@ export const tools = [
181
359
  },
182
360
  {
183
361
  name: "ateam_upload_connector_files",
362
+ core: false,
184
363
  description:
185
- "Upload source files for a connector's MCP server. Use this INSTEAD of mcp_store in ateam_deploy_solution when the source code is too large to inline. Upload files first (one call per file or a few small files at a time), then deploy the solution without mcp_store, then redeploy. Files are staged and automatically included in the next deploy. Requires authentication.",
364
+ "Upload source files for a connector's MCP server. Use this INSTEAD of mcp_store in ateam_build_and_run when the source code is too large to inline. Upload files first, then build_and_run without mcp_store. (Advanced.)",
186
365
  inputSchema: {
187
366
  type: "object",
188
367
  properties: {
@@ -202,49 +381,17 @@ export const tools = [
202
381
  },
203
382
  required: ["path"],
204
383
  },
205
- description: "Array of files to upload. Each file needs 'path' plus ONE of: 'content' (inline string), 'content_base64' (base64), or 'url' (server fetches it). Send one file per call for large files.",
384
+ description: "Array of files to upload. Each file needs 'path' plus ONE of: 'content' (inline string), 'content_base64' (base64), or 'url' (server fetches it).",
206
385
  },
207
386
  },
208
387
  required: ["connector_id", "files"],
209
388
  },
210
389
  },
211
- {
212
- name: "ateam_list_solutions",
213
- description: "List all solutions deployed in the Skill Builder.",
214
- inputSchema: {
215
- type: "object",
216
- properties: {},
217
- },
218
- },
219
- {
220
- name: "ateam_get_solution",
221
- description:
222
- "Read solution state — definition, skills, health, status, or export. Use this to inspect deployed solutions.",
223
- inputSchema: {
224
- type: "object",
225
- properties: {
226
- solution_id: {
227
- type: "string",
228
- description: "The solution ID",
229
- },
230
- view: {
231
- type: "string",
232
- enum: ["definition", "skills", "health", "status", "export", "validate", "connectors_health"],
233
- description:
234
- "What to read: 'definition' = full solution def, 'skills' = list skills, 'health' = live health check, 'status' = deploy status, 'export' = exportable bundle, 'validate' = re-validate from stored state, 'connectors_health' = connector status",
235
- },
236
- skill_id: {
237
- type: "string",
238
- description: "Optional: read a specific skill by ID (original or internal)",
239
- },
240
- },
241
- required: ["solution_id", "view"],
242
- },
243
- },
244
390
  {
245
391
  name: "ateam_update",
392
+ core: false,
246
393
  description:
247
- "Update a deployed solution or skill incrementally using PATCH. Supports dot notation for scalar fields and _push/_delete/_update for arrays. Requires authentication.",
394
+ "Update a deployed solution or skill incrementally using PATCH. (Advanced prefer ateam_patch which updates + redeploys + tests in one step.)",
248
395
  inputSchema: {
249
396
  type: "object",
250
397
  properties: {
@@ -272,8 +419,9 @@ export const tools = [
272
419
  },
273
420
  {
274
421
  name: "ateam_redeploy",
422
+ core: false,
275
423
  description:
276
- "Re-deploy after making updates. Regenerates MCP servers and pushes to A-Team Core. Requires authentication.",
424
+ "Re-deploy after making updates. (Advanced prefer ateam_patch which updates + redeploys in one step.)",
277
425
  inputSchema: {
278
426
  type: "object",
279
427
  properties: {
@@ -291,8 +439,9 @@ export const tools = [
291
439
  },
292
440
  {
293
441
  name: "ateam_solution_chat",
442
+ core: false,
294
443
  description:
295
- "Send a message to the Solution Bot — an AI assistant that understands your deployed solution and can help with modifications.",
444
+ "Send a message to the Solution Bot — an AI assistant that understands your deployed solution and can help with modifications. (Advanced.)",
296
445
  inputSchema: {
297
446
  type: "object",
298
447
  properties: {
@@ -308,13 +457,11 @@ export const tools = [
308
457
  required: ["solution_id", "message"],
309
458
  },
310
459
  },
311
-
312
- // ─── Developer Tools ────────────────────────────────────────────
313
-
314
460
  {
315
461
  name: "ateam_get_execution_logs",
462
+ core: false,
316
463
  description:
317
- "Get execution logs for a solution — recent jobs with step traces, tool calls, errors, and timing. Essential for debugging what actually happened during skill execution.",
464
+ "Get execution logs for a solution — recent jobs with step traces, tool calls, errors, and timing. Essential for debugging what actually happened during skill execution. (Advanced.)",
318
465
  inputSchema: {
319
466
  type: "object",
320
467
  properties: {
@@ -338,38 +485,11 @@ export const tools = [
338
485
  required: ["solution_id"],
339
486
  },
340
487
  },
341
- {
342
- name: "ateam_test_skill",
343
- description:
344
- "Send a test message to a deployed skill and get the full execution result. By default waits for completion (up to 60s). Set wait=false for async mode — returns job_id immediately, then poll with ateam_test_status.",
345
- inputSchema: {
346
- type: "object",
347
- properties: {
348
- solution_id: {
349
- type: "string",
350
- description: "The solution ID",
351
- },
352
- skill_id: {
353
- type: "string",
354
- description: "The skill ID to test (original or internal ID)",
355
- },
356
- message: {
357
- type: "string",
358
- description: "The test message to send to the skill",
359
- },
360
- wait: {
361
- type: "boolean",
362
- description:
363
- "If true (default), wait for completion. If false, return job_id immediately for polling via ateam_test_status.",
364
- },
365
- },
366
- required: ["solution_id", "skill_id", "message"],
367
- },
368
- },
369
488
  {
370
489
  name: "ateam_test_status",
490
+ core: false,
371
491
  description:
372
- "Poll the progress of an async skill test. Returns iteration count, tool call steps, status, pending questions, and result when done. Uses the same data pipeline as the Job Progress UI.",
492
+ "Poll the progress of an async skill test. Returns iteration count, tool call steps, status, pending questions, and result when done. (Advanced use ateam_test_skill with wait=true for synchronous testing.)",
373
493
  inputSchema: {
374
494
  type: "object",
375
495
  properties: {
@@ -391,8 +511,9 @@ export const tools = [
391
511
  },
392
512
  {
393
513
  name: "ateam_test_abort",
514
+ core: false,
394
515
  description:
395
- "Abort a running skill test. Stops the job execution at the next iteration boundary.",
516
+ "Abort a running skill test. Stops the job execution at the next iteration boundary. (Advanced.)",
396
517
  inputSchema: {
397
518
  type: "object",
398
519
  properties: {
@@ -414,8 +535,9 @@ export const tools = [
414
535
  },
415
536
  {
416
537
  name: "ateam_get_connector_source",
538
+ core: false,
417
539
  description:
418
- "Read the source code of a connector's MCP server. Returns the files that make up the connector implementation.",
540
+ "Read the source code of a connector's MCP server. Returns the files that make up the connector implementation. (Advanced.)",
419
541
  inputSchema: {
420
542
  type: "object",
421
543
  properties: {
@@ -433,8 +555,9 @@ export const tools = [
433
555
  },
434
556
  {
435
557
  name: "ateam_get_metrics",
558
+ core: false,
436
559
  description:
437
- "Get execution metrics — timing, tool stats, bottlenecks, signals, and recommendations. Use job_id for single-job deep analysis, or skill_id for recent history overview.",
560
+ "Get execution metrics — timing, tool stats, bottlenecks, signals, and recommendations. (Advanced.)",
438
561
  inputSchema: {
439
562
  type: "object",
440
563
  properties: {
@@ -456,8 +579,9 @@ export const tools = [
456
579
  },
457
580
  {
458
581
  name: "ateam_diff",
582
+ core: false,
459
583
  description:
460
- "Compare the current Builder definition against what's deployed in ADAS Core. Shows which skills are undeployed, orphaned, or have changed fields. Use skill_id to diff a single skill.",
584
+ "Compare the current Builder definition against what's deployed in ADAS Core. Shows which skills are undeployed, orphaned, or have changed fields. (Advanced.)",
461
585
  inputSchema: {
462
586
  type: "object",
463
587
  properties: {
@@ -473,23 +597,14 @@ export const tools = [
473
597
  required: ["solution_id"],
474
598
  },
475
599
  },
476
- {
477
- name: "ateam_delete_solution",
478
- description:
479
- "Delete a deployed solution and all its skills from A-Team. Use with caution — this removes the solution from both the Skill Builder and A-Team Core. Useful for cleaning up test solutions or starting fresh.",
480
- inputSchema: {
481
- type: "object",
482
- properties: {
483
- solution_id: {
484
- type: "string",
485
- description: "The solution ID to delete",
486
- },
487
- },
488
- required: ["solution_id"],
489
- },
490
- },
491
600
  ];
492
601
 
602
+ /**
603
+ * Core tools — shown in MCP tools/list.
604
+ * Advanced tools are still callable but not advertised.
605
+ */
606
+ export const coreTools = tools.filter(t => t.core !== false);
607
+
493
608
  // ─── Tool handlers ──────────────────────────────────────────────────
494
609
 
495
610
  const SPEC_PATHS = {
@@ -507,16 +622,25 @@ const EXAMPLE_PATHS = {
507
622
  solution: "/spec/examples/solution",
508
623
  };
509
624
 
510
- // Tools that require authentication (write operations)
511
- const WRITE_TOOLS = new Set([
625
+ // Tools that are tenant-aware — require EXPLICIT ateam_auth (env vars alone not enough).
626
+ // This prevents accidental reads/writes to the wrong tenant when env vars are
627
+ // baked into MCP config (e.g., ADAS_TENANT + ADAS_API_KEY in ~/.claude.json).
628
+ // Any tool that touches tenant-specific data (solutions, skills, logs, tests) is here.
629
+ const TENANT_TOOLS = new Set([
630
+ // Write operations
631
+ "ateam_build_and_run",
632
+ "ateam_patch",
512
633
  "ateam_deploy_solution",
513
634
  "ateam_deploy_skill",
514
635
  "ateam_deploy_connector",
515
636
  "ateam_upload_connector_files",
516
637
  "ateam_update",
517
638
  "ateam_redeploy",
639
+ "ateam_delete_solution",
518
640
  "ateam_solution_chat",
519
- // Developer tools (read tenant-specific runtime data)
641
+ // Read operations (tenant-specific data)
642
+ "ateam_list_solutions",
643
+ "ateam_get_solution",
520
644
  "ateam_get_execution_logs",
521
645
  "ateam_test_skill",
522
646
  "ateam_test_status",
@@ -524,9 +648,11 @@ const WRITE_TOOLS = new Set([
524
648
  "ateam_get_connector_source",
525
649
  "ateam_get_metrics",
526
650
  "ateam_diff",
527
- "ateam_delete_solution",
528
651
  ]);
529
652
 
653
+ /** Small delay helper */
654
+ const sleep = (ms) => new Promise(r => setTimeout(r, ms));
655
+
530
656
  const handlers = {
531
657
  ateam_bootstrap: async () => ({
532
658
  platform_positioning: {
@@ -549,20 +675,28 @@ const handlers = {
549
675
  { name: "Customer Support Operations Team", description: "Multi-role support system with escalation, refund controls, CRM integration" },
550
676
  { name: "Enterprise Compliance Platform", description: "Approval flows, audit logs, policy enforcement" },
551
677
  ],
552
- recommended_flow: [
553
- { step: 1, title: "Clarify the goal", description: "Understand what the user wants their Team to do", suggested_tools: [] },
554
- { step: 2, title: "Generate Team map", description: "Design skills, solution architecture, and connectors", suggested_tools: ["ateam_get_spec", "ateam_get_examples", "ateam_get_workflows"] },
555
- { step: 3, title: "Validate", description: "Run validation before deploying", suggested_tools: ["ateam_validate_skill", "ateam_validate_solution"] },
556
- { step: 4, title: "Deploy", description: "Push the Team to A-Team Core. If connector source code is too large for mcp_store, use ateam_upload_connector_files first (one file per call), then deploy without mcp_store.", suggested_tools: ["ateam_auth", "ateam_upload_connector_files", "ateam_deploy_solution"] },
557
- { step: 5, title: "Iterate", description: "Inspect, update, and redeploy as needed", suggested_tools: ["ateam_get_solution", "ateam_update", "ateam_redeploy", "ateam_solution_chat"] },
558
- { step: 6, title: "Operate & Debug", description: "Test skills (async or sync), poll progress, abort tests, read execution logs, analyze metrics, diff definitions, inspect connector source", suggested_tools: ["ateam_test_skill", "ateam_test_status", "ateam_test_abort", "ateam_get_execution_logs", "ateam_get_metrics", "ateam_diff", "ateam_get_connector_source"] },
559
- ],
678
+ developer_loop: {
679
+ _note: "This is the recommended build loop. Only 4 steps from definition to running skill.",
680
+ steps: [
681
+ { step: 1, action: "Learn", description: "Get the spec and study examples", tools: ["ateam_get_spec", "ateam_get_examples"] },
682
+ { step: 2, action: "Build & Run", description: "Define your solution + skills, then validate, deploy, and health-check in one call. Optionally include a test_message to verify it works immediately.", tools: ["ateam_build_and_run"] },
683
+ { step: 3, action: "Test", description: "Send test messages to your deployed skill and see the full execution trace.", tools: ["ateam_test_skill"] },
684
+ { step: 4, action: "Iterate", description: "Patch the skill (update + redeploy + re-test in one call), repeat until satisfied.", tools: ["ateam_patch"] },
685
+ ],
686
+ },
560
687
  first_questions: [
561
688
  { id: "goal", question: "What do you want your Team to accomplish?", type: "text" },
562
689
  { id: "domain", question: "Which domain fits best?", type: "enum", options: ["ecommerce", "logistics", "enterprise_ops", "other"] },
563
690
  { id: "systems", question: "Which systems should the Team connect to?", type: "multi_select", options: ["slack", "email", "zendesk", "shopify", "jira", "postgres", "custom_api", "none"] },
564
691
  { id: "security", question: "What environment constraints?", type: "enum", options: ["sandbox", "controlled", "regulated"] },
565
692
  ],
693
+ advanced_tools: {
694
+ _note: "These tools are available but hidden from the default tool list. Call them by name when you need fine-grained control.",
695
+ debugging: ["ateam_get_execution_logs", "ateam_get_metrics", "ateam_diff", "ateam_get_connector_source"],
696
+ manual_lifecycle: ["ateam_validate_skill", "ateam_validate_solution", "ateam_deploy_solution", "ateam_deploy_skill", "ateam_deploy_connector", "ateam_update", "ateam_redeploy"],
697
+ async_testing: ["ateam_test_status", "ateam_test_abort"],
698
+ other: ["ateam_upload_connector_files", "ateam_solution_chat"],
699
+ },
566
700
  static_pages: {
567
701
  features: "https://ateam-ai.com/#features",
568
702
  use_cases: "https://ateam-ai.com/#usecases",
@@ -589,19 +723,20 @@ const handlers = {
589
723
  "Define Skill vs Solution vs Connector",
590
724
  "Ask user what solution they want to build",
591
725
  ],
592
- thinking_order: ["Platform", "Solution", "Skills", "Connectors", "Governance", "Validation", "Deployment"],
726
+ thinking_order: ["Platform", "Solution", "Skills", "Connectors", "Governance", "Build & Run"],
593
727
  tone: "Architectural, enterprise-grade, serious",
594
728
  always: [
595
- "Explain Skill vs Solution vs Connector before deploying anything",
596
- "Validate before deploy",
729
+ "Explain Skill vs Solution vs Connector before building",
730
+ "Use ateam_build_and_run for the full lifecycle (validates automatically)",
731
+ "Use ateam_patch for iterations (updates + redeploys automatically)",
597
732
  "Study the connector example (ateam_get_examples type='connector') before writing connector code",
598
733
  "Ask discovery questions if goal unclear",
599
734
  ],
600
735
  never: [
601
- "Deploy before validation",
736
+ "Call validate + deploy + health separately when ateam_build_and_run does it in one step",
737
+ "Call update + redeploy separately when ateam_patch does it in one step",
602
738
  "Dump raw spec unless requested",
603
- "Write connector code that starts a web server (express, http.createServer, app.listen) — connectors MUST use stdio transport",
604
- "Use HttpServerTransport, SSEServerTransport, or any non-stdio MCP transport",
739
+ "Write connector code that starts a web server — connectors MUST use stdio transport",
605
740
  ],
606
741
  },
607
742
  }),
@@ -637,6 +772,188 @@ const handlers = {
637
772
 
638
773
  ateam_get_examples: async ({ type }, sid) => get(EXAMPLE_PATHS[type], sid),
639
774
 
775
+ // ─── Composite: Build & Run ────────────────────────────────────────
776
+ // Validates → Deploys → Health-checks → Optionally tests
777
+ // One call replaces: validate_solution + deploy_solution + get_solution(health)
778
+
779
+ ateam_build_and_run: async ({ solution, skills, connectors, mcp_store, test_message, test_skill_id }, sid) => {
780
+ const phases = [];
781
+
782
+ // Phase 1: Validate
783
+ let validation;
784
+ try {
785
+ validation = await post("/validate/solution", { solution, skills }, sid);
786
+ phases.push({ phase: "validate", status: "done" });
787
+ } catch (err) {
788
+ return {
789
+ ok: false,
790
+ phase: "validation",
791
+ error: err.message,
792
+ message: "Validation call failed. Check your solution/skill format against the spec (ateam_get_spec topic='solution').",
793
+ };
794
+ }
795
+
796
+ // Check for blocking errors
797
+ const errors = validation.errors || validation.validation?.errors || [];
798
+ if (errors.length > 0) {
799
+ return {
800
+ ok: false,
801
+ phase: "validation",
802
+ errors,
803
+ warnings: validation.warnings || validation.validation?.warnings || [],
804
+ message: `Validation found ${errors.length} error(s). Fix them and try again.`,
805
+ };
806
+ }
807
+
808
+ // Phase 2: Deploy
809
+ let deploy;
810
+ try {
811
+ deploy = await post("/deploy/solution", { solution, skills, connectors, mcp_store }, sid);
812
+ phases.push({ phase: "deploy", status: deploy.ok ? "done" : "failed" });
813
+ } catch (err) {
814
+ return {
815
+ ok: false,
816
+ phase: "deployment",
817
+ phases,
818
+ error: err.message,
819
+ validation_warnings: validation.warnings || [],
820
+ message: "Deployment failed. See error details above.",
821
+ };
822
+ }
823
+
824
+ if (!deploy.ok) {
825
+ return {
826
+ ok: false,
827
+ phase: "deployment",
828
+ phases,
829
+ deploy,
830
+ validation_warnings: validation.warnings || [],
831
+ message: "Deployment returned an error. See deploy details above.",
832
+ };
833
+ }
834
+
835
+ // Phase 3: Health check (with brief wait for propagation)
836
+ let health;
837
+ try {
838
+ await sleep(2000);
839
+ health = await get(`/deploy/solutions/${solution.id}/health`, sid);
840
+ phases.push({ phase: "health", status: "done" });
841
+ } catch (err) {
842
+ health = { error: err.message };
843
+ phases.push({ phase: "health", status: "error", error: err.message });
844
+ }
845
+
846
+ // Phase 4: Warm test (optional)
847
+ let test_result;
848
+ if (test_message) {
849
+ const skillId = test_skill_id || skills?.[0]?.id;
850
+ if (skillId) {
851
+ try {
852
+ test_result = await post(
853
+ `/deploy/solutions/${solution.id}/skills/${skillId}/test`,
854
+ { message: test_message },
855
+ sid,
856
+ { timeoutMs: 90_000 },
857
+ );
858
+ phases.push({ phase: "test", status: "done", skill_id: skillId });
859
+ } catch (err) {
860
+ test_result = { error: err.message };
861
+ phases.push({ phase: "test", status: "error", error: err.message });
862
+ }
863
+ }
864
+ }
865
+
866
+ return {
867
+ ok: true,
868
+ solution_id: solution.id,
869
+ phases,
870
+ deploy: {
871
+ skills_deployed: deploy.import?.skills || [],
872
+ connectors: deploy.import?.connectors || 0,
873
+ ...(deploy.deploy_warnings?.length > 0 && { warnings: deploy.deploy_warnings }),
874
+ ...(deploy.auto_expanded_skills?.length > 0 && { auto_expanded: deploy.auto_expanded_skills }),
875
+ },
876
+ health,
877
+ ...(test_result && { test_result }),
878
+ ...(validation.warnings?.length > 0 && { validation_warnings: validation.warnings }),
879
+ };
880
+ },
881
+
882
+ // ─── Composite: Patch ──────────────────────────────────────────────
883
+ // Updates → Redeploys → Optionally tests
884
+ // One call replaces: ateam_update + ateam_redeploy
885
+
886
+ ateam_patch: async ({ solution_id, target, skill_id, updates, test_message }, sid) => {
887
+ const phases = [];
888
+
889
+ // Phase 1: Apply PATCH
890
+ let patchResult;
891
+ try {
892
+ if (target === "skill") {
893
+ patchResult = await patch(`/deploy/solutions/${solution_id}/skills/${skill_id}`, { updates }, sid);
894
+ } else {
895
+ patchResult = await patch(`/deploy/solutions/${solution_id}`, { state_update: updates }, sid);
896
+ }
897
+ phases.push({ phase: "update", status: "done" });
898
+ } catch (err) {
899
+ return {
900
+ ok: false,
901
+ phase: "update",
902
+ error: err.message,
903
+ message: "Patch failed. Check your updates payload format.",
904
+ };
905
+ }
906
+
907
+ // Phase 2: Redeploy
908
+ let redeployResult;
909
+ try {
910
+ if (target === "skill" && skill_id) {
911
+ redeployResult = await post(`/deploy/solutions/${solution_id}/skills/${skill_id}/redeploy`, {}, sid);
912
+ } else {
913
+ redeployResult = await post(`/deploy/solutions/${solution_id}/redeploy`, {}, sid);
914
+ }
915
+ phases.push({ phase: "redeploy", status: "done" });
916
+ } catch (err) {
917
+ return {
918
+ ok: false,
919
+ phase: "redeploy",
920
+ phases,
921
+ patch: patchResult,
922
+ error: err.message,
923
+ message: "Update succeeded but redeploy failed. Try ateam_redeploy manually.",
924
+ };
925
+ }
926
+
927
+ // Phase 3: Optional re-test
928
+ let test_result;
929
+ if (test_message && skill_id) {
930
+ try {
931
+ await sleep(1000);
932
+ test_result = await post(
933
+ `/deploy/solutions/${solution_id}/skills/${skill_id}/test`,
934
+ { message: test_message },
935
+ sid,
936
+ { timeoutMs: 90_000 },
937
+ );
938
+ phases.push({ phase: "test", status: "done" });
939
+ } catch (err) {
940
+ test_result = { error: err.message };
941
+ phases.push({ phase: "test", status: "error", error: err.message });
942
+ }
943
+ }
944
+
945
+ return {
946
+ ok: true,
947
+ solution_id,
948
+ phases,
949
+ patch: patchResult,
950
+ redeploy: redeployResult,
951
+ ...(test_result && { test_result }),
952
+ };
953
+ },
954
+
955
+ // ─── Original handlers (unchanged) ────────────────────────────────
956
+
640
957
  ateam_validate_skill: async ({ skill }, sid) => post("/validate/skill", { skill }, sid),
641
958
 
642
959
  ateam_validate_solution: async ({ solution, skills }, sid) =>
@@ -820,21 +1137,28 @@ export async function handleToolCall(name, args, sessionId) {
820
1137
  };
821
1138
  }
822
1139
 
823
- // Check auth for write operations
824
- if (WRITE_TOOLS.has(name) && !isAuthenticated(sessionId)) {
1140
+ // Check auth for tenant-aware operations — requires explicit ateam_auth call.
1141
+ // Env vars (ADAS_API_KEY / ADAS_TENANT) are NOT sufficient — they may be
1142
+ // baked into MCP config and silently target the wrong tenant.
1143
+ // Only global/public tools (bootstrap, spec, examples, validate) bypass this.
1144
+ if (TENANT_TOOLS.has(name) && !isExplicitlyAuthenticated(sessionId)) {
1145
+ const hasEnvVars = isAuthenticated(sessionId);
825
1146
  return {
826
1147
  content: [{
827
1148
  type: "text",
828
1149
  text: [
829
- "🔐 Authentication required.",
1150
+ "Authentication required — call ateam_auth first.",
830
1151
  "",
831
- "This tool needs an API key. Please ask the user to:",
1152
+ hasEnvVars
1153
+ ? "Environment variables (ADAS_API_KEY) were detected, but they are not sufficient for tenant-aware operations. You must call ateam_auth explicitly to confirm which tenant you intend to use."
1154
+ : "No authentication found.",
832
1155
  "",
1156
+ "Please ask the user to:",
833
1157
  "1. Get their API key at: https://mcp.ateam-ai.com/get-api-key",
834
1158
  "2. Then call: ateam_auth(api_key: \"<their key>\")",
835
1159
  "",
836
- "The key looks like: adas_<tenant>_<32hex>",
837
- "The tenant is auto-extracted no separate tenant parameter needed.",
1160
+ "The key format is: adas_<tenant>_<32hex> — the tenant is auto-extracted.",
1161
+ "This prevents accidental operations on the wrong tenant from pre-configured env vars.",
838
1162
  ].join("\n"),
839
1163
  }],
840
1164
  isError: true,