Model Context Protocol · Remote server
Memory-MCP stores, retrieves, and semantically searches your AI agents' "memories", organized into isolated multi-tenant spaces protected by API Key — so context survives across conversations, without wasting tokens.
# call an MCP tool with your API Key POST /mcp X-Api-Key: mmcp_xxxxxxxxxxxxxxxxxxxxx tool: "search_memory" query: "user preferences" category: "profile" includeProfile: true # → relevant memories, by semantic # similarity, literal keyword, or category
Architecture
Clean Architecture: Domain knows nothing about the rest, Application defines use cases through interfaces, Infrastructure implements them with EF Core and the embedding providers, Api exposes everything via the Model Context Protocol.
Every API Key belongs to one user — a Writer or a Reader — and carries a Read/ReadWrite permission per space plus one default "active" space. The role caps the grant, so a Reader is read-only everywhere. Isolation between spaces is guaranteed at the query level; inside a space everyone reads everything, and every write records its author.
Cosine similarity ranked in the database by pgvector (halfvec(3072) column, HNSW index), paired with literal keyword matching and category filtering/listing — all combinable.
IEmbeddingProvider abstracts OpenAI, Azure OpenAI, and Gemini behind the same EmbeddingClient, resolved lazily so it doesn't block tools that don't need it.
MCP Tools
Every tool is a thin adapter: it resolves the access context, calls the application service, and formats the output.
Searches a space's memories by semantic similarity, literal keyword, and/or category (combinable), with an optional stable/recent profile.
Saves (save, with optional category) or removes (forget) a memory based on its content.
Paginated list of extracted memories in a space.
Paginated list of a space's source documents.
Metadata and available content of a single document.
Stores content as a new document (text/Markdown/CSV/PDF in this version — PDF bytes are base64, extracted to text server-side via PdfPig) — source-of-truth storage only, no fact extraction. Backs the upload-file widget.
Spaces accessible with the current API Key, with the effective access level (grant capped by the user's role) and counts.
Authenticated user (id, email, display name, Writer/Reader role), the key in use, accessible spaces, active space.
Sets which of the current API Key's accessible spaces is active (default). Backs the select-space widget.
MCP Resources & Prompt · Phase 3
For clients that support the Resources/Prompts primitives: thin adapters over the exact same
Application services the tools call, so profile/space context stays consistent everywhere.
Recent-active-memories profile context for the active space — the same set search_memory's includeProfile attaches.
The same compact space list as listSpaces, with the active space marked.
First page of the active space's memories (any status) — same shape as listMemories.
Nodes (memories, any status) and typed edges for the active space — feeds the memory-graph widget's visualization.
A ready-to-attach text message: the active space's profile, plus up to 3 other spaces ranked by their most recent memory.
MCP Apps · Phase 3
Via the experimental ModelContextProtocol.Extensions.Apps package: each widget is an
HTML resource opened by a tool, driven from inside its iframe by ordinary tools/call and
resources/read requests over a small postMessage JSON-RPC bridge — no
widget-specific business logic beyond that bridge.
Lists accessible spaces and switches the active one via setActiveSpace.
Editable content/category/space form that calls add_memory.
Local file picker that calls create_document, then optionally add_memory with the extracted text. Text/Markdown/CSV/PDF are fully ingested;
Reads memory-mcp://graph and renders it with a small hand-rolled force-directed layout — no CDN dependency.
ui://*
resources to confirm they're registered and serving HTML.
Technology stack
| Layer | Technology |
|---|---|
| Runtime | .NET 10, pinned in global.json |
| MCP Server | ModelContextProtocol / ModelContextProtocol.AspNetCore 2.1.0 + ModelContextProtocol.Extensions.Apps |
| Web host | ASP.NET Core Minimal API |
| Persistence | PostgreSQL + EF Core 10 (Npgsql.EntityFrameworkCore.PostgreSQL) |
| Vector search | pgvector ≥ 0.7.0 (halfvec + HNSW), via Pgvector.EntityFrameworkCore |
| Embedding | OpenAI / Azure.AI.OpenAI / Gemini, same EmbeddingClient |
| PDF text extraction | PdfPig (pure managed, no native/external dependency) |
| Authentication | Custom scheme based on API Key (SHA-256 hash) |
| Tests | xUnit · NSubstitute · AwesomeAssertions · Microsoft.AspNetCore.Mvc.Testing |
| Container | Multi-stage Dockerfile + docker-compose |
Roadmap
/mcpMemoryEdge / RelationType (Updates / Extends / Derives), generalizing SupersededByIFactExtractor (OpenAI / Azure OpenAI / Gemini / self-hosted), with a safe fallback when unconfiguredmemory_edges table + recursive-CTE traversal, no graph DB extension requiredsearch_memory enriched with relatedMemories; add_memory extracts and links multiple facts per savememory-mcp://profile, memory-mcp://spaces, memory-mcp://memories, memory-mcp://graphcontext, ranking up to 3 other spaces by recent activitypgvector with a native HNSW index, replacing in-app cosine scoring — kept in Postgres rather than a separate Qdrant service, so memories and vectors share one transactional storehalfvec(3072) column, working around pgvector's 2000-dimension index limit on the vector typetopK rows instead of the whole spaceUser with a Writer/Reader role; an API Key becomes a credential of a user, who may hold severalcreatedBy/updatedBy on memories and documents, returned as ids and display names — including who forgot or superseded a colleague's memoryupload-fileGetting started