Skip to main content

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.

Status

v0.1 is implemented. Design and rationale: docs/superpowers/specs/2026-07-03-tether-design.md. Implementation plan: docs/superpowers/plans/2026-07-03-tether-v0.1-implementation.md.

Design at a glance

  • Four verbs, nothing more: remember · recall · link · forget.
  • Upsert on write so the store doesn't rot into near-duplicates.
  • Rich recall (id, type, title, body, tags, updated_at) so an agent can judge staleness and cite what it updates.
  • 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.
  • Keyword search now, embeddings later — the SQLite schema is built so semantic search and a full entity/edge graph slot in without migrating data.

Install

Requires Python ≥3.10 on a POSIX system (Linux/macOS).

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.)

By default memory lives in a local SQLite file at ~/.local/share/tether/memory.db (override with TETHER_DB). No accounts, no network — this is the whole tool for a single machine.

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; writes converge when it comes back.

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
TETHER_SEMANTIC on set 0/false/off to force keyword-only recall
TETHER_EMBEDDING_MODEL minishlab/potion-base-8M 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
TETHER_CONSOLIDATE off on (1/true) merges a near-duplicate on write — supersedes the old fact instead of fragmenting the store (needs the [semantic] extra)
TETHER_DEDUP_THRESHOLD 0.92 cosine similarity required to treat two facts as duplicates
TETHER_DECAY_HALF_LIFE_DAYS off set a positive number to exponentially down-rank older facts in recall
TETHER_AUTHOR device id attribution recorded on each memory

Consolidation never deletes — only forget does. 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
budget (per call) TETHER_RECALL_BUDGET how far to follow associations; 0 = direct matches only
session (per call) time-bucketed group related recalls so they prime each other
TETHER_ASSOC on set 0/false/off for plain keyword+semantic recall
TETHER_RECALL_BUDGET 8 default association breadth
TETHER_PROTECT_HEAD 8 how many top direct hits are locked above associations

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) and a graph exists. Above the cap it shows two labeled slices — load-bearing memories (highest behavioral degree: explicit links + learned co-recall, never mere similarity) and the most recent ones — so the index stays small and shows what actually matters. Below the cap, or without a graph, it's the full newest-first list as before.
  • Forgetting-by-disconnection (opt-in, TETHER_FORGET). A bounded sweep runs every TETHER_FORGET_INTERVAL writes and soft-archives memories that are both old (TETHER_FORGET_AGE_DAYS, default 90) and behaviorally isolated (no explicit/hebbian edge — 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, below 2 × CAP memories, or more than TETHER_FORGET_MAX_PER_SWEEP (default 10) per sweep.
var default effect
TETHER_BOOT_INDEX_CAP 50 curate the boot-index above this size
TETHER_FORGET off enable the forgetting sweep
TETHER_FORGET_AGE_DAYS 90 minimum age to be eligible to fade
TETHER_FORGET_INTERVAL 20 writes between sweeps
TETHER_FORGET_MAX_PER_SWEEP 10 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
remember(type, title, body, tags?, links?) Save a memory; upserts on type+title so facts refine rather than duplicate
recall(query, type?, limit?, budget?, session?) Hybrid keyword + semantic search, then follows the usage graph to related memories; returns id/type/title/body/tags/updated_at + a via receipt
link(id_a, id_b) Bidirectional link between two memories
forget(id) Delete a memory

Plus an auto-loaded resource tether://memory-index — a compact one-line-per-memory index surfaced each session.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tether_memory-0.5.1.tar.gz (189.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tether_memory-0.5.1-py3-none-any.whl (31.4 kB view details)

Uploaded Python 3

File details

Details for the file tether_memory-0.5.1.tar.gz.

File metadata

  • Download URL: tether_memory-0.5.1.tar.gz
  • Upload date:
  • Size: 189.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tether_memory-0.5.1.tar.gz
Algorithm Hash digest
SHA256 bc32cb738658c03fcabcd182583ecd0fb96dc63bbb4f8e6f1467c5a3f3aa8030
MD5 6dba9e73362c18b0f7019f8665ac7eef
BLAKE2b-256 702d39d25f0b387e848c5b7b4bb1b726b6fa4b93247d89ca524108424d9a8a82

See more details on using hashes here.

Provenance

The following attestation bundles were made for tether_memory-0.5.1.tar.gz:

Publisher: release.yml on sidyellur/tether

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tether_memory-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: tether_memory-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 31.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tether_memory-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 899f8852da0e865de556bccb43b2d7b215701449ed45ab74f1c17c4b1d3dbae0
MD5 7789980a0421ebb0666feca69916c12f
BLAKE2b-256 d64eb4999976d057beae558b1721f219292de9cde7595deca0dc8f58bae0b022

See more details on using hashes here.

Provenance

The following attestation bundles were made for tether_memory-0.5.1-py3-none-any.whl:

Publisher: release.yml on sidyellur/tether

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.6.0

2 files

This release

0.5.1 This release

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page