Skip to content

peers.announce

Tell the other peers of this shared server what the calling session is doing and which paths it expects to touch. A peer may hold several claims at once: name one with 'claim' and it stands beside the others, announce under that name again and it is updated, leave it out and this is the peer's one unnamed claim. Name your claims when one session is doing several things at once — subagents share their parent's session, so an unnamed announcement from each of them would replace the last rather than adding to it. Changes this process's memory only; the repository is never written. Needs an MCP session: over plain HTTP there is no caller.

Tell the other peers of this shared server what the calling session is doing and which paths it expects to touch. A peer may hold several claims at once: name one with 'claim' and it stands beside the others, announce under that name again and it is updated, leave it out and this is the peer's one unnamed claim. Name your claims when one session is doing several things at once — subagents share their parent's session, so an unnamed announcement from each of them would replace the last rather than adding to it. Changes this process's memory only; the repository is never written. Needs an MCP session: over plain HTTP there is no caller.

  • command
  • behaviorally_verified
  • module peers
  • #peers
  • #coordination

Exposure

Where this one definition is reachable. Absence is explicit: a surface not listed does not carry it.

surfaceas
MCPtool majordomus_announce
HTTPPOST /api/v1/peers/announce operationId peers.announce
command line—

Schemas

The canonical input and output, from the Rust types; the MCP tool schema and the OpenAPI parameters and responses are derived from these.

input · AnnounceInput

The input of peers.announce: what the calling peer is working on.
propertytyperequireddescription
claim string | null no Which of this peer's claims this is, when the peer holds more than one.

One MCP session is not always one piece of work — a client that fans work out to subagents shares its session with all of them — and without a name every announcement replaces the last, so the board ends up describing whichever worker spoke most recently and the rest of the scope silently stops being claimed. Name a claim and it stands beside the others; announce under that name again and it is updated. Leave it out and this is the peer's one unnamed claim, which is what a single session announcing about itself wants.

intent string yes One line, in the peer's words: the task, the question, the intent.
scope array no Repository-relative paths the peer expects to touch. Informational: other peers read it to avoid a collision; nothing here enforces it. default []
JSON Schema
{
  "additionalProperties": false,
  "description": "The input of `peers.announce`: what the calling peer is working on.",
  "properties": {
    "claim": {
      "description": "Which of this peer's claims this is, when the peer holds more than one.\n\nOne MCP session is not always one piece of work — a client that fans work out to\nsubagents shares its session with all of them — and without a name every\nannouncement replaces the last, so the board ends up describing whichever worker\nspoke most recently and the rest of the scope silently stops being claimed. Name a\nclaim and it stands beside the others; announce under that name again and it is\nupdated. Leave it out and this is the peer's one unnamed claim, which is what a\nsingle session announcing about itself wants.",
      "type": [
        "string",
        "null"
      ]
    },
    "intent": {
      "description": "One line, in the peer's words: the task, the question, the intent.",
      "type": "string"
    },
    "scope": {
      "default": [],
      "description": "Repository-relative paths the peer expects to touch. Informational: other peers\nread it to avoid a collision; nothing here enforces it.",
      "items": {
        "type": "string"
      },
      "type": "array"
    }
  },
  "required": [
    "intent"
  ],
  "title": "AnnounceInput",
  "type": "object"
}

output · Announced

What announce answers: the peer as recorded, and who else is on that ground.
propertytyperequireddescription
address one of 2 no Where the worker behind this peer can be reached from outside its connection: its machine, its process, its provider session and the socket other sessions message it on; see PeerAddress. Derived when the peer attached, never announced by hand. Absent for a client that exposes none of it.
announcement one of 2 no The most recently made of Peer::claims, for a reader that wants one line rather than all of them. Derived from claims and never a second account of it: a peer holding one claim reports the same thing in both, which is what every peer that does not name its claims reports.
attached boolean yes Whether the session is still attached. A peer that announced something and then went away is kept and listed with attached: false: what it said it was working on outlives the connection that said it, because the work does.
checkout one of 2 no Which checkout of the repository this peer is attached to; see PeerCheckout.

The board itself never sets it — a board does not know where it is — and it is stamped by the reader that gathered this peer, which is the one thing that knows which checkout's server it asked.

claims array no Every claim this peer holds, in name order, the unnamed one first. Empty for a peer that has announced nothing.

One connection may be doing several things at once — a session that fans work out to subagents shares its MCP session with all of them — and before this field the board could hold only the last thing said, so each announcement silently erased the one before it and the other peers stopped seeing the rest of the scope.

client reference yes The client behind it.
connected_at string yes When it attached, RFC 3339, UTC.
id reference yes p1, p2, ...
last_seen_seconds_ago integer yes Seconds since its last message.
overlaps array no Every other peer whose claimed scope meets this one. Empty is the ordinary case.
released array no Paths the claim this one replaced had, and this one does not.

Announcing under a name a peer already used replaces that claim, which is what an update is. When the replacement is narrower, the peer has just stopped claiming ground it held a moment ago, and the other peers stop being warned off it. That is occasionally what was meant and is otherwise a mistake nobody would ever see, so it is said here, at the moment it happens, rather than left to be discovered in a collision later. Empty is the ordinary case.

transport reference yes How it is attached.
JSON Schema
{
  "$defs": {
    "Announcement": {
      "description": "One claim a peer has made: what it is working on, and where.\n\nA peer may hold several. The `name` is what tells them apart and what makes announcing\nagain an update rather than a second claim: announce under the same name and the claim\nis replaced, announce under a new one and the peer now holds both. A peer that never\nnames anything holds exactly one claim, the unnamed one, which is what a single session\nannouncing about itself has always had.",
      "properties": {
        "at": {
          "description": "When it was announced, RFC 3339, UTC.",
          "type": "string"
        },
        "intent": {
          "description": "One line: the task or intent, in the peer's words.",
          "type": "string"
        },
        "name": {
          "description": "What this claim is called, when the peer named it. Absent for a peer's unnamed claim.",
          "type": [
            "string",
            "null"
          ]
        },
        "scope": {
          "description": "Repository-relative paths the peer expects to touch; informational, never enforced here.",
          "items": {
            "type": "string"
          },
          "type": "array"
        }
      },
      "required": [
        "intent",
        "scope",
        "at"
      ],
      "type": "object"
    },
    "ClientInfo": {
      "description": "What a client said about itself in `initialize`.",
      "properties": {
        "name": {
          "description": "`clientInfo.name`: `claude-code`, `codex`, `gemini-cli`, whatever the client sends.",
          "type": "string"
        },
        "title": {
          "description": "`clientInfo.title`, when the client sends one.",
          "type": [
            "string",
            "null"
          ]
        },
        "version": {
          "description": "`clientInfo.version`.",
          "type": "string"
        }
      },
      "required": [
        "name",
        "version"
      ],
      "type": "object"
    },
    "Overlap": {
      "description": "Two peers that claimed the same ground.\n\nReported when the second of them announces, so that a collision is known at the moment\nit is created rather than discovered afterwards in the history of a branch.",
      "properties": {
        "attached": {
          "description": "Whether that peer is still attached. An overlap with a departed peer is a weaker\nsignal than one with a live session, and the reader is told which it is.",
          "type": "boolean"
        },
        "checkout": {
          "anyOf": [
            {
              "$ref": "#/$defs/PeerCheckout"
            },
            {
              "type": "null"
            }
          ],
          "description": "Which checkout the other peer is attached to, when the reader knew.\n\nThe overlap that matters most is the one nobody could see before: two workers in\ntwo worktrees of one repository, each reading a board that held only itself, both\nclaiming `apps/majordomus-cli/src`. Naming the checkout is what turns \"p1 is on\nyour ground\" into something a reader can act on when `p1` is not on this board."
        },
        "intent": {
          "description": "What it said it was doing.",
          "type": "string"
        },
        "paths": {
          "description": "The claims that meet: one line per pair, `yours` and `theirs`.",
          "items": {
            "$ref": "#/$defs/OverlapPath"
          },
          "type": "array"
        },
        "peer": {
          "$ref": "#/$defs/PeerId",
          "description": "The other peer."
        }
      },
      "required": [
        "peer",
        "attached",
        "intent",
        "paths"
      ],
      "type": "object"
    },
    "OverlapPath": {
      "description": "One pair of claims that contain one another.",
      "properties": {
        "theirs": {
          "description": "The path the other peer claimed.",
          "type": "string"
        },
        "yours": {
          "description": "The path the announcing peer claimed.",
          "type": "string"
        }
      },
      "required": [
        "yours",
        "theirs"
      ],
      "type": "object"
    },
    "PeerAddress": {
      "description": "Where the worker behind a peer can be reached from outside its MCP connection.\n\nA board row named a client and a version, and nothing a person or another session could\nact on: on 2026-10-22 the session running a 33-minute remote `cargo test --doc` could be\nfound only by walking an ssh client's parent chain to a provider pid, its messaging\nsocket, and a start time matched by hand. Every one of those facts is in the environment\nthe provider hands the processes it starts — the MCP server among them — so the address\nis read from there when the peer attaches, and never asked of the model.\n\nThe provider's messaging *token* is a credential and is never read: the address is where\nto knock, not the key.\n\n```\nuse majordomus_cli::peers::PeerAddress;\n// a client that exposes nothing still has a machine and a parent process\nlet bare = PeerAddress::from_env(|_| None, Some(4242), \"lundra\").unwrap();\nassert_eq!((bare.host.as_str(), bare.pid), (\"lundra\", Some(4242)));\nassert!(bare.provider.is_none() && bare.mesh_session().is_none());\n```",
      "properties": {
        "host": {
          "description": "The machine the worker runs on.",
          "type": "string"
        },
        "pid": {
          "description": "The worker's own process: the provider's pid, not the MCP bridge it started.",
          "format": "uint32",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "provider": {
          "description": "The provider whose session this is (`claude-code`), when its environment says so.",
          "type": [
            "string",
            "null"
          ]
        },
        "session": {
          "description": "The provider's id for the session, the one its lifecycle hooks are given.",
          "type": [
            "string",
            "null"
          ]
        },
        "socket": {
          "description": "The socket other sessions on the same machine message this one through.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "host"
      ],
      "type": "object"
    },
    "PeerCheckout": {
      "description": "Which checkout of the repository a peer is attached to.\n\nA board is one process's memory and a server serves one checkout, so a peer read from\na sibling checkout's server and a peer of this one are otherwise indistinguishable —\nand worse than indistinguishable, because the ids collide: `p1` is the first session\nof *every* board, so a repository-wide listing without this field would hold several\npeers called `p1` and no way to tell them apart. This is the qualification. The\nworktree path is the durable half of a worker's identity: the peer id is a position on\none board and is handed out again after a reconnect (`p3` this morning, `p1` after the\nbridge re-attached), while the checkout a worker is working in outlives its connection.\n\nAbsent on a peer a server reports about its own board without being asked which\ncheckout that is — `peers.announce` answers about the announcing session and the\nanswer is this server's by construction. The repository-wide listing stamps every\npeer it gathers, its own included, so that no entry of that answer is unqualified.\n\n```\nuse majordomus_cli::peers::PeerCheckout;\nlet c = PeerCheckout {\n    id: \"b8293f11\".into(),\n    worktree: \"/repo-wt/feature/x\".into(),\n    branch: Some(\"feature/x\".into()),\n    this_checkout: false,\n};\nlet v = serde_json::to_value(&c).unwrap();\nassert_eq!(v[\"branch\"], \"feature/x\");\nassert_eq!(v[\"this_checkout\"], false);\n```",
      "properties": {
        "branch": {
          "description": "The branch checked out there, when it could be read.",
          "type": [
            "string",
            "null"
          ]
        },
        "id": {
          "description": "The checkout's identity digest, the same value `server.status` reports as\n`checkout_id`. One per checkout, never a path, and stable across a restart.",
          "type": "string"
        },
        "this_checkout": {
          "description": "Whether this is the checkout of the server answering the call.",
          "type": "boolean"
        },
        "worktree": {
          "description": "Where the checkout is. The one thing on this board that a human can act on\ndirectly, and the one thing that survives a reconnect.",
          "type": "string"
        }
      },
      "required": [
        "id",
        "worktree",
        "this_checkout"
      ],
      "type": "object"
    },
    "PeerId": {
      "description": "A peer's identity for the life of the server: `p1`, `p2`, ... in attachment order.\n`p1` is the session that started the server.",
      "type": "string"
    },
    "Transport": {
      "description": "How a peer reached the server.",
      "oneOf": [
        {
          "const": "stdio",
          "description": "The owner's own client, on the process's stdin and stdout.",
          "type": "string"
        },
        {
          "const": "http",
          "description": "MCP over HTTP at `/mcp`: a `majordomus mcp` bridge, or a client speaking it directly.",
          "type": "string"
        }
      ]
    }
  },
  "description": "What `announce` answers: the peer as recorded, and who else is on that ground.",
  "properties": {
    "address": {
      "anyOf": [
        {
          "$ref": "#/$defs/PeerAddress"
        },
        {
          "type": "null"
        }
      ],
      "description": "Where the worker behind this peer can be reached from outside its connection: its\nmachine, its process, its provider session and the socket other sessions message it\non; see `PeerAddress`. Derived when the peer attached, never announced by hand.\nAbsent for a client that exposes none of it."
    },
    "announcement": {
      "anyOf": [
        {
          "$ref": "#/$defs/Announcement"
        },
        {
          "type": "null"
        }
      ],
      "description": "The most recently made of `Peer::claims`, for a reader that wants one line rather\nthan all of them. Derived from `claims` and never a second account of it: a peer\nholding one claim reports the same thing in both, which is what every peer that does\nnot name its claims reports."
    },
    "attached": {
      "description": "Whether the session is still attached. A peer that announced something and then\nwent away is kept and listed with `attached: false`: what it said it was working on\noutlives the connection that said it, because the work does.",
      "type": "boolean"
    },
    "checkout": {
      "anyOf": [
        {
          "$ref": "#/$defs/PeerCheckout"
        },
        {
          "type": "null"
        }
      ],
      "description": "Which checkout of the repository this peer is attached to; see `PeerCheckout`.\n\nThe board itself never sets it — a board does not know where it is — and it is\nstamped by the reader that gathered this peer, which is the one thing that knows\nwhich checkout's server it asked."
    },
    "claims": {
      "description": "Every claim this peer holds, in name order, the unnamed one first. Empty for a peer\nthat has announced nothing.\n\nOne connection may be doing several things at once — a session that fans work out to\nsubagents shares its MCP session with all of them — and before this field the board\ncould hold only the last thing said, so each announcement silently erased the one\nbefore it and the other peers stopped seeing the rest of the scope.",
      "items": {
        "$ref": "#/$defs/Announcement"
      },
      "type": "array"
    },
    "client": {
      "$ref": "#/$defs/ClientInfo",
      "description": "The client behind it."
    },
    "connected_at": {
      "description": "When it attached, RFC 3339, UTC.",
      "type": "string"
    },
    "id": {
      "$ref": "#/$defs/PeerId",
      "description": "`p1`, `p2`, ..."
    },
    "last_seen_seconds_ago": {
      "description": "Seconds since its last message.",
      "format": "uint64",
      "minimum": 0,
      "type": "integer"
    },
    "overlaps": {
      "description": "Every other peer whose claimed scope meets this one. Empty is the ordinary case.",
      "items": {
        "$ref": "#/$defs/Overlap"
      },
      "type": "array"
    },
    "released": {
      "description": "Paths the claim this one replaced had, and this one does not.\n\nAnnouncing under a name a peer already used replaces that claim, which is what an\nupdate is. When the replacement is narrower, the peer has just stopped claiming\nground it held a moment ago, and the other peers stop being warned off it. That is\noccasionally what was meant and is otherwise a mistake nobody would ever see, so it\nis said here, at the moment it happens, rather than left to be discovered in a\ncollision later. Empty is the ordinary case.",
      "items": {
        "type": "string"
      },
      "type": "array"
    },
    "transport": {
      "$ref": "#/$defs/Transport",
      "description": "How it is attached."
    }
  },
  "required": [
    "id",
    "client",
    "transport",
    "connected_at",
    "last_seen_seconds_ago",
    "attached"
  ],
  "title": "Announced",
  "type": "object"
}

Policies

benchmark
required — a target on every transport the exposure declares; the cases are the input type's
cache
disabled — every call runs the handler

Benchmark targets

Derived from the registry for this repository: one requirement per transport, and the cases the input type provides. The whole matrix is on the benchmarks page.

transportstatecasestargets
directcovered1default
mcpcovered1default
httpcovered1default

The moments this answers

Searches the titles and descriptions of every page this site publishes.