mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat: disable capabilities by model provider (#19442)
## Why Unsupported features must fail closed and Codex must not expose OpenAI-hosted fallback paths when the active provider cannot support them. In practice, Bedrock should not surface app connectors, MCP servers, tool search/suggestions, image generation, web search, or JS REPL until those paths are explicitly supported for that provider. This PR moves that decision into provider-owned capability metadata instead of scattering Bedrock-specific checks across callers. ## What changed - Adds `ProviderCapabilities` to `codex-model-provider`, with default support for existing providers and a Bedrock override that disables unsupported launch surfaces. - Adds `ToolCapabilityBounds` to `codex-tools` so provider capability limits can clamp otherwise-enabled tool config. - Applies capability bounds when building session and review-thread tool config. - Routes MCP/app connector configuration through `McpManager::mcp_config`, which filters configured MCP servers and app connectors based on the active provider. - Updates app-server MCP list/read paths to use the filtered MCP config. - Adds coverage for default provider capabilities, Bedrock disabled capabilities, and optional tool-surface clamping. ## Testing built locally and verified that bedrock responses api now return without errors calling unsupported tools.
This commit is contained in:
@@ -20,6 +20,7 @@ use codex_tools::AdditionalProperties;
|
||||
use codex_tools::ConfiguredToolSpec;
|
||||
use codex_tools::DiscoverableTool;
|
||||
use codex_tools::JsonSchema;
|
||||
use codex_tools::LoadableToolSpec;
|
||||
use codex_tools::ResponsesApiNamespaceTool;
|
||||
use codex_tools::ResponsesApiTool;
|
||||
use codex_tools::ShellCommandBackendConfig;
|
||||
@@ -40,6 +41,7 @@ use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::*;
|
||||
use crate::tools::tool_search_entry::build_tool_search_entries_for_config;
|
||||
|
||||
fn mcp_tool(name: &str, description: &str, input_schema: serde_json::Value) -> rmcp::model::Tool {
|
||||
rmcp::model::Tool {
|
||||
@@ -1030,6 +1032,62 @@ async fn search_tool_registers_namespaced_mcp_tool_aliases() {
|
||||
assert!(registry.has_handler(&mcp_alias));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn tool_search_entries_skip_namespace_outputs_when_namespace_tools_are_disabled() {
|
||||
let model_info = search_capable_model_info().await;
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::ToolSearch);
|
||||
let available_models = Vec::new();
|
||||
let mut tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
available_models: &available_models,
|
||||
features: &features,
|
||||
image_generation_tool_auth_allowed: true,
|
||||
web_search_mode: Some(WebSearchMode::Cached),
|
||||
session_source: SessionSource::Cli,
|
||||
permission_profile: &PermissionProfile::Disabled,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
tools_config.namespace_tools = false;
|
||||
let mcp_tools = HashMap::from([(
|
||||
"mcp__test_server__echo".to_string(),
|
||||
mcp_tool_info(mcp_tool(
|
||||
"echo",
|
||||
"Echo",
|
||||
serde_json::json!({"type": "object"}),
|
||||
)),
|
||||
)]);
|
||||
let dynamic_tools = vec![
|
||||
DynamicToolSpec {
|
||||
namespace: Some("codex_app".to_string()),
|
||||
name: "automation_update".to_string(),
|
||||
description: "Create or update automations.".to_string(),
|
||||
input_schema: serde_json::json!({"type": "object", "properties": {}}),
|
||||
defer_loading: true,
|
||||
},
|
||||
DynamicToolSpec {
|
||||
namespace: None,
|
||||
name: "plain_dynamic".to_string(),
|
||||
description: "Plain dynamic tool.".to_string(),
|
||||
input_schema: serde_json::json!({"type": "object", "properties": {}}),
|
||||
defer_loading: true,
|
||||
},
|
||||
];
|
||||
|
||||
let entries =
|
||||
build_tool_search_entries_for_config(&tools_config, Some(&mcp_tools), &dynamic_tools);
|
||||
let outputs = entries
|
||||
.into_iter()
|
||||
.map(|entry| entry.output)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(outputs.len(), 1);
|
||||
match &outputs[0] {
|
||||
LoadableToolSpec::Function(tool) => assert_eq!(tool.name, "plain_dynamic"),
|
||||
LoadableToolSpec::Namespace(_) => panic!("namespace tool_search output should be hidden"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn direct_mcp_tools_register_namespaced_handlers() {
|
||||
let config = test_config().await;
|
||||
|
||||
Reference in New Issue
Block a user