# Vayl

> Reconciling memory for AI agents (MCP server): a new value supersedes the old, removals actually retract, and history stays queryable — so agents get current facts, not stale ones.

- **Type:** MCP server
- **Install:** `agentstack add mcp-vayl-dev-vayl`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [vayl-dev](https://agentstack.voostack.com/s/vayl-dev)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [vayl-dev](https://github.com/vayl-dev)
- **Source:** https://github.com/vayl-dev/vayl
- **Website:** https://vayl.gitbook.io/vayl-docs

## Install

```sh
agentstack add mcp-vayl-dev-vayl
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Vayl

[](https://pypi.org/project/vayl-mcp/)
[](https://scorecard.dev/viewer/?uri=github.com/vayl-dev/vayl)

[](https://vayl.gitbook.io/vayl-docs)

  

**Vayl is the reconciling memory layer for AI agents.** Most memory layers *accumulate* — they save every fact and later hand your agent stale ones. Vayl **reconciles**: a new value replaces the old one, "we dropped X" actually removes X, and you can still ask what was true *before*. Drops into any MCP client (Claude Desktop, Cursor, Claude Code).

```
"We use Redux."                             → remembered
"Actually we moved off Redux to Zustand."   → Redux retired, Zustand active
"What do we use?"                           → "Zustand"   (not "Redux, Zustand")
"What did we use first?"                    → "Redux"     (history kept)
```

**Why it's different, in four lines:**

- ✅ **Reconciles, doesn't pile up** — a new value *supersedes* the old; a *same-slot invariant* means one thing never has two live values, so "what's true now" is always unambiguous.
- 🗑️ **Removal is first-class** — "we dropped X" actually *retracts* X. Graph memory stores can't model this; additive stores never do.
- 🕰️ **History is kept and auditable** — ask what's true *now* or what was true *before*; every change is on a tamper-evident, signed audit chain.
- 🔌 **Local, cheap, pluggable** — SQLite by default (no server to run), ~2 LLM calls per fact, one-command install into Claude / Cursor / Claude Code, and it speaks MCP so any agent client plugs in.

📚 **Full documentation:** [vayl.gitbook.io/vayl-docs](https://vayl.gitbook.io/vayl-docs) — guides, MCP tool reference, tutorials, and deployment.

**Jump to:** [Why Vayl](#why-vayl) · [Quickstart](#quickstart-2-min) · [Use it from code](#use-it-from-code) · [What your agent gets](#what-your-agent-gets) · [How it works](#how-it-works) · [Benchmarks](#benchmarks) · [Contributing](#contributing)

---

## Why Vayl

Two common kinds of agent memory each leave the same gap — a fact that *changed*:

|  | Additive / vector memory | Temporal-graph memory | **Vayl** |
|---|---|---|---|
| A value that changed | ranks both — model guesses which is current | reconciles | **reconciles by construction** |
| "we dropped X" | X still returned | usually **can't model removal** | **retracts X** |
| "what was true before?" | — | kept | **kept + signed audit chain** |
| Cost per fact | ~1 LLM call | graph DB + many LLM calls | **~2 LLM calls, no graph DB** |
| Runs local & free | varies | heavy | **SQLite, no server to run** |

**Honest about scope:** on a plain contradiction ("switched X → Y") a strong model reconciles well in most designs — Vayl isn't "more accurate" across the board. Its edges are **correct forgetting, low cost, current-truth by construction, and running free & local.**

**Built for** coding agents that track decisions · support copilots whose account state keeps changing · personal assistants that update preferences · and high-stakes domains (clinical, finance) where a stale fact is a hazard, not a typo. Worked examples for each are in the [docs](https://vayl.gitbook.io/vayl-docs).

---

## Quickstart (~2 min)

```bash
pip install vayl-mcp          # from PyPI
vayl-demo                     # 30s, no keys — see reconciling memory in action
```

Add to your MCP client (Claude Desktop / Cursor) config:

```json
{
  "mcpServers": {
    "vayl": {
      "command": "vayl-mcp",
      "env": {
        "LLM_PROVIDER": "openai",
        "OPENAI_API_KEY": "sk-…",
        "OPENAI_MODEL": "gpt-5-mini",
        "EMBED_BASE_URL": "https://api.openai.com/v1",
        "EMBED_MODEL": "text-embedding-3-small",
        "VAYL_DB": "/absolute/path/vayl.db"
      }
    }
  }
}
```

Restart the client — your agent now has the [tools below](#what-your-agent-gets). `VAYL_DB` is where memory persists. `gpt-5-mini` is the default and scores **0% silently-wrong** on the messy real-world suite; any OpenAI-compatible endpoint works (point `OPENAI_BASE_URL` at a self-hosted or EU-region deployment if data residency matters).

> **Models & reliable reconciliation.** For **declared slots** (a [preset](#declared-slots--presets) via `VAYL_SLOT_SCHEMA=preset:coding`) the same-slot invariant retires the old value *deterministically* — even a small local model reconciles a switch correctly. For **free-form** memory (no preset), use a capable model (the default `gpt-5-mini`, or a ~7B+ local model) so the extractor names slots consistently. A 3B local model works, but is less reliable without a preset.

**Or let the CLI write the config for you.** Vayl is a [FastMCP](https://gofastmcp.com) server, so `fastmcp install` wires it into your client in one command:

```bash
fastmcp install claude-desktop src/vayl/api/mcp_server.py:mcp \
  --with vayl-mcp --env OPENAI_API_KEY=sk-… --env VAYL_DB=/absolute/path/vayl.db
```

Targets: `claude-desktop` · `claude-code` · `cursor` · `gemini-cli` · `mcp-json` (prints the config JSON for any client).

---

## Use it from code

A small client wraps the MCP boilerplate, so you write methods, not `tools/call` JSON.

```python
from vayl import Vayl

with Vayl(user_id="proj_7") as m:        # local: spawns vayl-mcp over stdio
    m.remember("We moved off Redux to Zustand")
    print(m.recall("what do we use for state?"))   # -> "Zustand"

# a shared team server:
# with Vayl(url="https://memory.acme.com/mcp", api_key="vayl_sk_…") as m: ...
```

Any tool is callable as a method (`m.history(...)`, `m.check_before_act(...)`); a default `user_id`/`agent_id`/`run_id` is sent on every call. **TypeScript:** the same client for TS/JS agents — `npm i @vayl.dev/client`, source in [`clients/typescript/`](clients/typescript/).

### Framework integrations

Drop Vayl's reconciling memory into an existing agent framework as tools it can call — so a plain vector store's "hands you back both Redux and Zustand" problem just goes away. Every adapter exposes the same curated surface (`remember` · `recall` · `history` · `forget` · `list_memories`) and binds your scope server-side, so the model never sets whose memory it touches.

| Framework | Install | Import |
|---|---|---|
| **LangGraph / LangChain** | `pip install 'vayl-mcp[langgraph]'` | `from vayl.integrations.langgraph import VaylMemory` |
| **OpenAI Agents SDK** | `pip install 'vayl-mcp[openai-agents]'` | `from vayl.integrations.openai_agents import VaylMemory` |
| **CrewAI** | `pip install 'vayl-mcp[crewai]'` | `from vayl.integrations.crewai import VaylMemory` |
| **Vercel AI SDK** (TS) | `npm i @vayl.dev/client ai zod` | `import { vaylTools } from "@vayl.dev/client/vercel"` |
| **Mastra** (TS) | `npm i @vayl.dev/client @mastra/core zod` | `import { vaylTools } from "@vayl.dev/client/mastra"` |

```python
# Python — LangGraph / OpenAI Agents / CrewAI all follow this shape
from vayl.integrations.langgraph import VaylMemory

with VaylMemory(user_id="proj_7") as mem:
    agent = mem.agent("openai:gpt-4o-mini")          # ready agent wired to memory
    agent.invoke({"messages": [("user", "We moved off Redux to Zustand. What do we use now?")]})
    # or bind the tools onto your own agent: create_agent(model, tools=mem.tools())
```

```ts
// TypeScript — Vercel AI SDK (Mastra is the same, from "@vayl.dev/client/mastra")
import { generateText, stepCountIs } from "ai";
import { openai } from "@ai-sdk/openai";
import { Vayl } from "@vayl.dev/client";
import { vaylTools } from "@vayl.dev/client/vercel";

const m = await Vayl.connect({ userId: "proj_7" });
const { text } = await generateText({
  model: openai("gpt-4o"),
  tools: vaylTools(m),
  stopWhen: stepCountIs(5),
  prompt: "We moved off Redux to Zustand. What do we use now?",
});
```

---

## What your agent gets

Vayl exposes **30+ MCP tools**, grouped by the job they do:

| Group | Tools | For |
|---|---|---|
| **Memory** | `remember` · `recall` · `history` · `update_memory` · `forget` · `list_memories` | store, query, correct, and time-travel over facts |
| **Safety** | `check_before_act` · `safe_recall` · `pending_changes` · `confirm_change` / `reject_change` | gate irreversible actions; human approval for high-stakes writes |
| **Accountability** | `record_decision` · `explain_decision` · `attest` · `audit_log` · `verify_audit` | signed, tamper-evident record of what the agent believed and did |
| **Compliance (GDPR)** | `delete` · `delete_all` · `export_memory` · `purge_expired` · `verify_receipt` | erasure, DSAR export, retention — with signed receipts |
| **Team / admin** | `create_principal` · `set_reconcile_policy` · `license_status` · `stats` · `health` | multi-tenant scoping, conflict policy, ops |

`forget` vs `delete`: **forget** retires a fact but keeps it in history (the auditable "we used to use X"); **delete** hard-erases it for compliance. Both matter — one for correctness, one for privacy. Every tool carries MCP **safety annotations**, so clients can auto-run the reads (`recall`, `history`, …) and confirm before an irreversible erasure (`delete`, `delete_all`, `purge_expired` are marked destructive).

📖 Full tool reference — click to expand all 30+ tools with detailed behaviour

| Tool | What it does |
|------|--------------|
| `remember(text, source=…)` | Store fact(s). Automatically supersedes, retracts, or splits multiple facts in one message. `source` records who/what asserted it (belief provenance + source-aware reconciliation in a shared space). |
| `recall(question, explain=True)` | Answer from memory — current value, history, or multi-hop. Says **"I don't know"** rather than guessing. With `explain=True`, also returns the **provenance** — the exact facts used, each with who asserted it, its confidence, and what it superseded. `include_history=True` reaches retired facts for questions about the past; `critical_categories=…` forces named categories into the answer regardless of ranking. |
| `recall_related(question)` | Deep / relational / multi-hop questions via the entity graph (`"who owns the company Bob works for?"`). Falls back to `recall` when the graph isn't enabled. |
| `pending_changes()` | Proposed changes to **confirm-required** slots that have not been applied — the human-approval queue. Shows old → new plus the sentence that triggered it. |
| `confirm_change(memory_id, decided_by)` | Approve a proposed change, applying it. Records **who** decided. |
| `reject_change(memory_id, decided_by)` | Discard a proposed change. The current value stands; the proposal is kept as history. |
| `history(subject)` | The change-log for a subject — every value it has held, oldest → newest, with status. The "what did we use first vs now" view additive stores can't give you. |
| `get_memory(memory_id)` | One memory's structured detail (value, status, scope, confidence, metadata). Ids are shown as `#id` by `list_memories`. |
| `update_memory(memory_id, new_value)` | Correct a memory by id — **audit-preserving**: the old value is retired to history, the new one becomes active. |
| `forget(text)` | Retract a fact (`"we dropped Sentry"`, `"Alice left"`) — retired but **kept in history** for audit. Guaranteed removal. |
| `record_decision(action_summary, question)` | Log a decision bound to the **exact facts the agent consulted** — an immutable, signed snapshot of what it believed. Answers "why did the agent do X?" even after those facts later change. |
| `explain_decision(decision_id)` | Reconstruct a past decision: the action plus the beliefs held **at that moment**, with the signed receipt verified so you know the record wasn't altered. |
| `check_before_act(subject, …)` | **Safety gate before an irreversible action.** Returns SAFE, or BLOCKED with reasons — a disputed (FLAGGED) value, confidence below the bar, a stale fact, or one that just changed. Tune the policy (`min_confidence`, `max_staleness_days`, …). |
| `safe_recall(question, …)` | Like `recall`, but answers **only if every current fact behind the answer is safe to act on**; otherwise it withholds the answer and returns why. Use on the path to an action. |
| `delete(subject)` | **Permanently erase** a subject, history included — right-to-be-forgotten / GDPR. Unlike `forget`, nothing is retained; the erased values are also **redacted from decision snapshots** (re-signed, redaction audited). Issues a **signed erasure receipt**. |
| `delete_all()` | Permanently erase **all** of a user's memory (account deletion / GDPR), with their decision snapshots redacted too. Issues a signed erasure receipt. |
| `export_memory()` | **DSAR-complete export** (GDPR Art. 15/20): statements (active + history) plus the subject's decision snapshots, audit entries, and receipts — machine-readable JSON. |
| `purge_expired(days, …)` | Retention (Art. 5(1)(e)): hard-delete records older than N days; flags extend it to the audit log / decisions / receipts — the audit chain stays verifiable across purges via a **signed retention anchor**. |
| `audit_log()` | The accountability trail — who did what, when; detail encrypted at rest; never wiped by erasure. |
| `attest(subject)` | Issue a **signed, third-party-verifiable attestation** of the current value — "as of now, X is the value", anchored to the tamper-evident audit head. Prove what was known, and when. |
| `verify_receipt(receipt_id)` | Verify a signed erasure receipt or attestation — recomputes the body and checks the Ed25519 signature. VALID / INVALID. |
| `verify_audit()` | Verify the **tamper-evident audit chain** end-to-end — reports INTACT, or the exact row where it breaks (edited / reordered / truncated). |
| `export_public_key()` | Vayl's Ed25519 public key — anyone can verify receipts, attestations, and the audit chain **without the secret key or the database**. |
| `set_reconcile_policy(mode, authority)` | Configure how a **shared space** resolves conflicts between different contributors: `RECENCY` (newer wins), `AUTHORITY` (higher-ranked source wins; a lower one is flagged, not overwritten), or `REVIEW` (every cross-source conflict is flagged). |
| `get_reconcile_policy()` | Show the space's reconciliation policy. |
| `create_principal(name, role, scopes)` | *(admin)* Create a user/agent and issue its API key (shown once). Roles: admin, member, agent, viewer, auditor. `scopes` confines it to named `user_id`s — **required for multi-tenant**. |
| `list_principals()` | *(admin)* List the deployment's principals and their roles (never shows keys). |
| `revoke_principal(id, erase)` | *(admin)* Disable a principal — its API key stops working immediately. `erase=True` hard-deletes the record (Art. 17 for team members). |
| `license_status()` | Show the edition (Community or licensed), seats used vs. allowed, expiry, and unlocked features. |
| `list_memories()` | Current active facts (each with its `#id`) plus a history block of what was superseded / retracted. |
| `stats()` | On-device KPIs — per-tool call counts, avg latency, errors, and the distribution of reconciliation actions (SUPERSEDE / RETRACT / FLAG / SKIP …). Nothing leaves the machine. |
| `health()` | Diagnose setup — checks the database, embedder, LLM, and graph (if enabled) are reachable. Run this first if something isn't working. |

---

## How it works

Every message is turned into fact(s) by one LLM call, then reconciled against what's stored: a new value **supersedes** the old, a removal **retracts**, an ambiguous one gets **flagged** instead of guessed, and superseded/retracted facts stay in history. State persists in SQLite (per `user_id`). `recall` uses **hybrid retrieval** — it fuses semantic (embedding) and lexical (keyword) ranking so exact-term matches surface even when the embedding ranks them lower — and pulls only the top-k relevant facts, so the LLM context stays small and fast **no matter how big memory grows** (and recall still works if the embedder is unavailable).

```mermaid
flowchart TD
    A["Agent message'we moved off Redux to Zustand'"] -->|1 LLM call| B[Extract fact&#40;s&#41;]
    B --> C{"Reconcile vs stored(same-slot invariant)"}
    C -->|

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [vayl-dev](https://github.com/vayl-dev)
- **Source:** [vayl-dev/vayl](https://github.com/vayl-dev/vayl)
- **License:** Apache-2.0
- **Homepage:** https://vayl.gitbook.io/vayl-docs

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-vayl-dev-vayl
- Seller: https://agentstack.voostack.com/s/vayl-dev
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
