Documentation / Agents

MCP tools

Agents talk to the board through an embedded MCP server — reading is passive, and what creates or destroys asks for consent.

Stellar exposes a local MCP server. It is through it that a running agent sees the board and acts on it. Every tool that touches something you did not ask for goes through consent — in the default flow, a request with a declared reason. Reading is passive and does not ask.

Reading without asking

Tool What it returns
list_cards the open cards on the board: id, kind, provider, cwd
read_card the text scrollback of a terminal card
snapshot an image of a card, of a region or of the whole board
get_page_text the visible text of a browser card’s page
card_status the card’s state — see Reports and signals
read_report the structured report a card sent
read_sticky the text of a note
list_tasks / get_task the recorded tasks, the dependency graph, the transitions
list_sprints the board’s sprints, open and closed
list_connectors the arrows between cards
spawn_lineage who created a card and what it created: reason, origin, task, provider, cwd, depth
concurrency_status how many agents are running, against a cap — advisory, blocks nothing
unreported_work the coverage check: cards that worked and left no report
get_delivery / list_deliveries what state a scheduled delivery ended in (unconfirmed is common — confirm with read_card)
list_reservations this card’s queue of reserved tasks (not delivered yet), in order
list_prototypes the local http baseUrl for the board’s prototypes/ and the declared presets
board_mode is the board in autonomous mode?
build_identity mode, commit, version and protocol of the running app
browser_query / browser_snapshot one page element / the interactive elements
browser_console / browser_network the page console / the HTTP requests it made
reach_from_hunks who else, in the same repository, references what a diff touched
reach_across_literals where the same literals of a diff appear in another repository
suggest_qa_scope the batch of commits, files and areas touched, and a generated QA checklist

spawn_lineage exists because the connector is only the visual edge: the spawn reason and the chain depth survive a restart in the registry, not in the drawing.

unreported_work does not accuse: it confronts the provider’s own session store with what the app captured, and distinguishes “worked and did not report” from “left no session at all” and from “this provider declares no store” — three different answers, and the last two are nobody’s failure.

Asking first

Tool Risk
spawn_agent creates a terminal card of another provider — human consent, or the autonomous queue
spawn_card creates auxiliary cards (files, changes, note, browser, media) — same gate
open_url opens or navigates a URL in a browser card
open_prototype opens HTML from the board’s prototypes/ (local http — the browser refuses file://) — same gate as open_url
close_card closes ANY card; on a live terminal, kills the process — no undo
delete_card deletes for good. On an already closed card this became a conscious gesture, not a side effect

Closing archives, and only an explicit request deletes — delete_card, or the owner’s action on an archived card. A card from another board only accepts deletion if that board is in autonomous mode: there is no live interface there to ask anyone.

Typing into a card

send_to_card does not go through a modal: it queues text for the destination card and returns right away. What exists in the path is the card’s queue — if the person is in the middle of a line, the delivery waits — and the delivery outcome has states that matter (delivered, parked, unconfirmed, among others). Check with get_delivery and, for proof, read_card. cancel_deliveries collects what has not started being typed yet.

The text goes prefixed with who sent it. In the same call you can link the destination card to a task with linkTaskId + linkRole (implementer or reviewer) — the role is required when the link exists; there is no default.

Structural

create_task, update_task, request_task_status, answer_blocked_task, link_task_card, reorder_reservations, the sprint ones (list_sprints, open_sprint, close_sprint, rename_sprint, delete_sprint), set_connector_kind, set_connector_label, write_sticky, update_card_content, set_sticky_color and set_sticky_mode change the record of the work, not a process. They are the mechanics of The Queue and of Dependencies and sprints.

request_task_status exists because not every agent can judge: whoever is linked as a task’s implementer does not write done/failed on it — they ask, and the human decides (or a linked reviewer, when the task declares it wants review).

answer_blocked_task answers the structured question of a task in blocked (read it on get_task → blockedQuestion): clears the question, returns status to pending, and delivers the answer into the task’s card. Without an answer, blocked does not unblock by itself.

link_task_card with mode: "reserve" puts the task on the card’s queue without delivering the contract yet — list_reservations / reorder_reservations read and reorder that queue. Territory accepts shared:<path> (<note>); overrideTerritory (orchestrator only, on deliver) passes a territory conflict with a reason recorded on the trail.

Heavy command under lock

run_locked (and the mirror acbridge gate-lock --) runs a command of yours under the same lock the app uses for gates: repo (default) serializes against other heavy work on the same repository; machine uses one global lock for performance measurements. It is not a sandbox — only the queue. Declared task gates run in an isolated subprocess; a tool outside the repo goes in gateToolPaths on the board context.

Browser

A set of its own tools acts inside an already open browser card: click, type, scroll, evaluate JavaScript, read console, read network, wait for an element and take a snapshot. The nine, by name: browser_click, browser_type, browser_scroll, browser_query, browser_snapshot, browser_console, browser_network, browser_wait_for and browser_eval. It is what lets an agent check what its own change did on a real page.

Two of these tools refuse instead of doing it halfway:

  • browser_click re-reads the page immediately before clicking and refuses when it cannot aim — the target moved, something started covering it, or the point fell outside the visible area. A coordinate describes what was there at that instant, not the identity of an element; that is why the reliable path is to point by selector or by the snapshot reference, and not by x/y.
  • browser_type accepts replace: true, which clears before typing. Without it, it adds to what was already there — that is how a field became “Idy PlatformIdy Platform” with the agent thinking it had typed once. The replace uses Chromium’s real edit command (the same as Ctrl+A) and still enters as a single insertText, so as not to break composition input. It is refused, naming the reason, when the target is not an editable field — clearing a non-editable target would select the whole page.

browser_eval is the exception in tone, and it is worth saying why: it runs JavaScript in the page’s real context and returns the result, so it carries the power of the DevTools console — including reading the cookies and the sessionStorage of a logged-in page. The other browser_* touch the page; this one reads what the page can read. Use it against pages and data you would not mind a human colleague opening in the console.

Not every provider exposes the catalog

The app derives, per provider, whether the agent receives the declared tools, whether it only gets the CLI hint through the scrollback, whether it reaches neither, or whether it does not even apply (a plain shell). That is why the right briefing is “report with the tool if it is in your catalog; otherwise use the CLI” — not a blind order down a single path. See acbridge (CLI), including the known limitation of cline cards.