A self-hosted knowledge graph for persistent personal memory, written to and read by Claude over MCP. Claude is the relationship model; the local model is optional.
Personal GraphRAG demonstrates solid tool design with clear semantic intent and well-structured schemas. All 10 tools have descriptions and input schemas visible in mcp_server.py. Naming follows verb_noun conventions (remember, search_memory, get_entity). However, parameter descriptions lack depth, many omit format constraints, ranges, or validation rules. Output schemas are documented in descriptions but not formally specified. Error handling is minimal; tools return JSON summaries without recovery guidance. The 'remember' tool is exceptionally well-documented with inline enum examples; others are more terse. No tool annotations (readOnlyHint/destructiveHint) despite clear risk classifications in the spec.
Browse entities filtered by category, type, and/or tag. No LLM. E.g. 'everything in homelab' or 'all my open tasks'.
Remove an entity from memory (tombstoned in the journal, deleted from the live graph). The journal preserves the history.
Fetch one entity's stored attributes directly from the graph; fuzzy-suggests on a miss. No LLM.
List relationships around an entity (depth 1-3). Deterministic traversal, no LLM.
Connect two entities with a relationship (stubs either if new). A focused shortcut for when Claude just wants to add an edge. Same write path as remember.
Overview of your memory: totals, type + category breakdown, most-connected entities, and journal stats. No LLM, instant.
Parameter descriptions lack format constraints and validation rules. E.g., 'top_k' in search_memory states 'number of results' but omits the max:50 constraint visible only in code. 'depth' in get_relationships lacks min/max bounds (1-3). LLMs cannot read code, they rely on descriptions to understand constraints.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint) despite clear risk classifications. 'forget' is DESTRUCTIVE; 'remember' and 'link' are WRITE; others are READ_ONLY. Annotations enable agents to reason about side effects and retry safety.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 66 | 2026-07-28+ | v2 |
Answer from memory using the LOCAL model to synthesize prose (needs Ollama). Inside a Claude session prefer `search_memory` and let Claude write the answer. Modes: local|global|hybrid|naive.
The last n things written to memory, straight from the journal. No LLM.
Save something to long-term memory. THIS is how Claude adds to the graph. Claude (not a local model) decides the structure and passes it in: entities (list of objects with name, type, category, tags, description) and relationships (list of objects with source, target, relation, description). Unknown types fall back to 'note', unknown categories are kept, and any relationship endpoint not defined as an entity is auto-stubbed — a write is never rejected. Re-remembering an entity MERGES it (tags union; newest description/type win). Returns a JSON summary of what was written.
Semantic search across your memory. NO LLM — embeds the query and returns the most relevant entities, relationships, and notes for Claude to reason over. The primary recall tool inside a Claude session. Returns JSON with entities, relationships, and notes.
Output schemas not formally documented. Descriptions mention 'Returns JSON with...' but do not specify field names, types, or structure. E.g., search_memory returns {entities, relationships, notes} but this is inferred from description text, not a schema. LLMs need explicit output types for downstream chaining.
Error handling is absent. Tools return JSON summaries on success but no guidance on failure modes. E.g., 'forget' says 'tombstoned in journal' but does not explain what happens if the entity does not exist. 'get_entity' mentions 'fuzzy-suggests on a miss' but does not document the response structure for misses.
Parameter descriptions are terse (avg ~40 chars). Rubric baseline is 72 chars. E.g., 'entity' in get_relationships is described as 'entity name' (12 chars), no guidance on fuzzy matching, case sensitivity, or what happens if not found. Terse descriptions force LLMs to guess.