Skip to content

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 Kitplan.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:

  1. hadron_whoami — your open sessions, with the bound worker and App. This is where the session id comes back.
  2. 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: pr also 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/N and /issues/N are each accepted for both pr and issue — 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.gito/r. A .git suffix 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-helperdb-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.scope ties 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.