Skip to main content
Glama

Agent Teams

English | 简体中文

An MCP server that runs work as visible DSH worker sessions instead of hidden subagents. Each dispatch pins an exact provider, model and reasoning effort, writes a durable job journal under stateDir, and reports progress back to the chat that started it.

This is an experimental public repository extracted from a private deployment. An end-to-end install on a clean machine has not been verified: external supervisor identity records and an authenticated DSH owner remain prerequisites. The release targets Windows, Node.js 24 LTS, the DSH 0.2.1-rc.2 plugin interface and the loopback endpoint http://127.0.0.1:3080. Ownership attestation is a Windows PowerShell script (scripts/verify-owner.ps1). Other platforms, Node lines, DSH plugin versions and endpoints are outside this release.

Requirements

  • Windows. Attestation runs scripts/verify-owner.ps1 through Windows PowerShell 5.1.

  • Node.js 24 LTS; tested on 24.14.0. Codex thread state is read through node:sqlite, imported without a flag.

  • A running DSH owner at http://127.0.0.1:3080 that you can log in to. The server refuses any other URL and the hub plugin answers only that exact host, so the endpoint is part of this release's contract rather than a setting.

  • The DSH worker plugin in hub/ installed into your DSH profile (see below).

Related MCP server: arjun-mcp

Install

git clone https://github.com/Phantomthedog/agent-teams-mcp.git
Set-Location agent-teams-mcp
npm ci
Copy-Item config.example.json config.json
npm test

config.json is read from the repository root by default; pass a different path as argv[2] when starting the server. The angle-bracket entries in config.example.json (<provider>, <model>, <you>) are placeholders that must be replaced. The remaining values are example defaults: review them and adjust paths and settings for your deployment. package.json declares os: ["win32"], so npm ci refuses to install on another platform. Tests use temp fixtures and need no config.json.

1. State and workspace roots

stateDir holds the job journal, the async store and the hub token. allowedRoots lists the directories a worker's cwd and task files may resolve inside; anything outside is refused before a worker is created.

2. Hub token

The MCP server asks DSH to create a worker over HTTP, authenticated with a bearer token. Create it once, at the path the plugin reads:

node -e "const fs=require('node:fs'),path=require('node:path'),c=require('node:crypto');const f=process.argv[1];fs.mkdirSync(path.dirname(f),{recursive:true});fs.writeFileSync(f,c.randomBytes(32).toString('hex'),{encoding:'utf8',flag:'wx'})" "C:/agent-teams/state/hub-token"

That writes 64 lowercase hex characters as UTF-8 without a BOM, creates the parent directory, and flag: 'wx' fails instead of overwriting an existing token (you get an EEXIST error and the file is left as it was). Shell redirection is not used because Windows PowerShell 5.1 would write UTF-16LE. The plugin reads <stateDir>/hub-token and requires exactly /^[a-f0-9]{64}$/; a UTF-16LE file, which is what a PowerShell redirection would produce, is rejected. Set dsh.hubTokenFile to the same path.

3. Owner login

dsh.authFile points at a JSON file holding the DSH owner's browser session cookie:

{ "cookie": "<session cookie value>" }

The server sends that value as a Cookie header to the owner and refuses with auth-unavailable: DSH owner login is unavailable. when the file is missing or holds no non-empty cookie string. How you obtain the cookie is your deployment's business; DSH has no standard file for it.

4. DSH worker plugin

hub/ is a DSH plugin package: it exports one plugin that registers a single POST /_codex/dispatch/create endpoint on the DSH web server. The DSH plugin manager installs a local plugin straight from an absolute directory path — it reads hub/package.json, requires the dsh.bundle field that is already there, and records the path with pnpm — so no packaging or publishing step is needed:

  1. Edit hub/cordis.patch.yml: stateDir and allowedRoots must match config.json.

  2. Install that absolute path into the profile named by dsh.profile, then restart the profile.

A relative path is refused (a local path must be absolute).

5. Owner attestation (required)

Dispatch fails closed until your deployment can prove which process owns DSH. The server runs scripts/verify-owner.ps1 before it sends any credential or creates any worker, and refuses with:

owner-unverified: The DSH owner verifier is not configured for restart recovery; no credentials sent.

Set these four dsh fields, all published by your DSH supervisor:

Field

What it must point at

receiptFile

Lifecycle receipt written when the owner started: port, pid, startedAt, executable, home.

identityFile

Runtime identity of that same process: pid, port, startedAt, home, optional platform.

expectedHome

The DSH home directory the owner must be running from.

expectedEntrypoint

The script the owner process must be executing, compared against its real command line.

The verifier requires that exactly one process owns 127.0.0.1:<port> and that it executes nodeExecutable (or the receipt's executable). The receipt counts as current only when its pid equals the live process PID and its startedAt is within two seconds of that process's start time; otherwise the runtime identity must describe that same pid, port and home, and must have been published between process start and ten minutes later. The owner's real command line must name <expectedEntrypoint> as its script argument and must carry --profile <dsh.profile>; a --host, --port or --home that disagrees with the configuration is refused. That is the shape a DSH supervisor starts:

node <expectedEntrypoint> ... --profile <dsh.profile> --host 127.0.0.1 --port 3080 --home <expectedHome>

If your deployment publishes neither a matching receipt nor a runtime identity, there is no supported way to dispatch.

6. Register with Codex

Add one stanza to ~/.codex/config.toml and leave every other entry as it is:

[mcp_servers.agent-teams]
command = "C:/Program Files/nodejs/node.exe"
args = ["<checkout>/src/server.mjs", "<checkout>/config.json"]
tool_timeout_sec = 3700

Command and arguments alone are not a complete registration. Codex applies its own MCP tool timeout, which is far shorter than dispatch_wait's 600000 ms default budget, so a long wait would be cut off by the client. tool_timeout_sec = 3700 covers the 3600000 ms maximum wait plus process start-up. Point command at the Node executable you run the server with and args at this checkout's absolute paths.

Worked example

scripts/call.mjs runs one tool over stdio against config.json. It needs the prerequisites above: a live owner, the hub plugin, a hub token and owner records. <your-host> and <parent-thread-uuid> are the calling chat's own values; they are never inferred from the server environment.

# 1. Dispatch one worker. request_id is yours; reuse it verbatim on a retry.
Set-Content -Path args.json -Encoding utf8 -Value '{"request_id":"demo-0001","cwd":"C:/agent-teams/workspace","task":"List the files in this directory."}'
node scripts/call.mjs dispatch_dsh args.json
# -> {"job_id":"dispatch-<32 hex>","sessionId":"session-...","status":"queued",...}

# 2. Bind the chat that will receive events. Registration also arms session follow.
Set-Content -Path args.json -Encoding utf8 -Value '{"action":"register","job_id":"dispatch-<32 hex>","parent":{"host":"<your-host>","threadUuid":"<parent-thread-uuid>"}}'
node scripts/call.mjs dispatch_watch args.json

# 3. Wait for the first outcome. Pass the same parent so receipts are accounted once.
Set-Content -Path args.json -Encoding utf8 -Value '{"job_id":"dispatch-<32 hex>","parent":{"host":"<your-host>","threadUuid":"<parent-thread-uuid>"},"budget_ms":600000}'
node scripts/call.mjs dispatch_wait args.json
# -> {"job_id":"...","status":"completed","terminal":true,"actionable":true,"delivery":{"event_id":"..."},"wait":{...}}

# 4. Acknowledge receipt before reviewing the work. Receipt is not acceptance.
Set-Content -Path args.json -Encoding utf8 -Value '{"action":"ack","job_id":"dispatch-<32 hex>","parent":{"host":"<your-host>","threadUuid":"<parent-thread-uuid>"},"event_id":"<event_id>"}'
node scripts/call.mjs dispatch_watch args.json

Set-Content -Encoding utf8 produces a BOM under Windows PowerShell 5.1 and none under PowerShell 7; both are accepted, because the server and call.mjs strip a leading BOM before parsing JSON. The hub token is the one file that must stay plain UTF-8.

dispatch_status is the manual check when a watch was never registered or an event looks stale. It is not the routine progress loop.

Lifecycle limits

  • One watch binding per job, created by register; ensure recovers its observer, detach stops it. A killed watcher helper or a reboot still needs ensure.

  • Reports stay in the tool channel. Nothing wakes an idle chat, so a parent turn that has ended will not see a later tool result.

  • dispatch_wait holds one call open: 600000 ms by default, 3600000 ms at most. The client timeout must exceed the budget, which is why registration sets 3700 s.

  • A turn that ends as aborted is terminal: the job status becomes cancelled and dispatch_wait returns it like any other end.

  • Uncertain about a turn? dispatch_status reports the recorded attempt and whether its turn/end evidence exists. To keep working, continue the same session with dispatch_followup (default queue adds a next turn; steer works only while a turn is running) and a new request_id. To bind a turn that already exists, use dispatch_adopt_turn with that turn number and the rpcId of the user message that started it; it refuses running, unknown, earlier or unrelated turns. Do not dispatch a duplicate job to get past an uncertain state.

Legacy queue delivery

The source supports enqueueing reports into a parent chat through the Codex app-server. That path needs a logged-in Codex CLI and its private thread database, is unverified in this release, and is off by default (notifications.queueEnabled: false). Treat it as historical: this candidate delivers through tool results only.

Tools

Tool

Purpose

dispatch_models

List live DSH routes and their supported efforts.

dispatch_dsh

Create one visible worker with an immutable task and exact route.

dispatch_status

Recovery check of one job's turn: requires the correlated turn/end.

dispatch_cancel

Request cancellation of a bridge-owned task.

dispatch_adopt_turn

Bind an interrupted job to a later turn in its own session.

dispatch_followup

Steer a running turn or queue the next one, with replay-safe IDs.

dispatch_watch

Register the initiating chat, recover the observer, read and ack events.

handoff_prepare

Write an immutable task file without spawning anything.

dispatch_wait

Wait for the first selected job to finish, fail or need input.

Retry an uncertain dispatch with the same request_id and payload; DSH deduplicates the replay. Never substitute a different model to get past a failure.

Scripts

  • scripts/call.mjs — call one tool over stdio without a GUI.

  • scripts/smoke-mcp.mjs — assert the server starts and lists exactly the nine tools.

  • scripts/watch.mjs — the detached watcher helper the server spawns.

  • scripts/watch-activate.mjs — replace a watcher that is running stale code.

  • scripts/worker-report.mjs — a worker's own milestone/blocked report.

  • scripts/recover-writer-lock.mjs — clear a job writer lock left by a killed process.

  • scripts/verify-owner.ps1 — the owner attestation script.

Tests

npm test runs the suite with node --test. Tests use temp fixtures and fake thread IDs, and need no config.json. The owner-attestation tests run the real verifier against temp receipts and skip the live-owner cases when this host has no owner records. scripts/smoke-mcp.mjs and scripts/call.mjs do start the server, so they need config.json to exist.

License

This project's code is released under GPL-3.0-only. The full, authoritative English license text is LICENSE. Copyright (c) 2026 The Agent Teams contributors. The npm dependencies keep their own licenses — see THIRD_PARTY_NOTICES.md.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Lets Claude Code, Codex, or any MCP client drive DeepSeek Harness as a long-lived coding agent: starting and resuming conversations, dispatching tasks in the background, streaming live output and reasoning stats, interjecting or interrupting mid-turn, handling approval prompts, and collecting results later. Sessions persist by ID, so work survives process restarts and can be handed back to a DSH GUI when needed.
    750 npm
    15
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables host agents to launch and supervise durable, long-horizon AI jobs—starting, running, resuming, checking budgets, and inspecting assembled context—so tasks can survive restarts and outlive the conversation with verified completion.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables an opt-in manager–worker workflow where a manager agent plans work, dispatches scoped tasks with acceptance criteria to independent worker sessions, tracks progress on a live monitor, audits scope, and verifies artifacts by fingerprint before integrating them. Durable SQLite-backed state, heartbeats, and per-attempt logs support stop/resume, anti-duplicate dispatching, and recovery across closed chats.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables Codex/Astra to hand off complete task packets to DSH for real execution in a shared local workspace, with progress, tool calls, questions, and results written back to a shared .astra-dsh task board. Supports blocking waits, queued follow-up instructions, interrupts, cancellation, rework requests, and independent verification or diff review of the resulting changes.
    1
    MIT