use openai_protocol::{
common::{Function, Tool},
responses::{generate_id, McpToolInfo, ResponseOutputItem, ResponseTool, ResponseToolType},
};
use serde_json::{json, Value};
use crate::inventory::{QualifiedToolName, ToolEntry};
fn resolved_name_for_entry<'a>(
entry: &'a ToolEntry,
exposed_names: Option<&'a std::collections::HashMap<QualifiedToolName, String>>,
) -> &'a str {
exposed_names
.and_then(|m| m.get(&entry.qualified_name))
.map(|s| s.as_str())
.unwrap_or(entry.tool_name())
}
pub fn build_function_tools_json(entries: &[ToolEntry]) -> Vec<Value> {
build_function_tools_json_with_names(entries, None)
}
pub fn build_function_tools_json_with_names(
entries: &[ToolEntry],
exposed_names: Option<&std::collections::HashMap<QualifiedToolName, String>>,
) -> Vec<Value> {
entries
.iter()
.map(|entry| {
json!({
"type": "function",
"name": resolved_name_for_entry(entry, exposed_names),
"description": entry.tool.description,
"parameters": Value::Object((*entry.tool.input_schema).clone())
})
})
.collect()
}
pub fn build_chat_function_tools(entries: &[ToolEntry]) -> Vec<Tool> {
build_chat_function_tools_with_names(entries, None)
}
pub fn build_chat_function_tools_with_names(
entries: &[ToolEntry],
exposed_names: Option<&std::collections::HashMap<QualifiedToolName, String>>,
) -> Vec<Tool> {
entries
.iter()
.map(|entry| Tool {
tool_type: "function".to_string(),
function: Function {
name: resolved_name_for_entry(entry, exposed_names).to_string(),
description: entry.tool.description.as_ref().map(|d| d.to_string()),
parameters: Value::Object((*entry.tool.input_schema).clone()),
strict: None,
},
})
.collect()
}
pub fn build_response_tools(entries: &[ToolEntry]) -> Vec<ResponseTool> {
build_response_tools_with_names(entries, None)
}
pub fn build_response_tools_with_names(
entries: &[ToolEntry],
exposed_names: Option<&std::collections::HashMap<QualifiedToolName, String>>,
) -> Vec<ResponseTool> {
entries
.iter()
.map(|entry| ResponseTool {
r#type: ResponseToolType::Mcp,
function: Some(Function {
name: resolved_name_for_entry(entry, exposed_names).to_string(),
description: entry.tool.description.as_ref().map(|d| d.to_string()),
parameters: Value::Object((*entry.tool.input_schema).clone()),
strict: None,
}),
server_url: None,
authorization: None,
headers: None,
server_label: Some(entry.server_key().to_string()),
server_description: None,
require_approval: None,
allowed_tools: None,
})
.collect()
}
pub fn build_mcp_tool_infos(entries: &[ToolEntry]) -> Vec<McpToolInfo> {
entries
.iter()
.map(|entry| McpToolInfo {
name: entry.tool_name().to_string(),
description: entry.tool.description.as_ref().map(|d| d.to_string()),
input_schema: Value::Object((*entry.tool.input_schema).clone()),
annotations: entry
.tool
.annotations
.as_ref()
.and_then(|a| serde_json::to_value(a).ok()),
})
.collect()
}
pub fn build_mcp_list_tools_item(server_label: &str, entries: &[ToolEntry]) -> ResponseOutputItem {
ResponseOutputItem::McpListTools {
id: generate_id("mcpl"),
server_label: server_label.to_string(),
tools: build_mcp_tool_infos(entries),
}
}
pub fn build_mcp_list_tools_json(server_label: &str, entries: &[ToolEntry]) -> Value {
serde_json::to_value(build_mcp_list_tools_item(server_label, entries)).unwrap_or_else(
|_| json!({ "type": "mcp_list_tools", "server_label": server_label, "tools": [] }),
)
}