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. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| 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
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Filipe-Soares-de-Almeida/MemAI'
If you have feedback or need assistance with the MCP directory API, please join our Discord server