use std::fmt::Write as _;
use serde_json::Value;
use trusty_mpm::mcp::tools::tool_catalog;
pub(crate) fn render() -> String {
let catalog = tool_catalog();
let mut out = String::new();
out.push_str("# MCP Tool Reference\n\n");
out.push_str(
"Generated from `trusty_mpm::mcp::tools::tool_catalog()` — trusty-mpm's \
own MCP tool surface (`tools/list` over the `serve --stdio` bridge), \
in catalog order. Regenerate with `tm generate capabilities`.\n\n",
);
let _ = writeln!(out, "{} tools.\n", catalog.len());
for tool in &catalog {
render_tool(tool, &mut out);
}
out.push_str("## Sibling Daemon MCP Surfaces\n\n");
out.push_str(
"Each sibling daemon owns its own tool catalog in its own crate — out of \
scope for direct extraction here. See that daemon's own descriptor \
source for its exact tool list:\n\n",
);
out.push_str("- **trusty-search** — `crates/trusty-search/src/mcp/tools/descriptors.rs`\n");
out.push_str("- **trusty-analyze** — `crates/trusty-analyze/src/mcp/descriptors.rs`\n");
out.push_str("- **trusty-memory** — `crates/trusty-memory/src/mcp_service.rs`\n");
out
}
fn render_tool(tool: &Value, out: &mut String) {
let name = tool["name"].as_str().unwrap_or("<unnamed>");
let description = tool["description"].as_str().unwrap_or("");
let _ = writeln!(out, "## `{name}`\n");
let _ = writeln!(out, "{description}\n");
let schema = &tool["inputSchema"];
let required: Vec<&str> = schema["required"]
.as_array()
.map(|a| a.iter().filter_map(Value::as_str).collect())
.unwrap_or_default();
match schema["properties"].as_object() {
Some(props) if !props.is_empty() => {
out.push_str("| Parameter | Type | Required |\n|---|---|---|\n");
let mut names: Vec<&String> = props.keys().collect();
names.sort();
for name in names {
let prop = &props[name];
let ty = prop["type"].as_str().unwrap_or("any");
let req = if required.contains(&name.as_str()) {
"yes"
} else {
"no"
};
let _ = writeln!(out, "| `{name}` | `{ty}` | {req} |");
}
out.push('\n');
}
_ => out.push_str("No parameters.\n\n"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mcp_tools_render_contains_known_tool() {
let rendered = render();
assert!(rendered.contains("## `session_list`"), "{rendered}");
assert!(rendered.contains("## `agent_delegate`"), "{rendered}");
assert!(rendered.contains("trusty-search"), "{rendered}");
}
#[test]
fn mcp_tools_render_lists_all_catalog_tools() {
let rendered = render();
for tool in tool_catalog() {
let name = tool["name"].as_str().unwrap();
assert!(
rendered.contains(&format!("## `{name}`")),
"missing {name} in rendered output"
);
}
}
#[test]
fn mcp_tools_render_is_deterministic() {
assert_eq!(render(), render());
}
}