use rmcp::model::Tool;
use serde_json::json;
use super::tool_def;
pub fn granular_tool_defs() -> Vec<Tool> {
crate::server::registry::build_registry().tool_defs()
}
pub fn unified_tool_defs() -> Vec<Tool> {
vec![
tool_def(
"ctx_read",
"Read file (replaces native Read). Cached, re-reads ~13 tok. Modes: full|map|signatures|diff|aggressive|entropy|task|reference|lines:N-M. fresh=true re-reads.",
json!({
"type": "object",
"properties": {
"path": { "type": "string", "description": "File path" },
"mode": { "type": "string" },
"start_line": { "type": "integer" },
"fresh": { "type": "boolean" }
},
"required": ["path"]
}),
),
tool_def(
"ctx_shell",
"Run shell (replaces native Shell). Compressed output. raw=true skips compression. cwd sets working directory.",
json!({
"type": "object",
"properties": {
"command": { "type": "string", "description": "Shell command" },
"raw": { "type": "boolean", "description": "Skip compression for full output" },
"cwd": { "type": "string", "description": "Working directory (defaults to last cd or project root)" }
},
"required": ["command"]
}),
),
tool_def(
"ctx_search",
"Search code (replaces native Grep/rg). Regex, .gitignore aware, token-efficient.",
json!({
"type": "object",
"properties": {
"pattern": { "type": "string", "description": "Regex pattern" },
"path": { "type": "string" },
"ext": { "type": "string" },
"max_results": { "type": "integer" },
"ignore_gitignore": { "type": "boolean" }
},
"required": ["pattern"]
}),
),
tool_def(
"ctx_tree",
"Directory tree (replaces ls/find). Compact maps with file counts.",
json!({
"type": "object",
"properties": {
"path": { "type": "string" },
"depth": { "type": "integer" },
"show_hidden": { "type": "boolean" }
}
}),
),
tool_def(
"ctx",
"Meta-tool: set tool= to sub-tool name. Sub-tools: compress (checkpoint), metrics (stats), \
analyze (entropy), cache (status|clear|invalidate), discover (missed patterns), smart_read (auto-mode), \
delta (incremental diff), dedup (cross-file), fill (budget-aware batch read), intent (auto-read by task), \
response (compress LLM text), context (session state), graph (build|related|symbol|impact|status), \
session (load|save|task|finding|decision|status|reset|list|cleanup), \
knowledge (remember|recall|pattern|consolidate|timeline|rooms|search|wakeup|status|remove|export|embeddings_status|embeddings_reset|embeddings_reindex), \
agent (register|post|read|status|list|info|diary|recall_diary|diaries), overview (project map), \
wrapped (savings report), benchmark (file|project), multi_read (batch), semantic_search (BM25), \
cost (attribution), heatmap (file access), impact (graph impact), architecture (graph structure), \
task (A2A tasks), workflow (state machine), expand (retrieve archived output).",
json!({
"type": "object",
"properties": {
"tool": {
"type": "string",
"description": "compress|metrics|analyze|cache|discover|smart_read|delta|dedup|fill|intent|response|context|graph|session|knowledge|agent|overview|wrapped|benchmark|multi_read|semantic_search|cost|heatmap|impact|architecture|task|workflow|expand"
},
"action": { "type": "string" },
"path": { "type": "string" },
"paths": { "type": "array", "items": { "type": "string" } },
"query": { "type": "string" },
"value": { "type": "string" },
"category": { "type": "string" },
"key": { "type": "string" },
"to": { "type": "string" },
"spec": { "type": "string" },
"budget": { "type": "integer" },
"task": { "type": "string" },
"mode": { "type": "string" },
"text": { "type": "string" },
"message": { "type": "string" },
"session_id": { "type": "string" },
"period": { "type": "string" },
"format": { "type": "string" },
"agent_type": { "type": "string" },
"role": { "type": "string" },
"status": { "type": "string" },
"pattern_type": { "type": "string" },
"examples": { "type": "array", "items": { "type": "string" } },
"confidence": { "type": "number" },
"project_root": { "type": "string" },
"include_signatures": { "type": "boolean" },
"limit": { "type": "integer" },
"to_agent": { "type": "string" },
"task_id": { "type": "string" },
"agent_id": { "type": "string" },
"description": { "type": "string" },
"state": { "type": "string" },
"root": { "type": "string" },
"depth": { "type": "integer" },
"show_hidden": { "type": "boolean" }
},
"required": ["tool"]
}),
),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn granular_defs_match_registry() {
let granular = granular_tool_defs();
let registry = crate::server::registry::build_registry().tool_defs();
assert_eq!(
granular.len(),
registry.len(),
"granular must mirror the registry tool set"
);
for (g, r) in granular.iter().zip(registry.iter()) {
assert_eq!(g.name, r.name, "tool name drift");
assert_eq!(
g.description, r.description,
"description drift for {}",
g.name
);
assert_eq!(
g.input_schema, r.input_schema,
"schema drift for {}",
g.name
);
}
}
#[test]
fn core_tool_names_exist_in_registry() {
let registry = crate::server::registry::build_registry();
for &name in crate::tool_defs::CORE_TOOL_NAMES {
assert!(
registry.contains(name),
"CORE_TOOL_NAMES references '{name}' which is not registered"
);
}
}
#[test]
fn unified_names_are_registry_tools_or_meta() {
let registry = crate::server::registry::build_registry();
for t in unified_tool_defs() {
let name = t.name.as_ref();
assert!(
name == "ctx" || registry.contains(name),
"unified tool '{name}' is neither the ctx meta-tool nor a registry tool"
);
}
}
}