Metadata-Version: 2.4
Name: synapto
Version: 0.3.0
Summary: Persistent memory graph for AI coding agents — semantic search, knowledge graph, and time-based decay over MCP
Project-URL: Homepage, https://github.com/ramonlimaramos/synapto
Project-URL: Repository, https://github.com/ramonlimaramos/synapto
Project-URL: Issues, https://github.com/ramonlimaramos/synapto/issues
Author-email: Ramon de Lima Ramos <ramonlimaramos@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: agents,ai,claude,cursor,knowledge-graph,mcp,memory,vector-search
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.11
Requires-Dist: authlib>=1.6.11
Requires-Dist: click>=8.0
Requires-Dist: fastmcp>=2.0.0
Requires-Dist: numpy>=1.24
Requires-Dist: orjson>=3.10
Requires-Dist: pgvector>=0.3.0
Requires-Dist: psycopg[binary,pool]>=3.1
Requires-Dist: pydantic>=2.0
Requires-Dist: python-multipart>=0.0.27
Requires-Dist: redis>=5.0
Requires-Dist: sentence-transformers>=3.0
Requires-Dist: structlog>=24.0
Requires-Dist: tomli-w>=1.0
Requires-Dist: tomli>=2.0; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: bandit>=1.7; extra == 'dev'
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: pip-audit>=2.7; extra == 'dev'
Requires-Dist: pip>=26.1; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == 'openai'
Description-Content-Type: text/markdown

# Synapto

[![CI](https://github.com/ramonlimaramos/synapto/actions/workflows/ci.yml/badge.svg)](https://github.com/ramonlimaramos/synapto/actions/workflows/ci.yml)
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://opensource.org/licenses/MIT)
[![PyPI version](https://img.shields.io/pypi/v/synapto.svg)](https://pypi.org/project/synapto/)

**Your AI agent forgets everything between sessions. Synapto fixes that.**

Flat-file memory (`MEMORY.md`) doesn't scale — no search, no structure, no decay. Synapto gives any MCP-compatible agent a real memory: store once, recall by meaning, watch bad memories fade and good ones persist.

```bash
# remember
"Hermes uses the outbox relay pattern for Kafka"

# recall — weeks later, different session
"How does Hermes handle messaging?"
→ [stable] Hermes uses the outbox relay pattern for Kafka (score=0.94, trust=0.65)
```

Works with Claude Code, Cursor, Windsurf, Codex, LangGraph, Agno, or any MCP client.

## Cross-agent handoffs

Pass work between Codex, Claude Code, Cursor, and other agents in plain
language. Synapto stores the structured state under the hood, so the next agent
can continue from a memory ID instead of a long pasted brief.

```text
You → Codex: Plan this feature and leave a handoff for Claude to implement.
Codex → You: Handoff created for Claude: b0e1506e-d1b7-4bee-9223-4d0f8d18a1b2

You → Claude: Continue from Synapto handoff b0e1506e-d1b7-4bee-9223-4d0f8d18a1b2.
Claude → You: I read the handoff, fetched its context, and can continue.
```

| What you say | What Synapto does |
|---|---|
| "Codex, leave this for Claude." | Stores a `project` memory with `metadata.kind = "agent_handoff"`. |
| "Claude, continue from this handoff ID." | Fetches the full memory with `get_memory` and verifies the metadata. |
| "Any handoffs for me?" | Uses `recall` to find ranked candidates, then fetches the relevant packet. |
| "Mark it ready for review." | Appends a follow-up memory with the same `task_id`. |

See [Cross-agent handoffs](docs/handoffs.md) for the lifecycle, schema, and
Claude/Cursor recipes.

## Try it in 60 seconds

**Docker:**

```bash
git clone https://github.com/ramonlimaramos/synapto.git && cd synapto
docker compose up -d
docker compose exec synapto synapto search "hello world"
```

**Local:**

```bash
pip install synapto
createdb synapto && psql -d synapto -c "CREATE EXTENSION vector;"
synapto init
synapto search "hello world"
```

## What it does

**Search** — Ask a question, get the best memory. Behind the scenes, three signals (vector similarity, full-text, and compositional algebra) are fused into one score. You just call `recall`.

**Graph** — Entities are auto-extracted and linked. Ask "what depends on Kafka?" and get an answer via graph traversal, not keyword guessing.

**Decay** — Core memories live forever. Ephemeral notes fade in hours. Working context lasts about a week. Memories that get used stay alive; unused ones sink.

**Trust** — Mark memories as helpful or not. Bad info gets demoted 2x faster than good info gets promoted. Over time, your memory self-cleans.

**Handoffs** — Tell one agent to leave work for another in natural language.
Synapto turns that into a structured handoff memory, and the receiver continues
with `get_memory`, `context_ids`, and follow-up updates.

## Quickstart

### Prerequisites

- Python 3.11+
- PostgreSQL 14+ with [pgvector](https://github.com/pgvector/pgvector)
- Redis 7+

### Install and initialize

```bash
pip install synapto
createdb synapto && psql -d synapto -c "CREATE EXTENSION vector;"
synapto init            # or: synapto init --interactive
```

### Connect to your agent

The recommended way is `uvx` with `--refresh` — every restart pulls the latest version from PyPI, no manual upgrades:

**Claude Code** (`~/.claude/.mcp.json`):

```json
{
  "mcpServers": {
    "synapto": {
      "command": "uvx",
      "args": ["--refresh", "synapto", "serve"]
    }
  }
}
```

**Cursor** (`.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "synapto": {
      "command": "uvx",
      "args": ["--refresh", "synapto", "serve"]
    }
  }
}
```

> **Why `--refresh`?** Without it, `uvx` reuses the cached environment across restarts, so a new Synapto release on PyPI will not be picked up until the cache expires or you run `uv cache clean synapto` manually. `--refresh` tells `uv` to re-resolve the package on every launch, adding 1–3 seconds to startup in exchange for "always on the latest version" — the right default for an alpha project that ships often. Drop the flag (or pin a version like `"synapto==0.2.0"`) if you want to freeze the version.

Restart your agent. Synapto tools appear automatically, and any future release will be live on the next restart.

## MCP Tools

| Tool | What it does |
|------|-------------|
| `remember` | Store a memory (entities and search vectors are created automatically) |
| `recall` | Search memories by meaning |
| `get_memory` | Fetch the complete content and metadata for one recalled memory |
| `get_memories` | Fetch complete content for multiple recalled memories |
| `relate` | Link two entities ("Hermes" --[produces]--> "agent.messages") |
| `forget` | Soft-delete a memory |
| `trust_feedback` | Mark a memory as helpful or unhelpful |
| `find_contradictions` | Find memory pairs that disagree |
| `graph_query` | Walk the knowledge graph (N-hop) |
| `list_entities` | Browse known entities |
| `memory_stats` | View counts and distribution |
| `maintain` | Run decay and cleanup |

## CLI

```bash
synapto serve                   # start MCP server
synapto search "kafka topics"   # search from terminal
synapto doctor                  # check postgres, redis, embeddings health
synapto stats                   # memory statistics
synapto migrate status          # show applied/pending migrations
synapto export -o backup.json   # export memories
synapto import MEMORY.md --format markdown  # migrate from flat files
```

## Depth Layers

| Layer | Half-life | Example |
|-------|-----------|---------|
| `core` | Forever | "Our API uses REST, never GraphQL" |
| `stable` | ~6 months | "Auth service is in Go, everything else is Python" |
| `working` | ~1 week | "Currently refactoring the payment module" |
| `ephemeral` | ~6 hours | "Debugging: the timeout was 30s, changed to 60s" |

## How it works under the hood

When you call `recall("kafka patterns")`, Synapto runs three searches in parallel and fuses the results:

1. **Vector similarity** (pgvector HNSW) — finds semantically close memories
2. **Full-text search** (tsvector + BM25) — finds keyword matches
3. **HRR compositional algebra** — detects if "kafka" plays a structural role in the memory, not just appears as a word

The scores are combined via [Reciprocal Rank Fusion](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf), then weighted by decay, trust, and depth layer.

HRR (Holographic Reduced Representations) also enables queries that no vector database can do:

- **`probe("kafka")`** — find memories where Kafka is structurally involved (not just mentioned)
- **`reason(["kafka", "hermes"])`** — find memories about both entities simultaneously (vector-space AND)
- **`contradict()`** — find memory pairs that share entities but say different things

More in [docs/hrr.md](docs/hrr.md).

## Configuration

Config file: `~/.synapto/config.toml`

```toml
[postgresql]
dsn = "postgresql://localhost/synapto"

[redis]
url = "redis://localhost:6379/0"

[embeddings]
provider = ""  # auto-select (sentence-transformers on CPU, openai if API key set)
model = ""

[defaults]
tenant = "default"

[decay]
ephemeral_max_age_hours = 24
purge_after_days = 30
```

All values can be overridden with environment variables: `SYNAPTO_PG_DSN`, `SYNAPTO_REDIS_URL`, `SYNAPTO_EMBEDDING_PROVIDER`, `SYNAPTO_DEFAULT_TENANT`.

## Using as a Python library

```python
from synapto.db.postgres import PostgresClient
from synapto.db.migrations import run_migrations, ensure_hnsw_index
from synapto.embeddings.registry import get_provider
from synapto.search.hybrid import hybrid_search

pg = PostgresClient("postgresql://localhost/synapto")
await pg.connect()
await run_migrations(pg)

provider = get_provider()
await ensure_hnsw_index(pg, provider.dimension)

results = await hybrid_search(pg, provider, "outbox pattern", tenant="myproject")
for r in results:
    print(f"[{r.depth_layer}] trust={r.trust_score:.2f} {r.content}")
```

## Documentation

| | |
|---|---|
| [HRR deep dive](docs/hrr.md) | Compositional algebra, probe, reason, contradict |
| [Trust scoring](docs/trust-scoring.md) | Feedback loop and contradiction workflow |
| [Cross-agent handoffs](docs/handoffs.md) | Coordinate planning, implementation, and review across agents |
| [Migrations](docs/migrations.md) | Versioned SQL files with rollback |
| [Claude Code](docs/claude-code.md) | Setup and usage with Claude Code |
| [Cursor](docs/cursor.md) | Setup and usage with Cursor |
| [LangGraph](docs/langgraph.md) | Using Synapto as a LangGraph tool |
| [Agno](docs/agno.md) | Using Synapto with Agno agents |

## Development

```bash
git clone https://github.com/ramonlimaramos/synapto.git
cd synapto
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
synapto init
pytest                          # 83 tests
```

## License

MIT
