patina
Click on "Install 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., "@patinaRecall our memories from the beach trip, with a nostalgic mood."
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.
Patina
Affect-weighted persistent memory for LLM agents. Postgres + pgvector, served over MCP.
Your agent forgets everything between sessions. You can dump transcripts into a vector store and search them, but after a few thousand entries that store returns something plausible for any query you type. Delete the entries that look unimportant and you lose the March note that explains everything in September.
Patina makes forgetting an operation you design. Every memory carries emotional coordinates and a trace log: when it was written, when it was shown, when the agent actually used it. A daily worker recomputes its weight from that history, each trace decaying as a power of its age, so what you keep coming back to stays close and what you never touch drifts down the list. It stays reachable by direct search, the way you can't list your neighbor's phone number but recognize it when you see it.
valence ↑
│ tender excited
│ (+0.7, 0.3) (+0.9, 0.85)
│ · ·
─────────┼─────────────────────────────────→ intensity
│ · ·
│ withdrawn alarmed
│ (-0.5, 0.2) (-0.6, 0.85)
↓
weight = importance × affect × Σ w(trace) × age(trace)^(−d)
traces d from kind
affect = 1 + intensity·0.8 + |valence|·0.4
w = 1.0 created, 1.0 used, 0.1 surfacedWhat you get
Three claims carry the rest.
Forgetting runs on use. ACT-R base-level activation over a trace log: every use is its own decaying trace, so recency and frequency of use both count and use rejuvenates. Exposure counts a tenth of use, so appearing in a result list is not what keeps a memory appearing in result lists. Promises hold full weight until you resolve them. Milestones never decay.
Affect is a required column that ranks. recall(query, mood=-0.6) biases
results toward memories of matching valence, the way a low mood surfaces
different memories from the same store than a good day does.
The constants were measured rather than felt.
scripts/evaluate_activation.py scores the model on your own trace log by
temporal holdout, use as the label, against the previous model and naive
baselines. On a synthetic store built with the structure the model targets it
reaches AUC 0.84, where the old exponential model reaches 0.56. That store is
generated, and the number that counts is the one from your traces.
Under those sits the ordinary machinery. Retrieval fuses dense vectors with
trigram search through Reciprocal Rank Fusion, then expands along a typed
memory graph. recall(query, max_tokens=800) packs results by rank into a
token budget and tells you what it left out. Near-identical writes collapse
into the original, and a similar-but-evolved one supersedes its predecessor
without destroying it. Deletion archives first, enforced by database grants
rather than by good intentions. Raw session transcripts become searchable
summarized chunks, so the agent can recall a night nobody wrote down. Three
synthesized persona dimensions hold what the agent is, what it understands
about you, and what the relationship has become.
Every client reads the same rows: claude.ai, Claude Code, Claude Desktop, your own agent loop. There is nothing to sync.
Most people will point this at an assistant that needs to remember a project. It was built for the harder case, one person modeled over years, which is why supersession and affect and the persona layer look over-built for a notes file. docs/DIGITAL_TWIN.md covers that end.
Related MCP server: Engram-Mem
Quick start
You need Docker and Ollama for local embeddings. (To
try the system without a model first, set EMBED_PROVIDER=hash and
PATINA_ALLOW_HASH_EMBEDDINGS=1; it is deterministic and offline, and it
matches spelling, not meaning. Switch to Ollama before you store anything real.)
git clone https://github.com/Whale02/patina.git
cd patina
ollama pull bge-m3
cp .env.example .env
chmod 600 .env
# Fill in three secrets:
# POSTGRES_PASSWORD = $(openssl rand -hex 32)
# POSTGRES_APP_PASSWORD = $(openssl rand -hex 32)
# MCP_BEARER_TOKEN = $(openssl rand -hex 32)
docker compose up -d
curl http://localhost:8000/health # {"status":"ok"}
docker compose exec -T server python -m scripts.smoke_testGive the agent a persona to wake up as:
cp persona/agent.example.md persona/agent.md
cp persona/user.example.md persona/user.md
cp persona/shared.example.md persona/shared.md
$EDITOR persona/*.mdThe server refuses to start when a required variable is missing. Nothing security-relevant falls back to a default.
Connecting a client
Claude Code. Add to .mcp.json:
{
"mcpServers": {
"patina": {
"type": "http",
"url": "http://localhost:8000/mcp/",
"headers": { "Authorization": "Bearer YOUR_MCP_BEARER_TOKEN" }
}
}
}claude.ai and Claude Desktop need a public HTTPS URL and the OAuth flow. See docs/DEPLOYMENT.md.
In a session:
> wake()
# → pinned memories, unresolved promises, all three persona dimensions
> remember("Decided to leave the job. Terrified and certain at the same time.",
valence=0.2, intensity=0.85, importance=9, kind="milestone")
> recall("how did I feel about leaving")
> cite(["<id of the memory you drew on>"])
# use is what keeps a memory available; recall() alone only logs exposureThe verbs
Tool | What it does |
| Conversation start: default surface plus all three persona dimensions. |
| Recall. No args gives pinned and unresolved. |
| Write a memory. Affect coordinates required. Deduplicates on write. |
| Digest long text into 2-6 discrete memories. |
| Resolve, pin, supersede, retag, or delete (archived first). |
| Report which recalled memories you actually used. The signal that separates memories that matter from memories that keep appearing. |
| Typed edges between memories. |
| Counts, edges, health flags. |
| Recompute every weight from the trace log now. Idempotent. |
| Find duplicates. Find memories about to fade. |
| Read and propose updates to the self-model. |
| Rollups written to the markdown mirror. |
| Suggest a tag for the closed vocabulary. |
Full parameter reference: docs/TOOLS.md.
The names are the verbs an agent already thinks in. It wakes, recalls, remembers, digests, revises, forgets. Read the tool list and it reads as a description of what a memory does.
Documentation
Affect, decay, and how forgetting works. Start here. | |
How a write and a read flow through the system. | |
Every tool, every parameter. | |
The three-dimensional self-model. | |
The deep end: modeling one person over years. | |
Providers, dimensions, migrating between them. | |
Threat model, auth, and the gaps I left open. | |
What leaves your machine, and how to stop it. | |
Public exposure, OAuth enrollment, backups, scheduling. |
Scale
Patina runs one agent for one person, across thousands to tens of thousands of memories. At that size it holds up: SHA-pinned container digests, a least-privileged database role, audit logging, rate limiting, 125 tests, and CI that loads the real schema, checks the privilege model, applies every migration twice, and runs the database smoke suite as the app role.
It does not run at production scale, and you should know where it breaks before you find out:
No vector index. pgvector's ivfflat and hnsw cap at 2000 dimensions, and brute-force cosine over 10k rows takes about 10ms. Past roughly 50k memories you want to truncate to 1536 dims and add HNSW.
patina/schema.sqldescribes the migration.OAuth tokens live in process memory. A restart invalidates them and clients re-authenticate without noticing. Serving more than one person would need a table.
The trace log grows without bound, by design. One row per memory per hour it is surfaced, one per use, one per write. A busy single-person store adds a few hundred thousand rows a year, and the nightly recompute scans all of them (an indexed aggregate; seconds at that size). Past tens of millions of rows you would want to roll old traces up into a per-memory summary. The formula supports it, since a trace's contribution depends only on its age and kind, and nobody has needed it yet.
The constants are measured on synthetic data, not yet on yours. The activation harness gives
d, the exposure weight, and the retrieval prior a number on a generated store; a generated store is not a person's memory. The holdout on real traces is the measurement that counts, and every store starts with none. Run it after a month ofcite()traffic and send what you find; CONTRIBUTING.md says how.The use signal depends on the agent calling
cite(). Without it the model degrades gracefully toward exposure-and-age ordering, and throws away its best evidence. Watch theusedcount instatus().
If you need multi-tenant agent memory at scale, the ideas here will help you more than the code will.
Contributing
Retrieval evaluation, more embedding providers, and transcript ingestion for platforms beyond Claude Code are the areas where help goes furthest. See CONTRIBUTING.md.
License
MIT. See LICENSE.
The design leans on the memory literature instead of inventing its own: Ebbinghaus (1885) for the forgetting curve, Russell (1980) for the affect axes, Bjork and Bjork (1992) for the storage-versus-retrieval-strength split behind "fade, don't delete", Anderson and Schooler (1991) for ranking by use, Bower (1981) for mood-congruent recall, Cormack, Clarke, and Buettcher (2009) for rank fusion. docs/MEMORY_MODEL.md works through the derivation and carries the full reference list.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityBmaintenanceProvides a selective persistent memory layer for AI companions, enabling structured recall, reinforcement, and time-decayed retrieval through an MCP interface.10MIT
- Alicense-qualityCmaintenanceEnables persistent memory for AI agents, combining episodic and semantic memory with LLM reasoning, accessible via MCP.2MIT
- Alicense-qualityCmaintenancePersistent, self-organizing memory for AI agents via MCP, enabling natural language recall and fan-out perspectives.MIT
- Alicense-qualityBmaintenanceEnables AI agents to have persistent, self-managing memory with bi-temporal supersession, timely forgetting, and recall under a limited context window, using MCP protocol.MIT
Related MCP Connectors
Persistent memory and knowledge graphs for AI agents. Hybrid search, context checkpoints, and more.
Shared, governed long-term memory for AI agents across tools and sessions via MCP and REST.
Persistent memory for AI agents — verbatim conversations, searchable by meaning.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Whale02/patina'
If you have feedback or need assistance with the MCP directory API, please join our Discord server