MCP Server Ecosystem — Odysseus Dashboard
The four built-in Model Context Protocol servers in the Odysseus dashboard: email, memory, RAG, and image generation, with their tool schemas, transport model, and integration patterns.
MCP Server Ecosystem
The Odysseus dashboard ships with four built-in MCP (Model Context Protocol) servers that expose domain-specific tools to the LLM agent loop. Each server runs as a separate stdio process, communicating with the host via the MCP protocol. Admin-configurable MCP servers can also be registered through the database and UI, extending the tool palette dynamically. This page documents the built-in servers, their tool schemas, and the integration pattern that connects them to the co-fita-odysseus harness.
What Is MCP?
The Model Context Protocol is a standardized interface for exposing tools, resources, and prompts to LLM agents. Each MCP server:
- Declares a set of tools with JSON Schema input specifications
- Receives
call_toolrequests from the agent loop - Returns structured
TextContentresponses - Runs as an isolated process (stdio transport) or HTTP endpoint (SSE transport)
The dashboard uses the mcp Python library for server implementation and manages server lifecycle through src/mcp_manager.py.
Architecture Pattern
graph LR
subgraph AgentLoop["Agent Loop"]
AL["agent_loop.py"]
TI["tool_implementations.py"]
end
subgraph Host["Host Process (FastAPI)"]
MCPMgr["mcp_manager.py<br>(server lifecycle)"]
MCPRoutes["routes/mcp_routes.py<br>(admin CRUD)"]
DB[("mcp_servers table<br>(SQLAlchemy)")]
end
subgraph BuiltIn["Built-in MCP Servers (stdio)"]
EMail["email_server.py"]
Mem["memory_server.py"]
RAG["rag_server.py"]
IMG["image_gen_server.py"]
end
subgraph External["External MCP Servers (admin-configured)"]
Ext1["stdio: custom_command"]
Ext2["sse: http://remote:port"]
end
AL --> TI
TI --> MCPMgr
MCPMgr --> EMail
MCPMgr --> Mem
MCPMgr --> RAG
MCPMgr --> IMG
MCPMgr --> Ext1
MCPMgr --> Ext2
MCPRoutes --> DB
MCPMgr --> DB
Integration Flow
- Startup:
mcp_manager.pyreads enabled servers from themcp_serversdatabase table - Process launch: For stdio servers, a child process is spawned with the configured command and args. For SSE servers, an HTTP connection is established.
- Tool discovery: The agent loop calls
list_tools()on each server to learn available tools - Tool dispatch: When the LLM requests a tool call,
tool_implementations.pyroutes it to the appropriate MCP server - Response: The server returns
TextContentwhich is injected into the conversation context - Shutdown: Server processes are cleaned up on application exit
Admins can configure additional MCP servers through /api/mcp routes, specifying transport type, command, arguments, environment variables, and optional OAuth credentials.
Server 1: Email (email_server.py)
The email MCP server provides tools for reading and managing email through the dashboard's IMAP integration. It connects to local Dovecot IMAP or remote IMAP servers, reads from the AI summary cache for fast access, and supports multi-account email management.
Server Identity
| Property | Value |
|---|---|
| Name | email |
| File | mcp_servers/email_server.py |
| Lines | ~2,197 |
| Transport | stdio |
| Dependencies | imaplib, smtplib, sqlite3, email (stdlib) |
Configuration
The server reads account credentials from the email_accounts database table (Fernet-encrypted passwords). Multi-account support allows multiple IMAP/SMTP configurations per user, with exactly one default per owner.
Environment variables:
| Variable | Default | Purpose |
|---|---|---|
EMAIL_SOCKET_TIMEOUT | 20 | IMAP connection timeout (seconds) |
ODYSSEUS_MCP_OWNER | -- | Owner scope for account filtering |
Tool Schema
| Tool | Description | Key Parameters |
|---|---|---|
manage_email | Full email lifecycle management | action (list/read/draft/send/trash/archive/search/sync), account, uid, folder, query, to, subject, body |
The server supports:
- Listing unread/unresponded emails across folders
- Reading full email content with header parsing
- Drafting replies as email documents in the dashboard
- Sending composed emails via SMTP
- Searching across email content
- Syncing IMAP folders to the local cache
- Multi-account routing via the
accountparameter
Owner Scoping
Email data is strictly owner-scoped. The _account_visible_to_owner function ensures each user only sees their own configured accounts. The _odysseus_owner argument is injected by the host process to enforce this boundary.
Server 2: Memory (memory_server.py)
The memory MCP server exposes the dashboard's persistent memory system to the agent loop. It provides CRUD operations on fact storage and integrates with the vector store for semantic search.
Server Identity
| Property | Value |
|---|---|
| Name | memory |
| File | mcp_servers/memory_server.py |
| Transport | stdio |
| Dependencies | services/memory/ (MemoryManager, MemoryVectorStore) |
Architecture
The memory system has two layers:
- MemoryManager: Flat JSON-based fact storage in
data/memory.json. Each entry has text, category, source, timestamp, and owner. - MemoryVectorStore: ChromaDB-backed semantic search using fastembed embeddings. Enables natural-language retrieval of stored facts.
Lazy initialization ensures the memory managers are only created on first use, avoiding startup overhead when the memory MCP server is configured but not actively used.
Owner Scoping
Memory entries are strictly owner-scoped when any entry has an owner field. The _scope_entries function enforces this:
- Load all entries from the JSON store
- If any entry has an owner, only return entries matching the configured
ODYSSEUS_MCP_MEMORY_OWNER - If no entry has an owner (legacy single-user), return all unowned entries
- If the store is owner-scoped but no owner is configured, return an error
Tool Schema
| Tool | Description | Key Parameters |
|---|---|---|
manage_memory | Memory CRUD + search | action (list/add/edit/delete/search), text, category, query, id |
Categories include: fact, preference, instruction, context, skill.
Server 3: RAG (rag_server.py)
The RAG (Retrieval-Augmented Generation) MCP server manages indexed document directories. It provides tools for listing, adding, and removing document directories from the vector index, enabling the agent to search over personal documents.
Server Identity
| Property | Value |
|---|---|
| Name | rag |
| File | mcp_servers/rag_server.py |
| Transport | stdio |
| Dependencies | src/rag_singleton.py (RAGManager), src/personal_docs.py (PersonalDocsManager) |
Integration
The RAG server bridges two internal managers:
- RAGManager (
src/rag_singleton.py): Singleton that owns the ChromaDB collection and embedding pipeline. Handles document chunking, embedding, and semantic search. - PersonalDocsManager (
src/personal_docs.py): Manages thedata/personal/directory as a document workspace. Tracks which directories are indexed and provides file listing.
Tool Schema
| Tool | Description | Key Parameters |
|---|---|---|
manage_rag | RAG document management | action (list/add_directory/remove_directory), directory |
Actions:
list: Returns all currently indexed filesadd_directory: Indexes a new directory recursivelyremove_directory: Removes a directory from the index
Server 4: Image Generation (image_gen_server.py)
The image generation MCP server provides text-to-image generation through OpenAI-compatible APIs. It auto-detects available image models and manages the generation workflow.
Server Identity
| Property | Value |
|---|---|
| Name | image_gen |
| File | mcp_servers/image_gen_server.py |
| Transport | stdio |
| Dependencies | httpx, src/settings.py, src/ai_interaction.py |
Model Auto-Detection
The server probes for image-capable models in priority order:
gpt-image-1.5(preferred)gpt-image-1dall-e-3
If no model is explicitly configured, it uses _resolve_model from ai_interaction.py to find the first available image model from registered endpoints.
Tool Schema
| Tool | Description | Key Parameters |
|---|---|---|
generate_image | Generate an image from text | prompt (required), model, size, quality |
Parameters:
prompt: Text description of the image to generatemodel: Specific model name (auto-detected if omitted)size: Output dimensions (default:1024x1024)quality:low,medium,high, orauto(default:medium)
Generated images are saved to the GENERATED_IMAGES_DIR and tracked in the gallery_images database table.
Admin-Configurable MCP Servers
Beyond the four built-in servers, admins can register external MCP servers through the UI or API. These are stored in the mcp_servers database table:
| Column | Type | Purpose |
|---|---|---|
name | String | Display name |
transport | String | "stdio" or "sse" |
command | String | Executable path (stdio) |
args | JSON text | Command arguments array |
env | JSON text | Environment variables object |
url | String | Server URL (SSE) |
is_enabled | Boolean | Active/inactive toggle |
oauth_config | JSON text | Provider, keys file, token file, scopes |
disabled_tools | JSON text | Tool names hidden from LLM |
oauth_tokens | EncryptedText | OAuth tokens (Fernet-encrypted at rest) |
Transport Modes
stdio (default): The dashboard spawns a child process and communicates via stdin/stdout. Suitable for local tools, scripts, and CLI wrappers.
SSE (Server-Sent Events): The dashboard connects to a remote HTTP endpoint. Suitable for shared services, cloud-hosted tools, and multi-user deployments.
Per-Tool Visibility
Admins can hide specific tools from the LLM by adding them to the disabled_tools JSON array. This prevents the agent from invoking tools that are inappropriate for its current context while keeping the server running for other consumers.
OAuth Support
MCP servers that require OAuth authentication (e.g. Google, GitHub) can be configured with:
oauth_config: JSON object specifying the provider, client ID/secret file paths, token file path, and required scopesoauth_tokens: Encrypted storage of access and refresh tokens, managed by the dashboard's OAuth flow
Route API
The routes/mcp_routes.py module exposes administrative endpoints for managing MCP servers:
| Endpoint | Method | Purpose |
|---|---|---|
/api/mcp/servers | GET | List all configured MCP servers |
/api/mcp/servers | POST | Create a new MCP server configuration |
/api/mcp/servers/{id} | GET | Get server details + discovered tools |
/api/mcp/servers/{id} | PUT | Update server configuration |
/api/mcp/servers/{id} | DELETE | Remove server configuration |
/api/mcp/servers/{id}/tools | GET | List tools discovered from this server |
/api/mcp/servers/{id}/toggle | POST | Enable/disable server |
Relationship to the Tool System
The MCP servers feed into the broader tool architecture:
| Component | File | Role |
|---|---|---|
| Tool schemas | src/tool_schemas.py | JSON Schema definitions for all tools |
| Tool index | src/tool_index.py | RAG-based tool retrieval from ChromaDB |
| Tool implementations | src/tool_implementations.py | 33 do_* functions including MCP dispatch |
| Tool security | src/tool_security.py | Owner-scoped tool blocking |
| Tool policy | src/tool_policy.py | Guide-only directives, plan-mode restrictions |
MCP tools are discovered at startup and merged into the global tool registry. The agent loop's prompt assembly includes MCP tool schemas alongside built-in tools, so the LLM sees a unified tool palette.
Cross-References
- co-fita-odysseus -- The parent dashboard architecture
- co-fita-kanban -- The governance kanban system
- mcp-ecosystem -- The broader MCP ecosystem across projects
- co-fita-harness -- The parent Co-Fita harness
Design Decisions
stdio over SSE for built-in servers: All four built-in servers use stdio transport. This keeps them process-isolated (a crash in the email server does not bring down the dashboard), simplifies deployment (no port management), and aligns with the single-host design philosophy. SSE is reserved for external/admin-configured servers that may run on separate machines.
Lazy initialization: Memory, RAG, and image servers defer their heavy dependencies (ChromaDB, fastembed, model resolution) until the first tool call. This avoids startup latency when these servers are configured but not immediately used.
Owner scoping everywhere: Every MCP server that handles user data (email, memory) enforces owner-based access control. The host process injects _odysseus_owner to prevent cross-user data leakage, even if the LLM attempts to specify a different owner in tool arguments.
Encrypted at rest: OAuth tokens, email passwords, and API keys are stored Fernet-encrypted in SQLite. The encryption key lives at data/.app_key (mode 0o600, gitignored). This protects against stolen database backups while accepting that a live process with key access can read everything.