MCP tools¶
Hadron exposes around thirty hadron_* tools through the
Model Context Protocol. Any MCP
host (Claude Code, Cursor, the Claude desktop app, …) connected to a
Hadron MCP server can call them to read and write memory, drive
chats, and run actions.
This page is a categorized lookup of the tool surface — name, purpose, required inputs, and what the call returns. For walk-throughs that use these tools end-to-end, see Connecting an MCP host and Adding nodes to a memory.
Connecting¶
The MCP server is shipped as part of hadron-server and registered
in your MCP host's config (.mcp.json, Claude Desktop's settings,
etc.). The fastest way to wire it up in a coding-agent project is
the install script, which
configures .mcp.json and the Spec Kit extension in one step.
Once connected, every tool below is callable as
mcp__hadron__<tool-name> (or under whatever namespace your host
uses).
Active memory¶
Most tree tools take an optional memoryUrn (or a loc that starts
with one). When you don't pass one, they fall back to the active
memory for the session. Bind it once with hadron_set_active_memory and
subsequent calls operate on that memory by default. This is the
recommended pattern — passing memoryUrn on every call is verbose
and error-prone.
If your search returns "No nodes found" repeatedly across queries that should match, treat it as a configuration smell — verify the active memory is bound to the memory you expect rather than assuming the topic isn't covered.
Memory selection¶
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_list_memories |
List every memory accessible to the caller, with URN and access level (read-only or read-write). |
(none) |
hadron_set_active_memory |
Bind a memory as the session default for subsequent calls. | memoryUrn |
hadron_sync_graph |
Trigger a one-shot GitHub source sync for a memory backed by a Git source. | memoryUrn |
The list returned by hadron_list_memories is the authoritative inventory
of where you can read and write — agents are not memories, and apps
are not memories. If a memory you expect isn't in the list, your
identity is missing access to it.
Sessions¶
Sessions are optional but strongly recommended for traceability. Every node created inside a session is attributed to it; ending the session records summary metrics.
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_start_session |
Open a work session and get a session ID for attribution. | description (required); optional type (DEVELOPER/CHATBOT/AUTOMATION/EDGE), repo, branch, prNumber, language, llmModel, parentSessionId, prevSessionId, workerRef (work AS a named Worker), force (take over a WORKER_TAKEN worker — only on the user's explicit override) |
hadron_end_session |
Close a session. Records summary and final token / error counts. | id (required); optional summary, errorCount, turnCount, inputTokens, outputTokens |
hadron_whoami |
Answer "what am I driving right now?" — your own open sessions, each with the worker it is bound to, its App, and when it started. The way to recover a session id you no longer have. | Optional limit (default 10, cap 50), includeEnded (also list recently ended sessions) |
Returns: hadron_start_session returns a session ID and a 6-phase
protocol hint. The protocol's middle phases ("save plan", "update
memory") are bookkeeping markers under
Spec Kit — plan.md
is the saved plan; memory updates flow through hadron_create_node /
hadron_update_node directly. Always close with hadron_end_session.
Both tools are strict: they need App context and fail with
[no_active_app] without it. See MCP App selection.
Attribution and the end gate¶
hadron_start_session stamps the caller's user id on the session.
For an OAuth or PAT caller that is you; for a pure App-key caller it is
null. That stamp is what makes the session yours — it drives the
attributed-user self-read, and it decides who may close the session.
hadron_end_session is gated. Exactly two principals may end a
session through MCP:
- a pure App-key principal, for its own App's sessions, or
- the authenticated user the session is attributed to.
Anyone else gets Forbidden: session "<id>" belongs to a different App or
user. The App branch requires the absence of a user id: when you call
as a user with an active App selected, the selection is a scope, not a
credential — so co-members of one App cannot end each other's sessions.
(The GraphQL endSession applies the same gate, plus a platform-admin
branch that MCP does not expose.)
Ending a session you didn't start is therefore refused rather than silently succeeding. The full read/write table is in the Authorization reference.
Worker binding is on this surface; the rest of coding provenance is not
hadron_start_session accepts workerRef (a worker id from
hadron_cast_worker) to work as that named casting, and force
to take over a worker someone is already driving — binding a taken
worker refuses with WORKER_TAKEN, carrying who last drove it and
when (informed takeover, enforced server-side; retry with
force: true only on the user's explicit override). The remaining
coding-session fields — transcriptPath, host, agentRef — are
set through GraphQL startSession and the
hadron team session
CLI commands. There is no MCP equivalent of updateSession either;
hadron_end_session is the only session write MCP offers.
Recovering after a context compaction
On the MCP surface your working identity lives entirely in your context
window. hadron_start_session hands back the session id and the worker's
boot briefing once, and nothing on the wire carries either — there is no
equivalent of the CLI's .git/hadron-team-session.json. A compaction can
therefore leave an agent still named Iris but no longer working as her.
Two reads put it back, and neither costs a second session or a force: true
takeover of yourself:
hadron_whoami— your open sessions, with the bound worker and App. This is where the session id comes back.hadron_get_worker— the worker's boot briefing, re-rendered live.
Do this as soon as you are unsure, because the worst failure here is silent:
hadron_team_chat_post takes session as an optional argument, so
posting without it does not error — it records the message as authored by
you, the human, instead of by the worker.
Workers — named team members¶
Cast a Worker — a named AI team member ("Iris"): the casting of an
agent already installed in the team App under a name. The agent carries
the persona dressing (personaRole + the personaPrompt template with
{{name}} / {{role}} placeholders); the worker is the local named
identity that does attributable work. The role's name register lives
at the roles:<role> node in the Team Agent's system memory
(data.names, ordered) — readable and editable through the GraphQL
teamRoles / createTeamRole / updateTeamRole surface.
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_cast_worker |
Cast a worker and return its id, name, and resolved boot briefing (print the briefing; pass the id as workerRef when starting sessions). |
Optional role (picks the installed agent by personaRole and names the register), name (explicit — otherwise the register's next free name), agent (Agent URN/ID — must be installed in the App), app (defaults to the active App), teamAgent (otherwise auto-detected: the single installed Agent whose system memory has a roles: branch), promptOverride (per-worker individuality) |
hadron_list_workers |
List the App's staff — name, URN, role, whether each is currently taken (someone is driving it) and whether it is retired. The roster read: use it when you need a teammate's name and don't know it. | Optional includeRetired, limit, offset, app, session |
hadron_get_worker |
Read one worker, including its resolved boot briefing and its raw promptOverride. A pure read — it reserves nothing and mints nothing, so it is the way to recover a briefing you lost. |
workerRef (name, URN, or id — a name needs App context); optional app, session |
hadron_update_worker |
Amend a worker's promptOverride — the per-worker individuality layered over the role agent's shared template. Reaches the worker at its next bind. |
workerRef; promptOverride (replaces the whole override — omit to change nothing, empty string to clear); optional app, session |
All four delegate to the matching GraphQL resolvers, so their authorization is exactly the GraphQL surface's. The reads and the writes are gated differently, and the difference is deliberate: following the roster is not the same act as changing it.
Reading (hadron_list_workers, hadron_get_worker) — an
AppMember of the team App at any role, reader included; an org
member with CONTRIBUTOR+ on the App's org; the owner of a user-owned
App; or the App's own key, which may read its own staff so that
team automation can follow the roster.
Writing (hadron_cast_worker, hadron_update_worker) — narrower:
an org CONTRIBUTOR+, a non-reader AppMember, or the owner of a
user-owned App. A pure App-key principal may not — staffing is a
human act.
The typed refusals differ with the gate. Only the write path can
produce the allocation refusals: names are unique per App,
case-insensitively, forever, so a taken name (including a retired
worker's) is refused with WORKER_NAME_TAKEN, and an exhausted
register is WORKER_REGISTER_EXHAUSTED — add names to the register and
retry. hadron_update_worker additionally refuses a retired worker.
A read never returns any of these; a worker you may not read is
reported as not found.
To rehearse a cast without burning a name, use the GraphQL
castWorkerPreview query: the same resolution and refusals with no
writes (and no reservation — a previewed name may be gone at cast
time, by design). Note it takes the mint gate, not the read gate —
a preview reveals a name the caller could mint anyway.
Reading a worker by name. hadron_get_worker and
hadron_update_worker accept a worker's name ("Iris"), its URN, or its id.
A name is unique only within an App, so the name form needs App context —
the active App, an explicit app, or your session, which names its own App
and so still works right after a reconnect. An id or a URN needs none of that,
since each names exactly one worker and therefore exactly one App. Whichever
form you use, a worker you may not read is reported as not found, the same
answer an absent name gets: the roster is not something to probe by guessing.
Amending a worker. hadron_update_worker changes promptOverride and
nothing else. A worker's name is permanent, and its role and agent are the
casting — a different role is a different casting, not an edit. The override
replaces, so to extend one, read it with hadron_get_worker and amend the
text shown under Prompt override — not the composed boot briefing printed
below it, which already includes the role agent's shared template. A retired
worker is refused: an override is delivered at bind time, and a retired worker
takes no new bindings.
Team chat¶
Post to and read the team App's group chat — one well-known chat per
team App, living at chats:team in the Team Agent's shared app memory
and bootstrapped on the first post. Message ordering is a
server-assigned seq (atomic, unique per chat), so readers poll with a
watermark instead of comparing timestamps.
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_team_chat_post |
Post a message. Mention teammates as @worker-name / @handle (a multiword name by its slug, e.g. @mary-jane) — mentions are extracted server-side into the message envelope. Returns the posted message's seq. |
body (required, max 65 536 chars); optional replyToSeq (reply to an earlier message — wires a replies-to edge), session (a session id — the message is authored by that session's bound Worker; the session must belong to this App, be active, and be yours), app (App URN/ID — defaults to the active App) |
hadron_team_chat_read |
Read the chat, oldest first by seq, with the total count. |
Optional sinceSeq (only messages with seq strictly greater — pass the last seq you have seen), mentions (filter to messages mentioning a worker or user; the ref must be on the App's roster), limit / offset, app |
Both delegate to the GraphQL operations (createTeamChatMessage /
teamChatMessages), so authorization and typed refusals are identical:
any AppMember of the team App (every member of the host is a
participant), an org CONTRIBUTOR+, or a user-owned App's owner may post
and read; a pure App-key principal cannot post but may read its own
App's chat. Posting through a session records the driving session in
the envelope, so an agent message is attributable to the human behind
it.
Team worklog¶
Record externally visible work milestones — a PR opened, a branch pushed, an issue closed — against the session that did the work, and query them back. The worklog is the authoritative join between a work artifact and the sessions that produced it, which is what turns "which session wrote this PR?" into one lookup. It lives in the Team Agent's shared app memory alongside the chat, and is bootstrapped on the first record.
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_record_work |
Record one milestone. Echoes back the stored record — including the canonical ref it normalized to, which is worth reading if you passed a URL. |
session (required — the session id the work is attributed to, which must belong to this App), tool (required, e.g. github), kind (required: pr | issue | commit | branch | repo), ref (required — any accepted spelling, see below), action (required — a free verb: opened, merged, reviewed, closed, pushed, …); optional detail (a JSON bag of display extras such as a title or URL — stored, never filtered on), app (App URN/ID — defaults to the active App) |
hadron_team_work_items |
The provenance query. Newest first, with the total count. Each item carries its sessionId — read that session for its transcriptPath. |
All optional: ref (in any accepted spelling — it is normalized before matching), session, tool, kind, limit (default 50, cap 200), offset, app |
Both delegate to the GraphQL operations (recordTeamWork /
teamWorkItems), so authorization and typed refusals are identical.
Recording passes two independent gates, and it's worth keeping them
apart:
| Gate | What it decides |
|---|---|
| Team membership | Whether you may touch this App's worklog at all. Any AppMember of the team App (at any role), an org CONTRIBUTOR+, or a user-owned App's owner qualifies. |
| Session write access | Which sessions you may record against — the session must be one you are attributed to (or you are a platform admin). |
The second gate is the one that surprises people: being on the team
does not let you record work against a teammate's session. Reading is
team-wide, but a record is a claim about who did what, so it stays with
the person the session belongs to. Attempting it is FORBIDDEN — "You
cannot record work against a session you are not attributed to."
A pure App-key principal may read its own App's worklog but may not record — worker work has a human driver by construction, and App-key automation's platform activity is already covered by usage events. An impersonation session may read (bounded to the support org) and never records.
Four behaviours are worth knowing before you write a client:
- Ended sessions are accepted. Late attribution is the whole point: a merge lands days after the session that opened the PR has ended. (This differs from team chat, which requires a live session to author a message.)
- Records are append-only. There is no update and no delete — correct a wrong record by recording a newer one. That ledger posture is what provenance rests on.
kind: pralso refreshes the session's displayed PR number. That field is a latest-wins display convenience; the worklog record is the actual provenance.- Only external milestones belong here. The litmus is "would the platform otherwise have no record?" Platform calls are already usage events, and an App's declared capabilities live on the App.
Typed refusals: WORK_REF_INVALID, SESSION_NOT_FOUND,
SESSION_NOT_IN_APP, SESSION_WORKER_NOT_IN_APP (the session's bound
Worker belongs to a different App — a record is never attributed across
the team boundary), WORKER_RETIRED (a retired worker takes no new
records; its history stands, its authorship ended), TEAM_AGENT_NOT_FOUND
/ TEAM_AGENT_AMBIGUOUS (the first-write bootstrap couldn't identify the
Team Agent), APP_UNINSTALLED, FORBIDDEN, BAD_USER_INPUT. A session
with no Worker bound records under the attributed user's handle.
Work refs — one spelling per artifact¶
The worklog answers its question by equality lookup on ref, so
every writer and reader has to agree on one spelling per artifact. The
server accepts the spellings people actually paste and stores exactly
one canonical form. This grammar is a stable contract — CLI help text
and portal placeholders restate it.
Canonical spellings (tool github), one per kind:
| Kind | Canonical form | Notes |
|---|---|---|
pr, issue |
owner/repo#371 |
PRs and issues share GitHub's number space, so they share a spelling — a lookup by o/r#371 finds both. |
commit |
owner/repo@a1b2c3d |
SHA lowercased, 7–40 hex chars, stored at the length you gave. |
branch |
owner/repo:feat/x |
Branch case is preserved — git refnames are case-sensitive. |
repo |
owner/repo |
owner and repo are always lowercased, since GitHub treats them
case-insensitively.
Accepted inputs, all normalized to the above:
- Any canonical form (normalization is idempotent).
- Web URLs, with or without scheme or
www., query and fragment stripped:https://github.com/o/r/pull/371,.../issues/42,.../commit/<sha>,.../tree/feat/x. Trailing segments after a number or SHA (/files,/checks) are dropped./pull/Nand/issues/Nare each accepted for bothprandissue— GitHub redirects between them and the canonical spelling is identical, so refusing one would manufacture a failure for nothing. Everything after/tree/is the branch, because branch names contain slashes. - SSH remotes —
git@github.com:o/r.git→o/r. A.gitsuffix on a repo path is stripped.
A bare number is refused
371 and #371 fail with WORK_REF_INVALID. Inferring the repo
from a git remote is client-inherent context — the server never
guesses a repo. This is a deliberate split, not an omission: the
hadron CLI does accept a bare number and qualifies it from the
session's recorded repo before sending a canonical ref.
Other tools. The grammar above is github's. Any other tool has
no defined grammar yet: its refs are stored verbatim (trimmed) and
matched byte-for-byte. Reads honour the split — a query with
tool: 'github' matches canonical spellings, another tool matches that
tool's stored spelling, and a query with no tool matches either. A
read never fails on an unrecognised spelling; it just returns an empty
page.
Browse nodes¶
Read-only node lookups. None of these mutate anything.
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_find_nodes |
Relevance-ranked node search. mode: keyword (default, stemmed full-text), vector/hybrid (semantic, needs the memory's vector index), or regex. Also filters structurally by objectType and a where predicate. |
query (required — use "*" or "" for survey/list-all mode); optional mode, parentRel, memoryUrn, memoryUrns, allMemories, objectType, where, isRunnable, brief, limit, minScore, expand, granularity |
hadron_get_node |
Read a single node by loc path or full URN. |
loc |
hadron_get_next_node |
Read the next sibling under a parent, ordered by seq. Used to walk a conversation flow stage by stage. |
parent; optional current |
hadron_list_nodes |
Browse the tree under a parent — names + descriptions only, optionally to a depth. | optional parentRel, memoryUrn, depth |
hadron_find_nodes takes a brief: true flag that returns name, URN,
description, and edges only — useful for triage before deciding
which nodes to read in full. hadron_get_node returns the full content
including frontmatter, edges, properties, and data.
On a memory with a structured schema, narrow to a
collection with objectType and filter by exact field values with the where
predicate — where composes with every mode (it filters, then the mode
ranks the survivors). Ordering by a property (sortProperty) is not
exposed on this tool — use GraphQL findNodes or the CLI's --sort-property
to sort by a property; from MCP, sort client-side. See
Query nodes by their properties.
Mutate nodes¶
All require write access to the target memory.
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_create_node |
Create or upsert a node. The node type defaults to info; pass parent (a parent-type container) for sections. |
loc, name; optional nodeType, objectType, description, content, seq, tags, properties, data, edges, memoryId, llmModel, aiAgent |
hadron_create_parent_node |
Create a section/category container node. Convenience wrapper that pins nodeType: parent. |
loc, name; optional description, seq, tags |
hadron_update_node |
Partially update an existing node — only the fields you pass change. The data argument replaces the whole data bag; use hadron_update_node_data to merge instead. |
loc; any subset of name, nodeType, objectType, description, content, seq, tags, properties, data, edges, llmModel, aiAgent, plus reason (stored in revision history) |
hadron_update_node_data |
Merge a JSON object into a node's data bag, preserving keys you don't mention. The patch's top-level keys overwrite existing ones; other keys stay; a node with no data takes the patch as-is. Shallow — a nested object value is replaced wholesale, not deep-merged. Patch must be an object (arrays, scalars, and null are rejected). |
nodeUrn (URN or ID), data (the object to merge) |
hadron_create_edge |
Add a single outgoing edge from one node to another. Non-destructive (other edges on the source are preserved), idempotent on the edge loc. Cross-memory edges allowed if the caller can read the target memory. |
sourceUrn (fully-qualified node URN), targetUrn (URN or ID); optional name, loc, description, isRunnable |
hadron_move_node |
Move a node with its whole subtree, within a memory or across memories. The node keeps its stable id, so every edge reference stays valid, and it keeps its name — a move changes the address, not the display title. |
urn (current), targetUrn (destination; a different memory in the target URN performs a cross-memory move); optional overwrite, renameToLeaf (default false — pass true to also overwrite name with the target loc's last atom) |
hadron_delete_node |
Delete a node. Soft-delete by default (sets deletedAt); pass hard: true for a row delete. Refuses with descendants unless recursive: true. Hard-delete refuses cross-memory edges unless cascadeCrossMemoryEdges: true. |
urn (fully-qualified); optional hard, recursive, cascadeCrossMemoryEdges |
Replacing vs. merging data. hadron_create_node and
hadron_update_node write the data bag wholesale — the object
you pass becomes the node's entire data. hadron_update_node_data
merges instead: it overlays your patch's top-level keys onto the
existing data (patch wins on collision) and leaves the rest intact.
Reach for it when you want to set one or two keys without re-sending
the whole bag. The replace and merge tools cross-reference each other
so an agent picks the right one. The merge honors the same write-permission,
encryption, and revision-history invariants as a normal write.
edges on hadron_create_node / hadron_update_node is an array of edge objects.
Each edge object can include targetId (or loc/URN), plus optional
name, description, loc, and isRunnable. Passing edges
replaces the source node's full edge set. Use hadron_create_edge when
you want to append a single edge without touching the rest. Targets
can be loc paths or full URNs. Adding an edge to a missing target
does not fail at write time; hadron_validate will flag it later.
objectType on hadron_create_node / hadron_update_node tags a node as a
member of a schema collection (e.g. competitor),
orthogonal to nodeType. On a memory that has a schema, the write is
validated against the declared collection — a violation is returned as a
Schema violation: error. On an unschema'd memory objectType is free-form.
What a move does to name. Nothing, by default. A node's name is its
human-authored display title and survives a move, whether you arrive through
hadron_move_node or the GraphQL moveNode mutation. Pass renameToLeaf: true
to also re-derive the name from the destination loc's last atom
(services:db-helper → db-helper); that overwrites the existing title, so
pass it only when the name is meant to track the loc. Descendants' names are
never rewritten either way — only their loc prefix changes.
Changed in hadron-server #1017
Before that fix the MCP tool rewrote name on every move, silently and
with no way to decline, while moveNode did not. If you carry a habit of
re-setting the name after each move, you can drop it — doing so now
overwrites a title the platform just preserved.
Moving a subtree across memories. hadron_move_node relocates a node
together with its entire loc-subtree — every descendant whose loc is
prefixed by <loc>: moves too, and their URNs follow the parent. When
targetUrn names a different memory, the whole subtree relocates there: the
moved nodes carry their embeddings (re-indexed under the destination memory's
policy), and outgoing edges whose source moved are rewritten into the
destination. An edge to a node left behind becomes a cross-memory edge,
whose far endpoint is access-gated on read (so a reader who can't see the other
memory gets null for that edge's source/target). Cross-memory moves are
refused (a BAD_USER_INPUT-class error) for a documented safe subset:
- either the source or the destination memory is encrypted,
- either the source or the destination memory is Git-backed (the move isn't mirrored to the repo),
- the subtree contains a chat-root node (its
data.scopeties it to the memory class), or - a moved node would violate the destination memory's schema.
A collision at the target loc (or any descendant slot) refuses the move with
URN_ALREADY_EXISTS unless you pass overwrite: true (which soft-deletes the
explicitly-named target first). A same-memory move leaves existing edge locs
untouched.
Object store¶
The object-store tools are the record-oriented surface over
structured storage: an object is a node with an
objectType, presented as a flat record { id, type, ...fields }. id and
type are the reserved envelope; loc/name are auto-derived and hidden.
This is the friendly path for typed records — reach for it instead of the
node tools when you're doing CRUD on collection members. See
Object store API for the full contract.
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_create_object |
Create a record in a collection. Fields are validated against the memory's schema when the collection is declared. | memoryUrn, type, fields; optional key (natural id), name |
hadron_get_object |
Read one object by id or node URN as { id, type, ...fields }. |
ref |
hadron_update_object |
Shallow-merge fields into an object (atomic; patch wins, unmentioned fields kept), then re-validate. | ref, fields; optional reason |
hadron_delete_object |
Delete an object — soft by default; hard: true removes the row. Non-recursive. |
ref; optional hard |
hadron_find_objects |
Query one collection → { objects, total }. |
memoryUrn, type; optional match, where, sort, limit, offset |
hadron_update_object merges (unlike hadron_update_node, which replaces
the properties bag). hadron_find_objects takes match (equality shorthand
{field: value}, cast inferred from the schema), where (the full
hadron_find_nodes predicate grammar, ANDed with match), and sort
({field: "asc"|"desc"}); it returns { objects, total } for paging with
limit/offset. id and type are reserved and can't be field names.
Validate & run¶
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_validate |
Walk a memory and report broken edges, missing content, stale abstracts, embedding failures, and property-schema conformance violations. | memoryUrn (required, fully-qualified) |
hadron_run_action |
Load an action-type node, resolve its dependencies, and execute it. |
loc; optional args (key-value object) |
hadron_validate is safe to run any time — it's a read-only sweep. It takes
the memory to check as memoryUrn (fully-qualified). On a memory with a
structured schema it also reports nodes whose
objectType/properties violate the schema — the non-retroactive backstop
for records written before the schema existed.
hadron_run_action is what makes action nodes executable from any MCP
host: write the action once, run it from Claude Code or any other
host the same way.
Chat¶
The chat tools drive a Hadron-managed chatbot session — what the Chat API reference documents from the GraphQL side. The MCP tools are equivalent: they call the same resolvers, return the same shapes.
Lifecycle¶
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_chatbot_start |
Start a chat with an agent. Returns the system message, tool schema, and initial stage. | agentId; optional userId, conversationName |
hadron_chatbot_send |
Save a user message and return the recompiled system message + history for the next LLM turn. | chatId, userMessage |
hadron_chatbot_process |
Save the LLM's respond({ message, data, next_stage }) tool call. Extracts data, evaluates routing edges, returns the display message and any transition. |
chatId, message; optional data, next_stage |
Routing & navigation¶
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_chatbot_get_routing_map |
Download the agent's full conversation graph (topics, conversations, stages, edges). | agentId |
hadron_chatbot_get_route_history |
Read the route history and goal stack for an existing chat. | chatId |
hadron_chatbot_push_goal |
Push a new goal onto the chat's goal stack — typically when the LLM detects an implicit detour. | chatId, description; optional createdBy (ASSISTANT default, or USER) |
hadron_chatbot_pop_goal |
Mark the current top goal complete and return to the previous one. | chatId |
Training & personas¶
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_chatbot_add_training_entry |
Record a user statement and the conversation it should route to — grows goalSignals over time. |
agentId, userStatement, matchedConversation; optional language |
hadron_chatbot_define_persona |
Define a test persona for automated chat testing. | agentId, name, description, openingMessage; optional conversationName, followUpMessages, expectedConversations |
hadron_chatbot_list_personas |
List the personas defined for an agent. | agentId |
hadron_chatbot_run_persona |
Drive a chat with a persona one step at a time. Returns the chat ID and current step index. | agentId, personaLoc; optional step, chatId |
For the broader picture — what topics, conversations, stages, and edges are — see Conversation routing.
App selection¶
For OAuth callers who can see more than one Hadron App, two tools pin an App as the active scope for the rest of the MCP session:
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_list_apps |
List the Hadron Apps the caller has access to, each with its canonical URN and the caller's role on it. | (none) |
hadron_set_active_app |
Pin an App as the active scope. Subsequent App-scoped calls (hadron_list_memories, hadron_find_nodes, etc.) resolve to this App until the session ends. |
app (canonical App URN or unique display name) |
See MCP App selection for the full
walk-through — typed-error vocabulary (app_identifier_ambiguous,
app_not_found, …), short-form resolution rules, and the
session-lifecycle behavior.
Server identity¶
| Tool | Purpose | Key inputs |
|---|---|---|
hadron_server_info |
Return the deployment URL and version of the hadron-server this MCP session is bound to. Useful when a caller has multiple MCP namespaces wired up and needs to know which one a tool call landed on. |
(none) |
Does not require an active App or any memory access — safe to call before authentication is established.
Stability and changes¶
The MCP tool surface is stable — the names and core signatures have not churned. Recent activity adds capabilities (personas, goal stack tools) and fixes edge-case bugs. New tools are additive; removals would be breaking and have not occurred.
If a tool you call returns an unexpected error, check hadron-server's release notes before assuming a regression.
Related¶
- Connecting an MCP host — end-to-end walk-through of pointing Claude Code at a Hadron memory.
- Install Hadron with the install script —
one-liner to wire
.mcp.jsonand the Spec Kit extension. - Adding nodes to a memory —
how
hadron_create_nodeinteracts with the portal's auto-slug and validation rules. - Conversation routing — model behind
the
hadron_chat_*tools. - Chat API — the GraphQL side of the same chat-driving surface.
- MCP App selection — full reference for
hadron_list_appsandhadron_set_active_app.