mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
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:
co-authored by
Martin Au-Yeung
parent
fb8598df3f
commit
ec300bc7bd
@@ -2438,6 +2438,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2452,6 +2464,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
+18
@@ -12248,6 +12248,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -12262,6 +12274,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
+18
@@ -8652,6 +8652,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -8666,6 +8678,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -299,6 +299,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -313,6 +325,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -299,6 +299,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -313,6 +325,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -437,6 +437,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -451,6 +463,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -541,6 +541,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -555,6 +567,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -463,6 +463,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -477,6 +489,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
+18
@@ -463,6 +463,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -477,6 +489,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -463,6 +463,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -477,6 +489,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -541,6 +541,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -555,6 +567,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -463,6 +463,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -477,6 +489,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -541,6 +541,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -555,6 +567,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -463,6 +463,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -477,6 +489,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -463,6 +463,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -477,6 +489,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -437,6 +437,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -451,6 +463,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -437,6 +437,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -451,6 +463,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -437,6 +437,18 @@
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"appName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"connectorId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -451,6 +463,12 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
+1
-1
@@ -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 McpToolCallAppContext = { connectorId: string, linkId: string | null, resourceUri: string | null, };
|
||||
export type McpToolCallAppContext = { connectorId: string, linkId: string | null, resourceUri: string | null, appName: string | null, templateId: string | null, actionName: string | null, };
|
||||
|
||||
@@ -775,6 +775,9 @@ impl ThreadHistoryBuilder {
|
||||
connector_id,
|
||||
link_id: payload.link_id.clone(),
|
||||
resource_uri: payload.mcp_app_resource_uri.clone(),
|
||||
app_name: payload.app_name.clone(),
|
||||
template_id: payload.template_id.clone(),
|
||||
action_name: payload.action_name.clone(),
|
||||
}),
|
||||
mcp_app_resource_uri: payload.mcp_app_resource_uri.clone(),
|
||||
plugin_id: payload.plugin_id.clone(),
|
||||
@@ -825,6 +828,9 @@ impl ThreadHistoryBuilder {
|
||||
connector_id,
|
||||
link_id: payload.link_id.clone(),
|
||||
resource_uri: payload.mcp_app_resource_uri.clone(),
|
||||
app_name: payload.app_name.clone(),
|
||||
template_id: payload.template_id.clone(),
|
||||
action_name: payload.action_name.clone(),
|
||||
}),
|
||||
mcp_app_resource_uri: payload.mcp_app_resource_uri.clone(),
|
||||
plugin_id: payload.plugin_id.clone(),
|
||||
@@ -2418,6 +2424,9 @@ mod tests {
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
template_id: None,
|
||||
action_name: None,
|
||||
plugin_id: None,
|
||||
duration: Duration::from_millis(8),
|
||||
result: Err("boom".into()),
|
||||
@@ -2499,6 +2508,9 @@ mod tests {
|
||||
connector_id: Some("calendar".into()),
|
||||
mcp_app_resource_uri: Some("ui://widget/lookup.html".into()),
|
||||
link_id: Some("link_calendar".into()),
|
||||
app_name: Some("Calendar".into()),
|
||||
template_id: Some("calendar_template".into()),
|
||||
action_name: Some("lookup".into()),
|
||||
plugin_id: Some("sample@test".into()),
|
||||
duration: Duration::from_millis(8),
|
||||
result: Ok(CallToolResult {
|
||||
@@ -2533,6 +2545,9 @@ mod tests {
|
||||
connector_id: "calendar".into(),
|
||||
link_id: Some("link_calendar".into()),
|
||||
resource_uri: Some("ui://widget/lookup.html".into()),
|
||||
app_name: Some("Calendar".into()),
|
||||
template_id: Some("calendar_template".into()),
|
||||
action_name: Some("lookup".into()),
|
||||
}),
|
||||
mcp_app_resource_uri: Some("ui://widget/lookup.html".into()),
|
||||
plugin_id: Some("sample@test".into()),
|
||||
|
||||
@@ -396,6 +396,9 @@ pub struct McpToolCallAppContext {
|
||||
pub connector_id: String,
|
||||
pub link_id: Option<String>,
|
||||
pub resource_uri: Option<String>,
|
||||
pub app_name: Option<String>,
|
||||
pub template_id: Option<String>,
|
||||
pub action_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
@@ -895,6 +898,9 @@ impl From<CoreTurnItem> for ThreadItem {
|
||||
connector_id,
|
||||
link_id: mcp.link_id,
|
||||
resource_uri: mcp.mcp_app_resource_uri.clone(),
|
||||
app_name: mcp.app_name,
|
||||
template_id: mcp.template_id,
|
||||
action_name: mcp.action_name,
|
||||
}),
|
||||
mcp_app_resource_uri: mcp.mcp_app_resource_uri,
|
||||
plugin_id: mcp.plugin_id,
|
||||
|
||||
@@ -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!(
|
||||
|
||||
Reference in New Issue
Block a user