This commit is contained in:
Matthew Zeng
2026-03-31 23:17:03 -04:00
parent 1e376a90d3
commit 1c7bf9d35a
10 changed files with 60 additions and 89 deletions
@@ -1213,12 +1213,16 @@
"server": {
"type": "string"
},
"threadId": {
"type": "string"
},
"uri": {
"type": "string"
}
},
"required": [
"server",
"threadId",
"uri"
],
"type": "object"
@@ -8785,12 +8785,16 @@
"server": {
"type": "string"
},
"threadId": {
"type": "string"
},
"uri": {
"type": "string"
}
},
"required": [
"server",
"threadId",
"uri"
],
"title": "McpResourceReadParams",
@@ -5599,12 +5599,16 @@
"server": {
"type": "string"
},
"threadId": {
"type": "string"
},
"uri": {
"type": "string"
}
},
"required": [
"server",
"threadId",
"uri"
],
"title": "McpResourceReadParams",
@@ -4,12 +4,16 @@
"server": {
"type": "string"
},
"threadId": {
"type": "string"
},
"uri": {
"type": "string"
}
},
"required": [
"server",
"threadId",
"uri"
],
"title": "McpResourceReadParams",
@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type McpResourceReadParams = { server: string, uri: string, };
export type McpResourceReadParams = { threadId: string, server: string, uri: string, };
@@ -1972,6 +1972,7 @@ pub struct ListMcpServerStatusResponse {
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct McpResourceReadParams {
pub thread_id: String,
pub server: String,
pub uri: String,
}
+1 -1
View File
@@ -187,7 +187,7 @@ Example with notification opt-out:
- `tool/requestUserInput` — prompt the user with 1–3 short questions for a tool call and return their answers (experimental).
- `config/mcpServer/reload` — reload MCP server config from disk and queue a refresh for loaded threads (applied on each thread's next active turn); returns `{}`. Use this after editing `config.toml` without restarting the server.
- `mcpServerStatus/list` — enumerate configured MCP servers with their tools, resources, resource templates, and auth status; supports cursor+limit pagination.
- `mcpResource/read` — read a resource from a configured MCP server by `server` and `uri`, returning the resource `contents`.
- `mcpResource/read` — read a resource from a thread's configured MCP server by `threadId`, `server`, and `uri`, returning the resource `contents`.
- `windowsSandbox/setupStart` — start Windows sandbox setup for the selected mode (`elevated` or `unelevated`); accepts an optional absolute `cwd` to target setup for a specific workspace, returns `{ started: true }` immediately, and later emits `windowsSandbox/setupCompleted`.
- `feedback/upload` — submit a feedback report (classification + optional reason/logs, conversation_id, and optional `extraLogFiles` attachments array); returns the tracking thread id.
- `config/read` — fetch the effective config on disk after resolving config layering.
@@ -224,7 +224,6 @@ use codex_core::mcp::auth::discover_supported_scopes;
use codex_core::mcp::auth::resolve_oauth_scopes;
use codex_core::mcp::collect_mcp_snapshot;
use codex_core::mcp::group_tools_by_server;
use codex_core::mcp::read_mcp_resource;
use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig;
use codex_core::parse_cursor;
use codex_core::plugins::MarketplaceError;
@@ -5141,8 +5140,8 @@ impl CodexMessageProcessor {
params: McpResourceReadParams,
) {
let outgoing = Arc::clone(&self.outgoing);
let config = match self.load_latest_config(/*fallback_cwd*/ None).await {
Ok(config) => config,
let (_, thread) = match self.load_thread(&params.thread_id).await {
Ok(thread) => thread,
Err(error) => {
self.outgoing.send_error(request_id, error).await;
return;
@@ -5150,7 +5149,7 @@ impl CodexMessageProcessor {
};
tokio::spawn(async move {
let result = read_mcp_resource(&config, &params.server, &params.uri).await;
let result = thread.read_mcp_resource(&params.server, &params.uri).await;
match result {
Ok(result) => match serde_json::from_value::<McpResourceReadResponse>(result) {
Ok(response) => {
+21
View File
@@ -22,6 +22,7 @@ use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::TokenUsage;
use codex_protocol::protocol::W3cTraceContext;
use codex_protocol::user_input::UserInput;
use rmcp::model::ReadResourceRequestParams;
use std::path::PathBuf;
use tokio::sync::Mutex;
use tokio::sync::watch;
@@ -198,6 +199,26 @@ impl CodexThread {
self.codex.thread_config_snapshot().await
}
pub async fn read_mcp_resource(
&self,
server: &str,
uri: &str,
) -> anyhow::Result<serde_json::Value> {
let result = self
.codex
.session
.read_resource(
server,
ReadResourceRequestParams {
meta: None,
uri: uri.to_string(),
},
)
.await?;
Ok(serde_json::to_value(result)?)
}
pub fn enabled(&self, feature: Feature) -> bool {
self.codex.enabled(feature)
}
+17 -83
View File
@@ -14,20 +14,16 @@ use codex_protocol::mcp::ResourceTemplate;
use codex_protocol::mcp::Tool;
use codex_protocol::protocol::McpListToolsResponseEvent;
use codex_protocol::protocol::SandboxPolicy;
use rmcp::model::ReadResourceRequestParams;
use serde_json::Value;
use tokio_util::sync::CancellationToken;
use crate::AuthManager;
use crate::CodexAuth;
use crate::config::Config;
use crate::config::types::McpServerConfig;
use crate::config::types::McpServerTransportConfig;
use crate::mcp::auth::McpAuthStatusEntry;
use crate::mcp::auth::compute_auth_statuses;
use crate::mcp_connection_manager::McpConnectionManager;
use crate::mcp_connection_manager::SandboxState;
use crate::mcp_connection_manager::ToolInfo;
use crate::mcp_connection_manager::codex_apps_tools_cache_key;
use crate::plugins::PluginCapabilitySummary;
use crate::plugins::PluginsManager;
@@ -281,52 +277,6 @@ fn effective_mcp_servers(
}
pub async fn collect_mcp_snapshot(config: &Config) -> McpListToolsResponseEvent {
let (mcp_connection_manager, cancel_token, auth_status_entries) =
new_mcp_connection_manager(config).await;
if !mcp_connection_manager.has_servers() {
cancel_token.cancel();
return McpListToolsResponseEvent {
tools: HashMap::new(),
resources: HashMap::new(),
resource_templates: HashMap::new(),
auth_statuses: HashMap::new(),
};
}
let snapshot =
collect_mcp_snapshot_from_manager(&mcp_connection_manager, auth_status_entries).await;
cancel_token.cancel();
snapshot
}
pub async fn read_mcp_resource(config: &Config, server: &str, uri: &str) -> anyhow::Result<Value> {
let (mcp_connection_manager, cancel_token, _auth_status_entries) =
new_mcp_connection_manager(config).await;
let result = mcp_connection_manager
.read_resource(
server,
ReadResourceRequestParams {
meta: None,
uri: uri.to_string(),
},
)
.await;
cancel_token.cancel();
Ok(serde_json::to_value(result?)?)
}
async fn new_mcp_connection_manager(
config: &Config,
) -> (
McpConnectionManager,
CancellationToken,
HashMap<String, McpAuthStatusEntry>,
) {
let auth_manager = AuthManager::shared(
config.codex_home.clone(),
/*enable_codex_api_key_env*/ false,
@@ -337,13 +287,12 @@ async fn new_mcp_connection_manager(
let mcp_servers = mcp_manager.effective_servers(config, auth.as_ref());
let tool_plugin_provenance = mcp_manager.tool_plugin_provenance(config);
if mcp_servers.is_empty() {
let cancel_token = CancellationToken::new();
cancel_token.cancel();
return (
McpConnectionManager::new_uninitialized(&config.permissions.approval_policy),
cancel_token,
HashMap::new(),
);
return McpListToolsResponseEvent {
tools: HashMap::new(),
resources: HashMap::new(),
resource_templates: HashMap::new(),
auth_statuses: HashMap::new(),
};
}
let auth_status_entries =
@@ -352,6 +301,7 @@ async fn new_mcp_connection_manager(
let (tx_event, rx_event) = unbounded();
drop(rx_event);
// Use ReadOnly sandbox policy for MCP snapshot collection (safest default)
let sandbox_state = SandboxState {
sandbox_policy: SandboxPolicy::new_read_only_policy(),
codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(),
@@ -372,7 +322,12 @@ async fn new_mcp_connection_manager(
)
.await;
(mcp_connection_manager, cancel_token, auth_status_entries)
let snapshot =
collect_mcp_snapshot_from_manager(&mcp_connection_manager, auth_status_entries).await;
cancel_token.cancel();
snapshot
}
pub fn split_qualified_tool_name(qualified_name: &str) -> Option<(String, String)> {
@@ -404,21 +359,6 @@ pub fn group_tools_by_server(
grouped
}
fn get_mcp_snapshot_tool_name(tool: &ToolInfo) -> String {
if tool.server_name != CODEX_APPS_MCP_SERVER_NAME {
format!(
"{}{}{}{}{}",
MCP_TOOL_NAME_PREFIX,
MCP_TOOL_NAME_DELIMITER,
tool.server_name,
MCP_TOOL_NAME_DELIMITER,
tool.tool_name
)
} else {
format!("{}{}", tool.tool_namespace, tool.tool_name)
}
}
pub(crate) async fn collect_mcp_snapshot_from_manager(
mcp_connection_manager: &McpConnectionManager,
auth_status_entries: HashMap<String, crate::mcp::auth::McpAuthStatusEntry>,
@@ -436,22 +376,16 @@ pub(crate) async fn collect_mcp_snapshot_from_manager(
let tools = tools
.into_iter()
.filter_map(|(_name, tool)| match serde_json::to_value(&tool.tool) {
.filter_map(|(name, tool)| match serde_json::to_value(tool.tool) {
Ok(value) => match Tool::from_mcp_value(value) {
Ok(tool_value) => Some((get_mcp_snapshot_tool_name(&tool), tool_value)),
Ok(tool) => Some((name, tool)),
Err(err) => {
tracing::warn!(
"Failed to convert MCP tool '{}': {err}",
get_mcp_snapshot_tool_name(&tool)
);
tracing::warn!("Failed to convert MCP tool '{name}': {err}");
None
}
},
Err(err) => {
tracing::warn!(
"Failed to serialize MCP tool '{}': {err}",
get_mcp_snapshot_tool_name(&tool)
);
tracing::warn!("Failed to serialize MCP tool '{name}': {err}");
None
}
})