Revert "Internal MCP Crate Cleanup (#4800)" (#4883)

This commit is contained in:
Alex Hancock
2025-09-29 14:21:30 -04:00
committed by GitHub
parent 2cfef016e2
commit b9ba8dca29
78 changed files with 1090 additions and 844 deletions
@@ -3,7 +3,8 @@ use crate::model::ModelConfig;
use crate::providers::base::Usage;
use crate::providers::errors::ProviderError;
use anyhow::{anyhow, Result};
use rmcp::model::{object, CallToolRequestParam, ErrorCode, ErrorData, Role, Tool};
use mcp_core::ToolCall;
use rmcp::model::{ErrorCode, ErrorData, Role, Tool};
use serde_json::{json, Value};
use std::collections::HashSet;
@@ -229,24 +230,19 @@ pub fn response_to_message(response: &Value) -> Result<Message> {
let name = block
.get(NAME_FIELD)
.and_then(|n| n.as_str())
.ok_or_else(|| anyhow!("Missing tool_use name"))?
.to_string();
.ok_or_else(|| anyhow!("Missing tool_use name"))?;
let input = block
.get(INPUT_FIELD)
.ok_or_else(|| anyhow!("Missing tool_use input"))?;
let tool_call = CallToolRequestParam {
name: name.into(),
arguments: Some(object(input.clone())),
};
let tool_call = ToolCall::new(name, input.clone());
message = message.with_tool_request(id, Ok(tool_call));
}
Some(THINKING_TYPE) => {
let thinking = block
.get(THINKING_TYPE)
.and_then(|t| t.as_str())
.ok_or_else(|| anyhow!("Missing thinking content"))?
.to_string();
.ok_or_else(|| anyhow!("Missing thinking content"))?;
let signature = block
.get(SIGNATURE_FIELD)
.and_then(|s| s.as_str())
@@ -593,8 +589,7 @@ where
}
};
let tool_call = CallToolRequestParam{ name: name.into(), arguments: Some(object(parsed_args)) };
let tool_call = ToolCall::new(&name, parsed_args);
let mut message = Message::new(
rmcp::model::Role::Assistant,
chrono::Utc::now().timestamp(),
@@ -755,7 +750,7 @@ mod tests {
if let MessageContent::ToolRequest(tool_request) = &message.content[0] {
let tool_call = tool_request.tool_call.as_ref().unwrap();
assert_eq!(tool_call.name, "calculator");
assert_eq!(tool_call.arguments, Some(object!({"expression": "2 + 2"})));
assert_eq!(tool_call.arguments, json!({"expression": "2 + 2"}));
} else {
panic!("Expected ToolRequest content");
}
@@ -997,10 +992,7 @@ mod tests {
let messages = vec![
Message::assistant().with_tool_request(
"tool_1",
Ok(CallToolRequestParam {
name: "calculator".into(),
arguments: Some(object!({"expression": "2 + 2"})),
}),
Ok(ToolCall::new("calculator", json!({"expression": "2 + 2"}))),
),
Message::user().with_tool_response(
"tool_1",
+8 -11
View File
@@ -2,16 +2,13 @@ use std::borrow::Cow;
use std::collections::HashMap;
use std::path::Path;
use crate::mcp_utils::ToolResult;
use anyhow::{anyhow, bail, Result};
use aws_sdk_bedrockruntime::types as bedrock;
use aws_smithy_types::{Document, Number};
use base64::Engine;
use chrono::Utc;
use rmcp::model::{
object, CallToolRequestParam, Content, ErrorCode, ErrorData, RawContent, ResourceContents,
Role, Tool,
};
use mcp_core::{ToolCall, ToolResult};
use rmcp::model::{Content, ErrorCode, ErrorData, RawContent, ResourceContents, Role, Tool};
use serde_json::Value;
use super::super::base::Usage;
@@ -60,7 +57,7 @@ pub fn to_bedrock_message_content(content: &MessageContent) -> Result<bedrock::C
bedrock::ToolUseBlock::builder()
.tool_use_id(tool_use_id)
.name(call.name.to_string())
.input(to_bedrock_json(&Value::from(call.arguments.clone())))
.input(to_bedrock_json(&call.arguments))
.build()
} else {
bedrock::ToolUseBlock::builder()
@@ -75,7 +72,7 @@ pub fn to_bedrock_message_content(content: &MessageContent) -> Result<bedrock::C
bedrock::ToolUseBlock::builder()
.tool_use_id(tool_use_id)
.name(call.name.to_string())
.input(to_bedrock_json(&Value::from(call.arguments.clone())))
.input(to_bedrock_json(&call.arguments))
.build()
} else {
bedrock::ToolUseBlock::builder()
@@ -285,10 +282,10 @@ pub fn from_bedrock_content_block(block: &bedrock::ContentBlock) -> Result<Messa
bedrock::ContentBlock::Text(text) => MessageContent::text(text),
bedrock::ContentBlock::ToolUse(tool_use) => MessageContent::tool_request(
tool_use.tool_use_id.to_string(),
Ok(CallToolRequestParam {
name: tool_use.name.clone().into(),
arguments: Some(object(from_bedrock_json(&tool_use.input.clone())?)),
}),
Ok(ToolCall::new(
tool_use.name.to_string(),
from_bedrock_json(&tool_use.input)?,
)),
),
bedrock::ContentBlock::ToolResult(tool_res) => MessageContent::tool_response(
tool_res.tool_use_id.to_string(),
@@ -5,9 +5,9 @@ use crate::providers::utils::{
sanitize_function_name, ImageFormat,
};
use anyhow::{anyhow, Error};
use mcp_core::ToolCall;
use rmcp::model::{
object, AnnotateAble, CallToolRequestParam, Content, ErrorCode, ErrorData, RawContent,
ResourceContents, Role, Tool,
AnnotateAble, Content, ErrorCode, ErrorData, RawContent, ResourceContents, Role, Tool,
};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
@@ -109,7 +109,7 @@ fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<Data
"type": "function",
"function": {
"name": sanitized_name,
"arguments": tool_call.arguments,
"arguments": tool_call.arguments.to_string(),
}
}));
}
@@ -286,7 +286,6 @@ pub fn format_tools(tools: &[Tool]) -> anyhow::Result<Vec<Value>> {
}
/// Convert Databricks' API response to internal Message format
#[allow(clippy::too_many_lines)]
pub fn response_to_message(response: &Value) -> anyhow::Result<Message> {
let original = &response["choices"][0]["message"];
let mut content = Vec::new();
@@ -374,10 +373,7 @@ pub fn response_to_message(response: &Value) -> anyhow::Result<Message> {
Ok(params) => {
content.push(MessageContent::tool_request(
id,
Ok(CallToolRequestParam {
name: function_name.into(),
arguments: Some(object(params)),
}),
Ok(ToolCall::new(&function_name, params)),
));
}
Err(e) => {
@@ -775,10 +771,7 @@ mod tests {
Message::user().with_text("How are you?"),
Message::assistant().with_tool_request(
"tool1",
Ok(CallToolRequestParam {
name: "example".into(),
arguments: Some(object!({"param1": "value1"})),
}),
Ok(ToolCall::new("example", json!({"param1": "value1"}))),
),
];
@@ -814,10 +807,7 @@ mod tests {
fn test_format_messages_multiple_content() -> anyhow::Result<()> {
let mut messages = vec![Message::assistant().with_tool_request(
"tool1",
Ok(CallToolRequestParam {
name: "example".into(),
arguments: Some(object!({"param1": "value1"})),
}),
Ok(ToolCall::new("example", json!({"param1": "value1"}))),
)];
// Get the ID from the tool request to use in the response
@@ -966,7 +956,7 @@ mod tests {
if let MessageContent::ToolRequest(request) = &message.content[0] {
let tool_call = request.tool_call.as_ref().unwrap();
assert_eq!(tool_call.name, "example_fn");
assert_eq!(tool_call.arguments, Some(object!({"param": "value"})));
assert_eq!(tool_call.arguments, json!({"param": "value"}));
} else {
panic!("Expected ToolRequest content");
}
@@ -1037,7 +1027,7 @@ mod tests {
if let MessageContent::ToolRequest(request) = &message.content[0] {
let tool_call = request.tool_call.as_ref().unwrap();
assert_eq!(tool_call.name, "example_fn");
assert_eq!(tool_call.arguments, Some(object!({})));
assert_eq!(tool_call.arguments, json!({}));
} else {
panic!("Expected ToolRequest content");
}
+15 -38
View File
@@ -3,10 +3,9 @@ use crate::providers::base::Usage;
use crate::providers::errors::ProviderError;
use crate::providers::utils::{is_valid_function_name, sanitize_function_name};
use anyhow::Result;
use mcp_core::ToolCall;
use rand::{distributions::Alphanumeric, Rng};
use rmcp::model::{
object, AnnotateAble, CallToolRequestParam, ErrorCode, ErrorData, RawContent, Role, Tool,
};
use rmcp::model::{AnnotateAble, ErrorCode, ErrorData, RawContent, Role, Tool};
use std::borrow::Cow;
use crate::conversation::message::{Message, MessageContent};
@@ -44,14 +43,12 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
"name".to_string(),
json!(sanitize_function_name(&tool_call.name)),
);
if let Some(args) = &tool_call.arguments {
if !args.is_empty() {
function_call_part
.insert("args".to_string(), args.clone().into());
}
if tool_call.arguments.is_object()
&& !tool_call.arguments.as_object().unwrap().is_empty()
{
function_call_part
.insert("args".to_string(), tool_call.arguments.clone());
}
parts.push(json!({
"functionCall": function_call_part
}));
@@ -272,10 +269,7 @@ pub fn response_to_message(response: Value) -> Result<Message> {
if let Some(params) = parameters {
content.push(MessageContent::tool_request(
id,
Ok(CallToolRequestParam {
name: name.into(),
arguments: Some(object(params.clone())),
}),
Ok(ToolCall::new(&name, params.clone())),
));
}
}
@@ -347,7 +341,6 @@ pub fn create_request(
mod tests {
use super::*;
use crate::conversation::message::Message;
use rmcp::model::CallToolRequestParam;
use rmcp::{model::Content, object};
use serde_json::json;
@@ -355,7 +348,7 @@ mod tests {
Message::new(role, 0, vec![MessageContent::text(text.to_string())])
}
fn set_up_tool_request_message(id: &str, tool_call: CallToolRequestParam) -> Message {
fn set_up_tool_request_message(id: &str, tool_call: ToolCall) -> Message {
Message::new(
Role::User,
0,
@@ -363,14 +356,14 @@ mod tests {
)
}
fn set_up_tool_confirmation_message(id: &str, tool_call: CallToolRequestParam) -> Message {
fn set_up_tool_confirmation_message(id: &str, tool_call: ToolCall) -> Message {
Message::new(
Role::User,
0,
vec![MessageContent::tool_confirmation_request(
id.to_string(),
tool_call.name.to_string().clone(),
tool_call.arguments.unwrap_or_default().clone(),
tool_call.name.clone(),
tool_call.arguments.clone(),
Some("goose would like to call the above tool. Allow? (y/n):".to_string()),
)],
)
@@ -422,19 +415,10 @@ mod tests {
"param1": "value1"
});
let messages = vec![
set_up_tool_request_message(
"id",
CallToolRequestParam {
name: "tool_name".into(),
arguments: Some(object(arguments.clone())),
},
),
set_up_tool_request_message("id", ToolCall::new("tool_name", arguments.clone())),
set_up_tool_confirmation_message(
"id2",
CallToolRequestParam {
name: "tool_name_2".into(),
arguments: Some(object(arguments.clone())),
},
ToolCall::new("tool_name_2", arguments.clone()),
),
];
let payload = format_messages(&messages);
@@ -796,14 +780,7 @@ mod tests {
assert_eq!(message.content.len(), 1);
if let Ok(tool_call) = &message.content[0].as_tool_request().unwrap().tool_call {
assert_eq!(tool_call.name, "valid_name");
assert_eq!(
tool_call
.arguments
.as_ref()
.and_then(|args| args.get("param"))
.and_then(|v| v.as_str()),
Some("value")
);
assert_eq!(tool_call.arguments["param"], "value");
} else {
panic!("Expected valid tool request");
}
+10 -19
View File
@@ -8,9 +8,9 @@ use crate::providers::utils::{
use anyhow::{anyhow, Error};
use async_stream::try_stream;
use futures::Stream;
use mcp_core::ToolCall;
use rmcp::model::{
object, AnnotateAble, CallToolRequestParam, Content, ErrorCode, ErrorData, RawContent,
ResourceContents, Role, Tool,
AnnotateAble, Content, ErrorCode, ErrorData, RawContent, ResourceContents, Role, Tool,
};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
@@ -115,7 +115,7 @@ pub fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<
"type": "function",
"function": {
"name": sanitized_name,
"arguments": tool_call.arguments,
"arguments": tool_call.arguments.to_string(),
}
}));
}
@@ -220,7 +220,7 @@ pub fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<
"type": "function",
"function": {
"name": sanitized_name,
"arguments": tool_call.arguments,
"arguments": tool_call.arguments.to_string(),
}
}));
}
@@ -316,10 +316,7 @@ pub fn response_to_message(response: &Value) -> anyhow::Result<Message> {
Ok(params) => {
content.push(MessageContent::tool_request(
id,
Ok(CallToolRequestParam {
name: function_name.into(),
arguments: Some(object(params)),
}),
Ok(ToolCall::new(&function_name, params)),
));
}
Err(e) => {
@@ -518,7 +515,7 @@ where
Ok(params) => {
MessageContent::tool_request(
id.clone(),
Ok(CallToolRequestParam { name: function_name.clone().into(), arguments: Some(object(params)) }),
Ok(ToolCall::new(function_name.clone(), params)),
)
},
Err(e) => {
@@ -824,10 +821,7 @@ mod tests {
Message::user().with_text("How are you?"),
Message::assistant().with_tool_request(
"tool1",
Ok(CallToolRequestParam {
name: "example".into(),
arguments: Some(object!({"param1": "value1"})),
}),
Ok(ToolCall::new("example", json!({"param1": "value1"}))),
),
];
@@ -861,10 +855,7 @@ mod tests {
fn test_format_messages_multiple_content() -> anyhow::Result<()> {
let mut messages = vec![Message::assistant().with_tool_request(
"tool1",
Ok(CallToolRequestParam {
name: "example".into(),
arguments: Some(object!({"param1": "value1"})),
}),
Ok(ToolCall::new("example", json!({"param1": "value1"}))),
)];
// Get the ID from the tool request to use in the response
@@ -1009,7 +1000,7 @@ mod tests {
if let MessageContent::ToolRequest(request) = &message.content[0] {
let tool_call = request.tool_call.as_ref().unwrap();
assert_eq!(tool_call.name, "example_fn");
assert_eq!(tool_call.arguments, Some(object!({"param": "value"})));
assert_eq!(tool_call.arguments, json!({"param": "value"}));
} else {
panic!("Expected ToolRequest content");
}
@@ -1080,7 +1071,7 @@ mod tests {
if let MessageContent::ToolRequest(request) = &message.content[0] {
let tool_call = request.tool_call.as_ref().unwrap();
assert_eq!(tool_call.name, "example_fn");
assert_eq!(tool_call.arguments, Some(object!({})));
assert_eq!(tool_call.arguments, json!({}));
} else {
panic!("Expected ToolRequest content");
}
+14 -26
View File
@@ -3,8 +3,8 @@ use crate::model::ModelConfig;
use crate::providers::base::Usage;
use crate::providers::errors::ProviderError;
use anyhow::{anyhow, Result};
use rmcp::model::{object, CallToolRequestParam, Role, Tool};
use rmcp::object;
use mcp_core::tool::ToolCall;
use rmcp::model::{Role, Tool};
use serde_json::{json, Value};
use std::collections::HashSet;
@@ -181,22 +181,16 @@ pub fn parse_streaming_response(sse_data: &str) -> Result<Message> {
}
// Add tool use if complete
if let Some((id, name)) = tool_use_id.zip(tool_name) {
if let (Some(id), Some(name)) = (&tool_use_id, &tool_name) {
if !tool_input.is_empty() {
let input_value = serde_json::from_str::<Value>(&tool_input)
.unwrap_or_else(|_| Value::String(tool_input.clone()));
let tool_call = CallToolRequestParam {
name: name.into(),
arguments: Some(object(input_value)),
};
message = message.with_tool_request(&id, Ok(tool_call));
} else {
let tool_call = ToolCall::new(name, input_value);
message = message.with_tool_request(id, Ok(tool_call));
} else if tool_name.is_some() {
// Tool with no input - use empty object
let tool_call = CallToolRequestParam {
name: name.into(),
arguments: Some(object!({})),
};
message = message.with_tool_request(&id, Ok(tool_call));
let tool_call = ToolCall::new(name, Value::Object(serde_json::Map::new()));
message = message.with_tool_request(id, Ok(tool_call));
}
}
@@ -244,18 +238,14 @@ pub fn response_to_message(response: &Value) -> Result<Message> {
let name = content
.get("name")
.and_then(|n| n.as_str())
.ok_or_else(|| anyhow!("Missing tool_use name"))?
.to_string();
.ok_or_else(|| anyhow!("Missing tool_use name"))?;
let input = content
.get("input")
.ok_or_else(|| anyhow!("Missing tool input"))?
.clone();
let tool_call = CallToolRequestParam {
name: name.into(),
arguments: Some(object(input)),
};
let tool_call = ToolCall::new(name, input);
message = message.with_tool_request(id, Ok(tool_call));
}
Some("thinking") => {
@@ -435,7 +425,7 @@ mod tests {
if let MessageContent::ToolRequest(tool_request) = &message.content[0] {
let tool_call = tool_request.tool_call.as_ref().unwrap();
assert_eq!(tool_call.name, "calculator");
assert_eq!(tool_call.arguments, Some(object!({"expression": "2 + 2"})));
assert_eq!(tool_call.arguments, json!({"expression": "2 + 2"}));
} else {
panic!("Expected ToolRequest content");
}
@@ -546,7 +536,7 @@ data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-sonnet-4-2025
if let MessageContent::ToolRequest(tool_request) = &message.content[1] {
let tool_call = tool_request.tool_call.as_ref().unwrap();
assert_eq!(tool_call.name, "get_stock_price");
assert_eq!(tool_call.arguments, Some(object!({"symbol": "NVDA"})));
assert_eq!(tool_call.arguments, json!({"symbol": "NVDA"}));
assert_eq!(tool_request.id, "tooluse_FB_nOElDTAOKa-YnVWI5Uw");
} else {
panic!("Expected ToolRequest content second");
@@ -689,12 +679,10 @@ data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-sonnet-4-2025
#[test]
fn test_message_formatting_skips_tool_requests() {
use crate::conversation::message::Message;
use mcp_core::tool::ToolCall;
// Create a conversation with text, tool requests, and tool responses
let tool_call = CallToolRequestParam {
name: "calculator".into(),
arguments: Some(object!({"expression": "2 + 2"})),
};
let tool_call = ToolCall::new("calculator", json!({"expression": "2 + 2"}));
let messages = vec![
Message::user().with_text("Calculate 2 + 2"),