alexhancock/mcp-crate-cleanup (#4885)
This commit is contained in:
@@ -285,24 +285,30 @@ impl GooseAcpAgent {
|
||||
// Extract tool name and parameters from the ToolCall if successful
|
||||
let (tool_name, locations) = match &tool_request.tool_call {
|
||||
Ok(tool_call) => {
|
||||
let name = tool_call.name.clone();
|
||||
|
||||
// Extract file locations from certain tools for client tracking
|
||||
let mut locs = Vec::new();
|
||||
if name == "developer__text_editor" {
|
||||
if tool_call.name == "developer__text_editor" {
|
||||
// Try to extract the path from the arguments
|
||||
let args = &tool_call.arguments;
|
||||
if let Some(path_str) = args.get("path").and_then(|p| p.as_str()) {
|
||||
locs.push(acp::ToolCallLocation {
|
||||
path: path_str.into(),
|
||||
line: Some(1),
|
||||
meta: None,
|
||||
});
|
||||
if let Some(path_str) = tool_call
|
||||
.arguments
|
||||
.as_ref()
|
||||
.and_then(|args_map| args_map.get("path"))
|
||||
.and_then(|p| p.as_str())
|
||||
{
|
||||
let path = std::path::PathBuf::from(path_str);
|
||||
if path.exists() && path.is_file() {
|
||||
locs.push(acp::ToolCallLocation {
|
||||
path: path_str.into(),
|
||||
line: Some(1),
|
||||
meta: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
(name, locs)
|
||||
|
||||
(tool_call.name.to_string(), locs)
|
||||
}
|
||||
Err(_) => ("unknown".to_string(), Vec::new()),
|
||||
Err(_) => ("error".to_string(), vec![]),
|
||||
};
|
||||
|
||||
// Send tool call notification
|
||||
|
||||
@@ -17,6 +17,7 @@ use goose::agents::{Agent, AgentEvent};
|
||||
use goose::conversation::message::Message as GooseMessage;
|
||||
use goose::session::SessionManager;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
@@ -518,8 +519,10 @@ async fn process_message_streaming(
|
||||
serde_json::to_string(
|
||||
&WebSocketMessage::ToolRequest {
|
||||
id: req.id.clone(),
|
||||
tool_name: tool_call.name.clone(),
|
||||
arguments: tool_call.arguments.clone(),
|
||||
tool_name: tool_call.name.to_string(),
|
||||
arguments: Value::from(
|
||||
tool_call.arguments.clone(),
|
||||
),
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
@@ -536,8 +539,13 @@ async fn process_message_streaming(
|
||||
serde_json::to_string(
|
||||
&WebSocketMessage::ToolConfirmation {
|
||||
id: confirmation.id.clone(),
|
||||
tool_name: confirmation.tool_name.clone(),
|
||||
arguments: confirmation.arguments.clone(),
|
||||
tool_name: confirmation
|
||||
.tool_name
|
||||
.to_string()
|
||||
.clone(),
|
||||
arguments: Value::from(
|
||||
confirmation.arguments.clone(),
|
||||
),
|
||||
needs_confirmation: true,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! MockClient is a mock implementation of the McpClientTrait for testing purposes.
|
||||
//! add a tool you want to have around and then add the client to the extension router
|
||||
|
||||
use mcp_client::client::{Error, McpClientTrait};
|
||||
use goose::agents::mcp_client::{Error, McpClientTrait};
|
||||
use rmcp::{
|
||||
model::{
|
||||
CallToolResult, Content, ErrorData, GetPromptResult, ListPromptsResult,
|
||||
@@ -91,11 +91,11 @@ impl McpClientTrait for MockClient {
|
||||
async fn call_tool(
|
||||
&self,
|
||||
name: &str,
|
||||
arguments: Value,
|
||||
arguments: Option<serde_json::Map<String, Value>>,
|
||||
_cancel_token: CancellationToken,
|
||||
) -> Result<CallToolResult, Error> {
|
||||
if let Some(handler) = self.handlers.get(name) {
|
||||
match handler(&arguments) {
|
||||
match handler(&Value::Object(arguments.unwrap_or_default())) {
|
||||
Ok(content) => Ok(CallToolResult {
|
||||
content,
|
||||
is_error: None,
|
||||
|
||||
@@ -127,9 +127,11 @@ pub fn tool_request_to_markdown(req: &ToolRequest, export_all_content: bool) ->
|
||||
));
|
||||
md.push_str("**Arguments:**\n");
|
||||
|
||||
match call.name.as_str() {
|
||||
match call.name.as_ref() {
|
||||
"developer__shell" => {
|
||||
if let Some(Value::String(command)) = call.arguments.get("command") {
|
||||
if let Some(Value::String(command)) =
|
||||
call.arguments.as_ref().and_then(|args| args.get("command"))
|
||||
{
|
||||
md.push_str(&format!(
|
||||
"* **command**:\n ```sh\n {}\n ```\n",
|
||||
command.trim()
|
||||
@@ -137,7 +139,7 @@ pub fn tool_request_to_markdown(req: &ToolRequest, export_all_content: bool) ->
|
||||
}
|
||||
let other_args: serde_json::Map<String, Value> = call
|
||||
.arguments
|
||||
.as_object()
|
||||
.as_ref()
|
||||
.map(|obj| {
|
||||
obj.iter()
|
||||
.filter(|(k, _)| k.as_str() != "command")
|
||||
@@ -154,10 +156,16 @@ pub fn tool_request_to_markdown(req: &ToolRequest, export_all_content: bool) ->
|
||||
}
|
||||
}
|
||||
"developer__text_editor" => {
|
||||
if let Some(Value::String(path)) = call.arguments.get("path") {
|
||||
if let Some(Value::String(path)) =
|
||||
call.arguments.as_ref().and_then(|args| args.get("path"))
|
||||
{
|
||||
md.push_str(&format!("* **path**: `{}`\n", path));
|
||||
}
|
||||
if let Some(Value::String(code_edit)) = call.arguments.get("code_edit") {
|
||||
if let Some(Value::String(code_edit)) = call
|
||||
.arguments
|
||||
.as_ref()
|
||||
.and_then(|args| args.get("code_edit"))
|
||||
{
|
||||
md.push_str(&format!(
|
||||
"* **code_edit**:\n ```\n{}\n ```\n",
|
||||
code_edit
|
||||
@@ -166,7 +174,7 @@ pub fn tool_request_to_markdown(req: &ToolRequest, export_all_content: bool) ->
|
||||
|
||||
let other_args: serde_json::Map<String, Value> = call
|
||||
.arguments
|
||||
.as_object()
|
||||
.as_ref()
|
||||
.map(|obj| {
|
||||
obj.iter()
|
||||
.filter(|(k, _)| k.as_str() != "path" && k.as_str() != "code_edit")
|
||||
@@ -183,7 +191,15 @@ pub fn tool_request_to_markdown(req: &ToolRequest, export_all_content: bool) ->
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
md.push_str(&value_to_markdown(&call.arguments, 0, export_all_content));
|
||||
if let Some(args) = &call.arguments {
|
||||
md.push_str(&value_to_markdown(
|
||||
&Value::Object(args.clone()),
|
||||
0,
|
||||
export_all_content,
|
||||
));
|
||||
} else {
|
||||
md.push_str("*No arguments*\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,8 +386,8 @@ pub fn message_to_markdown(message: &Message, export_all_content: bool) -> Strin
|
||||
mod tests {
|
||||
use super::*;
|
||||
use goose::conversation::message::{Message, ToolRequest, ToolResponse};
|
||||
use mcp_core::tool::ToolCall;
|
||||
use rmcp::model::{Content, RawTextContent, TextContent};
|
||||
use rmcp::model::{CallToolRequestParam, Content, RawTextContent, TextContent};
|
||||
use rmcp::object;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
@@ -486,12 +502,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_tool_request_to_markdown_shell() {
|
||||
let tool_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let tool_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "ls -la",
|
||||
"working_dir": "/home/user"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let tool_request = ToolRequest {
|
||||
id: "test-id".to_string(),
|
||||
@@ -509,12 +525,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_tool_request_to_markdown_text_editor() {
|
||||
let tool_call = ToolCall {
|
||||
name: "developer__text_editor".to_string(),
|
||||
arguments: json!({
|
||||
let tool_call = CallToolRequestParam {
|
||||
name: "developer__text_editor".into(),
|
||||
arguments: Some(object!({
|
||||
"path": "/path/to/file.txt",
|
||||
"code_edit": "print('Hello World')"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let tool_request = ToolRequest {
|
||||
id: "test-id".to_string(),
|
||||
@@ -578,9 +594,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_message_to_markdown_with_tool_request() {
|
||||
let tool_call = ToolCall {
|
||||
name: "test_tool".to_string(),
|
||||
arguments: json!({"param": "value"}),
|
||||
let tool_call = CallToolRequestParam {
|
||||
name: "test_tool".into(),
|
||||
arguments: Some(object!({"param": "value"})),
|
||||
};
|
||||
|
||||
let message = Message::assistant().with_tool_request("test-id", Ok(tool_call));
|
||||
@@ -637,11 +653,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_shell_tool_with_code_output() {
|
||||
let tool_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let tool_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "cat main.py"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let tool_request = ToolRequest {
|
||||
id: "shell-cat".to_string(),
|
||||
@@ -683,11 +699,11 @@ if __name__ == "__main__":
|
||||
|
||||
#[test]
|
||||
fn test_shell_tool_with_git_commands() {
|
||||
let git_status_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let git_status_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "git status --porcelain"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let tool_request = ToolRequest {
|
||||
id: "git-status".to_string(),
|
||||
@@ -721,11 +737,11 @@ if __name__ == "__main__":
|
||||
|
||||
#[test]
|
||||
fn test_shell_tool_with_build_output() {
|
||||
let cargo_build_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let cargo_build_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "cargo build"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let _tool_request = ToolRequest {
|
||||
id: "cargo-build".to_string(),
|
||||
@@ -765,11 +781,11 @@ warning: unused variable `x`
|
||||
|
||||
#[test]
|
||||
fn test_shell_tool_with_json_api_response() {
|
||||
let curl_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let curl_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "curl -s https://api.github.com/repos/microsoft/vscode/releases/latest"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let _tool_request = ToolRequest {
|
||||
id: "curl-api".to_string(),
|
||||
@@ -811,13 +827,13 @@ warning: unused variable `x`
|
||||
|
||||
#[test]
|
||||
fn test_text_editor_tool_with_code_creation() {
|
||||
let editor_call = ToolCall {
|
||||
name: "developer__text_editor".to_string(),
|
||||
arguments: json!({
|
||||
let editor_call = CallToolRequestParam {
|
||||
name: "developer__text_editor".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "write",
|
||||
"path": "/tmp/fibonacci.js",
|
||||
"file_text": "function fibonacci(n) {\n if (n <= 1) return n;\n return fibonacci(n - 1) + fibonacci(n - 2);\n}\n\nconsole.log(fibonacci(10));"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let tool_request = ToolRequest {
|
||||
id: "editor-write".to_string(),
|
||||
@@ -852,12 +868,12 @@ warning: unused variable `x`
|
||||
|
||||
#[test]
|
||||
fn test_text_editor_tool_view_code() {
|
||||
let editor_call = ToolCall {
|
||||
name: "developer__text_editor".to_string(),
|
||||
arguments: json!({
|
||||
let editor_call = CallToolRequestParam {
|
||||
name: "developer__text_editor".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "view",
|
||||
"path": "/src/utils.py"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let _tool_request = ToolRequest {
|
||||
id: "editor-view".to_string(),
|
||||
@@ -902,11 +918,11 @@ def process_data(data: List[Dict]) -> List[Dict]:
|
||||
|
||||
#[test]
|
||||
fn test_shell_tool_with_error_output() {
|
||||
let error_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let error_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "python nonexistent_script.py"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let _tool_request = ToolRequest {
|
||||
id: "shell-error".to_string(),
|
||||
@@ -937,11 +953,11 @@ Command failed with exit code 2"#;
|
||||
|
||||
#[test]
|
||||
fn test_shell_tool_complex_script_execution() {
|
||||
let script_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let script_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "python -c \"import sys; print(f'Python {sys.version}'); [print(f'{i}^2 = {i**2}') for i in range(1, 6)]\""
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let tool_request = ToolRequest {
|
||||
id: "script-exec".to_string(),
|
||||
@@ -983,11 +999,11 @@ Command failed with exit code 2"#;
|
||||
|
||||
#[test]
|
||||
fn test_shell_tool_with_multi_command() {
|
||||
let multi_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let multi_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "cd /tmp && ls -la | head -5 && pwd"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let _tool_request = ToolRequest {
|
||||
id: "multi-cmd".to_string(),
|
||||
@@ -1027,11 +1043,11 @@ drwx------ 3 user staff 96 Dec 6 16:20 com.apple.launchd.abc
|
||||
|
||||
#[test]
|
||||
fn test_developer_tool_grep_code_search() {
|
||||
let grep_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let grep_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "rg 'async fn' --type rust -n"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let tool_request = ToolRequest {
|
||||
id: "grep-search".to_string(),
|
||||
@@ -1070,11 +1086,11 @@ src/middleware.rs:12:async fn auth_middleware(req: Request, next: Next) -> Resul
|
||||
#[test]
|
||||
fn test_shell_tool_json_detection_works() {
|
||||
// This test shows that JSON detection in tool responses DOES work
|
||||
let tool_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let tool_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "echo '{\"test\": \"json\"}'"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let _tool_request = ToolRequest {
|
||||
id: "json-test".to_string(),
|
||||
@@ -1104,11 +1120,11 @@ src/middleware.rs:12:async fn auth_middleware(req: Request, next: Next) -> Resul
|
||||
|
||||
#[test]
|
||||
fn test_shell_tool_with_package_management() {
|
||||
let npm_call = ToolCall {
|
||||
name: "developer__shell".to_string(),
|
||||
arguments: json!({
|
||||
let npm_call = CallToolRequestParam {
|
||||
name: "developer__shell".into(),
|
||||
arguments: Some(object!({
|
||||
"command": "npm install express typescript @types/node --save-dev"
|
||||
}),
|
||||
})),
|
||||
};
|
||||
let tool_request = ToolRequest {
|
||||
id: "npm-install".to_string(),
|
||||
|
||||
@@ -1050,11 +1050,10 @@ impl CliSession {
|
||||
}
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
.unwrap_or_else(|| "unknown".to_string().into());
|
||||
|
||||
let success = tool_response.tool_result.is_ok();
|
||||
let result_status = if success { "success" } else { "error" };
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.tool_completions = 1,
|
||||
tool_name = %tool_name,
|
||||
@@ -1328,7 +1327,12 @@ impl CliSession {
|
||||
let mut response_message = Message::user();
|
||||
let last_tool_name = tool_requests
|
||||
.last()
|
||||
.and_then(|(_, tool_call)| tool_call.as_ref().ok().map(|tool| tool.name.clone()))
|
||||
.and_then(|(_, tool_call)| {
|
||||
tool_call
|
||||
.as_ref()
|
||||
.ok()
|
||||
.map(|tool| tool.name.to_string().clone())
|
||||
})
|
||||
.unwrap_or_else(|| "tool".to_string());
|
||||
|
||||
let notification = if interrupt {
|
||||
|
||||
@@ -7,9 +7,8 @@ use goose::providers::pricing::get_model_pricing;
|
||||
use goose::providers::pricing::parse_model_id;
|
||||
use goose::utils::safe_truncate;
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
use mcp_core::tool::ToolCall;
|
||||
use regex::Regex;
|
||||
use rmcp::model::PromptArgument;
|
||||
use rmcp::model::{CallToolRequestParam, JsonObject, PromptArgument};
|
||||
use serde_json::Value;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
@@ -248,7 +247,7 @@ pub fn goose_mode_message(text: &str) {
|
||||
|
||||
fn render_tool_request(req: &ToolRequest, theme: Theme, debug: bool) {
|
||||
match &req.tool_call {
|
||||
Ok(call) => match call.name.as_str() {
|
||||
Ok(call) => match call.name.to_string().as_str() {
|
||||
"developer__text_editor" => render_text_editor_request(call, debug),
|
||||
"developer__shell" => render_shell_request(call, debug),
|
||||
"dynamic_task__create_task" => render_dynamic_task_request(call, debug),
|
||||
@@ -390,44 +389,55 @@ pub fn render_builtin_error(names: &str, error: &str) {
|
||||
println!();
|
||||
}
|
||||
|
||||
fn render_text_editor_request(call: &ToolCall, debug: bool) {
|
||||
fn render_text_editor_request(call: &CallToolRequestParam, debug: bool) {
|
||||
print_tool_header(call);
|
||||
|
||||
// Print path first with special formatting
|
||||
if let Some(Value::String(path)) = call.arguments.get("path") {
|
||||
println!(
|
||||
"{}: {}",
|
||||
style("path").dim(),
|
||||
style(shorten_path(path, debug)).green()
|
||||
);
|
||||
}
|
||||
if let Some(args) = &call.arguments {
|
||||
if let Some(Value::String(path)) = args.get("path") {
|
||||
println!(
|
||||
"{}: {}",
|
||||
style("path").dim(),
|
||||
style(shorten_path(path, debug)).green()
|
||||
);
|
||||
}
|
||||
|
||||
// Print other arguments normally, excluding path
|
||||
if let Some(args) = call.arguments.as_object() {
|
||||
let mut other_args = serde_json::Map::new();
|
||||
for (k, v) in args {
|
||||
if k != "path" {
|
||||
other_args.insert(k.clone(), v.clone());
|
||||
// Print other arguments normally, excluding path
|
||||
if let Some(args) = &call.arguments {
|
||||
let mut other_args = serde_json::Map::new();
|
||||
for (k, v) in args {
|
||||
if k != "path" {
|
||||
other_args.insert(k.clone(), v.clone());
|
||||
}
|
||||
}
|
||||
if !other_args.is_empty() {
|
||||
print_params(&Some(other_args), 0, debug);
|
||||
}
|
||||
}
|
||||
print_params(&Value::Object(other_args), 0, debug);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
fn render_shell_request(call: &ToolCall, debug: bool) {
|
||||
fn render_shell_request(call: &CallToolRequestParam, debug: bool) {
|
||||
print_tool_header(call);
|
||||
print_params(&call.arguments, 0, debug);
|
||||
println!();
|
||||
}
|
||||
|
||||
fn render_dynamic_task_request(call: &ToolCall, debug: bool) {
|
||||
fn render_dynamic_task_request(call: &CallToolRequestParam, debug: bool) {
|
||||
print_tool_header(call);
|
||||
|
||||
// Print task_parameters array
|
||||
if let Some(Value::Array(task_parameters)) = call.arguments.get("task_parameters") {
|
||||
if let Some(task_parameters) = call
|
||||
.arguments
|
||||
.as_ref()
|
||||
.and_then(|args| args.get("task_parameters"))
|
||||
.and_then(|v| match v {
|
||||
Value::Array(arr) => Some(arr),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
println!("{}:", style("task_parameters").dim());
|
||||
|
||||
for task_param in task_parameters.iter() {
|
||||
println!(" -");
|
||||
|
||||
@@ -447,7 +457,9 @@ fn render_dynamic_task_request(call: &ToolCall, debug: bool) {
|
||||
} else if let Value::Object(_) = item {
|
||||
// For objects in arrays, print them with indentation
|
||||
print!(" - ");
|
||||
print_params(item, 3, debug);
|
||||
if let Value::Object(obj) = item {
|
||||
print_params(&Some(obj.clone()), 3, debug);
|
||||
}
|
||||
} else {
|
||||
println!(
|
||||
" - {}",
|
||||
@@ -459,7 +471,9 @@ fn render_dynamic_task_request(call: &ToolCall, debug: bool) {
|
||||
Value::Object(_) => {
|
||||
// For objects, print them with proper indentation
|
||||
println!(" {}:", style(key).dim());
|
||||
print_params(value, 2, debug);
|
||||
if let Value::Object(obj) = value {
|
||||
print_params(&Some(obj.clone()), 2, debug);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// For other types (numbers, booleans, null)
|
||||
@@ -478,20 +492,22 @@ fn render_dynamic_task_request(call: &ToolCall, debug: bool) {
|
||||
println!();
|
||||
}
|
||||
|
||||
fn render_todo_request(call: &ToolCall, _debug: bool) {
|
||||
fn render_todo_request(call: &CallToolRequestParam, _debug: bool) {
|
||||
print_tool_header(call);
|
||||
|
||||
// For todo tools, always show the full content without redaction
|
||||
if let Some(Value::String(content)) = call.arguments.get("content") {
|
||||
println!("{}: {}", style("content").dim(), style(content).green());
|
||||
} else {
|
||||
// For todo__read, there are no arguments
|
||||
// Just print an empty line for consistency
|
||||
if let Some(args) = &call.arguments {
|
||||
if let Some(Value::String(content)) = args.get("content") {
|
||||
println!("{}: {}", style("content").dim(), style(content).green());
|
||||
} else {
|
||||
// For todo__read, there are no arguments
|
||||
// Just print an empty line for consistency
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
fn render_default_request(call: &ToolCall, debug: bool) {
|
||||
fn render_default_request(call: &CallToolRequestParam, debug: bool) {
|
||||
print_tool_header(call);
|
||||
print_params(&call.arguments, 0, debug);
|
||||
println!();
|
||||
@@ -499,7 +515,7 @@ fn render_default_request(call: &ToolCall, debug: bool) {
|
||||
|
||||
// Helper functions
|
||||
|
||||
fn print_tool_header(call: &ToolCall) {
|
||||
fn print_tool_header(call: &CallToolRequestParam) {
|
||||
let parts: Vec<_> = call.name.rsplit("__").collect();
|
||||
let tool_header = format!(
|
||||
"─── {} | {} ──────────────────────────",
|
||||
@@ -564,70 +580,65 @@ fn print_value(value: &Value, debug: bool, reserve_width: usize) {
|
||||
println!("{}", formatted);
|
||||
}
|
||||
|
||||
fn print_params(value: &Value, depth: usize, debug: bool) {
|
||||
fn print_params(value: &Option<JsonObject>, depth: usize, debug: bool) {
|
||||
let indent = INDENT.repeat(depth);
|
||||
|
||||
match value {
|
||||
Value::Object(map) => {
|
||||
for (key, val) in map {
|
||||
match val {
|
||||
Value::Object(_) => {
|
||||
println!("{}{}:", indent, style(key).dim());
|
||||
print_params(val, depth + 1, debug);
|
||||
}
|
||||
Value::Array(arr) => {
|
||||
// Check if all items are simple values (not objects or arrays)
|
||||
let all_simple = arr.iter().all(|item| {
|
||||
matches!(
|
||||
item,
|
||||
Value::String(_) | Value::Number(_) | Value::Bool(_) | Value::Null
|
||||
)
|
||||
});
|
||||
if let Some(json_object) = value {
|
||||
for (key, val) in json_object.iter() {
|
||||
match val {
|
||||
Value::Object(obj) => {
|
||||
println!("{}{}:", indent, style(key).dim());
|
||||
print_params(&Some(obj.clone()), depth + 1, debug);
|
||||
}
|
||||
Value::Array(arr) => {
|
||||
// Check if all items are simple values (not objects or arrays)
|
||||
let all_simple = arr.iter().all(|item| {
|
||||
matches!(
|
||||
item,
|
||||
Value::String(_) | Value::Number(_) | Value::Bool(_) | Value::Null
|
||||
)
|
||||
});
|
||||
|
||||
if all_simple {
|
||||
// Render inline for simple arrays, truncation will be handled by print_value if needed
|
||||
let values: Vec<String> = arr
|
||||
.iter()
|
||||
.map(|item| match item {
|
||||
Value::String(s) => s.clone(),
|
||||
Value::Number(n) => n.to_string(),
|
||||
Value::Bool(b) => b.to_string(),
|
||||
Value::Null => "null".to_string(),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect();
|
||||
let joined_values = values.join(", ");
|
||||
print_value_with_prefix(
|
||||
&format!("{}{}: ", indent, style(key).dim()),
|
||||
&Value::String(joined_values),
|
||||
debug,
|
||||
);
|
||||
} else {
|
||||
// Use the original multi-line format for complex arrays
|
||||
println!("{}{}:", indent, style(key).dim());
|
||||
for item in arr.iter() {
|
||||
if all_simple {
|
||||
// Render inline for simple arrays, truncation will be handled by print_value if needed
|
||||
let values: Vec<String> = arr
|
||||
.iter()
|
||||
.map(|item| match item {
|
||||
Value::String(s) => s.clone(),
|
||||
Value::Number(n) => n.to_string(),
|
||||
Value::Bool(b) => b.to_string(),
|
||||
Value::Null => "null".to_string(),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect();
|
||||
let joined_values = values.join(", ");
|
||||
print_value_with_prefix(
|
||||
&format!("{}{}: ", indent, style(key).dim()),
|
||||
&Value::String(joined_values),
|
||||
debug,
|
||||
);
|
||||
} else {
|
||||
// Use the original multi-line format for complex arrays
|
||||
println!("{}{}:", indent, style(key).dim());
|
||||
for item in arr.iter() {
|
||||
if let Value::Object(obj) = item {
|
||||
println!("{}{}- ", indent, INDENT);
|
||||
print_params(item, depth + 2, debug);
|
||||
print_params(&Some(obj.clone()), depth + 2, debug);
|
||||
} else {
|
||||
println!("{}{}- {}", indent, INDENT, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
print_value_with_prefix(
|
||||
&format!("{}{}: ", indent, style(key).dim()),
|
||||
val,
|
||||
debug,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
print_value_with_prefix(
|
||||
&format!("{}{}: ", indent, style(key).dim()),
|
||||
val,
|
||||
debug,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Value::Array(arr) => {
|
||||
for (i, item) in arr.iter().enumerate() {
|
||||
println!("{}{}.", indent, i + 1);
|
||||
print_params(item, depth + 1, debug);
|
||||
}
|
||||
}
|
||||
_ => print_value(value, debug, 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user