@ateam-ai/mcp 0.4.7 → 0.4.8

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 (3) hide show
  1. package/package.json +1 -1
  2. package/src/api.js +43 -0
  3. package/src/tools.js +54 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
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
@@ -256,6 +256,49 @@ export function getBaseUrl(sessionId) {
256
256
  return BASE_URL;
257
257
  }
258
258
 
259
+ /**
260
+ * Map an API base URL → the user-facing app URL where a deployed change is
261
+ * visible. ateam-mcp is a PUBLIC MCP: a given user talks to exactly ONE
262
+ * platform (their tenant is on one live deployment), so the useful thing to
263
+ * surface is NOT an internal "env" but WHERE to go look at the result.
264
+ * api.ateam-ai.com → app.ateam-ai.com (prod)
265
+ * dev-api.ateam-ai.com → dev-app.ateam-ai.com (our internal dev)
266
+ * anything else (self-host) → best-effort api→app swap, or the base itself
267
+ */
268
+ function apiToAppUrl(baseUrl) {
269
+ try {
270
+ const u = new URL(baseUrl);
271
+ // host swaps: <x>api.<domain> → <x>app.<domain>; "api." prefix → "app."
272
+ let host = u.hostname;
273
+ if (host.startsWith("api.")) host = "app." + host.slice(4);
274
+ else if (host.startsWith("dev-api.")) host = "dev-app." + host.slice(8);
275
+ else if (host.includes("-api.")) host = host.replace("-api.", "-app.");
276
+ else if (host.includes("api")) host = host.replace(/api/, "app");
277
+ return `${u.protocol}//${host}`;
278
+ } catch {
279
+ return baseUrl;
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Location stamp for a tool result: which tenant + which app URL a change
285
+ * landed on. Returned as `_where` so any consumer (desktop, mobile, cloud
286
+ * agent) can tell the user where to see it — no reliance on a plugin SKILL.md.
287
+ */
288
+ export function getWhere(sessionId) {
289
+ let tenant = null;
290
+ try { tenant = getCredentials(sessionId)?.tenant || null; } catch { /* unauthed */ }
291
+ const apiBase = getBaseUrl(sessionId);
292
+ const appUrl = apiToAppUrl(apiBase);
293
+ return {
294
+ tenant,
295
+ app_url: appUrl,
296
+ _note: tenant
297
+ ? `This change is on tenant "${tenant}". View it at ${appUrl}.`
298
+ : `View at ${appUrl}.`,
299
+ };
300
+ }
301
+
259
302
  /** Check if a bearer has an active auth override. */
260
303
  export function hasBearerAuth(sessionId) {
261
304
  const bearer = sessionBearers.get(sessionId);
package/src/tools.js CHANGED
@@ -12,8 +12,21 @@ import {
12
12
  get, post, patch, del,
13
13
  setSessionCredentials, isAuthenticated, isExplicitlyAuthenticated,
14
14
  getCredentials, parseApiKey, touchSession, getSessionContext,
15
- setAuthOverride, switchTenant, isMasterMode, listTenants,
15
+ setAuthOverride, switchTenant, isMasterMode, listTenants, getWhere,
16
16
  } from "./api.js";
17
+
18
+ // Mutating / stateful tools whose result should carry a `_where` stamp
19
+ // (tenant + the app URL to view the change). ateam-mcp is a PUBLIC MCP used
20
+ // from non-desktop clients too, so the location must live IN the tool result
21
+ // — not only in a desktop plugin's SKILL.md. Read-only/global tools skip it.
22
+ const STAMP_WHERE_TOOLS = new Set([
23
+ "ateam_build_and_run", "ateam_patch", "ateam_upload_connector", "ateam_redeploy",
24
+ "ateam_create_skill", "ateam_create_connector", "ateam_create_plugin",
25
+ "ateam_delete_skill", "ateam_delete_connector", "ateam_delete_solution",
26
+ "ateam_github_patch", "ateam_github_write", "ateam_github_push",
27
+ "ateam_github_promote", "ateam_github_rollback",
28
+ "ateam_test_skill", "ateam_test_pipeline", "ateam_test_connector", "ateam_test_notification",
29
+ ]);
17
30
  import { renderAgentDocHeader, mergeAgentDoc, AGENT_DOC_SENTINEL } from "./agentDoc.js";
18
31
 
19
32
  // ─── Async deploy helper ────────────────────────────────────────────
@@ -2219,25 +2232,49 @@ const handlers = {
2219
2232
  "Ask user what solution they want to build",
2220
2233
  ],
2221
2234
  thinking_order: ["Platform", "Solution", "Skills", "Connectors", "Governance", "Build & Run"],
2222
- tone: "Architectural, enterprise-grade, serious",
2235
+ tone: "Architectural, enterprise-grade, serious — BUT translate to plain language for non-technical (business) users; never assume the user is a developer.",
2236
+ // ── Conversation style — the user is usually a BUSINESS user, not a developer.
2237
+ // Follow this for every message you send them. (Backlog findings #1,#3,#4.)
2238
+ conversation_style: {
2239
+ audience: "Assume a business user with NO technical knowledge unless proven otherwise. Never say CLI, connector, persona, handoff, repo, JSON, deploy, source:local — translate to plain words: 'your tools', 'team member', 'their job', 'save it live'.",
2240
+ format: "Scannable, never a wall of text. Short lead line → bullets → done. Offer concrete choices (with an emoji) plus an open option. Ask ONE thing at a time.",
2241
+ grounding: "Ground every message in the user's REAL data — fetch the solution + skill names (ateam_show_solution_minimal) and open the FIRST message with them (e.g. 'You have ada — a personal assistant — 14 skills incl. Life Manager, Travel Agent…'). No generic filler welcomes.",
2242
+ build_time_vs_runtime: "Distinguish how a skill behaves for its END USERS (goes in the persona) from settings the BUILDER must choose now. Bake adaptive behavior into the persona; only ask the builder about genuine build-time choices (which tools, guardrails). Do NOT ask the builder runtime questions ('what is YOUR level?') for a reusable skill.",
2243
+ confirm: "Confirm in plain language before anything that changes the team, then show the result simply: '✅ Added Japanese Tutor to your team.'",
2244
+ },
2245
+ // ── Where the user's work lands — ALWAYS make this visible. (Backlog finding #6.)
2246
+ environment_transparency: {
2247
+ on_connect: "State it explicitly: 'Connected to <tenant> on <environment> — changes you make deploy here.' Derive environment from the authed api url (dev-api → DEV, api → PROD); show a human label, not a raw host. Never silently operate on an env the user didn't expect.",
2248
+ after_deploy: "Confirm WHERE it landed with a link: '✅ Added <thing> to <solution> (tenant <t>, <env>) — view it: <app url>.'",
2249
+ },
2250
+ // ── Delivering a build. (Backlog findings #2,#7,#8.)
2251
+ build_flow: {
2252
+ follow_the_stages: "Drive builds through thinking_order + minimal_authoring (below) — do NOT improvise. A skill is mainly its role.persona + connectors; the platform generates intents/tools/scenarios.",
2253
+ ui_is_in_scope: "If the user asks for a UI / app screen / dashboard, the WIDGET is part of the build — deliver it, don't silently defer it. If you must stage it, say so up front and get agreement.",
2254
+ pick_build_path_by_tenant_state: "Choose the write path by the tenant's GitHub state: repo connected → normal github flow; NO repo (Core-only / freshly onboarded) → use source:'local' for definition edits and ateam_create_plugin/ateam_upload_connector for widgets (they fall back to deployed source). If a github write returns github_not_connected / SOLUTION_NOT_FOUND, guide the user to connect GitHub (mcp.ateam-ai.com/connect-github) — do not surface the raw error.",
2255
+ },
2223
2256
  always: [
2224
- "Explain Skill vs Solution vs Connector before building",
2257
+ "Open with a grounded welcome built from the user's real solution + skill names (business-friendly).",
2258
+ "State tenant + environment on connect, and where things land after each deploy (with a link).",
2259
+ "Explain Skill vs Solution vs Connector in plain words before building",
2225
2260
  "Use ateam_build_and_run for the full lifecycle (validates automatically)",
2226
2261
  "Use ateam_patch for skill/solution definition changes (updates + redeploys automatically)",
2227
2262
  "Use ateam_github_patch + ateam_build_and_run(github:true) for connector code changes after first deploy",
2228
2263
  "Study the connector example (ateam_get_examples type='connector') before writing connector code",
2229
- "Ask discovery questions if goal unclear",
2264
+ "Ask discovery questions if goal unclear — one at a time, with choices",
2265
+ "Deliver the FULL ask, including any requested UI/widget; stage only with the user's agreement",
2230
2266
  "ALL changes go directly to main — suggest ateam_github_promote() to create a checkpoint before risky changes",
2231
- "After every build/patch, tell the user: 'Deployed to Core ✅ | Pushed to main | Create checkpoint: ateam_github_promote(solution_id)'",
2232
2267
  ],
2233
2268
  never: [
2269
+ "Talk to a business user like a developer — no jargon, no walls of text",
2270
+ "Send a generic welcome that ignores the user's actual solution/skills",
2271
+ "Ask the builder a runtime question that the deployed skill should ask its end-users",
2272
+ "Silently defer or drop a named part of the request (e.g. the UI)",
2273
+ "Leave the user guessing which tenant/environment they're changing",
2274
+ "Surface a raw error (524 / SOLUTION_NOT_FOUND / github_not_connected) — translate it and guide the next step",
2234
2275
  "Call validate + deploy + health separately when ateam_build_and_run does it in one step",
2235
- "Call update + redeploy separately when ateam_patch does it in one step",
2236
2276
  "Dump raw spec unless requested",
2237
2277
  "Write connector code that starts a web server — connectors MUST use stdio transport",
2238
- "Mention dev branch — there is no dev branch, everything is on main",
2239
- "Pass large connector code via mcp_store after the first deploy — use ateam_github_write/ateam_github_patch one file at a time instead",
2240
- "Try to pass ALL connector files at once in a single tool call — write them individually to GitHub",
2241
2278
  ],
2242
2279
  },
2243
2280
  }),
@@ -4067,6 +4104,14 @@ export async function handleToolCall(name, args, sessionId) {
4067
4104
  try {
4068
4105
  const result = await handler(args, sessionId);
4069
4106
 
4107
+ // Stamp WHERE this landed (tenant + app URL) on mutating-tool results, so
4108
+ // any client — desktop, mobile, cloud agent — can tell the user where to
4109
+ // see the change. Non-fatal + only for object results that don't already
4110
+ // carry it.
4111
+ if (STAMP_WHERE_TOOLS.has(name) && result && typeof result === "object" && !Array.isArray(result) && !result._where) {
4112
+ try { result._where = getWhere(sessionId); } catch { /* never break a tool on labeling */ }
4113
+ }
4114
+
4070
4115
  // For ateam_bootstrap, inject session context so the LLM knows what the user was working on
4071
4116
  if (name === "ateam_bootstrap") {
4072
4117
  const ctx = getSessionContext(sessionId);