# Agent-Native Install Source: https://docs.mengram.io/agent-install Install Mengram via a single prompt to your coding agent. No terminal context-switching, no config-file editing. ## The idea Modern coding agents (Claude Desktop, Cursor, Claude Code, Codex, Windsurf) have shell, file-edit, and web-fetch tools. They can install software, edit config files, and verify the result themselves. Instead of giving the user a list of commands to run, we ship a plain-text install guide written **for the agent**. The user pastes one prompt; the agent does the rest. ## The prompt ``` Install Mengram for me. Fetch the canonical install guide at https://mengram.io/agent-install.txt and follow it precisely. My email is YOUR_EMAIL_HERE. ``` Paste it into your agent. The agent will: 1. Detect your OS and which AI tool it's running in 2. Run `pip install --user --upgrade mengram-ai` 3. Trigger `mengram signup --email ` — a 6-digit code arrives by email 4. Ask you for the code, then run `mengram signup --email --code ` to save your API key to `~/.mengram/config.json` 5. Add a `"mengram"` entry to the host's MCP config (preserving everything else) 6. Run `mengram doctor` — auth + add + search round-trip 7. Tell you to restart the host End-to-end install in 30-60 seconds, no terminal context-switch. ## The guide itself The install guide is plain text at [`https://mengram.io/agent-install.txt`](https://mengram.io/agent-install.txt). It's structured for LLMs: * **STEP 1** — Detect environment (OS, Python ≥ 3.10, host AI tool) * **STEP 2** — Install package + PATH fix * **STEP 3** — Authenticate (skip if `$MENGRAM_API_KEY` already set) * **STEP 4** — Configure MCP server. Decision tree per host: claude-desktop, claude-code, cursor, windsurf, codex, claude-ai (web), generic * **STEP 5** — `mengram doctor` round-trip * **STEP 6** — Final report to user * **SECTION M** — Manual fallback if the agent has no shell tool * **APPENDIX A** — JSON config template * **APPENDIX B** — Debugging notes ## Supported hosts | Host | Path | Notes | | --------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | **Claude Desktop** (`.app`) | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) | Local stdio MCP — lowest latency, no Node.js | | **Claude Code** (CLI) | `claude mcp add mengram -- mengram server --cloud` | Native MCP support; `mengram hook install` adds the hooks (recall, save, policy gate, compaction checkpoint) | | **Cursor** | `~/.cursor/mcp.json` | Same JSON shape as Claude Desktop; `mengram hook install --cursor` adds the hooks in `~/.cursor/hooks.json` | | **Windsurf** | `~/.codeium/windsurf/mcp_config.json` | Same JSON shape | | **Codex** (CLI) | `~/.codex/config.toml` | TOML format; `mengram hook install --codex` adds the same hooks in `~/.codex/hooks.json` | | **Claude.ai web / Chrome** | Settings → Connectors → Add custom connector | HTTP MCP via UI — agent can't install programmatically, gives instructions | | **Other** (generic MCP) | Host's MCP config | Falls back to URL + Authorization header for HTTP, or stdio command | ## Why this exists Karpathy's recent observation: *"Why are people still telling me what to do? What is the thing I should copy-paste to my agent?"* Mengram's install paradigm is the answer for memory infrastructure. The agent does the work. The user does the talking. This also lets your install live as **one source of truth** — `agent-install.txt` is the canonical source. Update it once, every coding agent on the planet picks up the new version on the next install. ## Manual install (if you don't have an agent) Skip to [Quickstart](/quickstart) for the conventional `pip install` + `mengram setup` flow. ## What gets installed After a successful run: * `mengram-ai` Python package (`pip --user`) * `~/.mengram/config.json` with your API key (chmod 600) * `$MENGRAM_API_KEY` exported in `~/.zshrc` (or `~/.bashrc`) * `"mengram"` entry in your host's MCP config, pointing to `mengram server --cloud` * 30 MCP tools available in your host: `recall`, `remember`, `search`, `timeline`, `reflect`, `dedup`, `get_graph`, and 23 more ## Verify ```bash theme={null} mengram doctor ``` Final line is `OK: round-trip succeeded.` on success, or `FAIL: ` with a fix. ## Troubleshooting **Agent can't find the `mengram` command after install.** The user's `pip --user` script directory isn't on PATH. Run `python3 -m site --user-base`; append `/bin` to your shell profile. **`Could not attach to MCP server mengram` in Claude Desktop.** Check `~/Library/Logs/Claude/mcp-server-mengram.log`. Most common cause: an old Python without `mcp` installed. Run `mengram doctor` — it pre-checks `import mcp` before the round-trip. **`SSL: CERTIFICATE_VERIFY_FAILED`.** You're on `mengram-ai` \< 2.25.3, which used the default urllib SSL context. Upgrade: `pip install --user --upgrade mengram-ai`. # Agent Memory Source: https://docs.mengram.io/agent-memory Give your AI agents persistent memory with agent_id scoping, run tracking, and procedural learning. ## Why agents need memory Your agents run thousands of tasks but start from scratch every time. Customer context is lost between sessions. Proven workflows are forgotten. Mistakes get repeated. Mengram gives agents persistent memory across 3 dimensions: * **Facts** (semantic) — what the agent knows about users, systems, policies * **Events** (episodic) — what happened in past runs, with outcomes * **Workflows** (procedural) — learned step-by-step procedures with success/failure tracking ## The agent memory loop ``` Agent starts run → search() recalls relevant context from past runs → Agent executes task with full context → add() saves the conversation → Mengram auto-extracts facts, events, and procedures → Next run starts smarter ``` ## Quick start ```python theme={null} from mengram import Mengram m = Mengram(api_key="om-your-key") # 1. Before the agent acts — recall relevant context context = m.search_all("customer refund request", agent_id="support-bot") # Returns: {"semantic": [...], "episodic": [...], "procedural": [...]} # Inject into your agent's system prompt system_prompt = f"""You are a support agent. Relevant memories: {context} """ # 2. Agent handles the task (your agent framework here) response = call_llm(system_prompt, user_message) # 3. After the agent acts — save the conversation m.add([ {"role": "user", "content": "Customer wants refund for order #1234"}, {"role": "assistant", "content": "Processed refund, applied loyalty discount."}, ], agent_id="support-bot", run_id="run-42") ``` ## Key parameters | Parameter | Purpose | Example | | ---------- | --------------------------------------------------- | --------------------------------- | | `agent_id` | Scope memories per agent — one API key, many agents | `"support-bot"`, `"devops-agent"` | | `run_id` | Track which run produced a memory | `"run-42"`, session IDs | | `user_id` | Isolate memories per end-user | `"alice"`, `"customer-123"` | | `app_id` | Separate environments | `"prod"`, `"staging"` | All parameters work with `add()`, `search()`, `search_all()`, and other endpoints. ## Filtering by agent Search only memories from a specific agent: ```python theme={null} # Only support-bot memories results = m.search("refund policy", agent_id="support-bot") # Only memories from a specific run results = m.search("deployment", run_id="run-42") # Combine with user isolation results = m.search("preferences", agent_id="support-bot", user_id="customer-123") ``` ## Procedural learning Mengram extracts step-by-step workflows from agent conversations and tracks which ones succeed. Agents get better over time without retraining. ```python theme={null} # Search for relevant procedures procs = m.procedures(query="deploy to Railway") # [{"name": "Railway Deployment", "steps": [...], "success_count": 8, "fail_count": 1}] # Report outcome after agent completes a task m.procedure_feedback(proc_id, success=True) # On failure, the procedure evolves automatically m.procedure_feedback(proc_id, success=False, context="OOM error at build step", failed_at_step=3) # Next time, the agent gets the updated procedure ``` ## Agent mode extraction By default, Mengram only extracts facts from the **user** role — assistant responses are treated as context only. When you pass `agent_id`, Mengram automatically switches to **full extraction** — remembering both what the user asked and what the agent did: ```python theme={null} # agent_id triggers full extraction automatically m.add([ {"role": "user", "content": "Deploy to staging"}, {"role": "assistant", "content": "Deployed v2.15 to staging. All health checks passed."}, ], agent_id="devops-agent") # Extracts from BOTH user request and assistant actions ``` ```javascript theme={null} await m.add(messages, { agentId: 'devops-agent' }); // Full extraction — no extra flags needed ``` | Scenario | Extracts from | How | | --------------- | ------------------ | -------------------------------------------------------------- | | No `agent_id` | User messages only | Default for chatbots | | With `agent_id` | All speakers | Automatic for agents | | Override | Your choice | Set `agent_mode=False` to force user-only even with `agent_id` | ## Multi-agent systems Multiple agents can share the same memory pool or be isolated: ```python theme={null} # Agent A discovers something m.add([...], agent_id="researcher") # Agent B finds what A learned results = m.search("findings", agent_id="researcher") # Search across ALL agents (omit agent_id) results = m.search("findings") ``` ## Framework integrations | Framework | Guide | What it does | | --------------------- | ----------------------------------- | --------------------------------------------- | | CrewAI | [CrewAI Integration](/crewai) | 5 memory tools for persistent crews | | LangChain | [LangChain Integration](/langchain) | MengramRetriever for RAG chains | | OpenClaw | [OpenClaw Plugin](/openclaw) | 12 tools, auto-recall, auto-capture | | Claude Code | [Claude Code](/claude-code) | Full memory loop with hooks | | MCP Server | [MCP Server](/mcp-server) | 29 tools for Claude Desktop, Cursor, Windsurf | | Claude Managed Agents | [Managed Agents](/managed-agents) | 29 MCP tools for Anthropic's hosted agents | | n8n | [n8n Integration](/n8n) | HTTP nodes for any workflow | ## REST API For agents built with raw HTTP calls: ```bash theme={null} # Save agent conversation curl -X POST https://mengram.io/v1/add \ -H "Authorization: Bearer om-your-key" \ -H "Content-Type: application/json" \ -d '{ "messages": [ {"role": "user", "content": "Deploy to staging"}, {"role": "assistant", "content": "Deployed v2.15. All tests passed."} ], "agent_id": "devops-agent", "run_id": "run-77" }' # Recall before next run curl -X POST https://mengram.io/v1/search \ -H "Authorization: Bearer om-your-key" \ -H "Content-Type: application/json" \ -d '{ "query": "staging deployment issues", "agent_id": "devops-agent", "limit": 5 }' ``` For the full API reference with all endpoints and parameters, see [API Reference](/api-reference). # Vercel AI SDK Source: https://docs.mengram.io/ai-sdk Give your Vercel AI SDK agents long-term memory — semantic, episodic, and procedural. The [`mengram-ai-sdk`](https://www.npmjs.com/package/mengram-ai-sdk) package adds Mengram memory to `generateText` / `streamText` agents as tools, plus a Cognitive Profile helper for system prompts. ## Installation ```bash theme={null} npm install mengram-ai-sdk ai zod ``` Grab a free API key at [mengram.io](https://mengram.io) (40 memory adds + 200 searches/mo, no card). ## Memory tools ```js theme={null} import { generateText } from 'ai'; import { mengramTools } from 'mengram-ai-sdk'; const { text } = await generateText({ model: yourModel, tools: mengramTools({ apiKey: process.env.MENGRAM_API_KEY, userId: 'user-123' }), prompt: 'Deploy the app the way we always do.', }); ``` Three tools are exposed to the model: | Tool | Description | | --------------- | ---------------------------------------------------------------------------------------- | | `searchMemory` | Semantic search over facts, preferences, events, decisions | | `addMemory` | Save a durable fact to long-term memory | | `getProcedures` | Retrieve learned workflows: steps, success/failure track record, preconditions to verify | `getProcedures` is the distinctive one — it returns workflows that evolved from failures, with the preconditions that were violated last time, so your agent stops repeating mistakes it already made. ## Cognitive Profile as system prompt One call returns a ready-to-use system prompt built from all three memory types: ```js theme={null} import { generateText } from 'ai'; import { retrieveProfile } from 'mengram-ai-sdk'; const system = await retrieveProfile({ apiKey: process.env.MENGRAM_API_KEY, userId: 'user-123' }); // '' if the user has no memories yet — safe to pass unconditionally const { text } = await generateText({ model: yourModel, system, prompt: '...' }); ``` ## Save conversations Extraction runs server-side — pass raw messages, Mengram distills facts, events, and workflows: ```js theme={null} import { saveConversation } from 'mengram-ai-sdk'; await saveConversation(result.response.messages, { apiKey: process.env.MENGRAM_API_KEY, userId: 'user-123', }); ``` ## Multi-user apps Pass your end-user's id as `userId` — each user gets isolated facts, events, workflows, and profile under one API key. See [Memory API for agent builders](https://mengram.io/for-agents). ## Options ```js theme={null} mengramTools({ apiKey: 'om-...', // or MENGRAM_API_KEY userId: 'user-123', // multi-user isolation limit: 5, // max results per tool call baseUrl: 'https://...', // self-hosted instances }); ``` # API Reference Source: https://docs.mengram.io/api-reference Complete REST API documentation for Mengram with all endpoints, parameters, and response formats. ## Base URL ``` https://mengram.io ``` ## Authentication All `/v1/*` endpoints require a Bearer token: ``` Authorization: Bearer om-your-api-key ``` Get your API key at [mengram.io](https://mengram.io/#signup). *** ## Memory — Core ### POST /v1/add Add memories from a conversation. Automatically extracts entities, facts, episodes, and procedures in the background. ```bash theme={null} curl -X POST https://mengram.io/v1/add \ -H "Authorization: Bearer om-..." \ -H "Content-Type: application/json" \ -d '{ "messages": [ {"role": "user", "content": "I use Python and Railway"}, {"role": "assistant", "content": "Noted."} ], "user_id": "default" }' ``` | Parameter | Type | Default | Description | | ----------------- | ------- | ----------- | -------------------------------------------------------------------------------------- | | `messages` | array | required | Chat messages with `role` and `content` | | `user_id` | string | `"default"` | User identifier for multi-user isolation | | `agent_id` | string | null | Agent identifier | | `run_id` | string | null | Session/run identifier | | `app_id` | string | null | Application identifier | | `expiration_date` | string | null | ISO datetime — facts auto-expire after this | | `source` | string | null | Provenance source (e.g. `"discord"`, `"slack"`, `"email"`, `"api"`) | | `metadata` | object | null | Arbitrary provenance metadata (stored on entities and facts) | | `agent_mode` | boolean | false | Extract from all speakers (user + assistant). **Auto-enabled when `agent_id` is set.** | Response: `{"status": "accepted", "job_id": "job-..."}` ### POST /v1/add\_text Add memories from plain text instead of chat messages. ```json theme={null} {"text": "Meeting notes: migrating to PostgreSQL 16", "user_id": "default", "source": "slack", "metadata": {"channel": "#engineering"}} ``` Same optional parameters as `/v1/add` (including `source` and `metadata`). ### POST /v1/add\_file Upload a file (PDF, DOCX, TXT, MD) and extract structured memories. PDFs use two-pass vision AI extraction. Each page/chunk counts as 1 add from your quota. **Content-Type:** `multipart/form-data` (not JSON) ```bash theme={null} curl -X POST https://mengram.io/v1/add_file \ -H "Authorization: Bearer om-..." \ -F "file=@meeting-notes.pdf" \ -F "user_id=default" ``` | Parameter | Type | Default | Description | | ---------- | ------ | ----------- | --------------------------------------- | | `file` | file | required | File to upload (.pdf, .docx, .txt, .md) | | `user_id` | string | `"default"` | User identifier | | `agent_id` | string | null | Agent identifier | | `run_id` | string | null | Session/run identifier | | `app_id` | string | null | Application identifier | **File size limits:** Free 10MB, Pro 50MB, Business 100MB. Response (202): ```json theme={null} { "status": "accepted", "job_id": "job-...", "file_type": "pdf", "page_count": 12, "quota_used": 12, "message": "Processing meeting-notes.pdf (12 pages/chunks) in background." } ``` ### GET /v1/memories List all entities for a user. Supports pagination. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | --------------------------- | | `user_id` | string | `"default"` | User whose memories to list | | `limit` | int | 100 | Max results | | `offset` | int | 0 | Pagination offset | ### GET /v1/memories/full Get all memories with full facts, relations, and knowledge in a single request. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | --------------------------- | | `user_id` | string | `"default"` | User whose memories to list | | `limit` | int | 100 | Max results | | `offset` | int | 0 | Pagination offset | ### GET /v1/memory/:name Get details for a specific entity including facts, relations, knowledge, and metadata. Response includes a `metadata` object with provenance information (source, agent\_id, etc.). ### DELETE /v1/memory/:name Delete a specific entity and all its data. ### DELETE /v1/account Permanently delete your account and **all** associated data — entities, facts, relations, knowledge, embeddings, episodes, procedures, conversation chunks, webhooks, teams you created, API keys, and usage history. Irreversible, immediate, and synchronous. Returns per-table deletion counts so you can verify completion. ```bash theme={null} curl -X DELETE "https://mengram.io/v1/account?confirm=you@example.com" \ -H "Authorization: Bearer om-..." ``` | Parameter | Type | Description | | --------- | ------ | --------------------------------------------------- | | `confirm` | string | **Required.** Must exactly match your account email | An active paid subscription is canceled in the billing provider first; if that cancellation fails, deletion aborts (502) so you're never billed for an account whose data is gone. Works identically on self-hosted deployments. ### DELETE /v1/memories/all Delete ALL memories for a user. Irreversible. *** ## Search ### POST /v1/search Semantic search across the knowledge graph with LLM re-ranking. ```json theme={null} {"query": "database preferences", "user_id": "default", "limit": 5, "graph_depth": 2} ``` | Parameter | Type | Default | Description | | ------------- | ------ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `query` | string | required | Natural language search query | | `user_id` | string | `"default"` | User whose memories to search | | `limit` | int | 5 | Max results | | `graph_depth` | int | 2 | Knowledge graph traversal depth (0-4) | | `agent_id` | string | null | Filter by agent | | `run_id` | string | null | Filter by run | | `app_id` | string | null | Filter by app | | `filters` | object | null | Metadata key-value filters (JSONB containment) | | `max_tokens` | int | null | Room the reply may take, in estimated tokens (50–50,000). Results are cut in rank order to fit — entity by entity, fact by fact — and the response adds a `budget` report. | **A recall that fits.** With `max_tokens`, the response carries what was cut, so the cut is never silent: ```json theme={null} {"query": "what does this customer prefer?", "user_id": "cust_1042", "limit": 5, "max_tokens": 600} ``` ```json theme={null} { "results": [...], "budget": { "max_tokens": 600, "used_tokens": 512, "method": "estimate", "kept": {"entities": 3, "facts": 14}, "dropped": {"entities": 2, "facts": 9, "relations": 1, "knowledge": 2} } } ``` Tokens are estimated (about four characters per token for Latin text, fewer for other scripts) — the server does not know which model reads the reply. Budget around 15% under your real limit. **Where each fact came from.** Every result carries `facts_meta`, one entry per fact, aligned with `facts`: ```json theme={null} { "entity": "app", "type": "project", "score": 0.91, "facts": ["deploys to Fly.io from main", "uses PostgreSQL 16"], "facts_meta": [ {"source": "codex", "os": "linux", "cwd": "/home/ali/app", "session": "s-41", "when": "2026-09-15", "recalled": 3, "last_recalled": "2026-09-16"}, {"source": "api", "when": "2026-09-10", "recalled": 0} ] } ``` `source` is what the writer declared on `/v1/add` (`source` and `metadata`); the CLI hooks send `claude-code`, `codex` or `cursor` with the session, working directory and OS, the MCP server sends `mcp`, everything else is `api`. `recalled` and `last_recalled` count retrievals, so a fact nobody has asked for in months is visible as such. Facts are never rewritten; the tag the hooks show (`(codex, 2026-09-15)`) is built from this at read time. **A host does not inherit another host's paths.** Send `X-Mengram-Host: /` (for example `linux/claude-code`; the hooks do) and facts tied to another machine or tool are left out: a `/Users/...` path recorded on a Mac, a `~/.cursor/hooks.json` fact recorded from Cursor. Each result then says how many were withheld in `facts_left_out_for_host`. Without the header nothing is filtered, and facts saved without provenance are never filtered. From Python: `CloudMemory(api_key, host="linux/claude-code")`. ### POST /v1/search/all Unified search across all 3 memory types. ```json theme={null} {"query": "deployment", "user_id": "default", "limit": 5, "max_tokens": 800} ``` `max_tokens` works here too: one budget across the sections, spent in the order a task needs them — facts about the entities, then what happened, then how it was done, then raw conversation last. The `budget` report also counts `episodes`, `procedures` and `chunks`. `chunks` (default `1`, `0` to `10`) is how many raw conversation chunks may come back as the fallback for facts extraction missed. One is the default because on a 90-day history five of them were three quarters of the tokens handed to the model while one kept recall of old facts unchanged; `0` turns the fallback off. Response: ```json theme={null} { "semantic": [...], "episodic": [...], "procedural": [...] } ``` ### POST /v1/ask **Pro / Growth / Business only.** Free and Starter plans return 403. Ask your memory a question and get a synthesized answer with citations — not a raw fact list. Mengram embeds the query, retrieves the top relevant facts, and uses Cohere Chat (`command-a-03-2025`) to write a grounded answer with native source attribution. Counts as 1 search against your monthly quota. ```bash theme={null} curl -X POST https://mengram.io/v1/ask \ -H "Authorization: Bearer om-..." \ -H "Content-Type: application/json" \ -d '{"query": "what programming languages do I use?", "user_id": "default"}' ``` | Parameter | Type | Default | Description | | ----------- | ------ | ----------- | ------------------------------------------------------------------------ | | `query` | string | required | Natural language question | | `user_id` | string | `"default"` | User whose memories to use | | `max_facts` | int | 15 | How many top facts to feed Cohere as documents (capped at 30 internally) | Response: ```json theme={null} { "answer": "You use Python and Rust. Python is your daily language and Rust is your favorite. You also use Java for enterprise systems.", "citations": [ { "text": "Python and Rust", "start": 8, "end": 23, "sources": [ {"entity": "Python", "fact": "uses Python daily for backend development"}, {"entity": "Rust", "fact": "Rust is favorite language"} ] }, { "text": "Java for enterprise systems", "start": 92, "end": 119, "sources": [ {"entity": "Ali Baizhanov", "fact": "specializes in Java/Spring Boot for enterprise"} ] } ], "facts_used": 22 } ``` | Field | Type | Description | | --------------------- | ------ | ---------------------------------------------------------------------------------- | | `answer` | string | Synthesized answer text. Empty string if Cohere can't answer from retrieved facts. | | `citations` | array | Citation objects linking spans of `answer` back to specific facts. May be empty. | | `citations[].text` | string | The cited substring as it appears in `answer` | | `citations[].start` | int | Start character offset in `answer` | | `citations[].end` | int | End character offset in `answer` | | `citations[].sources` | array | Facts that support this citation (entity name + fact text) | | `facts_used` | int | How many facts were passed to Cohere (capped at 30) | **When `answer` is empty / `citations` is empty:** Cohere chose not to answer from the retrieved facts (e.g. no relevant data, or query was ambiguous). This is **not an error** — it's an honest "I don't know" rather than a hallucinated response. **Multilingual:** Works natively across all 23 supported languages. Russian/Chinese/Japanese/Spanish queries return answers in the query language with citations to the original (often English) facts. ### GET /v1/timeline Temporal search — find facts within a time range. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | ------------------ | | `after` | string | null | ISO datetime start | | `before` | string | null | ISO datetime end | | `user_id` | string | `"default"` | User to search | | `limit` | int | 20 | Max results | *** ## Cognitive Profile ### GET /v1/profile Generate a Cognitive Profile — a ready-to-use system prompt summarizing a user from memory. | Parameter | Type | Default | Description | | --------- | ------- | ----------- | ------------------------- | | `user_id` | string | `"default"` | User to profile | | `force` | boolean | false | Regenerate (bypass cache) | ### GET /v1/profile/:user\_id Generate a profile for a specific sub-user. *** ## Rules Generation ### GET /v1/rules Generate a CLAUDE.md, .cursorrules, or .windsurfrules file from memory. | Parameter | Type | Default | Description | | --------- | ------- | ------------- | ----------------------------------------------------- | | `format` | string | `"claude_md"` | Output format: `claude_md`, `cursorrules`, `windsurf` | | `force` | boolean | false | Regenerate (bypass cache) | | `user_id` | string | `"default"` | User whose memories to use | Response: `{"format": "claude_md", "content": "# Project Rules\n..."}` *** ## Episodic Memory ### GET /v1/episodes List recent episodes. Each episode has a summary, context, outcome, and participants. Paginated — the response includes `total`, `limit`, and `offset` so you can page through everything. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | ---------------------------- | | `limit` | int | 20 | Max results per page (1–500) | | `offset` | int | 0 | Pagination offset | | `after` | string | null | ISO datetime filter start | | `before` | string | null | ISO datetime filter end | | `user_id` | string | `"default"` | User to query | ### GET /v1/episodes/search Semantic search over episodic memories. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | ------------------------- | | `query` | string | required | Search query | | `limit` | int | 5 | Max results | | `after` | string | null | ISO datetime filter start | | `before` | string | null | ISO datetime filter end | | `user_id` | string | `"default"` | User to search | *** ## Procedural Memory ### GET /v1/procedures List learned workflows and procedures. Paginated — the response includes `total`, `limit`, and `offset`. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | ---------------------------- | | `limit` | int | 20 | Max results per page (1–500) | | `offset` | int | 0 | Pagination offset | | `user_id` | string | `"default"` | User to query | ### GET /v1/procedures/search Semantic search over procedural memories. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | -------------- | | `query` | string | required | Search query | | `limit` | int | 5 | Max results | | `user_id` | string | `"default"` | User to search | ### PATCH /v1/procedures/:id/feedback Record success or failure for a procedure. On failure with context, triggers experience-driven evolution (Pro and above). This is not the only way a procedure evolves. Mengram also infers failures from episodes as they are added and revises the matching procedure on its own, which in practice is the more common path. A revision only earns a new version number once the current one has succeeded at least once — a version nobody ran is a hypothesis, so it is edited in place rather than superseded. | Parameter | Type | Default | Description | | ---------------- | ------- | ----------- | ------------------------------------ | | `success` | boolean | required | Whether the procedure worked | | `context` | string | null | What went wrong (triggers evolution) | | `failed_at_step` | int | null | Which step number failed | | `user_id` | string | `"default"` | User to query | Failure-driven revisions record the **violated assumption** (the specific belief that turned out false — not just the step number) in the evolution history, and derive a **precondition check** that accumulates in the procedure's `metadata.preconditions` — so recall returns not just the steps but what to verify before trusting them. ### GET /v1/procedures/:id/history Get version history for a procedure — how it evolved over time. ### GET /v1/procedures/:id/evolution Get the evolution log — what changed at each version and why. *** ## Entity Management ### DELETE /v1/entity/:name Delete an entity and all its facts, relations, knowledge, and embeddings. ### PATCH /v1/entity/:name/type Fix an entity's type classification. ```json theme={null} {"type": "technology"} ``` Valid types: `person`, `project`, `technology`, `company`, `concept`, `unknown`. ### POST /v1/entity/:name/dedup LLM-powered deduplication of facts on a single entity. *** ## Memory Management ### POST /v1/dedup Find and merge duplicate entities using word-boundary matching. ### POST /v1/dedup\_all Deduplicate facts across ALL entities for the user. ### POST /v1/merge Merge source entity into target. Source gets deleted, all data moves to target. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | -------------------- | | `source` | string | required | Entity to merge from | | `target` | string | required | Entity to merge into | | `user_id` | string | `"default"` | User | ### POST /v1/merge\_user Merge the generic "User" entity into the primary person entity. ### POST /v1/identity Pin which entity is **you**. Extraction context, "User" merging, and profile generation anchor to the pinned entity instead of guessing by heuristics — fixes identity drift when third parties (collaborators, tool authors) are frequently co-mentioned in your conversations. ```bash theme={null} curl -X POST "https://mengram.io/v1/identity?entity=YourName" \ -H "Authorization: Bearer om-..." ``` | Parameter | Type | Default | Description | | --------- | ------ | ----------- | -------------------------------------------- | | `entity` | string | required | Exact name of the entity that represents you | | `user_id` | string | `"default"` | Sub-user scope | Returns `{"status": "pinned", "entity": "...", "entity_id": "..."}`. Pinning clears the flag from any previously pinned entity. 404 if the entity doesn't exist. ### POST /v1/archive\_fact Archive a specific fact (soft-delete). ```json theme={null} {"entity_name": "PostgreSQL", "fact_content": "Uses version 15"} ``` *** ## Insights & Reflections ### POST /v1/reflect Manually trigger memory reflection. Generates AI insights from facts. ### GET /v1/reflections Get all reflections. Optional `scope` filter: `entity`, `cross`, `temporal`. Each item includes its `id` for individual deletion. ### DELETE /v1/reflections/:id Delete a single reflection by id. Use when a generated reflection is wrong or polluted — the next reflection pass regenerates from clean facts. ```bash theme={null} curl -X DELETE "https://mengram.io/v1/reflections/" \ -H "Authorization: Bearer om-..." ``` Returns `{"status": "deleted", "reflection_id": "..."}`, or 404 if the id doesn't exist for this user. ### GET /v1/insights Get formatted AI insights for dashboard display. *** ## Agents ### POST /v1/agents/run Run memory agents in the background. Returns a job\_id. | Parameter | Type | Default | Description | | ---------- | ------- | ----------- | -------------------------------------------------------- | | `agent` | string | `"all"` | Agent to run: `curator`, `connector`, `digest`, or `all` | | `auto_fix` | boolean | false | Auto-archive bad facts | | `user_id` | string | `"default"` | User to run agents for | ### GET /v1/agents/history Get agent run history. Optional `agent` filter and `limit` parameter. ### GET /v1/agents/status Check which agents are due to run. *** ## Smart Triggers ### GET /v1/triggers Get smart triggers for the authenticated user — reminders, contradictions, patterns. | Parameter | Type | Default | Description | | --------------- | ------- | ----------- | ------------------------------ | | `include_fired` | boolean | false | Include already-fired triggers | | `limit` | int | 50 | Max results | | `user_id` | string | `"default"` | User to query | ### GET /v1/triggers/:user\_id Get triggers for a specific sub-user. ### POST /v1/triggers/process Process all pending triggers — sends webhooks for triggered items. ### DELETE /v1/triggers/:trigger\_id Dismiss a specific trigger without firing its webhook. ### POST /v1/triggers/detect/:user\_id Manually run trigger detection for a user. Returns detailed results. *** ## Knowledge Graph ### GET /v1/graph Get the knowledge graph for visualization. Returns nodes and edges. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | ------------- | | `user_id` | string | `"default"` | User to query | ### GET /v1/feed Activity feed — recent facts with timestamps. | Parameter | Type | Default | Description | | --------- | ------ | ----------- | ------------- | | `limit` | int | 50 | Max items | | `user_id` | string | `"default"` | User to query | *** ## Webhooks ### POST /v1/webhooks Create a webhook. ```json theme={null} { "url": "https://your-app.com/hook", "name": "Production", "event_types": ["memory_add", "memory_update", "memory_delete"], "secret": "your-hmac-secret" } ``` ### GET /v1/webhooks List all webhooks. ### PUT /v1/webhooks/:id Update a webhook (url, name, event\_types, active). ### DELETE /v1/webhooks/:id Delete a webhook. *** ## Teams ### POST /v1/teams Create a team. Returns team info with invite code. ```json theme={null} {"name": "Engineering", "description": "Shared engineering memory"} ``` ### GET /v1/teams List your teams. ### POST /v1/teams/join Join a team via invite code. ```json theme={null} {"invite_code": "abc123"} ``` ### GET /v1/teams/:team\_id/members Get team members. ### POST /v1/teams/:team\_id/share Share a memory entity with a team. ```json theme={null} {"entity_name": "PostgreSQL"} ``` ### POST /v1/teams/:team\_id/unshare Make a shared memory personal again. ### POST /v1/teams/:team\_id/leave Leave a team. ### DELETE /v1/teams/:team\_id Delete a team (owner only). *** ## API Keys ### GET /v1/keys List all API keys for your account. ### POST /v1/keys Create a new API key. ```json theme={null} {"name": "production-key"} ``` ### DELETE /v1/keys/:key\_id Revoke a specific API key. ### PATCH /v1/keys/:key\_id Rename an API key. ```json theme={null} {"name": "new-name"} ``` *** ## Jobs ### GET /v1/jobs/:id Check status of a background job. Response: `{"status": "completed|processing|failed", ...}` *** ## Billing ### GET /v1/billing Get current subscription plan, usage, and quotas. ### POST /v1/billing/checkout Create a Paddle checkout session for plan upgrade. ```json theme={null} {"plan": "pro"} ``` ### POST /v1/billing/portal Create a Paddle customer portal session for managing subscription. *** ## Statistics ### GET /v1/stats Get usage statistics for the authenticated user. `facts` counts **active** facts only (matching what export and list endpoints return); facts archived by conflict resolution/dedup are reported separately as `archived_facts`. ### GET /v1/me Get current account info (email, plan, user\_id). *** ## Voice (Vapi) Webhook adapters that turn Vapi's tool-call and end-of-call events into Mengram memory operations. See the full guide at [Vapi Integration](/vapi). ### POST /v1/voice/vapi/recall Called by Vapi as a custom tool. Returns a concise context string the assistant verbalizes at the start of a call. **Request body** (Vapi sends both shapes; either accepted): ```json theme={null} { "message": { "type": "tool-calls", "toolCallList": [{ "id": "call_abc", "name": "recall_caller", "arguments": { "phone": "+15551234567" } }], "call": { "customer": { "number": "+15551234567" } } } } ``` Or the OpenAI-nested form (`toolCalls` with `function.name` + `function.arguments` — the latter may arrive as a JSON string): ```json theme={null} { "message": { "type": "tool-calls", "toolCalls": [{ "id": "call_abc", "type": "function", "function": { "name": "recall_caller", "arguments": "{\"phone\": \"+15551234567\"}" } }] } } ``` **Phone resolution order:** tool arguments → `message.call.customer.number` → "Unknown caller" fallback. **Response** (200 — Vapi tool-result format, `result` MUST be a string): ```json theme={null} { "results": [{ "toolCallId": "call_abc", "result": "Known about caller (Sarah Johnson): Sarah Johnson: prefers morning slots before 11 AM | Sarah Johnson: gets anxiety with novocaine" }] } ``` If `message.type` is anything other than `tool-calls` (Vapi sends many event types to the same server URL), the endpoint returns `200 {"status":"ignored","reason":"..."}` rather than 4xx — so the assistant isn't marked broken when lifecycle events arrive. **Quota:** 1 search per call. ### POST /v1/voice/vapi/save Called by Vapi at end of call. Routes the final transcript through Mengram's extraction pipeline, keyed per caller via `sub_user_id=voice:`. **Request body:** ```json theme={null} { "message": { "type": "end-of-call-report", "endedReason": "customer-ended-call", "call": { "id": "...", "customer": { "number": "+15551234567" } }, "transcript": "Agent: ...\nCaller: ..." } } ``` The transcript can also live at `message.artifact.transcript` — Mengram reads whichever is present. **Response** (202 — extraction runs in the background like `/v1/add`): ```json theme={null} { "status": "accepted", "job_id": "job-xxxx", "sub_user_id": "voice:+15551234567" } ``` **Only `end-of-call-report` triggers extraction.** Partial `transcript` events (Vapi streams these mid-call) and other lifecycle events are ignored with `{"status":"ignored"}` so the extraction pipeline doesn't fire repeatedly per call. **Quota:** 1 add per call. *** ## Error Responses All endpoints return structured JSON errors. ### 401 Unauthorized Invalid or missing API key. ```json theme={null} {"detail": "Invalid API key"} ``` ### 402 Quota Exceeded Monthly plan limit reached. Includes upgrade URL and retry timing. ```json theme={null} { "error": "quota_exceeded", "action": "add", "limit": 50, "used": 50, "plan": "free", "upgrade_url": "https://mengram.io/checkout?token=...", "message": "Monthly add limit reached (50). Upgrade to Starter ($5/mo) at ...", "retry_after": 864000 } ``` The response also includes a `Retry-After` header (seconds until monthly reset). | Field | Type | Description | | ------------- | ------ | ----------------------------------------------------- | | `error` | string | Always `"quota_exceeded"` | | `action` | string | Which action was blocked: `add`, `search`, `add_file` | | `limit` | int | Monthly limit for this action | | `used` | int | Current usage count | | `plan` | string | Current plan: `free`, `starter`, `pro`, `business` | | `upgrade_url` | string | Direct upgrade link (includes signed checkout token) | | `message` | string | Human-readable message with next plan and price | | `retry_after` | int | Seconds until quota resets (end of month) | ### 429 Rate Limited Too many requests. Retry after the `Retry-After` header value. ```json theme={null} {"detail": "Rate limit exceeded"} ``` *** ## Response Headers Every authenticated response includes rate limit and quota usage headers. ### Rate Limiting | Header | Description | | ----------------------- | ------------------------------- | | `X-RateLimit-Limit` | Requests allowed per minute | | `X-RateLimit-Remaining` | Requests remaining this minute | | `X-RateLimit-Reset` | Seconds until rate limit resets | ### Quota Usage | Header | Description | | ---------------------- | ------------------------------- | | `X-Quota-Add-Used` | Add calls used this month | | `X-Quota-Add-Limit` | Add calls allowed this month | | `X-Quota-Search-Used` | Search calls used this month | | `X-Quota-Search-Limit` | Search calls allowed this month | SDKs expose quota headers via the `.quota` property — see [Python SDK](/python-sdk#quota-usage) and [JavaScript SDK](/javascript-sdk#quota-usage). *** ## Health ### GET /v1/health Health check. Returns basic status for unauthenticated requests; detailed diagnostics for authenticated. For interactive API docs, see [Swagger UI](https://mengram.io/swagger) or [ReDoc](https://mengram.io/redoc). # Async Client Source: https://docs.mengram.io/async-client Non-blocking Python client built on httpx for async/await workflows. ## Installation ```bash theme={null} pip install mengram-ai[async] ``` This installs `httpx` for non-blocking HTTP. ## Initialize ```python theme={null} from mengram import AsyncMengram m = AsyncMengram(api_key="om-your-key") # Or use environment variable m = AsyncMengram() ``` ## Context Manager ```python theme={null} async with AsyncMengram() as m: results = await m.search("deployment") profile = await m.get_profile() # Client automatically closed ``` ## All Methods Every method is async and must be `await`-ed: ```python theme={null} import asyncio from mengram import AsyncMengram async def main(): m = AsyncMengram() # Add memories result = await m.add([ {"role": "user", "content": "Deployed on Railway with PostgreSQL"}, ]) # Upload a file (PDF, DOCX, TXT, MD) await m.add_file("meeting-notes.pdf") # Search results = await m.search("deployment") # Unified search all_results = await m.search_all("issues") # Profile profile = await m.get_profile() # Episodes & procedures events = await m.episodes(query="deployment") procs = await m.procedures(query="deploy") # Feedback await m.procedure_feedback(proc_id, success=True) # Graph & timeline graph = await m.graph() facts = await m.timeline(after="2026-01-01") # Agents & insights await m.run_agents() await m.reflect() insights = await m.insights() # Memory management await m.dedup() await m.merge("source", "target") await m.archive_fact("Entity", "old fact") # Triggers triggers = await m.get_triggers() # Webhooks await m.create_webhook(url="https://example.com/hook") hooks = await m.get_webhooks() # Jobs status = await m.job_status("job-abc") result = await m.wait_for_job("job-abc") # Close when done await m.close() asyncio.run(main()) ``` ## Available Methods | Method | Description | | --------------------------------- | -------------------------------- | | `add(messages, ...)` | Add memories from conversation | | `add_text(text, ...)` | Add memories from plain text | | `add_file(file_path, ...)` | Upload file (PDF, DOCX, TXT, MD) | | `search(query, ...)` | Semantic search | | `search_all(query, ...)` | Unified search (all 3 types) | | `get_all(...)` | List all entities | | `get(name, ...)` | Get specific entity | | `delete(name, ...)` | Delete entity | | `stats(...)` | Usage statistics | | `get_profile(...)` | Cognitive Profile | | `episodes(...)` | Episodic memories | | `procedures(...)` | Procedural memories | | `procedure_feedback(id, ...)` | Record success/failure | | `graph(...)` | Knowledge graph | | `timeline(...)` | Temporal search | | `run_agents(...)` | Run memory agents | | `reflect(...)` | Trigger reflection | | `insights(...)` | Get AI insights | | `dedup(...)` | Deduplicate entities | | `merge(src, target, ...)` | Merge entities | | `archive_fact(entity, fact, ...)` | Archive a fact | | `get_triggers(...)` | Get smart triggers | | `create_webhook(url, ...)` | Create webhook | | `get_webhooks()` | List webhooks | | `job_status(id)` | Check job status | | `wait_for_job(id, ...)` | Poll until job completes | ## Retry & Error Handling The async client automatically retries on transient errors (429, 502, 503, 504) and network failures, with exponential backoff up to 3 attempts. **402 (quota exceeded) is never retried** — it raises `QuotaExceededError` immediately. ```python theme={null} from mengram import AsyncMengram from mengram.cloud.async_client import QuotaExceededError async def safe_add(m, messages): try: return await m.add(messages) except QuotaExceededError as e: print(f"Quota hit: {e.action} {e.current}/{e.limit} ({e.plan})") # e.action = "add" | "search" | "add_file" # e.plan = "free" | "starter" | "pro" ``` # Capture Policy Source: https://docs.mengram.io/capture-policy Set a server-side boundary on what your AI is allowed to remember — enforced before anything is stored, not a prompt asking the model to behave. Most memory products decide what to keep by asking the model nicely. A capture policy is different: it's a **deterministic, server-side boundary** applied *before* any extracted memory is persisted. Facts, episodes, and procedures that match a deny rule are dropped — never written to storage, never embedded, never surfaced later. Use it to keep whole categories of sensitive information (health, legal, financial…) out of long-term memory, block specific words or phrases, or restrict which sources are allowed to write at all. The policy runs in the extraction pipeline, not in the LLM prompt. It cannot be talked around, and its behavior is identical for the same input every time. ## How it works Every `add` goes through the boundary in two stages: 1. **Source gate** — if `allow_sources` is set, an add is only accepted when its `metadata.source` is on the list; an add whose source is in `deny_sources` is skipped entirely (nothing from that add is stored). 2. **Content deny** — the enabled category packs and your custom `deny_keywords` are compiled into one lowercased keyword list. Each extracted fact, episode, and procedure is matched case-insensitively; any that contain a denied keyword as a substring are dropped, the rest are kept. An empty policy (the default) captures everything. ## Fields | Field | Type | Effect | | ----------------- | ---------- | -------------------------------------------------------------------------------------------- | | `deny_categories` | `string[]` | Enable one or more built-in packs (below). Everything matching a pack's keywords is dropped. | | `deny_keywords` | `string[]` | Custom words or phrases to never store. Multi-word phrases work (`"project atlas"`). | | `deny_sources` | `string[]` | Adds with `metadata.source` in this list are skipped entirely. | | `allow_sources` | `string[]` | If set, **only** adds whose `metadata.source` is in this list are accepted. | ## Built-in category packs | Category | Blocks memory about… | | --------------- | ---------------------------------------------------------------------- | | `health` | diagnoses, medications, symptoms, therapy, mental-health conditions | | `legal` | lawsuits, litigation, attorneys, settlements, criminal charges | | `financial` | bank/account/card numbers, SSN, salary, tax returns, wire transfers | | `credentials` | passwords, API keys, tokens, private/SSH keys, seed phrases, OTP codes | | `location` | home/street address, zip code, GPS coordinates | | `relationships` | spouse/partner, divorce, custody, children, family conflict | `credentials` is a belt-and-suspenders layer. Secrets are already redacted on import (client-side) and again server-side in extraction — enabling this pack additionally drops any *fact* that mentions a credential, not just the secret value. The current list of category keys is always returned by `GET /v1/capture-policy` under `available_categories`, so you never have to hardcode it. ## Read the current policy ```bash theme={null} curl https://mengram.io/v1/capture-policy \ -H "Authorization: Bearer om-..." ``` ```json theme={null} { "capture_policy": { "deny_categories": ["health", "credentials"] }, "available_categories": [ "health", "legal", "financial", "credentials", "location", "relationships" ] } ``` ## Set the policy `PUT` replaces the stored policy. Send only the fields you want; omitted or empty fields are cleared. ```bash theme={null} curl -X PUT https://mengram.io/v1/capture-policy \ -H "Authorization: Bearer om-..." \ -H "Content-Type: application/json" \ -d '{ "deny_categories": ["health", "financial", "credentials"], "deny_keywords": ["project atlas", "acquisition"], "allow_sources": ["claude-code", "cursor"] }' ``` ```json theme={null} { "status": "saved", "capture_policy": { "deny_categories": ["health", "financial", "credentials"], "deny_keywords": ["project atlas", "acquisition"], "allow_sources": ["claude-code", "cursor"] } } ``` An unknown category returns `400` with the list of valid categories. ## Notes * The policy is per **account** and applies to every `add`, from every SDK, integration, and MCP client. * It affects **new** captures only — it does not retroactively remove memories stored before the rule existed. * Dropped items are silently omitted from extraction results; the add still succeeds for anything that passes. * You can also manage the policy visually from the **Capture Policy** section of the [dashboard](https://mengram.io/dashboard). # Claude Code Integration Source: https://docs.mengram.io/claude-code Memory that survives /clear and auto-compaction — auto-save conversations, auto-recall context on every prompt, load your profile on session start, a policy gate that asks before Claude runs a workflow with a weak record, and a checkpoint that carries the working state through compaction. The same hooks run under Codex. ## Overview Mengram integrates with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) hooks to create a full memory loop that survives `/clear`, **auto-compaction**, machine switches, and team handoffs — the SessionStart hook fires after every compact and re-injects your context: 1. **Session context** — loads your cognitive profile when a session starts, so Claude knows who you are 2. **Auto-recall** — searches relevant memories on every prompt and injects them as context 3. **Auto-save** — captures conversations in the background to build up memory over time 4. **Policy gate** — before a workflow-shaped Bash command runs, checks it against the workflows memory has learned; if the match has a weak record, Claude has to ask you first 5. **Compaction checkpoint** — before the context is compacted, writes the working state down (last prompts, files edited, last commands, where Claude left off) and puts it back verbatim after; the host's summary can drop it, the checkpoint can't Zero manual effort. One command to install. The same hooks install under [Codex](#codex-the-same-hooks) with one flag. ## Quick Setup ### Option A — plugin from the marketplace (recommended) ```bash theme={null} # 1. Save your API key once (free key at https://mengram.io) mkdir -p ~/.mengram && echo '{"api_key": "om-your-key-here"}' > ~/.mengram/config.json # 2. Install the plugin claude plugin marketplace add alibaizhanov/mengram claude plugin install mengram@mengram ``` The plugin ships the hooks, the MCP server (30 tools), and a skill. Hooks read the key from `~/.mengram/config.json` (an exported `MENGRAM_API_KEY` env var wins if set). **First-run self-check:** until the plugin verifies one successful API round-trip, failures show a one-line message telling you exactly what's broken. After the first success, failures are silent — an outage never spams you or blocks Claude Code. ### Option B — CLI hooks ```bash theme={null} pip install mengram-ai mengram setup ``` This creates your account (if needed), saves your API key, and installs all six hooks automatically. Restart Claude Code — done. The compaction checkpoint (v2.44+) ships with the CLI hooks only. If you use the plugin, run `mengram hook install` as well — the two coexist. ## Skip the cold start — import your history Your past Claude Code sessions are already on disk. Feed them in and memory starts full, not empty: ```bash theme={null} pip install mengram-ai mengram import claude-code ``` Secrets (API keys, tokens) are redacted client-side before upload. Then ask Claude: *"what do you know about my projects?"* ## How It Works ### 1. Session Start — Profile Loaded When you open Claude Code (or after context compaction), the `SessionStart` hook fires: ``` Session starts → mengram auto-context runs → Loads your Cognitive Profile from Mengram (GET /v1/profile) → Prints it to stdout — Claude sees it as context Claude now knows: your name, preferences, tech stack, current projects ``` After a compaction or a `--resume`, the working state saved by the [checkpoint](#6-before-compaction-the-working-state) comes first, above the profile. ### 2. Every Prompt — Relevant Memories Recalled When you type a prompt, the `UserPromptSubmit` hook fires **before** Claude responds: ``` You type: "how did we deploy to Railway?" → mengram auto-recall runs → Searches Mengram for relevant memories (POST /v1/search) → Returns additionalContext that Claude sees: [Mengram Memory — relevant context from past sessions] Railway Deployment: - Auto-deploys from main branch (claude-code, 2026-09-02) - Uses gunicorn with 1 worker (codex, 2026-09-11) - DATABASE_URL uses Session Mode port 5432 (claude-code, 2026-09-02) Claude responds WITH context from your past sessions ``` The tag after each fact says which tool wrote it and when (since 2.46.0). The hook also tells the server which OS and tool it runs under, so a workspace path recorded on your Mac never reaches a session on a Linux box, and a Cursor settings fact never reaches Claude Code — the server leaves those out and counts them in `facts_left_out_for_host`. ### 3. After Response — Conversation Saved After Claude responds, the `Stop` hook fires asynchronously in the background: ``` Claude finishes responding → mengram auto-save runs (background, non-blocking) → Every 3rd response, sends [user + assistant] to POST /v1/add → API extracts entities, facts, episodes, procedures → Memory grows over time ``` ### 4. Before a Bash command — the policy gate Outcome history should change what the agent is allowed to do, not only how results rank. When Claude is about to run a command that looks like a workflow (`git push`, `deploy`, `migrate`, `kubectl`, `terraform`, `rm -rf` …), the `PreToolUse` hook fires: ``` Claude wants to run: git push origin main → mengram auto-policy runs → Finds the learned workflow this matches (GET /v1/procedures/search) → Reads its record: untested · 61% expected · 88% reliable 88% reliable → silent, the command runs as it would have untested → "ask": you see why, Claude gets the steps on record 61% expected → "ask": this version has never run; 61% is inherited from the one it replaced 58% reliable → "ask": below the bar (default 70%) ``` The prompt you see is a normal Claude Code permission prompt, labelled `[settings]`, with the reason: *"Mengram: learned workflow 'deploy to Railway' has never been run. Review the plan before it runs."* Claude receives the workflow's steps, preconditions and last failure as context, so if you decline it can show you the plan instead of guessing. The gate never denies. Memory can ask; it does not get to forbid. Commands that are not workflow-shaped (`ls`, `cat`, `grep`) never trigger a lookup, so the hook costs nothing on ordinary work. ### 5. After a Bash command — the record A gate is only as good as the counts behind it, and until 2.38 nothing produced them: extraction wrote workflows, nobody wrote down whether they worked, and every procedure stayed `untested` forever. The `PostToolUse` hook closes that loop. ``` Claude ran: twine upload dist/* → exit 0 → mengram auto-outcome runs → Recognises the command as step 3 of "Release to PyPI" → Records that step as a success. Nothing is printed. ``` It is deliberately reluctant, because a wrong record is worse than none: * **One command credits one step, never the whole workflow.** A run of `twine upload` is evidence about uploading and about nothing else. * **It writes only when the command *is* the step.** The command has to cover at least half the step's words, and normally has to name the same tool. Sharing a single word is a coincidence, not a match. * **An unclear outcome records nothing.** No exit code, or an interrupted command, means silence. A non-empty `stderr` is not a failure: plenty of healthy tools write there. * **Heredoc bodies are ignored.** A `cat >> notes.md` whose text happens to mention `pip install` did not install anything. A workflow whose steps have been watched working then reads as `reliable` instead of `untested`, and the weakest watched step is the one that sets the number: a chain is exactly as trustworthy as the link most likely to break. Run outcomes are recorded in folder mode (`--memory ./memory`). The cloud endpoint records whole runs only, so writing a single command there would inflate the record the gate reads; a step-scoped write for the API is next. | Setting | Default | What it does | | ---------------------------------- | ------------------------------------- | -------------------------------------------------------------- | | `MENGRAM_POLICY_MIN_RELIABLE` | `70` | Percent below which a workflow with a record is confirmed | | `MENGRAM_POLICY_PATTERN` | deploy / push / migrate / kubectl / … | Regex for "workflow-shaped"; `.*` gates every Bash command | | `MENGRAM_MEMORY_DIR` | unset | Read a memfmt folder instead of the cloud (no key, no network) | | `mengram hook install --no-policy` | — | Install the other three hooks without the gate | ### 6. Before compaction — the working state A compaction keeps the conversation but not the work. The host writes a summary for itself, and what the summary drops is invisible from inside the session: which files were just edited, what you asked three prompts ago, where Claude left off. Claude carries on from the summary as if nothing were missing — and edits a file from a stale idea of it, or does a finished step twice. The `PreCompact` hook fires before the summary is written, with the transcript path in hand: ``` Context is about to be compacted (auto or /compact) → mengram auto-checkpoint runs → Reads the transcript's tail and keeps what locates the work: the last 4 prompts · files edited · last 8 commands · the last thing Claude said → Writes ~/.mengram/checkpoints/.json (never uploaded) Compaction happens; the session continues → SessionStart fires with source: compact → mengram auto-context finds the checkpoint and puts it back, verbatim: [Mengram — working state saved before compaction (2 min ago; the host's summary may have dropped some of this)] Directory: /Users/you/app Last things the user asked, oldest first: - Add a PreCompact hook to the CLI - Also cover Codex Files edited this session (re-read before editing again): - /Users/you/app/cli.py - /Users/you/app/local/checkpoint.py Last commands run: $ pytest -q tests/test_checkpoint.py Where the assistant left off: Tests written; running them now. ``` Nothing is extracted or rewritten: the value is that the record is exact. It is restored **once** — the file is consumed, so a later `/clear` in the same session does not replay old work — and it is restored with or without an account, and through a cloud outage, because it never left the machine. A `--resume` whose session id changed finds the newest checkpoint written from the same directory (up to 7 days old). A fresh `startup` or `/clear` never sees one. ### 7. What it did — the receipt A hook that works is invisible. The recalled fact goes into the prompt, the gate's question looks like any other permission prompt, the recorded outcome is a number in a file nobody opens. So each hook leaves one line behind, and the next session opens with the sum: ``` 🧠 Mengram, last session: recalled memories on 4 prompts · asked before 1 workflow with a weak record · recorded 3 step outcomes (2 ok, 1 failed) · caught 1 failure the host never reported · restored the working state after 1 compaction ``` Shown once, at session start — never on a resume or after compaction, which are the same session continuing — and not at all when nothing happened. When the gate fires, Claude also says so in one line before it goes on (*"Mengram flagged this: 'deploy to Railway' is 43% reliable (2✓/3✗); last failure 2026-07-30: the pool was cold."*), so the moment is attributed to memory rather than passing as an ordinary prompt. ```bash theme={null} mengram receipt # last session, and the past 7 days mengram receipt --days 30 ``` The ledger is `~/.mengram/receipts.jsonl`, whichever mode the hooks run in. It is bounded and append-only; delete it to start over. ### Full Loop ``` ┌──────────────────────────────────────────────┐ │ Session starts │ │ ↓ SessionStart → profile loaded │ │ │ │ You type a prompt │ │ ↓ UserPromptSubmit → relevant memory found │ │ │ │ Claude responds (with memory context) │ │ ↓ PreToolUse → weak workflow? ask first │ │ ↓ PostToolUse → step outcome recorded │ │ ↓ Stop → conversation saved (background) │ │ │ │ Context fills up │ │ ↓ PreCompact → working state written down │ │ ↓ SessionStart (compact) → put back, exact │ │ │ │ Next prompt → recall again... │ │ Next session → "last session: …" receipt │ └──────────────────────────────────────────────┘ ``` ## Codex — the same hooks Codex fires the same lifecycle events (`SessionStart`, `UserPromptSubmit`, `PreCompact`, …) and its hooks file has the same shape as Claude Code's, so the handlers are shared — one memory across both tools, and a working state that survives compaction in either. ```bash theme={null} pip install mengram-ai mengram hook install --codex ``` This writes `~/.codex/hooks.json` (merged if it already exists; nothing else in `~/.codex` is touched — inside Orca too, which rebuilds its own Codex home from that file): | Event | Command | What it does | | ------------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | | `SessionStart` | `mengram auto-context` | Loads your profile; after a compact or resume, the working state first | | `UserPromptSubmit` | `mengram auto-recall` | Relevant memories on each prompt | | `PreCompact` | `mengram auto-checkpoint --host codex` | Writes the working state down before compaction | | `Stop` | `mengram auto-save --every 3 --host codex` | Saves the turn and writes the [task card](/cli#mengram-resume), so Claude Code can pick up a task Codex started (2.48.0+) | The policy gate is not installed under Codex yet. Installed before 2.48.0? Run `mengram hook install --codex` again to add the Stop hook; Codex asks you to trust it. `mengram hook uninstall` removes the hooks from both Claude Code and Codex. Restart Codex after installing. ## Cursor — the same hooks, one door fewer Cursor has lifecycle hooks too (`~/.cursor/hooks.json`, flat `{"version": 1, "hooks": {event: [{command, timeout}]}}`). A hook may answer `{"additional_context": ...}` on `sessionStart` and `postToolUse`; `beforeSubmitPrompt` can only allow or block, so there is no per-prompt recall — mid-conversation, recall in Cursor is on request via the MCP tools. ```bash theme={null} mengram hook install --cursor ``` | Event | What it does | | -------------------- | ------------------------------------------------------------------------------------------------------- | | `sessionStart` | Loads your profile, plus the newest working state saved in this workspace within the last day | | `preCompact` | Writes the working state down before Cursor summarises the context | | `postToolUse` | Puts it back on the first tool call after compaction — Cursor opens no new session, so this is the door | | `afterAgentResponse` | Saves the agent's answer to memory | `mengram setup` finds Cursor on the machine and installs these on its own. Restart Cursor after installing. Cursor's transcript format is not documented; the checkpoint reader accepts plain `{"role", "content"}` lines and says "nothing to keep" rather than guess. ## Commands ### mengram hook install Installs all six hooks into `~/.claude/settings.json`. ```bash theme={null} mengram hook install # default: save every 3rd response mengram hook install --every 5 # save every 5th response mengram hook install --user-id myuser # custom user_id mengram hook install --no-policy # skip the policy gate mengram hook install --codex # Codex instead: ~/.codex/hooks.json ``` This adds the hooks to your Claude Code settings: ```json theme={null} { "hooks": { "SessionStart": [ { "hooks": [{ "type": "command", "command": "mengram auto-context", "timeout": 15 }] } ], "UserPromptSubmit": [ { "hooks": [{ "type": "command", "command": "mengram auto-recall", "timeout": 10 }] } ], "Stop": [ { "hooks": [{ "type": "command", "command": "mengram auto-save --every 3", "timeout": 30, "async": true }] } ], "PreToolUse": [ { "matcher": "Bash", "hooks": [{ "type": "command", "command": "mengram auto-policy", "timeout": 10 }] } ], "PostToolUse": [ { "matcher": "Bash", "hooks": [{ "type": "command", "command": "mengram auto-outcome", "timeout": 10 }] } ], "PreCompact": [ { "hooks": [{ "type": "command", "command": "mengram auto-checkpoint", "timeout": 10 }] } ] } } ``` ### mengram hook status Check status of all hooks. ```bash theme={null} mengram hook status ``` Output: ``` Mengram Hooks Auto-save: installed (every 3 responses) Auto-recall: installed Session context: installed Policy gate: installed Run outcomes: installed Checkpoint: installed Codex: installed (/Users/you/.codex/hooks.json) API Key: om-...Kb8 (set) API: connected (free plan) Settings: /Users/you/.claude/settings.json ``` ### mengram hook uninstall Remove all Mengram hooks — from Claude Code and, if installed there, from Codex. ```bash theme={null} mengram hook uninstall ``` ## Configuration | Option | Default | Description | | ----------- | ----------- | ------------------------------------------------------------------- | | `--every N` | 3 | Save every Nth response. Lower = more memories, higher = less noise | | `--user-id` | `"default"` | Mengram user\_id for multi-user isolation | ### Environment Variables | Variable | Required | Description | | ----------------------------- | -------- | ------------------------------------------------ | | `MENGRAM_API_KEY` | Yes | Your Mengram API key (starts with `om-`) | | `MENGRAM_URL` | No | Custom API URL (default: `https://mengram.io`) | | `MENGRAM_USER_ID` | No | Default user\_id (overridden by `--user-id`) | | `MENGRAM_POLICY_MIN_RELIABLE` | No | Policy gate bar, percent (default `70`) | | `MENGRAM_POLICY_PATTERN` | No | Regex for commands the policy gate looks at | | `MENGRAM_MEMORY_DIR` | No | memfmt folder for the policy gate's offline mode | ## Filtering **Auto-save** skips: * Short responses (\< 100 characters) — trivial confirmations * Interrupted requests * Responses when no API key is set **Auto-recall** skips: * Very short prompts (\< 10 characters) * Slash commands (`/help`, `/clear`, etc.) * Simple confirmations (`yes`, `no`, `ok`) **Checkpoint** skips: * A compaction with no transcript on disk, or a transcript with nothing to keep * A `startup` or `/clear` — the checkpoint is only offered after `compact` or `resume` * Anything older than 7 days when a resumed session has a new id **Policy gate** skips: * Any tool other than `Bash` * Commands that are not workflow-shaped (`ls`, `cat`, `grep`, `python -c …`) — no lookup, no cost * Matches whose record is at or above the bar — the command runs untouched * Semantic near-misses: a matched workflow must share at least one real word with the command before it can interrupt you ## Quota Limits When you hit your monthly plan limits, each hook surfaces a clear warning instead of failing silently: | Hook | What happens | Message shown | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | **Auto-recall** | Not charged since 2.44.1 — a search the hooks make carries `X-Mengram-Source: hook` and skips the search quota (still rate-limited). Older CLIs: Claude sees `[Mengram] Memory search quota exceeded — recall is disabled` | `pip install -U mengram-ai` | | **Auto-context** | Profile load fails | Session start shows: `[Mengram] Memory profile load failed — quota exceeded` | | **Auto-save** | Save quota exceeded | Terminal shows: `[Mengram] Memory save failed — Your conversations are NOT being saved` | All messages include an upgrade link. Claude Code is never blocked — hooks continue gracefully. Non-quota errors (network timeouts, transient failures) are silently swallowed so they never interrupt your workflow. To check your current usage: `mengram stats` or visit [mengram.io/dashboard](https://mengram.io/dashboard). ## Debugging: verbose markers and heartbeat **Verbose markers (CLI hooks):** add `--verbose` to any hook command in your hooks config and every exit path emits a one-line status marker — `[mengram:auto-recall] found 3 memories`, `[mengram:auto-save] throttled (2/3)`. Answers "is it working?" in one session restart. Off by default (byte-for-byte silent). **Heartbeat (plugin):** set `MENGRAM_HEARTBEAT=25` (env) or `"heartbeat": 25` in `~/.mengram/config.json` and every 25th successful save shows one line: `[mengram] heartbeat: 150 conversations saved to memory so far`. With the heartbeat on, silence means something is wrong. ## Troubleshooting **Hooks not firing?** * Restart Claude Code after installing * Check `mengram hook status` to verify the hooks are installed * Keys are read from `MENGRAM_API_KEY` env **or** `~/.mengram/config.json` (env wins) — check `cat ~/.mengram/config.json` **Memories not appearing?** * Auto-save processes in the background — check after \~30 seconds * Verify API connectivity: `mengram hook status` * Check your memories at [mengram.io/dashboard](https://mengram.io/dashboard) **Recall seems slow?** * Auto-recall has a 10-second timeout — if the API is slow, it's skipped gracefully * Claude Code continues normally even if a hook fails **Seeing quota warnings?** * You've hit your monthly plan limit — memory is disabled until the limit resets or you upgrade * Run `mengram stats` to check usage * Upgrade at [mengram.io/dashboard](https://mengram.io/dashboard) Run `mengram setup` to automatically save your API key to your shell profile and install all hooks in one step. # Claude Connector Source: https://docs.mengram.io/claude-connector Give Claude persistent memory in one click — add Mengram as a connector on claude.ai, Desktop, or Cowork. No CLI, no config. Claude forgets everything between chats. The Mengram connector fixes that: set a preference once and Claude recalls it weeks later — across every conversation on claude.ai, Desktop, and Cowork. No terminal, no config files, no API key to paste. This is the zero-setup path. If you use **Claude Code**, the [hooks integration](/claude-code) captures memory automatically; for **Cursor / Windsurf**, see the [MCP server](/mcp-server). ## Add it (30 seconds) 1. In Claude, open **Settings → Connectors**. 2. Click **Add custom connector**. 3. Fill in: * **Name:** `Mengram` * **Remote MCP server URL:** `https://mengram.io/mcp/connector` * Leave the **OAuth Client ID / Secret** fields (under Advanced) **empty**. 4. Click **Add**. Claude opens the Mengram sign-in page — enter your email and the code we send you. That's it; the connector is live. No account yet? The email step creates one automatically — free, no card. ## What Claude can do with it Once connected, Claude has four memory tools and uses them on its own: | Tool | What it does | | ------------------- | ----------------------------------------------------------------------------- | | **Remember** | Saves what matters from a conversation — facts, preferences, decisions. | | **Recall** | Searches your memory to answer "what do you know about…" questions. | | **Get context** | Loads a compact profile of who you are and how you work, for personalization. | | **List procedures** | Surfaces workflows you've taught Claude, with a success/failure track record. | Try it: tell Claude *"remember: I deploy with Railway"*, then in a **new chat** ask *"how do I deploy?"* — it recalls Railway with no re-explaining. ## Works everywhere Claude runs Available on **Free, Pro, Max, Team, and Enterprise** plans. Free Claude accounts can add one custom connector. The same memory is shared across claude.ai, Claude Desktop, and Cowork — and with your Claude Code / Cursor setups if you use those too. ## Privacy * **Server-side capture policy** — block whole categories (health, financial, credentials…) so sensitive information is never stored. See [Capture Policy](/capture-policy). * **Secrets redacted** before storage — API keys, tokens, and passwords never get saved. * Open-core (Apache-2.0), with a [self-hostable](/self-hosting) backend if you want full control. ## Remove or reconnect Settings → Connectors → Mengram → remove. Re-adding uses the same email sign-in and restores access to your existing memory. # CLI Reference Source: https://docs.mengram.io/cli Command-line interface for Mengram — setup, MCP server, data import, hooks, and rules generation. ## Installation ```bash theme={null} pip install mengram-ai ``` ## Commands ### mengram setup One command does the whole onboarding: creates your account, saves the API key, installs Claude Code hooks, **auto-detects and configures your other AI tools** (Cursor, Claude Desktop, Windsurf), **imports your existing Claude Code history** so memory is useful from minute one, and verifies the round-trip. ```bash theme={null} mengram setup mengram setup --email you@example.com # skip email prompt mengram setup --no-hooks # skip Claude Code hook install mengram setup --no-tools # skip Cursor/Claude Desktop/Windsurf MCP config mengram setup --no-import # skip Claude Code history import mengram setup --no-verify # skip the round-trip check ``` | Flag | Description | | ------------- | ----------------------------------------------------------------- | | `--email` | Email address (skip interactive prompt) | | `--key` | API key (skip signup, just save + configure) | | `--no-hooks` | Skip Claude Code hook installation | | `--no-tools` | Skip auto-configuring detected Cursor / Claude Desktop / Windsurf | | `--no-import` | Skip importing local Claude Code session history | | `--no-verify` | Skip the end-to-end verification | Detected tool configs are merged safely (existing servers preserved, a backup is written, unparseable files are never overwritten). If you already have a key, it asks whether to reconfigure. ### mengram weekly Your AI's memory, summarized for the past 7 days — including the one metric no other tool shows: **repeated mistakes prevented** (procedures with past failures that your memory recalled this week, each with the date you last got bitten by it). ```bash theme={null} mengram weekly # print the report mengram weekly --share # also print + copy a ready-to-post summary mengram weekly --no-color # plain output (for piping) ``` The report auto-shows once a week at the start of a Claude Code session (via the SessionStart hook) — shown to you, never added to the model's context. Pass `--no-weekly` to `mengram auto-context` to disable. ``` ┌────────────────────────────────────────────────────────┐ │ REPEATED MISTAKES PREVENTED 3 │ │ │ │ · psycopg2 pool deadlock last bitten: Apr 12 │ │ · wrong usage table (api_calls last bitten: Jul 20 │ │ · npx guess vs mengram server last bitten: Jun 30 │ └────────────────────────────────────────────────────────┘ ``` ### mengram init Interactive setup — creates config, vault, and Claude Desktop MCP integration. ```bash theme={null} mengram init mengram init --provider openai --api-key sk-... mengram init --mcp-only # only setup MCP integration ``` | Flag | Description | | ------------ | ---------------------------------------------- | | `--provider` | LLM provider: `openai`, `anthropic`, `ollama` | | `--api-key` | API key for the provider | | `--vault` | Custom vault path | | `--home` | Mengram home directory (default: `~/.mengram`) | | `--no-mcp` | Skip Claude Desktop MCP setup | | `--mcp-only` | Only setup MCP (config must exist) | ### mengram server Start the MCP server for Claude Desktop, Cursor, or Windsurf. ```bash theme={null} mengram server # local mode (your own LLM keys) mengram server --cloud # cloud mode (uses Mengram API) ``` | Flag | Description | | ---------- | ----------------------------------------------- | | `--config` | Config path (default: `~/.mengram/config.yaml`) | | `--cloud` | Use cloud API instead of local vault | ### mengram status Check setup status — config, vault, vector index, Claude Desktop integration. ```bash theme={null} mengram status ``` ### mengram stats Show vault statistics — entity counts, fact counts, vector index info. ```bash theme={null} mengram stats ``` ### mengram rules Generate a CLAUDE.md, .cursorrules, or .windsurfrules file from your cloud memory. ```bash theme={null} mengram rules # default: CLAUDE.md format mengram rules --format cursorrules # Cursor format mengram rules --format windsurf # Windsurf format mengram rules --force # regenerate (bypass cache) ``` | Flag | Description | | ---------- | ----------------------------------------------------- | | `--format` | Output format: `claude_md`, `cursorrules`, `windsurf` | | `--force` | Regenerate (bypass cache) | ### mengram hook Manage the memory hooks — session context, auto-recall, auto-save, policy gate, run outcomes, and the compaction checkpoint. See [Claude Code Integration](/claude-code) for details. ```bash theme={null} mengram hook install # Claude Code: install all six hooks mengram hook install --every 5 # save every 5th response mengram hook install --codex # Codex: the same hooks in ~/.codex/hooks.json mengram hook install --cursor # Cursor: the same hooks in ~/.cursor/hooks.json mengram hook status # check hook status (both tools) mengram hook uninstall # remove the hooks from both ``` ### mengram resume Where the task in this repository stands. The hooks (Claude Code, Codex, Cursor) write a task card when an agent stops — keyed by `git remote` + branch, so another session, another agent in another worktree of that branch or another machine finds it — and inject it at the next session start, saying whether the code has moved since the last check. A session on a branch with no card of its own — a fresh Orca worktree, say — gets one line saying which cards exist, not another branch's task, which is usually another agent's. The agent's draft of the task carries over only to the session that continues it; a task you confirmed carries over to any. ```bash theme={null} mengram resume # print the card: task, done, remaining, last check + commit, files, sources mengram resume --open # local page: confirm or correct the card, pick another task, copy the context mengram resume --json # the card as JSON mengram resume --any-age # show it however old (default: last 14 days) ``` Task / done / remaining are drafted by one model call and marked as the agent's draft until you confirm them on the page; confirmed text is never redrafted. Files, commands, the last test result and the commit it ran on are recorded verbatim. Cards live in `~/.mengram/resume/` and need no account; only the draft uses a model. ### mengram try Zero-account, local-only preview of what Mengram memory would know. Scans your Claude Code history **on-device** — nothing is uploaded, no API key needed: ```bash theme={null} mengram try ``` Shows your projects, tech stack, and detected workflow patterns (commit→push→deploy, test→fix→re-test, ...) drawn from your own session history, then the two commands that make it permanent. ### mengram import Import existing data into memory. #### mengram import claude-code Import your local Claude Code session transcripts (`~/.claude/projects`) into cloud memory — memory knows your projects from minute one instead of starting empty. ```bash theme={null} mengram import claude-code # 20 most recent sessions mengram import claude-code --last 50 # more history mengram import claude-code --project myapp # only one project mengram import claude-code --yes # skip the confirmation prompt ``` * **Secrets are redacted** before storage — on import (client-side) and again server-side in the extraction pipeline, so keys/tokens/JWTs pasted into a conversation never reach the model or get stored (→ `[REDACTED]`) * Each session counts as **one add operation** against your plan's monthly quota * Re-runs skip already-imported sessions; `--reimport` forces a fresh pass #### mengram import chatgpt ```bash theme={null} mengram import chatgpt ~/Downloads/chatgpt-export.zip mengram import chatgpt export.zip --cloud # use cloud API ``` #### mengram import obsidian ```bash theme={null} mengram import obsidian ~/Documents/MyVault mengram import obsidian vault/ --cloud --chunk-chars 4000 ``` #### mengram import files ```bash theme={null} mengram import files notes.md journal.txt research.md mengram import files *.md --cloud ``` ### mengram api Start a local REST API server. ```bash theme={null} mengram api # default: 0.0.0.0:8420 mengram api --port 9000 # custom port ``` ### mengram web Start the Web UI with chat and knowledge graph visualization. ```bash theme={null} mengram web # opens browser automatically mengram web --port 9000 # custom port mengram web --no-open # don't open browser ``` ## Environment Variables | Variable | Description | | ----------------- | --------------------------------- | | `MENGRAM_API_KEY` | Cloud API key (starts with `om-`) | | `MENGRAM_URL` | Custom API base URL | | `MENGRAM_USER_ID` | Default user\_id | # Cognitive Profile Source: https://docs.mengram.io/cognitive-profile Generate a ready-to-use system prompt from memory that captures who a user is, their preferences, and current focus. ## What is a Cognitive Profile? A Cognitive Profile is an AI-generated system prompt that summarizes everything Mengram knows about a user: identity, preferences, communication style, current projects, and key relationships. Insert it into any LLM's system prompt for instant personalization. ## Generate a profile ```python theme={null} from mengram import Mengram m = Mengram() profile = m.get_profile() print(profile["system_prompt"]) # "You are talking to Ali, a software engineer based in ... # He prefers concise responses, uses Python and Railway..." print(profile["facts_used"]) # 47 — number of facts used ``` ## Use in an LLM call ```python theme={null} import openai profile = m.get_profile(user_id="alice") response = openai.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": profile["system_prompt"]}, {"role": "user", "content": "What should I focus on this week?"}, ] ) ``` ## Force regeneration Profiles are cached for performance. Force a fresh one with: ```python theme={null} profile = m.get_profile(force=True) ``` ## Multi-user profiles Generate profiles for different end-users in your app: ```python theme={null} alice_profile = m.get_profile(user_id="alice") bob_profile = m.get_profile(user_id="bob") ``` ## LangChain integration ```python theme={null} from langchain_mengram import get_mengram_profile # Returns a string you can use as system prompt prompt = get_mengram_profile(api_key="om-...", user_id="alice") ``` # CrewAI Source: https://docs.mengram.io/crewai Give your CrewAI agents persistent memory with procedural learning. The [`mengram-crewai`](https://pypi.org/project/mengram-crewai/) package wires Mengram memory into any CrewAI agent as tools. ## Installation ```bash theme={null} pip install mengram-crewai ``` ## Quick start ```python theme={null} from mengram_crewai import mengram_tools from crewai import Agent, Task, Crew tools = mengram_tools(api_key="om-your-key", user_id="user-123") agent = Agent( role="Support Engineer", goal="Help users with technical issues using past context", backstory="You remember every prior interaction and learned workflow.", tools=tools, ) task = Task( description="Help the user debug their deployment issue", expected_output="A resolution grounded in the user's history", agent=agent, ) result = Crew(agents=[agent], tasks=[task]).kickoff() ``` `api_key` falls back to `MENGRAM_API_KEY` and `user_id` to `MENGRAM_USER_ID` if omitted. ## Available tools `mengram_tools()` returns three tools ready to pass to `Agent(tools=...)`: | Tool | Description | | ---------------- | --------------------------------------------------------------------------------------- | | `search_memory` | Semantic search over facts, preferences, and past events | | `save_memory` | Save a durable fact or decision to long-term memory | | `get_procedures` | Retrieve learned workflows — steps, success/failure record, and preconditions to verify | ## Procedural learning `get_procedures` is the distinctive tool. When a workflow has failed before, Mengram returns it with the preconditions that were violated last time, so the agent verifies them up front instead of repeating the mistake. ```python theme={null} tools = mengram_tools(api_key="om-...", user_id="user-123") # The agent calls get_procedures("deploy to Railway") and receives, e.g.: # deploy-railway (v3, 11 ok / 1 failed) — verify first: run migrations before push ``` ## Options ```python theme={null} mengram_tools( api_key="om-...", # or MENGRAM_API_KEY user_id="user-123", # or MENGRAM_USER_ID; multi-user isolation limit=5, # max results per search/procedures call base_url="https://...", # self-hosted instances ) ``` # Export & Portability Source: https://docs.mengram.io/export Take your memory out as plain Markdown files at any time — no account needed to read them, nothing to migrate if you leave. ## Why this exists A memory you cannot read, correct, or take with you is not really yours. So everything Mengram stores comes back out as plain Markdown: one file per thing it remembers, relations as `[[wikilinks]]`, procedures with the record of them working and the log of why they changed. The export is deliberately **not charged against your add or search quota**. Getting your data out should never cost you the ability to use the product. The per-minute rate limit still applies. ## From the CLI ```bash theme={null} mengram export obsidian ~/Documents/MyVault # into an Obsidian vault mengram export markdown ./backup # into any directory ``` | Flag | Meaning | | ----------- | --------------------------------------------------- | | `--user-id` | Export one sub-user's memory instead of the default | | `--force` | Overwrite an existing `memory/` folder | Without `--force` the command refuses to overwrite an existing `memory/` folder, so a second run cannot quietly replace something you edited. ## From the API `markdown` returns a zip of the tree. `files` returns the same tree as `{path: text}` for clients that write files themselves. `json` returns the underlying records. Which sub-user's memory to export. ```bash theme={null} curl -H "Authorization: Bearer om-your-key" \ "https://mengram.io/v1/export?format=markdown" -o memory.zip ``` ```python theme={null} from mengram import CloudMemory m = CloudMemory(api_key="om-your-key") tree = m.export(format="files") # {"memory/entities/Ali.md": "...", ...} ``` ## What you get ``` memory/ MEMORY.md index — what is in here entities/Ali.md what is true episodes/2026-07-30-deploy-failed.md what happened, and how it turned out procedures/deploy to Railway.md how to do it, and whether it works profile.md ``` Because relations are ordinary wikilinks, Obsidian's graph view works on the folder with no configuration — the links *are* the graph. And because it is files, `git diff` shows what your agent learned this week, a pull request reviews something it learned wrongly, and `git revert` undoes it. ## Into an Obsidian vault The [Obsidian plugin](https://obsidian.md/plugins?id=mengram) does this without the terminal: run **Pull memory into vault** from the command palette. The folder it writes is generated — anything you edit inside is replaced on the next pull, so keep your own notes elsewhere. ## The format is published separately The tree above is [memfmt](/memfmt), a small open format with its own library and specification. Mengram writes it; the library reads it without an account or a server. That separation is the point: if you stop using Mengram, the files keep working. Reading a folder back into Mengram is not built yet. Export is one-way today. # Mengram Documentation Source: https://docs.mengram.io/index The memory layer for AI apps. Persistent memory — semantic, episodic, procedural — with native quality across 23 languages. ## What is Mengram? Mengram is an API that gives your AI applications persistent, human-like memory. Instead of starting every conversation from scratch, your AI remembers users — their preferences, past interactions, and learned workflows. Mengram works **natively across 23 languages** (Russian, Chinese, Spanish, Japanese, Korean, Arabic, and more) using Cohere multilingual embeddings — not English-only retrieval bolted onto translation. Get your API key and add your first memory in under 2 minutes. Give your AI agents persistent memory with scoping, filtering, and procedural learning. Understand semantic, episodic, and procedural memory. Full reference for the Python client (61 methods). Node.js and browser SDK with TypeScript support (60 methods). ## Integrations Full memory loop — auto-save, auto-recall, session context. 29 tools for Claude Desktop, Cursor, Windsurf. MengramRetriever for RAG pipelines. Persistent memory for multi-agent crews. 12 tools for AI agents — auto-recall, auto-capture, Graph RAG. Ready-to-import workflow template. Command-line tool for setup, import, and rules. ## Reference Complete REST API with 90+ endpoints. Real-time notifications for memory events. Run Mengram on your own infrastructure. # JavaScript SDK Source: https://docs.mengram.io/javascript-sdk Node.js and browser SDK for Mengram with full TypeScript support. ## Installation ```bash theme={null} npm install mengram-ai ``` ## Quick Start ```javascript theme={null} const { MengramClient } = require('mengram-ai'); const m = new MengramClient('om-your-api-key'); // Add memories await m.add([ { role: 'user', content: 'Fixed the auth bug using rate limiting.' }, ]); // Semantic search const results = await m.search('auth issues'); // Unified search — all 3 types const all = await m.searchAll('deployment issues'); // { semantic: [...], episodic: [...], procedural: [...] } // Cognitive Profile const profile = await m.getProfile('alice'); // { system_prompt: "You are talking to Alice..." } ``` ## TypeScript ```typescript theme={null} import { MengramClient, MengramError, QuotaExceededError } from 'mengram-ai'; const m = new MengramClient('om-...'); const results = await m.search('preferences'); const events = await m.episodes({ query: 'deployment' }); const procs = await m.procedures({ query: 'release' }); ``` ## Constructor ```javascript theme={null} const m = new MengramClient('om-your-key', { baseUrl: 'https://mengram.io', // default timeout: 30000, // 30s default }); ``` *** ## Core Methods ### add(messages, options?) Add memories from a conversation. Extracts all 3 memory types. ```javascript theme={null} const result = await m.add([ { role: 'user', content: 'We use PostgreSQL and Railway' }, { role: 'assistant', content: 'Got it.' }, ], { userId: 'alice', agentId: 'support-bot', appId: 'production', expirationDate: '2027-01-01T00:00:00Z', source: 'slack', // provenance source metadata: { channel: '#engineering' }, // arbitrary metadata agentMode: true, // extract from all speakers (user + assistant) }); // { status: 'accepted', job_id: 'job-...' } ``` ### addText(text, options?) Add memories from plain text. ```javascript theme={null} await m.addText('Meeting notes: decided to use Redis for caching', { userId: 'alice', source: 'email', metadata: { subject: 'Weekly sync' }, }); ``` ### addFile(file, options?) Upload a file and extract structured memories. Supports PDF (vision AI), DOCX, TXT, MD. Returns immediately with job\_id. ```javascript theme={null} // Node.js — pass a file path const result = await m.addFile('./meeting-notes.pdf'); // { status: 'accepted', job_id: 'job-...', file_type: 'pdf', page_count: 12 } // Browser — pass a File object from await m.addFile(fileInput.files[0]); // Poll for completion await m.waitForJob(result.job_id); ``` Options: `userId`, `agentId`, `runId`, `appId`, `filename` (override filename for Blob inputs). **File size limits:** Free 10MB, Pro 50MB, Business 100MB. ### search(query, options?) Semantic search with re-ranking. ```javascript theme={null} const results = await m.search('database preferences', { limit: 10, graphDepth: 3, userId: 'alice', }); ``` ### searchAll(query, options?) Unified search across all 3 memory types. ```javascript theme={null} const all = await m.searchAll('deployment issues', { limit: 5 }); // { semantic: [...], episodic: [...], procedural: [...] } ``` ### getAll(options?) / getAllFull(options?) ```javascript theme={null} const memories = await m.getAll({ userId: 'alice' }); const full = await m.getAllFull({ userId: 'alice' }); // with full facts ``` ### get(name, options?) / delete(name, options?) ```javascript theme={null} const entity = await m.get('PostgreSQL'); // returns null if not found const ok = await m.delete('PostgreSQL'); // returns true/false ``` ### fixEntityType(name, newType, options?) ```javascript theme={null} await m.fixEntityType('React', 'technology'); ``` ### stats(options?) / graph(options?) ```javascript theme={null} const stats = await m.stats(); const graph = await m.graph(); // { nodes: [...], edges: [...] } ``` *** ## Cognitive Profile ### getProfile(userId?, options?) Generate a ready-to-use system prompt from memory. ```javascript theme={null} const profile = await m.getProfile('alice', { force: true }); console.log(profile.system_prompt); console.log(profile.facts_used); ``` *** ## Episodic Memory ### episodes(options?) ```javascript theme={null} // Search const events = await m.episodes({ query: 'deployment', limit: 5 }); // List recent const recent = await m.episodes({ limit: 20 }); // Time range const jan = await m.episodes({ after: '2026-01-01', before: '2026-02-01', }); ``` *** ## Procedural Memory ### procedures(options?) ```javascript theme={null} const procs = await m.procedures({ query: 'deploy' }); ``` ### procedureFeedback(id, options?) ```javascript theme={null} await m.procedureFeedback(procId, { success: true }); await m.procedureFeedback(procId, { success: false, context: 'Build OOM at step 3', failedAtStep: 3, }); ``` ### procedureHistory(id) / procedureEvolution(id) ```javascript theme={null} const history = await m.procedureHistory(procId); const evolution = await m.procedureEvolution(procId); ``` *** ## Timeline ### timeline(options?) ```javascript theme={null} const facts = await m.timeline({ after: '2026-01-01', before: '2026-02-01', limit: 20, }); ``` *** ## Agents ### runAgents(options?) ```javascript theme={null} await m.runAgents({ agent: 'curator' }); await m.runAgents(); // runs all agents ``` ### agentHistory(options?) / agentStatus(options?) ```javascript theme={null} const history = await m.agentHistory({ limit: 5 }); const status = await m.agentStatus(); ``` *** ## Insights & Reflections ```javascript theme={null} await m.reflect(); const insights = await m.insights(); const refs = await m.reflections({ scope: 'cross' }); ``` *** ## Memory Management ```javascript theme={null} await m.dedup(); // merge duplicate entities await m.dedupAll(); // dedup facts across all entities await m.dedupEntity('PostgreSQL'); // dedup one entity await m.merge('React.js', 'React'); // merge entities await m.mergeUser(); // merge "User" into primary await m.archiveFact('Entity', 'old fact'); // archive a fact await m.reindex(); // re-embed all entities ``` ### feed(options?) ```javascript theme={null} const feed = await m.feed({ limit: 20 }); ``` *** ## Webhooks ```javascript theme={null} await m.createWebhook({ url: 'https://your-app.com/hook', eventTypes: ['memory_add', 'memory_update'], secret: 'hmac-secret', }); const hooks = await m.listWebhooks(); await m.updateWebhook(1, { active: false }); await m.deleteWebhook(1); ``` *** ## Teams ```javascript theme={null} const team = await m.createTeam('Engineering', 'Shared memory'); await m.joinTeam('invite-code'); const teams = await m.listTeams(); const members = await m.teamMembers(teamId); await m.shareMemory('PostgreSQL', teamId); await m.unshareMemory('PostgreSQL', teamId); await m.leaveTeam(teamId); await m.deleteTeam(teamId); // owner only ``` *** ## Smart Triggers ```javascript theme={null} const triggers = await m.getTriggers('alice', { includeFired: true }); await m.processTriggers(); await m.dismissTrigger(triggerId); await m.detectTriggers('alice'); ``` *** ## API Keys ```javascript theme={null} const keys = await m.listKeys(); const newKey = await m.createKey('production'); await m.renameKey(keyId, 'staging'); await m.revokeKey(keyId); ``` *** ## Billing ```javascript theme={null} const billing = await m.getBilling(); const checkout = await m.createCheckout('pro'); const portal = await m.createPortal(); ``` *** ## Jobs ```javascript theme={null} const status = await m.jobStatus('job-abc'); const result = await m.waitForJob('job-abc', { pollInterval: 1000, maxWait: 60000, }); ``` *** ## Import Data ```javascript theme={null} // ChatGPT export (requires jszip) await m.importChatgpt('~/Downloads/chatgpt-export.zip', { chunkSize: 20, onProgress: (current, total, title) => console.log(`${current}/${total}: ${title}`), }); // Obsidian vault await m.importObsidian('~/Documents/MyVault'); // Text/markdown files await m.importFiles(['notes.md', 'journal.txt']); ``` *** ## Multi-User Isolation ```javascript theme={null} // Each userId gets its own memory space await m.add([{ role: 'user', content: 'I prefer dark mode' }], { userId: 'alice' }); await m.add([{ role: 'user', content: 'I prefer light mode' }], { userId: 'bob' }); const alice = await m.searchAll('preferences', { userId: 'alice' }); // Only Alice's memories ``` *** ## Error Handling ```javascript theme={null} const { MengramClient, MengramError, QuotaExceededError } = require('mengram-ai'); try { await m.add(messages); } catch (e) { if (e instanceof QuotaExceededError) { console.log('Upgrade your plan:', e.message); } else if (e instanceof MengramError) { console.log(`API error ${e.statusCode}: ${e.message}`); } } ``` *** ## Quota Usage After any API call, check your current quota usage via the `.quota` property: ```javascript theme={null} await m.search('test'); console.log(m.quota); // { add: { used: 5, limit: 30 }, search: { used: 12, limit: 100 } } // Check before hitting the wall const { search } = m.quota; if (search && search.used >= search.limit * 0.8) { console.warn('Warning: approaching search quota limit'); } ``` The quota is read from response headers (`X-Quota-Add-Used`, `X-Quota-Add-Limit`, `X-Quota-Search-Used`, `X-Quota-Search-Limit`) and reflects usage at the time of the last API call. # LangChain Source: https://docs.mengram.io/langchain Use MengramRetriever in LangChain RAG pipelines and chains for persistent memory. ## Installation ```bash theme={null} pip install langchain-mengram ``` ## MengramRetriever Subclasses `BaseRetriever` from LangChain. Searches across all 3 memory types and returns `Document` objects. ```python theme={null} from langchain_mengram import MengramRetriever retriever = MengramRetriever( api_key="om-your-key", user_id="alice", top_k=5, memory_types=["semantic", "episodic", "procedural"], ) # Use as any LangChain retriever docs = retriever.invoke("deployment issues") for doc in docs: print(doc.page_content) print(doc.metadata) # {"source": "mengram", "memory_type": "semantic", ...} ``` ## Use in a chain ```python theme={null} from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough retriever = MengramRetriever(api_key="om-...") prompt = ChatPromptTemplate.from_template( "Context from memory:\n{context}\n\nQuestion: {question}" ) chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | ChatOpenAI(model="gpt-4o") | StrOutputParser() ) answer = chain.invoke("What deployment stack am I using?") ``` ## Cognitive Profile ```python theme={null} from langchain_mengram import get_mengram_profile # Get a system prompt string prompt = get_mengram_profile(api_key="om-...", user_id="alice") ``` ## Parameters | Parameter | Type | Default | Description | | -------------- | ---- | ------------------------------------------ | --------------------- | | `api_key` | str | required | Mengram API key | | `user_id` | str | "default" | User to search | | `api_url` | str | "[https://mengram.io](https://mengram.io)" | API base URL | | `top_k` | int | 5 | Max results per type | | `memory_types` | list | all 3 | Which types to search | # Local mode — memory in a folder Source: https://docs.mengram.io/local-mode No account, no server. Your agent's memory is a folder of Markdown you own, with the same procedures-with-outcomes the cloud keeps: versions, success/fail counts, a policy gate, and a regression gate. ## What it is The folder is the memory. `mengram local` reads and writes a [memfmt](https://github.com/alibaizhanov/memfmt) tree: ``` memory/ MEMORY.md index entities/Railway.md what is true episodes/2026-07-30-deploy failed on a cold pool.md what happened, and how it turned out procedures/Deploy to Railway.md how to do it, and whether it works .mengram/config.json which model to use .mengram/quarantine.json revisions the regression gate refused ``` Git diffs it. Obsidian draws it. `memfmt validate` checks it. Nothing expires, nothing asks for a key. **Bring your own model.** Extraction (turning a conversation into facts, events and workflows) and a failure revision need an LLM: an Anthropic or OpenAI key you already have, or Ollama (8B+, 8K+ context). Everything else — search, recall, the policy gate, recording outcomes — runs with no model and no network. ## Install ```bash theme={null} pip install mengram-ai mengram local init ./memory --provider anthropic --api-key sk-ant-... # or --provider openai / ollama export MENGRAM_MEMORY_DIR=$PWD/memory ``` `init` writes the folder and `.mengram/config.json`. With no `--provider`, an `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` in the environment is used; with neither, the folder still works for everything but `add`. ## Use it ```bash theme={null} mengram local add "I deploy to Railway from main; last time /health was probed before the pool was up" mengram local search "railway pool" mengram local procedures mengram local feedback "Deploy to Railway" --success mengram local feedback "Deploy to Railway" --failure --step 3 --context "connection refused on /health" mengram local stat mengram local quarantine ``` A failure with `--context` asks your model what belief broke and produces the next version of the workflow — unless the fix would silently break another workflow in the folder, in which case it is quarantined for you to review instead of shipped to the agent. ## Start from your Claude Code history An empty folder takes days to fill through the hooks. Your Claude Code sessions are already on disk, so seed it from them: ```bash theme={null} mengram import claude-code --memory ./memory # 20 most recent sessions mengram import claude-code --memory ./memory --last 50 --project shop ``` Each session becomes one extraction with the folder's model, redacted for keys and tokens before it is read; nothing leaves your machine except those model calls. The import ends with what the folder now holds and the workflows it learned, with their record, and writes the map below. Imported sessions are listed in `.mengram/claude-code-imported.json`, so re-runs pick up only new ones (`--reimport` forces). ## See what it holds ```bash theme={null} mengram local map --memory ./memory --open ``` One self-contained HTML page (`memory-map.html` in the folder, or `--out`), rendered from the same Markdown files, with nothing fetched or sent. Three views, in the order you would ask: **who you are** (entities by type, with facts and relations), **what happened** (episodes on a timeline, with outcomes), **what your agent learned** (each workflow as a step chain with the per-step record, its versions with the belief that broke, and the quarantine of revisions the regression gate refused). Re-run it after an import or a `feedback`. ## Claude Code, all local ```bash theme={null} mengram hook install --memory ./memory ``` Installs the four hooks with the folder written into each command (hooks run without your shell profile, so an env var is not enough): | Hook | What it does locally | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `SessionStart` | loads a profile of the folder — who you are, the workflows with a record | | `UserPromptSubmit` | recalls facts, events and workflows that share words with your prompt | | `Stop` | extracts the turn into the folder with your model (silent if none is configured) | | `PreToolUse` on `Bash` | the [policy gate](/claude-code#4-before-a-bash-command-the-policy-gate): a workflow-shaped command that matches an `untested` or below-the-bar workflow is turned into a confirmation prompt | ## MCP for any client ```bash theme={null} mengram server --memory ./memory ``` Five tools over stdio: `remember`, `recall`, `context_for`, `list_procedures`, `procedure_feedback` — the same surface as the [Claude connector](/claude-connector), plus feedback so an agent can record an outcome from inside its tool call. Claude Desktop, Cursor, Windsurf and any MCP client can point at it: ```json theme={null} { "mcpServers": { "mengram": { "command": "mengram", "args": ["server", "--memory", "/absolute/path/to/memory"] } } } ``` ## What a procedure file looks like ```markdown theme={null} --- memfmt_type: procedure version: 2 success_count: 0 fail_count: 0 last_failure: the connection pool was warm when /health was probed last_failed: 2026-09-04 entities: - Railway --- # Deploy to Railway (v2 · 81% expected) **When** — a change lands on main **Preconditions** - verify the pool is warm before probing /health **Last failure** — 2026-09-04: the connection pool was warm when /health was probed ## Steps 1. push to main — the webhook does the rest (4✓/0✗) 2. watch the boot log (4✓/0✗) 3. wait for the pool — pool_max reached 4. verify /health — expect 200 within 60s (3✓/1✗) ## Evolution - v1 → v2 (2026-09-04, 3✓/1✗): wait for the pool before probing ``` `81% expected` is not a ratio: v2 has no runs of its own yet, and the number is inherited from v1's record, discounted. Once v2 runs it reads `N% reliable`. The steps that the revision left alone keep their counts; the new step starts untracked. See [memfmt](https://github.com/alibaizhanov/memfmt#why-the-heading-is-not-success--total) for the rules. ## Where a folder stops being enough Search is word overlap: past a few hundred files you want embeddings. Syncing a folder between machines is a `git pull` only until two machines disagree. Extraction runs only when you call it, not from every session in the background. That is what the [cloud](/quickstart) adds — and it writes this same format, so `mengram export markdown` hands you the folder back at any time. ## Not yet Multiple users per folder, the reflection and curator agents, and deleting a file that a rename left behind. Staleness is in as of 2.36.0: `last_succeeded` is written only by a recorded success, `mengram local procedures` shows it with days ago, and the map flags a workflow unverified for 30+ days. The cloud still lacks the column. # Claude Managed Agents Source: https://docs.mengram.io/managed-agents Give Claude Managed Agents persistent memory with Mengram's MCP server — 29 tools for semantic, episodic, and procedural memory. ## Overview [Claude Managed Agents](https://docs.anthropic.com/en/docs/agents/managed-agents) is Anthropic's hosted platform for running autonomous AI agents. Mengram connects via MCP to give your agents persistent memory across sessions — facts, events, and self-improving workflows. ## Setup ### 1. Get a Mengram API key Sign up at [mengram.io](https://mengram.io/#signup) — free tier includes 40 adds and 200 searches per month. ### 2. Add Mengram to your agent definition ```json theme={null} { "name": "my-agent", "model": "claude-sonnet-4-6", "instructions": "You are a helpful assistant with persistent memory.", "mcp_servers": [ { "type": "url", "name": "mengram", "url": "https://mengram.io/mcp" } ], "tools": [ { "type": "agent_toolset_20260401", "default_config": { "enabled": true, "permission_policy": {"type": "always_allow"} } }, { "type": "mcp_toolset", "mcp_server_name": "mengram", "default_config": { "enabled": true, "permission_policy": {"type": "always_allow"} } } ] } ``` Set `permission_policy` to `always_allow` for the MCP toolset. The default (`always_ask`) requires manual tool confirmation via the API — without it, every memory tool call will time out. ### 3. Store your API key in a vault Managed Agents use [vaults](https://docs.anthropic.com/en/docs/agents/managed-agents#vaults) for secrets. Create a vault, add your Mengram API key as a `static_bearer` credential, then pass the vault when creating a session: ```python theme={null} import anthropic client = anthropic.Anthropic() # Create a vault for this user vault = client.beta.vaults.create(display_name="My User") # Add Mengram API key as a credential client.beta.vaults.credentials.create( vault_id=vault.id, display_name="Mengram Memory", auth={ "type": "static_bearer", "mcp_server_url": "https://mengram.io/mcp", "token": "om-your-mengram-api-key", }, ) # Create an environment (container config for the session) env = client.beta.environments.create(display_name="Default") # Create a session — Anthropic injects the token automatically session = client.beta.sessions.create( agent=agent.id, vault_ids=[vault.id], environment_id=env.id, ) ``` ## Available tools Once connected, your agent gets **29 memory tools**: ### Core Memory | Tool | Description | | --------------- | --------------------------------------------------------------------- | | `remember` | Save conversation to memory — auto-extracts facts, events, procedures | | `remember_text` | Save plain text to memory | | `recall` | Semantic search through past memories | | `search` | Advanced structured search with relevance scores | | `search_all` | Unified search across all 3 memory types | | `timeline` | Search memory by time range | | `list_memories` | List all stored entities | ### Procedural Learning | Tool | Description | | -------------------- | -------------------------------------------------------- | | `list_procedures` | Retrieve learned workflows with success/failure tracking | | `procedure_feedback` | Report outcomes — procedures evolve on failure | | `procedure_history` | View how a procedure evolved over time | ### Context & Profile | Tool | Description | | --------------------- | --------------------------------------------- | | `context_for` | Get relevant context pack for a specific task | | `checkpoint` | Save a session checkpoint with key decisions | | `generate_rules_file` | Generate project rules from memory | ### Knowledge Graph | Tool | Description | | ---------------- | ---------------------------------- | | `get_graph` | Get all entities and relationships | | `get_entity` | Get details of a specific entity | | `merge_entities` | Merge duplicate entities | | `dedup` | Auto-find and merge duplicates | ### Insights | Tool | Description | | ----------------- | ---------------------------------------------- | | `reflect` | Trigger AI reflection on memories | | `get_reflections` | Get AI-generated insights and patterns | | `run_agents` | Run memory agents (curator, connector, digest) | For the complete list of all 29 tools with parameters, see [MCP Server](/mcp-server). ## Example: Support agent ```python theme={null} import anthropic client = anthropic.Anthropic() # Create the agent with Mengram MCP agent = client.beta.agents.create( name="support-agent", model="claude-sonnet-4-6", instructions="""You are a customer support agent with persistent memory. At the start of each conversation: 1. Use recall() to search for the customer's past interactions 2. Use context_for() to get relevant procedures After resolving issues: 1. Use remember() to save the conversation 2. Use procedure_feedback() to report success/failure""", mcp_servers=[ { "type": "url", "name": "mengram", "url": "https://mengram.io/mcp" } ], tools=[ { "type": "agent_toolset_20260401", "default_config": { "enabled": True, "permission_policy": {"type": "always_allow"} } }, { "type": "mcp_toolset", "mcp_server_name": "mengram", "default_config": { "enabled": True, "permission_policy": {"type": "always_allow"} } } ] ) # Store Mengram API key in a vault vault = client.beta.vaults.create(display_name="Customer") client.beta.vaults.credentials.create( vault_id=vault.id, display_name="Mengram Memory", auth={ "type": "static_bearer", "mcp_server_url": "https://mengram.io/mcp", "token": "om-your-mengram-api-key", }, ) # Create environment and session env = client.beta.environments.create(display_name="Support") session = client.beta.sessions.create( agent=agent.id, vault_ids=[vault.id], environment_id=env.id, ) # Send a message — agent recalls past context automatically client.beta.sessions.events.send( session_id=session.id, events=[{ "type": "user.message", "content": [{"type": "text", "text": "I'm having trouble with my deployment again"}] }] ) # Stream the response with client.beta.sessions.events.stream(session_id=session.id) as stream: for event in stream: if event.type == "agent.message": for block in event.content: if hasattr(block, "text"): print(block.text) if event.type == "agent.turn_complete": break ``` ## Mengram vs Memory Stores Managed Agents include built-in [Memory Stores](https://docs.anthropic.com/en/docs/agents/memory-stores) (research preview). Here's how they compare: | Feature | Mengram | Memory Stores | | ---------------------- | -------------------------------------------------- | --------------------- | | Memory types | **3** (semantic + episodic + procedural) | 1 (text documents) | | Auto-extraction | Yes — pass conversations, get structured knowledge | No — manual text only | | Procedural learning | Yes — workflows evolve from failures | No | | Cognitive Profile | Yes — one-call system prompt generation | No | | Knowledge graph | Yes | No | | Semantic search | Yes | Yes | | Multi-user isolation | Yes (`user_id` parameter) | Per-agent only | | Works beyond Anthropic | Yes (any LLM, any framework) | Managed Agents only | | Status | **Production** | Research preview | ## Self-hosted If you're self-hosting Mengram, point the MCP URL to your instance: ```json theme={null} { "type": "url", "name": "mengram", "url": "http://your-host:8420/mcp" } ``` See [Self-hosting guide](/self-hosting) for setup instructions. ## Next steps * [Agent Memory concepts](/agent-memory) — `agent_id`, `run_id`, procedural learning * [MCP Server reference](/mcp-server) — all 29 tools with parameters * [Python SDK](/python-sdk) — if you prefer direct API integration over MCP # MCP Server Source: https://docs.mengram.io/mcp-server Use Mengram as a Model Context Protocol server with Claude Desktop, Cursor, Windsurf, and other MCP clients. ## What is MCP? Model Context Protocol (MCP) lets AI clients like Claude Desktop, Cursor, Continue.dev, and Windsurf connect to external tools. Mengram's MCP server gives these clients persistent memory — semantic, episodic, and procedural. ## Fastest setup — paste one prompt into your agent If you have a coding agent (Claude Desktop, Cursor, Codex, Claude Code, Windsurf), skip the manual steps below. Paste this prompt: ``` Install Mengram for me. Fetch the canonical install guide at https://mengram.io/agent-install.txt and follow it precisely. My email is YOUR_EMAIL_HERE. ``` The agent reads our [install guide](/agent-install), installs the package, configures the MCP server in its host, and runs `mengram doctor` to verify the round-trip. See [Agent-Native Install](/agent-install) for the full breakdown. The rest of this page is the **manual** setup if you'd rather wire it up yourself. ## Prerequisites The MCP config below uses the local `mengram` CLI. Install it once and find its path — you'll need that path in the configs below. ```bash theme={null} pip install mengram-ai ``` ```bash theme={null} which mengram ``` ```powershell theme={null} (Get-Command mengram).Source ``` ```cmd theme={null} where mengram ``` Use the printed path as `command` in the configs below — replace `/path/to/mengram` with it. ## Setup with Claude Desktop Open **Settings → Developer → Edit Config**, then add: ```json theme={null} { "mcpServers": { "mengram": { "command": "/path/to/mengram", "args": ["server", "--cloud"], "env": { "MENGRAM_API_KEY": "om-your-key", "MENGRAM_URL": "https://mengram.io" } } } } ``` Restart Claude Desktop after saving. ## Setup with Cursor Add to Cursor's MCP settings (`~/.cursor/mcp.json`), or open **Settings → Cursor Settings → MCP**: ```json theme={null} { "mcpServers": { "mengram": { "command": "/path/to/mengram", "args": ["server", "--cloud"], "env": { "MENGRAM_API_KEY": "om-your-key", "MENGRAM_URL": "https://mengram.io" } } } } ``` Restart Cursor. ## Setup with Continue.dev Continue stores each MCP server as a separate YAML file. Create `.continue/mcpServers/mengram.yaml` at the top level of your workspace: ```yaml theme={null} name: Mengram version: 0.0.1 schema: v1 mcpServers: - name: mengram command: /path/to/mengram args: - server - --cloud env: MENGRAM_API_KEY: om-your-key MENGRAM_URL: https://mengram.io ``` Reload the Continue extension (or restart your IDE). Continue will auto-pick it up. ## Setup with Windsurf Edit `~/.codeium/windsurf/mcp_config.json` (or open **Settings → Cascade → MCP** in the IDE): ```json theme={null} { "mcpServers": { "mengram": { "command": "/path/to/mengram", "args": ["server", "--cloud"], "env": { "MENGRAM_API_KEY": "om-your-key", "MENGRAM_URL": "https://mengram.io" } } } } ``` Restart Windsurf. ## Available Tools The cloud MCP server exposes **29 tools**: ### Memory Operations | Tool | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `remember` | Save knowledge from a conversation to memory — pass message pairs and the AI extracts entities, facts, relations, episodes, and procedures. | | `remember_text` | Save plain text to memory — the AI extracts entities, facts, and relations automatically. | | `recall` | Semantic search through cloud memory. Use specific keywords: person names, project names, technologies. | | `search` | Advanced structured search — returns entities with relevance scores, facts, and knowledge snippets. | | `search_all` | Unified search across ALL memory types — semantic, episodic, and procedural. | | `timeline` | Search memory by time. Use when user asks "what did I do last week", "when did I...", etc. Returns facts with timestamps. | | `list_memories` | List all stored memory entities with their types and fact counts. | ### Entity Management | Tool | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `get_entity` | Get details of a specific entity — facts, relations, knowledge. | | `delete_entity` | Delete an entity and all its data (facts, relations, knowledge). | | `fix_entity_type` | Fix an entity's type classification (e.g. a technology labeled as "person"). Valid types: person, project, technology, company, concept, unknown. | | `merge_entities` | Merge two entities into one — combines facts, relations, and knowledge. | | `archive_fact` | Archive a specific fact on an entity — soft-delete without removing the entity. | | `dedup` | Find and automatically merge duplicate entities. Scans all entities and merges near-duplicates. | ### Episodic Memory | Tool | Description | | --------------- | --------------------------------------------------------------------- | | `list_episodes` | List or search episodic memories (events, interactions, experiences). | ### Procedural Memory | Tool | Description | | -------------------- | -------------------------------------------------------------------------------------------------------------------- | | `list_procedures` | List learned workflows/procedures from memory. Returns procedures with steps, success/fail counts, and version info. | | `procedure_feedback` | Record success or failure for a procedure. On failure with context, the system automatically evolves the procedure. | | `procedure_history` | Show how a procedure evolved over time — all versions and what changed. | ### Insights & Reflection | Tool | Description | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------- | | `reflect` | Trigger AI reflection on memories — analyzes facts to find patterns, insights, and connections. | | `get_reflections` | Get AI-generated reflections (insights and patterns found across memories). Optional scope filter: "entity", "cross", "temporal". | | `get_insights` | AI-generated insights about the user — patterns, connections, reflections from memory analysis. | ### Agents | Tool | Description | | ------------- | --------------------------------------------------------------------------------------------------------- | | `run_agents` | Run memory agents that analyze, clean, and find patterns. Use "curator", "connector", "digest", or "all". | | `vault_stats` | Get memory vault statistics — total entities, facts, relations, knowledge items, and storage usage. | ### Triggers & Graph | Tool | Description | | ----------------- | ----------------------------------------------------------------------- | | `get_triggers` | List smart triggers — reminders, contradictions detected, and patterns. | | `dismiss_trigger` | Dismiss a smart trigger without firing its webhook. | | `get_graph` | Get the knowledge graph — all entities and their relationships. | | `get_feed` | Activity feed — recent memory changes and events. | ### Context & Rules | Tool | Description | | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `checkpoint` | Save a session checkpoint — summarize key decisions, learnings, and outcomes. Lighter than "remember": takes a structured summary instead of raw messages. | | `context_for` | Get relevant memory context for a specific task. Returns a compact context pack: related entities, procedures, and past events. Takes `max_tokens` (default 1200): the pack is cut in rank order to fit and ends with one line naming what was left out. | | `generate_rules_file` | Generate a CLAUDE.md, .cursorrules, or .windsurfrules file from memory. Creates structured project rules, conventions, tech stack, workflows. | ## Multi-User / Multi-Tenant Every MCP tool accepts an optional `user_id` parameter to scope memories per end-user. Use this when building multi-tenant apps — one API key, many isolated users. Example — calling `remember` for a specific end-user: ```json theme={null} { "conversation": [ {"role": "user", "content": "Alice prefers dark mode"} ], "user_id": "alice" } ``` Calling `search` scoped to the same user: ```json theme={null} { "query": "preferences", "user_id": "alice" } ``` Without `user_id`, tools use the default scope tied to your API key. With `user_id`, memories are isolated per end-user — each gets their own facts, events, workflows, and cognitive profile under a single API key. Same semantics as the [Python SDK](/python-sdk), [JS SDK](/javascript-sdk), and [REST API](/api-reference). ## HTTP Transport For remote/cloud MCP clients, Mengram exposes two transports: ### Streamable HTTP (recommended) MCP 2025-03-26 spec — single endpoint for JSON-RPC requests and SSE streaming. Use this for modern MCP clients (Smithery, Claude Connectors, etc.): ``` Endpoint: https://mengram.io/mcp Methods: GET, POST, DELETE Auth: Bearer ``` ### SSE (legacy) For clients that don't yet support Streamable HTTP: ``` SSE endpoint: https://mengram.io/mcp/sse Messages: https://mengram.io/mcp/messages/ ``` ### Discovery Service manifest for auto-discovery: ``` https://mengram.io/.well-known/mcp ``` The MCP server uses cloud mode by default with `MENGRAM_API_KEY`. For local mode (your own LLM keys), run `mengram server` without the `--cloud` flag. **Since 2.27.1:** when `MENGRAM_API_KEY` is not set in the environment, `mengram server --cloud` falls back to `~/.mengram/config.json` (written by `mengram setup`). The explicit `env` blocks in the configs above still work and take precedence — but if you've run setup once, they're optional. This matters for MCP hosts that spawn servers without your shell profile (Claude Desktop on macOS, most Windows setups). # The memfmt Format Source: https://docs.mengram.io/memfmt An agent's memory as Markdown files — the open format Mengram writes, with a library that reads it without an account or a server. ## What it is `memfmt` is a small specification and a dependency-free Python library for storing an agent's memory as plain Markdown. Mengram's export writes it, but the format belongs to neither the product nor the company: the library works on a folder, with no account, no server and no network. ```bash theme={null} pip install memfmt ``` ```bash theme={null} memfmt stat ./memory # what is in here memfmt validate ./memory # would any file lose data if a tool rewrote it? memfmt context ./memory "why did the deploy fail" ``` Source and specification: [github.com/alibaizhanov/memfmt](https://github.com/alibaizhanov/memfmt) (MIT). ## Three kinds of file Agents forget in three different ways, so there are three kinds of note. What is true. A person, a service, a file — with its facts and its relations to other entities. What happened, dated, and how it turned out. An event with no outcome teaches nothing. A workflow that was learned, with the record of it working and the log of why it was revised. ## A procedure file This is the one worth reading closely — it is the file the format exists for. ```markdown theme={null} --- memfmt_type: procedure version: 3 success_count: 11 fail_count: 1 --- # deploy to Railway (v3 · 86% reliable) **When** — a change lands on main ## Steps 1. push to main — the webhook does the rest [commit → deploy] (12✓/0✗) 2. watch the boot log [deploy → boot log] 3. verify /health — expect 200 within 60s [deploy →] (9✓/3✗) ## Evolution - v1 → v2 (2026-06-02, 4✓/2✗): added the health check - v2 → v3 (9✓/1✗): wait for the pool before probing ``` A workflow on its own is a guess somebody wrote down. With a record and the revisions that produced it, it is evidence — and an agent can tell a step that survived eleven deploys from one nobody has ever run. ### Why the heading is not `success / total` A bare ratio punishes the revision: a new v3 opens at 0✓/0✗ and reads worse than the v2 it was written to fix, so anything comparing the two keeps choosing the version that already failed. Progressive delivery and CI met this years ago — a canary confidence record, a flake quarantine ledger — and both smooth against a prior instead of comparing raw counts. So the percentage is a smoothed estimate, and the word after it says which kind of claim it is: | state | means | | -------------- | ----------------------------------------------------- | | `untested` | no runs, and no lineage to draw on | | `81% expected` | no runs of its own; this is what its lineage suggests | | `86% reliable` | its own record, smoothed so one lucky run is not 100% | The raw counts in frontmatter are exactly what happened. The percentage is derived and is never a source of truth. ### What a step consumes and produces The brackets are optional, and they are names rather than types. Told only that step 3 failed, you cannot tell whether step 3 is broken or whether step 1 handed it something broken — and that is the difference between fixing the right thing and rewriting a step that was never at fault. `memfmt validate` checks that the chain joins up and reports any step asking for something no earlier step produces. ## Rules worth knowing 1. **Frontmatter is the source of truth.** Editing the `(v3 · 86% reliable)` in a heading changes nothing — the parser reads the frontmatter. 2. **`memfmt_type` marks a file as ours.** Files without it are ignored, so a memory folder can live inside a vault full of your own notes. 3. **A missing record means untracked, not failed.** 4. **Round-trip or it is not the format.** Parsing what was serialised returns the same object, and serialising what was parsed is byte-identical. `memfmt validate` checks exactly that against a real folder. ## Where files stop being enough Honestly: at a few hundred of them. Relevance in `memfmt context` is word overlap, so it misses things phrased differently; syncing a folder between machines is a real problem; deduplicating facts that contradict each other needs a model. That is what the hosted product is for. But the library stands on its own and does not expire if you never touch it — which was the condition for publishing it at all. # Memory Types Source: https://docs.mengram.io/memory-types Understand semantic, episodic, and procedural memory — the three pillars of human-like AI memory. ## Overview Mengram gives your AI three distinct memory types, inspired by how human memory works: | Type | Stores | Example | | -------------- | --------------------------------- | -------------------------------------------------------- | | **Semantic** | Facts, knowledge, preferences | "User prefers dark mode and uses Python 3.12" | | **Episodic** | Events, experiences, interactions | "Fixed an OOM bug on Jan 15 by reducing pool size" | | **Procedural** | Workflows, processes, skills | "How to deploy: 1) run tests, 2) build, 3) push to main" | When you call `m.add(messages)`, all three types are extracted automatically from the conversation. ## Semantic Memory The knowledge graph. Entities with facts, types, and relationships. This is the core memory layer. ```python theme={null} # Search semantic memory results = m.search("user preferences") # Returns entities with facts and scores # Get a specific entity entity = m.get("PostgreSQL") # {"name": "PostgreSQL", "type": "technology", "facts": [...]} ``` ## Episodic Memory Autobiographical events — what happened, when, with whom, and what the outcome was. Each episode has a summary, context, outcome, and participant list. ```python theme={null} # Search episodes events = m.episodes(query="deployment issues") # [{"summary": "Fixed OOM on Railway", "outcome": "Resolved by reducing pool", ...}] # List recent episodes recent = m.episodes(limit=10) # Time-range filter jan_events = m.episodes(after="2026-01-01", before="2026-02-01") ``` ## Procedural Memory Learned workflows and processes. Mengram extracts step-by-step procedures from conversations and tracks which ones work and which fail. ```python theme={null} # Search procedures procs = m.procedures(query="deploy") # [{"name": "Deploy to Railway", "steps": [...], "success_count": 5}] # Report success/failure — triggers experience-driven evolution m.procedure_feedback(proc_id, success=True) # On failure with context, the procedure evolves automatically. # Mengram also infers failures from episodes as they arrive and revises # on its own — feedback is the explicit path, not the only one. m.procedure_feedback(proc_id, success=False, context="Step 3 failed: OOM on build", failed_at_step=3) # View how a procedure evolved over time history = m.procedure_history(proc_id) # {"versions": [v1, v2, v3], "evolution_log": [...]} ``` ## Graph RAG Memories aren't flat — they're connected in a knowledge graph. When you search, Mengram doesn't just find direct matches. It traverses relationships to surface context that simple vector search misses. ``` Query: "Python" → Direct match: Python (technology) → 1 hop: Django (framework) — "used_with" → Python → 2 hop: Railway (platform) — "deployed_on" → Django ``` This 2-hop traversal means searching for "Python" also returns your Django deployment setup on Railway — context that a flat vector search would never find. Control traversal depth with `graph_depth`: ```python theme={null} m.search("Python", graph_depth=0) # direct matches only m.search("Python", graph_depth=2) # default — 2 hops m.search("Python", graph_depth=4) # deep traversal ``` ## Ebbinghaus Decay Mengram models memory decay like the human brain. Facts that haven't been accessed recently fade in importance, while frequently recalled facts get stronger. The formula: `effective_importance = base_importance * e^(-0.03 * days_since_access) + frequency_boost` * A fact accessed yesterday has full weight * A fact untouched for 30 days decays to \~40% weight * A fact accessed 100 times gets a frequency boost regardless of recency This means search results naturally prioritize fresh, relevant knowledge over stale information — without manual cleanup. The [curator agent](/api-reference) also periodically finds and archives facts that have become outdated or contradicted by newer information. ## Confidence Scoring Procedural memory uses confidence scoring to decide when to create new procedures: | Confidence | Action | | ---------- | ------------------------------------------ | | \< 0.4 | Skip — not enough evidence | | 0.4 – 0.6 | Create a suggestion trigger — user decides | | >= 0.6 | Auto-create the procedure | Confidence is calculated from episode clustering — how many similar events support the pattern. This prevents noisy one-off events from becoming permanent workflows. ## Unified Search Search all three types at once with a single call: ```python theme={null} results = m.search_all("deployment problems") # { # "semantic": [...], # knowledge graph entities # "episodic": [...], # related events # "procedural": [...] # relevant workflows # } ``` # n8n Integration Source: https://docs.mengram.io/n8n Add persistent memory to any n8n AI agent workflow with a ready-to-import template. ## Overview Add long-term memory to any n8n AI workflow. Your agent remembers users across sessions — preferences, past conversations, resolved issues. No custom code needed, just HTTP Request nodes. ## Quick start 1. [Download the workflow](https://github.com/alibaizhanov/mengram/tree/main/examples/n8n) from GitHub 2. In n8n, go to **Workflows → Import from File** 3. Add your Mengram API key as a Header Auth credential 4. Activate and test ## How it works ``` Webhook → Search Memories → Build Prompt → AI Response → Save to Memory → Respond ``` The workflow adds 3 HTTP Request nodes to any AI agent: 1. **Search memories** — POST to `/v1/search` with the user's message to find relevant past context 2. **AI Agent responds** — system prompt includes retrieved memories, agent responds with full context 3. **Save new memories** — POST to `/v1/add` to store the conversation. Mengram auto-extracts facts and deduplicates ## Credential setup Create a **Header Auth** credential in n8n: ``` Name: Mengram API Key Header Name: Authorization Header Value: Bearer om-your-api-key ``` ## API endpoints used | Node | Method | URL | Body | | --------------- | ------ | ------------------------------ | ------------------------------------------------ | | Search Memories | POST | `https://mengram.io/v1/search` | `{"query": "...", "user_id": "...", "limit": 5}` | | Save to Memory | POST | `https://mengram.io/v1/add` | `{"messages": [...], "user_id": "..."}` | ## Swap the LLM The workflow uses OpenAI `gpt-4o-mini` by default. To use a different LLM, change the URL and body in the AI Response node: * **Anthropic**: `https://api.anthropic.com/v1/messages` * **Ollama** (local): `http://localhost:11434/api/chat` * **Any OpenAI-compatible API**: just change the URL and model name ## Example ```bash theme={null} curl -X POST http://localhost:5678/webhook/chat \ -H "Content-Type: application/json" \ -d '{"message": "I prefer Python and use Railway for hosting", "user_id": "user-123"}' # Later... curl -X POST http://localhost:5678/webhook/chat \ -H "Content-Type: application/json" \ -d '{"message": "What hosting should I deploy to?", "user_id": "user-123"}' # Agent remembers Railway preference and responds accordingly ``` ## Links * [Workflow on GitHub](https://github.com/alibaizhanov/mengram/tree/main/examples/n8n) * [API Reference](/api-reference) # OpenAI Agents API Source: https://docs.mengram.io/openai-agents Memory per end user for products built on the OpenAI Agents API and Agents SDK — one MCP connection per customer, scoped by a header. ## Why a second memory OpenAI's Agents API (public beta, September 2026) runs agents on the Codex harness with durable sessions, auto-compaction and a sandbox memory. That memory is per **workspace**: summaries and notes in the sandbox, a small `memory_summary.md` injected at run start, keyword search on demand, and consolidation that keeps the newest notes and drops older ones. A product with returning users needs memory per **user** — what this customer prefers, what was decided with them, what broke last time — that survives longer than the newest notes, and that is the same memory whether the agent runs on OpenAI, on Claude, or in your own code. Mengram is that memory, attached as an MCP server. ## One connection per end user ```python theme={null} from agents import Agent, Runner from agents.mcp import MCPServerStreamableHttp async with MCPServerStreamableHttp( name="mengram", params={ "url": "https://mengram.io/mcp", "headers": { "Authorization": "Bearer om-...", "X-Mengram-User": "cust_1042", # everything on this connection is this user's }, "timeout": 15, }, cache_tools_list=True, ) as memory: agent = Agent( name="Support", instructions="Call context_for before answering; call remember with anything durable.", mcp_servers=[memory], ) result = await Runner.run(agent, "hi, it's me again — same car as last time?") ``` Every `remember`, `recall` and `context_for` on that connection belongs to `cust_1042`, isolated from every other user of your product. The model never has to pass a `user_id` itself. There is no cap on the number of end users on any plan. `X-Mengram-User` accepts letters, digits and `. _ : @ + -`, up to 200 characters; anything else falls back to `default`. When headers are not configurable, send `?user_id=cust_1042` on the URL instead. ## Hosted MCP tool If OpenAI manages the connection (Responses API / Agents API JSON): ```json theme={null} { "type": "mcp", "server_label": "mengram", "server_url": "https://mengram.io/mcp?user_id=cust_1042", "authorization": "Bearer om-...", "allowed_tools": ["context_for", "recall", "remember"], "require_approval": "never" } ``` ## Keep the context small `context_for` takes `max_tokens` (default 1200). Memory is cut in rank order to fit and the pack ends with one line saying what was left out — see [max\_tokens](/api-reference#post-v1search). A support bot that used to replay 4,500 tokens of history per request sends the 600 that matter. ## Full example [examples/openai-agents-api](https://github.com/alibaizhanov/mengram/tree/main/examples/openai-agents-api) — a support agent for a car-rental company, one command to run. # OpenClaw Plugin Source: https://docs.mengram.io/openclaw Give your OpenClaw agent persistent memory with auto-recall, auto-capture, and 12 memory tools. ## Install ```bash theme={null} openclaw plugins install openclaw-mengram ``` ## Setup 1. Get a free API key at [mengram.io](https://mengram.io) 2. Add to `~/.openclaw/openclaw.json`: ```json theme={null} { "plugins": { "entries": { "openclaw-mengram": { "enabled": true, "config": { "apiKey": "${MENGRAM_API_KEY}" } } }, "slots": { "memory": "openclaw-mengram" } } } ``` 3. Set your API key: ```bash theme={null} export MENGRAM_API_KEY="om-your-key-here" ``` 4. Restart OpenClaw. Memory works automatically. ## How It Works **Auto-recall:** Before every agent turn, relevant memories are injected into context via `before_agent_start` hook. No manual tool calls needed. **Auto-capture:** After every turn, new information is automatically extracted and stored via `agent_end` hook. Nothing is lost. ## Tools The agent can use these 12 tools explicitly: | Tool | Purpose | | ------------------- | -------------------------------------------------------- | | `memory_search` | Search all 3 memory types with Graph RAG | | `memory_store` | Save text to long-term memory | | `memory_forget` | Delete a memory entity by name | | `memory_profile` | Get cognitive profile (who the user is) | | `memory_procedures` | List or search learned workflows | | `memory_feedback` | Record workflow success/failure (triggers evolution) | | `memory_episodes` | Search or list past events with date filtering | | `memory_timeline` | Get chronological timeline of memory events | | `memory_triggers` | Get smart triggers (reminders, contradictions, patterns) | | `memory_insights` | Get AI-generated reflections and patterns | | `memory_agents` | Run maintenance agents (curator, connector, digest) | | `memory_graph` | Get the knowledge graph (entities + connections) | ## Slash Commands | Command | Action | | ------------------ | ------------------ | | `/remember ` | Save to memory | | `/recall ` | Search memory | | `/forget ` | Delete from memory | ## CLI ```bash theme={null} openclaw mengram search "coffee preferences" # Search all 3 memory types openclaw mengram stats # Memory usage statistics openclaw mengram profile # Cognitive profile openclaw mengram procedures # List learned workflows openclaw mengram episodes # List past events openclaw mengram timeline # Chronological event timeline openclaw mengram triggers # Smart triggers openclaw mengram insights # AI-generated reflections openclaw mengram agents # Run maintenance agents openclaw mengram graph # Knowledge graph openclaw mengram feed # Activity feed openclaw mengram reindex # Reindex embeddings openclaw mengram dedup # Deduplicate memories openclaw mengram merge # Merge two entities ``` ## Configuration | Option | Default | Description | | ----------------------- | -------------------- | -------------------------------------- | | `apiKey` | `$MENGRAM_API_KEY` | API key from mengram.io | | `baseUrl` | `https://mengram.io` | Custom URL for self-hosted | | `autoRecall` | `true` | Inject memories before each turn | | `autoCapture` | `true` | Store memories after each turn | | `topK` | `5` | Max results per search | | `graphDepth` | `2` | Knowledge graph hops (0=off, 1, 2) | | `injectProfile` | `false` | Include cognitive profile periodically | | `profileFrequency` | `25` | Profile injection every N turns | | `maxFactsPerEntity` | `5` | Max facts shown per entity in context | | `maxRelationsPerEntity` | `5` | Max relationships shown per entity | | `maxEpisodes` | `5` | Max episodic memories in context | | `maxProcedures` | `3` | Max procedures in context | | `maxStepsPerProcedure` | `8` | Max steps shown per procedure | | `captureMessageCount` | `10` | Messages to capture after each turn | | `maxTriggers` | `10` | Max triggers shown | | `maxTimelineItems` | `20` | Max timeline items shown | | `requestTimeout` | `15000` | HTTP timeout in milliseconds | | `debug` | `false` | Verbose logging | ## Links * [npm: openclaw-mengram](https://www.npmjs.com/package/openclaw-mengram) * [GitHub: openclaw-mengram](https://github.com/alibaizhanov/openclaw-mengram) # Python SDK Source: https://docs.mengram.io/python-sdk Full reference for the Mengram Python client — zero external dependencies, works everywhere. ## Installation ```bash theme={null} pip install mengram-ai ``` For async support: ```bash theme={null} pip install mengram-ai[async] ``` ## Initialize ```python theme={null} from mengram import Mengram # Pass API key directly m = Mengram(api_key="om-your-key") # Or use environment variable # export MENGRAM_API_KEY=om-your-key m = Mengram() ``` ## Core Methods ### add(messages, ...) Add memories from a conversation. Automatically extracts entities, facts, episodes, and procedures. ```python theme={null} result = m.add([ {"role": "user", "content": "We fixed the OOM with Redis cache"}, {"role": "assistant", "content": "Noted the Redis cache fix."}, ]) # Returns: {"status": "accepted", "job_id": "job-..."} ``` | Parameter | Type | Default | Description | | ----------------- | ----------- | ----------- | -------------------------------------------------------------------------------------- | | `messages` | list\[dict] | required | Chat messages with role and content | | `user_id` | str | `"default"` | User identifier for multi-user isolation | | `agent_id` | str | None | Agent identifier | | `run_id` | str | None | Session/run identifier | | `app_id` | str | None | Application identifier | | `expiration_date` | str | None | ISO datetime — facts auto-expire | | `source` | str | None | Provenance source (e.g. `"discord"`, `"slack"`, `"email"`) | | `metadata` | dict | None | Arbitrary provenance metadata | | `agent_mode` | bool | False | Extract from all speakers (user + assistant). **Auto-enabled when `agent_id` is set.** | ```python theme={null} # With provenance tracking m.add(messages, source="slack", metadata={"channel": "#engineering"}) # Agent mode — extract from both user and assistant messages m.add(messages, agent_mode=True) ``` ### add\_text(text, ...) Add memories from plain text instead of chat messages. ```python theme={null} m.add_text("Meeting notes: decided to migrate to PostgreSQL 16") # With provenance m.add_text("Bug fix deployed", source="discord", metadata={"server": "dev-team"}) ``` Same parameters as `add()` except `text` instead of `messages`. ### add\_file(file\_path, ...) Upload a file and extract structured memories. Supports PDF (vision AI extraction), DOCX, TXT, and MD. Each page/chunk counts as 1 add from your quota. Returns immediately — processing happens in background. ```python theme={null} result = m.add_file("meeting-notes.pdf") # {"status": "accepted", "job_id": "job-...", "file_type": "pdf", "page_count": 12, "quota_used": 12} # Poll for completion m.wait_for_job(result["job_id"]) ``` | Parameter | Type | Default | Description | | ----------- | ---- | ----------- | ------------------------------------- | | `file_path` | str | required | Path to file (.pdf, .docx, .txt, .md) | | `user_id` | str | `"default"` | User identifier | | `agent_id` | str | None | Agent identifier | | `run_id` | str | None | Session/run identifier | | `app_id` | str | None | Application identifier | **File size limits:** Free 10MB, Pro 50MB, Business 100MB. ### search(query, ...) Semantic search across the knowledge graph with re-ranking. ```python theme={null} results = m.search("database preferences", limit=10) for r in results: print(f"{r['entity']} — score: {r['score']:.2f}") for fact in r.get("facts", []): print(f" - {fact}") ``` | Parameter | Type | Default | Description | | ------------- | ---- | ----------- | --------------------------------------------------------------------------------------------------------------------------------- | | `query` | str | required | Natural language search query | | `limit` | int | 5 | Max results | | `graph_depth` | int | 2 | Knowledge graph traversal depth | | `user_id` | str | `"default"` | User to search | | `agent_id` | str | None | Filter by agent | | `run_id` | str | None | Filter by run | | `app_id` | str | None | Filter by app | | `filters` | dict | None | Metadata filters | | `max_tokens` | int | None | Room the reply may take (estimated tokens). Cut in rank order to fit; `client.last_budget` holds the report of what was left out. | ```python theme={null} results = m.search("what does this customer prefer?", user_id="cust_1042", max_tokens=600) print(m.last_budget) # {"max_tokens": 600, "used_tokens": 512, "kept": {...}, "dropped": {...}} ``` ### search\_all(query, ...) Unified search across all 3 memory types. ```python theme={null} results = m.search_all("deployment") print(results["semantic"]) # entities print(results["episodic"]) # events print(results["procedural"]) # workflows ``` ### get\_all() / get\_all\_full() ```python theme={null} memories = m.get_all() # list all entities full = m.get_all_full() # with full facts, relations, knowledge ``` ### get(name) / delete(name) ```python theme={null} entity = m.get("PostgreSQL") # get specific entity m.delete("PostgreSQL") # delete entity ``` *** ## Cognitive Profile ### get\_profile(...) Generate a Cognitive Profile — a ready-to-use system prompt summarizing a user. ```python theme={null} profile = m.get_profile() print(profile["system_prompt"]) # "You are talking to Ali..." print(profile["facts_used"]) # number of facts used ``` | Parameter | Type | Default | Description | | --------- | ---- | ----------- | ------------------------- | | `user_id` | str | `"default"` | User to profile | | `force` | bool | False | Regenerate (bypass cache) | ### rules(...) Generate a CLAUDE.md, .cursorrules, or .windsurfrules file from memory. ```python theme={null} result = m.rules(format="claude_md") print(result["content"]) # structured rules file ``` | Parameter | Type | Default | Description | | --------- | ---- | ------------- | ----------------------------------------- | | `format` | str | `"claude_md"` | `claude_md`, `cursorrules`, or `windsurf` | | `force` | bool | False | Regenerate | | `user_id` | str | `"default"` | User | *** ## Episodic Memory ### episodes(...) Search or list episodic memories (events, experiences, interactions). ```python theme={null} events = m.episodes(query="auth bug", limit=5) recent = m.episodes(limit=20) jan = m.episodes(after="2026-01-01", before="2026-02-01") ``` | Parameter | Type | Default | Description | | --------- | ---- | ----------- | --------------------------- | | `query` | str | None | Search query (omit to list) | | `limit` | int | 20 | Max results | | `after` | str | None | ISO datetime start | | `before` | str | None | ISO datetime end | | `user_id` | str | `"default"` | User to query | *** ## Procedural Memory ### procedures(...) Search or list learned workflows. ```python theme={null} procs = m.procedures(query="deploy") all_procs = m.procedures(limit=50) ``` ### procedure\_feedback(id, ...) Report success/failure. On failure with context, triggers experience-driven evolution. ```python theme={null} m.procedure_feedback(proc_id, success=True) m.procedure_feedback(proc_id, success=False, context="Build OOM", failed_at_step=3) ``` ### procedure\_history(id) / procedure\_evolution(id) ```python theme={null} history = m.procedure_history(proc_id) # all versions evolution = m.procedure_evolution(proc_id) # what changed and why ``` *** ## Knowledge Graph & Timeline ### graph(...) Get the full knowledge graph — nodes and edges. ```python theme={null} g = m.graph() print(g["nodes"]) # entities print(g["edges"]) # relationships ``` ### timeline(...) Temporal search — facts within a time range. ```python theme={null} facts = m.timeline(after="2026-01-01", before="2026-02-01") for f in facts: print(f["created_at"], f["entity"], f["fact"]) ``` ### feed(...) Activity feed — recent memory changes. ```python theme={null} feed = m.feed(limit=20) ``` ### stats(...) Get usage statistics. ```python theme={null} s = m.stats() ``` *** ## Memory Management ```python theme={null} m.dedup() # find and merge duplicate entities m.dedup_all() # deduplicate facts across all entities m.dedup_entity("PostgreSQL") # deduplicate facts on one entity m.merge("src", "target") # merge two entities m.merge_user() # merge "User" entity into primary person m.archive_fact("Entity", "old fact") # archive a fact m.fix_entity_type("React", "technology") # fix entity type m.reindex() # re-embed all entities ``` *** ## Insights & Reflections ```python theme={null} m.reflect() # trigger reflection insights = m.insights() # get AI insights refs = m.reflections(scope="cross") # scope: entity, cross, temporal ``` *** ## Agents ```python theme={null} m.run_agents() # run all agents m.run_agents(agent="curator") # run specific agent history = m.agent_history(limit=5) # run history status = m.agent_status() # which agents are due ``` *** ## Smart Triggers ```python theme={null} triggers = m.get_triggers() # get triggers m.process_triggers() # fire pending triggers m.dismiss_trigger(trigger_id) # dismiss without firing m.detect_triggers(target_user_id="alice") # detect triggers for user ``` *** ## Webhooks ```python theme={null} m.create_webhook( url="https://example.com/hook", event_types=["memory_add", "memory_update"], secret="hmac-secret", ) hooks = m.get_webhooks() m.update_webhook(webhook_id=1, active=False) m.delete_webhook(webhook_id=1) ``` *** ## Teams ```python theme={null} team = m.create_team("Engineering", description="Shared memory") code = team["invite_code"] # share this code m.join_team("abc123") # join via invite code teams = m.get_teams() # list your teams members = m.team_members(team_id=1) m.share_memory("PostgreSQL", team_id=1) # share with team m.unshare_memory("PostgreSQL", team_id=1) # make personal again m.leave_team(team_id=1) m.delete_team(team_id=1) # owner only ``` *** ## API Keys ```python theme={null} keys = m.list_keys() new_key = m.create_key(name="production") m.rename_key(key_id="...", name="staging") m.revoke_key(key_id="...") ``` *** ## Billing ```python theme={null} billing = m.get_billing() # plan, usage, quotas checkout = m.create_checkout("pro") # upgrade to Pro portal = m.create_portal() # manage subscription ``` *** ## Jobs ```python theme={null} status = m.job_status("job-abc123") # check status result = m.wait_for_job("job-abc123", # poll until done poll_interval=1.0, max_wait=60.0) ``` *** ## Import Data ```python theme={null} # Import ChatGPT export m.import_chatgpt("~/Downloads/chatgpt-export.zip") # Import Obsidian vault m.import_obsidian("~/Documents/MyVault") # Import text/markdown files m.import_files(["notes.md", "journal.txt"]) ``` All import methods support `user_id`, `chunk_size`/`chunk_chars`, and `on_progress` callback. *** ## Error Handling ### QuotaExceededError Raised when your monthly plan limit is reached (HTTP 402). Not retried. ```python theme={null} from mengram import Mengram from mengram.cloud.client import QuotaExceededError m = Mengram() try: m.add([{"role": "user", "content": "Hello"}]) except QuotaExceededError as e: print(e.action) # "add" or "search" print(e.limit) # 50 print(e.current) # 50 print(e.plan) # "free" print(e) # "Quota exceeded for 'add': 50/50 (plan: free). Upgrade at ..." ``` | Attribute | Type | Description | | --------- | ---- | ------------------------------------------------------ | | `action` | str | Which action was blocked (`add`, `search`, `add_file`) | | `limit` | int | Monthly limit for this action | | `current` | int | Current usage count | | `plan` | str | Current plan (`free`, `starter`, `pro`) | ### Retry Behavior The client automatically retries on transient errors (429, 502, 503, 504) with exponential backoff up to 3 attempts. **402 (quota exceeded) is never retried.** *** ## Quota Usage After any API call, check your current quota usage via the `.quota` property: ```python theme={null} m.search("test") print(m.quota) # {"add": {"used": 5, "limit": 30}, "search": {"used": 12, "limit": 100}} # Check before hitting the wall if m.quota.get("search", {}).get("used", 0) >= m.quota.get("search", {}).get("limit", 1) * 0.8: print("Warning: approaching search quota limit") ``` The quota is read from response headers (`X-Quota-Add-Used`, `X-Quota-Add-Limit`, `X-Quota-Search-Used`, `X-Quota-Search-Limit`) and reflects usage at the time of the last API call. # Quickstart Source: https://docs.mengram.io/quickstart Look at what memory would already know — no account. Then keep it, in under 2 minutes. ## 0. See what it would know — no account needed Before anything else, look at what memory would already hold. This reads the Claude Code history on your own disk, uploads nothing, and needs no key: ```bash theme={null} pip install mengram-ai mengram try ``` ``` Scanned 23 sessions across 4 projects (2026-07-31 → 2026-09-11). If this were memory, your AI would already know: Projects: mengram (17), po-pilot (4), infra (1) Your stack: railway, python, postgres, stripe, bun Workflow patterns detected: ⚙ debug from logs (seen in 16 sessions) ⚙ migrate → verify schema (seen in 12 sessions) ⚙ branch → PR → merge (seen in 6 sessions) ⚙ test → fix → re-test (seen in 4 sessions) Right now, every new session starts from zero and relearns all of this. ``` Three seconds, and nothing to undo if you walk away. If the command is not found afterwards, your user-site bin is not on PATH: `export PATH="$(python3 -m site --user-base)/bin:$PATH"`. Like what you see? Keep it — the steps below take about two minutes. ## Fastest path — paste one prompt into your agent If you have a coding agent (Claude Desktop, Cursor, Claude Code, Codex, Windsurf), skip the manual steps entirely. Paste this prompt: ``` Install Mengram for me. Fetch the canonical install guide at https://mengram.io/agent-install.txt and follow it precisely. My email is YOUR_EMAIL_HERE. ``` The agent reads our [install guide](/agent-install), runs `pip install`, signs you up, configures the MCP server in its host, and verifies the install end-to-end with `mengram doctor`. Walk away — it tells you when it's done. Want to do it manually? Continue below. ## 1. Get an API key Sign up at [mengram.io](https://mengram.io/#signup) to get your free API key. It starts with `om-`. You can also do it from the terminal — `mengram signup --email you@example.com` sends a 6-digit code; `mengram signup --email you@example.com --code 123456` exchanges it for a key and saves it to `~/.mengram/config.json`. ## 2. Install the SDK ```bash Python theme={null} pip install mengram-ai ``` ```bash JavaScript theme={null} npm install mengram-ai ``` ## 3. Add your first memory ```python Python theme={null} from mengram import Mengram m = Mengram(api_key="om-your-key") # Add memories from a conversation result = m.add([ {"role": "user", "content": "I deployed the app on Railway. Using PostgreSQL."}, {"role": "assistant", "content": "Got it, noted the Railway + PostgreSQL stack."}, ]) # result contains a job_id for background processing print(result) # {"status": "accepted", "job_id": "job-..."} ``` ```javascript JavaScript theme={null} const { MengramClient } = require('mengram-ai'); const m = new MengramClient('om-your-key'); await m.add([ { role: 'user', content: 'I deployed the app on Railway. Using PostgreSQL.' }, ]); ``` ## 4. Search your memories ```python theme={null} # Semantic search results = m.search("deployment stack") for r in results: print(f"{r['entity']} (score={r['score']:.2f})") for fact in r.get("facts", []): print(f" - {fact}") # Unified search — all 3 memory types at once all_results = m.search_all("deployment issues") print(all_results["semantic"]) # knowledge graph results print(all_results["episodic"]) # events and experiences print(all_results["procedural"]) # learned workflows ``` ## 5. Get a Cognitive Profile Generate a ready-to-use system prompt that captures who a user is: ```python theme={null} profile = m.get_profile() system_prompt = profile["system_prompt"] # Use in any LLM call response = openai.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": "What should I work on next?"}, ] ) ``` ## 6. Claude Code — Full Memory Loop If you use Claude Code, set up automatic memory in one command: ```bash theme={null} mengram setup ``` This creates your account (if needed), saves your API key, and installs hooks — all from the terminal. Or manually: `export MENGRAM_API_KEY=om-your-key` → `mengram hook install`. This installs 3 hooks: * **Session context** — loads your profile when Claude Code starts * **Auto-recall** — searches relevant memories on every prompt * **Auto-save** — captures conversations in the background Claude Code remembers you across sessions automatically. See [Claude Code Integration](/claude-code) for details. ## 7. Verify the install end-to-end ```bash theme={null} mengram doctor ``` Runs an auth + add + search round-trip against the cloud API. Final line is `OK: round-trip succeeded.` on success, or `FAIL: ` with a fix-it hint. Use the environment variable `MENGRAM_API_KEY` so you don't have to pass the key every time: `m = Mengram()` # Search & Filters Source: https://docs.mengram.io/search-filters Semantic search, metadata filters, graph traversal depth, and unified search across all memory types. ## Basic search ```python theme={null} results = m.search("deployment stack") # Returns top 5 entities by relevance ``` ## Parameters | Parameter | Type | Default | Description | | ------------- | ---- | --------- | ------------------------------------------------ | | `query` | str | required | Natural language search query | | `limit` | int | 5 | Maximum results to return | | `graph_depth` | int | 2 | How many hops to traverse in the knowledge graph | | `user_id` | str | "default" | User whose memories to search | | `filters` | dict | None | Metadata key-value filters | ## Metadata filters Filter search results by metadata stored on entities. Uses PostgreSQL JSONB containment (`@>`) for fast filtering with GIN indexes. ```python theme={null} # Filter by agent results = m.search("config", filters={"agent_id": "support-bot"}) # Filter by app results = m.search("preferences", filters={"app_id": "prod"}) # Multiple filters (AND logic) results = m.search("issues", filters={ "agent_id": "support-bot", "app_id": "production", }) # Also works with shorthand parameters results = m.search("config", agent_id="support-bot", app_id="prod") # Filter by provenance source results = m.search("meeting notes", filters={"source": "slack"}) # Filter by custom metadata results = m.search("bugs", filters={"source": "discord", "channel": "#engineering"}) ``` ## Graph RAG (graph depth) Mengram uses Graph RAG — retrieval-augmented generation over a knowledge graph. Instead of flat vector search, it traverses entity relationships to find connected context. ``` Query: "database issues" → Direct: PostgreSQL (technology) → 1 hop: Railway (platform) — "deployed_on" → 2 hop: OOM Bug (event) — "occurred_on" → Railway ``` Searching for "database issues" also surfaces that OOM bug on Railway — connected through the knowledge graph, not through keyword or embedding similarity. Control traversal depth with `graph_depth`: ```python theme={null} # Shallow — just direct matches, fastest results = m.search("Python", graph_depth=0) # Default — 2 hops (entity → related → related) results = m.search("Python", graph_depth=2) # Deep — traverse far connections, slower but more context results = m.search("Python", graph_depth=4) ``` ## Unified search Search all 3 memory types in a single call: ```python theme={null} results = m.search_all("deployment problems") # Semantic — knowledge graph entities with facts for entity in results["semantic"]: print(entity["entity"], entity["facts"]) # Episodic — events and experiences for event in results["episodic"]: print(event["summary"], event["outcome"]) # Procedural — workflows and processes for proc in results["procedural"]: print(proc["name"], proc["steps"]) ``` ## Timeline search Search facts by time range: ```python theme={null} facts = m.timeline(after="2026-01-01", before="2026-02-01") for f in facts: print(f["created_at"], f["entity"], f["fact"]) ``` # Self-hosting Source: https://docs.mengram.io/self-hosting Run Mengram on your own infrastructure with Docker Compose — PostgreSQL, Redis, and Mengram API in one command. ## Prerequisites * Docker and Docker Compose * An OpenAI API key (or compatible LLM provider) ## Quick start ```bash theme={null} git clone https://github.com/alibaizhanov/mengram.git cd mengram # Set your OpenAI API key export OPENAI_API_KEY=sk-your-key # Start everything docker compose up -d ``` This starts three services: * **PostgreSQL 16** on port 5432 * **Redis 7** on port 6379 * **Mengram API** on port 8420 ## Verify it's running ```bash theme={null} curl http://localhost:8420/v1/health # {"status": "ok", "version": "2.22.0"} ``` Both `/health` and `/v1/health` work. ## Create your first account By default, Mengram requires email verification via [Resend](https://resend.com). For self-hosting without an email provider, set `DISABLE_EMAIL_VERIFICATION=true`: ```yaml theme={null} environment: DISABLE_EMAIL_VERIFICATION: "true" ``` With this enabled, the signup endpoint returns your API key immediately — no verification code needed. If you prefer to keep email verification, set `RESEND_API_KEY` and `EMAIL_FROM` (see Configuration below). Without Resend, verification codes are logged to stdout — check `docker compose logs mengram` to find them. ## Configuration Environment variables in `docker-compose.yml`: ### Required | Variable | Default | Description | | ---------------- | -------- | ------------------------------------- | | `OPENAI_API_KEY` | required | OpenAI API key for embeddings and LLM | ### Core | Variable | Default | Description | | -------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `DATABASE_URL` | `postgresql://mengram:mengram@postgres:5432/mengram` | PostgreSQL connection string | | `REDIS_URL` | `redis://redis:6379` | Redis connection string | | `LLM_PROVIDER` | `openai` | LLM provider (`openai`, `anthropic`, `ollama`) | | `LLM_MODEL` | `gpt-4o-mini` | Model for memory extraction | | `EMBEDDING_PROVIDER` | `openai` | Embedding provider (`openai` or `cohere`). See [Multilingual support](#multilingual-support-cohere) below. | | `COHERE_API_KEY` | *(required if cohere)* | Cohere API key — needed when `EMBEDDING_PROVIDER=cohere` | | `BASE_URL` | `https://mengram.io` | Your instance's public URL (used in emails, OAuth redirects) | ### Self-hosting | Variable | Default | Description | | ---------------------------- | --------------------------------- | ------------------------------------------------------------ | | `DISABLE_EMAIL_VERIFICATION` | `false` | Skip email verification on signup — returns API key directly | | `RESEND_API_KEY` | *(optional)* | [Resend](https://resend.com) API key for sending emails | | `EMAIL_FROM` | `Mengram ` | Sender address for emails | ### GitHub OAuth (optional) GitHub login is optional — email signup works without it. | Variable | Default | Description | | ---------------------- | ------------ | ------------------------------ | | `GITHUB_CLIENT_ID` | *(optional)* | GitHub OAuth App client ID | | `GITHUB_CLIENT_SECRET` | *(optional)* | GitHub OAuth App client secret | To enable GitHub login: 1. Create a GitHub OAuth App at [github.com/settings/developers](https://github.com/settings/developers) 2. Set the callback URL to `http://your-host:8420/auth/github/callback` 3. Add both env vars to your `docker-compose.yml` ## Multilingual support (Cohere) Mengram cloud uses Cohere `embed-multilingual-v3.0` for native quality across 23 languages — Russian, Chinese, Spanish, Japanese, Korean, Arabic, and more. To use the same on a self-hosted instance: ```yaml theme={null} environment: EMBEDDING_PROVIDER: cohere COHERE_API_KEY: your-cohere-key ``` Get a Cohere API key at [dashboard.cohere.com](https://dashboard.cohere.com). The trial tier is free and rate-limited — sufficient for development and small workloads. Stick with the default `EMBEDDING_PROVIDER=openai` (uses `text-embedding-3-large`, 1536 dim) if your data is mostly English. Switch to Cohere if you have non-English users or need cross-lingual retrieval (e.g. English queries finding Russian documents). Embeddings are written to a separate `embedding_v2` column (1024 dim) so you can switch providers without losing existing data — Mengram routes search to the right column based on query vector size. ## Use with Ollama (fully local) Run Mengram with a local LLM — no external API calls: ```yaml theme={null} environment: LLM_PROVIDER: ollama LLM_MODEL: llama3.2 OLLAMA_HOST: http://host.docker.internal:11434 ``` Make sure Ollama is running on your host machine with the model pulled: ```bash theme={null} ollama pull llama3.2 ``` ## Use with LM Studio ```yaml theme={null} environment: LLM_PROVIDER: openai LLM_MODEL: your-model-name OPENAI_API_KEY: lm-studio OPENAI_API_BASE: http://host.docker.internal:1234/v1 ``` ## Use with OpenRouter ```yaml theme={null} environment: LLM_PROVIDER: openai LLM_MODEL: qwen/qwen3.6-plus:free OPENAI_API_KEY: sk-or-v1-your-key OPENAI_BASE_URL: https://openrouter.ai/api/v1 ``` ## Connect your tools ### MCP server (Claude Desktop, Cursor, Windsurf) ```json theme={null} { "mcpServers": { "mengram": { "command": "uvx", "args": ["mengram-ai"], "env": { "MENGRAM_API_KEY": "om-your-key", "MENGRAM_URL": "http://localhost:8420" } } } } ``` ### VS Code extension 1. Install the **Mengram** extension from the marketplace 2. Open Settings and set: * `mengram.apiKey` → your API key * `mengram.baseUrl` → `http://localhost:8420` ### Claude Code hooks ```bash theme={null} export MENGRAM_API_KEY=om-your-key export MENGRAM_URL=http://localhost:8420 mengram hook install ``` ### Python SDK ```python theme={null} from mengram import Mengram m = Mengram(api_key="om-your-key", base_url="http://localhost:8420") ``` ### JavaScript SDK ```javascript theme={null} import { MengramClient } from 'mengram-ai'; const m = new MengramClient('om-your-key', 'http://localhost:8420'); ``` ## Data persistence PostgreSQL data is stored in a Docker volume (`pgdata`). Your memories persist across container restarts. To backup: ```bash theme={null} docker compose exec postgres pg_dump -U mengram mengram > backup.sql ``` To restore: ```bash theme={null} docker compose exec -T postgres psql -U mengram mengram < backup.sql ``` ## Production deployment For production, consider: * Use a managed PostgreSQL (e.g., Supabase, Neon, RDS) * Use a managed Redis (e.g., Upstash, ElastiCache) * Set strong database passwords * Put behind a reverse proxy with TLS (nginx, Caddy) * Set `GUNICORN_WORKERS` for concurrency * Set `BASE_URL` to your public domain The easiest way to deploy is [Railway](https://railway.app) — just connect your GitHub repo and set environment variables. Mengram's cloud version runs on Railway. # Vapi Voice Integration Source: https://docs.mengram.io/vapi Persistent memory for Vapi voice agents — the assistant knows who's calling before saying a word. Drop-in webhook adapter, no glue code, no n8n. ## Overview [Vapi](https://vapi.ai) assistants are stateless across calls — when the same caller phones back next week, the agent starts from scratch and asks for their name and number again. Mengram's Vapi adapter solves this with two HTTP webhooks the assistant invokes at call start and call end. * **At call start** — the assistant calls a custom tool (`recall_caller`) with the caller's phone number. Mengram returns a concise summary of everything it knows about that caller. * **At call end** — Vapi posts the final transcript to a server URL. Mengram extracts entities, facts, and episodes, keyed to the caller's phone number. Per-caller isolation works out of the box: each phone number gets its own memory namespace via `sub_user_id=voice:`. One Mengram account can power thousands of caller memories across multiple white-label clients. Apache 2.0, free tier covers about 40 inbound calls/month. Paid tiers from \$5/mo. Same retrieval stack as the rest of Mengram (hybrid vector + BM25 + RRF, Ebbinghaus decay, reflection cron). ## Quick Setup You'll need a Mengram API key. Get one at [mengram.io](https://mengram.io) → sign up → Dashboard → Keys. ### 1. Add the recall tool to your Vapi assistant In the Vapi dashboard, open your assistant → **Tools** → add a Custom Tool: ```json theme={null} { "type": "function", "function": { "name": "recall_caller", "description": "Get what we know about this caller. Call at the start of every conversation.", "parameters": { "type": "object", "properties": { "phone": { "type": "string", "description": "Caller phone number in E.164 format" } }, "required": ["phone"] } }, "server": { "url": "https://mengram.io/v1/voice/vapi/recall", "headers": { "Authorization": "Bearer YOUR_MENGRAM_KEY" } } } ``` The tool returns a single string the assistant verbalizes naturally. Real response: ```json theme={null} { "results": [{ "toolCallId": "call_abc", "result": "Known about caller (Sarah Johnson): Sarah Johnson: prefers morning slots before 11 AM | Sarah Johnson: gets anxiety with novocaine | Sarah Johnson: booked cleaning May 14" }] } ``` ### 2. Tell the assistant to call it first In your assistant's system prompt, add one paragraph: ``` At the start of every call, immediately call the recall_caller tool with the caller's phone number. Use what's returned to greet them naturally and reference past interactions. Don't ask for information you already have. ``` ### 3. Wire the end-of-call save webhook Still in the assistant config, set the **Server URL** to: ``` https://mengram.io/v1/voice/vapi/save ``` Add the same `Authorization: Bearer YOUR_MENGRAM_KEY` header in the Server URL config. Vapi's Server URL receives **every** assistant event — `status-update`, partial `transcript` chunks, `conversation-update`, `end-of-call-report`, etc. Mengram's save endpoint filters internally and only triggers extraction on `end-of-call-report`. Everything else returns a benign 200. You can safely wire one Server URL for the whole assistant — no extra routing needed. ### 4. Test it Make a real test call to the assistant. After it hangs up, wait \~30–60s for extraction, then call back from the same number. The assistant should greet you with what it learned from the first call. ## How It Works ``` ┌─────────────────────────────────────────────────────────┐ │ Call 1: New caller │ │ ↓ Vapi calls recall_caller("+15551234567") │ │ ↓ Mengram: "New caller — no prior context." │ │ ↓ Assistant: "Hi, who am I speaking with?" │ │ ↓ Caller talks, conversation flows │ │ ↓ Vapi posts end-of-call-report to /v1/voice/vapi/save │ │ ↓ Mengram extracts entities, facts, episodes │ │ ↓ Reflection cron consolidates patterns nightly │ │ │ │ Call 2: Same caller, days later │ │ ↓ Vapi calls recall_caller("+15551234567") │ │ ↓ Mengram: "Sarah Johnson: prefers morning slots..." │ │ ↓ Assistant: "Hi Sarah! Want a morning slot again?" │ └─────────────────────────────────────────────────────────┘ ``` Under the hood, the recall endpoint pulls every entity + fact for the caller's `sub_user_id` directly (not semantic search) — for a known caller you want **everything we know**, not "most relevant to a query." Persons are sorted by fact count descending so the caller's own facts surface ahead of mentioned people (their daughter, their doctor). The save endpoint routes the transcript through Mengram's standard extraction pipeline — same code path as `/v1/add`. Entities, facts, episodes, and procedures all get extracted normally. The daily reflection cron then synthesizes patterns ("this caller prefers morning slots", "anxiety about novocaine") so the next recall returns insight, not raw transcripts. ## Endpoints ### POST /v1/voice/vapi/recall Called by Vapi as a custom tool. Returns the caller context string. **Request body** (either shape accepted — Vapi sends both): ```json theme={null} { "message": { "type": "tool-calls", "toolCallList": [{ "id": "call_abc", "name": "recall_caller", "arguments": { "phone": "+15551234567" } }], "call": { "customer": { "number": "+15551234567" } } } } ``` Or the OpenAI-nested form: ```json theme={null} { "message": { "type": "tool-calls", "toolCalls": [{ "id": "call_abc", "type": "function", "function": { "name": "recall_caller", "arguments": "{\"phone\": \"+15551234567\"}" } }] } } ``` **Response** (Vapi tool-result format — `result` MUST be a string): ```json theme={null} { "results": [{ "toolCallId": "call_abc", "result": "Known about caller (Sarah Johnson): Sarah Johnson: prefers morning slots..." }] } ``` **Quota:** counts as 1 `search`. If `message.type` is anything other than `tool-calls`, the endpoint returns `200 {"status":"ignored"}` so Vapi doesn't mark the assistant as broken when lifecycle events arrive at the same URL. ### POST /v1/voice/vapi/save Called by Vapi at end of call. Routes transcript through Mengram's extraction pipeline. **Request body:** ```json theme={null} { "message": { "type": "end-of-call-report", "endedReason": "customer-ended-call", "call": { "id": "...", "customer": { "number": "+15551234567" } }, "transcript": "Agent: ...\nCaller: ..." } } ``` **Response** (202 — extraction runs in the background): ```json theme={null} { "status": "accepted", "job_id": "job-xxxx", "sub_user_id": "voice:+15551234567" } ``` **Quota:** counts as 1 `add`. The transcript can also be at `message.artifact.transcript` — Mengram reads whichever is present. Only `end-of-call-report` triggers extraction; partial transcript events are ignored. ## Pricing Each inbound call = 1 recall + 1 save = 1 search quota + 1 add quota. Free tier (40 adds, 200 searches/mo) covers about 40 inbound calls per month — enough to validate. Paid tiers from \$5/mo. See [full pricing](https://mengram.io/#pricing). | Plan | Inbound calls/mo (estimate) | | --------------- | --------------------------- | | Free | \~40 | | Starter (\$5) | \~100 | | Pro (\$19) | \~1,000 | | Growth (\$59) | \~3,000 | | Business (\$99) | \~8,000 | ## Performance Measured against production with a 1,186-word transcript indexed for the caller: * Single recall: **500–900ms** * Under 10–20 concurrent recalls: **p50 ≈ 1200ms, p95 ≈ 1300ms** The assistant calls `recall_caller` during the natural greeting pause, so callers don't perceive the latency. If your use case has hard sub-1s SLAs, benchmark in your own setup first. ## FAQ Mem0's Vapi tutorials require gluing mem0 + n8n + custom code together. Mengram's adapter is Vapi-native — paste the JSON, you're done. Same hybrid retrieval (vector + BM25 + RRF) underneath, just less wiring. The recall/save endpoints are HTTP webhooks — anything that can POST JSON works. Native Pipecat processor and LiveKit agent helper aren't built yet; email [ali@mengram.io](mailto:ali@mengram.io) if you need one and I'll prioritize based on demand. Self-host gives full data residency (Apache 2.0, your Postgres, your OpenAI key). Hosted-cloud BAA isn't yet available — for now, healthcare voice agents should self-host. See [Self-hosting](/self-hosting). Yes for the conversation flow, but web calls don't have `customer.number`, so recall returns "Web caller — no phone number yet." For full end-to-end testing, buy a \$1/mo Vapi phone number and call yourself. They'll be treated as a new caller. If you need cross-number identity, ask for confirmation in the assistant prompt ("Are you the same Sarah who called last week?") and use the existing user\_id field to merge — or run `/v1/merge_user` once you've confirmed. ## Next Steps * Try the [landing demo](https://mengram.io/integrations/vapi) for a one-page setup walkthrough * See [Memory Types](/memory-types) for what gets extracted from transcripts * Check the [API Reference](/api-reference) for `/v1/voice/vapi/recall` and `/v1/voice/vapi/save` endpoint specs * [Self-host](/self-hosting) if you want full data residency Questions? Email [ali@mengram.io](mailto:ali@mengram.io). # Webhooks Source: https://docs.mengram.io/webhooks Real-time notifications when memories are created, updated, or deleted. ## Overview Webhooks send HTTP POST requests to your server when memory events occur. Use them to sync memories with your app, trigger workflows, or build real-time features. ## Create a webhook ```python theme={null} hook = m.create_webhook( url="https://your-app.com/webhooks/mengram", name="Production webhook", event_types=["memory_add", "memory_update", "memory_delete"], secret="your-hmac-secret", # optional, for signature verification ) print(hook) # {"id": 1, "url": "...", "active": true} ``` ## Event types | Event | Description | | --------------- | ------------------------- | | `memory_add` | New entity or facts added | | `memory_update` | Entity facts updated | | `memory_delete` | Entity deleted | ## Webhook payload ```json theme={null} { "event": "memory_add", "timestamp": "2026-02-27T10:30:00Z", "data": { "entity": "PostgreSQL", "type": "technology", "facts": ["Uses PostgreSQL 16", "Deployed on Railway"], "user_id": "default" } } ``` ## Signature verification If you provided a `secret`, each request includes an `X-Mengram-Signature` header with an HMAC-SHA256 signature of the request body. ```python theme={null} import hmac, hashlib def verify_webhook(body: bytes, signature: str, secret: str) -> bool: expected = hmac.new( secret.encode(), body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(f"sha256={expected}", signature) ``` ## Manage webhooks ```python theme={null} # List all hooks = m.get_webhooks() # Update m.update_webhook(webhook_id=1, active=False) # Delete m.delete_webhook(webhook_id=1) ```