Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion connectors/claude/.mcp.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"mcpServers": {
"mupot": {
"type": "sse",
"type": "http",
"url": "https://YOUR-POT.example.workers.dev/mcp",
"headers": {
"Authorization": "Bearer <MUPOT_MEMBER_TOKEN>"
Expand Down
28 changes: 25 additions & 3 deletions connectors/claude/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ pot. It connects over MCP — the same seam an agent uses — carrying nothing b
scoped **member token**. Identity and permissions are resolved by the pot from that
token, never from anything Claude says about itself.

For the topology-A headless driver (BYOA slice 3), see
[`scripts/claude-code-worker.py`](../../scripts/claude-code-worker.py) — `claude -p`
with `--output-format stream-json`, remote MCP via `.mcp.json` (`type: "http"`,
`url`, `headers.Authorization`), land-at-review via `runtime-adapter/v1`. Starts
from the [`packs/claude-code/flock-agent`](../../packs/claude-code/flock-agent/) pack.

## Two ways to connect

### A. Raw MCP config (`.mcp.json`)
### A. Raw MCP config (`.mcp.json`) — preferred for Claude Code

Use [`.mcp.json`](./.mcp.json) as a template for Claude Code (project or user
scope) or Claude Desktop.
scope). **HTTP only** for the BYOA headless path — `type: "http"` with
`headers.Authorization`.

1. Copy `.mcp.json` into your project root (Claude Code) or merge it into your
Claude Desktop config.
Expand All @@ -30,7 +37,7 @@ curl https://YOUR-POT.example.workers.dev/mcp/tools
You should see the tool surface: `task_create`, `remember`, `recall`,
`wake_agent`, `squad_message`, `status`.

### B. The `/mupot` skill (recommended for Claude Code)
### B. The `/mupot` skill (recommended for interactive Claude Code)

The [`skills/mupot/`](./skills/mupot/) plugin wraps the three everyday actions —
**task**, **status**, **recall** — as `/mupot` commands so you don't hand-write
Expand All @@ -50,6 +57,21 @@ MCP calls. Install:
3. In Claude Code, run `/mupot task ...`, `/mupot status`, or `/mupot recall ...`.
See [`skills/mupot/SKILL.md`](./skills/mupot/SKILL.md) for the full command set.

## Headless driver (topology A)

```bash
# Plan mint + attach without live Claude creds:
MINT_ATTACH=1 DRY_RUN=1 python3 scripts/claude-code-worker.py

# One-shot poll loop (token at ~/.fleet/agents/claude-code-member.token):
DRY_RUN=1 python3 scripts/claude-code-worker.py
python3 scripts/claude-code-worker.py
```

The driver writes a worktree `.mcp.json` (`type: "http"` + Bearer header), runs
`claude -p --output-format stream-json`, verifies commits + `tsc`, opens the PR,
and lands the task at `review`. It never merges, deploys, or self-verdicts.

## What Claude can do (gated by your capability)

Everything Claude does goes through the member token's capabilities. With a token
Expand Down
55 changes: 38 additions & 17 deletions connectors/codex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,67 @@ as Claude — `POST /mcp` with a member token — and gets the same tool surface
gated by the same capability RBAC. Identity is derived by the pot from the token,
never from anything Codex says about itself.

For the topology-A headless driver (BYOA slice 2), see
[`scripts/codex-worker.py`](../../scripts/codex-worker.py) — `codex exec`
with `--sandbox` + `--json`, land-at-review via `runtime-adapter/v1`.

## Connect

Codex reads MCP servers from its config (`~/.codex/config.toml`, or the JSON form
some builds use). Add a `mupot` server pointing at your pot's `/mcp` endpoint with
your member token.
Codex reads MCP servers from `~/.codex/config.toml`. Add a `mupot` server
pointing at your pot's `/mcp` endpoint. **Streamable-HTTP only** — Codex does
**not** support SSE for remote MCP.

### TOML form (`~/.codex/config.toml`)
### TOML form (`~/.codex/config.toml`) — preferred

See [`config.toml`](./config.toml):

```toml
[mcp_servers.mupot]
type = "sse"
url = "https://YOUR-POT.example.workers.dev/mcp"

[mcp_servers.mupot.headers]
Authorization = "Bearer <MUPOT_MEMBER_TOKEN>"
bearer_token_env_var = "MUPOT_MCP_TOKEN"
# then: export MUPOT_MCP_TOKEN=<MEMBER_TOKEN>
```

### JSON form (Codex builds that share Claude's `.mcp.json` shape)
Do **not** set `type = "sse"`. The token comes from an env var so the raw value
never lands in the config file.

See [`mcp.json`](./mcp.json) — identical structure to the Claude connector's
`.mcp.json`.
### JSON form (some Codex builds that share Claude's `.mcp.json` shape)

See [`mcp.json`](./mcp.json) — use `type: "http"`, never `"sse"`. Prefer the
TOML + `bearer_token_env_var` form above for Codex CLI.

## Fill in

1. Replace `YOUR-POT.example.workers.dev` with **your** pot's host.
2. Replace `<MUPOT_MEMBER_TOKEN>` with the raw member token your pot minted
(`channel: workspace`). See the top-level [connectors README](../README.md) for
how to mint one. **Never commit the filled-in file.**
2. Export `MUPOT_MCP_TOKEN` (or your chosen env var) to the raw member token your
pot minted (`channel: workspace`). See the top-level
[connectors README](../README.md) for how to mint one. **Never commit the
token.**

Verify the tool surface (no token needed for discovery; token needed for calls):

Verify the tool surface (no token needed):
```bash
curl -sS https://YOUR-POT.example.workers.dev/mcp \
-H "Authorization: Bearer $MUPOT_MCP_TOKEN" \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

## Headless driver (topology A)

```bash
curl https://YOUR-POT.example.workers.dev/mcp/tools
# Plan mint + attach without live Codex creds:
MINT_ATTACH=1 DRY_RUN=1 python3 scripts/codex-worker.py

# One-shot poll loop (token at ~/.fleet/agents/codex-member.token):
DRY_RUN=1 python3 scripts/codex-worker.py
python3 scripts/codex-worker.py
```

## Capability

Codex acts within the member token's capabilities — same as Claude. `member` on a
squad unlocks `task_create` / `squad_message`; `lead`+ unlocks `wake_agent`;
`remember` / `recall` / `status` are self-scoped. Anything above the grant returns
`403 forbidden`. Codex cannot escalate.
`403 forbidden`. Codex cannot escalate. The driver lands work at `review` and
never merges, deploys, or self-verdicts.
16 changes: 9 additions & 7 deletions connectors/codex/config.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Codex ── mupot MCP server (EXAMPLE)
# Merge into ~/.codex/config.toml. Replace the host with your pot, and the token
# with the raw member token your pot minted (channel: workspace). Never commit the
# filled-in file — this placeholder version is the only one that belongs in git.
# Merge into ~/.codex/config.toml. Replace the host with your pot.
# Set the env var named below to the raw member token your pot minted
# (channel: workspace). Never commit the filled-in token — this placeholder
# version is the only one that belongs in git.
#
# Streamable-HTTP ONLY. Do NOT set type="sse" / transport=sse — Codex does not
# support SSE for remote MCP (see docs/connect-mcp-client.md).

[mcp_servers.mupot]
type = "sse"
url = "https://YOUR-POT.example.workers.dev/mcp"

[mcp_servers.mupot.headers]
Authorization = "Bearer <MUPOT_MEMBER_TOKEN>"
bearer_token_env_var = "MUPOT_MCP_TOKEN"
# then: export MUPOT_MCP_TOKEN=<MEMBER_TOKEN> (one line, no quotes/newline)
2 changes: 1 addition & 1 deletion connectors/codex/mcp.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"mcpServers": {
"mupot": {
"type": "sse",
"type": "http",
"url": "https://YOUR-POT.example.workers.dev/mcp",
"headers": {
"Authorization": "Bearer <MUPOT_MEMBER_TOKEN>"
Expand Down
3 changes: 2 additions & 1 deletion docs/runtime-adapter-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ Agent identity and runtime binding are separate concepts.

- `agent_id` names the durable Mupot agent.
- `runtime` names the carrying process type, such as `codex`,
`claude-code`, `hermes`, `hermes-cron`, `systemd-user`, `tmux`, or `python`.
`claude-code`, `cursor`, `hermes`, `hermes-cron`, `systemd-user`, `tmux`,
or `python`.
- `lifecycle` describes the expected host model: `on_demand` or `always_on`.
- `member_id` is derived from the bearer token or registered agent key.
- `tenant` is always `env.TENANT_SLUG`; client-supplied tenant fields are not
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"seed:local:test": "wrangler d1 execute mupot-local-test --local --config wrangler-local-test.toml --file scripts/local-test-seed.sql",
"smoke:local": "node scripts/local-browser-smoke.mjs",
"conformance:runtime:local": "node scripts/local-runtime-conformance.mjs",
"conformance:runtime:drivers": "node scripts/runtime-adapter-driver-conformance.mjs",
"receipt:host": "node fleet-runtime/host-receipt.mjs",
"receipt:runtime": "node fleet-runtime/runtime-receipt.mjs",
"receipt:control": "node fleet-runtime/control-receipt.mjs",
Expand Down
12 changes: 6 additions & 6 deletions packs/claude-code/flock-agent/.mcp.json.template
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"//": "Flock agent pack — Claude Code. Connects this agent to a mupot flock over the SOS bus.",
"//token": "Replace <SCOPED_BUS_TOKEN> with the project-scoped, agent-bound token your operator minted (project=<slug>, agent=<your-agent-name>). NEVER commit this file with a real token — it is gitignored.",
"//": "Flock agent pack — Claude Code (BYOA topology A). Connects this agent to mupot over remote HTTP MCP.",
"//token": "Replace <MUPOT_MEMBER_TOKEN> with the project-scoped, agent-bound token your operator minted. NEVER commit this file with a real token — it is gitignored.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not claim the member token is project-scoped

The prescribed mint_agent_token credential is not project-scoped: mintAgentBoundToken grants member on the agent's entire squad, and task_list defaults to that squad unless a project_id is explicitly supplied. When a squad contains multiple projects, an agent installed from this pack can therefore read and mutate tasks outside its purported project boundary, despite the template and skill telling operators that the token enforces project isolation.

Useful? React with 👍 / 👎.

"mcpServers": {
"mumega-bus": {
"type": "sse",
"url": "https://mcp.mumega.com/sse",
"mupot": {
"type": "http",
"url": "https://YOUR-POT.example.workers.dev/mcp",
"headers": {
"Authorization": "Bearer <SCOPED_BUS_TOKEN>"
"Authorization": "Bearer <MUPOT_MEMBER_TOKEN>"
Comment on lines +5 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update the bundled heartbeat for the new pot endpoint

After following this new configuration and minting a Mupot member token, the pack still directs users to its unchanged heartbeat.sh, which defaults to https://mcp.mumega.com/check_in and sends the old {"agent": ...} payload. Pot-native member-token presence is instead POST /api/fleet/checkin on the configured pot, so the documented heartbeat and verification steps fail unless users independently override and rewrite the bundled script.

Useful? React with 👍 / 👎.

}
}
}
Expand Down
20 changes: 13 additions & 7 deletions packs/claude-code/flock-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ Make a Claude Code agent a live member of a mupot flock: it appears in the pot's
`/fleet` when in, ages out when gone, and works the tenant's task queue. Reference
implementation of the [harness pack contract](../../../docs/flock-harness-pack-contract.md).

For **topology-A headless dispatch** (BYOA slice 3), use
[`scripts/claude-code-worker.py`](../../../scripts/claude-code-worker.py) — it
loads this pack's `.mcp.json` shape (`type: "http"`, `url`,
`headers.Authorization`), runs `claude -p --output-format stream-json`, and lands
work at `review` via `runtime-adapter/v1`.

## Onboard (5 steps)

1. **Get a scoped token.** Ask your operator to mint a bus token bound to your agent
name and scoped to the pot's project: `project=<slug>`, `agent=<your-name>`,
read + check-in (outbound work is gated, not granted to the token).
> Operators: mint via the tenant-agent provisioning path
> (`POST /api/internal/tenants/<slug>/agents/activate`, internal-secret authed).
1. **Get a scoped token.** Ask your operator to mint a member token bound to your
agent and scoped to the pot (`channel: workspace`). Least-privilege; outbound
work is gated, not granted to the token.
> Operators: mint via `mint_agent_token` / the tenant-agent provisioning path.
> NEVER an admin/null-scoped token — see the #44 invariant.

2. **Drop the config.** Copy `.mcp.json.template` to `.mcp.json` in the agent's
working dir and replace `<SCOPED_BUS_TOKEN>` with your token. `.mcp.json` is
gitignored — never commit the token.
working dir, set your pot host, and replace `<MUPOT_MEMBER_TOKEN>` with your
token. `.mcp.json` is gitignored — never commit the token. Shape is
`type: "http"` + `headers.Authorization` (not SSE).

3. **Add the skill.** Copy `SKILL.md` into the agent's skills (or point the agent at
this directory). It tells the agent to `boot_context` → `check_in` on start, then
Expand Down
5 changes: 3 additions & 2 deletions packs/claude-code/flock-agent/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ description: Use when this Claude Code agent should act as a member of a mupot f
This makes a Claude Code agent a live member of a tenant's flock on the SOS bus. It
is the reference implementation of the [harness pack contract](../../../docs/flock-harness-pack-contract.md).

The bus tools come from the `mumega-bus` MCP server (configured via `.mcp.json` — see
this pack's `.mcp.json.template`). Your identity, project scope, and permissions are
The tools come from the `mupot` MCP server (configured via `.mcp.json` —
`type: "http"`, `url`, `headers.Authorization`; see this pack's
`.mcp.json.template`). Your identity, project scope, and permissions are
derived from your token — never assume them.

## On session start (join the flock)
Expand Down
27 changes: 27 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,33 @@ It writes `tmp/local-runtime-conformance/report.json` and prints the same JSON
report to stdout. Failures write `tmp/local-runtime-conformance/failure-*.json`
when the process can create artifacts.

### Topology-A driver conformance (offline)

The cursor + mumcp + codex + claude-code topology-A drivers must stay on
`runtime-adapter/v1`. This smoke is offline (no live pot) and asserts contract
markers + preserved rails:

```bash
npm run conformance:runtime:drivers
```

Shared client: `scripts/runtime_adapter_v1.py`. Drivers:
`scripts/cursor-worker.py` (runtime=`cursor`), `scripts/mumcp-worker.py`
(runtime=`claude-code`, WordPress draft-only), `scripts/codex-worker.py`
(runtime=`codex`, `codex exec --sandbox --json`, remote MCP via
`~/.codex/config.toml` `url` + `bearer_token_env_var` — no SSE),
`scripts/claude-code-worker.py` (runtime=`claude-code`, `claude -p`
`--output-format stream-json`, remote MCP via worktree `.mcp.json`
`type: "http"` + `headers.Authorization` — flock-agent pack shape).

```bash
# Codex mint+attach plan (no live Codex creds required):
MINT_ATTACH=1 DRY_RUN=1 python3 scripts/codex-worker.py

# Claude Code mint+attach plan (no live Claude creds required):
MINT_ATTACH=1 DRY_RUN=1 python3 scripts/claude-code-worker.py
```

## CI local evidence

GitHub Actions runs the same local evidence gate with:
Expand Down
Loading