Skip to content

The Five Tools

TapanManu edited this page Sep 8, 2026 · 1 revision

The five tools

Goal of this page: what each operation takes, what it gives back, and why there are only five.

Tool descriptions are re-sent to the model on every turn of every agent. That makes the size of this surface a standing per-turn tax, so it is budgeted rather than designed for completeness. The budget is 600 tokens and a test in the build fails if it is exceeded. Current measured size: 469 tokens.

Two terms used below: compare-and-swap means "write only if the entry is still at the version you last read, otherwise reject" — it replaces locking. Tab-separated values means rows of data under a single header line, which is far cheaper in tokens than JSON for repetitive records.


update_state — write a note

update_state(uri, digest, body | source_path | columns+rows | append,
             expect_version?, sources?, confidence?)
  → {version, digest, warnings}
  • A digest is required. The write is rejected without one.
  • expect_version is the compare-and-swap check. Pass it on every write.
  • Content over 10 KB is automatically stored outside the note.
  • source_path makes the server read and summarize a file, so the content never passes through the agent's context window.
  • Several alternative shapes exist for the body, each cheaper for a particular situation. → Reading and writing cheaply

get_state — read notes

get_state(uris[], mode=digest|fields|table|full|ref,
          fields?, budget_tokens=2000)
  • Batched: pass several addresses in one call.
  • Summary by default. You have to ask for more.
  • Truncates to the budget and reports exactly what it omitted.

list_keys — see what exists

list_keys(topic?, kind?, order?, limit=20, mode=digest|table|ref,
          fields?, budget_tokens=2000)
  • Scoped to the topics the caller's access token permits. There is no global scan, by design.
  • mode=table returns tab-separated rows. Use it for three or more entries.
  • Always pass topic=. An unfiltered listing returns every topic in the workspace.

search_keys — full-text search

search_keys(q, topic?, limit=10) → [{uri, digest, score}]

Searches summaries and contents. Returns addresses and summaries, never full contents. Searching before an expensive derivation is the cheapest win the board offers and the most commonly skipped one — and an empty result is an answer: it means nothing on the board relates to your task.

link_state — record a relationship

link_state(src, rel, dst[])

rel is one of depends_on, derived_from, supersedes, refines, cites, part_of, contradicts. This is how provenance survives a hand-off: the next agent can see that one finding was derived from another, or that two notes disagree.


What is deliberately not a tool

Thing you might expect Why it isn't here
A "hello, catch me up" tool Convention instead: read the well-known address bb://<workspace>/run/state/current. A tool would cost schema tokens on every turn to do what an address already does.
A patch or partial-update tool Re-write with expect_version. append covers the common case of extending a running entry. A dedicated patch tool earns its place only if payload sizes prove it.
A board-health tool blackboard-mcp health from a shell, run by a human. Agents do not need it.
A task queue, a scheduler, locking Deferred on purpose. → Roadmap

What every read returns

{
  "items": [{"uri": "bb://...", "digest": "...", "version": 3, "est_tokens": 180}],
  "spent_tokens": 540,
  "truncated": false,
  "omitted": 0,
  "omitted_uris": [],
  "cursor": 10482
}

spent_tokens, truncated and omitted_uris are the honesty of the budget mechanism: the agent always knows whether it saw everything, and if not, exactly what it did not see.

Clone this wiki locally