MCP tools
Every tool mushroomdb mcp serves, with the description and the arguments the server itself
sends to a client. The server answers 25 tools and lists 23 of them by default. mushroomdb mcp <db> --all-tools lists the rest.
This page is the reference. Agent memory is the guide: how to register the server,
a full session from remember to explain_association, and what each answer looks like.
An argument marked required must be sent; the others have defaults the description names.
Nothing here is an access boundary: role, mask and namespace ask to be answered as a
caller, and the stdio server holds no one to them. Access control says where the
boundary is.
Task tools
These answer a question in prose in one call, on any store, and are what the /mushroom skill
reaches for. Each also takes json: true for the raw report.
explain_association
Why are A and B related — every relationship between the two keys and its evidence: the rule that derived it, its edge type, the match score, the predicate it matched on, and the values the two actually share (which specialties overlapped, which field was equal, how far apart they are). Answer from those shared values; the nodes’ full property lists name everything either one holds, not what they have in common. Both keys must already exist.
| Argument | Type | Required | What it is |
|---|---|---|---|
a | string | yes | First node key. |
b | string | yes | Second node key. |
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
node_edges
What is K related to — every relationship of one node, grouped by edge type with a count, each listed edge carrying its direction, the rule that derived it, its score and the predicate it matched on. Answers ‘why is this here’ in the same call that lists it, so no follow-up explain is needed. Which partners are linked by all of these types? pass all_of and the reply is just their keys; pass one edge_type for that type’s partner keys with the rule named once. label narrows partners. With json:true the report carries listed and total, so a reply that was cut still says how much there was.
| Argument | Type | Required | What it is |
|---|---|---|---|
all_of | array of string | no | Only the partners linked by EVERY one of these types — the intersection, as keys. |
direction | one of "out", "in", "any" | no | Which edges count (default any). |
edge_type | string | no | Only this type: the reply is that type’s partner keys, compactly. Omit for the grouped listing over every type. |
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
key | string | yes | Node key. |
label | string | no | Only partners carrying this node label, counts included. |
limit | integer | no | Edges listed per edge type (default 10, max 100), or partner keys listed under edge_type/all_of (default 200, max 2000). The rest are counted as ‘… and N more’. |
neighborhood
What is around K — one hop out, as the same grouped relationship listing node_edges gives, with the rule and score behind each edge. With depth above 1 it is the breadth-first table of (key, label, depth) instead, because past one hop no single rule accounts for a row. label narrows the reply to nodes carrying it, at either depth.
| Argument | Type | Required | What it is |
|---|---|---|---|
depth | integer | no | Hops to traverse (default 1). 1 gives the relationship listing; above 1 gives the traversal table. |
direction | one of "out", "in", "both" | no | Edge direction to follow (default both). |
edge_types | array of string | no | Only follow these edge types. Omit for every type. |
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
key | string | yes | Node key to start from. |
label | string | no | Only nodes carrying this label: at depth 1 the partners, counts included; above it the rows of the traversal table. The walk itself is never narrowed — a hop through another label is how the label you asked about is reached. |
limit | integer | no | At depth 1, edges listed per edge type (default 10). |
edges_at
What did K’s relationships look like on DATE (or at commit C) — the edges that were live at one point in the store’s history, with the rule that had derived each. at takes EITHER an RFC 3339 date (“2026-06-19”, or “2026-06-19T12:00:00Z”) OR a 0-based WAL commit index. Pass the date directly: it resolves to the last commit at or before that instant. Do NOT guess a commit index for a date, and do not go probing commits to find one — a store that cannot answer a date says so by name. Which partners were linked by all of these types on that day? pass all_of and the reply is just their keys; pass one edge_type for that type’s partner keys with the rule named once. label narrows partners. With json:true the report carries listed and total, so a reply that was cut still says how much there was.
| Argument | Type | Required | What it is |
|---|---|---|---|
all_of | array of string | no | Only the partners linked by EVERY one of these types at that commit — the intersection, as keys. |
at | string or integer | yes | When to read the edges. Either an RFC 3339 date string — “2026-06-19”, “2026-06-19T12:00:00Z”, offsets accepted — which resolves to the last commit at or before that instant, or a 0-based WAL commit index. Prefer the date when the question names one. |
direction | one of "out", "in", "any" | no | Which edges count (default any). |
edge_type | string | no | Only this type: the reply is that type’s partner keys, compactly. Omit for the grouped listing over every type. |
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
key | string | yes | Node key. |
label | string | no | Only partners carrying this node label, counts included. |
limit | integer | no | Edges listed per edge type (default 10, max 100), or partner keys listed under edge_type/all_of (default 200, max 2000). The rest are counted as ‘… and N more’. |
what_if
What changes if K’s FIELD became VALUE — the relationships lost and gained, with the rule behind each. Does not change the store: nothing is written, nothing on disk is copied, and the live graph answers the same way before and after the call. Which partners of one type would it cost? pass edge_type and the lost and gained lists are just their keys, with the rule named once. label narrows partners.
| Argument | Type | Required | What it is |
|---|---|---|---|
edge_type | string | no | Only this type, counts included: the reply is the partner keys lost and gained, compactly. |
field | string | yes | Property name to set. |
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
key | string | yes | Node key to change. |
label | string | no | Only partners carrying this node label, counts included. |
limit | integer | no | Edges (or partner keys) listed per side (default 10). The rest are counted as ‘… and N more’. |
value | any | yes | The value it would take: a string, number, boolean, or a list or map of those. Not null. |
recall
What do I already know about this — ranked nodes matching a free-text topic across every indexed text field, with one line each.
| Argument | Type | Required | What it is |
|---|---|---|---|
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
topic | string | yes | Free-form text. A question, a name, or a phrase. |
remember
Remember this for next time — write a note into the graph and return what it did. Use ‘about’ for a subject whose type you don’t know yet — an unknown key there is created as a provisional entity (label ‘Entity’), not refused, and stays that label permanently until it is named through ‘entities’ instead. Use ‘entities’ for a subject whose type you DO know: it is created (or described, if it already exists) under the label you give it, correctly labelled the first time — the label can never be changed afterward, not even by upsert_entity, so name it here when you can rather than leaving it for ‘about’ to guess at. Pass ‘facts’ for the relationships you recognised, between keys named in ‘entities’ or ‘about’, in the same commit.
| Argument | Type | Required | What it is |
|---|---|---|---|
about | array of string | no | Node keys the note is about, when you don’t know their type. A key that does not exist yet is created as a provisional entity (label ‘Entity’) — permanently: prefer ‘entities’ when you know the label, since a provisional node’s label can never change later. |
entities | array of object | no | Entities you recognised in the text. |
entities[].aliases | array of string | no | Other names this entity goes by, kept as written in its ‘alias_keys’ list. With the identity preset, one equal to a provisional stub’s key (exact, case-sensitive) links that stub. |
entities[].key | string | yes | |
entities[].label | string | yes | |
entities[].props | object | no | |
facts | array of object | no | Relationships you recognised, between keys named above. |
facts[].object | string | yes | |
facts[].predicate | string | yes | |
facts[].subject | string | yes | |
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
kind | one of "note", "decision", "todo" | no | What kind of note this is (default: note). |
source | string | no | Where this came from — a session id, a file, a person. Defaults to “agent”. |
text | string | yes | The note itself, 1 to 4000 characters. |
ts | integer | no | Unix seconds the fact dates from. Defaults to now. Pass it when importing. |
schema
What’s in here — the store’s labels with their property names, its edge types and what derives them, every rule with its predicate, the full-text fields recall searches, the equality indexes, and how many provisional nodes remember created. Call it before writing Cypher against a store you have not seen.
| Argument | Type | Required | What it is |
|---|---|---|---|
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
analyze
What matters here, and what clusters — over the whole store, with no role or mask applied. kind ‘central’ ranks nodes by PageRank along edge direction, ‘degree’ by edge count, ‘components’ groups connected nodes with sizes, ‘clusters’ finds communities of two or more, ‘identities’ resolves SAME_AS links into sets every pair of which is linked, oldest node first. ‘top’ (default 10, at most 50) bounds the rows; ‘edge_type’ restricts to one type. The same store always gets the same answer.
| Argument | Type | Required | What it is |
|---|---|---|---|
edge_type | string | no | Only edges of this type. |
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
kind | one of "central", "clusters", "components", "degree", "identities" | yes | Which question. |
top | integer | no | Rows to list (default 10). |
suggest_rules
What relationships are in my data — rules the store proposes from its own values, each with an estimate, examples and the exact create_rule arguments. Nothing is created: show the proposal and wait for approval before calling create_rule. Fields the store writes for bookkeeping are never proposed. Every proposal is global and links across namespaces.
| Argument | Type | Required | What it is |
|---|---|---|---|
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
forget
Forget that — tombstone a node (‘key’), remove one property (‘key’ and ‘prop’), or retract one fact edge (‘fact’: subject, predicate, object). An edge a rule derived is refused with the rule that owns it. Notes that still state what was forgotten are listed, not deleted. History keeps it until the log is pruned, and the reply says so. There is no role check: this server has no auth.
| Argument | Type | Required | What it is |
|---|---|---|---|
fact | object | no | The edge to retract, as remember’s facts name it. |
fact.object | string | yes | |
fact.predicate | string | yes | |
fact.subject | string | yes | |
json | boolean | no | Answer with the report as JSON, not the rendered digest. |
key | string | no | The node to forget, or whose property to forget. |
prop | string | no | With ‘key’: the one property to remove. |
Graph tools
These reach the store directly. In the listing a client receives, each description opens with
Advanced:, so an assistant knows the task tools come first.
query
Who may see this, and anything else one pattern can answer — run a Cypher query (read or write) against the graph. Pass ‘role’ to answer as one of the store’s roles: only the nodes that role may see, writes refused. ‘mask’ is the same restriction written out as an explicit key allow-list. Pass ‘as_of’ to answer from a past commit; it composes with ‘role’ or with ‘mask’. Pass ‘namespace’ to answer from one namespace only. Cypher dialect: MATCH/WHERE/RETURN, CREATE, MERGE, SET, DELETE, with $named parameters in ‘params’. A node’s key and label read as properties (n.key, n.label) or as key(n)/labels(n). One MATCH takes comma-separated patterns that share variables — MATCH (t)-[:A]->(c), (t)-[:B]->(c) is the intersection of both, and count(DISTINCT t) after WITH counts each t once. WHERE takes STARTS WITH, ENDS WITH, CONTAINS, IN, and a list subscript (n.location[0]) — which is null when the index is out of range, the property is not a list, or the index is not an integer, so a subscript never errors and never matches.
| Argument | Type | Required | What it is |
|---|---|---|---|
as_of | integer | no | 0-based WAL commit index: answer from the graph as it was at that commit. Composes with ‘role’ or with ‘mask’ — never both, which is refused as it is without ‘as_of’ — and whichever is passed is resolved against the graph as it was then. Deleting a node does not remove it from a role’s past, and a role’s ‘keys’ resolve to whichever node held the key at that commit. Writes are refused. |
cypher | string | yes | Cypher query text. |
mask | array of string | no | Optional node key allow-list. When present, only these nodes are visible; write statements are rejected. |
namespace | string | no | Answer only from this namespace. Intersects with ‘role’ and ‘mask’ — it can only narrow what they already allow. A role bound to namespaces honours them with no argument here. ‘default’ is the namespace of every node that names none; a name no node uses answers with nothing. |
params | object | no | Named JSON-scalar query parameters. |
role | string | no | Answer as this role from the store’s roles: only the nodes it may see. A role may also be narrowed by one property test (status in [...]), declared in the store’s roles. |
ingest_json
Fill the store from a batch — ingest a JSON array of objects as nodes of one label. Each object becomes a node; declare the rules that should link them with ‘create_rule’.
| Argument | Type | Required | What it is |
|---|---|---|---|
auto_fk_suffix | string | no | |
edges | array of any | no | Optional user edges [{edge_type, src, dst}]. |
key_field | string | no | |
label | string | yes | |
namespace | string | no | Namespace for every node this call creates. Omitted means the ‘default’ namespace. A row that carries its own ‘ns’ must name the same namespace. A namespace is set at insert and cannot be changed afterwards. |
rows_json | string | yes | JSON text of an array of objects. |
create_rule
How should this kind of relationship be derived from now on — declare a rule (RuleDef JSON) and the engine maintains its edges as the data changes. Propose it and show the edges it would derive before creating one.
| Argument | Type | Required | What it is |
|---|---|---|---|
approximate | boolean | no | Find candidates through the HNSW index instead of comparing every pair (default: false). Only for a VectorSimilar predicate; the edges are approximate, not exact. |
dst_label | string | yes | |
edge_type | string | yes | |
max_edges | integer or null | no | |
name | string | yes | |
namespace | string | no | Scope the rule to one namespace: it sees only that namespace’s nodes — source, via hop and destination — so every edge it derives stays inside. Omitted means a global rule, which is the only kind that may derive an edge across a boundary. |
predicate | object | yes | |
src_label | string | yes | |
weight_prop | string or null | no | Edge property that stores the score (default: weight). |
explain
Why are A and B related, as a raw array — the same rule-derived edges explain_association renders, for a caller that wants the JSON without asking.
Listed only with --all-tools; the server answers it either way.
| Argument | Type | Required | What it is |
|---|---|---|---|
a | string | yes | |
b | string | yes |
stats
How big is this store — live node, edge and rule counts, plus history_floor, the oldest commit history still reaches (0 when nothing has been pruned). Pass ‘role’ or ‘namespace’ to also get namespaces, the namespaces that argument may see with a live-node count each; without either argument the roster is omitted entirely.
| Argument | Type | Required | What it is |
|---|---|---|---|
namespace | string | no | Report only this namespace. Intersects with ‘role’. |
role | string | no | Report only the namespaces this role may see. The store-wide counts beside them are unchanged. |
node_info
What is K — its label and every property it holds.
| Argument | Type | Required | What it is |
|---|---|---|---|
key | string | yes |
upsert_entity
Record what is now true about K — insert or update a node by key. If the key exists, updates the supplied properties atomically: every property is checked before any is written, so a refusal leaves the node unchanged. If not, creates a new node with the given label and properties. ‘id’ in ‘props’ is ignored on both paths: a created node stores ‘id’ as its key, and ‘rename_node’ is the only way to change it. Useful for agent memory: store or refresh an entity without checking existence first.
| Argument | Type | Required | What it is |
|---|---|---|---|
aliases | array of string | no | Other names this entity goes by, kept as written in ‘alias_keys’: with the identity preset, one equal to a provisional stub’s key (exact, case-sensitive) links that stub. The store derives ‘aliases’ from the key and the name only. Do not set either in props. |
key | string | yes | Unique node key. |
label | string | no | Node label (required when creating a new entity). |
namespace | string | no | Namespace for a node this call creates. Omitted means the ‘default’ namespace. On a node that already exists, naming the namespace it is in is a no-op and naming another one is refused — a namespace is set at insert and cannot be changed. |
props | object | yes | Properties to set. Values must be scalars (string, number, bool) or arrays of scalars. |
find_similar
What is most like this — two modes: (1) Vector search — provide vector (and optionally field, label, k, min, where, exact) to find the k most similar nodes by cosine similarity using the HNSW index when available, brute-force otherwise. where is a property predicate ({field, eq} or {field, in}) and implies exact search. exact true skips HNSW. (2) Edge traversal — provide key (and optionally edge_type, limit) to return neighbors previously connected by a derived rule edge. Results from mode 2 come only from edges already derived by a VectorSimilar rule. Edge-traversal mode ignores where and exact. In both modes, the optional mask array limits visibility: hidden nodes never appear in results, and a hidden query key in edge mode behaves identically to a nonexistent key.
| Argument | Type | Required | What it is |
|---|---|---|---|
edge_type | string | no | Edge type to filter by in edge-traversal mode (default: SIMILAR). |
exact | boolean | no | When true, vector-search mode uses exact GEMM brute force and does not consult HNSW. Default false. Edge-traversal mode ignores this. |
field | string | no | Property field holding the embedding vectors (default: embedding). Used in vector-search mode. |
k | integer | no | Maximum results to return in vector-search mode (default: 10). |
key | string | no | Source node key for edge-traversal mode. |
label | string | no | Restrict search to nodes with this label. Empty string means all labels. Used in vector-search mode. |
limit | integer | no | Maximum neighbors to return in edge-traversal mode (default: 10). |
mask | array of string | no | Optional node key allow-list for vector-search mode. When present, only nodes whose key appears in this list are eligible for results. Hidden nodes are excluded before k-truncation. The beam widens until it has k visible hits, then falls back to an exhaustive masked scan at the same cap an exact VectorSimilar rule uses, so the result is not short while more visible hits exist. Unknown keys are silently ignored. |
min | number | no | Minimum cosine similarity threshold in vector-search mode (default: 0.8 on every surface; before 0.7 HTTP and the Python binding defaulted to 0.0). Pass 0.0 to keep low-similarity hits. |
vector | array of number | no | Query embedding vector for vector-similarity search. When present, vector-search mode is used and key is ignored. |
where | object | no | Optional property predicate for vector-search mode, same shape as visible_where: {“field”: “…”, “eq”: value} or {“field”: “…”, “in”: [values]}. Implies exact search (skips HNSW). Invalid predicates are a tool error. Edge-traversal mode ignores this. |
pairwise_similar
Which of these are most like each other — exact cosine top-k among a caller key set. Self excluded. Never uses HNSW. Unknown keys, missing embeddings, zero-norm and wrong-dimension vectors are skipped. Duplicate keys collapse to first-seen order. Empty keys returns nothing. n above PAIRWISE_MAX_N is a tool error.
| Argument | Type | Required | What it is |
|---|---|---|---|
field | string | yes | Property field holding the embedding vectors. |
k | integer | no | Maximum neighbors per key (default: 10). |
keys | array of string | yes | Node keys to score against each other. |
min | number | no | Minimum cosine similarity threshold (default: 0.0). |
hybrid_search
What matches these words and this vector at once — Reciprocal Rank Fusion (RRF) over fulltext + vector results. Provide query_text and text_field for the fulltext leg. Optionally provide vector (embedding array) and vector_field (default: embedding) for the vector leg; omitting vector gives text-only ranking through the same RRF path. label restricts the vector search to nodes with that label (required for brute-force; omit to rely on HNSW rules). k controls result count (default: 10). RRF constant is fixed at 60; scores are 1/(60+rank) summed over lists a node appears in.
| Argument | Type | Required | What it is |
|---|---|---|---|
k | integer | no | Maximum results to return (default: 10). |
label | string | no | Restrict vector search to nodes with this label. Required when relying on brute-force (no HNSW rule covers the field). If omitted, the vector leg always returns empty results (no rule-created HNSW index covers the unlabeled path); ranking is text-only in that case. |
query_text | string | yes | Fulltext query string. |
text_field | string | yes | Property field to search with fulltext. |
vector | array of number | no | Query embedding vector. Omit for text-only ranking. |
vector_field | string | no | Property field holding embedding vectors (default: embedding). |
node_history
What has happened to K — every recorded change to one node, newest last. Events include NodeInserted, PropSet, PropRemoved, EdgeAdded, EdgeRemoved, and NodeDeleted. The response includes total_commits (the horizon upper bound) and horizon, the oldest commit still retained; events before it are gone. History is WAL-scoped — pre-snapshot commits are not visible.
| Argument | Type | Required | What it is |
|---|---|---|---|
key | string | yes | Node key to look up. |
edge_history
When did A and B become linked, and when did it break — the full add/retract lifecycle for every edge between the two keys. Includes derived (rule-attributed) edges via DerivedEdgeAdded/DerivedEdgeRetracted WAL markers. The response includes total_commits (the horizon upper bound) and horizon, the oldest commit still retained; events before it are gone.
| Argument | Type | Required | What it is |
|---|---|---|---|
a | string | yes | First node key. |
b | string | yes | Second node key. |
was_linked
Were A and B linked on DATE (or at commit C) — whether an edge of edge_type existed between the two keys (either direction) at that point in the store’s history. Pass the date directly; do not guess a commit index for it. Returns an error when the commit is outside the retained horizon (horizon..total_commits).
| Argument | Type | Required | What it is |
|---|---|---|---|
a | string | yes | First node key. |
at_commit | string or integer | yes | When to check. Either an RFC 3339 date string — “2026-06-19”, “2026-06-19T12:00:00Z”, offsets accepted — which resolves to the last commit at or before that instant, or a 0-based WAL commit index. |
b | string | yes | Second node key. |
edge_type | string | yes | Edge type to check. |
rename_node
Rename K — the key changes and nothing else does. The dense id and all edges/properties remain stable. Returns 404 if old_key does not exist, 409 if new_key is already taken.
Listed only with --all-tools; the server answers it either way.
| Argument | Type | Required | What it is |
|---|---|---|---|
new_key | string | yes | Desired new node key. |
old_key | string | yes | Current node key. |