Docs: update

This commit is contained in:
2026-05-04 15:34:51 -04:00
parent be71709608
commit 705b4f8cbe

View File

@@ -1,102 +1,142 @@
# Mnemosyne Integration — Pallas Reference
This document summarises the Pallas-specific changes required for Mnemosyne knowledge integration. The full specification lives in [`daedalus/docs/mnemosyne_integration.md`](../../daedalus/docs/mnemosyne_integration.md).
This document describes how Pallas-hosted agents connect to Mnemosyne for workspace-scoped knowledge search. The full integration specification lives in [`daedalus/docs/mnemosyne_integration.md`](../../daedalus/docs/mnemosyne_integration.md).
---
## Overview
Pallas agents gain access to Mnemosyne's content-type-aware knowledge graph as a downstream MCP server. Agents can search documents, browse libraries, retrieve items, and traverse the concept graph — all via standard MCP tool calls.
Mnemosyne is a downstream MCP server like any other from Pallas's perspective. Agents declare `"mnemosyne"` in their `servers` list; the server URL and bearer-forward opt-in live in the project's `fastagent.config.yaml`.
What makes Mnemosyne different from other downstream servers:
- **Workspace-scoped search.** Daedalus mints a per-turn HS256 JWT carrying the workspace UUID and sends it as `Authorization: Bearer` on the `send_message` call to Pallas. Pallas captures it in `request_bearer_token`, and the fast-agent patch (`pallas._fastagent_patch`) forwards it on outgoing calls to Mnemosyne when `forward_inbound_auth: true` is set. Mnemosyne validates the JWT and scopes all Cypher searches to that workspace.
- **The LLM never sees `workspace_id`.** The scoping is claim-driven: Mnemosyne reads the JWT claims, overwrites any `workspace_id` the model may have produced in tool arguments, and enforces containment server-side. Pallas is transparent transport.
---
## Configuration Changes
## Configuration
### fastagent.config.yaml
Add the Mnemosyne MCP server:
Add the `mnemosyne` stanza to `mcp.servers`. The only Mnemosyne-specific flag is `forward_inbound_auth: true`:
```yaml
mcp:
servers:
# ... existing servers (argos, neo4j_cypher, kernos, rommie, gitea, grafana) ...
mnemosyne:
transport: http
url: "http://puck.incus:22091/mcp"
url: "https://mnemosyne.ouranos.helu.ca/mcp/"
forward_inbound_auth: true
```
This is already deployed in `iolaus/fastagent.config.yaml`, `kottos/fastagent.config.yaml`, and `mentor/fastagent.config.yaml` and their Ansible templates in `virgo/ansible/`.
### Agent Definitions
#### Research Agent (port 23031)
Add `"mnemosyne"` to the `servers` list of any agent that should be able to search workspace content. Sub-agents (e.g. `research`, `tech_research`) that are orchestrated by primary agents do not need it unless they independently issue search calls.
The `knowledge` agent in the research chain gains Mnemosyne access:
**iolaus** — all primary agents have Mnemosyne access: `shawn`, `david`, `hypatia`, `watson`, `nate`, `garth`, `bourdain`, `cousteau`, `marcus`, `cristiano`, `mikael`.
**kottos**`harper`, `scotty`.
**mentor**`alan`, `ann`, `jeffrey`, `jarvis`, `aws_sa`.
Example (from `iolaus/agents/shawn.py`):
```python
@fast.agent(name="search", servers=["argos"])
@fast.agent(name="knowledge", servers=["neo4j_cypher", "mnemosyne"])
@fast.chain(name="research", sequence=["search", "knowledge"], default=True)
@fast.agent(
name="shawn",
instruction=_INSTRUCTION,
servers=["argos", "mnemosyne", "neo4j_cypher", "kernos", "time"],
default=True,
)
async def _shawn():
pass
```
The `knowledge` agent's system instruction should guide tool selection:
---
> Use `mnemosyne.search_knowledge` for document content retrieval — it handles chunking, vector search, re-ranking, and content-type-aware context. Use `neo4j_cypher` for graph topology queries, relationship exploration, and data not managed by Mnemosyne.
#### Infrastructure Agent (port 23032)
No changes — Infrastructure does not use Mnemosyne.
#### Orchestrator (port 23033)
```python
@fast.agent(name="research_sub", servers=["argos", "neo4j_cypher", "mnemosyne"])
@fast.agent(name="infra_sub", servers=["kernos", "gitea", "rommie"])
@fast.orchestrator(name="orchestrator", agents=["research_sub", "infra_sub"],
plan_type="iterative", default=True)
```
### Registry Update
Update agent descriptions to reflect Mnemosyne access:
## How Bearer Forwarding Works
1. Daedalus mints a per-turn JWT:
```json
{
"server": {
"name": "ca.helu.ouranos/pallas-research",
"title": "Research Agent",
"description": "Web search via Argos, knowledge graph via Neo4j, and content library search via Mnemosyne",
"version": "1.1.0",
"remotes": [
{ "type": "streamable-http", "url": "http://puck.incus:23031/mcp" }
]
}
"iss": "daedalus",
"sub": "chat",
"ws": "<workspace_uuid>",
"libs": [],
"iat": <now>,
"exp": <now + 600>,
"jti": "<uuid4>"
}
```
2. Daedalus calls Pallas's `send_message` tool with `Authorization: Bearer <token>` in the HTTP request headers.
3. Pallas's `MultimodalAgentMCPServer` captures the token via FastMCP's `get_access_token()` into the `request_bearer_token` context variable (see `pallas/multimodal_server.py`).
4. The fast-agent patch in `pallas/_fastagent_patch.py` (installed at import time in `pallas/__init__.py`) wraps `_prepare_headers_and_auth`. When a server config has `forward_inbound_auth: true`, the patch reads `request_bearer_token.get()` and injects `Authorization: Bearer <token>` into the outgoing HTTP headers for that MCP call.
5. Mnemosyne receives the same token, validates the HMAC signature against its `MCPSigningKey` table, and scopes all search Cypher queries to `ws` from the claims.
The `forward_inbound_auth` flag is **per-server** — other servers in the same agent (`argos`, `neo4j_cypher`, `time`, etc.) never receive the bearer.
---
## Available Mnemosyne MCP Tools
These tools become available to agents with `mnemosyne` in their `servers` list:
These tools become available to agents with `"mnemosyne"` in their `servers` list:
| Tool | Purpose | When to Use |
|------|---------|-------------|
| `search_knowledge` | Hybrid vector + full-text + graph search with re-ranking | Document content retrieval, question answering over stored knowledge |
| `search_by_category` | Search scoped to a library type (fiction, technical, etc.) | When the user specifies or implies a content domain |
| `list_libraries` | List all knowledge libraries | Discovering what knowledge domains exist |
| `list_collections` | List collections within a library | Browsing a specific knowledge domain |
| `get_item` | Retrieve item metadata + chunk previews + concept links | Deep dive on a specific document/item |
| `get_concepts` | Traverse concept graph | Exploring relationships between topics, people, places |
| Tool | Purpose |
|------|---------|
| `search_knowledge` | Hybrid vector + full-text + graph search with re-ranking, scoped to the current workspace |
| `search_by_category` | Search within a specific library type (technical, fiction, business, etc.) |
| `list_libraries` | List accessible libraries |
| `list_collections` | List collections within a library |
| `get_item` | Retrieve item metadata, chunk previews, and concept links |
| `get_concepts` | Traverse the concept graph |
All tools are transparently scoped to the workspace by JWT claims. An agent in workspace A cannot retrieve content from workspace B regardless of what arguments it produces.
---
## Downstream MCP Servers (Updated)
## Downstream MCP Servers
| Server | Host | URL | Used by |
|--------|------|-----|---------|
| argos | miranda.incus | `http://miranda.incus:25534/mcp` | Research, Orchestrator |
| neo4j_cypher | circe.helu.ca | `http://circe.helu.ca:22034/mcp` | Research, Orchestrator |
| **mnemosyne** | **puck.incus** | **`http://puck.incus:22091/mcp`** | **Research, Orchestrator** |
| kernos | caliban.incus | `http://caliban.incus:22021/mcp` | Infrastructure, Orchestrator |
| gitea | miranda.incus | `http://miranda.incus:25535/mcp` | Infrastructure, Orchestrator |
| rommie | caliban.incus | `http://caliban.incus:22031/mcp` | Infrastructure, Orchestrator |
| grafana | miranda.incus | `http://miranda.incus:25533/mcp` | Infrastructure |
| Server | URL | `forward_inbound_auth` |
|--------|-----|----------------------|
| mnemosyne | `https://mnemosyne.ouranos.helu.ca/mcp/` | `true` |
| argos | `http://miranda.incus:25534/mcp` | |
| neo4j_cypher | `http://circe.helu.ca:22034/mcp` | — |
| kernos | `http://caliban.incus:22021/mcp` | |
| gitea | `http://miranda.incus:25535/mcp` | |
| rommie | `http://caliban.incus:22031/mcp` | |
| grafana | `http://miranda.incus:25533/mcp` | — |
---
## Provisioning (one-time, server-side)
1. On the Mnemosyne host, generate the signing key:
```bash
docker compose exec app python manage.py seed_signing_key --kid daedalus-1
# Copy the printed hex secret
```
2. Set on Daedalus (`.env` or Ansible vault):
```
DAEDALUS_MNEMOSYNE_MCP_URL=https://mnemosyne.ouranos.helu.ca/mcp/
DAEDALUS_MNEMOSYNE_SIGNING_KID=daedalus-1
DAEDALUS_MNEMOSYNE_SIGNING_SECRET=<hex from step 1>
DAEDALUS_MNEMOSYNE_TOKEN_TTL_SECONDS=600
```
3. Restart Daedalus and the three agent deployments (iolaus, kottos, mentor).
The OCI vault secret is `virgo-mnemosyne-signing-secret`; Ansible injects it via `mnemosyne_signing_secret`.
---
## Degraded Mode
If Daedalus's `MNEMOSYNE_SIGNING_SECRET` is blank or `MNEMOSYNE_MCP_URL` is empty, `mint_chat_token` returns `None`. Pallas calls proceed without a bearer; Mnemosyne search is unavailable but all other agent tools continue normally. No error is surfaced to the user.