Metadata-Version: 2.4
Name: memty
Version: 0.2.0
Summary: User-owned, local-first memory for AI assistants: one private vault for Claude, ChatGPT, Gemini, Perplexity, Cursor, and any MCP client
Author: RohanNK86
License-Expression: MIT
Project-URL: Homepage, https://github.com/RohanNK86/memty
Project-URL: Repository, https://github.com/RohanNK86/memty
Project-URL: Documentation, https://github.com/RohanNK86/memty/tree/main/memty#readme
Project-URL: Issues, https://github.com/RohanNK86/memty/issues
Project-URL: Changelog, https://github.com/RohanNK86/memty/blob/main/memty/CHANGELOG.md
Keywords: memty,ai-memory,portable-memory,local-first,privacy,mcp,model-context-protocol,llm,chatgpt,claude,gemini,perplexity,browser-extension,sqlite
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mcp<2,>=1.9
Provides-Extra: mongodb
Requires-Dist: pymongo>=4.6; extra == "mongodb"
Provides-Extra: crypto
Requires-Dist: cryptography>=43.0; extra == "crypto"
Provides-Extra: mcp
Provides-Extra: server
Requires-Dist: fastapi>=0.115; extra == "server"
Requires-Dist: uvicorn>=0.30; extra == "server"
Dynamic: license-file

# Memty

**Your AI memory, owned by you.** Memty keeps the facts, decisions, and preferences you share with AI assistants in one private vault on your own computer. It brings that memory to Claude, ChatGPT, Gemini, Perplexity, Cursor, Windsurf, and any MCP client, so you stop repeating yourself every time you open a new chat or switch to a different assistant.

[![PyPI](https://img.shields.io/pypi/v/memty.svg)](https://pypi.org/project/memty/)
[![Python](https://img.shields.io/pypi/pyversions/memty.svg)](https://pypi.org/project/memty/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/RohanNK86/memty/blob/main/LICENSE)

- **Local-first.** The vault is a single SQLite file in `~/.memty/`. There are no accounts, no cloud sync, and no telemetry.
- **Works everywhere.** It connects to MCP clients (Claude Desktop, Claude Code, Cursor, Windsurf), to chat websites through a browser extension, and to anything else through the clipboard.
- **Under your control.** You can view, edit, delete, and mark any fact private. Deleted facts stay deleted, and AI extraction can never bring them back.
- **Private by design.** Private values are hidden from cloud AIs and appear only as `[private]`. Secrets never leave your machine.
- **Small and fast.** Capsules stay under a fixed token budget, and the core uses only the Python standard library.

> Memty is in alpha (0.x). The storage format is stable and covered by tests, but commands and options may still change between minor versions. See the [changelog](https://github.com/RohanNK86/memty/blob/main/memty/CHANGELOG.md).

---

## Quick start (about 3 minutes)

```shell
pip install memty

memty serve --autostart   # 1. start the local bridge now and at every login (one time)
memty extension           # 2. install the browser extension folder and show where it is
memty connect             # 3. optional: connect Claude Desktop, Claude Code, Cursor, Windsurf
```

Then load the extension once. Open `chrome://extensions` (or `edge://extensions`), turn on **Developer mode**, click **Load unpacked**, and choose the folder that `memty extension` printed. There's no token to copy and nothing to configure: the extension pairs itself with Memty on your computer.

In any chat on ChatGPT, Claude.ai, Gemini, or Perplexity:

- **Select text → ＋ Save to Memty.** Memty proposes short, atomic facts. You edit or untick them, then save.
- **⚡ Memty (or Alt+M).** Your saved facts are added to the top of the prompt, newest first, where you can see and edit them before sending:

  ```
  Company: MEMTY
  Category: Portable AI Memory Infrastructure
  Memory store: Memty Vault
  Client: [private]
  ```

- **☰ (or the extension icon).** Opens **Your Memty facts**, where you can view, search, edit, add, delete, or mark facts private.

Prefer the terminal?

```shell
memty remember work.project editor "VS Code"
memty recall "Which editor do I use?" --namespace work.project
memty clip work.project            # copy your facts to the clipboard for any AI
```

---

## How it works

```
 ChatGPT · Claude.ai · Gemini · Perplexity          Claude Desktop · Claude Code · Cursor · Windsurf
                │  browser extension                               │  MCP (stdio)
                ▼                                                  ▼
     memty serve  (127.0.0.1 only, paired token)          memty mcp
                └──────────────────────┬───────────────────────────┘
                                       ▼
                        memtyd  (one local daemon per vault)
                                       ▼
                ~/.memty/ledger.sqlite3   (your vault: SQLite, on your disk)
```

1. **Atomic facts.** Memory is stored as small `namespace.slot = value` facts, for example `work.project.editor = "VS Code"`. There is one value per slot and each fact is at most 40 tokens. Saving a new value replaces the old one and keeps an audit trail, so memory never contradicts itself.
2. **Namespaces** keep contexts apart (`work.project`, `personal`, `client.acme`). A chat only receives the namespace you choose.
3. **The compiler** builds a small capsule for each request. Without a question it lists the newest facts first. With a question it ranks facts by relevance. Either way the capsule stays within a hard token budget.
4. **The privacy gate** runs on every capsule:
   - **PUBLIC** facts are shared.
   - **CONFIDENTIAL** (private) facts appear only as `[private]` to cloud assistants.
   - **SECRET** facts stop the request and ask for a local model instead.
5. **You stay in control.** Facts saved from chats are proposals until you approve them. Facts you confirm can't be overwritten by automatic extraction. Deleting a fact removes it and its history, and it can't be brought back automatically.

---

## Browser extension (Chrome, Edge, Brave)

The **Memty Bridge** extension ships inside this package.

```shell
memty extension          # copies it to ~/.memty/browser-extension and prints the steps
memty extension --open   # also opens the folder
```

After `pip install -U memty`, run `memty extension` again and click reload on the extension in `chrome://extensions`. Open chat tabs pick up the new version without a refresh.

| Feature | What it does |
| --- | --- |
| ⚡ Memty / Alt+M | Adds your facts to the prompt as plain `Title: value` lines (up to about 350 tokens, newest first) |
| Select text → ＋ Save to Memty | Proposes atomic facts from the selection. You edit titles and values, then save. |
| ＋ Save | The same, for your selection or the latest answer |
| ☰ Your facts | View, search, edit, add, delete, and mark facts private |
| Draggable pill | Drag the ⋮⋮ grip anywhere. Each site remembers the position, and a double-click resets it. |

How facts are extracted, all on your machine:

- Headings, questions, and Memty blocks you pasted earlier are skipped.
- Bare file paths are grouped into one `files` fact.
- Code is never stored. Each code block becomes one summary, such as `JavaScript code in content.js defines saveFacts`.
- Set `MEMTY_OLLAMA_MODEL` (for example `llama3.1`) to extract with a local Ollama model. If Ollama doesn't respond, Memty falls back to its built-in rules.

### Bridge commands

```shell
memty serve --autostart        # start now and at every login (Windows, macOS, Linux; per-user, no admin)
memty serve --no-autostart     # stop it and remove the login item
memty serve --background       # start quietly once
memty serve --stop
memty pair                     # connect another browser or a reinstalled extension (2-minute window)
memty serve --new-token        # revoke access: new token, forget the paired extension (restart the bridge)
memty serve --show-token       # for manual pairing (extension options > Advanced)
```

### Security model

The bridge runs on your computer, so it's built so that websites can't read or change your vault:

- It binds only to `127.0.0.1`, and the address can't be configured.
- Every data route needs `Authorization: Bearer <token>`. The token is stored owner-only in `<db>.web-token`.
- **Pairing is trust on first use.** `POST /pair` only answers browser-extension origins, and the first extension to ask is remembered. Web pages and other extensions are refused (`403`). To add another browser, or an extension you reinstalled, run `memty pair`: it lets exactly one more extension connect within two minutes.
- Requests from web-page origins are refused, and the `Host` header must be `127.0.0.1` or `localhost`, which blocks DNS rebinding. Writes must be `application/json`.
- Capsules are compiled for each site as a cloud provider. Private values are never sent to the browser, not even to the facts page, and redaction maps never leave the bridge.
- The extension's background worker is the only part that holds the token. Scripts running inside chat sites never see it.

| Route | Purpose |
| --- | --- |
| `GET /health` | Liveness check (no token, no data) |
| `POST /pair` | Token for the paired extension only |
| `GET /capsule?namespace=&provider=&query=&tokens=350&format=text\|xml` | The capsule for a chat |
| `GET /namespaces` | Namespace names and fact counts |
| `GET /facts?namespace=` | Facts, newest first; private values are `null` |
| `POST /extract` `{"text"}` | Proposed facts. Writes nothing. |
| `POST /facts` `{"namespace", "facts": [{"slot", "value"}], "confirmed"}` | Saves reviewed facts |
| `POST /facts/update` `{"namespace", "slot", "new_slot", "value", "private"}` | Edits, renames, or changes privacy (saved as user-confirmed) |
| `POST /facts/delete` `{"namespace", "slot"}` | Forgets a fact and its history |

---

## Clipboard: any AI, any app

```shell
memty clip work.project                    # copy your facts as Title: value lines
memty clip work.project --query "deploy"   # rank facts for a question
memty clip work.project --print            # print instead of copying
memty clip work.project --format xml       # the <memty_state> block for agents and tools
```

The clipboard counts as a cloud destination: private values are redacted, and the command exits with status 2 if a SECRET fact matches. Use `--provider local` only when the paste goes to a local model. Memty uses the operating system's own clipboard tool: `clip` on Windows, `pbcopy` on macOS, and `wl-copy`, `xclip`, or `xsel` on Linux.

To bind it to a hotkey:

- **Windows**: create a shortcut whose target is `pythonw -m memty clip work.project`, then set a **Shortcut key** in its properties.
- **macOS**: in Shortcuts, add *Run Shell Script* with `memty clip work.project` and assign a shortcut.
- **Linux**: bind the command in your desktop's keyboard settings.

---

## MCP clients: Claude Desktop, Claude Code, Cursor, Windsurf

```shell
memty connect --dry-run            # show the plan; run and open nothing
memty connect                      # connect every detected client
memty connect --no-open            # print the Cursor link instead of opening it
memty connect --client Cursor      # only one client (repeatable)
```

**Memty never creates, edits, or deletes another application's settings file.** Each change goes through the client's own installer, or through you:

| Client | How `memty connect` connects it |
| --- | --- |
| Claude Code | Runs Claude Code's official `claude mcp add --scope user memty -- <python> -m memty mcp` |
| Cursor | Opens Cursor's official MCP install link, and Cursor asks you to confirm |
| Claude Desktop | Prints the exact entry and steps (Settings > Developer > Edit Config) |
| Windsurf | Prints the exact entry and steps for Windsurf's MCP settings |

Restart each client after connecting it. The server exposes the `memty_sync` tool (`recall`, `remember`, or `forget`) and the resource template `memty://capsule/{namespace}`. MCP capsules are limited to 120 tokens, and MCP clients are treated as cloud providers, so the privacy gate applies. Set `MEMTY_PROVIDER` to choose another identity; choose a local one only when the model really runs locally.

Note: Memty 0.1.0 and 0.1.1 could write client settings files directly. If a client broke after one of those versions ran, use that client's recovery steps. Don't delete its whole configuration file.

---

## CLI reference

```shell
memty remember work.preferences editor "VS Code" --verification USER_CONFIRMED
memty recall "Which editor do I use?" --provider claude --tokens 600
memty list                                   # active facts
memty history work.preferences editor        # superseded versions
memty forget work.preferences editor --purge-raw
memty summary repo.memty --set "Memty uses Postgres and a local context compiler."
memty rule repo.memty database_queries "writing database queries" "always use async Motor"
memty rules --namespace repo.memty
memty rule-delete repo.memty database_queries
memty stats
memty migrate ~/.memty/memory.sqlite3        # import from an earlier Memty vault
memty --version
```

`memty recall` prints a deterministic `<memty_state>` capsule and never includes archived raw source text. If a SECRET fact matches a cloud provider, it exits with status 2 and prints a `local_routing_required` payload.

### Configuration

| Setting | Default | Purpose |
| --- | --- | --- |
| `--db` / `MEMTY_VAULT_PATH` (or `MEMTY_DB`) | `~/.memty/ledger.sqlite3` | Vault location |
| `--user` / `MEMTY_USER` | `local` | User ID inside the vault |
| `MEMTY_PROVIDER` | external | Identity used by MCP capsules |
| `MEMTY_OLLAMA_MODEL`, `MEMTY_OLLAMA_HOST` | not set, `http://127.0.0.1:11434` | Local model for fact extraction |

### The local daemon

The MCP servers, the CLI, and the web bridge share one `memtyd` process for each vault. It starts automatically when it's needed and keeps one SQLite connection. It uses authenticated loopback connections, and its endpoint and secret are stored beside the database, so keep that folder private.

```shell
memtyd status
memtyd start
memtyd stop     # add --db PATH for a non-default vault
```

---

## Python API

```python
from memty import open_ledger, LocalRoutingRequired

vault = open_ledger()  # ~/.memty/ledger.sqlite3, user "local"

vault.record("work.preferences", "editor", "VS Code", verification_state="USER_CONFIRMED")
vault.record("repo.memty", "primary_db", "Postgres 16", verification_state="USER_CONFIRMED")
vault.set_summary("repo.memty", "Memty uses Postgres and a local context compiler.")

compiled = vault.context("Which editor and database do I use?", provider="claude", token_budget=600)
print(compiled["text"])                   # <memty_state> capsule within the budget
print(compiled["approx_tokens"], compiled["sections"])

# Privacy gate
vault.record("user.vault", "api_key", "...", sensitivity="SECRET", verification_state="USER_CONFIRMED")
try:
    vault.context("api key", provider="claude")
except LocalRoutingRequired as exc:
    print(exc.as_dict())                  # suggests a local provider; no secret value included

vault.record("repo.memty", "internal_host", "db.internal.example",
             sensitivity="CONFIDENTIAL", verification_state="USER_CONFIRMED")
compiled = vault.context("internal host", provider="claude")   # value masked as a placeholder
answer = vault.rehydrate(model_output, compiled["redactions"]) # put it back locally
```

A provider is trusted when it is local (`local`, `ollama`, `local_ollama`, `memty_local`), was passed to `MemtyLedger(..., trusted_providers=[...])`, or is named in the fact's `allowed_providers`.

Archived raw text is never injected into a capsule. You can only read it back with an explicit call:

```python
fact = vault.record("repo.memty", "deployment_host", "db.internal.example",
                    verification_state="USER_CONFIRMED", source_raw="full log excerpt ...")
vault.drill_down(fact_id=fact["id"])
```

### Guarantees

| Guarantee | How |
| --- | --- |
| One value per slot, no contradictions | `UNIQUE(user_id, namespace, slot)`; superseded values move to `atomic_fact_audit` |
| Facts stay small | `record()` rejects values over 40 tokens |
| Deletion is permanent | `forget()` tombstones the slot and purges its plaintext and audit history |
| Extraction can't undo you | `EXTRACTED` writes can't overwrite user-confirmed slots or restore deleted ones |
| Bounded context | The compiler enforces `estimate_tokens(text) <= token_budget` |
| Fast | Warm compile median about 3 ms with 300 facts and a 600-token budget |
| Search works everywhere | FTS5 when SQLite supports it, with a SQL fallback otherwise |

---

## Uninstall

```shell
memty serve --no-autostart      # stop the bridge and remove the login item
pip uninstall memty
```

Remove the extension from `chrome://extensions`. Your vault stays in `~/.memty/` until you delete that folder yourself.

## License

MIT. See [LICENSE](https://github.com/RohanNK86/memty/blob/main/LICENSE).
