mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Scope MCP app resource reads to their originating call (#39187)
## Why App widgets need to read resources using the app and account context of the tool call that produced them, including after a thread is restored. ## What changed - Add an optional `originCallId` to `mcpServer/resource/read` requests and return it on successful scoped reads. - Track bounded provenance for successful app tool calls in thread history and use the current tool binding, account link, and app policy when reading the associated widget resource. - Reject missing, mismatched, failed, or ambiguous origins, while keeping app-only tools available for widget reads without exposing them to the model. ## Testing Add app-server coverage for scoped widget reads across both history modes, ephemeral and persistent threads, server restarts, app-only visibility, and invalid origin cases. GitOrigin-RevId: 11eaefc066b2a3b639599e4cbdef680233d150a2
This commit is contained in:
1 parent
711a5f8b3a
commit
880f1135ea
26 files changed
+779
-22
No files matched your search
@@ -422,6 +422,7 @@ impl McpRequestProcessor {
|
||||
let outgoing = Arc::clone(&self.outgoing);
|
||||
let McpResourceReadParams {
|
||||
thread_id,
|
||||
origin_call_id,
|
||||
server,
|
||||
uri,
|
||||
} = params;
|
||||
@@ -431,12 +432,22 @@ impl McpRequestProcessor {
|
||||
let request_id = request_id.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let result = thread.read_mcp_resource(&server, &uri).await;
|
||||
Self::send_mcp_resource_read_response(outgoing, request_id, result).await;
|
||||
let origin_call_id =
|
||||
origin_call_id.filter(|_| server == codex_mcp::CODEX_APPS_MCP_SERVER_NAME);
|
||||
let result = match origin_call_id.as_deref() {
|
||||
Some(call_id) => thread.read_mcp_resource_for_call(call_id, &uri).await,
|
||||
None => thread.read_mcp_resource(&server, &uri).await,
|
||||
};
|
||||
Self::send_mcp_resource_read_response(outgoing, request_id, result, origin_call_id)
|
||||
.await;
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if origin_call_id.is_some() {
|
||||
return Err(invalid_request("originCallId requires threadId"));
|
||||
}
|
||||
|
||||
let config = self.load_latest_config(/*fallback_cwd*/ None).await?;
|
||||
let mcp_manager = self.thread_manager.mcp_manager();
|
||||
let mcp_config = mcp_manager.runtime_config(&config).await;
|
||||
@@ -463,7 +474,10 @@ impl McpRequestProcessor {
|
||||
)
|
||||
.await
|
||||
.and_then(|result| serde_json::to_value(result).map_err(anyhow::Error::from));
|
||||
Self::send_mcp_resource_read_response(outgoing, request_id, result).await;
|
||||
Self::send_mcp_resource_read_response(
|
||||
outgoing, request_id, result, /*origin_call_id*/ None,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@@ -472,6 +486,7 @@ impl McpRequestProcessor {
|
||||
outgoing: Arc<OutgoingMessageSender>,
|
||||
request_id: ConnectionRequestId,
|
||||
result: anyhow::Result<serde_json::Value>,
|
||||
origin_call_id: Option<String>,
|
||||
) {
|
||||
let result = result
|
||||
.map_err(|error| internal_error(format!("{error:#}")))
|
||||
@@ -481,6 +496,10 @@ impl McpRequestProcessor {
|
||||
"failed to deserialize MCP resource read response: {error}"
|
||||
))
|
||||
})
|
||||
})
|
||||
.map(|mut response| {
|
||||
response.origin_call_id = origin_call_id;
|
||||
response
|
||||
});
|
||||
outgoing.send_result(request_id, result).await;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user