ATMcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ATMcplist agents in my team"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ATMcp — Agent Teams MCP Server
English · 中文文档
A single, network-reachable MCP server that lets LLM coding agents (Claude Code and any other MCP client) on different devices / networks / regions form a team and work together — sharing knowledge, memory, task goals, progress, and completion — with a live web dashboard showing every agent's status.
Built with Python + FastAPI + Redis + SQLite, drawing on distributed-systems patterns (cloud presence/heartbeats, an append-only content-addressed log, CRDT-style merge semantics, and lease-based task scheduling) — kept to the parts that earn their keep at MVP scale.
Remote agents (different devices/networks) Browser
Claude Code A Claude Code B … Dashboard
│ streamable-HTTP MCP │ │ HTTP + WebSocket
▼ ▼ ▼
┌──────────────────── one FastAPI process (uvicorn) ───────────────────┐
│ /mcp FastMCP(streamable-http) /dashboard /ws/{team} /api/* │
│ SQLite (WAL) = source of truth · events log = audit/replay/feed │
│ in-proc hub → WebSocket fan-out · reaper → re-queue expired leases │
└───────────────────────────────────┬───────────────────────────────────┘
▼ soft state (rebuildable)
Redis: heartbeats (presence TTL) · task leases · streams (catch-up/fan-out)Key properties
SQLite is the source of truth (WAL, single in-process writer serialized by one lock). Redis is soft state — lose it and you lose only liveness (presence, live fan-out, lease-based re-queue), never durable data. Everything rebuilds from SQLite.
Commit-then-publish: every mutation is
BEGIN IMMEDIATE → write → append one events row → COMMIT → fan out. Tool responses are read-your-writes.Presence is derived, never stored:
online = heartbeat key exists(30s TTL, ~10s refresh). A crashed/partitioned agent self-cleans on expiry.Knowledge is content-addressed (
sha256): identical findings auto-dedupe and gain provenance (contributor_count); modeled as an OR-Set with a fast projection + FTS5 search.Memory is a LWW-register ordered by a per-team Lamport clock; optional
expected_versiongives optimistic CAS that returns conflicts as data.Tasks use lease-based claiming: a DB-arbitrated atomic claim + a Redis lease + a
fencing_tokenmeans cross-device agents never duplicate work, and a 5s reaper re-queues work abandoned by a crashed agent. Zombies are rejected by their stale token.Multi-tenant isolation is structural:
team_idleads every index and prefixes every Redis key; scoped tools derive the team from the join session, never from client input.
Related MCP server: JustClone Coordination MCP Server
Quick start (Docker)
cp .env.example .env # set ATMCP_ADMIN_TOKEN
docker compose up --build # starts redis + atmcp on :8000
# Create a team (returns its join token + URLs):
curl -s -X POST http://localhost:8000/api/teams \
-H "X-Admin-Token: $ATMCP_ADMIN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"my-team"}' | jqQuick start (local dev)
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
./run_local.sh # starts a redis container + uvicorn on :8000
# Create a team without the HTTP API (writes straight to SQLite):
python -m atmcp.admin create-team my-teamConnect an agent
Point any streamable-HTTP MCP client at http://<host>:8000/mcp, carrying the team join
token (and an optional stable agent name) in headers. With the headers set, the agent is
auto-joined on its first tool call — no explicit join_team needed.
# Claude Code
claude mcp add --transport http atmcp http://<host>:8000/mcp \
--header "Authorization: Bearer <join_token>" \
--header "X-ATMcp-Agent: alice"Adding the server only makes the tools available. Agents (Claude/Cursor/Qwen) won't report anything until told to — MCP is pull, not push, and LLMs have no timer. So:
Give each agent the workflow prompt — ready-to-paste rules for Claude Code / Cursor / Qwen are in
prompts/.For reliable presence (stay "online" even while only thinking), run the sidecar — it heartbeats over REST, decoupled from the LLM:
python scripts/atmcp_heartbeat.py --url http://<host>:8000 \ --team <team> --token <join_token> --name alice --interval 10
If your client can't set headers, the agent passes the token to join_team directly:
join_team(team_name="my-team", display_name="alice", join_token="<join_token>").
Dashboard
Open http://<host>/dashboard?team=<team> — a full-height three-column app (no page
scrolling; each column scrolls on its own):
Left: goal progress + stats and the live agent roster (presence green/amber/grey).
Center (tabbed): Task board · Activity feed · Knowledge · Tokens (per-agent token & cost meter with rolling 5h/7d windows) — and clicking an agent opens an Agent tab right there with its directives, tasks, and live output (no scrolling down).
Right (always visible): the Team console — type
/teamcommands (a "pseudo-model" chat) with the input pinned at the bottom; results and live directive events stream in.
It loads a JSON snapshot then live-updates over a WebSocket (auto-reconnects with catch-up).
Auth is off by default; set ATMCP_DASHBOARD_AUTH=1 to require a per-team read-only token
(the console always needs the join token to send commands).
MCP tools
Group | Tools |
Identity |
|
Presence |
|
Knowledge |
|
Memory |
|
Goals/Tasks |
|
Directives |
|
Output |
|
Status/Sync |
|
Every mutating tool accepts an optional idem_key (idempotency). Expected conditions are
returned as data ({conflict}, {taken_by}, {stale_token}, {not_joined}), not errors.
sync(since_event_id, wait_ms) long-polls for the next event so agents can react quickly.
Token & cost monitoring (so quota never runs out silently)
claude -p --output-format json already returns a usage + total_cost_usd block, so the
token-free poller captures it for free and reports it (POST …/agents/{agent}/usage). The server
keeps an append-only usage_events meter and the dashboard Tokens tab shows per-agent
in/out/cache tokens, cost, and rolling 5h/7d windows (mirroring Claude's rate-limit windows) so
you can see how close each agent is to its limit in real time.
The poller also enforces a hard budget brake: --cost-budget <USD> / --token-budget <N>
(0 = unlimited; cumulative totals persist per worker in --state-dir across restarts). When the
budget is reached the worker stops claiming directives and shows "paused: budget reached" on
the dashboard — so a background fleet can't burn through your quota unnoticed. Raise the budget or
pass --reset-usage to resume.
Configuration
See .env.example. Highlights: ATMCP_ADMIN_TOKEN, ATMCP_SQLITE_PATH, ATMCP_REDIS_URL,
ATMCP_PUBLIC_URL, heartbeat TTL/interval (30/10s), lease TTL (90s), reaper interval
(5s), ATMCP_TASK_MAX_ATTEMPTS, ATMCP_DASHBOARD_AUTH, usage retention
(ATMCP_USAGE_RETENTION_S, 30d).
Failure model (summary)
Failure | Behavior |
Agent crash / partition | heartbeat key expires → shown offline; held lease expires → reaper re-queues; last durable progress kept. |
Transient upstream API failure (overload / 429 / 5xx / network) | the poller retries the directive with jittered exponential backoff ( |
Agent reconnect | re- |
Duplicate / retried call |
|
Redis down | mutations still commit to SQLite; presence falls back to |
Server restart | SQLite intact (WAL); agents reconnect & re-join; reaper reconciles leases. |
Testing
source .venv/bin/activate
pip install -r requirements.txt
pytest -q # service-level tests: claim race, fencing/zombie, reaper,
# dedupe, LWW/CAS, isolation, REST snapshotLayout
atmcp/
app.py FastAPI assembly + lifespan (mounts /mcp, wires publisher)
mcp_server.py FastMCP tool surface (~33 tools)
web.py dashboard, /api/*, /ws/{team}, REST heartbeat/output, health, admin
db.py single-writer SQLite, transaction() = commit-then-publish
redis_bus.py soft state: heartbeats, leases, streams, sessions (best-effort)
hub.py in-process WebSocket fan-out + long-poll notify (generation counter)
reaper.py re-queues expired-lease tasks; prunes idempotency + agent output
events.py append to the monotonic events log
idempotency.py durable, in-transaction idempotency (retry-safe mutating tools)
session.py MCP-session → (team, agent) binding + header auto-join
canonical.py content-addressing (canonical JSON + sha256)
schema.sql full DDL (+ FTS5)
services/ identity · presence · knowledge · memory · tasks · status · clock
· directives (console→agent commands) · output (agent output stream)
static/ dashboard.html + dashboard.js
prompts/ ready-to-paste agent rules + console/worker setup
scripts/ atmcp_worker_poller.py (token-free worker) · atmcp_heartbeat.py
· atmcp_output_hook.py · atmcp_worker_runner.{sh,ps1}
skills/ team (console) + atmcp-worker (worker loop) Claude Code skills
agents/ atmcp-executor (Opus subagent the worker delegates execution to)Team console — manage the whole team from one window
One interactive console window + N background worker loops. From the console you list
everyone's status & TODOs, send a directive to a specific agent, watch its result, and tail
another agent's live output. See prompts/console-worker.md
and the skills/ (/team console + /atmcp-worker loop).
/team status # roster + TODO board
/team send bob "refactor X" # → directive_id
/team watch <directive_id> # long-polls until bob reports done/failed, prints result
/team logs bob --follow # live-tail bob's outputServer-side this is the directive bus (send_directive/inbox/claim_directive/
report_directive/wait_directive) + the agent output stream (append_output/
get_agent_output, plus POST /api/teams/{team}/agents/{agent}/output for the hook).
"Watching" is long-poll, so results surface as soon as the worker reports.
Run workers cheaply. An in-agent loop pays a full model turn (system prompt + all tool
schemas) on every poll just to find an empty inbox — millions of wasted tokens/day. Prefer
the token-free poller scripts/atmcp_worker_poller.py: it long-polls the inbox over plain
HTTP (zero tokens while idle) and invokes claude -p only when a directive actually arrives:
python scripts/atmcp_worker_poller.py --url http://<host>:8000 \
--team <team> --token <join_token> --name bob --model opus # --dry-run to testFull flag/env reference (memory, budget brake, retry, custom executors): see
prompts/poller-usage.md or --help.
By default it keeps one resumable session per worker (--session-mode resume: captures the
Claude session_id and --resumes it each directive) so the worker remembers prior tasks
while idle polling stays token-free; the inbox long-poll returns the instant a directive is sent
(don't use /loop — 1-min cron). It uses the worker REST API (GET …/agents/{agent}/inbox
long-poll, POST …/directives/{id}/claim, …/report). If you'd rather run the atmcp-worker
skill as an agent loop, use the runner
scripts/atmcp_worker_runner.{sh,ps1} (fast poller model + Opus atmcp-executor subagent) —
and avoid bare /loop /atmcp-worker (dynamic mode can silently stop, esp. Windows PowerShell;
use /loop 30s).
Workbench (preview)
A chat-style, any-device control plane that runs alongside the dashboard (it does not
replace anything — /team and the dashboard keep working). Open
http://<host>/workbench?team=<team>: a collapsible team → agent → session tree on the
left, a streaming web chat on the right (pinned input; output streams in token-by-token).
Each session is an independent conversation thread (its own memory). Drive it with the
concurrent streaming host (one host per agent, multiple threads at once, each in its own git
worktree):
python scripts/atmcp_workbench_host.py --url http://<host>:8000 \
--team <team> --token <join_token> --name bob --base-repo ~/code/myrepo # --dry-run to testPick the executor per agent with --executor: claude (default, structured streaming + usage),
codex / cursor (generic CLI streaming; --executor-cmd to customize), or openai —
any OpenAI-compatible endpoint (Ollama / LM Studio / vLLM / hosted) running a tool-calling
agent loop so a local model can actually run Bash / edit files, e.g.:
python scripts/atmcp_workbench_host.py --team <team> --token <jt> --name qwen \
--executor openai --api-base http://localhost:11434/v1 --model qwen3.6:35b \
--base-repo ~/code/myrepoEvery tool call (especially shell) passes through Command Guard — a per-team allow/deny gate
with a built-in dangerous-command deny-list (audited; /guard/* API), an ask tier that escalates
gray-zone commands for human approval in the dashboard Security tab, and a local fail-closed
fallback if the guard is unreachable.
New here? Follow docs/workbench-quickstart.md — a step-by-step
walkthrough (server → team → host → page), including running a local Ollama model as a
tool-using agent. Full design/roadmap in docs/workbench-design.md.
Making agents actually use it
MCP is pull, not push: the tools are available, but the model decides when to call them and
has no timer. See prompts/ for the per-client workflow rules, the
auto-join headers (Authorization + X-ATMcp-Agent), and three ways to keep presence fresh
(model-driven, the sidecar, or a client hook). The REST presence endpoint
POST /api/teams/{team}/heartbeat (auth = join token) backs the sidecar.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/MidCheck/ATMcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server