use crate::tool_spec::ToolSpec;
use agtrace_types::{ToolKind, ToolOrigin};
use serde_json::Value;
const GEMINI_TOOLS: &[ToolSpec] = &[
ToolSpec::new("google_web_search", ToolOrigin::System, ToolKind::Search),
ToolSpec::new("read_file", ToolOrigin::System, ToolKind::Read),
ToolSpec::new("replace", ToolOrigin::System, ToolKind::Write),
ToolSpec::new("write_file", ToolOrigin::System, ToolKind::Write),
ToolSpec::new("run_shell_command", ToolOrigin::System, ToolKind::Execute),
ToolSpec::new("write_todos", ToolOrigin::System, ToolKind::Plan),
];
pub fn classify_tool(tool_name: &str) -> Option<(ToolOrigin, ToolKind)> {
if let Some(spec) = GEMINI_TOOLS.iter().find(|t| t.name == tool_name) {
return Some((spec.origin, spec.kind));
}
if tool_name.starts_with("mcp__") {
return Some((ToolOrigin::Mcp, ToolKind::Other));
}
None
}
pub fn extract_summary(tool_name: &str, _kind: ToolKind, arguments: &Value) -> Option<String> {
match tool_name {
"write_todos" => extract_todo_summary(arguments),
_ => None,
}
}
fn extract_todo_summary(args: &Value) -> Option<String> {
let todos = args.get("todos")?.as_array()?;
let count = todos.len();
let first = todos.first()?;
let text = first.get("description")?.as_str()?;
if count > 1 {
Some(format!("{} (+{} more)", text, count - 1))
} else {
Some(text.to_string())
}
}
pub fn parse_mcp_name(full_name: &str) -> Option<(String, String)> {
if !full_name.starts_with("mcp__") {
return None;
}
let rest = &full_name[5..]; let parts: Vec<&str> = rest.splitn(2, "__").collect();
if parts.len() == 2 {
Some((parts[0].to_string(), parts[1].to_string()))
} else {
None
}
}
pub fn mcp_server_name(full_name: &str) -> Option<String> {
parse_mcp_name(full_name).map(|(server, _)| server)
}
pub fn mcp_tool_name(full_name: &str) -> Option<String> {
parse_mcp_name(full_name).map(|(_, tool)| tool)
}
pub fn is_mcp_tool(display_name: Option<&str>) -> bool {
display_name
.map(|name| name.contains("MCP Server"))
.unwrap_or(false)
}
pub fn extract_mcp_server_name(display_name: Option<&str>) -> Option<String> {
let name = display_name?;
let start = name.find('(')?;
let end = name.find(" MCP Server)")?;
if start < end {
let server = name[start + 1..end].trim();
Some(server.to_string())
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_mcp_tool() {
assert!(is_mcp_tool(Some("o3-search (o3 MCP Server)")));
assert!(is_mcp_tool(Some("brave-search (brave MCP Server)")));
assert!(!is_mcp_tool(Some("Google Web Search")));
assert!(!is_mcp_tool(Some("read_file")));
assert!(!is_mcp_tool(None));
}
#[test]
fn test_extract_mcp_server_name() {
assert_eq!(
extract_mcp_server_name(Some("o3-search (o3 MCP Server)")),
Some("o3".to_string())
);
assert_eq!(
extract_mcp_server_name(Some("brave-search (brave MCP Server)")),
Some("brave".to_string())
);
assert_eq!(
extract_mcp_server_name(Some("sqlite-query (sqlite MCP Server)")),
Some("sqlite".to_string())
);
assert_eq!(extract_mcp_server_name(Some("Google Web Search")), None);
assert_eq!(extract_mcp_server_name(Some("read_file")), None);
assert_eq!(extract_mcp_server_name(None), None);
}
}