-
Notifications
You must be signed in to change notification settings - Fork 1
Knowledge Graph API
All tools are exposed via MCP and called by Claude Code automatically. You don't call these directly — Claude does, guided by the hidden skill descriptions loaded at session start.
Memory usually arrives without any tool call at all (0.9.17+): the SessionStart hook injects the rendered graph — including the
session_id— into Claude's context before the first message.kg_readremains the fallback (server still warming up) and the API for node reads and re-reads.
The primary entry point. Two modes:
Mode 1: Full graph read (no id/ids)
Loads both user and project graphs: active nodes rendered in cluster order (related nodes together, hubs first) with their relationships indented beneath them, archived anchors, health stats, and a per-graph DEBT: line (maintenance urgency with its raw factors — see How It Works). Each edge appears once, under its first-rendered endpoint. Output is guaranteed to fit inline. The first call (with cwd) initializes a session and returns its session_id.
Mode 2: Node read (with id, or ids for a batch)
Returns full content — gist + notes + touches + all of the node's edges (the crumbs to follow next) — as compact text. Archived/orphaned nodes are promoted to active. Batch several related nodes into one ids=[...] call instead of sequential single reads.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
cwd |
string | First call only | Project root directory. Initializes the session. |
session_id |
string | Later calls | From the first read (or the preloaded block). Reuses the session instead of minting one per read. |
id |
string | No | Node ID to read in full. |
ids |
string[] | No | Several node IDs to read in one call (batch crumb-following). |
level |
string | No | Hint: "user" or "project". If omitted, searches both. |
When it's called: the loud full-graph read before substantive work (the preload is a compact core; this read renders the rest, showing preloaded gists as id-only anchors), and node reads throughout the session.
Full-text search across node IDs, gists, notes, and touches in both user and project graphs. Reaches all three tiers — active, archived, and orphaned — and flags orphaned results so they can be promoted via a node read.
The query is tokenized on whitespace; each term is ranked across nodes by occurrence count and ranks are merged via Reciprocal Rank Fusion (RRF, k=60). Results from the user and project graphs are unified into a single ranking.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
query |
string | Yes | One or more terms (case-insensitive). Multiple terms are RRF-merged. |
session_id |
string | No | Scopes the project search to this session's project and enables seen-dedup. Omitting it falls back to a best-effort search across all currently-loaded project graphs. |
Returns (compact text, capped at 10K chars):
- Top 5 hits with full treatment — notes included only for nodes the session hasn't already been shown (a seen hit renders as a one-line gist reminder; notes never re-dump — they stay one explicit node read away)
- Connections between the hits — nodes on the shortest paths linking the top hits (id + gist) plus the path edges, so the results arrive with their relationships
-
Remaining matches as one-line
id: gistentries
When to use: Before creating nodes (check duplicates), when a problem feels familiar, locating nodes by topic.
Creates or updates a node. If the node exists, fields are merged. If the node was archived, it's automatically unarchived. Saves to disk immediately (write-through).
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
session_id |
string | Yes | From kg_read
|
level |
"user" or "project"
|
Yes | Storage level |
id |
string | Yes | Node ID (kebab-case) |
gist |
string | Yes | Compressed headline — the core insight |
notes |
string[] | No | Rationale, constraints, "why" |
touches |
string[] | No | Related file paths or artifacts |
Side effects: Triggers auto-compaction check. Broadcasts change to WebSocket clients.
Example:
kg_put_node(
session_id="abc12345",
level="project",
id="api-rate-limiting",
gist="Redis-backed sliding window; 429 response includes Retry-After header",
touches=["src/middleware/rate_limit.py"],
notes=["window size configurable via env var RATE_LIMIT_WINDOW"]
)
Creates or updates an edge. from and to can be node IDs or file paths — nodes for those IDs don't need to exist.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
session_id |
string | Yes | From kg_read
|
level |
"user" or "project"
|
Yes | Storage level |
from |
string | Yes | Source node ID or file path |
to |
string | Yes | Target node ID or file path |
rel |
string | Yes | Relationship type (kebab-case) |
notes |
string[] | No | Context about this relationship |
Example:
kg_put_edge(
session_id="abc12345",
level="project",
from="auth-module",
to="src/config.yaml",
rel="reads-config",
notes=["JWT secret and token TTL"]
)
Deletes a node and all edges connected to it. Automatically finds which graph (user or project) the node is in.
Returns: Node ID deleted, count of edges removed, and resolved level.
Deletes a specific edge. All three identifiers (from, to, rel) must match exactly. Automatically finds which graph the edge is in.
Returns: {"deleted": true/false, "level": "..."}
Marks the nodes that actually helped this session — explicit usefulness endorsement that feeds archival scoring (useful knowledge stays active longer). Called toward the end of a session, judged against real results rather than mid-flight promise.
- Budget: 5 likes per session, one vote per node per session.
- Each like lands as a decaying timestamp on the node (90-day half-life).
- Reads deliberately do not feed this signal — a well-formed gist never needs the full read, so read-counting would reward the weakest gists.
- A like is not a content write: node versions, recency, and sync state are untouched.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
session_id |
string | Yes | From kg_read/preload |
ids |
string[] | Yes | Node IDs that proved genuinely useful |
Returns: accepted ids, per-id rejection reasons (already liked / budget exhausted / not found), and the remaining budget.
Gets changes made by other sessions since this session's last sync. Returns diff of nodes and edges modified by other sessions.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
session_id |
string | Yes | From kg_read
|
Returns: Diff with user and project sections showing changed nodes/edges.
When to use: After subagents finish, before important decisions, periodically in long sessions (~30 min).
Tracks multi-step task progress across context compaction and session boundaries. Omit state to read current progress; include state to write.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
session_id |
string | Yes | From kg_read
|
task_id |
string | Yes | e.g. "scout", "extract"
|
state |
object | No | Progress state to persist. Omit to read. |
level |
string | No | Default: "user"
|
Example (write):
kg_progress(
session_id="abc12345",
task_id="scout",
state={
"last_ts": 1706000000,
"sessions_reviewed": ["abc123"],
"patterns_found": ["docker-networking"]
}
)
Example (read):
kg_progress(session_id="abc12345", task_id="scout")
The task id
"maintain"has a system meaning: a/kg-maintainpass stampsstate.last_tsthere when it completes, and that stamp is what resets the staleness factor of the graph'sDEBT:line. Only stamped passes count.
Beyond the MCP tools, the server exposes REST endpoints that power the ambient behavior. Claude never calls these — the plugin's hooks and external dispatchers do:
| Endpoint | Caller | Purpose |
|---|---|---|
GET /api/session_bootstrap?project_path= |
SessionStart hook | Registers a session and returns the compact-core preload (≤10K chars, injectable text + stats) |
POST /api/prompt_context |
UserPromptSubmit hook | Posts the raw hook payload; returns ready-to-print hook output — the full-read nudge, prompt-matched recall (≤3 unseen gists), or {} for "fall back to the reminder pools" |
POST /api/tool_event |
PostToolUse hook (Read|WebFetch|WebSearch) |
Counts the target per project; returns a capture nudge only for an uncovered target re-derived across sessions (throttled) |
GET /api/maintenance_debt |
Maintenance dispatchers | Debt survey of every graph on disk, neediest first, project paths attached |
GET /api/session_state?project_path= |
Legacy remind hook (pre-0.9.24 servers) | Full-read flag only — kept for version skew |
GET /health |
Anything | Liveness + version |
The contract is deliberate: hooks post raw stdin JSON and print whatever comes back — every decision (matching, thresholds, throttles, wording) lives server-side, so hook scripts stay trivial and can never break a session.
-
Node IDs: kebab-case, descriptive, include domain hint. Good:
auth-token-refresh. Bad:refresh. -
Edge relationships: kebab-case verbs. Common:
depends-on,requires,implements,configures,persists,calls,related-to. -
Edges relate concepts; touches locate them. Prefer node→node edges; a file important enough to relate to several concepts graduates to a component node. Touches work best as precise pointers with a semantic anchor:
config/prod.yaml:30-40 (upstream block)— the next session reads 10 lines instead of the whole file. -
Cross-level edges are legitimate: a project node may point up to a user-level node (
proj-decision --applies--> user-principle). Store such edges in the project graph.