Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5df7002
feat(image): consolidate image-tool and transform updates
TingtingZhou7 Apr 9, 2026
e02306f
feat(mcp): wire request headers into response->MCP client setup
TingtingZhou7 Apr 9, 2026
9d875c7
feat(mcp): forward configured request headers for builtin MCP routing
TingtingZhou7 Apr 9, 2026
515d2f2
fix(mcp): preserve builtin image tool format on forwarded dynamic rou…
TingtingZhou7 Apr 9, 2026
8086643
fix(mcp): preserve builtin routing metadata and suppress mcp_list_too…
TingtingZhou7 Apr 9, 2026
768f121
Merge branch 'lightseekorg:main' into support-compartment-id
TingtingZhou7 Apr 9, 2026
6782a85
revert(mcp): restore tool_loop behavior from 515d2f2e
TingtingZhou7 Apr 9, 2026
547d323
refactor(mcp): simplify dynamic tool config and builtin routing handling
TingtingZhou7 Apr 9, 2026
5212b8d
fix(mcp): restore dynamic response-format mapping and stabilize built…
TingtingZhou7 Apr 9, 2026
50179ac
fix(mcp): keep dynamic builtin override semantics aligned with static…
TingtingZhou7 Apr 9, 2026
13a6cff
fix(mcp): only use dynamic routing when forwarded headers are resolved
TingtingZhou7 Apr 9, 2026
35fcb16
fix(mcp): dedupe builtin routing by server_key only
TingtingZhou7 Apr 9, 2026
db00e94
fix(mcp): restore direct MCP header handling and clarify forwarding r…
TingtingZhou7 Apr 9, 2026
981f136
chore(mcp): remove inline dedupe rationale comment
TingtingZhou7 Apr 9, 2026
4e2089c
docs(mcp): clarify HTTP-only header forwarding transport check
TingtingZhou7 Apr 9, 2026
93cd4e1
refactor(mcp): reuse static config-apply logic for dynamic server ent…
TingtingZhou7 Apr 9, 2026
9f0dbb7
chore(mcp): add debug logs for static vs dynamic client path
TingtingZhou7 Apr 9, 2026
000610b
refactor(mcp): unify builtin route lookup and remove find_server_config
TingtingZhou7 Apr 9, 2026
a6cc177
style: run rustfmt for builtin lookup refactor changes
TingtingZhou7 Apr 9, 2026
ae482d3
chore(mcp): remove temporary static/dynamic client path debug logs
TingtingZhou7 Apr 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 36 additions & 15 deletions crates/mcp/src/core/orchestrator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,14 @@ impl McpOrchestrator {

/// Apply tool configurations from server config (aliases, response formats, arg mappings).
fn apply_tool_configs(&self, config: &McpServerConfig) {
self.apply_tool_configs_for_server_key(&config.name, config);
}

/// Apply tool configurations for a specific inventory server key.
///
/// Static servers use `config.name` as the server key; dynamic servers use
/// URL-based server keys from the connection pool.
fn apply_tool_configs_for_server_key(&self, server_key: &str, config: &McpServerConfig) {
let Some(tools) = &config.tools else {
return;
};
Expand All @@ -469,11 +477,11 @@ impl McpOrchestrator {
// Check if the tool exists
if !self
.tool_inventory
.has_tool_qualified(&config.name, tool_name)
.has_tool_qualified(server_key, tool_name)
{
warn!(
"Tool config for '{}:{}' but tool not found on server",
config.name, tool_name
server_key, tool_name
);
continue;
}
Expand All @@ -498,30 +506,30 @@ impl McpOrchestrator {
if let Some(alias_name) = &tool_config.alias {
if let Err(e) = self.register_alias(
alias_name,
&config.name,
server_key,
tool_name,
arg_mapping,
response_format.clone(),
) {
warn!(
"Failed to register alias '{}' for '{}:{}': {}",
alias_name, config.name, tool_name, e
alias_name, server_key, tool_name, e
);
} else {
info!(
"Registered alias '{}' → '{}:{}' with format {:?}",
alias_name, config.name, tool_name, response_format
alias_name, server_key, tool_name, response_format
);
}
} else if response_format != ResponseFormat::Passthrough {
// No alias, but has custom response format - update the entry directly
if let Some(mut entry) = self.tool_inventory.get_entry(&config.name, tool_name) {
if let Some(mut entry) = self.tool_inventory.get_entry(server_key, tool_name) {
entry.response_format = response_format.clone();
entry.arg_mapping.clone_from(&arg_mapping);
self.tool_inventory.insert_entry(entry);
info!(
"Set response format {:?} for '{}:{}'",
response_format, config.name, tool_name
response_format, server_key, tool_name
);
}
}
Expand All @@ -534,6 +542,14 @@ impl McpOrchestrator {
/// corresponding tool should use the response format associated with the builtin type
/// (e.g., WebSearchPreview -> WebSearchCall) unless explicitly overridden in the tools config.
fn apply_builtin_response_format(&self, config: &McpServerConfig) {
self.apply_builtin_response_format_for_server_key(&config.name, config);
}

fn apply_builtin_response_format_for_server_key(
&self,
server_key: &str,
config: &McpServerConfig,
) {
let Some(builtin_type) = &config.builtin_type else {
return;
};
Expand All @@ -548,7 +564,7 @@ impl McpOrchestrator {

if has_explicit_config {
debug!(
server = %config.name,
server = %server_key,
tool = %tool_name,
"Builtin tool has explicit config, skipping auto-apply of response_format"
);
Expand All @@ -559,10 +575,10 @@ impl McpOrchestrator {

let updated = self
.tool_inventory
.update_entry(&config.name, tool_name, |entry| {
.update_entry(server_key, tool_name, |entry| {
if entry.response_format != response_format {
info!(
server = %config.name,
server = %server_key,
tool = %tool_name,
builtin_type = %builtin_type,
format = ?response_format,
Expand All @@ -574,7 +590,7 @@ impl McpOrchestrator {

if !updated {
warn!(
server = %config.name,
server = %server_key,
tool = %tool_name,
builtin_type = %builtin_type,
"Builtin tool not found on server"
Expand Down Expand Up @@ -832,14 +848,15 @@ impl McpOrchestrator {
/// - `server_key` - Internal identifier for the server (used for `call_tool`)
/// - `tool_name` - The MCP tool to call on that server
/// - `response_format` - The format to use for response transformation
/// - `server_config` - The matched MCP server config
///
/// Returns `None` if no server is configured for this built-in type.
///
/// # Example
///
/// ```ignore
/// // Check if web_search_preview is configured
/// if let Some((server_key, tool_name, format)) =
/// if let Some((server_key, tool_name, format, _server_cfg)) =
/// orchestrator.find_builtin_server(BuiltinToolType::WebSearchPreview)
/// {
/// // Route to MCP server
Expand All @@ -857,7 +874,7 @@ impl McpOrchestrator {
pub fn find_builtin_server(
&self,
builtin_type: BuiltinToolType,
) -> Option<(String, String, ResponseFormat)> {
) -> Option<(String, String, ResponseFormat, McpServerConfig)> {
// Helper to extract builtin info from a server config
let extract_builtin = |server_config: &McpServerConfig| {
if let (Some(cfg_type), Some(tool_name)) = (
Expand All @@ -877,6 +894,7 @@ impl McpOrchestrator {
server_config.name.clone(),
tool_name.clone(),
response_format,
server_config.clone(),
));
}
}
Expand Down Expand Up @@ -1585,6 +1603,9 @@ impl McpOrchestrator {
.with_category(ToolCategory::Dynamic);
inventory_clone.insert_entry(entry);
}

self.apply_tool_configs_for_server_key(&server_key, &config);
self.apply_builtin_response_format_for_server_key(&server_key, &config);
}
Err(e) => warn!("Failed to list tools from '{}': {}", server_key, e),
}
Expand Down Expand Up @@ -2475,7 +2496,7 @@ mod tests {
let result = orchestrator.find_builtin_server(BuiltinToolType::WebSearchPreview);
assert!(result.is_some());

let (server_key, tool_name, response_format) = result.unwrap();
let (server_key, tool_name, response_format, _) = result.unwrap();
assert_eq!(server_key, "brave");
assert_eq!(tool_name, "brave_web_search");
assert_eq!(response_format, ResponseFormat::WebSearchCall);
Expand Down Expand Up @@ -2543,7 +2564,7 @@ mod tests {
let result = orchestrator.find_builtin_server(BuiltinToolType::WebSearchPreview);
assert!(result.is_some());

let (server_key, tool_name, response_format) = result.unwrap();
let (server_key, tool_name, response_format, _) = result.unwrap();
assert_eq!(server_key, "custom-search");
assert_eq!(tool_name, "my_search");
// Should use the custom Passthrough format, not the default WebSearchCall
Expand Down
2 changes: 2 additions & 0 deletions model_gateway/src/routers/anthropic/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ impl RouterTrait for AnthropicRouter {
url: Some(server.url.clone()),
authorization: server.authorization_token.clone(),
headers: HashMap::new(),
builtin_type: None,
builtin_tool_name: None,
allowed_tools: toolset_allowed.get(&server.name).and_then(|v| v.clone()),
})
.collect();
Expand Down
2 changes: 1 addition & 1 deletion model_gateway/src/routers/grpc/common/responses/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub(crate) async fn ensure_mcp_connection(
}

if let Some(tools) = tools {
match ensure_request_mcp_client(mcp_orchestrator, tools).await {
match ensure_request_mcp_client(mcp_orchestrator, tools, None).await {
Some(mcp_servers) => {
return Ok((true, mcp_servers));
}
Expand Down
Loading