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_clickre-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 byx/y.browser_typeacceptsreplace: 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. Thereplaceuses Chromium’s real edit command (the same as Ctrl+A) and still enters as a singleinsertText, 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.