78 lines
3.8 KiB
Markdown
78 lines
3.8 KiB
Markdown
# 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<MemoryState>) -> Result<String, String>;
|
|
}
|
|
```
|
|
Tools are registered into a `HashMap<String, Box<dyn McpTool>>` 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<MemoryState>) -> Result<String, String>;
|
|
}
|
|
```
|
|
**Implementation:**
|
|
* Add `pub resources: std::collections::HashMap<String, Box<dyn McpResource>>` 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<MemoryState>) -> Result<serde_json::Value, String>;
|
|
}
|
|
```
|
|
**Implementation:**
|
|
* Add `pub prompts: std::collections::HashMap<String, Box<dyn McpPrompt>>` 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.
|