@ateam-ai/mcp
A-Team MCP Server — build, validate, and deploy multi-agent solutions from any AI environment
npm · fingerprint 190f839709a88bae0ed829ef · repository · RSS
What changed
Changed 2026-10-14 11:46 UTC
2 descriptions rewritten, 1 schema or annotation change with the description left byte-identical.
--- pinned/ateam_build_and_run +++ observed/ateam_build_and_run { "_meta": { "agent_exposure": "all" - "description": "DEPLOY THE CURRENT MAIN BRANCH TO A-TEAM CORE. ⚠️ HEAVIEST OPERATION (60-180s): validates solution+skills → deploys all connectors+skills to Core (regenerates MCP servers) → health-checks → optionally runs a warm test → then ateam_github_push (when it runs: its /spec entry).\n\nOVER A HOSTED CONNECTION (HTTP), which is cut off after ~100s without an answer, the call answers within 75s. A run not finished by then answers status:\"running\" with a run_id and goes on: nothing is stopped or sent again. ateam_build_and_run(solution_id, resume:true, run_id) then answers with THAT run's result (waiting up to the same time again) and deploys nothing. A local (stdio) connection waits for the whole run.\n\n🌳 DEV/PROD WORKFLOW:\n 1. Edit files → ateam_github_patch (writes `dev` by default)\n 2. (Optional) Preview → ateam_github_diff\n 3. Ship dev → main → ateam_github_promote (merges + auto-tags `prod-YYYY-MM-DD-NNN`)\n 4. Deploy main to Core → ateam_build_and_run\n\nWhatever you do not pass inline comes from the `main` branch. A part you DO pass (solution, skills, mcp_store) deploys as you sent it. To TEST dev work without shipping it, use the iterate tools (ateam_patch, ateam_upload_connector, ateam_redeploy), which deploy from `dev`; to SHIP it, promote first.\n\n`ref` (branch, tag or commit SHA; with solution_id only) deploys that exact commit (a Git push); off `main` it warns. `deployed_commit` names it; `running_record` records it.\n\nAUTO-DETECTS GitHub repo: if you omit mcp_store and a repo exists, connector code is pulled from main. First deploy requires mcp_store. For small changes prefer ateam_patch.\n\nREFUSED, before anything is saved or deployed (409 UNPUSHED_BUILDER_CHANGE, naming the files), while a solution or skill file it would take from `main` holds a Builder change that no branch has: a Builder save held off `dev` (after a ref:'main' hotfix or a rollback) or one whose push to GitHub failed. Deploying would overwrite it and it would exist nowhere. Place it: ateam_redeploy(solution_id) — the whole solution, not one skill — writes the Builder's copy of solution.json and of every skill to `dev` (run ateam_github_sync_from_main first when the file holds a hotfix — ateam_redeploy says so; its not_written_to_github says what it could not place), then ateam_github_promote and deploy again. Or drop it: ateam_github_pull(solution_id, discard_builder_changes:true) replaces the Builder's copy with `dev`'s.", + "description": "DEPLOY THE CURRENT MAIN BRANCH TO A-TEAM CORE. ⚠️ HEAVIEST OPERATION (60-180s): validates solution+skills → deploys all connectors+skills to Core (regenerates MCP servers) → health-checks → optionally runs a warm test → then ateam_github_push (when it runs: its /spec entry).\n\nOVER A HOSTED CONNECTION (HTTP), which is cut off after ~100s without an answer, the call answers within 45s. A run not finished by then answers status:\"running\" with a run_id and goes on: nothing is stopped or sent again. ateam_build_and_run(solution_id, resume:true, run_id) then answers with THAT run's result (waiting up to the same time again) and deploys nothing. A local (stdio) connection waits for the whole run.\n\n🌳 DEV/PROD WORKFLOW:\n 1. Edit files → ateam_github_patch (writes `dev` by default)\n 2. (Optional) Preview → ateam_github_diff\n 3. Ship dev → main → ateam_github_promote (merges + auto-tags `prod-YYYY-MM-DD-NNN`)\n 4. Deploy main to Core → ateam_build_and_run\n\nWhatever you do not pass inline comes from the `main` branch. A part you DO pass (solution, skills, mcp_store) deploys as you sent it. To TEST dev work without shipping it, use the iterate tools (ateam_patch, ateam_upload_connector, ateam_redeploy), which deploy from `dev`; to SHIP it, promote first.\n\n`ref` (branch, tag or commit SHA; with solution_id only) deploys that exact commit (a Git push); off `main` it warns. `deployed_commit` names it; `running_record` records it.\n\nAUTO-DETECTS GitHub repo: if you omit mcp_store and a repo exists, connector code is pulled from main. First deploy requires mcp_store. For small changes prefer ateam_patch.\n\nREFUSED, before anything is saved or deployed (409 UNPUSHED_BUILDER_CHANGE, naming the files), while a solution or skill file it would take from `main` holds a Builder change that no branch has: a Builder save held off `dev` (after a ref:'main' hotfix or a rollback) or one whose push to GitHub failed. Deploying would overwrite it and it would exist nowhere. Place it: ateam_redeploy(solution_id) — the whole solution, not one skill — writes the Builder's copy of solution.json and of every skill to `dev` (run ateam_github_sync_from_main first when the file holds a hotfix — ateam_redeploy says so; its not_written_to_github says what it could not place), then ateam_github_promote and deploy again. Or drop it: ateam_github_pull(solution_id, discard_builder_changes:true) replaces the Builder's copy with `dev`'s.", "inputSchema": { "properties": { "connectors": {
--- pinned/ateam_redeploy +++ observed/ateam_redeploy { "_meta": { "agent_exposure": "all" - "description": "Re-deploy skills WITHOUT changing any definitions. ⚠️ HEAVY OPERATION: regenerates MCP servers (Python code) for every skill, pushes each to A-Team Core, restarts connectors, and verifies tool discovery. Takes 30-120s depending on skill count. Use after connector restarts, Core hiccups, or stale state. For incremental changes, prefer ateam_patch (which updates + redeploys in one step).", + "description": "Re-deploy skills WITHOUT changing any definitions. ⚠️ HEAVY OPERATION: regenerates MCP servers (Python code) for every skill, pushes each to A-Team Core, restarts connectors, and verifies tool discovery. Takes 30-120s depending on skill count; over a hosted connection the call answers within 45s, and a redeploy not done by then answers status:'running' with a job_id (ateam_redeploy({solution_id, job_id, resume:true}) waits for it again). Use after connector restarts, Core hiccups, or stale state. For incremental changes, prefer ateam_patch (which updates + redeploys in one step).", "inputSchema": { "properties": { + "job_id": { + "description": "With resume:true: the job_id a status:\"running\" answer gave.", + "type": "string" + }, + "resume": { + "description": "true with job_id: wait again for a redeploy that answered status:\"running\" and answer with its result. Deploys nothing new.", + "type": "boolean" + }, "skill_id": { "description": "Optional: redeploy a single skill only. Omit to redeploy ALL skills in the solution.", "type": "string"
--- pinned/ateam_upload_connector +++ observed/ateam_upload_connector { "_meta": { "agent_exposure": "outside_only" + "job_id": { + "description": "With resume:true: the job_id a status:\"running\" answer gave.", + "type": "string" + }, "ref": { "description": "GitHub branch to read from for the BASE state. Default: 'dev' (matches ateam_github_patch). Pass 'main' to read from production. Pre-2026-06-05 callers that relied on the silent-main default must pass ref:'main' explicitly.", "type": "string" + "resume": { + "description": "true with job_id: wait again for an upload that answered status:\"running\" and answer with its result. Uploads nothing new.", + "type": "boolean" + }, "solution_id": { "description": "The solution ID", "type": "string"
Changed 2026-10-13 14:13 UTC
6 descriptions rewritten, 52 schema or annotation changes with the description left byte-identical, 2 tools added, 1 tool removed.
--- pinned/ateam_auth +++ observed/ateam_auth { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": false, "readOnlyHint": false
--- pinned/ateam_bootstrap +++ observed/ateam_bootstrap { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_build_and_run +++ observed/ateam_build_and_run { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false - "description": "DEPLOY THE CURRENT MAIN BRANCH TO A-TEAM CORE. ⚠️ HEAVIEST OPERATION (60-180s): validates solution+skills → deploys all connectors+skills to Core (regenerates MCP servers) → health-checks → optionally runs a warm test → then ateam_github_push (when it runs: its /spec entry).\n\nOVER A HOSTED CONNECTION (HTTP), which is cut off after ~100s without an answer, the call answers within 75s. A run not finished by then answers status:\"running\" with a run_id and goes on: nothing is stopped or sent again. ateam_build_and_run(solution_id, resume:true, run_id) then answers with THAT run's result (waiting up to the same time again) and deploys nothing. A local (stdio) connection waits for the whole run.\n\n🌳 DEV/PROD WORKFLOW:\n 1. Edit files → ateam_github_patch (writes to `dev` branch by default)\n 2. (Optional) Preview what's about to ship → ateam_github_diff\n 3. Ship dev → main → ateam_github_promote (merges + auto-tags `prod-YYYY-MM-DD-NNN`)\n 4. Deploy main to Core → ateam_build_and_run\n\nWhatever you do not pass inline comes from the `main` branch — there is no `ref` parameter. A part you DO pass (solution, skills, mcp_store) deploys as you sent it. To TEST dev work without shipping it, use the iterate tools (ateam_patch, ateam_upload_connector, ateam_redeploy), which deploy from `dev`; to SHIP it, promote first.\n\nAUTO-DETECTS GitHub repo: if you omit mcp_store and a repo exists, connector code is pulled from main automatically. First deploy requires mcp_store. After that, edit via ateam_github_patch + promote, then build_and_run. For small changes prefer ateam_patch (faster, incremental). Requires authentication.\n\nREFUSED, before anything is saved or deployed (409 UNPUSHED_BUILDER_CHANGE, naming the files), while a solution or skill file it would take from `main` holds a Builder change that no branch has: a Builder save held off `dev` (after a ref:'main' hotfix or a rollback) or one whose push to GitHub failed. Deploying would overwrite it and it would exist nowhere. Place it: ateam_redeploy(solution_id) — the whole solution, not one skill — writes the Builder's copy of solution.json and of every skill to `dev` (run ateam_github_sync_from_main first when the file holds a hotfix — ateam_redeploy says so; its not_written_to_github says what it could not place), then ateam_github_promote and deploy again. Or drop it: ateam_github_pull(solution_id, discard_builder_changes:true) replaces the Builder's copy with `dev`'s.", + "description": "DEPLOY THE CURRENT MAIN BRANCH TO A-TEAM CORE. ⚠️ HEAVIEST OPERATION (60-180s): validates solution+skills → deploys all connectors+skills to Core (regenerates MCP servers) → health-checks → optionally runs a warm test → then ateam_github_push (when it runs: its /spec entry).\n\nOVER A HOSTED CONNECTION (HTTP), which is cut off after ~100s without an answer, the call answers within 75s. A run not finished by then answers status:\"running\" with a run_id and goes on: nothing is stopped or sent again. ateam_build_and_run(solution_id, resume:true, run_id) then answers with THAT run's result (waiting up to the same time again) and deploys nothing. A local (stdio) connection waits for the whole run.\n\n🌳 DEV/PROD WORKFLOW:\n 1. Edit files → ateam_github_patch (writes `dev` by default)\n 2. (Optional) Preview → ateam_github_diff\n 3. Ship dev → main → ateam_github_promote (merges + auto-tags `prod-YYYY-MM-DD-NNN`)\n 4. Deploy main to Core → ateam_build_and_run\n\nWhatever you do not pass inline comes from the `main` branch. A part you DO pass (solution, skills, mcp_store) deploys as you sent it. To TEST dev work without shipping it, use the iterate tools (ateam_patch, ateam_upload_connector, ateam_redeploy), which deploy from `dev`; to SHIP it, promote first.\n\n`ref` (branch, tag or commit SHA; with solution_id only) deploys that exact commit (a Git push); off `main` it warns. `deployed_commit` names it; `running_record` records it.\n\nAUTO-DETECTS GitHub repo: if you omit mcp_store and a repo exists, connector code is pulled from main. First deploy requires mcp_store. For small changes prefer ateam_patch.\n\nREFUSED, before anything is saved or deployed (409 UNPUSHED_BUILDER_CHANGE, naming the files), while a solution or skill file it would take from `main` holds a Builder change that no branch has: a Builder save held off `dev` (after a ref:'main' hotfix or a rollback) or one whose push to GitHub failed. Deploying would overwrite it and it would exist nowhere. Place it: ateam_redeploy(solution_id) — the whole solution, not one skill — writes the Builder's copy of solution.json and of every skill to `dev` (run ateam_github_sync_from_main first when the file holds a hotfix — ateam_redeploy says so; its not_written_to_github says what it could not place), then ateam_github_promote and deploy again. Or drop it: ateam_github_pull(solution_id, discard_builder_changes:true) replaces the Builder's copy with `dev`'s.", "inputSchema": { "properties": { "connectors": { + "ref": { + "description": "Optional: deploy exactly this commit instead of `main`. A branch, a tag (e.g. a prod-… one from ateam_github_list_versions), a commit SHA, or `owner/repo@<ref>`. The Builder resolves it to ONE commit of this workspace's bound repository (ateam_get_repository_binding names it), builds that commit, and the answer's `deployed_commit` names it; a branch that moves later changes nothing. Pass only solution_id with it: no solution, skills, connectors or mcp_store. ref deploys exactly one commit: it is resolved once to a SHA of this workspace's own repository (another repository is refused) and `deployed_commit` names it. Main's tip deploys as a call without ref does. Any other commit (a `dev` commit you pushed, an older prod-* tag) is deployed as you named it and the answer WARNS (deploy.warnings, REF_DEPLOY): whether the commit is part of `main`, and which files `dev` changed that it lacks, which replaces what an earlier deploy from `dev` put in Core. It never pushes to `dev` or `main` and writes no CLAUDE.md; the connector files Core runs are the commit's files and no others (connector_files_removed lists any it dropped). Deployed is not shipped: a commit that is not part of `main` is still not production, and the next ateam_build_and_run without ref deploys `main` over it.", + "type": "string" + }, "resume": { "description": "Optional: true answers with the result of a run already started (one that answered status:\"running\") instead of deploying. Needs solution_id; every other deploy argument is ignored. Waits for the run up to the hosted call's budget again.", "type": "boolean"
--- pinned/ateam_chain_status +++ observed/ateam_chain_status { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_connector_logs +++ observed/ateam_connector_logs { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_conversation +++ observed/ateam_conversation { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_create_connector +++ observed/ateam_create_connector { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_create_plugin +++ observed/ateam_create_plugin { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_delete_connector +++ observed/ateam_delete_connector { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_delete_skill +++ observed/ateam_delete_skill { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_delete_solution +++ observed/ateam_delete_solution { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_design_advisor +++ observed/ateam_design_advisor { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_get_chain +++ observed/ateam_get_chain { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_get_connector_source +++ observed/ateam_get_connector_source { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_get_deployed_connector_source +++ observed/ateam_get_deployed_connector_source { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_get_examples +++ observed/ateam_get_examples { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_get_execution_logs +++ observed/ateam_get_execution_logs { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_get_lessons +++ observed/ateam_get_lessons { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_get_metrics +++ observed/ateam_get_metrics { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_get_progress +++ observed/ateam_get_progress { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
+ tool added: ateam_get_repository_binding
--- pinned/ateam_get_solution +++ observed/ateam_get_solution { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true }, - "description": "Read solution state — definition, skills, health, status, or export. Use this to inspect deployed solutions.", + "description": "Read solution state — definition, skills, health, status, or export. Use this to inspect deployed solutions. status and health also answer `running`: the commit last deployed, the ref asked, who deployed it and when, and whether the solution was deployed to again since (known:false and the reason when no deploy was recorded).", "inputSchema": { "properties": { "limit": {
--- pinned/ateam_get_spec +++ observed/ateam_get_spec { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_get_widget_catalog +++ observed/ateam_get_widget_catalog { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_get_workflows +++ observed/ateam_get_workflows { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_github_diff +++ observed/ateam_github_diff { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_github_list_versions +++ observed/ateam_github_list_versions { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true }, - "description": "List the prod-YYYY-MM-DD-NNN tags each ateam_github_promote wrote for a solution — the points ateam_github_rollback can return main to. Shows tag name, date, counter, and commit SHA. Solutions older than 2026-05-19 may also carry retired safe-* tags; rollback accepts those too.", + "description": "List the prod-YYYY-MM-DD-NNN tags each ateam_github_promote wrote for a solution — the points ateam_github_rollback can restore dev to. Shows tag name, date, counter, and commit SHA. Solutions older than 2026-05-19 may also carry retired safe-* tags; rollback accepts those too.", "inputSchema": { "properties": { "solution_id": {
--- pinned/ateam_github_log +++ observed/ateam_github_log { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_github_patch +++ observed/ateam_github_patch { + "_meta": { + "agent_exposure": "outside_only" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_github_promote +++ observed/ateam_github_promote { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false - "description": "SHIP DEV TO PROD. Merges the `dev` branch into `main` and auto-tags the new main HEAD as prod-YYYY-MM-DD-NNN. Use after testing your dev work, when you're ready to deploy changes to production.\n\nWorkflow: 1) ateam_github_patch (writes to dev) → 2) ateam_github_promote (merges dev→main) → 3) ateam_build_and_run (deploys main).\n\nPass dry_run:true to see what's about to ship without merging.\n\nON 409 MERGE CONFLICT: main holds commits dev never received. Call ateam_github_sync_from_main(solution_id) to merge main into dev, then promote again. Only if THAT also returns 409 did both sides edit the same lines — that one needs a human (open a PR on GitHub).", + "description": "SHIP DEV TO PROD. Moves `main` forward to the exact version tried on `dev` (no merge commit) and auto-tags the new main HEAD as prod-YYYY-MM-DD-NNN. Use after testing your dev work, when you're ready to deploy changes to production.\n\nWorkflow: 1) ateam_github_patch (writes to dev) → 2) ateam_github_promote (moves main to the tested dev version) → 3) ateam_build_and_run (deploys main).\n\nPass dry_run:true to see what's about to ship without merging.\n\nmain only ever moves to a commit dev already has, so a promote does not conflict. An older repo where main holds a commit dev never received is brought level by the platform on the next build, not by you: do not open a PR.", "inputSchema": { "properties": { "dry_run": {
--- pinned/ateam_github_pull +++ observed/ateam_github_pull { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_github_push +++ observed/ateam_github_push { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_github_read +++ observed/ateam_github_read { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_github_rollback +++ observed/ateam_github_rollback { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false - "description": "Roll prod (`main` branch) back to a previous state.\n\nADDITIVE — does NOT destroy history. Creates a new commit on top of main whose tree matches the target's tree. The history of everything between target and current main is preserved (you can roll back the rollback).\n\nWorkflow: 1) ateam_github_list_versions (find a prod-YYYY-MM-DD-NNN tag) → 2) ateam_github_rollback(target: '<that tag>') → 3) ateam_build_and_run(solution_id) (deploys the reverted state) → 4) ateam_github_sync_from_main(solution_id), so `dev` carries it too: the iterate tools deploy from `dev`. Solutions older than 2026-05-19 may also carry retired safe-* tags; rollback accepts those too.", + "description": "RESTORE `dev` TO A PROMOTED VERSION. Puts the files of a previous promoted version (an exact promoted-version tag) back on `dev` as a new commit on top of it, history preserved, and deploys nothing by itself. Production is never rolled back directly (that would put it on a state dev never had): restore, try it on dev, then ateam_github_promote.\n\nWorkflow: 1) ateam_github_list_versions (find a prod-YYYY-MM-DD-NNN tag) → 2) ateam_github_rollback(target: '<that tag>') → 3) ateam_build_and_run to try it → 4) ateam_github_promote. Solutions older than 2026-05-19 may also carry retired safe-* tags; rollback accepts those too.", "inputSchema": { "properties": { "solution_id": { - "description": "A prod-YYYY-MM-DD-NNN tag or a commit SHA to revert main to. Use ateam_github_list_versions to find the tags. Solutions older than 2026-05-19 may also carry retired safe-* tags; rollback accepts those too.", + "description": "A prod-YYYY-MM-DD-NNN tag (an exact promoted version) to restore dev to. Use ateam_github_list_versions to find the tags. Solutions older than 2026-05-19 may also carry retired safe-* tags; rollback accepts those too.", "type": "string" } },
--- pinned/ateam_github_status +++ observed/ateam_github_status { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_github_sync_from_main +++ observed/ateam_github_sync_from_main { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false - "description": "BRING `dev` UP TO DATE WITH `main` — merges main into dev. The mirror of ateam_github_promote.\n\nUSE THIS WHEN PROMOTE RETURNS 409. promote only ships dev→main, so the moment anything lands on main directly — a hotfix, a manual edit, an ateam_github_rollback, or a write that mis-targeted the branch — dev falls behind and can never be promoted again. Without this tool that divergence is unfixable from A-Team: the only exits are the GitHub web UI or a raw API call.\n\nWorkflow on a 409: 1) ateam_github_diff (confirm status:'diverged') → 2) ateam_github_sync_from_main → 3) ateam_github_promote.\n\nPass dry_run:true FIRST to see exactly which commits and files would come into dev without changing anything.\n\nThis is a real merge, not a force: if main and dev edited the SAME lines it returns 409 too, and that one genuinely needs a human (open a PR).", + "description": "BRING `dev` UP TO DATE WITH `main` — merges main into dev. The mirror of ateam_github_promote.\n\nREPAIR TOOL FOR AN OLD REPO — not a step of building or shipping. main now only moves to a commit dev already has, so dev does not fall behind it and a promote does not return 409. Use this only when ateam_github_diff shows status:'diverged' on a repo that split before that rule.\n\nWorkflow: 1) ateam_github_diff (confirm status:'diverged') → 2) ateam_github_sync_from_main → 3) ateam_github_promote.\n\nPass dry_run:true FIRST to see exactly which commits and files would come into dev without changing anything.\n\nThis is a real merge, not a force: if main and dev edited the SAME lines it returns 409 too, and that one genuinely needs a human (open a PR).", "inputSchema": { "properties": { "dry_run": {
--- pinned/ateam_github_write +++ observed/ateam_github_write { + "_meta": { + "agent_exposure": "outside_only" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
+ tool added: ateam_inspect_data
--- pinned/ateam_list_solutions +++ observed/ateam_list_solutions { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_log_lesson +++ observed/ateam_log_lesson { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": false, "readOnlyHint": false
--- pinned/ateam_log_progress +++ observed/ateam_log_progress { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": false, "readOnlyHint": false
--- pinned/ateam_patch +++ observed/ateam_patch { + "_meta": { + "agent_exposure": "outside_only" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_recover_connector_source +++ observed/ateam_recover_connector_source { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_redeploy +++ observed/ateam_redeploy { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_show_skill_minimal +++ observed/ateam_show_skill_minimal { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_show_solution_minimal +++ observed/ateam_show_solution_minimal { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_spec_search +++ observed/ateam_spec_search { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_status_all +++ observed/ateam_status_all { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_sync_all +++ observed/ateam_sync_all { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_test_abort +++ observed/ateam_test_abort { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_test_connector +++ observed/ateam_test_connector { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_test_notification +++ observed/ateam_test_notification { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": false, "readOnlyHint": false
--- pinned/ateam_test_pipeline +++ observed/ateam_test_pipeline { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": false, "readOnlyHint": false
--- pinned/ateam_test_skill +++ observed/ateam_test_skill { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_test_status +++ observed/ateam_test_status { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_test_voice +++ observed/ateam_test_voice { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_upload_connector +++ observed/ateam_upload_connector { + "_meta": { + "agent_exposure": "outside_only" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_verify +++ observed/ateam_verify { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false
--- pinned/ateam_verify_consistency +++ observed/ateam_verify_consistency { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "readOnlyHint": true },
--- pinned/ateam_verify_surface +++ observed/ateam_verify_surface { + "_meta": { + "agent_exposure": "all" + }, "annotations": { "destructiveHint": true, "readOnlyHint": false - "description": "Optional assertion. { tools: ['memory.get', ...] } — each listed tool MUST be called by the plugin, else ok:false. { values: ['<a value from a row you created, exactly as the widget shows it, e.g. 7.2 km, Living room, Pasta carbonara>'] } — each string must appear in visible_text AND must DISAPPEAR when the data path is disabled: the probe renders the plugin a second time with every data call answered by an error, and a value still rendered then is hardcoded in the plugin, so the probe fails it. Use a value from a record you created, formatted as the widget shows it. Then finish: delete that record through the solution's own delete, and show it gone with the SAME read that showed it after you created it. The whole rule, TEST_ROW_DONE_RULE: ateam_get_spec({ topic: \"widgets\", search: \"data_fidelity\" }).", + "description": "Optional assertion. { tools: ['memory.get', ...] } — each listed tool MUST be called by the plugin, else ok:false. { values: ['<a value from a row you created, exactly as the widget shows it, e.g. 7.2 km, Living room, Pasta carbonara>'] } — each string must appear in visible_text AND must DISAPPEAR when the data path is disabled: the probe renders the plugin a second time with every data call answered by an error, and a value still rendered then is hardcoded in the plugin, so the probe fails it. Use a value from a record you created, formatted as the widget shows it. Then finish: delete that record by its id through the solution's own delete. The whole rule, TEST_ROW_DONE_RULE: ateam_get_spec({ topic: \"widgets\", search: \"data_fidelity\" }).", "properties": { "tools": { "items": {
tool removed: ateam_github_reconcile
Changed 2026-10-06 13:20 UTC
2 descriptions rewritten.
--- pinned/ateam_github_patch +++ observed/ateam_github_patch { "annotations": { "destructiveHint": true, - "description": "Edit a file in the solution's GitHub repo and commit — ONE file per call. Modes:\n1. FULL FILE: provide `content` — replaces entire file (good for new files or small files)\n2. SEARCH/REPLACE: provide `search` + `replace` — surgical edit without sending full file (preferred for large files like server.js)\n3. DELETE A STRAY: `delete: true` — removes a stray connector file, of either kind: one at the repo root, OUTSIDE connectors/<connector-id>/ (a root server.js, package.json or ui-dist/…), or a copy NESTED under a connector's own prefix (connectors/<connector-id>/connectors/<connector-id>/…). Core runs neither. Only such a file: anything else is refused (DELETE_ONLY_STRAY_CONNECTOR_FILES). It is removed from the working branch (`dev`) only, in one commit — production changes only through ateam_github_promote, like every other edit; git history keeps it. Move what it holds into connectors/<connector-id>/ first if it is still needed — the CONNECTOR_FILE_OUTSIDE_CONNECTOR or CONNECTOR_PATH_NESTED refusal says how the two copies differ and, when the stray is on the branch, names the delete call.\nConnector files belong under connectors/<connector-id>/; a write anywhere else is refused (CONNECTOR_FILE_OUTSIDE_CONNECTOR), and so is one to a nested copy, connectors/<connector-id>/connectors/<connector-id>/… (CONNECTOR_PATH_NESTED).\nAlways use search/replace for large files (>5KB). Always read the file first with ateam_github_read to get the exact text to search for.\n\nDEFAULTS TO `dev` BRANCH — writes don't touch prod. Use ateam_github_promote to ship dev→main when ready. Pass ref:'main' only for emergency hotfixes. After one, run ateam_github_sync_from_main so `dev` has it too. Until `dev` holds the same content, the Builder's copy of that file is `main` content `dev` does not have: ateam_redeploy and ateam_patch refuse to deploy the solution — any skill of it, not only that file's — and name the file (they never ship `dev`'s older copy over the hotfix, nor write the hotfix over `dev`), and ateam_build_and_run deploys it from `main`. A Builder save of that file meanwhile stays in the Builder (its reply says NOT_WRITTEN_TO_GITHUB), and ateam_build_and_run is refused (UNPUSHED_BUILDER_CHANGE) until it is placed — ateam_github_pull too, unless told discard_builder_changes:true. Connector code has no such check: ateam_upload_connector deploys `dev`'s code, so sync before the next upload of that connector. The reply's fs_mirror says what the Builder did with the file (a note when it did NOT copy it: its own copy had a change of its own).", + "description": "Edit a file in the solution's GitHub repo and commit — ONE file per call. Modes:\n1. FULL FILE: provide `content` — replaces entire file (good for new files or small files)\n2. SEARCH/REPLACE: provide `search` + `replace` — surgical edit without sending full file (preferred for large files like server.js)\n3. DELETE A STRAY: `delete: true` — removes a stray connector file, of either kind: one at the repo root, OUTSIDE connectors/<connector-id>/ (a root server.js, package.json or ui-dist/…), or a copy NESTED under a connector's own prefix (connectors/<connector-id>/connectors/<connector-id>/…). Core runs neither. Only such a file: anything else is refused (DELETE_ONLY_STRAY_CONNECTOR_FILES). It is removed from the working branch (`dev`) only, in one commit — production changes only through ateam_github_promote, like every other edit; git history keeps it. Move what it holds into connectors/<connector-id>/ first if it is still needed — the CONNECTOR_FILE_OUTSIDE_CONNECTOR or CONNECTOR_PATH_NESTED refusal says how the two copies differ and, when the stray is on the branch, names the delete call.\nConnector files belong under connectors/<connector-id>/; a write anywhere else is refused (CONNECTOR_FILE_OUTSIDE_CONNECTOR), and so is one to a nested copy, connectors/<connector-id>/connectors/<connector-id>/… (CONNECTOR_PATH_NESTED).\nAlways use search/replace on an existing file. Always read the file first with ateam_github_read to get the exact text to search for.\n\nDEFAULTS TO `dev` BRANCH — writes don't touch prod. Use ateam_github_promote to ship dev→main when ready. Pass ref:'main' only for emergency hotfixes. After one, run ateam_github_sync_from_main so `dev` has it too. Until `dev` holds the same content, the Builder's copy of that file is `main` content `dev` does not have: ateam_redeploy and ateam_patch refuse to deploy the solution — any skill of it, not only that file's — and name the file (they never ship `dev`'s older copy over the hotfix, nor write the hotfix over `dev`), and ateam_build_and_run deploys it from `main`. A Builder save of that file meanwhile stays in the Builder (its reply says NOT_WRITTEN_TO_GITHUB), and ateam_build_and_run is refused (UNPUSHED_BUILDER_CHANGE) until it is placed — ateam_github_pull too, unless told discard_builder_changes:true. Connector code has no such check: ateam_upload_connector deploys `dev`'s code, so sync before the next upload of that connector. The reply's fs_mirror says what the Builder did with the file (a note when it did NOT copy it: its own copy had a change of its own).", "inputSchema": { "properties": { "branch": {
--- pinned/ateam_github_write +++ observed/ateam_github_write { "annotations": { "destructiveHint": true, - "description": "Write a file to the solution's GitHub repo. Use this to create new connector files or replace existing ones — one file per call. This is the PRIMARY way to write connector code after first deploy. Write each file individually under connectors/<connector-id>/ (server.js, package.json, ui-dist/… assets), then call ateam_github_promote() to ship to prod (dev→main), then ateam_build_and_run() to deploy. Core deploys connectors/<connector-id>/ only, so a connector file written anywhere else (a root server.js, package.json or ui-dist/) is refused with CONNECTOR_FILE_OUTSIDE_CONNECTOR.\n\nDEFAULTS TO `dev` BRANCH.", + "description": "Write a NEW file to the solution's GitHub repo — one file per call. A file that ALREADY EXISTS is changed with ateam_github_patch search/replace (send only the lines that change), never rewritten whole here: streaming a whole file costs minutes. Use it for new connector files. Write each file individually under connectors/<connector-id>/ (server.js, package.json, ui-dist/… assets), then call ateam_github_promote() to ship to prod (dev→main), then ateam_build_and_run() to deploy. Core deploys connectors/<connector-id>/ only, so a connector file written anywhere else (a root server.js, package.json or ui-dist/) is refused with CONNECTOR_FILE_OUTSIDE_CONNECTOR.\n\nDEFAULTS TO `dev` BRANCH.", "inputSchema": { "properties": { "content": {
Changed 2026-10-05 14:22 UTC
34 descriptions rewritten, 23 schema or annotation changes with the description left byte-identical, 2 tools added.
--- pinned/ateam_auth +++ observed/ateam_auth { + "annotations": { + "destructiveHint": false, + "readOnlyHint": false + }, "core": true, - "description": "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. For cross-tenant admin operations, use master_key instead of api_key.", + "description": "Sign this session in to an A-Team workspace (tenant) with an API key — for a caller that holds the key OUTSIDE the chat: a local (stdio) ateam-mcp process reading it from a file, a platform proxy, a script, a self-hosted setup. A session must be signed in before any workspace operation. These need no sign-in: ateam_bootstrap, ateam_auth, ateam_get_spec, ateam_get_examples, ateam_get_workflows, ateam_spec_search, ateam_validate_skill, ateam_validate_solution.\n\nNO KEY IN THE CHAT: the agent never asks for an A-Team key and never accepts one in the chat. If the user pastes a key anyway, do not use it (not in ateam_auth, not anywhere): tell them it is now exposed, and that a workspace owner or admin should rotate it (Tokens & Keys, in the A-Team app) before it is used again.\n\nHow a user signs in depends on how this server is connected (the hosted connector signs in in the browser, with no ateam_auth call): ateam_bootstrap's `session` field says which workspace this session is on and gives the steps that apply to it.\n\nAn A-Team key names its own API, so it needs no url (adas_prod_… → https://api.ateam-ai.com). An ADAS_API_KEY environment variable does NOT sign a session in: a key baked into a shared config could point work at the wrong workspace, so workspace tools refuse until the session signs in. For cross-tenant admin operations, use master_key instead of api_key.", "inputSchema": { "properties": { "api_key": { - "description": "Your A-Team API key (e.g., adas_xxxxx)", + "description": "An A-Team API key held outside the chat — never one a user typed or pasted into it. An A-Team key names its own API, so it needs no url (adas_prod_… → https://api.ateam-ai.com).", "type": "string" }, "master_key": { - "description": "Tenant name (e.g., dev, main). Optional with api_key if format is adas_<tenant>_<hex>. REQUIRED with master_key.", + "description": "Tenant (workspace) name, e.g. acme. Leave it out with api_key: the key names its own workspace (ateam_auth asks the API when the key does not spell it out). REQUIRED with master_key.", "type": "string" }, "url": { - "description": "Optional API URL override (e.g., https://dev-api.ateam-ai.com). Use this to target a different environment without restarting the MCP server.", + "description": "Only for a host other than A-Team's own API (localhost, a self-hosted deployment), or for an older key that names no API (adas_<tenant>_<hex>, which otherwise lands on this server's default API). Leave it out for a current A-Team key: the key already names its API, and a url that contradicts it is refused.", "type": "string" } },
--- pinned/ateam_bootstrap +++ observed/ateam_bootstrap { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "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.", "inputSchema": {
--- pinned/ateam_build_and_run +++ observed/ateam_build_and_run { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "DEPLOY THE CURRENT MAIN BRANCH TO A-TEAM CORE. ⚠️ HEAVIEST OPERATION (60-180s): validates solution+skills → deploys all connectors+skills to Core (regenerates MCP servers) → health-checks → optionally runs a warm test → auto-pushes to GitHub.\n\n🌳 DEV/PROD WORKFLOW:\n 1. Edit files → ateam_github_patch (writes to `dev` branch by default)\n 2. (Optional) Preview what's about to ship → ateam_github_diff\n 3. Ship dev → main → ateam_github_promote (merges + auto-tags `prod-YYYY-MM-DD-NNN`)\n 4. Deploy main to Core → ateam_build_and_run\n\nThis tool ALWAYS deploys the `main` branch — there is no `ref` parameter. To deploy in-progress dev work, first promote it.\n\nAUTO-DETECTS GitHub repo: if you omit mcp_store and a repo exists, connector code is pulled from main automatically. First deploy requires mcp_store. After that, edit via ateam_github_patch + promote, then build_and_run. For small changes prefer ateam_patch (faster, incremental). Requires authentication.", + "description": "DEPLOY THE CURRENT MAIN BRANCH TO A-TEAM CORE. ⚠️ HEAVIEST OPERATION (60-180s): validates solution+skills → deploys all connectors+skills to Core (regenerates MCP servers) → health-checks → optionally runs a warm test → then ateam_github_push (when it runs: its /spec entry).\n\nOVER A HOSTED CONNECTION (HTTP), which is cut off after ~100s without an answer, the call answers within 75s. A run not finished by then answers status:\"running\" with a run_id and goes on: nothing is stopped or sent again. ateam_build_and_run(solution_id, resume:true, run_id) then answers with THAT run's result (waiting up to the same time again) and deploys nothing. A local (stdio) connection waits for the whole run.\n\n🌳 DEV/PROD WORKFLOW:\n 1. Edit files → ateam_github_patch (writes to `dev` branch by default)\n 2. (Optional) Preview what's about to ship → ateam_github_diff\n 3. Ship dev → main → ateam_github_promote (merges + auto-tags `prod-YYYY-MM-DD-NNN`)\n 4. Deploy main to Core → ateam_build_and_run\n\nWhatever you do not pass inline comes from the `main` branch — there is no `ref` parameter. A part you DO pass (solution, skills, mcp_store) deploys as you sent it. To TEST dev work without shipping it, use the iterate tools (ateam_patch, ateam_upload_connector, ateam_redeploy), which deploy from `dev`; to SHIP it, promote first.\n\nAUTO-DETECTS GitHub repo: if you omit mcp_store and a repo exists, connector code is pulled from main automatically. First deploy requires mcp_store. After that, edit via ateam_github_patch + promote, then build_and_run. For small changes prefer ateam_patch (faster, incremental). Requires authentication.\n\nREFUSED, before anything is saved or deployed (409 UNPUSHED_BUILDER_CHANGE, naming the files), while a solution or skill file it would take from `main` holds a Builder change that no branch has: a Builder save held off `dev` (after a ref:'main' hotfix or a rollback) or one whose push to GitHub failed. Deploying would overwrite it and it would exist nowhere. Place it: ateam_redeploy(solution_id) — the whole solution, not one skill — writes the Builder's copy of solution.json and of every skill to `dev` (run ateam_github_sync_from_main first when the file holds a hotfix — ateam_redeploy says so; its not_written_to_github says what it could not place), then ateam_github_promote and deploy again. Or drop it: ateam_github_pull(solution_id, discard_builder_changes:true) replaces the Builder's copy with `dev`'s.", "inputSchema": { "properties": { "connectors": { + "resume": { + "description": "Optional: true answers with the result of a run already started (one that answered status:\"running\") instead of deploying. Needs solution_id; every other deploy argument is ignored. Waits for the run up to the hosted call's budget again.", + "type": "boolean" + }, + "run_id": { + "description": "Optional, with resume:true: the run_id a status:\"running\" answer gave, so the answer is THAT run's. Omitted: the last run started for this solution with these credentials.", + "type": "string" + }, "skills": { "description": "Optional after first deploy: skill definitions. If omitted, auto-pulled from main (skills/{id}/skill.json).", "items": { + "monitoring": { + "cost": "heavy", + "latency_ms_p95": 75000, + "output": "bounded", + "safe": false + }, "name": "ateam_build_and_run" }
--- pinned/ateam_chain_status +++ observed/ateam_chain_status { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "SLIM chain status — the chip-quick poll. Given a chain_id (from ateam_conversation), returns the WHOLE-CHAIN aggregate status cheaply: chain_status + chain_done (true only when the ENTIRE chain — root job + every handoff + askAnySkill subcall — is terminal), plus pending_question, result, and a short progress line.\n\nThis is what you poll on a loop after ateam_conversation — NOT ateam_get_chain (that returns the full tree; too heavy for periodic polling). A single job can finish while the chain is still running, so poll chain_done, not a job's status.\n\nLoop: call every ~2s until chain_done === true (or pending_question is set — the assistant is waiting on the user). Then read `result` / fetch the full tree once via ateam_get_chain if you need per-job detail.", + "description": "SLIM chain status — the chip-quick poll. Given a chain_id (from ateam_conversation), returns the WHOLE-CHAIN aggregate status cheaply: chain_status + chain_done (true only when the ENTIRE chain — root job + every handoff + askAnySkill subcall — is terminal), plus pending_question, result, and a short progress line.\n\nThis is what you poll on a loop after ateam_conversation — NOT ateam_get_chain (that returns the full tree; too heavy for periodic polling). A single job can finish while the chain is still running, so poll chain_done, not a job's status.\n\nLoop: call every ~2s until chain_done === true. A step that waits for a person is just your next message: when pending_question is set, or the reply asks the person something, send ateam_conversation(same actor_id, the answer that person would give). Otherwise read `result` / fetch the full tree once via ateam_get_chain if you need per-job detail.", "inputSchema": { "properties": { "actor_id": { - "required": [ - "chain_id" - ], + "required": [], "type": "object" }, "monitoring": {
--- pinned/ateam_connector_logs +++ observed/ateam_connector_logs { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "Read what a connector process actually PRINTED to stderr. This is the only place a connector's internal failure is visible: a tool that catches its own error still returns ok:true, and the widget then renders an empty state that looks like real data.\n\nReal case (2026-08-11): a dashboard connector's ledger.getData got 401 Authentication required from Core, swallowed it, returned an empty ledger, and displayed 0.00 everywhere — while the upload said ok, the tool said ok:true, and the surface probe said surface_ok. The word 'Authentication' appeared ONLY here.\n\nUSE IT whenever a tool succeeds but the data is empty, wrong, or zero — that combination is the signature of a swallowed error, and 'the call returned ok' is not evidence it worked. Pass the returned `cursor` back as `since` to read only what is new since your last look, so you can bracket an action and see exactly what it printed. Only stdio (solution) connectors stream stderr through Core; a platform/HTTP connector answers ok:false with a reason.", + "description": "Read what a connector process actually PRINTED to stderr. This is the only place a connector's internal failure is visible: a tool that catches its own error still returns ok:true, and the widget then renders an empty state that looks like real data.\n\nReal case (2026-08-11): a dashboard connector's data tool got 401 Authentication required from Core, swallowed it, returned an empty list, and showed zeros everywhere — while the upload said ok, the tool said ok:true, and the surface probe said surface_ok. The word 'Authentication' appeared ONLY here.\n\nUSE IT whenever a tool succeeds but the data is empty, wrong, or zero — that combination is the signature of a swallowed error, and 'the call returned ok' is not evidence it worked. Pass the returned `cursor` back as `since` to read only what is new since your last look, so you can bracket an action and see exactly what it printed. Only stdio (solution) connectors stream stderr through Core; a platform/HTTP connector answers ok:false with a reason.", "inputSchema": { "properties": { "connector_id": { - "description": "The connector ID (e.g. 'accounting-dashboard-mcp')", + "description": "The connector ID (e.g. 'my-connector-mcp')", "type": "string" }, "errors_only": {
--- pinned/ateam_conversation +++ observed/ateam_conversation { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Send a chat message to a deployed solution. No skill_id needed — the system auto-routes to the right skill.\n\nALWAYS ASYNC: returns a chain_id immediately — the assistant's reply is NOT in this response (a conversation can run for minutes across handoffs + subcalls, so a synchronous wait would hit the 100s edge timeout → 524).\n\nPOLL BY CHAIN, NEVER BY JOB: an individual job can terminate while the chain is still running, so poll ateam_chain_status(chain_id) on a loop (~2s) and stop when chain_done === true (or pending_question is set — the assistant is waiting on the user). That is the cheap chip-quick poll (Core's whole-chain computeChainStatus — the same thing the standard chat uses). Use ateam_get_chain(chain_id) only ONCE at the end if you want the full tree / per-job detail — it's too heavy to loop on.\n\nMulti-turn: pass the actor_id from a previous response back in to continue the same thread (e.g. reply to a confirmation prompt). Each call starts a new chain; the same actor_id maintains conversation context.", + "description": "Chat with a deployed solution (auto-routed; no skill_id).\n\nALWAYS ASYNC: returns a chain_id at once; the reply is NOT here. Poll ateam_chain_status(chain_id) every ~2s until chain_done === true (by chain, never by job); ateam_get_chain(chain_id) once at the end.\n\nMulti-turn: Each call is a new chain, and your key, not actor_id, continues the conversation: Core keys it by the actor the job runs as (passing the reply's actor_id back is harmless). Core sends a message to the skill of that actor's last turn only within 60 s of it (5 min if the message is 25 characters or fewer), never when it starts with a new-task verb (build, create, make, …); later it is routed like a first message, so an answer can miss its question. A step that waits for a person is just your next message: when pending_question is set, or the reply asks the person something, send ateam_conversation(same actor_id, the answer that person would give). Play the person with an input you wrote, so you know the answer that person would give — never invent one for a real record. Who a test runs as: ateam_get_spec({ topic: \"skill\", search: \"actor_id\" }) → testing_and_runtime.conversation_testing.key_concepts.actor_id.", "inputSchema": { "properties": { "actor_id": { - "description": "Optional: actor ID from a previous response to continue the conversation. Omit for a new conversation.", + "description": "Optional: the conversation thread (the description says what continues a conversation, and the window an answer has). ateam-mcp keeps the actor_id you pass, and the one the reply returns (a test_ id excepted), as this session's actor for later job reads such as ateam_chain_status, so pass only a real one: an id Core does not know makes those reads fail (401). Who a test runs as: ateam_get_spec({ topic: \"skill\", search: \"actor_id\" }) → testing_and_runtime.conversation_testing.key_concepts.actor_id.", "type": "string" }, + "attachments": { + "description": "Optional files sent with the message, exactly as a file dropped into the chat: Core stores and analyses them the same way (image/* → vision, application/pdf → document, text/*, application/json, application/xml → inline text). Each item is { data: base64 (no data: prefix), mimeType, name? } OR { url, name?, mimeType? }: exactly one of data or url. Up to 10 files and 7 MB in total (decoded) per message: Core accepts a request of at most 10 MB, and base64 adds a third. Any other type, or more, is refused before anything is sent. A url is fetched by this MCP process only when it runs locally over stdio (its type comes from the response unless you give mimeType, its name from the URL path). The hosted server does not fetch URLs: send the file as base64 data.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "description": "The file's bytes, base64-encoded, without a data: prefix.", + "type": "string" + }, + "mimeType": { + "description": "The file's MIME type. Required with data. With url, overrides the type the server answered with.", + "type": "string" + }, + "name": { + "description": "File name shown to the skill (e.g. photo.jpg, notes.pdf, recipe.png). With url, defaults to the URL's last path segment.", + "type": "string" + }, + "url": { + "description": "http(s) URL of the file. Local stdio only; the hosted server refuses it.", + "type": "string" + } + }, + "type": "object" + }, + "maxItems": 10, + "type": "array" + }, "message": { "description": "The message to send (e.g., 'send email to X' or 'I confirm')", "type": "string"
--- pinned/ateam_create_connector +++ observed/ateam_create_connector { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Scaffold a new MCP connector with server.js + package.json + README. Eliminates ~50% of identical boilerplate (MCP server setup, tool registration, stdio transport). You then fill in the tool implementations. Set ui_capable=true to include ui.listPlugins / ui.getPlugin stubs (plugin source files added separately via ateam_create_plugin). After scaffolding, the files are uploaded to Core via the same path as ateam_upload_connector.", + "description": "Scaffold a NEW MCP connector with server.js + package.json + README: a defineConnector skeleton (@ateam-ai/sdk/serve): you write handlers; caller identity arrives as ctx. Where its data goes: ateam_get_spec('connector-multi-user') → storage_decision. You then fill in the tool implementations. Set ui_capable=true to include ui.listPlugins / ui.getPlugin stubs (plugin source files added separately via ateam_create_plugin). After scaffolding, the files are uploaded to Core via the same path as ateam_upload_connector. Create never replaces: the Builder writes the scaffold only if the connector exists nowhere (its source, the repo's working branch, Core), in the same request that checks. Otherwise CONNECTOR_EXISTS names where, with the way on, or, when a read failed, a retryable CONNECTOR_UNREADABLE. Nothing is uploaded either way.", "inputSchema": { "properties": { "connector_id": { + "monitoring": { + "cost": "heavy", + "latency_ms_p95": 75000, + "output": "bounded", + "safe": false + }, "name": "ateam_create_connector" }
--- pinned/ateam_create_plugin +++ observed/ateam_create_plugin { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Scaffold a UI plugin (iframe HTML, React Native TSX, or both) inside an existing connector. Eliminates ~50% of identical plugin boilerplate (imports, theme/bridge hooks, postMessage protocol, default export shape). You then fill in the component body. Use kind='iframe' for web-only, 'rn' for mobile-only, 'adaptive' for both. Also writes ui-dist/<plugin>/manifest.json with the required render block.\n\n⚠️ RENDERING IS NOT AUTOMATIC. At deploy, Phase 5 discovers plugins by calling each connector's ui.listPlugins + ui.getPlugin — a plugin only appears (and renders) if the connector ADVERTISES it there with a render.{mode, iframeUrl?, reactNative?} block. Dropping the scaffold files alone does NOT register it. If the connector generates its plugin list from ui-dist/<plugin>/manifest.json, the emitted manifest is picked up automatically; if the connector has a HARDCODED list (e.g. personal-assistant-ui-mcp: UI_PLUGINS[] + PLUGIN_MANIFESTS{} in server.js), you MUST add this plugin there (copy the render block from the manifest.json). Verify after deploy with ateam_get_solution(solution_id, 'connectors_health') or ateam_get_widget_catalog. Then declare it at solution ui_plugins[] so a skill can open it via sys.focusUiPlugin (see ateam_get_spec topic:'widgets').\n\nThe scaffold MERGES into the existing connector (server.js + other files preserved) — works on GitHub-backed AND repo-less tenants; merge base is the GitHub repo when connected, else the deployed connector source.", + "description": "Scaffold a UI plugin (iframe HTML, RN TSX, or both) inside an existing connector. Writes the boilerplate (imports, theme/bridge hooks, postMessage protocol, default export shape); you fill in the component body. 'iframe' = HTML (web + phone WebView), 'rn' = native (phone only), 'adaptive' = both. Also writes ui-dist/<plugin>/manifest.json with the required render block. ONE create-only request: refused with PLUGIN_EXISTS if this plugin's files exist anywhere, CONNECTOR_BASE_MISSING if the connector does not; never falls back to replace.\n\n⚠️ RENDERING IS NOT AUTOMATIC. A plugin renders only if its connector ADVERTISES it (ui.listPlugins + ui.getPlugin) with a render.{mode, iframeUrl?, reactNative?} block. The scaffold files alone register nothing. Core lists a running connector's plugins live, so ateam_upload_connector needs no deploy and no promote to show one. If the connector generates its plugin list from ui-dist/<plugin>/manifest.json, the emitted manifest is picked up automatically; if the connector has a HARDCODED list (e.g. personal-assistant-ui-mcp: UI_PLUGINS[] + PLUGIN_MANIFESTS{} in server.js), you MUST add this plugin there (copy the manifest.json render block). Nothing to declare: ateam_build_and_run's deploy, on every run, calls ui.listPlugins + ui.getPlugin on each connector listed in platform_connectors or in a skill's connectors[] (list this connector in the skill that opens the plugin) and MERGES each plugin it finds into solution.ui_plugins[], a shallow merge in which the fields you set yourself win. Add a solution.ui_plugins[] entry only to override surface, roles or uiActions, or for a runtime:'device' connector, which is never introspected; do not restate render, native or stateDomains (a partial render replaces the discovered one whole). A skill opens the plugin with sys.focusUiPlugin — see ateam_get_spec(topic:'widgets').\n\nThe scaffold MERGES into the existing connector (server.js + other files preserved), GitHub-backed or repo-less.", "inputSchema": { "properties": { "connector_id": { + "monitoring": { + "cost": "heavy", + "latency_ms_p95": 75000, + "output": "bounded", + "safe": false + }, "name": "ateam_create_plugin" }
--- pinned/ateam_delete_connector +++ observed/ateam_delete_connector { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, "description": "⚠️ CASCADING — any skill whose engine.bootstrap_tools or tools[] name a tool from this connector will FAIL its next execution. Stops and deletes the connector from A-Team Core; drops references from the solution definition (grants, platform_connectors, ui_plugins ids starting `mcp:<connector-id>:*`) and skill definitions (connectors array); cleans up mcp-store files. ALSO REMOVES THE SOURCE: `connectors/<id>/` is deleted from the repo (main AND dev) in the same call, so the delete is DURABLE. This CHANGED on 2026-08-23 — it used to preserve the source and name `ateam_build_and_run(github:true)` as a way to resurrect. That made a delete undo itself on a later publish, and only in some states, since connectors[] is synthesized from mcp_store keys ONLY when it is empty. If you want the code kept, copy it out (ateam_get_connector_source) BEFORE deleting. If the repo removal fails the response says so under `github` — Core is clean but the source is still there. REQUIRES `confirm:true`.", "inputSchema": {
--- pinned/ateam_delete_skill +++ observed/ateam_delete_skill { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "⚠️ IRREVERSIBLE in Core + Builder FS — kills the running MCP process, unregisters from skill registry, deletes the Mongo record, drops from solution.skills[] and solution.linked_skills, and removes the skill's files from Builder FS. REQUIRES `confirm:true`. RECOVERY: the skill still lives in GitHub — `ateam_github_pull` rebuilds the whole solution (no per-skill restore path).", + "description": "⚠️ IRREVERSIBLE — kills the running MCP process, unregisters from skill registry, deletes the Mongo record, drops from solution.skills[] and solution.linked_skills, and removes the skill's files from Builder FS. ALSO REMOVES THE SOURCE: `skills/<id>/` is deleted from the repo on BOTH dev and main in the same call. If that removal fails, the response says so under `github`. REQUIRES `confirm:true`. RECOVERY: Code and config can be recovered from git history, and rolling main back to a prod tag restores the files. There is no per-skill restore path.", "inputSchema": { "properties": { "confirm": {
--- pinned/ateam_delete_solution +++ observed/ateam_delete_solution { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "⚠️ IRREVERSIBLE — kills Mongo state, running MCP processes, and Builder FS for the whole solution and every skill. REQUIRES `confirm:true` AND `confirm_solution_id` echoing the solution id you're destroying (defeats typos and hallucinated ids). RECOVERY: the GitHub repo is untouched; `ateam_github_pull` rebuilds the solution from `main`. Prefer that over re-deploying from memory.", + "description": "⚠️ IRREVERSIBLE — THIS CLEARS THE WHOLE TENANT, not just the named solution. TENANT === SOLUTION. It removes EVERY skill and connector from the tenant's Core registry, including any ORPHAN that no longer belongs to a solution, and deletes the solution record and its Builder records. It WIPES the tenant's conversations and history, memory facts and stored actor data, and clears the voice configuration. It KEEPS the tenant account, its members and its settings. The name says 'solution'; the blast radius is the tenant. Read the tenant's skill list first (ateam_get_solution view:'skills' or Core's registry) and know what you are destroying — an orphan from an earlier solution is still in there and still goes. REQUIRES `confirm:true` AND `confirm_solution_id` echoing the solution id you're destroying (defeats typos and hallucinated ids). A forced delete is sent ONCE and never re-sent: if it answers NO_ANSWER, check with the preview before issuing it again. RECOVERY: Code and config can be recovered from git history, and rolling main back to a prod tag restores the files. Conversations, memory and stored data are gone and cannot be recovered. A skill authored FS-only, or orphaned before its solution was pushed, has no copy anywhere.", "inputSchema": { "properties": { "confirm": { - "description": "REQUIRED. Must exactly equal `solution_id`. This defeats typos and hallucinated ids — you can't wipe a solution you couldn't spell.", + "description": "Required with force:true. Must exactly equal `solution_id`. This defeats typos and hallucinated ids — you can't wipe a solution you couldn't spell.", "type": "string" }, + "force": { + "description": "Omit (DEFAULT) to PREVIEW: returns will_clear — every skill and connector that would be destroyed, with orphan_skills called out separately — and clears NOTHING. The conversations, memory and stored data a forced delete wipes are not in that list. Pass true only after reading it. The preview exists because a description cannot name the orphan you did not know was in the registry.", + "type": "boolean" + }, "solution_id": { - "description": "The solution ID to delete", + "description": "The solution ID to delete. Letters, digits, '-' and '_' only: anything else is refused before any request is sent.", "type": "string" } }, - "solution_id", - "confirm", - "confirm_solution_id" + "solution_id" ], "type": "object" },
--- pinned/ateam_design_advisor +++ observed/ateam_design_advisor { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "CONSULT THIS DURING DESIGN — before and while you design a skill/solution. Describe what you're building; it returns POINTERS to the platform capabilities that fit (per-actor storage, widgets, triggers, sub-agents, mobile data, run-scripts, multi-skill, GitHub, …), each with the /spec topic to read next (via ateam_get_spec) and the tool to wire it. Also returns 'missing' hints (capabilities your goal implies but the design hasn't wired) and lifecycle hints (e.g. connect GitHub when the project will iterate). ADVISORY ONLY — you decide and own the design. Stateless: pass the current design_state each call; consult it as often as you like as the design evolves.", + "description": "CONSULT THIS DURING DESIGN — before and while you design a skill/solution. If this call fails, your design is unchecked: do not write storage code on a store you picked before asking. ateam_get_spec('connector-multi-user') → storage_decision answers storage with no LLM (a failure the advisor answers carries it as `storage_decision`; on a 401 it may be cut — sign in and ask again for the full answer); retry after a short wait (a 'circuit open … cooling down' error names no time). Describe what you're building; it returns POINTERS to the capabilities that fit (storage, widgets, triggers, sub-agents, mobile data, …), each with the ateam_get_spec topic to read next and the tool to wire it, plus 'missing' and lifecycle hints. `conflicts_with_platform_rules` names a forbidden store your design plainly uses; `words_to_check` lists words that often mean one (a 'never /tmp' is listed too): check them against storage_decision. ADVISORY ONLY — you own the design. Stateless: pass the current design_state each call. `truncated: true` means the answer was CUT OFF: what is there is correct, but a capability's ABSENCE proves nothing — ask again with a narrower goal, or use ateam_spec_search.", "inputSchema": { "properties": { "design_state": {
--- pinned/ateam_get_chain +++ observed/ateam_get_chain { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "Inspect the full chain tree — the whole run rooted at chain_id, walking down through every handoff and askAnySkill subcall.\n\nUse when a chain has already run and you want to analyze the structure: which skill called which, how deep the call tree went, which tool inside which job invoked which sub-tool. The two main shapes:\n • response.data.chainJobs[] — one entry per job in the chain. Fields: jobId, skill, status, iteration, depth (0 = root, +1 per askAnySkill subcall hop), relation ('root' | 'subcall' | 'handoff'), parentJobId, parentSkill, goal.\n • response.data.executionSteps[] — every tool call across all chain jobs, tagged with _skill, _jobId, _depth (= job depth), _relation, _parentSkill, _parentJobId, _toolDepth (tool-in-tool nesting via opId/parentOpId).\n\nDiffers from ateam_test_status by purpose: status is for live polling of a job you just kicked off; get_chain is for post-hoc tree analysis (debugging multi-skill flows, regression testing, comparing two runs).\n\nAuth: forwards your authed api_key. Tenant scoped by the key itself. Actor scoping: you can only inspect chains rooted at jobs your actor has access to.", "inputSchema": {
--- pinned/ateam_get_connector_source +++ observed/ateam_get_connector_source { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "Read the source code files of a deployed MCP connector. Returns all files (server.js, package.json, etc.) stored in the mcp_store for this connector. Use this BEFORE patching or rewriting a connector — always read the current code first so you can make surgical fixes instead of blind full rewrites.", + "description": "Read a connector's AUTHORED source — the code the Builder holds as the source of record (its authored store, or GitHub). Returns a file manifest, or one file's content with path:'<file>'. Use this BEFORE patching or rewriting a connector, so you make surgical fixes instead of blind full rewrites. Every answer carries `provenance` (authored_fs | github) so you know which store you are reading.\n\nThis does NOT return what Core is currently RUNNING. Those are different questions and used to share one answer: a connector could be deployed and healthy while no authored copy of it existed anywhere, and this tool would hand back the runtime bytes as though they were the source. If there is no authored source you get AUTHORED_SOURCE_MISSING, not a silent substitute. To see the deployed copy use ateam_get_deployed_connector_source; to adopt it as authored source use ateam_recover_connector_source.", "inputSchema": { "properties": { "connector_id": {
+ tool added: ateam_get_deployed_connector_source
--- pinned/ateam_get_examples +++ observed/ateam_get_examples { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "Get complete working examples that pass validation. Study these before building your own.", "inputSchema": { - "description": "Example type: 'skill' = Order Support Agent, 'connector' = stdio MCP connector, 'connector-ui' = UI-capable connector, 'solution' = full 3-skill e-commerce solution, 'script-cache-skill' = fat-tool skill with script_cache opt-in (reference implementation of script-level JIT shortcuts — study this before building any browser-automation skill), 'ui-plugin-native' = complete working React Native (mobile) UI plugin (rn-src/index.tsx + esbuild build:rn → rn-bundle, @adas/plugin-sdk, es2015), 'index' = list all available examples", + "description": "Example type: 'skill' = Order Support Agent, 'connector' = stdio MCP connector, 'connector-ui' = UI-capable connector, 'solution' = full 3-skill e-commerce solution, 'script-cache-skill' = fat-tool skill with script_cache opt-in (reference implementation of script-level JIT shortcuts — study this before building any browser-automation skill), 'ui-plugin-native' = complete working React Native (mobile) UI plugin (rn-src/index.tsx + esbuild build:rn → rn-bundle, @adas/plugin-sdk, es2015), 'ui-plugin-iframe' = complete working web (iframe) UI plugin with the postMessage protocol, 'device-tools' = a solution's OWN tools that execute ON THE PHONE (runtime:\"device\") — BOTH halves and why they must agree: the plugin-bundle implementation, the connector manifest that declares it (Core cannot introspect a phone, so the manifest is the entire contract), and the skill wiring without which the skill gets none of them. Read this before designing anything that needs a LIVE device reading rather than the last synced one, 'index' = list all available examples", "enum": [ "skill", "connector", + "ui-plugin-iframe", + "device-tools", "index" ], "type": "string"
--- pinned/ateam_get_execution_logs +++ observed/ateam_get_execution_logs { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "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.)", "inputSchema": { - "description": "The actor whose job this is. REQUIRED for per-job detail: a job belongs to an actor and Core refuses the detail endpoint without one (the list form does not check). Use the same actor_id you passed to ateam_conversation.", + "description": "The actor whose job this is. REQUIRED for per-job detail: a job belongs to an actor and Core refuses the detail endpoint without one (the list form does not check). Usually the session already holds it. Otherwise pass the ran_as of the ateam_conversation / ateam_test_skill reply that started the job, not the actor_id you passed. Who a test runs as: ateam_get_spec({ topic: \"skill\", search: \"actor_id\" }) → testing_and_runtime.conversation_testing.key_concepts.actor_id.", "type": "string" }, "chain_id": {
--- pinned/ateam_get_lessons +++ observed/ateam_get_lessons { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "Read what EARLIER runs on this solution learned — newest first, bounded. Call this during orientation, BEFORE planning: it is the only thing that carries context across runs, and it is cheap. Each entry says which tool misled a previous run, the verbatim error, what was tried instead, and whether that worked. An empty list is a real answer (nothing learned yet).", + "description": "Read what EARLIER runs on this solution learned — the CURRENT lessons, newest first, bounded, each with its `id`. Call this during orientation, BEFORE planning: it is the only thing that carries context across runs, and it is cheap. Each entry says which tool misled a previous run, the verbatim error, what was tried instead, and whether that worked. An empty list is a real answer (nothing learned yet).\n\nEvery answer carries `correction_rule`: how a lesson that proved wrong is corrected. A superseded lesson is counted in `superseded_count`, never listed in `lessons`; include_superseded:true returns it in a separate `superseded` list, each with `superseded_by`. A correction carries `corrects`: the tool and error the lesson it replaced quoted.", "inputSchema": { "properties": { + "include_superseded": { + "description": "Also return the superseded lessons, in their own `superseded` list (default false)", + "type": "boolean" + }, "limit": { - "description": "Max entries, newest first (default 20)", + "description": "Max current lessons, newest first (default 20)", "type": "number" }, "solution_id": {
--- pinned/ateam_get_metrics +++ observed/ateam_get_metrics { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "Get execution metrics — timing, tool stats, bottlenecks, signals, and recommendations. (Advanced.)", "inputSchema": {
--- pinned/ateam_get_progress +++ observed/ateam_get_progress { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "What this build has already ACHIEVED — read it FIRST when continuing a run, before any orientation call. Returns the CURRENT status per step (latest entry wins) plus recent history.\n\nThen build only the steps that are ABSENT or not yet `verified`.\n\nABSENT IS NOT \"NOTHING WAS DONE\" — the journal may predate a step, or a run may have died before writing. It is a fast path, not a new source of truth: check before rebuilding something expensive.\n\nComplements ateam_get_lessons, which records what BROKE. This records what WORKS.", "inputSchema": {
--- pinned/ateam_get_solution +++ observed/ateam_get_solution { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "Read solution state — definition, skills, health, status, or export. Use this to inspect deployed solutions.", "inputSchema": { - "description": "Optional (with skill_id): return ONLY this section of the skill instead of the whole definition — avoids the ~50KB output truncation on big skills. Dotted paths work (e.g. 'role', 'tools', 'intents.supported', 'policy', 'engine'). Omit for the full skill; use ateam_show_skill_minimal for the slim authoring view.", + "description": "Optional (with skill_id): return ONLY this section of the skill instead of the whole definition — avoids the ~50KB output truncation on big skills. Dotted paths work (e.g. 'role', 'tools', 'intents.supported', 'policy', 'engine'). 'tools' answers every tool the skill's deploy sends — the ones written in skill.json (authored_tools) and the ones imported from its connectors — not only the file's list. Omit for the full skill; use ateam_show_skill_minimal for the slim authoring view.", "type": "string" }, "skill_id": { - "description": "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", + "description": "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, 'triggers' = every trigger each skill defines and every trigger Core has registered, each marked registered:true/false (null for an event trigger, with registered_source), with Core's state (pause, last run, next run) plus system_halted, the platform-wide trigger switch — a schedule is done only when it shows registered:true with a next run and system_halted:false. Anything less is not done: report what the result's done_rule says — system_halted_source and next_run_at_source name what Core did not report — never that the schedule will run (with skill_id: that skill only)", "enum": [ "definition", "skills", - "connectors_health" + "connectors_health", + "triggers" ], "type": "string" }
--- pinned/ateam_get_spec +++ observed/ateam_get_spec { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "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. Use 'section' to get just one part of the skill spec (much smaller than the full spec). Use 'search' to find specific fields or concepts across the spec.\n\nWhen designing a persona that orchestrates logic via run_python_script (the Python-as-orchestrator pattern), also fetch topic='python_helpers' — that returns the adas.* helper namespace reference. Skills designed without knowing about adas.* produce 5-10x larger / brittler scripts.\n\nWhen wiring widgets (UI plugins) into a solution, fetch topic='widgets' — that returns the widget spec (catalog model, how_to_use blocks, opener_call shape, persona phrasing rules, binding semantics) so you can declare `ui_plugins` correctly. For the live catalog of widgets actually available in a deployed tenant, use ateam_get_widget_catalog instead.", + "description": "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. Use 'section' to get just one part of the skill spec (much smaller than the full spec). Use 'search' to find specific fields or concepts across the spec. Needs no sign-in. Every answer carries `served_by`: the environment whose API answered (prod or dev, or the API base itself for any other host), the API this session talks to, so two environments' docs are never read as one.\n\nWhen designing a persona that orchestrates logic via run_python_script (the Python-as-orchestrator pattern), also fetch topic='python_helpers' — that returns the adas.* helper namespace reference. Skills designed without knowing about adas.* produce 5-10x larger / brittler scripts.\n\nWhen wiring widgets (UI plugins) into a solution, fetch topic='widgets' — that returns the widget spec (catalog model, how_to_use blocks, opener_call shape, persona phrasing rules, binding semantics) so you can declare `ui_plugins` correctly. For the live catalog of widgets actually available in a deployed tenant, use ateam_get_widget_catalog instead.", "inputSchema": { "properties": { "search": { - "description": "What to fetch: 'overview' = API overview + endpoints, 'skill' = full skill spec, 'solution' = full solution spec, 'enums' = all enum values, 'connector-multi-user' = multi-user connector guide, 'python_helpers' = adas.* helper namespace for run_python_script orchestration (read this when designing personas that read state → call tools → checkpoint → status; without it, scripts hand-roll JSON parsing and tool delegation = 5-10x larger and brittler), 'widgets' = widget (UI plugin) spec: catalog model, how_to_use block shape (solution.json snippet + opener_call + persona_phrasing + binding_notes), and rules for declaring ui_plugins. Pair with ateam_get_widget_catalog for the live per-tenant inventory. 'ui-plugins' = the DEEP React Native (mobile) plugin build guide: author in rn-src/, compile with a build:rn esbuild script (format=cjs, target=es2015, external react/react-native/@adas/plugin-sdk) to rn-bundle/index.bundle.js, plain-object export — read this before authoring any MOBILE widget. 'monitoring' = THE MONITORING CONTRACT: which tools are safe to call in a poll loop (with cost / poll interval / whether output stays bounded as the run grows), which are not and what to use instead, plus the running ateam-mcp version. Read this BEFORE writing any loop that watches a build — the safe poll is ateam_chain_status, never ateam_get_chain.", + "description": "What to fetch: 'realizations' = HOW to build a capability: for each one the valid physical routes with use_when / do_not_use_when / execution / freshness, so device-dependent design picks a route deliberately instead of by accident. 'capabilities' = START HERE IF YOU ARE NEW — the capability index, organised by what a solution DOES rather than by our build artifacts: can I see what the user sees? talk with them out loud? know where they are and that they are moving? act while they sleep? remember each user? show them something? Each question gets a one-word answer (yes / yes-with-gaps / not yet / unknown) and the topics to read next. Every other topic below is named after an ARTIFACT, so if you do not already know our vocabulary this is the only door you can find by thinking about your own problem. 'overview' = API overview + endpoints, 'skill' = full skill spec, 'solution' = full solution spec, 'enums' = all enum values, 'connector-multi-user' = multi-user connector guide, 'python_helpers' = adas.* helper namespace for run_python_script orchestration (read this when designing personas that read state → call tools → checkpoint → status; without it, scripts hand-roll JSON parsing and tool delegation = 5-10x larger and brittler), 'widgets' = widget (UI plugin) spec: catalog model, how_to_use block shape (solution.json snippet + opener_call + persona_phrasing + binding_notes), rules for declaring ui_plugins, and sections.data_fidelity: what a plugin that shows data must render, and when a test that wrote records is done. A plain read can arrive with that section stubbed (one response holds 50,000 characters), so read it whole with ateam_get_spec({ topic: \"widgets\", search: \"data_fidelity\" }). Pair with ateam_get_widget_catalog for the live per-tenant inventory. 'ui-plugins' = the UI plugin BUILD guide for every render mode: iframe (HTML under ui-dist/, rendered by the web and by the phone's WebView), react-native (phone only) and adaptive (both) — the manifest schema, the postMessage protocol, device tools, deployment, and the deep React Native build (author in rn-src/, compile with a build:rn esbuild script (format=cjs, target=es2015, external react/react-native/@adas/plugin-sdk) to rn-bundle/index.bundle.js, plain-object export). Read it before authoring any widget. What a plugin that shows data must render is read whole with ateam_get_spec({ topic: \"widgets\", search: \"data_fidelity\" }) (that page only points there). 'device-capabilities' = THE DEVICE CAPABILITY MATRIX, GENERATED from the mobile SDK's own artefacts and stamped with their hashes: every native.* API (mechanical one-shot verbs), every deviceState.* domain (semantic state a reasoning loop reads, with freshness + confidence) and every server-called device.* tool, each with status (done / partial / shape-only / missing) and what is left. READ THIS before concluding the phone cannot do something — camera, video, scanning, vision, sensors, location, on-device storage. Absence from any other spec topic is NOT evidence. 'mobile-connector' = building functional connectors (background services) for ateam-mobile that use device capabilities through the Native Bridge SDK. 'actor-storage' = per-actor storage (production): a per-(tenant, actor, skill) SQLite database served by the actorstore-mcp platform connector — read this instead of hand-rolling per-user isolation in a connector. 'consumer-roles' = role-based access for your solution's END-USERS: you declare the config, the platform resolves and enforces one RoleProfile per request (roles decide WHO may act; actor-storage decides WHOSE data they touch). 'triggers' = the ONLY way a skill acts proactively — on a schedule or an event, with no user message; read before designing anything that must happen by itself, decision_guide first: any time word in a requirement (every day, at 8, weekly) is a trigger; a recurring check the user gave no cadence for gets none (ask, add no trigger: decision_guide.implied_repetition_without_cadence); and a trigger is done only when ateam_get_solution(view:'triggers') shows it registered:true with a next run and system_halted:false — anything less is not done: report what that result's done_rule says. 'sub-agent' = sub-agents are a TOOL CALL (sys.callAiWithTools with a curated toolNames set), not a definition-level construct; caveats stated inline. 'voice' = the voice channel: phone (Twilio) and web/mobile callers reach the SAME skill runtime as chat — what you control (solution.voice, routing.voice.default_skill, a per-skill voice block, ateam_test_voice) and what you do not. 'voice-native' = the exception to that model: a `voice_native` block puts a skill inside the live audio loop (persona layer, one server skill tool, plus local device tools), with the boundaries that come with it. 'platform-connectors' = the built-in platform connectors (memory, browser, gmail, whatsapp, …) with their LIVE tool schemas and the inter-connector calling pattern — read before writing a connector that duplicates one. 'sdk' = the @ateam-ai/sdk runtime API reference (platform, context, memory, progress, log, llm) for custom connector and skill code. 'host-contract' = the normative boundary between a host shell (mobile app, web shell, kiosk, watch) and the solutions it renders: ownership matrix, forbidden host behaviours, host capability allow-list — read when reviewing a host or designing a portable solution. 'platform-truth' = does this deployment's published sys.* tools and platform connectors agree with what the RUNNING Core exposes (including planner visibility)? Answers agrees:null when Core cannot be reached, never silence. 'workflows' = the Builder's step-by-step state machines for building skills and solutions (the same document ateam_get_workflows returns). 'finalization' = HOW A SKILL'S RUN ENDS, every way: finish with sys.finalizePlan, ask with sys.askUser, hand off with sys.handoffToSkill, or let role.finalize_tool decide; the checks a reply goes through before it goes out; what the platform does when a skill does not finish (step and stuck-loop limits); what a skill that called yours sees (ok, infra_ok, how long it waits); the settings that do nothing; and what does not work yet. It is the finalization part of the skill spec, returned whole. 'monitoring' = THE MONITORING CONTRACT: which tools are safe to call in a poll loop (with cost / poll interval / whether output stays bounded as the run grows), which are not and what to use instead, plus the running ateam-mcp version. Read this BEFORE writing any loop that watches a build — the safe poll is ateam_chain_status, never ateam_get_chain.", "enum": [ + "capabilities", + "realizations", "overview", "skill", "solution", + "device-capabilities", + "host-contract", + "platform-connectors", + "platform-truth", + "sdk", + "workflows", + "finalization", "monitoring" ], "type": "string"
--- pinned/ateam_get_widget_catalog +++ observed/ateam_get_widget_catalog { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "Get the live catalog of widgets (UI plugins) available in this tenant's solution. Returns platform-bundled + solution-bundled + skill-declared widgets, each with a paste-ready how_to_use block (solution.json snippet + opener_call + persona_phrasing + binding_notes).\n\nUse this when wiring widgets into a skill or solution — the how_to_use block is designed to be copied verbatim into the solution.json ui_plugins[] entry and into the persona's opener phrasing, so you don't have to hand-roll either. The catalog reflects what is actually deployed in the tenant right now, not the abstract spec (for the spec itself, use ateam_get_spec topic='widgets').\n\nOrigins:\n • 'platform' = widgets bundled with the platform (always available).\n • 'solution' = widgets bundled with this tenant's solution.\n • 'skill' = widgets declared by a specific skill in the solution.\n\nAuth: forwards your authed api_key to Core (no master-secret involvement). Tenant scope is pinned by the key itself.", "inputSchema": {
--- pinned/ateam_get_workflows +++ observed/ateam_get_workflows { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "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.", "inputSchema": {
--- pinned/ateam_github_diff +++ observed/ateam_github_diff { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "PRE-FLIGHT BEFORE PROMOTE. Compares `dev` (head) vs `main` (base) by default — shows exactly which commits and files are about to ship if you call ateam_github_promote() next.\n\nUse this when you want to:\n • Review changes before promoting to prod\n • See if dev is ahead of main at all (returns ahead_by: 0 if nothing to promote)\n • **Diagnose a failed promote** — check `behind_by` and `status`. `status: 'diverged'` (behind_by > 0) means main holds commits dev never received, which is what makes ateam_github_promote return 409 Merge conflict. ALWAYS call this after a promote failure, before reporting anything to the user.\n • Inspect arbitrary branch/tag/commit comparisons (override base/head)\n\nNote: `files[]` lists what DIFFERS, not what conflicts. For solution.json and skills/*/skill.json the difference is often deploy-generated data (regenerated connector tools, timestamps) rather than authored change — see ateam_github_read's `_ateam_representation`.", "inputSchema": {
--- pinned/ateam_github_list_versions +++ observed/ateam_github_list_versions { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "List all available checkpoints (safe-* tags) for a solution. Shows tag name, date, counter, and commit SHA. Use before rollback to see available safe points.", + "description": "List the prod-YYYY-MM-DD-NNN tags each ateam_github_promote wrote for a solution — the points ateam_github_rollback can return main to. Shows tag name, date, counter, and commit SHA. Solutions older than 2026-05-19 may also carry retired safe-* tags; rollback accepts those too.", "inputSchema": { "properties": { "solution_id": {
--- pinned/ateam_github_log +++ observed/ateam_github_log { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "View commit history for a solution's GitHub repo. Shows recent commits with messages, SHAs, timestamps, and links. Default reads from `main` (prod). Pass `ref: 'dev'` to see in-progress work.", + "description": "View commit history for a solution's GitHub repo. Shows recent commits with messages, SHAs, timestamps, and links. Default reads `dev`, the working branch your writes land on (the Builder picks it when no ref is passed). Pass `ref: 'main'` to see what has been promoted to production. The response's `branch` says which one you got.", "inputSchema": { "properties": { "limit": { - "default": "main", - "description": "Branch to read commits from. Default: 'main'.", + "description": "Branch to read commits from. Omit it for `dev` (the working branch — the Builder resolves it). Use 'main' for production.", "type": "string" }, "solution_id": {
--- pinned/ateam_github_patch +++ observed/ateam_github_patch { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Edit a file in the solution's GitHub repo and commit. Two modes:\n1. FULL FILE: provide `content` — replaces entire file (good for new files or small files)\n2. SEARCH/REPLACE: provide `search` + `replace` — surgical edit without sending full file (preferred for large files like server.js)\nAlways use search/replace for large files (>5KB). Always read the file first with ateam_github_read to get the exact text to search for.\n\nDEFAULTS TO `dev` BRANCH — writes don't touch prod. Use ateam_github_promote to ship dev→main when ready. Pass ref:'main' only for emergency hotfixes.", + "description": "Edit a file in the solution's GitHub repo and commit — ONE file per call. Modes:\n1. FULL FILE: provide `content` — replaces entire file (good for new files or small files)\n2. SEARCH/REPLACE: provide `search` + `replace` — surgical edit without sending full file (preferred for large files like server.js)\n3. DELETE A STRAY: `delete: true` — removes a stray connector file, of either kind: one at the repo root, OUTSIDE connectors/<connector-id>/ (a root server.js, package.json or ui-dist/…), or a copy NESTED under a connector's own prefix (connectors/<connector-id>/connectors/<connector-id>/…). Core runs neither. Only such a file: anything else is refused (DELETE_ONLY_STRAY_CONNECTOR_FILES). It is removed from the working branch (`dev`) only, in one commit — production changes only through ateam_github_promote, like every other edit; git history keeps it. Move what it holds into connectors/<connector-id>/ first if it is still needed — the CONNECTOR_FILE_OUTSIDE_CONNECTOR or CONNECTOR_PATH_NESTED refusal says how the two copies differ and, when the stray is on the branch, names the delete call.\nConnector files belong under connectors/<connector-id>/; a write anywhere else is refused (CONNECTOR_FILE_OUTSIDE_CONNECTOR), and so is one to a nested copy, connectors/<connector-id>/connectors/<connector-id>/… (CONNECTOR_PATH_NESTED).\nAlways use search/replace for large files (>5KB). Always read the file first with ateam_github_read to get the exact text to search for.\n\nDEFAULTS TO `dev` BRANCH — writes don't touch prod. Use ateam_github_promote to ship dev→main when ready. Pass ref:'main' only for emergency hotfixes. After one, run ateam_github_sync_from_main so `dev` has it too. Until `dev` holds the same content, the Builder's copy of that file is `main` content `dev` does not have: ateam_redeploy and ateam_patch refuse to deploy the solution — any skill of it, not only that file's — and name the file (they never ship `dev`'s older copy over the hotfix, nor write the hotfix over `dev`), and ateam_build_and_run deploys it from `main`. A Builder save of that file meanwhile stays in the Builder (its reply says NOT_WRITTEN_TO_GITHUB), and ateam_build_and_run is refused (UNPUSHED_BUILDER_CHANGE) until it is placed — ateam_github_pull too, unless told discard_builder_changes:true. Connector code has no such check: ateam_upload_connector deploys `dev`'s code, so sync before the next upload of that connector. The reply's fs_mirror says what the Builder did with the file (a note when it did NOT copy it: its own copy had a change of its own).", "inputSchema": { "properties": { "branch": { + "delete": { + "description": "Mode 3: true removes `path` — ONLY a stray connector file: one at the repo root, outside connectors/<connector-id>/ (root server.js, package.json, package-lock.json, ui-dist/…, plugins/…, rn-bundle/…), or a copy nested under a connector's own prefix (connectors/<connector-id>/connectors/<connector-id>/…). Takes no content or search. Deletes on the working branch (`dev`) only; a ref naming another branch is refused (DELETE_WORKING_BRANCH_ONLY) — promote carries the removal to main.", + "type": "boolean" + }, "message": { - "description": "Optional commit message (default: 'Update <path>')", + "description": "Optional commit message (default: 'Update <path>'; search/replace mode: 'Edit <path> (N replacements)'; delete: 'Delete <path>')", "type": "string" }, "path": {
--- pinned/ateam_github_promote +++ observed/ateam_github_promote { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "SHIP DEV TO PROD. Merges the `dev` branch into `main` and auto-tags the new main HEAD as safe-YYYY-MM-DD-NNN. Use after testing your dev work, when you're ready to deploy changes to production.\n\nWorkflow: 1) ateam_github_patch (writes to dev) → 2) ateam_github_promote (merges dev→main) → 3) ateam_build_and_run (deploys main).\n\nPass dry_run:true to see what's about to ship without merging.\n\nON 409 MERGE CONFLICT: main holds commits dev never received. Call ateam_github_sync_from_main(solution_id) to merge main into dev, then promote again. Only if THAT also returns 409 did both sides edit the same lines — that one needs a human (open a PR on GitHub).", + "description": "SHIP DEV TO PROD. Merges the `dev` branch into `main` and auto-tags the new main HEAD as prod-YYYY-MM-DD-NNN. Use after testing your dev work, when you're ready to deploy changes to production.\n\nWorkflow: 1) ateam_github_patch (writes to dev) → 2) ateam_github_promote (merges dev→main) → 3) ateam_build_and_run (deploys main).\n\nPass dry_run:true to see what's about to ship without merging.\n\nON 409 MERGE CONFLICT: main holds commits dev never received. Call ateam_github_sync_from_main(solution_id) to merge main into dev, then promote again. Only if THAT also returns 409 did both sides edit the same lines — that one needs a human (open a PR on GitHub).", "inputSchema": { "properties": { "dry_run": {
--- pinned/ateam_github_pull +++ observed/ateam_github_pull { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Deploy a solution FROM its GitHub repo. Reads .ateam/export.json + connector source from the repo and feeds it into the deploy pipeline. Use this to restore a previous version or deploy from GitHub as the source of truth.", + "description": "Deploy a solution FROM its GitHub repo. Reads .ateam/export.json + connector source from the repo and feeds it into the deploy pipeline. Use this to restore a previous version or deploy from GitHub as the source of truth. It REPLACES the Builder's copy of each file with the repo's (`dev`). Where the Builder holds a change that never reached GitHub (a save held off `dev`, one whose push failed, a file changed on both sides), that change would exist nowhere afterwards, so the pull is REFUSED (409 UNPUSHED_BUILDER_CHANGE, naming the files) before anything is uploaded or deployed — unless you pass discard_builder_changes:true, the explicit way to take `dev`'s copy of a file changed on both sides, or to drop such a change. To keep the change instead, ateam_redeploy(solution_id) writes it to `dev` first.", "inputSchema": { "properties": { + "discard_builder_changes": { + "description": "true = drop any Builder change that never reached GitHub, replacing it with the repo's copy. Only when you mean to lose it: without it, a pull that would drop one is refused and names the files.", + "type": "boolean" + }, "solution_id": { "description": "The solution ID to pull and deploy from GitHub", "type": "string"
--- pinned/ateam_github_push +++ observed/ateam_github_push { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Push the current deployed solution to GitHub. Auto-creates the repo on first use. Commits the full bundle (solution + skills + connector source) atomically. Use after ateam_build_and_run to version your solution, or anytime you want to snapshot the current state.", + "description": "Push the solution to its GitHub repo. When it runs, what it writes, and what it keeps as the branch holds it: ateam_get_spec({ topic: \"overview\", search: \"github/push\" }).", "inputSchema": { "properties": { "message": {
--- pinned/ateam_github_read +++ observed/ateam_github_read { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "Read any file from a solution's GitHub repo. Returns the file content. Use this to read connector source code, skill definitions, or any versioned file. Default reads from `main` (deployed/prod state). Pass `ref: 'dev'` to read in-progress work.\n\n⚠️ NOT RUNTIME STATE. For `solution.json` and `skills/<id>/skill.json` this returns a git MIRROR, not what is deployed. Connector-imported tools are regenerated at deploy time, so a repo copy's `tools[]` can differ from production (on one solution `dev` showed 29 tools while production ran 66). Reads of those paths carry an `_ateam_representation` field saying what you are holding. To answer \"what can this skill actually do?\", call ateam_get_solution(solution_id, skill_id, section:'tools') — never this tool.", + "description": "Read any file from a solution's GitHub repo. Returns the file content. Use this to read connector source code, skill definitions, or any versioned file. Default reads `dev`, the working branch your writes land on (the Builder picks it when no ref is passed, and creates it if the repo has none yet). Pass `ref: 'main'` to read the promoted/production state. The response's `branch` says which one you got.\n\n⚠️ NOT RUNTIME STATE. For `solution.json` and `skills/<id>/skill.json` this returns a git MIRROR, not what is deployed. Connector-imported tools are regenerated at deploy time, so a repo copy's `tools[]` can differ from production (on one solution `dev` showed 29 tools while production ran 66). Reads of those paths carry an `_ateam_representation` field saying what you are holding. To answer \"what can this skill actually do?\", call ateam_get_solution(solution_id, skill_id, section:'tools') — never this tool.", "inputSchema": { "properties": { "branch": { - "default": "main", - "description": "Branch, tag, or commit SHA to read from. Default: 'main' (prod). Use 'dev' to read in-progress work.", + "description": "Branch, tag, or commit SHA to read from. Omit it to read `dev` (the working branch — the Builder resolves it). Use 'main' for the promoted/production state.", "type": "string" }, "solution_id": {
--- pinned/ateam_github_reconcile +++ observed/ateam_github_reconcile { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, "description": "JOIN a diverged dev and main. Use when ateam_github_promote returns PROMOTE_NEEDS_HUMAN or PROMOTE_PRECONDITION_FAILED — i.e. the automatic main→dev back-merge could not resolve itself.\n\nTRY sync_from_main FIRST; this tool calls it internally (a plain merge keeps both sides with no judgement call) and only escalates when git genuinely conflicts.\n\nOn conflict it writes a TWO-PARENT merge commit so the histories actually join. That matters: copying one branch's tree onto the other makes the contents match while leaving NO merge base, so the very next promote conflicts again — equal content is not a reconciled history.\n\nConflicting files are resolved per file, NEWEST WINS, and every decision is reported. Recency is a heuristic, not intent: read the decisions. On a real tenant main held the newer solution.json while dev held the newer widget, so a blanket choice would have reverted one of them.\n\nWHO NEEDS THIS: any tenant whose deploys predate the dev-routing fix carries main-only commits the platform itself wrote, and hits this on its first promote afterwards. Pass dry_run:true to see the decisions before writing anything.", "inputSchema": {
--- pinned/ateam_github_rollback +++ observed/ateam_github_rollback { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Roll prod (`main` branch) back to a previous state.\n\nADDITIVE — does NOT destroy history. Creates a new commit on top of main whose tree matches the target's tree. The history of everything between target and current main is preserved (you can roll back the rollback).\n\nWorkflow: 1) ateam_github_list_versions (find a safe-* tag) → 2) ateam_github_rollback(target: 'safe-...') → 3) ateam_build_and_run (deploys the reverted state).", + "description": "Roll prod (`main` branch) back to a previous state.\n\nADDITIVE — does NOT destroy history. Creates a new commit on top of main whose tree matches the target's tree. The history of everything between target and current main is preserved (you can roll back the rollback).\n\nWorkflow: 1) ateam_github_list_versions (find a prod-YYYY-MM-DD-NNN tag) → 2) ateam_github_rollback(target: '<that tag>') → 3) ateam_build_and_run(solution_id) (deploys the reverted state) → 4) ateam_github_sync_from_main(solution_id), so `dev` carries it too: the iterate tools deploy from `dev`. Solutions older than 2026-05-19 may also carry retired safe-* tags; rollback accepts those too.", "inputSchema": { "properties": { "solution_id": { - "description": "Tag (e.g., 'safe-2026-05-19-001') or commit SHA to revert main to. Use ateam_github_list_versions to find safe-* tags.", + "description": "A prod-YYYY-MM-DD-NNN tag or a commit SHA to revert main to. Use ateam_github_list_versions to find the tags. Solutions older than 2026-05-19 may also carry retired safe-* tags; rollback accepts those too.", "type": "string" } },
--- pinned/ateam_github_status +++ observed/ateam_github_status { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "Check if a solution has a GitHub repo, its URL, and the latest commit. Use this to verify GitHub integration is working for a solution.", "inputSchema": {
--- pinned/ateam_github_sync_from_main +++ observed/ateam_github_sync_from_main { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, "description": "BRING `dev` UP TO DATE WITH `main` — merges main into dev. The mirror of ateam_github_promote.\n\nUSE THIS WHEN PROMOTE RETURNS 409. promote only ships dev→main, so the moment anything lands on main directly — a hotfix, a manual edit, an ateam_github_rollback, or a write that mis-targeted the branch — dev falls behind and can never be promoted again. Without this tool that divergence is unfixable from A-Team: the only exits are the GitHub web UI or a raw API call.\n\nWorkflow on a 409: 1) ateam_github_diff (confirm status:'diverged') → 2) ateam_github_sync_from_main → 3) ateam_github_promote.\n\nPass dry_run:true FIRST to see exactly which commits and files would come into dev without changing anything.\n\nThis is a real merge, not a force: if main and dev edited the SAME lines it returns 409 too, and that one genuinely needs a human (open a PR).", "inputSchema": {
--- pinned/ateam_github_write +++ observed/ateam_github_write { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Write a file to the solution's GitHub repo. Use this to create new connector files or replace existing ones — one file per call. This is the PRIMARY way to write connector code after first deploy. Write each file individually (server.js, package.json, UI assets), then call ateam_github_promote() to ship to prod (dev→main), then ateam_build_and_run() to deploy.\n\nDEFAULTS TO `dev` BRANCH.", + "description": "Write a file to the solution's GitHub repo. Use this to create new connector files or replace existing ones — one file per call. This is the PRIMARY way to write connector code after first deploy. Write each file individually under connectors/<connector-id>/ (server.js, package.json, ui-dist/… assets), then call ateam_github_promote() to ship to prod (dev→main), then ateam_build_and_run() to deploy. Core deploys connectors/<connector-id>/ only, so a connector file written anywhere else (a root server.js, package.json or ui-dist/) is refused with CONNECTOR_FILE_OUTSIDE_CONNECTOR.\n\nDEFAULTS TO `dev` BRANCH.", "inputSchema": { "properties": { "content": { - "description": "Optional commit message (default: 'Write <path>')", + "description": "Optional commit message (default: 'Update <path>')", "type": "string" }, "path": {
--- pinned/ateam_list_solutions +++ observed/ateam_list_solutions { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "List all solutions deployed in the Skill Builder.", "inputSchema": {
--- pinned/ateam_log_lesson +++ observed/ateam_log_lesson { + "annotations": { + "destructiveHint": false, + "readOnlyHint": false + }, "core": true, - "description": "Record ONE lesson this run learned, so the NEXT run does not relearn it. A building agent starts every run empty — it does not know which tool misled the last run or the workaround that got past it. Log a lesson the moment a tool misleads you AND you find a way through.\n\nAPPEND-ONLY. You cannot edit or delete earlier lessons, and you do not supply the timestamp — the server stamps it, so it cannot be forged.\n\nPROVENANCE CAVEAT, stated because the earlier wording over-promised: `job_id` and `actor` are recorded ONLY when the caller supplies x-adas-job-id / x-adas-actor-id. An agent calling this tool does not, so those fields are usually null — a lesson cannot currently be traced back to the run that produced it, and the file cannot tell 'three runs hit this' from 'one run hit it three times'. Do not put a job id in `error` to compensate; keep that field verbatim.\n\nLOG ONLY WHAT YOU OBSERVED. Quote the error VERBATIM; never paraphrase it and never write a theory about platform internals. A wrong lesson is worse than no lesson, because the next run cannot check it and will act on it.\n\nUse kind='misleading_success' when a call REPORTED success while the thing you wanted did not happen — that class is the most expensive to rediscover and it is invisible to a failures-only log.", + "description": "Record ONE lesson this run learned, so the NEXT run — which starts empty — does not relearn it. Log it the moment a tool misleads you AND you find a way through.\n\nYou cannot edit or delete earlier lessons; supersede them: `supersedes` takes the `id` of the lesson to replace, from ateam_get_lessons, whose `correction_rule` says when and how. An unknown id is refused (404); a lesson already superseded is refused (409) naming the current one. The server stamps the id and the time.\n\nPROVENANCE: `job_id` and `actor` are usually null (an agent calling this tool sends no x-adas-job-id / x-adas-actor-id): a lesson cannot be traced to its run, nor 'three runs hit this' told from 'one run, three times'. Do not put a job id in `error`; keep it verbatim.\n\nLOG ONLY WHAT YOU OBSERVED, a correction too. Quote the error VERBATIM; never a paraphrase, never a theory about platform internals. A wrong lesson is worse than no lesson: the next run will act on it.\n\nkind='misleading_success': a call REPORTED success while the effect you wanted did not happen — the class a failures-only log cannot hold.", "inputSchema": { "properties": { "error": { + "supersedes": { + "description": "The `id` of the lesson this one supersedes, from ateam_get_lessons — see `correction_rule` in its answer", + "type": "string" + }, "tool": { "description": "The tool that misled you, e.g. \"ateam_build_and_run\"", "type": "string"
--- pinned/ateam_log_progress +++ observed/ateam_log_progress { + "annotations": { + "destructiveHint": false, + "readOnlyHint": false + }, "core": true, "description": "Record that a build STEP is done, so a later run does not redo it. Write it AS IT HAPPENS, never at the end — the runs that most need a journal are the ones the clock kills.\n\nWHY: a continuation cannot otherwise tell what the previous run achieved, so it re-runs the whole orientation (bootstrap, get_workflows, list_solutions, get_solution, get_spec, get_examples, github_read) to re-derive from documents what was already established — and re-inflates its prompt into the region where provider latency collapses. Measured across 13 runs: stalls track PROMPT SIZE (~60k), not turn count. ateam_get_progress is ONE call instead of nine.\n\nstatus — three values, and the third is the point:\n built — the artefact exists (files written, committed)\n deployed — the platform accepted it\n verified — YOU CALLED IT AND GOT REAL DATA BACK\n\n`verified` REQUIRES verified_by, and a deploy response is not verification. `connected` and `tools > 0` are tools/list facts: a clinic connector showed 9 tools while every storage call returned 401. A journal that stops at `deployed` records that build as finished.\n\nRe-log the same step as it advances (built → deployed → verified) — latest wins, no update path. If a step REGRESSES, re-log it at the lower status: silence must not read as \"still fine\".", "inputSchema": {
--- pinned/ateam_patch +++ observed/ateam_patch { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Surgically update ANY field in a skill or solution definition, redeploy, and optionally re-test — all in one step.\n\n⚠️ MERGE-BY-DEFAULT (v0.4.0) — Arrays are protected from silent replace. Bare array writes on solution.linked_skills / ui_plugins / platform_connectors / handoffs / grants / triggers (etc.) and skill.tools / connectors / handoffs / scenarios are REFUSED to prevent sibling loss. Add or remove items with the _push / _delete / _update suffixes; opt into a full-array replace only when you really mean it.\n\nOPERATIONS (safe by construction):\n1. Scalar (dot notation): { \"problem.statement\": \"new value\", \"role.persona\": \"You are...\" }\n2. Deep nested: { \"intents.thresholds.accept\": 0.9, \"policy.escalation.enabled\": true }\n3. Array APPEND: { \"tools_push\": [{ name: \"new_tool\", description: \"...\" }] }\n4. Array REMOVE: { \"tools_delete\": [\"tool_name\"] }\n5. Array MODIFY-ONE: { \"tools_update\": [{ name: \"existing_tool\", description: \"updated\" }] }\n6. Full-array REPLACE (opt-in): { \"linked_skills\": [...], \"linked_skills_replace\": true } — or { _replace: true, ... } to opt every array in this call.\n\nSOLUTION-LEVEL EXAMPLES (target='solution'):\n- ADD a skill to the solution: updates: { \"linked_skills_push\": [\"my-new-skill\"] } ← NOT { linked_skills: [\"my-new-skill\"] } (that would REFUSE — it drops your other skills)\n- REMOVE a skill: updates: { \"linked_skills_delete\": [\"old-skill\"] }\n- ADD a UI plugin: updates: { \"ui_plugins_push\": [{ id: \"mcp:conn:panel\", ... }] }\n- ADD a handoff: updates: { \"handoffs_push\": [{ id: \"h1\", ... }] }\n\nSKILL-LEVEL EXAMPLES (target='skill' + skill_id):\n- Change persona: updates: { \"role.persona\": \"You are a friendly assistant\" }\n- Append to persona: updates: { \"persona_append\": \"\\n\\nALWAYS respond in 2 sentences.\" }\n- Add a guardrail: updates: { \"policy.guardrails.never_push\": [\"Never share passwords\"] }\n- Add a tool: updates: { \"tools_push\": [{ name: \"conn.tool\", description: \"...\", inputs: [...], output: {...} }] }\n- Change intent: updates: { \"intents.supported_update\": [{ id: \"i1\", description: \"new desc\" }] }\n- CREATE a new skill: target='skill', skill_id='my-new-skill', updates: { \"problem.statement\": \"...\", \"role.persona\": \"...\" } — auto-scaffolded and added to solution topology.\n\nPREVIEW BEFORE WRITING: pass dry_run:true to see the diff (arrays_merged, arrays_replaced, dropped_ids, added_ids) without applying. Use this before any destructive-looking edit.\n\nVERDICT (skill target): the response carries a NON-BLOCKING `validation` block { skill_id, valid, ready_to_export, error_count, incomplete_sections[], unresolved_refs } — the patch always saves even if the def is now invalid, so CHECK valid: false and fix incomplete_sections before relying on it (build_and_run will refuse to deploy an invalid skill). error_count can include auto-import connector-tool artifacts, so act on incomplete_sections first.", + "description": "Surgically update ANY field in a skill or solution definition, redeploy, and optionally re-test — all in one step.\n\n⚠️ MERGE-BY-DEFAULT (v0.4.0) — Arrays are protected from silent replace. Bare array writes on solution.linked_skills / ui_plugins / platform_connectors / handoffs / grants / triggers (etc.) and skill.tools / connectors / handoffs / scenarios are REFUSED to prevent sibling loss. Add or remove items with the _push / _delete / _update suffixes; opt into a full-array replace only when you really mean it.\n\nOPERATIONS (safe by construction):\n1. Scalar (dot notation): { \"problem.statement\": \"new value\", \"role.persona\": \"You are...\" }\n2. Deep nested: { \"intents.thresholds.accept\": 0.9, \"policy.escalation.enabled\": true }\n3. Array APPEND: { \"tools_push\": [{ name: \"new_tool\", description: \"...\" }] }\n4. Array REMOVE: { \"tools_delete\": [\"tool_name\"] }\n5. Array MODIFY-ONE: { \"tools_update\": [{ name: \"existing_tool\", description: \"updated\" }] }\n6. Full-array REPLACE (opt-in): { \"linked_skills\": [...], \"linked_skills_replace\": true } — or { _replace: true, ... } to opt every array in this call.\n\nSOLUTION-LEVEL EXAMPLES (target='solution'):\n- ADD a skill to the solution: updates: { \"linked_skills_push\": [\"my-new-skill\"] } ← NOT { linked_skills: [\"my-new-skill\"] } (that would REFUSE — it drops your other skills)\n- REMOVE a skill: updates: { \"linked_skills_delete\": [\"old-skill\"] }\n- ADD a UI plugin: updates: { \"ui_plugins_push\": [{ id: \"mcp:conn:panel\", ... }] }\n- ADD a handoff: updates: { \"handoffs_push\": [{ id: \"h1\", ... }] }\n\nSKILL-LEVEL EXAMPLES (target='skill' + skill_id):\n- Change persona: updates: { \"role.persona\": \"You are a friendly assistant\" }\n- Append to persona: updates: { \"persona_append\": \"\\n\\nALWAYS respond in 2 sentences.\" }\n- Add a guardrail: updates: { \"policy.guardrails.never_push\": [\"Never share passwords\"] }\n- Add a tool: updates: { \"tools_push\": [{ name: \"conn.tool\", description: \"...\", inputs: [...], output: {...} }] }\n- Change intent: updates: { \"intents.supported_update\": [{ id: \"i1\", description: \"new desc\" }] }\n- CREATE a new skill: target='skill', skill_id='my-new-skill', updates: { \"problem.statement\": \"...\", \"role.persona\": \"...\" } — auto-scaffolded and added to solution topology.\n\nPREVIEW BEFORE WRITING: pass dry_run:true to see the diff (arrays_merged, arrays_replaced, dropped_ids, added_ids) without applying. Use this before any destructive-looking edit.\n\nVERDICT (skill target): the response carries a NON-BLOCKING `validation` block { skill_id, valid, ready_to_export, error_count, incomplete_sections[], unresolved_refs } — the patch always saves (and redeploys) even if the def is now invalid, and build_and_run does not run this check (its gate is the solution validator, POST /validate/solution), so nothing refuses a skill for it: CHECK valid: false yourself and fix incomplete_sections before relying on the skill. error_count can include auto-import connector-tool artifacts, so act on incomplete_sections first.", "inputSchema": { "properties": { "dry_run": {
+ tool added: ateam_recover_connector_source
--- pinned/ateam_redeploy +++ observed/ateam_redeploy { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, "description": "Re-deploy skills WITHOUT changing any definitions. ⚠️ HEAVY OPERATION: regenerates MCP servers (Python code) for every skill, pushes each to A-Team Core, restarts connectors, and verifies tool discovery. Takes 30-120s depending on skill count. Use after connector restarts, Core hiccups, or stale state. For incremental changes, prefer ateam_patch (which updates + redeploys in one step).", "inputSchema": {
--- pinned/ateam_show_skill_minimal +++ observed/ateam_show_skill_minimal { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "Show the minimal authoring view of a skill — persona + connectors + handoff_when + style + policy guardrails only. ~10× smaller than ateam_get_solution(view:'skills') for the same skill. Use this when you only need the irreducible author content (Phase 9 of the strip).", "inputSchema": {
--- pinned/ateam_show_solution_minimal +++ observed/ateam_show_solution_minimal { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "Show the minimal authoring view of a solution — name + description + style + routing_mode + identity_mode + skill ids + connector ids only. Skips deployed metadata, handoffs (auto-generated), grants, ui_plugins, validation results. Use this for fast inspection without the verbose fields (Phase 9 of the strip).", "inputSchema": {
--- pinned/ateam_spec_search +++ observed/ateam_spec_search { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "Semantic search over the FULL ateam platform /spec documentation — the deep fallback behind ateam_design_advisor. Ask a natural-language 'how do I…' question and get the most relevant doc chunks (with their topic + heading), then read the full topic via ateam_get_spec(topic). Use this when the advisor's pointer isn't enough, or for details/examples on anything — including topics outside the curated capability list. Read-only.", + "description": "Semantic search over the FULL ateam platform /spec documentation — the deep fallback behind ateam_design_advisor. Ask a natural-language 'how do I…' question and get the most relevant doc chunks (with their topic + heading), then read the full topic via ateam_get_spec(topic); an example a hit names or points to (examples/<type>, /spec/examples/<type>) is read via ateam_get_examples(type), which ateam_get_spec does not serve. Use this when the advisor's pointer isn't enough, or for details/examples on anything — including topics outside the curated capability list. Read-only. Needs NO sign-in, tenant or LLM, so it answers when the advisor refuses a session that has not signed in. The result carries `served_by` (prod or dev, or the base itself for any other host): the environment whose docs were searched.", "inputSchema": { "properties": { "query": {
--- pinned/ateam_status_all +++ observed/ateam_status_all { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "Show GitHub sync status for ALL tenants and solutions in one call. Requires master key authentication. Returns a summary table of every tenant's solutions with their GitHub sync state.", "inputSchema": {
--- pinned/ateam_sync_all +++ observed/ateam_sync_all { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Sync ALL tenants: push Builder FS → GitHub, then pull GitHub → Core MongoDB. Requires master key authentication. Returns a summary table with results for each tenant/solution.", + "description": "Sync ALL tenants: ateam_github_push for each solution (what it writes, and what it keeps: ateam_get_spec({ topic: \"overview\", search: \"github/push\" })), then pull GitHub → Core MongoDB. Requires master key authentication. Returns a summary table with results for each tenant/solution.", "inputSchema": { "properties": { "pull_only": {
--- pinned/ateam_test_abort +++ observed/ateam_test_abort { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Abort a running test. Pass chain_id to abort the WHOLE run — every job in the chain — and get back which ones stopped. Aborting by job_id stops that job only, leaving handoffs running. Stops at the next iteration boundary. (Advanced.)", + "description": "Abort a running test. Pass chain_id to abort the WHOLE run — every job in the chain — and get back which ones stopped. Aborting by job_id stops that job only, leaving handoffs running. Stops at the next iteration boundary. The abort uses the actor the test's start ran as, whatever actor_id says: Core stops a job that actor may access and refuses any other with 403 JOB_ACCESS_DENIED, so an anonymous abort stops only anonymous runs. Who a test runs as: ateam_get_spec({ topic: \"skill\", search: \"actor_id\" }) → testing_and_runtime.conversation_testing.key_concepts.actor_id. (Advanced.)", "inputSchema": { "properties": { "actor_id": { - "description": "Optional. WHO is asking. A job belongs to an actor and Core enforces that on per-job reads, so a tenant key alone is refused. Usually unnecessary — the session remembers the actor from ateam_conversation/ateam_test_skill. Pass it to inspect a job run by a DIFFERENT actor (e.g. a real user's).", + "description": "Optional, and NOT who aborts (see the description). What it does is what actor_id does on any call: ateam-mcp keeps it (a test_ thread id excepted) as this session's actor and sends it on later calls, and the chain_id form reads the chain's jobs as that actor before aborting them. The session already holds it after ateam_conversation / ateam_test_skill; in a new session, pass the ran_as of the reply that started the run.", "type": "string" }, "chain_id": {
--- pinned/ateam_test_connector +++ observed/ateam_test_connector { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Call a tool on a running connector and get the result. Use this to test individual connector tools (e.g., triggers.list, entities.list, google.command) without deploying to a client. The connector must be connected and running.", + "description": "Call ONE tool on a running connector DIRECTLY and get its raw result — no skill, no guardrails, no user turn. It proves a tool's plumbing (arguments in, result out). It can NOT prove a step that waits for a person — a confirmation, an approval, a value only the user knows: test those with ateam_conversation (ateam_get_spec('skill') → agent_guide.key_concepts.testing_and_runtime.human_step_testing). Never call a write tool with ateam_test_connector on a record your test did not create. Who a test runs as: ateam_get_spec({ topic: \"skill\", search: \"actor_id\" }) → testing_and_runtime.conversation_testing.key_concepts.actor_id. A master_key session has no key person: this tool runs as the actor_id the session last passed to another tool, or as the platform's service identity when it passed none (a master session's other tests run anonymously). If a per-user tool answers NO_INDIVIDUAL_USER here, the call had no person behind it: that is about this test, not a connector bug, and never a reason to change where the connector stores data (ateam_get_spec('connector-multi-user') → storage_decision).", "inputSchema": { "properties": { "args": {
--- pinned/ateam_test_notification +++ observed/ateam_test_notification { + "annotations": { + "destructiveHint": false, + "readOnlyHint": false + }, "core": true, - "description": "Fire a REAL notification at an existing actor in a deployed solution — for end-to-end testing of the system-initiated notification path (telegram/push/app channels).\n\nUnlike ateam_test_skill (synthetic test actor with no channels) and ateam_conversation (user-initiated thread), this calls the /api/internal/notify-user path that PCM and other sibling services use — so the actor's real enabled channels actually receive the message.\n\nUse for:\n • Channel fan-out smoke (does telegram/push/app actually receive it?)\n • Delivery-result verification (per-channel ok/failed in the response).\n\nAuth: forwards your authed api_key to Core (no master-secret involvement). Tenant is pinned by the key itself — cross-tenant targeting is structurally impossible.\n\n⚠️ SAFETY:\n • The text is prefixed with [TEST] in the actual notification — visible to the user, anti-phishing.\n • Rate-limited: 10 calls/min per session.\n • Every call is audited (caller, tenant, actor, content hash) regardless of outcome.\n • actor_id is scoped to your tenant — cross-tenant targeting is rejected by Core's per-tenant Mongo isolation.\n • reply_handler is NOT supported via api-key auth (Core ignores it). Routing the user's next reply to an arbitrary skill is a privilege-escalation surface. For routing/engagement tests, use ateam_test_skill.", + "description": "Fire a REAL notification at an existing actor in a deployed solution — for end-to-end testing of the system-initiated notification path (telegram/push/app channels).\n\nUnlike ateam_test_skill and ateam_conversation (a test turn answered in the tool result; who it runs as: their actor_id), this calls the /api/internal/notify-user path that PCM and other sibling services use — so the actor's real enabled channels actually receive the message.\n\nUse for:\n • Channel fan-out smoke (does telegram/push/app actually receive it?)\n • Delivery-result verification (per-channel ok/failed in the response).\n\nAuth: forwards your authed api_key to Core (no master-secret involvement). Tenant is pinned by the key itself — cross-tenant targeting is structurally impossible.\n\n⚠️ SAFETY:\n • The text is prefixed with [TEST] in the actual notification — visible to the user, anti-phishing.\n • Rate-limited: 10 calls/min per session.\n • Every call is audited (caller, tenant, actor, content hash) regardless of outcome.\n • actor_id is scoped to your tenant — cross-tenant targeting is rejected by Core's per-tenant Mongo isolation.\n • reply_handler is NOT supported via api-key auth (Core ignores it). Routing the user's next reply to an arbitrary skill is a privilege-escalation surface. For routing/engagement tests, use ateam_test_skill.", "inputSchema": { "properties": { "actor_id": {
--- pinned/ateam_test_pipeline +++ observed/ateam_test_pipeline { + "annotations": { + "destructiveHint": false, + "readOnlyHint": false + }, "core": true, "description": "Test the decision pipeline (intent detection → planning) for a skill WITHOUT executing tools. Returns intent classification, first planned action, and timing. Use this to debug why a skill classifies intent incorrectly or plans the wrong action.", "inputSchema": {
--- pinned/ateam_test_skill +++ observed/ateam_test_skill { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Send a test message to a deployed skill and get the execution result.\n\nWait modes (wait_for):\n • 'root' (default, back-compat) — wait until the message's root job completes, return single-job result. Fast, ignores any sub-skills the root delegated to via askAnySkill.\n • 'chain' — wait until EVERY job in the chain (root + handoffs + askAnySkill subcalls, recursively) reaches a terminal state, then return the full chain tree. Use when testing multi-skill flows (orchestrator → workers, builders → sub-builders, etc.). The response.chain field carries chainJobs[] with parentJobId/relation/depth and executionSteps[] with tool-nesting (opId/parentOpId/_toolDepth).\n\nLegacy: wait:false is equivalent to wait_for:'never' — returns job_id immediately for polling via ateam_test_status. wait:true is the same as the default wait_for:'root'.", + "description": "Send a test message to a deployed skill and get the execution result.\n\nWait modes (wait_for):\n • 'root' (default, back-compat) — wait until the message's root job completes, return single-job result. Fast, ignores any sub-skills the root delegated to via askAnySkill.\n • 'chain' — wait until EVERY job in the chain (root + handoffs + askAnySkill subcalls, recursively) reaches a terminal state, then return the full chain tree. Use when testing multi-skill flows (orchestrator → workers, builders → sub-builders, etc.). The response.chain field carries chainJobs[] with parentJobId/relation/depth and executionSteps[] with tool-nesting (opId/parentOpId/_toolDepth).\n\nLegacy: wait:false is equivalent to wait_for:'never' — returns job_id immediately for polling via ateam_test_status. wait:true is the same as the default wait_for:'root'.\n\nAttachments: pass `attachments` to send files with the message exactly as a file dropped into the chat (see the parameter).\n\nThe reply carries ran_as (with wait_for:'chain', inside response.kickoff) beside actor_id. Who a test runs as: ateam_get_spec({ topic: \"skill\", search: \"actor_id\" }) → testing_and_runtime.conversation_testing.key_concepts.actor_id.", "inputSchema": { "properties": { "actor_id": { - "description": "Optional actor ID for conversation continuity. Pass the actor_id from a previous test response to continue the conversation. Omit to auto-generate a test actor (test_<timestamp>_<random>, auto-expires in 24h).", + "description": "Optional: the conversation thread. Pass the actor_id from a previous test response to continue that thread. Who a test runs as: ateam_get_spec({ topic: \"skill\", search: \"actor_id\" }) → testing_and_runtime.conversation_testing.key_concepts.actor_id.", "type": "string" }, + "attachments": { + "description": "Optional files sent with the message, exactly as a file dropped into the chat: Core stores and analyses them the same way (image/* → vision, application/pdf → document, text/*, application/json, application/xml → inline text). Each item is { data: base64 (no data: prefix), mimeType, name? } OR { url, name?, mimeType? }: exactly one of data or url. Up to 10 files and 7 MB in total (decoded) per message: Core accepts a request of at most 10 MB, and base64 adds a third. Any other type, or more, is refused before anything is sent. A url is fetched by this MCP process only when it runs locally over stdio (its type comes from the response unless you give mimeType, its name from the URL path). The hosted server does not fetch URLs: send the file as base64 data.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "description": "The file's bytes, base64-encoded, without a data: prefix.", + "type": "string" + }, + "mimeType": { + "description": "The file's MIME type. Required with data. With url, overrides the type the server answered with.", + "type": "string" + }, + "name": { + "description": "File name shown to the skill (e.g. photo.jpg, notes.pdf, recipe.png). With url, defaults to the URL's last path segment.", + "type": "string" + }, + "url": { + "description": "http(s) URL of the file. Local stdio only; the hosted server refuses it.", + "type": "string" + } + }, + "type": "object" + }, + "maxItems": 10, + "type": "array" + }, "chain_timeout_ms": { "description": "Optional. Max total ms to wait when wait_for:'chain'. Default 300000 (5 min). Long-running chains (skill-factory, large bundle builds) may need higher. Clamped to [10000, 900000].", "type": "number"
--- pinned/ateam_test_status +++ observed/ateam_test_status { + "annotations": { + "readOnlyHint": true + }, "core": true, "description": "Poll the progress of an async test. Pass chain_id for the WHOLE run (recommended — the root job finishing does NOT mean the run finished; a handoff may still be going). Pass job_id to poll one job alone: iteration count, tool call steps, status, and result when done.\n\nSet include_chain:true to ALSO include the full chain tree (every job in the chain, rooted at this job_id, with parent/child linkage). Use when this job dispatched askAnySkill subcalls and you want a single snapshot of the whole multi-skill state instead of polling each child job_id separately.", "inputSchema": {
--- pinned/ateam_test_voice +++ observed/ateam_test_voice { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Simulate a voice conversation with a deployed solution. Runs the full voice pipeline (session → caller verification → prompt → skill dispatch → response) using text instead of audio. Returns each turn with bot response, verification status, tool calls, and entities. Use this to test voice-enabled solutions end-to-end without making a phone call.", + "description": "Simulate a voice call with text, not audio. A person's key reaches the skill (skill job included); with no person on the key, or a phone caller, Core refuses the skill call today (C6): test it with ateam_conversation. Each turn returns response, verification, entities; ran_as repeats its actor_id.\n\nWho a test runs as: ateam_get_spec({ topic: \"skill\", search: \"actor_id\" }) → testing_and_runtime.conversation_testing.key_concepts.actor_id.", "inputSchema": { "properties": { "messages": {
--- pinned/ateam_upload_connector +++ observed/ateam_upload_connector { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "Upload connector code to Core and restart — WITHOUT redeploying skills.\n\nMERGES with the GitHub state at `ref` by default (default ref: 'dev'). Sending a partial file set ONLY overlays those files — the rest of the connector is preserved from GitHub. To fully replace the connector dir (historical behavior), pass replace:true.\n\nModes:\n • github:true (no files) — deploy the GitHub state at `ref` as-is.\n • github:true + files:[] — GitHub state at `ref` as BASE, your files overlay on top (incoming wins).\n • files:[] (no github) — default MERGE with GitHub state at `ref`. Refuses if no GitHub base exists (no silent nuke).\n • files:[] + replace:true — full replace. Wipes connector dir + writes only the provided files. Use deliberately.\n\nMulti-file connectors (server.js + dashboard HTML + RN bundle + package/manifest): pass each file with content_base64 (a single-line, escape-safe base64 string) instead of content — so you don't hand-escape ~90KB of HTML/JS/JSON inside one tool call. This is the CANONICAL agent path for a full connector; do NOT hand-roll `curl` against the raw endpoint (that skips connector registration / PAT provisioning).\n\nCommon traps this design prevents:\n • Pre-fix bug (2026-06-06): sending just ui-dist HTML wiped server.js + node_modules — connector broke until a full re-upload. Now: those files merge with the GitHub base.\n • Pre-fix bug: github:true silently read from `main` even when patches were on `dev`. Now: defaults to dev; pass ref:'main' to opt into the legacy path.", + "description": "Upload connector code to Core and restart it. Then the skills that import this connector's tools are redeployed when they need it — a redeploy of the WHOLE skill as the Builder holds it, so its other connectors' tools and any saved edit not yet deployed go live too. The reply's skill_tools says, per skill and per connector, what changed in what Core runs; stages.skills is its verdict. A tools[] you wrote yourself is never changed. The rule: ateam_get_spec('skill') → agent_guide.key_concepts.how_a_skill_gets_its_tools.when_the_connector_changes.\n\nModes:\n • github:true — deploy connectors/<id>/ from the repo at `ref` (default 'dev'); files:[] overlays yours on it.\n • files:[] — MERGE (default): your files over the repo at `ref`, over the files Core ALREADY runs. A file you leave out is kept, except a binary or a file over 512 KB that Core runs: it is dropped, and the reply's dropped[] and dropped_warning name it.\n • files:[] + replace:true — the connector becomes EXACTLY these files. Every other file is DELETED from Core, from the Builder's source and, when GitHub is connected, from the repo's working branch (dev), deployed or not — files written with ateam_github_patch included. Some files are kept — repo.kept names each with its reason.\n\nNothing is written with no base (409 CONNECTOR_BASE_MISSING: a new connector is ateam_create_connector's) or a failed read (retryable 502 CONNECTOR_UNREADABLE).\n\nMulti-file connectors: pass each file as content_base64 (single-line, escape-safe) instead of content — the canonical path for a full connector; do not curl the raw endpoint (it skips connector registration and PAT provisioning).", "inputSchema": { "properties": { "connector_id": { - "description": "Files to upload — each needs 'path' plus ONE of content (inline string) or content_base64 (escape-safe base64; preferred for multi-file connectors). By default merges with the GitHub state at `ref`. Set replace:true to wipe the connector dir and write only these files.", + "description": "Files to upload — each needs 'path' plus ONE of content (inline string) or content_base64 (escape-safe base64; preferred for multi-file connectors). By default merges with the GitHub state at `ref`. With replace:true the connector becomes exactly these files (see replace). A merge or github:true upload that leaves out a binary or a file over 512 KB that Core runs goes through and drops it: the reply's dropped[] and dropped_warning name it. A built file under ui-dist/ or rn-bundle/ that a build script of the package.json you send writes again on every upload is listed in regenerated[], not as lost; a binary never is.", "items": { "properties": { "content": { - "description": "Opt into FULL REPLACE: wipe the connector dir and write only the provided `files`. Default: false (= merge with GitHub state at `ref`). Use with intent — sending an incomplete file set with replace:true will break the connector.", + "description": "FULL REPLACE: the connector becomes exactly `files`. Every other file is deleted from Core, from the Builder's source and, when GitHub is connected, from the repo's working branch (dev) — every file under connectors/<connector-id>/ you leave out, deployed or not, files written with ateam_github_patch included (some files are kept — repo.kept names each with its reason). The reply names each: dropped (Core), authored.removed (the Builder's source), repo.deleted (the branch), repo.branch_only (the branch files that were never deployed), repo.kept (kept, with the reason). Main is never touched (a promote carries it). With github:true the repo is left as it is. Default: false (= merge). An incomplete file set with replace:true deletes the rest of the connector.", "type": "boolean" }, "solution_id": { + "monitoring": { + "cost": "heavy", + "latency_ms_p95": 75000, + "output": "bounded", + "safe": false + }, "name": "ateam_upload_connector" }
--- pinned/ateam_verify +++ observed/ateam_verify { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, - "description": "ONE call that returns the REAL runtime end-state of a solution — connectors connected + tools discovered, every declared widget actually rendering, skills deployed — with the EXACT failing gaps. Use this instead of guess-and-check after a deploy/patch: it tells you the truth (what's actually live) and names precisely what's broken, not a generic warning. Reliable from any connection (routes through the Builder, not a direct Core call).", + "description": "ONE call that returns the REAL runtime end-state of a solution — connectors connected + tools discovered, every declared widget actually rendering, skills deployed — with the EXACT failing gaps. Use this instead of guess-and-check after a deploy/patch: it tells you the truth (what's actually live) and names precisely what's broken, not a generic warning. ok:false with gaps is the answer; a result that also carries `error` means a check could not run (key refused, Builder down) — that is a failed call, not a verdict. Reliable from any connection (routes through the Builder, not a direct Core call).", "inputSchema": { "properties": { "solution_id": {
--- pinned/ateam_verify_consistency +++ observed/ateam_verify_consistency { + "annotations": { + "readOnlyHint": true + }, "core": true, - "description": "Check that the Builder filesystem state and GitHub state are in sync for a solution. Read-only probe — does NOT trigger a deploy.\n\nReturns:\n • ok: true + drifts: [] if everything matches\n • ok: false + drifts: [{path, kind}] listing files that differ (kinds: fs_missing, gh_missing, content_differs)\n\nDrift can creep in when GitHub writes happen but Builder FS doesn't get the mirror update (network blip, container restart mid-write). Boot sync heals most of it on next backend restart; this tool surfaces drift earlier.\n\nRun after a series of ateam_github_patch calls to confirm the Builder backend is consistent with GitHub before you ateam_build_and_run.", + "description": "Check that the Builder filesystem state and GitHub state are in sync for a solution. Read-only probe — does NOT trigger a deploy.\n\nReturns { ok: true, consistent, drifts: [{path, kind}] }:\n • consistent: true + drifts: [] if everything matches\n • consistent: false + drifts listing files that differ — kind ∈ fs_missing | content_differs | gh_missing | gh_read_error | repo_unreachable\n`ok` says the probe RAN; `consistent` is the answer. Drift is consistent:false, not a failed call. The comparison strips ephemeral fields (timestamps, runtime/deploy-state, resolved-on-load flags), so only REAL content drift surfaces.\n\nDrift can creep in when GitHub writes happen but Builder FS doesn't get the mirror update (network blip, container restart mid-write). Boot sync heals most of it on next backend restart; this tool surfaces drift earlier.\n\nRun after a series of ateam_github_patch calls to confirm the Builder backend is consistent with GitHub before you ateam_build_and_run.", "inputSchema": { "properties": { "solution_id": {
--- pinned/ateam_verify_surface +++ observed/ateam_verify_surface { + "annotations": { + "destructiveHint": true, + "readOnlyHint": false + }, "core": true, "description": "PROVE a connector ui_plugin actually renders WITH DATA — the required evidence that a user-visible UI fix is done. A plugin fetches its data over postMessage from its parent window, so opening its iframe alone shows the empty state and 'confirms' the very bug you're checking. This opens the plugin in the REAL host surface in headless Chromium, records every MCP tool call it makes, and returns { ok, verdict, visible_text, calls, failures }. It distinguishes 'invented tool name' / 'right tool, no data' / 'plugin never asked'. FAIL-CLOSED: a browser-mcp outage returns ok:false verdict:'inconclusive' (never a soft pass). Run AFTER a UI/data fix; quote visible_text in your report. Requires authentication.", "inputSchema": { - "description": "Optional assertion: { tools: ['memory.get', ...] } — each MUST be called by the plugin, else ok:false.", + "description": "Optional assertion. { tools: ['memory.get', ...] } — each listed tool MUST be called by the plugin, else ok:false. { values: ['<a value from a row you created, exactly as the widget shows it, e.g. 7.2 km, Living room, Pasta carbonara>'] } — each string must appear in visible_text AND must DISAPPEAR when the data path is disabled: the probe renders the plugin a second time with every data call answered by an error, and a value still rendered then is hardcoded in the plugin, so the probe fails it. Use a value from a record you created, formatted as the widget shows it. Then finish: delete that record through the solution's own delete, and show it gone with the SAME read that showed it after you created it. The whole rule, TEST_ROW_DONE_RULE: ateam_get_spec({ topic: \"widgets\", search: \"data_fidelity\" }).", "properties": { "tools": { "items": { + }, + "values": { + "items": { + "type": "string" + }, + "type": "array" } }, "type": "object" - "description": "The ui_plugin id to probe, e.g. 'mcp:accounting-mcp:spending-dashboard'.", + "description": "The ui_plugin id to probe, in the form 'mcp:<connector-id>:<plugin-name>', e.g. 'mcp:home-mcp:room-panel'.", "type": "string" }, "solution_id": {
Current tools
Show all 60 tool fingerprints
Watch this server yourself
If you run this server, put the proxy in front of it. It pins these exact fingerprints on first connect and stops the session if they move.
npx -y mcp-pin@0.2.3 -- <your @ateam-ai/mcp command>
Or subscribe to this page's RSS feed to be told when it changes.
Badge
The badge states one fact about time and nothing else. It never claims a server is safe.
[](https://mcp-pin.gautamkhosla.com/servers/899fd2747582dc4c.html)