From 55551fe9af1e5b27ae09bf3e947e58da5f72567c Mon Sep 17 00:00:00 2001 From: Robert Helewka Date: Thu, 21 May 2026 05:55:45 -0400 Subject: [PATCH] Docs: Mnemosyne MCP --- docs/mnemosyne_mcp.md | 240 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 docs/mnemosyne_mcp.md diff --git a/docs/mnemosyne_mcp.md b/docs/mnemosyne_mcp.md new file mode 100644 index 0000000..888a85d --- /dev/null +++ b/docs/mnemosyne_mcp.md @@ -0,0 +1,240 @@ +# Mnemosyne MCP Server Tools + +Mnemosyne exposes a retrieval surface via the [Model Context Protocol](https://modelcontextprotocol.io/) using [FastMCP](https://github.com/jlowin/fastmcp). The server is a **retrieval surface, not a RAG pipeline**: it returns ranked evidence and the calling LLM is responsible for synthesis and citation. + +## Concepts + +**Library** — the top-level container. Each library has a `library_type` that drives chunking, embedding, and re-ranking strategy: + +| `library_type` | Content | +|---|---| +| `fiction` | Novels, short stories. Cover art available. | +| `nonfiction` | General non-fiction prose. | +| `technical` | Manuals, textbooks, docs. Diagrams and code-like content. | +| `music` | Lyrics, liner notes, album artwork. | +| `film` | Scripts, synopses, stills. | +| `art` | Catalogs, descriptions, artwork itself. | +| `journal` | Personal entries; temporal/reflective. | +| `business` | Proposals, marketing, sales, strategy. Commercial context. | +| `finance` | Statements, tax, market commentary. Quote figures exactly. | + +**Collection** — a named group of items inside a library (e.g. a novel series, a multi-volume manual). + +**Item** — an indexed document or file. Only items with `embedding_status = "completed"` appear in search results. + +**Chunk** — a text segment of an item, stored in S3. Search returns a `text_preview` (~500 chars); use `get_chunk` to fetch the full text. + +## Recommended Workflow + +``` +list_libraries + → search(query, library_type=..., library_uid=...) + → get_chunk(chunk_uid) # only when text_preview is insufficient +``` + +--- + +## Tools + +### `search` + +Hybrid retrieval: vector + full-text + concept-graph candidates fused by RRF (Reciprocal Rank Fusion), with optional Synesis re-ranking. + +**Parameters** + +| Name | Type | Default | Description | +|---|---|---|---| +| `query` | `str` | required | The search query. | +| `library_uid` | `str \| None` | `None` | Restrict to one library by UID. | +| `library_type` | `str \| None` | `None` | Restrict by library type (see table above). | +| `collection_uid` | `str \| None` | `None` | Restrict to one collection by UID. | +| `limit` | `int` | `20` | Maximum candidates to return. | +| `rerank` | `bool` | `True` | Apply Synesis re-ranking. Set `False` to skip. | +| `include_images` | `bool` | `True` | Include matching images in the response. | +| `search_types` | `list[str] \| None` | `["vector", "fulltext", "graph"]` | Which retrieval strategies to run. | + +**Response** + +```json +{ + "query": "...", + "candidates": [ + { + "chunk_uid": "...", + "item_uid": "...", + "item_title": "...", + "library_type": "...", + "text_preview": "... (~500 chars) ...", + "score": 0.92, + "source": "vector|fulltext|graph" + } + ], + "images": [...], + "total_candidates": 42, + "search_time_ms": 85, + "reranker_used": true, + "reranker_model": "...", + "search_types_used": ["vector", "fulltext", "graph"] +} +``` + +--- + +### `get_chunk` + +Fetch the full text of a single chunk by its UID. Use this when the `text_preview` returned by `search` is not enough. + +**Parameters** + +| Name | Type | Description | +|---|---|---| +| `chunk_uid` | `str` | The chunk UID from a `search` result. | + +**Response** + +```json +{ + "chunk_uid": "...", + "chunk_index": 3, + "item_uid": "...", + "item_title": "...", + "library_type": "...", + "text": "Full chunk text..." +} +``` + +--- + +### `list_libraries` + +Enumerate libraries the caller is authorized to read. Use the returned `uid` or `library_type` to scope a subsequent `search`. + +**Parameters** + +| Name | Type | Default | Description | +|---|---|---|---| +| `limit` | `int` | `50` | Max libraries to return (capped at 200). | +| `offset` | `int` | `0` | Pagination offset. | + +**Response** + +```json +{ + "libraries": [ + { + "uid": "...", + "name": "...", + "library_type": "fiction", + "description": "..." + } + ], + "limit": 50, + "offset": 0 +} +``` + +--- + +### `list_collections` + +Enumerate collections, optionally filtered to a single library. Use the returned `uid` to scope `search` or `list_items` to one collection. + +**Parameters** + +| Name | Type | Default | Description | +|---|---|---|---| +| `library_uid` | `str \| None` | `None` | Filter to one parent library. | +| `limit` | `int` | `50` | Max collections to return (capped at 200). | +| `offset` | `int` | `0` | Pagination offset. | + +**Response** + +```json +{ + "collections": [ + { + "uid": "...", + "name": "...", + "description": "...", + "library_uid": "...", + "library_name": "..." + } + ], + "limit": 50, + "offset": 0 +} +``` + +--- + +### `list_items` + +Enumerate indexed documents/files, optionally filtered by library or collection. Check `embedding_status` before searching — only `"completed"` items appear in search results. Use `chunk_count` to gauge document size. + +**Parameters** + +| Name | Type | Default | Description | +|---|---|---|---| +| `collection_uid` | `str \| None` | `None` | Filter to one collection. | +| `library_uid` | `str \| None` | `None` | Filter to one library. | +| `limit` | `int` | `50` | Max items to return (capped at 200). | +| `offset` | `int` | `0` | Pagination offset. | + +**Response** + +```json +{ + "items": [ + { + "uid": "...", + "title": "...", + "item_type": "...", + "file_type": "...", + "chunk_count": 120, + "image_count": 4, + "embedding_status": "completed" + } + ], + "limit": 50, + "offset": 0 +} +``` + +--- + +### `get_health` + +Health check for infrastructure pollers (Pallas, Daedalus). Does not require authentication. + +Returns a Pallas-compatible status object. `neo4j` and `s3` failures result in `"error"` (critical). A missing or unconfigured embedding model results in `"degraded"` (non-critical). + +**Parameters:** none + +**Response** + +```json +{ + "status": "ok | degraded | error", + "checks": { + "neo4j": { "status": "ok", "duration_ms": 2.1 }, + "s3": { "status": "ok", "duration_ms": 8.4 }, + "embedding": { "status": "ok", "model": "...", "duration_ms": 0.3 } + } +} +``` + +--- + +## Authentication + +All tools except `get_health` require a `Bearer` token in the `Authorization` header. Three credential types are accepted: + +| Type | Issued by | Lifetime | Scope | +|---|---|---|---| +| **Opaque `MCPToken`** | Mnemosyne admin | Long-lived (optional expiry) | `allowed_libraries` list on the token row. Per-tool ACL available. | +| **Per-turn JWT** (`iss=daedalus`) | Daedalus chat | ≤10 minutes | `libs` claim (list of Library UIDs). | +| **Team JWT** (`iss=mnemosyne`, `typ=team`) | Mnemosyne | 10-year lifetime | Resolved live from `TeamWorkspaceAssignment` → Neo4j `Library.workspace_id`. Revoked via `active_jti` rotation. | + +Every authenticated request resolves to a `resolved_libraries` list — the set of Library UIDs the caller may read. Tools enforce this list at the query layer; an empty list means the caller is authenticated but sees nothing (fail-closed). `None` (no auth) is also fail-closed. + +The `MCP_REQUIRE_AUTH` Django setting (default `True`) controls whether unauthenticated requests are rejected.