use crate::tool_spec::ToolSpec;
use agtrace_types::{ToolKind, ToolOrigin};
use serde_json::Value;
const CODEX_TOOLS: &[ToolSpec] = &[
ToolSpec::new("apply_patch", ToolOrigin::System, ToolKind::Write),
ToolSpec::new("read_mcp_resource", ToolOrigin::System, ToolKind::Read),
ToolSpec::new("shell", ToolOrigin::System, ToolKind::Execute),
ToolSpec::new("shell_command", ToolOrigin::System, ToolKind::Execute),
ToolSpec::new("update_plan", ToolOrigin::System, ToolKind::Plan),
];
pub fn classify_tool(tool_name: &str) -> Option<(ToolOrigin, ToolKind)> {
if let Some(spec) = CODEX_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 {
"apply_patch" => extract_patch_filename(arguments),
"shell" => extract_shell_command(arguments),
"update_plan" => arguments
.get("explanation")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
_ => None,
}
}
fn extract_patch_filename(args: &Value) -> Option<String> {
let raw = args.get("raw")?.as_str()?;
let start = raw.find("Update File: ")?;
let rest = &raw[start + 13..];
let end = rest.find('\n').unwrap_or(rest.len());
Some(rest[..end].trim().to_string())
}
fn extract_shell_command(args: &Value) -> Option<String> {
let cmd_array = args.get("command")?.as_array()?;
let cmd_str = cmd_array
.iter()
.filter_map(|s| s.as_str())
.collect::<Vec<_>>()
.join(" ");
Some(cmd_str)
}
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)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_mcp_name() {
assert_eq!(
parse_mcp_name("mcp__o3__o3-search"),
Some(("o3".to_string(), "o3-search".to_string()))
);
assert_eq!(
parse_mcp_name("mcp__sqlite__query"),
Some(("sqlite".to_string(), "query".to_string()))
);
assert_eq!(parse_mcp_name("not_mcp_tool"), None);
assert_eq!(parse_mcp_name("mcp__only_server"), None);
}
#[test]
fn test_mcp_server_and_tool_name() {
assert_eq!(
mcp_server_name("mcp__o3__o3-search"),
Some("o3".to_string())
);
assert_eq!(
mcp_tool_name("mcp__o3__o3-search"),
Some("o3-search".to_string())
);
assert_eq!(mcp_server_name("not_mcp_tool"), None);
assert_eq!(mcp_tool_name("not_mcp_tool"), None);
}
}