Use it from an agent
Introduction
Section titled “Introduction”Brain serves one MCP endpoint, POST /mcp, over Streamable HTTP. An agent holding a Brain token gets six memory tools, six workflow tools more when the token holds the connectors grant, and five configuration tools more when it holds the configure grant. The token carries grants per memory, and an agent never reaches more than the person who owns the token.
Create a token
Section titled “Create a token”Open the web app, or call POST /api/tokens while signed in, and name the token with one grant per memory:
{ "name": "Claude Code", "grants": [ { "memoryId": "<graph memory id>", "access": "read" }, { "memoryId": "<collection memory id>", "access": "write" }, { "memoryId": "<personal memory id>", "access": "remember" } ]}The response includes secret once, starting with pb_. Only its SHA-256 is stored, so a lost secret cannot be recovered; create another and delete the old one with DELETE /api/tokens/:id.
| Access | Allows | Memory kinds |
|---|---|---|
read |
memory_list, memory_search, memory_get |
any |
write |
also memory_add_source |
graph, collection |
remember |
memory_remember, memory_forget |
personal only |
connectors |
the six workflow_* tools, for connectors that write into this memory |
graph, collection |
configure |
the five memory_config_*, memory_labels_apply, memory_members_set and memory_relabel tools |
graph, collection |
connectors and configure are the management grants. Each may carry until, the time the grant ends; without it the grant lasts until you remove it.
{ "memoryId": "<graph memory id>", "access": "configure", "until": "2026-11-14T18:00:00Z" }Change or end a token
Section titled “Change or end a token”Open Agents & tokens and choose Edit on a token, or call PATCH /api/tokens/:id while signed in. The name and the grants change under the same checks as creation; the secret stays. Only the token’s owner can do it, and only from the web app: a pb_ token, with any grant, is refused.
{ "grants": [ { "memoryId": "<graph memory id>", "access": "read" }, { "memoryId": "<graph memory id>", "access": "configure", "until": "2026-11-14T18:00:00Z" } ]}Grants are read at every request, so a change applies from the agent’s next call. To end a job without rotating the secret, remove the grant or let its until pass: the token keeps the access it had besides.
A management grant is live only while three things hold: the token holds it, it has not reached until, and the token’s owner is still an admin of the memory. A call made when one fails is refused with forbidden.
Connect Claude Code
Section titled “Connect Claude Code”claude mcp add --transport http brain https://brain.example.com/mcp \ --header "Authorization: Bearer pb_..."Any MCP client that speaks Streamable HTTP and sends an Authorization: Bearer header works the same way. The endpoint is stateless: every request is its own session, and GET and DELETE on /mcp answer 405.
A pb_ token is accepted only on /mcp. Every other route needs a signed-in session.
The tools
Section titled “The tools”| Tool | Input | Returns |
|---|---|---|
memory_list |
none | {memories: [{slug, kind, name, description, access}]} for the memories the token may use. Call it first. |
memory_search |
memory (slug), query, limit (1 to 50, default 8) |
{passages: [{text, sourceId, sourceTitle, locator, score}]}. In a personal memory it searches the facts. |
memory_get |
memory (slug), id of a page, source or fact from a search |
The whole document: a page, a source with its text, or a fact. |
memory_remember |
text, one standalone sentence |
The fact, with operation: add, update, delete or noop. Needs remember. |
memory_forget |
id of a fact |
{forgotten: true}. Needs remember. |
memory_add_source |
memory (slug), title, text, labels?, externalId? |
The created source. Needs write on that memory. |
A memory the token may only configure is listed too, with access: "configure", so the agent learns its slug.
A failed call returns an MCP result with isError: true and a JSON body {error: {code, message}}, where code is invalid, not_found, forbidden or conflict.
Managing connectors from an agent
Section titled “Managing connectors from an agent”With the connectors grant the endpoint also lists workflow_list, workflow_run, workflow_kinds, workflow_get, workflow_validate and workflow_save. They have the same names, inputs and answers as the workflow tools of the desktop app, so the desktop’s workflow skill describes them as they are. Any MCP client can use them, such as Claude Desktop, Claude Code or Cursor, and so can the Pilotoo desktop app: two settings in its .env give its agents these tools as brain_workflow_save and the rest (Brain from the desktop app). Without a live grant a call is refused with forbidden: “This token cannot manage connectors. An admin can allow it under Tokens.”
The token acts as its owner, and every connector it touches belongs to that owner. It sees, reads, saves, turns on and runs only the connectors whose steps all write to memories the token holds connectors on. A connector that writes anywhere else is invisible to it, and saving one that does, or one whose step names no memory or picks it at run time, is refused with the issue. workflow_kinds offers only the granted memories for a Brain step.
| Tool | In Brain |
|---|---|
workflow_list |
The connectors, with enabled. |
workflow_get |
One connector: nodes, edges and enabled. |
workflow_kinds |
The kinds Brain offers to connectors, and the memories the token may write to. |
workflow_validate |
The engine’s issues plus Brain’s, and memory_not_granted for a memory outside the grants. |
workflow_save |
Creates a connector, or replaces id. It starts off; pass enabled: true to turn it on, which needs a valid workflow. |
workflow_run |
Runs it now and answers {output: {added, unchanged, skipped, items}, nodes}; a failed run answers Not done: with the failed step. |
The registry of each memory shows what the agent changed, under the token name.
Setting up a memory from an agent
Section titled “Setting up a memory from an agent”With the configure grant on a graph or collection memory, an agent sets the memory up from a description of what you need. The five tools address everything by name, so the agent needs no ids, and each runs as the token’s owner through the same rules as the web app.
| Tool | Input | What it does |
|---|---|---|
memory_config_get |
memory |
Reads the name, description, language, labeling, schema and models, the catalogue of models with their availability, the label families with their labels and meanings, the roles with what they grant, the members with their roles, and counts: passages without labels, Review items waiting, vectors pending. |
memory_config_set |
memory, and any of name, description, language, labeling, schema (with retype for a page type it drops), models |
Changes only what differs. The reply says what changed and what follows, such as how many passages will be embedded again. |
memory_labels_apply |
memory, families: [{name, labels: [{name, meaning?}]}], roles: [{name, grants: [{family, mode, labels?}]}], remove? |
Creates the families, labels and roles that are missing and updates meanings and grants. mode is none, all or some with labels naming labels of that family. It deletes only what remove lists: {families?, labels?: [{family?, name}], roles?}. |
memory_members_set |
memory, members: [{email, roles}] |
Sets the roles of people who are already members, by email and role name. |
memory_relabel |
memory, source? |
Has the model judge the passages again, for the whole memory or for one document named by title or id. |
Every tool answers the same way when you send it twice: the second reply reports changed: false and nothing is written. A call is all or nothing, and every name in it is checked before anything is written: a role that names a label that is not defined is refused with the label’s name. When a label name exists in two families, remove asks for the family.
For example, to give a company memory one family, “Area”, with three labels and four roles, the agent sends:
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "memory_labels_apply", "arguments": { "memory": "company", "families": [ { "name": "Area", "labels": [ { "name": "Sales", "meaning": "Selling and accounts" }, { "name": "Support", "meaning": "Helping customers" }, { "name": "Engineering", "meaning": "Building the product" } ] } ], "roles": [ { "name": "Sales team", "grants": [{ "family": "Area", "mode": "some", "labels": ["Sales"] }] }, { "name": "Support team", "grants": [{ "family": "Area", "mode": "some", "labels": ["Support"] }] }, { "name": "Product team", "grants": [{ "family": "Area", "mode": "some", "labels": ["Engineering", "Support"] }] }, { "name": "Everyone in Area", "grants": [{ "family": "Area", "mode": "all" }] } ] } }}The reply lists what was created and what to do next:
{ "changed": true, "created": ["family Area", "label Area / Sales", "label Area / Support", "label Area / Engineering", "role Sales team", "role Support team", "role Product team", "role Everyone in Area"], "updated": [], "removed": [], "follows": ["Run memory_relabel so the existing passages are judged with the new labels"]}Then memory_members_set gives people their roles, and memory_relabel labels the passages already in the memory.
No token can create, change or delete tokens or worker tokens, delete a memory, add or remove members, or change who is admin or owner, whatever it holds. Those stay in the web app. Every change an agent makes is in the memory’s audit log under the token’s name.
Writes from agents to a personal memory
Section titled “Writes from agents to a personal memory”A personal memory has a write policy, set with PUT /api/memories/:mid/write-policy:
auto: facts agents send are saved at once (the default).ask: facts wait for the owner to keep them; they come back withpending: true.off:memory_rememberis refused.
Brain compares each new fact with the ones it already has. operation tells the agent what happened: add saved a new fact, update rewrote an older one, delete retired an older fact the new one contradicts, and noop means it was already known. Under ask, an update or delete also waits until the owner keeps the fact.
An agent can add a source to a graph memory, and that source goes through the memory’s review like any other.
Re-sending a document
Section titled “Re-sending a document”Give memory_add_source an externalId, your own stable id for the document, and send it again whenever it changes. The same id with the same text does nothing; with new text it becomes the next version of that source and only what differs is recompiled.
Brain from the desktop app
Section titled “Brain from the desktop app”The desktop app reaches Brain the way it reaches Pilotoo Connect. Create a token under Agents & tokens with write on the memories agents may fill, and the connectors grant to let them manage connectors (configure is for MCP clients that set a memory up; the desktop app does not use it). Add it to ~/.pilotoo/.env and restart the app:
BRAIN_REMOTE_URL=https://brain.example.comBRAIN_REMOTE_TOKEN=pb_...Every tool the token is offered arrives with the prefix brain_: brain_memory_search, brain_memory_add_source, brain_workflow_save and the rest. They are opt-in, as the Connect tools are: an agent gets one only by listing it in its tools.
tools: [brain_memory_search, brain_memory_add_source]With only one of the two settings the app warns at start and offers no Brain tool. When Brain cannot be reached at start, or refuses the token, the app starts anyway and the warning names the cause; the only tool is then brain_status, which tries again and, once Brain answers, registers the others (list them in an agent, or restart the app). A call that fails comes back to the agent as an error naming Brain.
Next steps
Section titled “Next steps”- Run the standalone service to get a
/mcpendpoint. - HTTP API for token routes.