diff --git a/Cargo.lock b/Cargo.lock index 0eda119e6bafe7017516c254d270d7a26d533f65..f08ed4cd173f7aa68ad62b99a0c3f4692fde60e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,6 +179,7 @@ dependencies = [ "gpui", "gpui_tokio", "handlebars 4.5.0", + "heck 0.5.0", "html_to_markdown", "http_client", "indoc", diff --git a/crates/agent/Cargo.toml b/crates/agent/Cargo.toml index 2ea713a04d66b6e351cdb163318c39498bb412c3..9f563cf0b1b009a496d36a6f090b0f4b476433a7 100644 --- a/crates/agent/Cargo.toml +++ b/crates/agent/Cargo.toml @@ -38,6 +38,7 @@ futures.workspace = true git.workspace = true gpui.workspace = true handlebars = { workspace = true, features = ["rust-embed"] } +heck.workspace = true html_to_markdown.workspace = true http_client.workspace = true indoc.workspace = true diff --git a/crates/agent/src/tests/mod.rs b/crates/agent/src/tests/mod.rs index 499d9bdd30d50b236023e872805acaf6680f75ee..6434ba09a872a4674d53450606834a5b2923436b 100644 --- a/crates/agent/src/tests/mod.rs +++ b/crates/agent/src/tests/mod.rs @@ -1767,6 +1767,23 @@ async fn test_mcp_tool_truncation(cx: &mut TestAppContext) { cx, ); + // Server with spaces in name - tests snake_case conversion for API compatibility + let _server4_calls = setup_context_server( + "Azure DevOps", + vec![context_server::types::Tool { + name: "echo".into(), // Also conflicts - will be disambiguated as azure_dev_ops_echo + description: None, + input_schema: serde_json::to_value(EchoTool::input_schema( + LanguageModelToolSchemaFormat::JsonSchema, + )) + .unwrap(), + output_schema: None, + annotations: None, + }], + &context_server_store, + cx, + ); + thread .update(cx, |thread, cx| { thread.send(UserMessageId::new(), ["Go"], cx) @@ -1777,6 +1794,7 @@ async fn test_mcp_tool_truncation(cx: &mut TestAppContext) { assert_eq!( tool_names_for_completion(&completion), vec![ + "azure_dev_ops_echo", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", "delay", diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs index 26026175e68f3fecd20d21441bb7f1e41a438207..1820aebae547afa1a01968bb5d160b34503e9e1e 100644 --- a/crates/agent/src/thread.rs +++ b/crates/agent/src/thread.rs @@ -32,6 +32,7 @@ use futures::{ use gpui::{ App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, WeakEntity, }; +use heck::ToSnakeCase as _; use language_model::{ LanguageModel, LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelId, LanguageModelImage, LanguageModelProviderId, LanguageModelRegistry, LanguageModelRequest, @@ -2466,13 +2467,14 @@ impl Thread { } // When there are duplicate tool names, disambiguate by prefixing them - // with the server ID. In the rare case there isn't enough space for the - // disambiguated tool name, keep only the last tool with this name. + // with the server ID (converted to snake_case for API compatibility). + // In the rare case there isn't enough space for the disambiguated tool + // name, keep only the last tool with this name. for (server_id, tool_name, tool) in context_server_tools { if duplicate_tool_names.contains(&tool_name) { let available = MAX_TOOL_NAME_LENGTH.saturating_sub(tool_name.len()); if available >= 2 { - let mut disambiguated = server_id.0.to_string(); + let mut disambiguated = server_id.0.to_snake_case(); disambiguated.truncate(available - 1); disambiguated.push('_'); disambiguated.push_str(&tool_name);