Expose MCP app identity in app context (#29934)

## Why

MCP tool-call events need to expose trusted app identity and action
metadata directly so v2 clients do not have to infer it from tool names
or resource URIs.

## What changed

- Add optional `appName`, `templateId`, and `actionName` fields to MCP
tool-call `appContext`.
- Populate `appName` and `templateId` from trusted Codex Apps metadata,
and derive `actionName` from the trusted app resource metadata.
- Preserve all three fields through core events, legacy protocol events,
persisted thread history, resume redaction, and app-server v2 responses.
- Document the public `appContext` fields in
`codex-rs/app-server/README.md`.
- Regenerate app-server JSON and TypeScript schemas and add coverage for
serialization, persistence, redaction, and metadata propagation.

## Validation

- `just test -p codex-app-server-protocol mcp_tool_call`
- `just test -p codex-core
mcp_tool_call_item_metadata_only_trusts_codex_apps_identity
mcp_tool_call_item_includes_app_identity`
- `just write-app-server-schema`

---------

Co-authored-by: Martin Au-Yeung <280153141+martinauyeung-oai@users.noreply.github.com>
This commit is contained in:
Martin Au-YeungandMartin Au-Yeung authored and GitHub committed 2026-06-25 18:31:10 -07:00
1 parent fb8598df3f
commit ec300bc7bd
32 files changed
+516 -4

No files matched your search

@@ -2677,6 +2677,9 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
connector_id: Some("calendar".to_string()),
mcp_app_resource_uri: Some("app://connector".to_string()),
link_id: Some("link_calendar".to_string()),
app_name: Some("Calendar".to_string()),
template_id: Some("calendar_template".to_string()),
action_name: Some("create_event".to_string()),
plugin_id: Some("sample@test".to_string()),
status: CoreMcpToolCallStatus::InProgress,
result: None,
@@ -2696,6 +2699,9 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
connector_id: "calendar".to_string(),
link_id: Some("link_calendar".to_string()),
resource_uri: Some("app://connector".to_string()),
app_name: Some("Calendar".to_string()),
template_id: Some("calendar_template".to_string()),
action_name: Some("create_event".to_string()),
}),
mcp_app_resource_uri: Some("app://connector".to_string()),
plugin_id: Some("sample@test".to_string()),
@@ -2713,6 +2719,9 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
connector_id: None,
mcp_app_resource_uri: None,
link_id: None,
app_name: None,
template_id: None,
action_name: None,
plugin_id: None,
status: CoreMcpToolCallStatus::Completed,
result: Some(CallToolResult {
@@ -2759,6 +2768,9 @@ fn mcp_tool_call_app_context_serializes_connector_id() {
connector_id: "calendar".to_string(),
link_id: Some("link_calendar".to_string()),
resource_uri: Some("app://connector".to_string()),
app_name: Some("Calendar".to_string()),
template_id: Some("calendar_template".to_string()),
action_name: Some("create_event".to_string()),
}),
mcp_app_resource_uri: Some("app://connector".to_string()),
plugin_id: None,
@@ -2780,6 +2792,9 @@ fn mcp_tool_call_app_context_serializes_connector_id() {
"connectorId": "calendar",
"linkId": "link_calendar",
"resourceUri": "app://connector",
"appName": "Calendar",
"templateId": "calendar_template",
"actionName": "create_event",
},
"mcpAppResourceUri": "app://connector",
"pluginId": null,
@@ -2790,6 +2805,29 @@ fn mcp_tool_call_app_context_serializes_connector_id() {
);
}
#[test]
fn mcp_tool_call_app_context_serializes_missing_mixed_version_fields_as_null() {
assert_eq!(
serde_json::to_value(McpToolCallAppContext {
connector_id: "calendar".to_string(),
link_id: None,
resource_uri: None,
app_name: None,
template_id: None,
action_name: None,
})
.expect("MCP tool call app context should serialize"),
json!({
"connectorId": "calendar",
"linkId": null,
"resourceUri": null,
"appName": null,
"templateId": null,
"actionName": null,
})
);
}
#[test]
fn user_input_into_core_preserves_image_detail() {
assert_eq!(