mirror of
https://github.com/openai/codex.git
synced 2026-10-20 00:18:49 +00:00
Allow cached catalogs to satisfy MCP startup readiness (#47935)
## Why Required MCP servers can block startup while establishing a live connection even when a usable tool catalog is already cached. ## What changed - Add per-server `startup_readiness = "catalog"` to expose cached tools while the connection starts, retaining `"connection"` as the default. - Allow an eligible cached catalog to satisfy required-server startup validation and model binding capture. Explicit server or plugin requirements still wait for a live connection, and tool execution still requires the current connection. - Check tool filters and model visibility when accepting cached catalogs, and preserve cached catalog revisions for binding reuse. - Serialize the new setting, expose it in the configuration schema, and trace startup readiness at validation and model binding gates. GitOrigin-RevId: a1cbdf1757ebb5824cffe2cc56408be49711c422
This commit is contained in:
@@ -424,6 +424,7 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -41,6 +41,7 @@ fn server(url: &str) -> McpServerConfig {
|
||||
environment_id: DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: true,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: true,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -62,6 +62,7 @@ use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use anyhow::bail;
|
||||
use codex_config::McpServerTransportConfig;
|
||||
use codex_config::McpStartupReadiness;
|
||||
use codex_diagnostics::Gauge;
|
||||
use codex_diagnostics::GaugeGuard;
|
||||
use codex_protocol::mcp::CallToolResult;
|
||||
@@ -161,6 +162,7 @@ impl Drop for McpServerConnection {
|
||||
struct McpServerView {
|
||||
connection: Arc<McpServerConnection>,
|
||||
protocol_mode: crate::McpProtocolMode,
|
||||
startup_readiness: McpStartupReadiness,
|
||||
metadata: McpServerMetadata,
|
||||
tool_filter: ToolFilter,
|
||||
tool_timeout: Option<Duration>,
|
||||
@@ -168,6 +170,28 @@ struct McpServerView {
|
||||
}
|
||||
|
||||
impl McpServerView {
|
||||
fn allows_cached_startup(&self) -> bool {
|
||||
self.startup_readiness == McpStartupReadiness::Catalog
|
||||
|| self.connection.startup_is_dormant()
|
||||
}
|
||||
|
||||
fn cached_startup_tools(&self, fallback: Option<Vec<ToolInfo>>) -> Option<Vec<ToolInfo>> {
|
||||
self.connection
|
||||
.client
|
||||
.cached_tools_or(fallback)
|
||||
.filter(|tools| self.accepts_cached_tools(tools))
|
||||
}
|
||||
|
||||
fn accepts_cached_tools(&self, tools: &[ToolInfo]) -> bool {
|
||||
self.connection.client.is_codex_apps_mcp_server
|
||||
|| match self.startup_readiness {
|
||||
McpStartupReadiness::Connection => !tools.is_empty(),
|
||||
McpStartupReadiness::Catalog => tools.iter().any(|tool| {
|
||||
self.tool_filter.allows(&tool.tool.name) && tool_is_model_visible(tool)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
async fn listed_tools(
|
||||
&self,
|
||||
tool_plugin_context: &ToolPluginContext,
|
||||
@@ -496,6 +520,7 @@ impl McpConnectionSet {
|
||||
McpServerView {
|
||||
connection,
|
||||
protocol_mode,
|
||||
startup_readiness: configured_config.startup_readiness,
|
||||
metadata,
|
||||
tool_filter: configured_tool_filter,
|
||||
tool_timeout: configured_tool_timeout,
|
||||
@@ -633,6 +658,7 @@ impl McpConnectionSet {
|
||||
_diagnostics_guard: LIVE_CONNECTIONS.track(),
|
||||
}),
|
||||
protocol_mode,
|
||||
startup_readiness: configured_config.startup_readiness,
|
||||
metadata,
|
||||
tool_filter: configured_tool_filter,
|
||||
tool_timeout: configured_tool_timeout,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use codex_protocol::protocol::McpStartupFailure;
|
||||
@@ -8,12 +10,13 @@ use super::McpConnectionSet;
|
||||
use crate::rmcp_client::StartupOutcomeError;
|
||||
|
||||
impl McpConnectionSet {
|
||||
/// Waits for every required server and reports their startup failures together.
|
||||
/// Waits for required servers unless their readiness policy accepts a cached catalog.
|
||||
///
|
||||
/// The manager must already be reachable through [`crate::McpRuntime`] so
|
||||
/// startup-time elicitation can resolve while validation waits.
|
||||
pub(crate) async fn validate_required_servers(&self) -> Result<()> {
|
||||
let failures = async {
|
||||
self.record_startup_readiness("required_startup", &[], &HashSet::new());
|
||||
let mut failures = Vec::new();
|
||||
for server_name in &self.required_servers {
|
||||
let Some(view) = self.servers.get(server_name) else {
|
||||
@@ -23,7 +26,8 @@ impl McpConnectionSet {
|
||||
});
|
||||
continue;
|
||||
};
|
||||
if view.connection.startup_is_dormant() && view.connection.client.has_cached_tools()
|
||||
if view.allows_cached_startup()
|
||||
&& view.cached_startup_tools(/*fallback*/ None).is_some()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::time::Instant;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use codex_config::McpStartupReadiness;
|
||||
use codex_connectors::ConnectorRuntimeFetchSource;
|
||||
use futures::future::join_all;
|
||||
use tracing::Instrument;
|
||||
@@ -59,14 +60,58 @@ pub fn tool_is_model_visible(tool: &ToolInfo) -> bool {
|
||||
.any(|target| target.as_str() == Some(MCP_UI_MODEL_VISIBILITY))
|
||||
}
|
||||
|
||||
/// Catalog identity within one published connection set, including dormant servers.
|
||||
/// Catalog identity within one published connection set, including cached declarations.
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub(crate) enum BindingCatalogRevision {
|
||||
Ready(ClientToolCatalogRevision),
|
||||
Dormant(u64),
|
||||
Cached(u64),
|
||||
}
|
||||
|
||||
impl McpConnectionSet {
|
||||
/// Observes opted-in servers at startup gates without waiting or cloning catalogs.
|
||||
pub(crate) fn record_startup_readiness(
|
||||
&self,
|
||||
phase: &'static str,
|
||||
required_servers: &[String],
|
||||
required_plugins: &HashSet<String>,
|
||||
) {
|
||||
if !tracing::enabled!(tracing::Level::INFO) {
|
||||
return;
|
||||
}
|
||||
for (server_name, view) in &self.servers {
|
||||
if view.startup_readiness != McpStartupReadiness::Catalog {
|
||||
continue;
|
||||
}
|
||||
let client = &view.connection.client;
|
||||
let startup_pending = !client.startup_complete.load(Ordering::Acquire);
|
||||
let cache_eligible = startup_pending
|
||||
&& client
|
||||
.tool_catalog_cache_context
|
||||
.as_ref()
|
||||
.is_some_and(|cache| {
|
||||
cache
|
||||
.current_revision_if(|tools| view.accepts_cached_tools(tools))
|
||||
.is_some()
|
||||
});
|
||||
let explicitly_required = required_servers.contains(server_name)
|
||||
|| (self.is_selected_plugin_mcp_server(server_name)
|
||||
&& self
|
||||
.plugin_id_for_mcp_server_name(server_name)
|
||||
.is_some_and(|plugin_id| required_plugins.contains(plugin_id)));
|
||||
tracing::info!(
|
||||
event.name = "mcp.startup_readiness",
|
||||
server_name = %server_name,
|
||||
phase,
|
||||
startup_pending,
|
||||
startup_readiness = "catalog",
|
||||
cache_eligible,
|
||||
required = self.required_servers.binary_search(server_name).is_ok(),
|
||||
explicitly_required,
|
||||
"MCP startup readiness observed"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn stable_catalog_revisions(
|
||||
&self,
|
||||
required_servers: &[String],
|
||||
@@ -74,15 +119,17 @@ impl McpConnectionSet {
|
||||
) -> Option<HashMap<String, BindingCatalogRevision>> {
|
||||
let mut revisions = HashMap::new();
|
||||
for (server_name, view) in &self.servers {
|
||||
if !view
|
||||
let startup_complete = view
|
||||
.connection
|
||||
.client
|
||||
.startup_complete
|
||||
.load(Ordering::Acquire)
|
||||
.load(Ordering::Acquire);
|
||||
if !startup_complete
|
||||
|| (view.startup_readiness == McpStartupReadiness::Catalog
|
||||
&& view.connection.client.ready_client().is_none())
|
||||
{
|
||||
// A cached catalog can remain stable without starting its server.
|
||||
// Explicit requirements must still start the server during binding capture.
|
||||
if !view.connection.startup_is_dormant()
|
||||
// Explicit requirements still wait for a live connection during capture.
|
||||
if !view.allows_cached_startup()
|
||||
|| view.connection.client.cancel_token.is_cancelled()
|
||||
|| required_servers
|
||||
.iter()
|
||||
@@ -99,10 +146,10 @@ impl McpConnectionSet {
|
||||
.client
|
||||
.tool_catalog_cache_context
|
||||
.as_ref()?
|
||||
.current_revision()?;
|
||||
.current_revision_if(|tools| view.accepts_cached_tools(tools))?;
|
||||
revisions.insert(
|
||||
server_name.clone(),
|
||||
BindingCatalogRevision::Dormant(revision),
|
||||
BindingCatalogRevision::Cached(revision),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -222,12 +269,10 @@ impl McpConnectionSet {
|
||||
{
|
||||
let required = self.required_servers.binary_search(server_name).is_ok();
|
||||
// Keep the catalog that lets us skip startup even if it expires during the wait.
|
||||
let cached_tools = view.connection.client.cached_tools().filter(|tools| {
|
||||
view.connection.client.is_codex_apps_mcp_server || !tools.is_empty()
|
||||
});
|
||||
let cached_tools = view.cached_startup_tools(/*fallback*/ None);
|
||||
let has_cached_tools = cached_tools.is_some();
|
||||
let must_wait_for_startup = (required
|
||||
&& (!view.connection.startup_is_dormant() || !has_cached_tools))
|
||||
&& (!view.allows_cached_startup() || !has_cached_tools))
|
||||
|| required_servers
|
||||
.iter()
|
||||
.any(|required| required == server_name)
|
||||
@@ -237,6 +282,7 @@ impl McpConnectionSet {
|
||||
.is_some_and(|plugin_id| required_plugins.contains(plugin_id)))
|
||||
|| (server_name == CODEX_APPS_MCP_SERVER_NAME && !has_cached_tools);
|
||||
if !must_wait_for_startup && has_cached_tools {
|
||||
trace!(server_name = %server_name, "using cached MCP catalog without waiting for startup");
|
||||
return (server_name, view, cached_tools);
|
||||
}
|
||||
if !must_wait_for_startup && optional_mcp_startup_grace.is_zero() {
|
||||
@@ -283,14 +329,30 @@ impl McpConnectionSet {
|
||||
}))
|
||||
.await;
|
||||
let server_results = join_all(server_snapshots.into_iter().map(|(server_name, view, cached_tools)| async move {
|
||||
let (client, server_tools) = if !view
|
||||
let startup_pending = !view
|
||||
.connection
|
||||
.client
|
||||
.startup_complete
|
||||
.load(Ordering::Acquire)
|
||||
.load(Ordering::Acquire);
|
||||
let accepts_cached_catalog = view.startup_readiness == McpStartupReadiness::Catalog;
|
||||
let cached_tools = if startup_pending
|
||||
|| (accepts_cached_catalog && view.connection.client.ready_client().is_none())
|
||||
{
|
||||
(None, view.connection.client.cached_tools_or(cached_tools)?)
|
||||
view.cached_startup_tools(cached_tools)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (client, server_tools) = if let Some(cached_tools) = cached_tools {
|
||||
(None, cached_tools)
|
||||
} else {
|
||||
// A server may opt out of caching after the first pass. Required catalog
|
||||
// readiness must then wait for discovery rather than silently omit its tools.
|
||||
if startup_pending
|
||||
&& !(accepts_cached_catalog
|
||||
&& self.required_servers.binary_search(server_name).is_ok())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
view.connection.client.reconnect_failed_startup().await;
|
||||
let Ok(mut client) = view.connection.client().await else {
|
||||
trace!(server_name = %server_name, "omitting MCP server without an exact ready client");
|
||||
|
||||
@@ -136,6 +136,7 @@ impl McpConnectionSet {
|
||||
McpServerView {
|
||||
tool_filter: ToolFilter::default(),
|
||||
protocol_mode: crate::McpProtocolMode::Legacy,
|
||||
startup_readiness: Default::default(),
|
||||
connection: Arc::new(McpServerConnection {
|
||||
identity: None,
|
||||
client,
|
||||
@@ -4668,6 +4669,7 @@ async fn no_local_runtime_fails_local_stdio_but_keeps_local_http_server() {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -4696,6 +4698,7 @@ async fn no_local_runtime_fails_local_stdio_but_keeps_local_http_server() {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -4807,6 +4810,7 @@ fn mcp_init_error_display_prompts_for_github_pat() {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -4967,6 +4971,7 @@ fn mcp_init_error_display_reports_generic_errors() {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -5046,6 +5051,7 @@ fn reusable_server_config(url: &str) -> McpServerConfig {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -5110,6 +5116,7 @@ async fn manager_with_reusable_ready_server(
|
||||
"docs".to_string(),
|
||||
McpServerView {
|
||||
protocol_mode: crate::McpProtocolMode::Legacy,
|
||||
startup_readiness: Default::default(),
|
||||
connection: Arc::new(McpServerConnection {
|
||||
identity: Some(reusable_server_identity("docs", config, runtime_context)),
|
||||
client: create_ready_async_managed_client(tools).await,
|
||||
@@ -5535,6 +5542,7 @@ async fn reconciliation_reuses_connection_without_relisting_regular_tools() -> a
|
||||
"docs".to_string(),
|
||||
McpServerView {
|
||||
protocol_mode: crate::McpProtocolMode::Legacy,
|
||||
startup_readiness: Default::default(),
|
||||
connection: Arc::new(McpServerConnection {
|
||||
identity: Some(reusable_server_identity("docs", &config, &runtime_context)),
|
||||
client: AsyncManagedClient {
|
||||
|
||||
@@ -655,6 +655,7 @@ fn mcp_server_config_for_url(
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -592,6 +592,7 @@ async fn effective_mcp_servers_preserve_runtime_servers() {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -620,6 +621,7 @@ async fn effective_mcp_servers_preserve_runtime_servers() {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -535,6 +535,7 @@ fn stdio_server(
|
||||
environment_id: environment_id.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -571,6 +572,7 @@ fn declared_placement_preserves_local_plugin_normalization() {
|
||||
environment_id: DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -891,6 +893,7 @@ fn local_environment_placement_preserves_http_env_references() {
|
||||
environment_id: DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -417,12 +417,13 @@ impl McpRuntime {
|
||||
required_servers: &[String],
|
||||
required_plugins: &HashSet<String>,
|
||||
) -> Option<Arc<McpBinding>> {
|
||||
Self::binding_from_published_runtime(
|
||||
self.current.load_full(),
|
||||
let current = self.current.load_full();
|
||||
current.connections.record_startup_readiness(
|
||||
"model_binding",
|
||||
required_servers,
|
||||
required_plugins,
|
||||
)
|
||||
.await
|
||||
);
|
||||
Self::binding_from_published_runtime(current, required_servers, required_plugins).await
|
||||
}
|
||||
|
||||
async fn binding_from_published_runtime(
|
||||
@@ -948,6 +949,7 @@ mod tests {
|
||||
environment_id: environment_id.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -116,18 +116,22 @@ impl Default for ToolCatalogCacheEntry {
|
||||
|
||||
impl McpToolCatalogCacheContext {
|
||||
pub(crate) fn has_tools(&self) -> bool {
|
||||
self.current_revision().is_some()
|
||||
self.current_revision_if(|_| true).is_some()
|
||||
}
|
||||
|
||||
/// Identifies the current usable catalog without cloning its tool definitions.
|
||||
/// The entry is fixed for a connection; accepted publications advance its revision.
|
||||
pub(crate) fn current_revision(&self) -> Option<u64> {
|
||||
/// Checks eligibility and reads the revision under one lock without cloning tools.
|
||||
/// The predicate borrows the current catalog while the cache entry is locked.
|
||||
pub(crate) fn current_revision_if(
|
||||
&self,
|
||||
accepts_tools: impl FnOnce(&[ToolInfo]) -> bool,
|
||||
) -> Option<u64> {
|
||||
let state = lock_unpoisoned(&self.entry.state);
|
||||
let snapshot = state.snapshot.as_ref()?;
|
||||
(!state.disabled_by_server
|
||||
&& !snapshot.tools.is_empty()
|
||||
&& snapshot.published_at.elapsed() <= TOOL_CATALOG_CACHE_TTL)
|
||||
.then_some(state.last_accepted_generation)
|
||||
&& snapshot.published_at.elapsed() <= TOOL_CATALOG_CACHE_TTL
|
||||
&& accepts_tools(&snapshot.tools))
|
||||
.then_some(state.last_accepted_generation)
|
||||
}
|
||||
|
||||
pub(crate) fn optional_startup_deadline(
|
||||
|
||||
@@ -176,6 +176,7 @@ pub use mcp_types::McpServerEnvVar;
|
||||
pub use mcp_types::McpServerOAuthConfig;
|
||||
pub use mcp_types::McpServerToolConfig;
|
||||
pub use mcp_types::McpServerTransportConfig;
|
||||
pub use mcp_types::McpStartupReadiness;
|
||||
pub use mcp_types::RawMcpServerConfig;
|
||||
pub use merge::ShellEnvironmentPolicyFilterRepresentation;
|
||||
pub use merge::is_structured_feature_path;
|
||||
|
||||
@@ -17,6 +17,7 @@ fn stdio_server(command: &str, args: &[&str]) -> McpServerConfig {
|
||||
environment_id: crate::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -213,10 +213,20 @@ pub enum McpServerAuth {
|
||||
EmaAuth,
|
||||
}
|
||||
|
||||
impl McpServerAuth {
|
||||
fn is_default(&self) -> bool {
|
||||
self == &Self::default()
|
||||
}
|
||||
/// Readiness needed before startup can expose this server's tools to the model.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum McpStartupReadiness {
|
||||
/// Use the live connection to determine startup readiness.
|
||||
#[default]
|
||||
Connection,
|
||||
/// Allow a valid cached tool catalog while the live connection starts.
|
||||
/// Tool execution still requires the current connection.
|
||||
Catalog,
|
||||
}
|
||||
|
||||
fn is_default<T: Default + PartialEq>(value: &T) -> bool {
|
||||
value == &T::default()
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone, PartialEq)]
|
||||
@@ -225,7 +235,7 @@ pub struct McpServerConfig {
|
||||
pub transport: McpServerTransportConfig,
|
||||
|
||||
/// Authentication flow, including an explicit no-fallback EMA mode.
|
||||
#[serde(default, skip_serializing_if = "McpServerAuth::is_default")]
|
||||
#[serde(default, skip_serializing_if = "is_default")]
|
||||
pub auth: McpServerAuth,
|
||||
|
||||
/// Effective environment id for where Codex should start this MCP server.
|
||||
@@ -236,9 +246,15 @@ pub struct McpServerConfig {
|
||||
pub enabled: bool,
|
||||
|
||||
/// When `true`, `codex exec` exits with an error if this MCP server fails to initialize.
|
||||
/// With `startup_readiness = "catalog"`, a valid cached catalog can satisfy startup;
|
||||
/// connection failures are then reported when a tool is invoked.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub required: bool,
|
||||
|
||||
/// Whether startup requires a live connection or can use a valid cached tool catalog.
|
||||
#[serde(default, skip_serializing_if = "is_default")]
|
||||
pub startup_readiness: McpStartupReadiness,
|
||||
|
||||
/// When `true`, every tool from this server is advertised as safe for parallel tool calls.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub supports_parallel_tool_calls: bool,
|
||||
@@ -391,6 +407,9 @@ pub struct RawMcpServerConfig {
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub required: Option<bool>,
|
||||
/// Whether startup requires a live connection or can use a valid cached tool catalog.
|
||||
#[serde(default)]
|
||||
pub startup_readiness: Option<McpStartupReadiness>,
|
||||
#[serde(default)]
|
||||
pub supports_parallel_tool_calls: Option<bool>,
|
||||
#[serde(default)]
|
||||
@@ -437,6 +456,7 @@ impl TryFrom<RawMcpServerConfig> for McpServerConfig {
|
||||
tool_timeout_sec,
|
||||
enabled,
|
||||
required,
|
||||
startup_readiness,
|
||||
supports_parallel_tool_calls,
|
||||
omit_tools_from,
|
||||
default_tools_approval_mode,
|
||||
@@ -556,6 +576,7 @@ impl TryFrom<RawMcpServerConfig> for McpServerConfig {
|
||||
tool_timeout_sec,
|
||||
enabled: enabled.unwrap_or_else(default_enabled),
|
||||
required: required.unwrap_or_default(),
|
||||
startup_readiness: startup_readiness.unwrap_or_default(),
|
||||
supports_parallel_tool_calls: supports_parallel_tool_calls.unwrap_or_default(),
|
||||
omit_tools_from,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -640,6 +640,7 @@ fn deserialize_ignores_unknown_server_fields() {
|
||||
environment_id: crate::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -13,6 +13,7 @@ pub use crate::mcp_types::McpServerEnvVar;
|
||||
pub use crate::mcp_types::McpServerOAuthConfig;
|
||||
pub use crate::mcp_types::McpServerToolConfig;
|
||||
pub use crate::mcp_types::McpServerTransportConfig;
|
||||
pub use crate::mcp_types::McpStartupReadiness;
|
||||
pub use crate::mcp_types::RawMcpServerConfig;
|
||||
pub use crate::shell_environment_policy::ShellEnvironmentPolicyToml;
|
||||
pub use codex_protocol::config_types::AltScreenMode;
|
||||
|
||||
@@ -981,6 +981,7 @@ async fn load_plugins_loads_default_skills_and_mcp_servers() {
|
||||
environment_id: "local".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -1083,6 +1084,7 @@ enabled = true
|
||||
environment_id: "local".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -2186,6 +2188,7 @@ async fn load_plugins_uses_manifest_configured_component_paths() {
|
||||
environment_id: "local".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -2525,6 +2528,7 @@ async fn load_plugins_ignores_manifest_component_paths_without_dot_slash() {
|
||||
environment_id: "local".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -2780,6 +2784,7 @@ fn capability_index_filters_inactive_and_zero_capability_plugins() {
|
||||
environment_id: "local".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -2393,6 +2393,25 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"McpStartupReadiness": {
|
||||
"description": "Readiness needed before startup can expose this server's tools to the model.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Use the live connection to determine startup readiness.",
|
||||
"enum": [
|
||||
"connection"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Allow a valid cached tool catalog while the live connection starts. Tool execution still requires the current connection.",
|
||||
"enum": [
|
||||
"catalog"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"MemoriesToml": {
|
||||
"additionalProperties": false,
|
||||
"description": "Memories settings loaded from config.toml.",
|
||||
@@ -3659,6 +3678,15 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"startup_readiness": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpStartupReadiness"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Whether startup requires a live connection or can use a valid cached tool catalog."
|
||||
},
|
||||
"startup_timeout_ms": {
|
||||
"default": null,
|
||||
"format": "uint64",
|
||||
|
||||
@@ -141,6 +141,7 @@ fn stdio_mcp_with_args(command: &str, args: &[&str]) -> McpServerConfig {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -169,6 +170,7 @@ fn http_mcp(url: &str) -> McpServerConfig {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -6889,6 +6891,7 @@ async fn replace_mcp_servers_round_trips_entries() -> anyhow::Result<()> {
|
||||
environment_id: "remote".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7324,6 +7327,7 @@ async fn replace_mcp_servers_serializes_env_sorted() -> anyhow::Result<()> {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7402,6 +7406,7 @@ async fn replace_mcp_servers_serializes_env_vars() -> anyhow::Result<()> {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7465,6 +7470,7 @@ async fn replace_mcp_servers_serializes_sourced_env_vars() -> anyhow::Result<()>
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7519,6 +7525,7 @@ async fn replace_mcp_servers_serializes_cwd() -> anyhow::Result<()> {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7576,6 +7583,7 @@ async fn replace_mcp_servers_streamable_http_serializes_bearer_token() -> anyhow
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7650,6 +7658,7 @@ async fn replace_mcp_servers_streamable_http_serializes_custom_headers() -> anyh
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7735,6 +7744,7 @@ async fn replace_mcp_servers_streamable_http_removes_optional_sections() -> anyh
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7773,6 +7783,7 @@ async fn replace_mcp_servers_streamable_http_removes_optional_sections() -> anyh
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7847,6 +7858,7 @@ async fn replace_mcp_servers_streamable_http_isolates_headers_between_servers()
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7875,6 +7887,7 @@ async fn replace_mcp_servers_streamable_http_isolates_headers_between_servers()
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -7965,6 +7978,7 @@ async fn replace_mcp_servers_serializes_disabled_flag() -> anyhow::Result<()> {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: false,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -8017,6 +8031,7 @@ async fn replace_mcp_servers_serializes_required_flag() -> anyhow::Result<()> {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: true,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -8069,6 +8084,7 @@ async fn replace_mcp_servers_serializes_tool_filters() -> anyhow::Result<()> {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -8126,6 +8142,7 @@ async fn replace_mcp_servers_streamable_http_serializes_oauth_resource() -> anyh
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -5,6 +5,7 @@ use codex_config::types::McpServerConfig;
|
||||
use codex_config::types::McpServerEnvVar;
|
||||
use codex_config::types::McpServerToolConfig;
|
||||
use codex_config::types::McpServerTransportConfig;
|
||||
use codex_config::types::McpStartupReadiness;
|
||||
use codex_config::types::ToolSuggestDisabledTool;
|
||||
use codex_config::types::ToolSuggestDiscoverableType;
|
||||
use toml_edit::Array as TomlArray;
|
||||
@@ -115,6 +116,10 @@ fn serialize_mcp_server_table(config: &McpServerConfig) -> anyhow::Result<TomlTa
|
||||
if config.required {
|
||||
entry["required"] = value(true);
|
||||
}
|
||||
match config.startup_readiness {
|
||||
McpStartupReadiness::Connection => {}
|
||||
McpStartupReadiness::Catalog => entry["startup_readiness"] = value("catalog"),
|
||||
}
|
||||
if config.supports_parallel_tool_calls {
|
||||
entry["supports_parallel_tool_calls"] = value(true);
|
||||
}
|
||||
|
||||
@@ -1080,6 +1080,7 @@ fn blocking_replace_mcp_servers_round_trips() {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: true,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -1113,6 +1114,7 @@ fn blocking_replace_mcp_servers_round_trips() {
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: false,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -1205,6 +1207,7 @@ fn blocking_replace_mcp_servers_serializes_tool_approval_overrides(output_token_
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -1281,6 +1284,7 @@ foo = { command = "cmd" }
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -1333,6 +1337,7 @@ foo = { command = "cmd" } # keep me
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: false,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -1384,6 +1389,7 @@ foo = { command = "cmd", args = ["--flag"] } # keep me
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -1436,6 +1442,7 @@ foo = { command = "cmd" }
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: false,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -416,6 +416,7 @@ fn mcp_dependency_to_server_config(
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -455,6 +456,7 @@ fn mcp_dependency_to_server_config(
|
||||
environment_id: codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -337,7 +337,11 @@ impl Session {
|
||||
self.mcp_refresh.invalidate();
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "mcp.runtime.resolve_for_step", skip_all)]
|
||||
#[tracing::instrument(
|
||||
name = "mcp.runtime.resolve_for_step",
|
||||
skip_all,
|
||||
fields(turn.id = %turn_context.sub_id)
|
||||
)]
|
||||
pub(crate) async fn mcp_runtime_for_step(
|
||||
self: &Arc<Self>,
|
||||
turn_context: &TurnContext,
|
||||
|
||||
@@ -9676,6 +9676,7 @@ async fn step_context_keeps_its_mcp_runtime_for_tools() -> anyhow::Result<()> {
|
||||
environment_id: DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -732,6 +732,7 @@ async fn run_code_mode_turn_with_rmcp_config(
|
||||
environment_id: "local".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -291,6 +291,7 @@ fn insert_rmcp_test_server(
|
||||
environment_id,
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -45,6 +45,7 @@ async fn refresh_keeps_superseded_mcp_server_alive_for_in_flight_calls() -> anyh
|
||||
environment_id: DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -135,6 +135,7 @@ async fn local_mcp_startup_and_refresh_use_configured_http_client() -> Result<()
|
||||
environment_id: DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -377,6 +377,7 @@ fn insert_mcp_server(
|
||||
environment_id: options.environment_id,
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: options.supports_parallel_tool_calls,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -1326,6 +1326,7 @@ async fn tool_search_indexes_only_enabled_non_app_mcp_tools() -> Result<()> {
|
||||
environment_id,
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: Some(Duration::from_secs(10)),
|
||||
tool_timeout_sec: None,
|
||||
@@ -1459,6 +1460,7 @@ async fn tool_search_surfaced_mcp_tool_errors_are_returned_to_model() -> Result<
|
||||
environment_id,
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: Some(Duration::from_secs(10)),
|
||||
tool_timeout_sec: None,
|
||||
@@ -1608,6 +1610,7 @@ async fn tool_search_uses_non_app_mcp_server_instructions_as_namespace_descripti
|
||||
environment_id,
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
disabled_reason: None,
|
||||
startup_timeout_sec: Some(Duration::from_secs(10)),
|
||||
tool_timeout_sec: None,
|
||||
|
||||
@@ -882,6 +882,7 @@ async fn mcp_call_marks_thread_memory_mode_polluted_when_configured() -> Result<
|
||||
environment_id: "local".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -772,6 +772,7 @@ async fn token_budget_context_injects_plain_thread_hint_text() -> Result<()> {
|
||||
environment_id: "local".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -413,6 +413,7 @@ async fn mcp_tool_call_output_exceeds_limit_truncated_for_model() -> Result<()>
|
||||
environment_id: "local".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -516,6 +517,7 @@ async fn mcp_image_output_preserves_image_and_no_text_summary() -> Result<()> {
|
||||
environment_id: "local".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
@@ -195,6 +195,7 @@ async fn reads_declared_config_only_through_executor_file_system() {
|
||||
environment_id: "executor-test".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -223,6 +224,7 @@ async fn reads_declared_config_only_through_executor_file_system() {
|
||||
environment_id: "executor-test".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
@@ -281,6 +283,7 @@ async fn reads_manifest_object_config_without_executor_file_system_access() {
|
||||
environment_id: "executor-test".to_string(),
|
||||
enabled: true,
|
||||
required: false,
|
||||
startup_readiness: Default::default(),
|
||||
supports_parallel_tool_calls: false,
|
||||
omit_tools_from: None,
|
||||
disabled_reason: None,
|
||||
|
||||
Reference in New Issue
Block a user