tether
Tether is an MCP server that gives AI agents durable, cross-device shared memory via a local SQLite file with optional sync, letting them remember, search, and organize facts across sessions.
Remember facts: Save memories with a type (user, feedback, project, reference), title, body, optional tags, links, and crystallization sources; upserts on type+title so updates refine rather than duplicate.
Recall memories: Search by plain-language keyword and optional semantic similarity, filter by type/tags, fetch by id, get relevance-centered excerpts, and see why each hit surfaced via a
viareceipt.Link related memories: Create bidirectional explicit links between memories by id.
Forget memories: Soft-delete a memory by id so it is excluded from recall and the boot index, but remains recoverable via the admin CLI.
Leverage the boot index: Automatically receive a compact one-line-per-memory index at session start, led by the current project's memories.
Use associative recall: Follow a usage graph built from semantic neighbors, explicit links, and learned co-recall to surface connected context, with tunable budget and session priming.
Manage the store over time: Optionally consolidate near-duplicates, decay older facts, curate the boot index by behavioral importance, run a forgetting-by-disconnection sweep, and detect crystallization clusters for naming principles.
Sync across devices: Point tether at a Turso/libSQL primary to make the local file an embedded replica with near-real-time sync, including read-path pulls and offline-safe degradation.
Export, import, restore, and purge: Use the admin CLI to dump current memories to JSON, merge exports back in, reverse soft-deletions, or permanently delete specific memories.
Provides persistent, local memory storage using an embedded SQLite database, enabling durable recall and management of memories without requiring an external service.
Enables synchronization of the memory database across devices via a hosted Turso/libSQL database, allowing write propagation and periodic read-pull when configured with TETHER_SYNC_URL and TETHER_SYNC_TOKEN.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@tetherremember that I prefer dark mode in code editors"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
tether
A shared memory layer for personal agents, across devices. tether is an
MCP server backed by a local SQLite file. Any
MCP-compatible agent can remember, recall, link, and forget durable notes
— facts about you, your projects, your preferences — so context follows you
instead of dying with each session.
It runs local-only with zero configuration. Point it at a hosted libSQL/Turso primary and the same file becomes an embedded replica that syncs your memory across every device in near-real-time.
Why
The near future is personal agents living across many devices — laptop, desktop, phone. For that to feel like one assistant rather than several amnesiac ones, memory has to be a substrate that follows you: readable and writable from every device and from any agent, not siloed inside a single tool.
tether is that substrate. It is deliberately a convenience layer — it makes an
agent more useful when present, and never breaks the agent's work when degraded.
Related MCP server: AI-MemoryHub MCP Server
Status
v0.6.1. The core (four memory verbs + boot index + FTS5) shipped in v0.1; since then recall has grown a semantic arm, consolidation, an associative usage graph, a self-organizing store, and opt-in crystallization — each additive and each degrading cleanly to plain keyword recall. Every feature below is implemented.
Design and rationale start at
docs/superpowers/specs/2026-07-03-tether-design.md;
the associative core, seed-dominant recall, self-organizing store (Tier B1), and
crystallization (Tier B2) each have their own design doc under
docs/superpowers/specs/, with matching plans in
docs/superpowers/plans/.
Design at a glance
Four memory verbs:
remember·recall·link·forget. (Enabling crystallization adds one reflection-control tool,dismiss_cluster— not a memory operation.)Upsert on write so the store doesn't rot into near-duplicates.
Rich recall (id, type, title, body, tags,
updated_at, plus aviareceipt saying why each hit surfaced) so an agent can judge staleness and cite what it updates.bodyis a query-centered excerpt, not the whole memory — see Excerpts.An auto-loaded boot index — a compact one-line-per-memory list surfaced to the agent each session, so memory helps even when the agent doesn't think to search.
Local-first, sync optional — the local path is untouched when no backend is configured; degradation never throws.
Hybrid search, associative on top — FTS5 keyword hits and local static embeddings are fused, then a usage graph (explicit links, learned co-recall, semantic neighbours) pulls in connected memories. Every layer is additive and degrades to plain keyword recall.
Safe under parallel tool calls — an agent that fires several
remember/recallcalls at once gets atomic, correctly-reported results; see Performance and durability.
Install
Requires Python ≥3.10 on Linux, macOS, or Windows.
Register it with Claude Code — with uv:
claude mcp add tether -- uvx tether-memory…or install it first:
pip install tether-memory
claude mcp add tether -- tether-memory(The package is named tether-memory on PyPI — tether was already reserved
as a common brand name. tether in claude mcp add tether -- ... is just the
label Claude Code uses to refer to this server; it doesn't need to match the
installed command.)
Or add it as a Claude Code plugin (this repo doubles as a one-plugin
marketplace; the plugin runs uvx --from 'tether-memory[semantic]', so
semantic recall comes along):
/plugin marketplace add sidyellur/tether
/plugin install tether@tetherIt is also listed in the MCP Registry
as mcp-name: io.github.sidyellur/tether.
By default memory lives in a local SQLite file at
~/.local/share/tether/memory.db — on Windows,
%LOCALAPPDATA%\tether\memory.db (override either with TETHER_DB, or set
XDG_DATA_HOME, which is honored on every platform). No accounts, no
network — this is the whole tool for a single machine.
Project awareness
tether knows which project it is serving: Claude Code sets
CLAUDE_PROJECT_DIR in every MCP server's environment, and tether takes the
directory's name as the project (override or disable with TETHER_PROJECT).
That drives three things, none of which need configuration:
The boot index leads with this project. The auto-loaded index opens with a
# This project (<name>)section, then# Everything else, so the agent starts a session already looking at the decisions and gotchas for the repo it is in rather than whatever you touched last, anywhere.Work memories are tagged automatically.
project,feedbackandreferencememories get aproj:<name>tag unless the agent passes aproj:tag itself.usermemories are about you, not the work, and stay global. The tag is an ordinary tag:recall(tags="proj:<name>")lists a project's memories deterministically.Recall prefers this project on a near-tie. A hit tagged with the current project ranks a few places ahead of an equally-good hit from another project; it never outranks a clearly better match, and untagged memories are neither boosted nor penalized.
Var | Default | Effect |
| basename of | name the project explicitly; |
Nothing falls back to the working directory: outside Claude Code (or with
TETHER_PROJECT=off) tether behaves exactly as before.
Sync across devices (optional)
Point tether at a Turso / libSQL database and the local file becomes an embedded replica — local-speed reads, writes that propagate to your other devices. Install the extra and set two env vars:
pip install 'tether-memory[sync]'
export TETHER_SYNC_URL='libsql://<your-db>.turso.io'
export TETHER_SYNC_TOKEN='<your-auth-token>'If the backend is unreachable, tether logs sync offline and keeps working
against the local file. Sync is a single-primary design: every write goes to
the hosted primary and replicas only pull, so a device that was merely
offline catches up (including tombstones from forget) on its next pull and
can never resurrect a memory another device forgot. The one gap: writes made
while degraded land only in that device's local file and are not merged
back later (#99 tracks reconciliation).
Writes push immediately. Reads also pull, debounced to at most once every
TETHER_SYNC_READ_INTERVAL seconds (default 30) — so a device that only
asks things still sees what your other devices wrote, instead of staying
frozen at its own startup state until it happens to write something. The
read-path pull is bounded much more tightly than the write-path one: if the
backend is slow, the recall returns local data and the pull lands for the
next read rather than making you wait.
Var | Default | Effect |
|
| seconds between read-path pulls; |
| hostname | the device id recorded on each memory (and the default |
One thing to know about replicas: libSQL forwards every write to the
hosted primary, and with the associative graph on (the default) recall
writes too — it records what was recalled together so memories can wire up
over time. On a replica that makes each recall a few network round-trips on
top of the local search. If that matters more to you than learned
associations, TETHER_ASSOC=0 makes recall read-only again.
Keyword search
The keyword arm is SQLite FTS5 over title, body and tags, ranked by bm25. Ask
in plain language: a memory that contains some of the query's words is a
hit, and one that contains more of them ranks higher, so "how do we run the
integration tests?" finds the note that says "pytest runs the tests" (common
function words are ignored). The index stems English words, so tests
matches test and deciding matches decided. Stemming is English-only;
turn it off for a store in another language and tether rebuilds the index
on the next start.
Var | Default | Effect |
| on | set |
Measured on the LoCoMo long-conversation benchmark (one memory per dialogue turn, 1,536 questions, "did recall return the turns that answer it" in the top 10), the keyword arm alone finds the evidence for 61% of questions, against 54% for a textbook BM25 over the same text. That harness ships in the repo — see Benchmarks.
Semantic search (optional)
By default recall is hybrid: keyword (FTS5) results are fused with
semantic (vector) results, so a query finds relevant memories even when the
exact words differ ("automobile" recalls a note about your "car"). Semantic
recall runs a small static embedding model locally — no network, no API
key, nothing to hang on. Install the extra:
pip install 'tether-memory[semantic]'Without the extra (or with TETHER_SEMANTIC=0), tether runs keyword-only
FTS5 — semantic is a pure add-on and never a requirement. The first run embeds
existing memories once (a one-time backfill); after that it is incremental.
Environment:
Var | Default | Effect |
| on | set |
|
| override the local static model |
Consolidation (optional)
tether keeps a superseded fact rather than overwriting it: when a memory is
replaced, the old one is marked no longer current (retained for history) and
excluded from recall and the boot index. Recall also gently favors more
recent facts. Two opt-in behaviors go further:
Var | Default | Effect |
| off | on ( |
|
| cosine similarity required to treat two facts as duplicates |
| off | set a positive number to exponentially down-rank older facts in recall |
| device id | attribution recorded on each memory |
Consolidation never deletes — forget soft-deletes (see Tools), and
only the admin CLI's purge is permanent. All of this degrades to plain
keyword recall when the semantic extra is absent.
Associative recall (optional)
recall doesn't just return keyword/semantic matches — it follows a usage
graph to related memories, so asking about one thing surfaces its connected
context. The graph's edges come from three local, deterministic sources — no
LLM, no network:
semantic — nearest neighbours by embedding (needs the
[semantic]extra),explicit — the
link()verb,hebbian — memories you recall together get wired together over time.
Every hit carries a via receipt saying why it surfaced (a direct match, or the
edge it came through), and two optional recall args tune it:
Arg / var | Default | Effect |
|
| how far to follow associations; |
| time-bucketed | group related recalls so they prime each other |
| on | set |
|
| default association breadth |
|
| how many top direct hits are locked above associations |
|
| minimum cosine similarity a semantic hit needs to seed an associative walk; below it a memory is only reachable through an edge. |
Associative recall is seed-dominant: the top direct matches are locked in place, and associations only fill the slots below them — so turning association on never demotes a hit that keyword/semantic search already ranked highly.
With TETHER_ASSOC=0 (or budget=0, or an empty graph), recall behaves exactly
as before — associative recall is purely additive and never breaks a lookup.
Self-organizing store (optional)
As a store grows, tether keeps it legible using the same usage graph:
Hub-curated boot-index. The auto-loaded memory index is capped once it passes
TETHER_BOOT_INDEX_CAP(default 50) — the cap always applies, so a large store never gets an unbounded index. With a graph, above the cap it shows two labeled slices — load-bearing memories (highest behavioral degree:explicitlinks + learned co-recall, never mere similarity) and the most recent ones — so the index stays small and shows what actually matters. Without a graph (TETHER_ASSOC=0), it falls back to a plain most-recent-N list instead of the hub/recency split. Below the cap it's the full newest-first list either way.Forgetting-by-disconnection (opt-in,
TETHER_FORGET). A bounded sweep runs everyTETHER_FORGET_INTERVALwrites and soft-archives memories that are both old (TETHER_FORGET_AGE_DAYS, default 90) and behaviorally isolated (noexplicit/hebbianedge — semantic similarity doesn't count). Archived memories drop out of recall and the boot-index but are retained and reversible (it reuses the same mark-invalid machinery as consolidation; nothing is deleted). Safety rails: never runs without a live behavioral graph, below2 × CAPmemories, or more thanTETHER_FORGET_MAX_PER_SWEEP(defaultper sweep.
var | default | effect |
|
| curate the boot-index above this size |
| off | enable the forgetting sweep |
|
| minimum age to be eligible to fade |
|
| writes between sweeps |
|
| max archived per sweep |
With TETHER_FORGET off (default) and a normal store size, recall and the
boot-index behave exactly as before.
Crystallization (optional, off by default)
With TETHER_CRYSTALLIZE=1, tether reflects: it detects dense clusters of
related memories and offers them for naming. Read tether://crystallization
during a reflection pass (it is pull-only, never auto-loaded) to get candidate
clusters; name a real principle with remember(..., crystallizes=[source_ids])
— which writes the principle and links it over its sources — or drop a candidate
with dismiss_cluster(id_a, id_b). Clusters are seeded by explicit links +
usage (semantic similarity fills out membership), so this finds "these belong
together" structure, not mere topical similarity. tether finds the structure;
your agent supplies the words.
A crystallized principle becomes a boot-index hub and is reachable from its sources in recall. Note: this makes "named" a third importance signal alongside "used" and "linked" — deliberate, since an agent judging something principle-worthy is a strong signal.
Tools
Tool | What it does |
| Save a memory; upserts on |
| Hybrid keyword + semantic search, then follows the usage graph to related memories; returns id/type/title/body/tags/updated_at + a |
| Bidirectional link between two memories |
| Soft-delete a memory: marks it no longer current (excluded from recall/the boot index) via the same reversible |
| Reflection control (crystallization): drop the candidate cluster nucleated by peak edge |
Plus three resources: the auto-loaded tether://memory-index (a compact
one-line-per-memory index surfaced each session), the pull-only
tether://status (runtime config: semantic/sync state, memory and edge
counts, DB path — for debugging what's actually active), and, with
TETHER_CRYSTALLIZE, the pull-only tether://crystallization (candidate
clusters for a reflection pass).
Excerpts
recall returns a relevance-centered excerpt of each memory's body, not
the whole thing — the window is centered on the first query term that appears,
so you see why the memory matched rather than just its opening lines. A hit
that was cut also carries truncated: true and body_chars (the real length),
and you fetch the one memory you actually want in full with recall(id=N).
This is the search-engine shape: the result list is an index of pointers with enough text to judge relevance, not a payload of documents. It matters because memories can be large — a single 44KB journal memory made unrelated queries cost ~57–67KB per call while the retrieval itself took under a millisecond. The response was fat, not the engine:
query | full bodies | excerpts |
| 57.0KB | 1.4KB |
| 66.9KB | 2.0KB |
| 66.9KB | 2.0KB |
A memory shorter than the excerpt width is returned whole and unmarked, exactly as before.
Var / arg | Default | Effect |
|
| excerpt width; |
| — | fetch just this memory, whole |
|
| full bodies for every hit — costs the whole payload; prefer |
Performance and durability
tether is meant to be invisible in an agent's loop, so the hot paths are measured and kept flat as the store grows. Numbers below are from a local SQLite store with the semantic and associative layers on, single process:
memories |
|
|
|
500 | 1.6 ms | 1.8 ms | 3.7 ms |
2,000 | 1.8 ms | 2.5 ms | 7.7 ms |
8,000 | 3.4 ms | 1.0 ms | 19 ms |
A few things that make this hold:
Writes don't scale with the store. The embedding matrix used for semantic search and neighbour wiring is kept in memory and patched row by row on every write, rather than re-read from SQLite. It is rebuilt only when vectors change wholesale (a model change, a backfill) or when another process has written to the file (a CLI
purge, a second server, a sync pull) — SQLite'sdata_versioncounter catches that.Parallel tool calls are serialized. MCP runs each tool call on its own thread, and agents issue calls in parallel. All Store operations take one lock, so a
recalland arememberarriving together are each atomic: no interleaved transactions, no half-committed writes, andactionis always right.Commits don't fsync. Local connections run WAL with
synchronous=NORMAL: still safe against corruption, but the last few transactions can be lost if the machine loses power before a checkpoint (an application crash loses nothing). Everyrememberand, with the graph on, everyrecallcommits, so this is one disk sync saved per call.Search stays cheap. Vector search is a single numpy matmul over the in-memory matrix — well under a millisecond at thousands of memories, which is why there is no vector-index extension to install. Keyword cost is FTS5's: proportional to how many memories match the query.
Costs to expect once: the first boot after installing the [semantic] extra
(or changing the model) embeds every existing memory and wires its
neighbours, which takes a second or two per few thousand memories. The boot
index and tag-only lookups scan the store on each call; both are fast at
typical sizes (under 10 ms at 2,000 memories) and are the next things on the
list to cache.
Benchmarks
Two harnesses live in bench/, both runnable without an API key:
python -m bench.locomo— retrieval-only evaluation on LoCoMo, ten long multi-session conversations with ~1,500 questions each labelled with the dialogue turns that answer it. Every turn becomes a memory; the score is whetherrecall(question)returns those turns (recall@k, MRR), per condition: keyword only, keyword + semantic, and the full associative path, next to a textbook BM25 baseline. No LLM is involved, so the number measures the one thing a memory layer controls — did it hand the agent the right facts — and is not comparable to the LLM-judged "accuracy" figures memory vendors publish on the same dataset. The data (~1.5 MB) is downloaded on first use. Install the[semantic]extra to measure the semantic and associative conditions with the real model.python -m bench.run— tether's own associative-recall evaluation: a controlled corpus of tasks whose members are used together, measuring what the usage graph adds over keyword + semantic search after simulated use. Needs the[semantic]extra.
Export and permanent deletion
forget never deletes data — it soft-deletes, like consolidation. Two admin
operations, deliberately kept off the MCP tool surface so an agent can't
trigger them, live in a small CLI instead:
tether export # dump all current memories to JSON (stdout)
tether export -o backup.json # ...or to a file
tether import backup.json # merge an export back into the store
tether restore <id> # un-forget a soft-deleted memory
tether purge <id> --yes # permanently delete a memory (bypasses forget)import replays records through the normal write path, so it upserts on
type+title like remember does — importing into a non-empty store merges
rather than duplicating. Ids are not preserved (an id in the file may map to a
different one here); links are remapped accordingly, and a link pointing
outside the file is dropped rather than pointed at the wrong memory. The
report tells you what happened: {"created", "updated", "skipped", "linked", "dropped_links"}.
restore clears valid_to, reversing a forget (or a consolidation, or a
forgetting sweep). It refuses if a newer memory has since claimed the same
type+title, naming the blocker rather than failing opaquely.
purge refuses to run without --yes. All commands honor the same
TETHER_DB/TETHER_SYNC_* env vars as the server.
License
MIT
Available Tools
4 toolsforgetA
Soft-delete a memory by id: marks it no longer current (excluded from recall/the boot index) but keeps the row, reversibly, like consolidation and the forgetting sweep already do. Returns {"forgotten", "existed"}. (Permanent purge is an admin-only CLI operation, not available here.)
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description transparently discloses the side effects: marks as no longer current, excluded from recall, retains the row, and is reversible. It also specifies the return values. No annotations contradict these behaviors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise but includes an unnecessary analogy ('like consolidation and the forgetting sweep already do') that slightly elongates it without adding critical guidance. Nonetheless, it is structured logically and remains focused.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the absence of an output schema, the description provides essential return values and clarifies the tool's scope by noting the unavailability of permanent purge. It lacks potential error handling details, but the core usage context is sufficiently covered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has one parameter 'id' with no description (0% coverage). The description only says 'by id' without detailing the meaning, constraints, or possible values beyond implying it is a memory identifier. This minimal elaboration does not sufficiently compensate for the missing schema description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's action: soft-delete a memory by id. It distinguishes from sibling tools like 'recall' (for reading) and 'remember' (for creating) by explicitly indicating that the memory is marked no longer current and excluded from recall.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explains when to use this tool (when a memory should be soft-deleted) and provides context by mentioning that permanent purge is an admin-only CLI operation, thereby guiding the agent away from attempting permanent deletion. However, it does not explicitly contrast with 'link' or other alternatives, so a small deduction.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
linkB
Create a bidirectional link between two memories by id.
| Name | Required | Description | Default |
|---|---|---|---|
| id_a | Yes | ||
| id_b | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral disclosure burden. It only discloses that the operation 'Create's a bidirectional link — a write — but says nothing about idempotency (re-linking an existing pair), behavior when an id is invalid or missing, reversibility (no unlink sibling exists), or side effects on recall. The 'bidirectional' detail is useful but minimal.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single 9-word sentence that front-loads the payload: the action ('Create'), the key trait ('bidirectional'), and the target ('link between two memories by id'). Every word contributes; there is zero padding or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple 2-integer-param tool, the description covers the essentials needed to make a correct call: operation, relativity, and param meaning. However, given the absence of annotations and an output schema, it leaves important gaps — return value, error handling for invalid ids, and whether the link affects the sibling 'recall' tool — that an agent would reasonably need.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for the two params. 'by id' and 'between two memories' establish that id_a and id_b are memory identifiers, and 'bidirectional' implies order independence between them. This adds real meaning beyond the bare schema, but leaves validity rules and id provenance unspecified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Create a bidirectional link between two memories by id.' This clearly identifies the operation and, at the schema level, sets it apart from siblings (remember, recall, forget) that operate on individual memories rather than relationships. It misses explicit sibling differentiation, but the relational vs storage distinction is evident.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no guidance on when to use this tool versus the alternative siblings, no prerequisites, and no note on when linking is appropriate. There is no mention of when 'remember' or 'recall' would be preferred, or what makes a memory link necessary. An agent gets no usable decision context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recallA
Search memories by keyword and semantic similarity, then follow the usage graph to related memories, most relevant first.
Ask in plain language, the way you would ask a colleague ("how do we run the integration tests?", "what did the user decide about the auth library?") - a memory matching some of the words is a hit, and the best match ranks first. Recall BEFORE starting a task, not only when stuck: the index you were given at session start is titles only. Memories from the current project rank slightly ahead of equally-good ones from elsewhere.
Each hit carries {id, type, title, body, tags, updated_at} and a via
receipt explaining why it surfaced (a direct match, or the edge it was
reached through). Use updated_at to judge staleness (an old fact may no
longer hold; verify before relying on it) and id to cite what you update
via remember/link.
body is an EXCERPT centered on your query, not the whole memory. When a
memory was longer than the excerpt, the hit also carries truncated: true
and body_chars (the full length). To read one in full, call
recall(id=N) - that returns just that memory, whole.
Args:
query: free text; punctuation is safe. May be omitted if tags is given.
type: optional filter ("user"/"feedback"/"project"/"reference").
limit: max results (default 20).
budget: how far to follow associations (0 = direct matches only).
session: optional id grouping related recalls so they prime each other.
tags: optional comma-separated tags; exact-match filter (a memory must
carry every listed tag). Combine with query to filter its ranked
hits, or use alone (query omitted) to list every current memory
with those tags, newest first, deterministic rather than
ranked - raise limit to fetch beyond the default page size.
id: fetch this one memory in full instead of searching. Use it after a
search returns a truncated hit you want to read completely.
full: return complete bodies for every hit instead of excerpts. Costs
the whole payload - prefer id= for the one memory you actually need.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | ||
| full | No | ||
| tags | No | ||
| type | No | ||
| limit | No | ||
| query | No | ||
| budget | No | ||
| session | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden, and it delivers: result ranking, project bias, the "via" receipt, excerpt behavior with truncated/body_chars, deterministic newest-first listing for tag-only queries, staleness guidance via updated_at, and full/complete-body modes. This is unusually transparent about how the tool behaves beyond its inputs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and then organized into usage, return-shape, and Args sections. It is longer than average, but the length is justified by missing schema descriptions. There is minor redundancy between the prose explanation of id/full and the Args block, so it is not maximally lean, but every section earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter tool with no annotations, no output schema, and no schema descriptions, this is remarkably complete. It explains all parameters, the shape of each hit, what truncated means, how to fetch full bodies, staleness handling, and how to use tags versus query. An agent has everything needed to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the description's Args section documents all 8 parameters with meaningful behavior: query is free text and optional with tags; type filter values; limit default; budget controls association depth; session groups recalls; tags are exact-match and can be used alone; id fetches one full memory; full returns complete bodies and notes the payload cost. This fully compensates for the empty schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific verb and resource: "Search memories by keyword and semantic similarity, then follow the usage graph to related memories, most relevant first." It also covers the secondary fetch mode via recall(id=N), and the role as the read/search tool is clearly distinct from sibling tools remember, link, and forget.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives strong timing guidance: "Recall BEFORE starting a task, not only when stuck" and explains why (the session index is titles only). It also gives clear internal alternatives such as "To read one in full, call recall(id=N)" and "prefer id= for the one memory you actually need." It does not explicitly state when to choose remember/link/forget over recall, only mentioning them as update targets, so some sibling routing is left implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rememberA
Save a durable memory. UPSERTS: a memory of the same type with the same
(whitespace/case-normalized) title is updated in place instead of
duplicated, so re-remembering a fact refines it rather than cluttering.
Worth remembering: decisions and their reasons, conventions, gotchas that cost time, the user's preferences and how they like to work, facts about the environment (paths, commands, accounts) you had to discover. Not worth it: anything derivable from the code, or a transcript of what you did. Remember it when you learn it, not at the end of the session.
Memories of type project/feedback/reference are tagged with the current
project automatically (proj:<name>, from CLAUDE_PROJECT_DIR) unless you
pass a proj: tag yourself; user memories are about the person and
stay global.
Args: type: one of "user", "feedback", "project", "reference". title: a short label; also the dedup key within a type. body: the fact. For feedback/project, a "Why:" / "How to apply:" line helps. tags: optional comma-separated tags. links: optional list of related memory ids. Merged (union) into any links already on the memory, never replaces them - omitting this on a refine call preserves links set earlier. crystallizes: optional list of source memory ids this memory abstracts; links it over them as a crystallized principle (needs TETHER_CRYSTALLIZE).
Returns {"id", "action"} where action is "created", "updated", or (with TETHER_CONSOLIDATE on) "consolidated" - a near-duplicate was superseded.
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | ||
| tags | No | ||
| type | Yes | ||
| links | No | ||
| title | Yes | ||
| crystallizes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries the burden. It discloses created/updated/consolidated outcomes, link merge behavior, crystallize requirements, and auto-tagging. Does not explicitly mention persistence side effects, but 'durable memory' implies it. Strong transparency overall.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long but well-organized with clear sections: purpose, guidance, tagging, parameter list, return. Each sentence provides distinct value; no fluff or redundancy. Front-loaded with core proposition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers all operational aspects: return value shape, action outcomes, conditional flags, auto-tagging, link merging, and dedup behavior. Sufficient for an agent to invoke correctly without external context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, but the description fully compensates: explains type enum values, title as dedup key, body with formatting advice, tags format, links merge semantics, and crystallizes condition. Every parameter is covered with usage context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Save') and resource ('durable memory') with explicit upsert semantics. Clearly differentiates from siblings recall/link/forget as the write operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit 'Worth remembering' vs 'Not worth it' guidance, advises when to use (learn it when you learn it) and when not (derivable from code). Also explains automatic tagging and flag-dependent behaviors.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.6.0- First observed
forget - First observed
link - First observed
recall - First observed
remember
TDQS
Scored across 4 tools
Each tool has a clearly distinct role: remember writes/upserts, recall searches/reads, link creates relationships, and forget deletes. There is no overlap or ambiguity between the four operations.
All tool names are single lowercase verbs that directly describe their action: remember, recall, link, forget. The naming pattern is uniform and predictable.
Four tools cover the core memory operations without redundancy or bloat. This is a well-scoped set for a persistent memory server.
The set provides full lifecycle coverage: create/update via remember, read via recall, delete via forget, plus relation management via link. No essential memory operation is missing.
Maintenance
Related MCP Connectors
Persistent memory for AI agents across Claude, ChatGPT and any MCP client.
- TaprootOAuthcom.taproothq
Persistent memory layer for AI tools. Save and recall notes across Claude and other MCP clients.
Cross-session, cross-device memory for your agent: remember and recall notes. No key to start.
Persistent memory for AI agents — log and recall conversation context over MCP.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to share persistent memory via an MCP server using SQLite, supporting multi-tenant, categorized knowledge with TTL and semantic links, without requiring vector databases.-
- AlicenseAqualityBmaintenanceProvides a hybrid memory architecture with a thin SQLite index and Markdown cold storage, enabling AI agents to write, query, link, and rebuild long-term memories via MCP tools, model-agnostic and zero third-party dependencies.74MIT
- AlicenseNot gradedqualityFmaintenanceLocal-first, auditable memory for AI agents. Provides durable context for MCP hosts with SQLite storage, CLI, and MCP tools for memory management.2Apache 2.0
- AlicenseAqualityBmaintenanceProvides persistent, searchable memory for AI agents across any MCP-compatible client, storing project context, user preferences, and session learnings locally in SQLite with tools to save, retrieve, search, and manage them.125 npmMIT