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
@@ -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!(
|
||||
|
||||
@@ -1372,7 +1372,7 @@ Today both notifications carry an empty `items` array even when item events were
|
||||
- `reasoning` — `{id, summary, content}` where `summary` holds streamed reasoning summaries (applicable for most OpenAI models) and `content` holds raw reasoning blocks (applicable for e.g. open source models).
|
||||
- `commandExecution` — `{id, command, cwd, status, commandActions, aggregatedOutput?, exitCode?, durationMs?}` for sandboxed commands; `status` is `inProgress`, `completed`, `failed`, or `declined`.
|
||||
- `fileChange` — `{id, changes, status}` describing proposed edits; `changes` list `{path, kind, diff}` and `status` is `inProgress`, `completed`, `failed`, or `declined`.
|
||||
- `mcpToolCall` — `{id, server, tool, status, arguments, appContext, mcpAppResourceUri?, pluginId, result?, error?}` describing MCP calls; `appContext` is `{connectorId, linkId, resourceUri}` for calls through a trusted MCP app, where `connectorId` identifies the connector that owns the tool, `linkId` identifies the app link, and `resourceUri` points to the widget template. The top-level `mcpAppResourceUri` is deprecated and temporarily duplicated for client migration. `tool` identifies the invoked action. `status` is `inProgress`, `completed`, or `failed`.
|
||||
- `mcpToolCall` — `{id, server, tool, status, arguments, appContext, mcpAppResourceUri?, pluginId, result?, error?}` describing MCP calls; `appContext` is `{connectorId, linkId, resourceUri, appName, templateId, actionName}` for calls through a trusted MCP app, where `connectorId` identifies the connector that owns the tool, `linkId` identifies the app link, `resourceUri` points to the widget template, `appName` is the connector's display name, `templateId` identifies the app template, and `actionName` is the stable connector `Action.name`. `appName`, `templateId`, and `actionName` may be null for older rollout entries. The top-level `mcpAppResourceUri` is deprecated and temporarily duplicated for client migration. `tool` identifies the raw MCP tool. `status` is `inProgress`, `completed`, or `failed`.
|
||||
- `collabToolCall` — `{id, tool, status, senderThreadId, receiverThreadId?, newThreadId?, prompt?, agentStatus?}` describing collab tool calls (`spawn_agent`, `send_input`, `resume_agent`, `wait`, `close_agent`); `status` is `inProgress`, `completed`, or `failed`.
|
||||
- `webSearch` — `{id, query, action?}` for a web search request issued by the agent; `action` mirrors the Responses API web_search action payload (`search`, `open_page`, `find_in_page`) and may be omitted until completion.
|
||||
- `imageView` — `{id, path}` emitted when the agent invokes the image viewer tool.
|
||||
|
||||
@@ -83,6 +83,9 @@ mod tests {
|
||||
connector_id: "calendar".to_string(),
|
||||
link_id: Some("link_calendar".to_string()),
|
||||
resource_uri: Some("ui://widget/lookup.html".to_string()),
|
||||
app_name: Some("Calendar".to_string()),
|
||||
template_id: Some("calendar_template".to_string()),
|
||||
action_name: Some("lookup".to_string()),
|
||||
}),
|
||||
mcp_app_resource_uri: Some("ui://widget/lookup.html".to_string()),
|
||||
plugin_id: Some("sample@test".to_string()),
|
||||
@@ -130,6 +133,9 @@ mod tests {
|
||||
connector_id: "calendar".to_string(),
|
||||
link_id: Some("link_calendar".to_string()),
|
||||
resource_uri: Some("ui://widget/lookup.html".to_string()),
|
||||
app_name: Some("Calendar".to_string()),
|
||||
template_id: Some("calendar_template".to_string()),
|
||||
action_name: Some("lookup".to_string()),
|
||||
}),
|
||||
mcp_app_resource_uri: Some("ui://widget/lookup.html".to_string()),
|
||||
plugin_id: Some("sample@test".to_string()),
|
||||
|
||||
@@ -834,6 +834,9 @@ async fn thread_resume_redacts_payloads_for_chatgpt_remote_clients() -> Result<(
|
||||
connector_id: "calendar".to_string(),
|
||||
link_id: Some("link_calendar".to_string()),
|
||||
resource_uri: Some("ui://widget/lookup.html".to_string()),
|
||||
app_name: Some("Calendar".to_string()),
|
||||
template_id: Some("calendar_template".to_string()),
|
||||
action_name: Some("lookup".to_string()),
|
||||
})
|
||||
);
|
||||
let result = result.as_ref().expect("redacted MCP result");
|
||||
@@ -884,6 +887,9 @@ async fn thread_resume_redacts_payloads_for_chatgpt_remote_clients() -> Result<(
|
||||
connector_id: "calendar".to_string(),
|
||||
link_id: Some("link_calendar".to_string()),
|
||||
resource_uri: Some("ui://widget/lookup.html".to_string()),
|
||||
app_name: Some("Calendar".to_string()),
|
||||
template_id: Some("calendar_template".to_string()),
|
||||
action_name: Some("lookup".to_string()),
|
||||
})
|
||||
);
|
||||
let result = result.as_ref().expect("normal MCP result");
|
||||
@@ -990,6 +996,9 @@ fn append_resume_redaction_history(
|
||||
connector_id: Some("calendar".to_string()),
|
||||
mcp_app_resource_uri: Some("ui://widget/lookup.html".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("lookup".to_string()),
|
||||
plugin_id: None,
|
||||
duration: Duration::from_millis(8),
|
||||
result: Ok(CallToolResult {
|
||||
|
||||
@@ -326,6 +326,9 @@ struct McpToolCallItemMetadata {
|
||||
connector_id: Option<String>,
|
||||
link_id: Option<String>,
|
||||
mcp_app_resource_uri: Option<String>,
|
||||
app_name: Option<String>,
|
||||
template_id: Option<String>,
|
||||
action_name: Option<String>,
|
||||
plugin_id: Option<String>,
|
||||
}
|
||||
|
||||
@@ -342,6 +345,15 @@ impl McpToolCallItemMetadata {
|
||||
link_id: trusted_mcp_app_metadata.and_then(|metadata| metadata.link_id.clone()),
|
||||
mcp_app_resource_uri: metadata
|
||||
.and_then(|metadata| metadata.mcp_app_resource_uri.clone()),
|
||||
app_name: trusted_mcp_app_metadata.and_then(|metadata| metadata.connector_name.clone()),
|
||||
template_id: trusted_mcp_app_metadata.and_then(|metadata| metadata.template_id.clone()),
|
||||
action_name: trusted_mcp_app_metadata
|
||||
.and_then(|metadata| metadata.codex_apps_meta.as_ref())
|
||||
.and_then(|meta| meta.get(MCP_TOOL_RESOURCE_URI_META_KEY))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.and_then(|resource_uri| resource_uri.trim_matches('/').rsplit('/').next())
|
||||
.filter(|action_name| !action_name.is_empty())
|
||||
.map(str::to_string),
|
||||
plugin_id: metadata.and_then(|metadata| metadata.plugin_id.clone()),
|
||||
}
|
||||
}
|
||||
@@ -881,6 +893,9 @@ async fn notify_mcp_tool_call_started(
|
||||
connector_id: item_metadata.connector_id,
|
||||
mcp_app_resource_uri: item_metadata.mcp_app_resource_uri,
|
||||
link_id: item_metadata.link_id,
|
||||
app_name: item_metadata.app_name,
|
||||
template_id: item_metadata.template_id,
|
||||
action_name: item_metadata.action_name,
|
||||
plugin_id: item_metadata.plugin_id,
|
||||
status: McpToolCallStatus::InProgress,
|
||||
result: None,
|
||||
@@ -923,6 +938,9 @@ async fn notify_mcp_tool_call_completed(
|
||||
connector_id: item_metadata.connector_id,
|
||||
mcp_app_resource_uri: item_metadata.mcp_app_resource_uri,
|
||||
link_id: item_metadata.link_id,
|
||||
app_name: item_metadata.app_name,
|
||||
template_id: item_metadata.template_id,
|
||||
action_name: item_metadata.action_name,
|
||||
plugin_id: item_metadata.plugin_id,
|
||||
status,
|
||||
result,
|
||||
@@ -999,6 +1017,7 @@ pub(crate) struct McpToolApprovalMetadata {
|
||||
tool_title: Option<String>,
|
||||
tool_description: Option<String>,
|
||||
mcp_app_resource_uri: Option<String>,
|
||||
template_id: Option<String>,
|
||||
codex_apps_meta: Option<serde_json::Map<String, serde_json::Value>>,
|
||||
openai_file_input_params: Option<Vec<String>>,
|
||||
}
|
||||
@@ -1009,6 +1028,8 @@ const MCP_TOOL_LINK_ID_META_KEY: &str = "link_id";
|
||||
const MCP_TOOL_PLUGIN_ID_META_KEY: &str = "plugin_id";
|
||||
const MCP_TOOL_THREAD_ID_META_KEY: &str = "threadId";
|
||||
const MCP_TOOL_CONNECTED_ACCOUNT_EMAIL_META_KEY: &str = "connected_account_email";
|
||||
const MCP_TOOL_TEMPLATE_ID_META_KEY: &str = "template_id";
|
||||
const MCP_TOOL_RESOURCE_URI_META_KEY: &str = "resource_uri";
|
||||
|
||||
async fn custom_mcp_tool_approval_mode(
|
||||
sess: &Session,
|
||||
@@ -1531,6 +1552,11 @@ pub(crate) async fn lookup_mcp_tool_metadata(
|
||||
tool_title: tool_info.tool.title,
|
||||
tool_description: tool_info.tool.description.map(std::borrow::Cow::into_owned),
|
||||
mcp_app_resource_uri: get_mcp_app_resource_uri(tool_info.tool.meta.as_deref()),
|
||||
template_id: codex_apps_meta
|
||||
.as_ref()
|
||||
.and_then(|meta| meta.get(MCP_TOOL_TEMPLATE_ID_META_KEY))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.map(str::to_string),
|
||||
codex_apps_meta,
|
||||
// Disallow custom MCPs from uploading files via fileParams.
|
||||
openai_file_input_params: openai_file_input_params_for_server(
|
||||
|
||||
@@ -87,6 +87,7 @@ fn approval_metadata(
|
||||
tool_title: tool_title.map(str::to_string),
|
||||
tool_description: tool_description.map(str::to_string),
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_params: None,
|
||||
}
|
||||
@@ -1195,12 +1196,22 @@ async fn plugin_mcp_tool_call_request_meta_includes_plugin_id() {
|
||||
fn mcp_tool_call_item_metadata_only_trusts_codex_apps_identity() {
|
||||
let mut metadata = approval_metadata(
|
||||
Some("asdk_app_0123456789abcdef0123456789abcdef"),
|
||||
/*connector_name*/ None,
|
||||
Some("Calendar"),
|
||||
/*connector_description*/ None,
|
||||
/*tool_title*/ None,
|
||||
Some("Create a calendar event"),
|
||||
/*tool_description*/ None,
|
||||
);
|
||||
metadata.link_id = Some("link_fedcba9876543210fedcba9876543210".to_string());
|
||||
metadata.template_id = Some("calendar_template".to_string());
|
||||
metadata.codex_apps_meta = Some(
|
||||
serde_json::json!({
|
||||
"resource_uri": "/asdk_app_0123456789abcdef0123456789abcdef/link_fedcba9876543210fedcba9876543210/create_event",
|
||||
"template_id": "calendar_template",
|
||||
})
|
||||
.as_object()
|
||||
.cloned()
|
||||
.expect("_codex_apps metadata should be an object"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
McpToolCallItemMetadata::from_tool_metadata(CODEX_APPS_MCP_SERVER_NAME, Some(&metadata),),
|
||||
@@ -1208,6 +1219,9 @@ fn mcp_tool_call_item_metadata_only_trusts_codex_apps_identity() {
|
||||
connector_id: Some("asdk_app_0123456789abcdef0123456789abcdef".to_string()),
|
||||
link_id: Some("link_fedcba9876543210fedcba9876543210".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
app_name: Some("Calendar".to_string()),
|
||||
template_id: Some("calendar_template".to_string()),
|
||||
action_name: Some("create_event".to_string()),
|
||||
plugin_id: None,
|
||||
}
|
||||
);
|
||||
@@ -1217,6 +1231,9 @@ fn mcp_tool_call_item_metadata_only_trusts_codex_apps_identity() {
|
||||
connector_id: None,
|
||||
link_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
app_name: None,
|
||||
template_id: None,
|
||||
action_name: None,
|
||||
plugin_id: None,
|
||||
}
|
||||
);
|
||||
@@ -1239,6 +1256,9 @@ async fn mcp_tool_call_item_includes_app_identity() {
|
||||
connector_id: Some("asdk_app_0123456789abcdef0123456789abcdef".to_string()),
|
||||
link_id: Some("link_fedcba9876543210fedcba9876543210".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
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()),
|
||||
},
|
||||
)
|
||||
@@ -1264,6 +1284,8 @@ async fn mcp_tool_call_item_includes_app_identity() {
|
||||
Some("link_fedcba9876543210fedcba9876543210")
|
||||
);
|
||||
assert_eq!(item.plugin_id.as_deref(), Some("sample@test"));
|
||||
assert_eq!(item.app_name.as_deref(), Some("Calendar"));
|
||||
assert_eq!(item.action_name.as_deref(), Some("create_event"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1284,6 +1306,7 @@ async fn codex_apps_tool_call_request_meta_includes_turn_metadata_and_codex_apps
|
||||
tool_title: Some("Create Event".to_string()),
|
||||
tool_description: Some("Create a calendar event.".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: Some(
|
||||
serde_json::json!({
|
||||
"resource_uri": "connector://calendar/tools/calendar_create_event",
|
||||
@@ -1771,6 +1794,7 @@ fn guardian_mcp_review_request_includes_annotations_when_present() {
|
||||
tool_title: None,
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_params: None,
|
||||
};
|
||||
@@ -2473,6 +2497,7 @@ async fn approve_mode_skips_when_annotations_do_not_require_approval() {
|
||||
tool_title: Some("Read Only Tool".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_params: None,
|
||||
};
|
||||
@@ -2549,6 +2574,7 @@ async fn guardian_mode_skips_auto_when_annotations_do_not_require_approval() {
|
||||
tool_title: Some("Read Only Tool".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_params: None,
|
||||
};
|
||||
@@ -2608,6 +2634,7 @@ async fn permission_request_hook_allows_mcp_tool_call() {
|
||||
tool_title: Some("Create entities".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_params: None,
|
||||
};
|
||||
@@ -2746,6 +2773,7 @@ async fn permission_request_hook_runs_after_remembered_mcp_approval() {
|
||||
tool_title: Some("Create entities".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_params: None,
|
||||
};
|
||||
@@ -2835,6 +2863,7 @@ async fn guardian_mode_mcp_denial_returns_rationale_message() {
|
||||
tool_title: Some("Dangerous Tool".to_string()),
|
||||
tool_description: Some("Reads calendar data.".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_params: None,
|
||||
};
|
||||
@@ -2891,6 +2920,7 @@ async fn prompt_mode_waits_for_approval_when_annotations_do_not_require_approval
|
||||
tool_title: Some("Read Only Tool".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_params: None,
|
||||
};
|
||||
@@ -2948,6 +2978,7 @@ async fn full_access_mode_skips_mcp_tool_approval_for_all_approval_modes() {
|
||||
tool_title: Some("Dangerous Tool".to_string()),
|
||||
tool_description: Some("Performs a risky action.".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_params: None,
|
||||
};
|
||||
@@ -3003,6 +3034,7 @@ async fn approve_mode_skips_guardian_in_every_permission_mode() {
|
||||
tool_title: Some("Dangerous Tool".to_string()),
|
||||
tool_description: Some("Performs a risky action.".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
template_id: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_params: None,
|
||||
};
|
||||
|
||||
@@ -224,6 +224,9 @@ async fn emit_tool_call_begin(
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
template_id: None,
|
||||
action_name: None,
|
||||
plugin_id: None,
|
||||
status: McpToolCallStatus::InProgress,
|
||||
result: None,
|
||||
@@ -265,6 +268,9 @@ async fn emit_tool_call_end(
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
template_id: None,
|
||||
action_name: None,
|
||||
plugin_id: None,
|
||||
status,
|
||||
result,
|
||||
|
||||
@@ -1359,6 +1359,9 @@ async fn stdio_image_responses_round_trip() -> anyhow::Result<()> {
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
template_id: None,
|
||||
action_name: None,
|
||||
plugin_id: None,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -545,6 +545,8 @@ async fn tool_search_returns_deferred_tools_without_follow_up_tool_injection() -
|
||||
unreachable!("event guard guarantees McpToolCallBegin");
|
||||
};
|
||||
assert_eq!(begin.call_id, "calendar-call-1");
|
||||
assert_eq!(begin.app_name.as_deref(), Some("Calendar"));
|
||||
assert_eq!(begin.action_name.as_deref(), Some("calendar_create_event"));
|
||||
assert_eq!(
|
||||
begin.mcp_app_resource_uri.as_deref(),
|
||||
Some(CALENDAR_CREATE_EVENT_MCP_APP_RESOURCE_URI)
|
||||
@@ -559,6 +561,8 @@ async fn tool_search_returns_deferred_tools_without_follow_up_tool_injection() -
|
||||
};
|
||||
assert_eq!(end.call_id, "calendar-call-1");
|
||||
assert_eq!(end.connector_id.as_deref(), Some("calendar"));
|
||||
assert_eq!(end.app_name.as_deref(), Some("Calendar"));
|
||||
assert_eq!(end.action_name.as_deref(), Some("calendar_create_event"));
|
||||
assert_eq!(
|
||||
end.mcp_app_resource_uri.as_deref(),
|
||||
Some(CALENDAR_CREATE_EVENT_MCP_APP_RESOURCE_URI)
|
||||
|
||||
@@ -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