mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
Forward caller metadata on MCP resource reads (#59509)
## What changed Accept an optional `_meta` object on `mcpServer/resource/read` and forward it to MCP `resources/read`, including reads bound to an originating tool call. Update the protocol schemas and generated TypeScript and Python types. Reject caller metadata containing host-owned `threadId`, `x-codex-turn-metadata`, or `codex/connectionLease` keys. For `codex_apps`, also reserve `connector_id`, `link_id`, and `_codex_apps.resource_uri` to preserve app and account routing. ## Testing Add integration coverage for metadata forwarding through stdio, hosted app reads with and without a thread, and origin-bound widget reads. Cover rejection of reserved keys and non-object metadata. GitOrigin-RevId: 227ee0fb5fe15fa2ff2ec66d4d5ae16de8935a8c
This commit is contained in:
@@ -39,7 +39,8 @@ use crate::tool_catalog_cache::McpToolCatalogCacheContext;
|
||||
/// Opt-in resource lifecycle for negotiated stdio connections. Tool calls and resource reads
|
||||
/// carry a host-owned lease ID. Catalog requests remain independent of a particular lease.
|
||||
pub(crate) const LEASE_CAPABILITY: &str = "codex/connection-lease";
|
||||
pub(crate) const LEASE_META_KEY: &str = "codex/connectionLease";
|
||||
/// Request metadata set by the host when a connection lease is negotiated.
|
||||
pub const LEASE_META_KEY: &str = "codex/connectionLease";
|
||||
const LEASE_REQUEST_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -10,6 +10,7 @@ pub use codex_rmcp_client::McpProtocolMode;
|
||||
pub use connection_manager::NetworkPolicyMetadata;
|
||||
pub use connection_manager::NetworkPolicyMetadataProvider;
|
||||
pub use connection_manager::tool_is_model_visible;
|
||||
pub use connection_pool::LEASE_META_KEY as MCP_CONNECTION_LEASE_META_KEY;
|
||||
pub use connection_pool::McpConnectionPool;
|
||||
pub use elicitation::ElicitationLifecycle;
|
||||
pub use elicitation::ElicitationReviewRequest;
|
||||
|
||||
@@ -249,9 +249,9 @@ impl ResourceOrigin {
|
||||
&self,
|
||||
binding: &McpBinding,
|
||||
thread_id: ThreadId,
|
||||
uri: &str,
|
||||
mut params: ReadResourceRequestParams,
|
||||
) -> anyhow::Result<ReadResourceResult> {
|
||||
if self.uri != uri {
|
||||
if self.uri != params.uri {
|
||||
anyhow::bail!("originating MCP tool call does not match the requested resource");
|
||||
}
|
||||
if self.ambiguous_account {
|
||||
@@ -297,20 +297,19 @@ impl ResourceOrigin {
|
||||
anyhow::bail!("originating MCP tool is disabled by app configuration");
|
||||
}
|
||||
|
||||
let meta = serde_json::from_value(serde_json::json!({
|
||||
"threadId": thread_id,
|
||||
"x-codex-turn-metadata": {
|
||||
let meta = params.meta.get_or_insert_default();
|
||||
meta.insert("threadId".into(), serde_json::json!(thread_id));
|
||||
meta.insert(
|
||||
"x-codex-turn-metadata".into(),
|
||||
serde_json::json!({
|
||||
"mcp_request_meta": {
|
||||
"selected_connector_ids": [&self.connector_id],
|
||||
"link_id": &self.link_id,
|
||||
}
|
||||
},
|
||||
}))?;
|
||||
}),
|
||||
);
|
||||
binding
|
||||
.read_resource(
|
||||
CODEX_APPS_MCP_SERVER_NAME,
|
||||
ReadResourceRequestParams::new(uri).with_meta(meta),
|
||||
)
|
||||
.read_resource(CODEX_APPS_MCP_SERVER_NAME, params)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ impl McpRuntime {
|
||||
&self,
|
||||
thread_id: ThreadId,
|
||||
call_id: &str,
|
||||
uri: &str,
|
||||
params: ReadResourceRequestParams,
|
||||
) -> anyhow::Result<ReadResourceResult> {
|
||||
let origin = self
|
||||
.resource_origins
|
||||
@@ -341,7 +341,7 @@ impl McpRuntime {
|
||||
.await
|
||||
.ok_or_else(|| anyhow::anyhow!("codex_apps MCP server is unavailable"))?;
|
||||
|
||||
origin.read(&binding, thread_id, uri).await
|
||||
origin.read(&binding, thread_id, params).await
|
||||
}
|
||||
|
||||
pub async fn new(input: McpRuntimeInput) -> Self {
|
||||
|
||||
Reference in New Issue
Block a user