# Architecture Design: MCP Resources & Prompts ## 1. Current Architecture (Tools) Currently, the `mcp-memory` server handles MCP tools using an elegant trait-based approach in `router.rs`: ```rust #[async_trait] pub trait McpTool: Send + Sync { fn name(&self) -> &'static str; fn schema(&self) -> Value; async fn execute(&self, args: Value, state: Arc) -> Result; } ``` Tools are registered into a `HashMap>` within the `MemoryHandler`. This prevents the main JSON-RPC match block from becoming a monolithic switch statement. ## 2. The Problem Currently, the `resources/list`, `resources/read`, `prompts/list`, and `prompts/get` endpoints are hardcoded directly inside the `MemoryHandler::handle_request` match block in `router.rs`. As we expand our usage of Resources (to expose the database state dynamically) and Prompts (to bundle complex workflows), continuing to hardcode them in `router.rs` will result in massive code duplication and tearup. ## 3. The Proposed Solution (Trait Extensibility) We will replicate the success of the `McpTool` trait by introducing `McpResource` and `McpPrompt` traits. ### A. MCP Resources **Trait Definition (`router.rs` or `resources.rs`):** ```rust #[async_trait] pub trait McpResource: Send + Sync { /// The exact URI the client requests (e.g. "memory://tasks/active") fn uri(&self) -> &'static str; /// Human-readable name for the client UI fn name(&self) -> &'static str; /// Description for the client UI fn description(&self) -> Option<&'static str> { None } /// Mime type of the content (usually "application/json" or "text/markdown") fn mime_type(&self) -> Option<&'static str> { Some("application/json") } /// Retrieve the resource content async fn read(&self, state: Arc) -> Result; } ``` **Implementation:** * Add `pub resources: std::collections::HashMap>` to `MemoryHandler`. * In `handle_request("resources/list")`, iterate over `self.resources.values()` and build the JSON payload. * In `handle_request("resources/read")`, lookup the requested URI in `self.resources` and call `.read(state).await`. * Move the existing `memory://graph/entities` logic into its own handler struct. ### B. MCP Prompts **Trait Definition (`router.rs` or `prompts.rs`):** ```rust #[async_trait] pub trait McpPrompt: Send + Sync { /// The unique name of the prompt (e.g. "analyze_tech_debt") fn name(&self) -> &'static str; /// Description for the client UI fn description(&self) -> Option<&'static str> { None } /// Schema or array defining arguments (can default to empty) fn arguments(&self) -> serde_json::Value { serde_json::json!([]) } /// Execute the prompt and return the `messages` array payload async fn get(&self, args: Value, state: Arc) -> Result; } ``` **Implementation:** * Add `pub prompts: std::collections::HashMap>` to `MemoryHandler`. * In `handle_request("prompts/list")`, map over `self.prompts.values()`. * In `handle_request("prompts/get")`, call `.get(args, state).await`. ## 4. Execution Plan 1. **Refactor `router.rs` (No functional changes yet):** Define the `McpResource` and `McpPrompt` traits. Update the `MemoryHandler` struct to hold these HashMaps. Migrate the existing hardcoded stubs (`memory://graph/entities` and `analyze_tech_debt`) into structs implementing these traits. 2. **Expand Resources (Phase 1):** Add new handlers for `memory://tasks/active`, `memory://session/delta`, etc. 3. **Expand Prompts (Phase 2):** Add new handlers for `handoff_routine`, etc. This design guarantees we do not needlessly tear up code—we merely extend the existing robust `McpTool` pattern to the rest of the protocol.