agentic_memory_mcp/tools/
memory_workspace_query.rs1use 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
24pub 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
55pub 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 ¶ms.workspace_id,
66 ¶ms.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}