use serde_json::Value;
use trusty_mcp_core::openrpc::discover_response;
use crate::tools::tool_definitions_with;
mod scopes {
pub const MEMORY_READ: &str = "memory.read";
pub const MEMORY_WRITE: &str = "memory.write";
}
pub fn scopes_for_tool(name: &str) -> Vec<String> {
use scopes::*;
let s: &[&str] = match name {
"memory_recall" | "memory_recall_deep" | "memory_recall_all" | "memory_list"
| "palace_list" | "palace_info" | "kg_query" => &[MEMORY_READ],
"memory_remember" | "memory_forget" | "palace_create" | "palace_compact" | "kg_assert" => {
&[MEMORY_WRITE]
}
_ => &[],
};
s.iter().map(|x| (*x).to_string()).collect()
}
pub fn build_discover_response(version: &str, has_default: bool) -> Value {
let defs = tool_definitions_with(has_default);
let empty: Vec<Value> = Vec::new();
let tools: &[Value] = defs["tools"]
.as_array()
.map(|a| a.as_slice())
.unwrap_or(&empty);
discover_response("trusty-memory-mcp", version, tools, scopes_for_tool)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_tool_has_scopes() {
let defs = tool_definitions_with(false);
let tools = defs["tools"].as_array().expect("tools array");
for tool in tools {
let name = tool["name"].as_str().unwrap();
let scopes = scopes_for_tool(name);
assert!(
!scopes.is_empty(),
"tool {name} must declare at least one scope"
);
}
}
#[test]
fn read_write_classification() {
assert_eq!(scopes_for_tool("memory_recall"), vec!["memory.read"]);
assert_eq!(scopes_for_tool("memory_remember"), vec!["memory.write"]);
assert_eq!(scopes_for_tool("kg_query"), vec!["memory.read"]);
assert_eq!(scopes_for_tool("kg_assert"), vec!["memory.write"]);
assert_eq!(scopes_for_tool("palace_compact"), vec!["memory.write"]);
assert_eq!(scopes_for_tool("palace_info"), vec!["memory.read"]);
}
#[test]
fn discover_response_lists_all_tools() {
let doc = build_discover_response("9.9.9", false);
assert_eq!(doc["openrpc"], "1.3.2");
assert_eq!(doc["info"]["title"], "trusty-memory-mcp");
assert_eq!(doc["info"]["version"], "9.9.9");
let methods = doc["methods"].as_array().expect("methods array");
let defs = tool_definitions_with(false);
let tool_count = defs["tools"].as_array().unwrap().len();
assert_eq!(methods.len(), tool_count);
}
#[test]
fn discover_response_x_scopes_present() {
let doc = build_discover_response("0.1.0", false);
let methods = doc["methods"].as_array().unwrap();
for m in methods {
let scopes = m["x-scopes"].as_array().expect("x-scopes array");
assert!(
!scopes.is_empty(),
"method {} must carry at least one scope",
m["name"]
);
}
}
}