Skip to main content

agentic_memory_mcp/tools/
memory_workspace_query.rs

1//! Tool: memory_workspace_query — Query across all memory contexts in a workspace.
2
3use std::sync::Arc;
4use tokio::sync::Mutex;
5
6use serde::Deserialize;
7use serde_json::{json, Value};
8
9use crate::session::SessionManager;
10use crate::types::{McpError, McpResult, ToolCallResult, ToolDefinition};
11
12#[derive(Debug, Deserialize)]
13struct QueryParams {
14    workspace_id: String,
15    query: String,
16    #[serde(default = "default_max_per_context")]
17    max_per_context: usize,
18}
19
20fn default_max_per_context() -> usize {
21    10
22}
23
24/// Return the tool definition for memory_workspace_query.
25pub fn definition() -> ToolDefinition {
26    ToolDefinition {
27        name: "memory_workspace_query".to_string(),
28        description: Some(
29            "Search across all memory contexts in a workspace. Returns matches from \
30             each loaded context, enabling cross-project knowledge discovery."
31                .to_string(),
32        ),
33        input_schema: json!({
34            "type": "object",
35            "required": ["workspace_id", "query"],
36            "properties": {
37                "workspace_id": {
38                    "type": "string",
39                    "description": "ID of the workspace to query"
40                },
41                "query": {
42                    "type": "string",
43                    "description": "Text query to search across all contexts"
44                },
45                "max_per_context": {
46                    "type": "integer",
47                    "default": 10,
48                    "description": "Maximum matches per context"
49                }
50            }
51        }),
52    }
53}
54
55/// Execute the memory_workspace_query tool.
56pub async fn execute(
57    args: Value,
58    session: &Arc<Mutex<SessionManager>>,
59) -> McpResult<ToolCallResult> {
60    let params: QueryParams =
61        serde_json::from_value(args).map_err(|e| McpError::InvalidParams(e.to_string()))?;
62
63    let session = session.lock().await;
64    let results = session.workspace_manager().query_all(
65        &params.workspace_id,
66        &params.query,
67        params.max_per_context,
68    )?;
69
70    let context_results: Vec<Value> = results
71        .iter()
72        .map(|cr| {
73            let matches: Vec<Value> = cr
74                .matches
75                .iter()
76                .map(|m| {
77                    json!({
78                        "node_id": m.node_id,
79                        "content": m.content,
80                        "event_type": m.event_type,
81                        "confidence": m.confidence,
82                        "score": m.score,
83                    })
84                })
85                .collect();
86
87            json!({
88                "context_id": cr.context_id,
89                "context_role": cr.context_role.label(),
90                "match_count": cr.matches.len(),
91                "matches": matches,
92            })
93        })
94        .collect();
95
96    let total_matches: usize = results.iter().map(|cr| cr.matches.len()).sum();
97
98    Ok(ToolCallResult::json(&json!({
99        "workspace_id": params.workspace_id,
100        "query": params.query,
101        "total_matches": total_matches,
102        "contexts_searched": context_results.len(),
103        "results": context_results
104    })))
105}