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:
committed by
GitHub
Unverified
parent
fb8598df3f
commit
ec300bc7bd
@@ -202,6 +202,15 @@ pub struct McpToolCallItem {
|
||||
pub link_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub app_name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub template_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub action_name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub plugin_id: Option<String>,
|
||||
pub status: McpToolCallStatus,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
@@ -560,6 +569,9 @@ impl McpToolCallItem {
|
||||
connector_id: self.connector_id.clone(),
|
||||
mcp_app_resource_uri: self.mcp_app_resource_uri.clone(),
|
||||
link_id: self.link_id.clone(),
|
||||
app_name: self.app_name.clone(),
|
||||
template_id: self.template_id.clone(),
|
||||
action_name: self.action_name.clone(),
|
||||
plugin_id: self.plugin_id.clone(),
|
||||
})
|
||||
}
|
||||
@@ -581,6 +593,9 @@ impl McpToolCallItem {
|
||||
mcp_app_resource_uri: self.mcp_app_resource_uri.clone(),
|
||||
connector_id: self.connector_id.clone(),
|
||||
link_id: self.link_id.clone(),
|
||||
app_name: self.app_name.clone(),
|
||||
template_id: self.template_id.clone(),
|
||||
action_name: self.action_name.clone(),
|
||||
plugin_id: self.plugin_id.clone(),
|
||||
duration: self.duration?,
|
||||
result,
|
||||
|
||||
@@ -2357,6 +2357,15 @@ pub struct McpToolCallBeginEvent {
|
||||
pub link_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub app_name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub template_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub action_name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub plugin_id: Option<String>,
|
||||
}
|
||||
|
||||
@@ -2376,6 +2385,15 @@ pub struct McpToolCallEndEvent {
|
||||
pub link_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub app_name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub template_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub action_name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub plugin_id: Option<String>,
|
||||
#[ts(type = "string")]
|
||||
pub duration: Duration,
|
||||
@@ -5140,6 +5158,9 @@ mod tests {
|
||||
connector_id: Some("connector".into()),
|
||||
mcp_app_resource_uri: Some("app://connector".into()),
|
||||
link_id: Some("link_123".into()),
|
||||
app_name: Some("Calendar".into()),
|
||||
template_id: Some("calendar_template".into()),
|
||||
action_name: Some("create_event".into()),
|
||||
plugin_id: Some("sample@test".into()),
|
||||
status: McpToolCallStatus::InProgress,
|
||||
result: None,
|
||||
@@ -5161,6 +5182,8 @@ mod tests {
|
||||
Some("app://connector")
|
||||
);
|
||||
assert_eq!(event.link_id.as_deref(), Some("link_123"));
|
||||
assert_eq!(event.app_name.as_deref(), Some("Calendar"));
|
||||
assert_eq!(event.action_name.as_deref(), Some("create_event"));
|
||||
assert_eq!(event.plugin_id.as_deref(), Some("sample@test"));
|
||||
}
|
||||
_ => panic!("expected McpToolCallBegin event"),
|
||||
@@ -5251,6 +5274,9 @@ mod tests {
|
||||
connector_id: Some("connector".into()),
|
||||
mcp_app_resource_uri: Some("app://connector".into()),
|
||||
link_id: Some("link_123".into()),
|
||||
app_name: Some("Calendar".into()),
|
||||
template_id: Some("calendar_template".into()),
|
||||
action_name: Some("create_event".into()),
|
||||
plugin_id: Some("sample@test".into()),
|
||||
status: McpToolCallStatus::Completed,
|
||||
result: Some(CallToolResult {
|
||||
@@ -5277,6 +5303,8 @@ mod tests {
|
||||
Some("app://connector")
|
||||
);
|
||||
assert_eq!(event.link_id.as_deref(), Some("link_123"));
|
||||
assert_eq!(event.app_name.as_deref(), Some("Calendar"));
|
||||
assert_eq!(event.action_name.as_deref(), Some("create_event"));
|
||||
assert_eq!(event.plugin_id.as_deref(), Some("sample@test"));
|
||||
assert_eq!(event.duration, Duration::from_millis(42));
|
||||
assert!(event.is_success());
|
||||
|
||||
Reference in New Issue
Block a user