@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.
- package/package.json +1 -1
- package/src/api.js +43 -0
- package/src/tools.js +54 -9
package/package.json
CHANGED
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
|
-
"
|
|
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);
|