diff --git a/src/cli/misc.rs b/src/cli/misc.rs index ec5634b..f3fd97b 100644 --- a/src/cli/misc.rs +++ b/src/cli/misc.rs @@ -264,6 +264,52 @@ pub fn get_group_content(group: &crate::config::ContextGroup, store: &crate::sto } } +/// MCP tool schema with CLI routing info. +/// +/// Each tool definition includes: +/// - name, description, inputSchema (standard MCP) +/// - cli: the CLI args prefix to invoke this tool +/// - stdin_param: which parameter (if any) should be sent via stdin +/// +/// Tools with cli=null are agent-internal (not exposed via MCP CLI bridge). +pub fn cmd_mcp_schema() -> Result<(), String> { + use serde_json::json; + + // Map tool names to CLI args + stdin param. + // Tools not listed here are skipped (agent-internal). + let cli_map: std::collections::HashMap<&str, (Vec<&str>, Option<&str>)> = [ + ("memory_render", (vec!["render"], None)), + ("memory_write", (vec!["write"], Some("content"))), + ("memory_search", (vec!["search"], None)), + ("memory_search_content", (vec!["search", "--content"], None)), + ("memory_spread", (vec!["graph", "spread"], None)), + ("memory_links", (vec!["graph", "link"], None)), + ("memory_link_set", (vec!["graph", "link-set"], None)), + ("memory_link_add", (vec!["graph", "link-add"], None)), + ("memory_used", (vec!["used"], None)), + ("memory_weight_set", (vec!["weight-set"], None)), + ("memory_rename", (vec!["node", "rename"], None)), + ("memory_query", (vec!["query"], None)), + ].into_iter().collect(); + + let defs = crate::thought::memory::definitions(); + let json_out: Vec<_> = defs.iter().filter_map(|d| { + let name = &d.function.name; + let (cli, stdin_param) = cli_map.get(name.as_str())?; + Some(json!({ + "name": name, + "description": d.function.description, + "inputSchema": d.function.parameters, + "cli": cli, + "stdin_param": stdin_param, + })) + }).collect(); + + println!("{}", serde_json::to_string_pretty(&json_out) + .map_err(|e| e.to_string())?); + Ok(()) +} + pub fn cmd_load_context(stats: bool) -> Result<(), String> { let cfg = crate::config::get(); let store = crate::store::Store::load()?; diff --git a/src/main.rs b/src/main.rs index 99197c8..bf58350 100644 --- a/src/main.rs +++ b/src/main.rs @@ -220,6 +220,10 @@ EXAMPLES: /// Admin operations (fsck, health, import, export) #[command(subcommand)] Admin(AdminCmd), + + /// Output MCP tool definitions as JSON (for generic MCP bridge) + #[command(name = "mcp-schema")] + McpSchema, } #[derive(Subcommand)] @@ -310,6 +314,14 @@ enum GraphCmd { /// Node key key: Vec, }, + /// Find related nodes via spreading activation from seed nodes + Spread { + /// Seed node keys + keys: Vec, + /// Maximum results (default: 20) + #[arg(short = 'n', default_value_t = 20)] + max_results: usize, + }, /// Add a link between two nodes #[command(name = "link-add")] LinkAdd { @@ -806,6 +818,7 @@ impl Run for Command { Self::Cursor(sub) => sub.run(), Self::Agent(sub) => sub.run(), Self::Admin(sub) => sub.run(), + Self::McpSchema => cli::misc::cmd_mcp_schema(), } } } @@ -837,6 +850,7 @@ impl Run for GraphCmd { fn run(self) -> Result<(), String> { match self { Self::Link { key } => cli::graph::cmd_link(&key), + Self::Spread { keys, max_results } => cli::graph::cmd_spread(&keys, max_results), Self::LinkAdd { source, target, reason } => cli::graph::cmd_link_add(&source, &target, &reason), Self::LinkSet { source, target, strength }