MemAI
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| MEMAI_HOME | No | Directory to store the memai.db file. Defaults to ~/.memai. | |
| MEMAI_ADMIN_PORT | No | Port for the admin dashboard (default: 8765). | |
| MEMAI_EMBED_MODEL | No | Set to a Hugging Face repo id or a local path to use a different embedding model instead of the bundled one. |
Capabilities
Features and capabilities supported by this server
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| noteB | Save a general long-term memory (fact, decision, finding). Stored as type='note'. Timeless knowledge -- retrieved by relevance, not recency. Bring it back with recall() (or search(type='note')); pulse() also shows the few most recent ones as warm-up breadcrumbs. tags: comma-separated keywords/synonyms -- write generously; tags feed both the keyword index and the embedding, so they make the memory findable even when the vector side is unavailable. |
| checkpointA | Snapshot current working state (intent/established/pursuing/open_questions). A summary of where the work stands, so the next session picks up the right bearing via pulse(). Fields are free-length; still prefer a readable summary here and put timeless detail into note() -- checkpoints are read for bearing, not as an archive. Stored as type='checkpoint'. |
| anti_patternC | Record a mistake/temptation to avoid repeating, and the correct approach. Stored as type='anti_pattern'; open ones for a domain are surfaced by pulse(). |
| reasoningB | Record a reasoning trace / analysis worth keeping (not a fact, a thought process). Stored as type='reasoning' -- filter search/list_* with type='reasoning' to get these back. |
| handoffA | Leave a note for another agent/session picking up this work. Stored as type='handoff'; open ones for a domain are surfaced by pulse(). |
| searchA | Hybrid search over memory content+tags+domain: BM25 keywords + local-model vectors. Each result is annotated with match_source ("fts" | "vec" | "both"), fts_rank (bm25, lower = better) and/or vec_distance (cosine, lower = closer). Both retrievers only widen the candidate set -- judge the returned candidates yourself. Multiple space-separated paraphrases still help the keyword side (they're OR'd together). Only active memories by default. Falls back to keyword-only if the embedding model is unavailable. Content is snippet-truncated per result -- call get_memory(uid) for the full record. type filters (one writer each): 'note', 'reasoning', 'checkpoint', 'anti_pattern', 'handoff'. To recall note()'d knowledge specifically, recall() is the sugar for search(type='note'). |
| recallA | Recall long-term knowledge saved with note() (type='note'). The dedicated verb for "bring back what I noted": a hybrid search (BM25 + vectors) scoped to type='note', ranked by relevance -- which is what you want for timeless facts/rules/decisions. note() has no recency warm-up hook the way checkpoints have pulse(); this (or search(type='note')) is how notes come back. Content is snippet-truncated -- call get_memory(uid) for the full record. |
| list_by_domainA | List active memories for a domain, most recent first. Fallback when search misses. Matches domain exactly -- see list_domains() for the real strings in use. Content is snippet-truncated per result -- call get_memory(uid) for the full record. |
| list_recentA | List the most recent active memories, optionally filtered by type/domain. Content is snippet-truncated per result -- call get_memory(uid) for the full record. |
| list_domainsA | List distinct domains with their memory count and latest activity. Warm-up discovery. domain is free text and drifts over time (e.g. 'PROJ-1042' vs 'proj-1042'), and pulse/list_by_domain match it exactly -- this surfaces the real strings so you target the right one instead of guessing. Ordered by most recent activity. |
| pulseA | Session warm-up: latest checkpoint + open handoffs/anti-patterns + recent notes. Picks the checkpoint by created_at DESC, never by similarity -- a similarity-ranked top-1 can return a stale checkpoint over a same-day one, which is exactly the failure mode this avoids. latest_checkpoint is returned in full (that's the point of pulse), with its relations attached so linked memories are visible without a separate get_relations call. handoffs and anti_patterns are notes left for whoever resumes; recent_notes are the newest note()'d facts, as recency breadcrumbs -- for relevance-ranked recall use recall()/search(). Those three lists are snippet-truncated -- call get_memory(uid) for one in full. |
| get_memoryB | Fetch a single memory's full record, including its edit history and relations. |
| edit_memoryB | Correct/update a memory's content, keeping the previous version in edit history. Corrections are common in append-only memory stores that only support delete, not edit; this preserves the old content instead of losing it. |
| link_memoriesA | Create a queryable edge between two memories. relation_type is free text but keep it consistent, e.g. 'supersedes', 'relates_to', 'contradicts', 'links_to'. |
| get_relationsA | List all relations (incoming and outgoing) for a memory. |
| set_confidenceB | Set a memory's confidence: unverified | confirmed | contradicted. |
| forgetA | Archive a memory (soft delete -- content is kept, just excluded from default search/list). A |
| purge_memoryA | PERMANENTLY delete a memory + its edit history + relations. Irreversible. Use forget() instead unless the user explicitly asked to permanently remove data -- forget() is reversible (archived, content kept), this is not. Guardrail: confirm_phrase must exactly equal "DELETE ", typed by the user in their own message. Do not construct this string yourself from an inferred "yes"/"confirm" -- it must come from the user actually stating the uid back. |
| dedup_scanA | Surface likely-duplicate/contradictory memory pairs. Semantic (cosine over the embedded store) when vectors are available,
lexical overlap otherwise -- each pair carries its |
| optimize_scanA | Dump the memory corpus compactly so you can plan a curation pass. Step 1 of the "optimize my memories" workflow. Returns every memory's curation-relevant fields, the relation edges among them, and dedup-candidate pairs as a starting hint. Read this, then decide what to compact/reword/retag/redomain/set_confidence/archive/link/merge/ distill and stage it with optimize_stage. The listing is slim on purpose so a few-hundred-memory store fits one
response: content is a ~120-char snippet plus On a grown store, prefer INCREMENTAL curation over full-corpus
passes:
Before proposing any change, CHECK IT AGAINST LIVE FACTS -- do not rewrite or archive something that was true then but stale now, and do not "correct" something that is still true:
|
| optimize_stageA | Stage a batch of curation suggestions for human review in the dashboard. Step 2 of the "optimize my memories" workflow. Writes the suggestions to a new optimization run; they are NOT applied here -- the user reviews and applies/rejects each one in the admin dashboard's Optimization tab, where a backup is taken before the first apply and every applied change can be undone. Each suggestion is an object: {"kind": ..., "target_uid": ..., "payload": {...}, "rationale": "why", "verified": "what live-facts check you did"} Kinds and their payload: compact / reword {"new_content": str} retag {"tags": str} comma-separated redomain {"domain": str} set_confidence {"confidence": "unverified|confirmed|contradicted"} archive {"reason": str} soft/reversible; never hard-deletes link {"from_uid", "to_uid", "relation_type", "note"?} merge {"keep_uid", "drop_uid", "note"?} links supersedes + archives drop distill {"source_uids": [uid, ...], "new_type": "note|reasoning|anti_pattern", "new_content": str, "tags"?, "domain"?} distill extracts the durable knowledge out of one or MORE source
memories into a newly authored one: creates it, links it link/merge derive target_uid from the payload (from_uid / drop_uid)
and distill creates its target -- omit target_uid for those kinds.
Destructive suggestions (archive, set_confidence=contradicted,
distill) require a non-empty Invalid suggestions are skipped and reported in |
| optimize_runsA | List optimization runs with their review progress. Read-only companion to optimize_stage: after staging, use this to see whether the user has applied/rejected your suggestions in the admin dashboard. Each run carries total/pending/applied/rejected counts, its note, and the safety-backup path once the first apply happened. Applying/rejecting stays in the dashboard by design -- the agent proposes, the human disposes. |
| optimize_statusA | Inspect one optimization run: every suggestion and its decision. Read-only. Returns the run header plus each suggestion's kind, target_uid, payload, rationale, verified, status (pending/applied/rejected) and decided_at -- so you can tell which proposals landed, follow up on rejected ones, or build on applied ones in a later pass. |
| helpA | Explain the memai tools, read directly from their code docstrings. Without arguments: every tool with its one-line summary. With command='': that tool's full signature and docstring. The docs can't drift from behavior because they ARE the code's own docstrings, extracted at call time. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Filipe-Soares-de-Almeida/MemAI'
If you have feedback or need assistance with the MCP directory API, please join our Discord server