mirror of
https://github.com/openai/codex.git
synced 2026-11-02 19:43:24 +00:00
Preserve MCP App UI metadata in tool-call events and history (#45805)
## Why Clients need widget presentation details to render MCP Apps without waiting for the full MCP catalog, including when replaying saved history. ## What changed - Add `mcpToolCall.mcpAppUi` with the invoked descriptor's `resourceUri` and `preferredModelDisplayMode`, and carry it through tool-call events and saved history. - Support `fullscreen` and default to `inline` when the display preference is missing or unsupported. - Keep existing resource URI fields for compatibility. Leave `mcpAppUi` null for older history and tools that declare widgets only in result metadata, where clients still use catalog discovery. - Update protocol schemas, TypeScript and Python types, and app-server documentation. ## Testing Add parameterized integration coverage for fullscreen, missing and unsupported preferences, legacy URI metadata, and result-only widgets, verifying consistent tool events and preservation across session resume. GitOrigin-RevId: 2a9bed804dcea8c2b4903b8406fa6710681f7f5e
This commit is contained in:
@@ -5659,6 +5659,7 @@ async fn turn_event_counts_completed_tool_items() {
|
||||
action_name: None,
|
||||
}),
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: Some("sample@test".to_string()),
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
@@ -5912,6 +5913,7 @@ async fn mcp_elicitation_classification_survives_turn_completion_and_preserves_c
|
||||
action_name: None,
|
||||
}),
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
|
||||
@@ -3869,6 +3869,7 @@ mod tests {
|
||||
arguments: serde_json::json!({}),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
|
||||
@@ -2693,6 +2693,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpServerEventNotification": {
|
||||
"properties": {
|
||||
"method": {
|
||||
@@ -4917,12 +4940,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
+35
-1
@@ -14410,6 +14410,29 @@
|
||||
"title": "MarketplaceUpgradeResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/v2/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpAuthStatus": {
|
||||
"enum": [
|
||||
"unknown",
|
||||
@@ -21826,12 +21849,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
+35
-1
@@ -10462,6 +10462,29 @@
|
||||
"title": "MarketplaceUpgradeResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpAuthStatus": {
|
||||
"enum": [
|
||||
"unknown",
|
||||
@@ -19540,12 +19563,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
+35
-1
@@ -510,6 +510,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1137,12 +1160,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -510,6 +510,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1137,12 +1160,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -650,6 +650,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1325,12 +1348,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -754,6 +754,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1939,12 +1962,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -510,6 +510,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1137,12 +1160,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -676,6 +676,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1730,12 +1753,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
+35
-1
@@ -676,6 +676,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1730,12 +1753,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -676,6 +676,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1730,12 +1753,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -770,6 +770,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1991,12 +2014,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -676,6 +676,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1730,12 +1753,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -754,6 +754,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1939,12 +1962,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
+35
-1
@@ -676,6 +676,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1730,12 +1753,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -650,6 +650,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1325,12 +1348,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -676,6 +676,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1730,12 +1753,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
+35
-1
@@ -650,6 +650,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1325,12 +1348,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -650,6 +650,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1325,12 +1348,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -650,6 +650,29 @@
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppDisplayMode": {
|
||||
"enum": [
|
||||
"inline",
|
||||
"fullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpAppUi": {
|
||||
"description": "UI resource and display preference for model invocations, captured from the tool descriptor.",
|
||||
"properties": {
|
||||
"preferredModelDisplayMode": {
|
||||
"$ref": "#/definitions/McpAppDisplayMode"
|
||||
},
|
||||
"resourceUri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredModelDisplayMode",
|
||||
"resourceUri"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolCallAppContext": {
|
||||
"properties": {
|
||||
"actionName": {
|
||||
@@ -1325,12 +1348,23 @@
|
||||
"type": "string"
|
||||
},
|
||||
"mcpAppResourceUri": {
|
||||
"description": "Deprecated: use `appContext.resourceUri` instead.",
|
||||
"description": "Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mcpAppUi": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpAppUi"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Presentation captured from the invoked descriptor; absent in older history."
|
||||
},
|
||||
"pluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type McpAppDisplayMode = "inline" | "fullscreen";
|
||||
@@ -0,0 +1,9 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { McpAppDisplayMode } from "./McpAppDisplayMode";
|
||||
|
||||
/**
|
||||
* UI resource and display preference for model invocations, captured from the tool descriptor.
|
||||
*/
|
||||
export type McpAppUi = { resourceUri: string, preferredModelDisplayMode: McpAppDisplayMode, };
|
||||
@@ -21,6 +21,7 @@ import type { DynamicToolCallOutputContentItem } from "./DynamicToolCallOutputCo
|
||||
import type { DynamicToolCallStatus } from "./DynamicToolCallStatus";
|
||||
import type { FileUpdateChange } from "./FileUpdateChange";
|
||||
import type { HookPromptFragment } from "./HookPromptFragment";
|
||||
import type { McpAppUi } from "./McpAppUi";
|
||||
import type { McpToolCallAppContext } from "./McpToolCallAppContext";
|
||||
import type { McpToolCallError } from "./McpToolCallError";
|
||||
import type { McpToolCallResult } from "./McpToolCallResult";
|
||||
@@ -70,9 +71,13 @@ exitCode: number | null,
|
||||
*/
|
||||
durationMs: number | null, } | { "type": "fileChange", id: string, changes: Array<FileUpdateChange>, status: PatchApplyStatus, } | { "type": "mcpToolCall", id: string, server: string, tool: string, status: McpToolCallStatus, arguments: JsonValue, appContext: McpToolCallAppContext | null,
|
||||
/**
|
||||
* Deprecated: use `appContext.resourceUri` instead.
|
||||
* Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.
|
||||
*/
|
||||
mcpAppResourceUri?: string, pluginId: string | null, readOnlyHint: boolean | null, result: McpToolCallResult | null, error: McpToolCallError | null,
|
||||
mcpAppResourceUri?: string,
|
||||
/**
|
||||
* Presentation captured from the invoked descriptor; absent in older history.
|
||||
*/
|
||||
mcpAppUi: McpAppUi | null, pluginId: string | null, readOnlyHint: boolean | null, result: McpToolCallResult | null, error: McpToolCallError | null,
|
||||
/**
|
||||
* The duration of the MCP tool call in milliseconds.
|
||||
*/
|
||||
|
||||
@@ -251,6 +251,8 @@ export type { MarketplaceRemoveResponse } from "./MarketplaceRemoveResponse";
|
||||
export type { MarketplaceUpgradeErrorInfo } from "./MarketplaceUpgradeErrorInfo";
|
||||
export type { MarketplaceUpgradeParams } from "./MarketplaceUpgradeParams";
|
||||
export type { MarketplaceUpgradeResponse } from "./MarketplaceUpgradeResponse";
|
||||
export type { McpAppDisplayMode } from "./McpAppDisplayMode";
|
||||
export type { McpAppUi } from "./McpAppUi";
|
||||
export type { McpAuthStatus } from "./McpAuthStatus";
|
||||
export type { McpElicitationArrayType } from "./McpElicitationArrayType";
|
||||
export type { McpElicitationBooleanSchema } from "./McpElicitationBooleanSchema";
|
||||
|
||||
@@ -784,6 +784,7 @@ impl ThreadHistoryBuilder {
|
||||
action_name: payload.action_name.clone(),
|
||||
}),
|
||||
mcp_app_resource_uri: payload.mcp_app_resource_uri.clone(),
|
||||
mcp_app_ui: payload.mcp_app_ui.clone(),
|
||||
plugin_id: payload.plugin_id.clone(),
|
||||
read_only_hint: payload.read_only_hint,
|
||||
result: None,
|
||||
@@ -841,6 +842,7 @@ impl ThreadHistoryBuilder {
|
||||
action_name: payload.action_name.clone(),
|
||||
}),
|
||||
mcp_app_resource_uri: payload.mcp_app_resource_uri.clone(),
|
||||
mcp_app_ui: payload.mcp_app_ui.clone(),
|
||||
plugin_id: payload.plugin_id.clone(),
|
||||
read_only_hint: payload.read_only_hint,
|
||||
result,
|
||||
@@ -3118,6 +3120,7 @@ mod tests {
|
||||
},
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
action_name: None,
|
||||
@@ -3181,6 +3184,7 @@ mod tests {
|
||||
arguments: serde_json::json!({"id":"123"}),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
@@ -3213,6 +3217,7 @@ mod tests {
|
||||
},
|
||||
connector_id: Some("calendar".into()),
|
||||
mcp_app_resource_uri: Some("ui://widget/lookup.html".into()),
|
||||
mcp_app_ui: None,
|
||||
link_id: Some("link_calendar".into()),
|
||||
app_name: Some("Calendar".into()),
|
||||
action_name: Some("lookup".into()),
|
||||
@@ -3255,6 +3260,7 @@ mod tests {
|
||||
action_name: Some("lookup".into()),
|
||||
}),
|
||||
mcp_app_resource_uri: Some("ui://widget/lookup.html".into()),
|
||||
mcp_app_ui: None,
|
||||
plugin_id: Some("sample@test".into()),
|
||||
read_only_hint: Some(false),
|
||||
result: Some(Box::new(McpToolCallResult {
|
||||
@@ -3646,6 +3652,7 @@ mod tests {
|
||||
arguments: serde_json::json!({"query": "incident"}),
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
action_name: None,
|
||||
|
||||
@@ -31,6 +31,8 @@ use codex_protocol::items::CollabAgentTool as CoreCollabAgentTool;
|
||||
use codex_protocol::items::CollabAgentToolCallStatus as CoreCollabAgentToolCallStatus;
|
||||
use codex_protocol::items::CommandExecutionStatus as CoreCommandExecutionStatus;
|
||||
use codex_protocol::items::DynamicToolCallStatus as CoreDynamicToolCallStatus;
|
||||
pub use codex_protocol::items::McpAppDisplayMode;
|
||||
pub use codex_protocol::items::McpAppUi;
|
||||
use codex_protocol::items::McpToolCallStatus as CoreMcpToolCallStatus;
|
||||
use codex_protocol::items::TurnItem as CoreTurnItem;
|
||||
use codex_protocol::memory_citation::MemoryCitation as CoreMemoryCitation;
|
||||
@@ -337,8 +339,10 @@ pub enum ThreadItem {
|
||||
app_context: Option<McpToolCallAppContext>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
/// Deprecated: use `appContext.resourceUri` instead.
|
||||
/// Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.
|
||||
mcp_app_resource_uri: Option<String>,
|
||||
/// Presentation captured from the invoked descriptor; absent in older history.
|
||||
mcp_app_ui: Option<McpAppUi>,
|
||||
plugin_id: Option<String>,
|
||||
read_only_hint: Option<bool>,
|
||||
result: Option<Box<McpToolCallResult>>,
|
||||
@@ -1038,6 +1042,7 @@ impl From<CoreTurnItem> for ThreadItem {
|
||||
action_name: mcp.action_name,
|
||||
}),
|
||||
mcp_app_resource_uri: mcp.mcp_app_resource_uri,
|
||||
mcp_app_ui: mcp.mcp_app_ui,
|
||||
plugin_id: mcp.plugin_id,
|
||||
read_only_hint: mcp.read_only_hint,
|
||||
result: mcp.result.map(McpToolCallResult::from).map(Box::new),
|
||||
|
||||
@@ -3463,6 +3463,7 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
|
||||
arguments: json!({"arg": "value"}),
|
||||
connector_id: Some("calendar".to_string()),
|
||||
mcp_app_resource_uri: Some("app://connector".to_string()),
|
||||
mcp_app_ui: None,
|
||||
link_id: Some("link_calendar".to_string()),
|
||||
app_name: Some("Calendar".to_string()),
|
||||
action_name: Some("create_event".to_string()),
|
||||
@@ -3490,6 +3491,7 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
|
||||
action_name: Some("create_event".to_string()),
|
||||
}),
|
||||
mcp_app_resource_uri: Some("app://connector".to_string()),
|
||||
mcp_app_ui: None,
|
||||
plugin_id: Some("sample@test".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
result: None,
|
||||
@@ -3505,6 +3507,7 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
|
||||
arguments: JsonValue::Null,
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
action_name: None,
|
||||
@@ -3531,6 +3534,7 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
|
||||
arguments: JsonValue::Null,
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: Some(false),
|
||||
result: Some(Box::new(McpToolCallResult {
|
||||
@@ -3560,6 +3564,7 @@ fn mcp_tool_call_app_context_serializes_connector_id() {
|
||||
action_name: Some("create_event".to_string()),
|
||||
}),
|
||||
mcp_app_resource_uri: Some("app://connector".to_string()),
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: Some(false),
|
||||
result: None,
|
||||
@@ -3584,6 +3589,7 @@ fn mcp_tool_call_app_context_serializes_connector_id() {
|
||||
"actionName": "create_event",
|
||||
},
|
||||
"mcpAppResourceUri": "app://connector",
|
||||
"mcpAppUi": null,
|
||||
"pluginId": null,
|
||||
"readOnlyHint": false,
|
||||
"result": null,
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
# MCP App UI
|
||||
|
||||
`mcpToolCall.mcpAppUi` records the invoked descriptor's `resourceUri`
|
||||
and `preferredModelDisplayMode` (`inline` or `fullscreen`). Descriptors with a widget
|
||||
URI default to `inline` when the preference is missing or unsupported. The
|
||||
UI information is preserved in tool-call events and saved history so clients can
|
||||
render without waiting for the full MCP catalog.
|
||||
|
||||
The field is null for older history and tools that declare widgets only in
|
||||
result metadata; clients retain catalog discovery for those calls. Existing
|
||||
resource URI fields remain available for older clients.
|
||||
|
||||
# Initial Daybreak choice (experimental)
|
||||
|
||||
Persistent threads accept `daybreakEnabled` on `thread/start` with the
|
||||
|
||||
@@ -90,6 +90,7 @@ mod tests {
|
||||
action_name: Some("lookup".to_string()),
|
||||
}),
|
||||
mcp_app_resource_uri: Some("ui://widget/lookup.html".to_string()),
|
||||
mcp_app_ui: None,
|
||||
plugin_id: Some("sample@test".to_string()),
|
||||
read_only_hint: None,
|
||||
result: Some(Box::new(McpToolCallResult {
|
||||
@@ -146,6 +147,7 @@ mod tests {
|
||||
action_name: Some("lookup".to_string()),
|
||||
}),
|
||||
mcp_app_resource_uri: Some("ui://widget/lookup.html".to_string()),
|
||||
mcp_app_ui: None,
|
||||
plugin_id: Some("sample@test".to_string()),
|
||||
read_only_hint: None,
|
||||
result: Some(Box::new(redacted_mcp_tool_call_result())),
|
||||
@@ -165,6 +167,7 @@ mod tests {
|
||||
arguments: serde_json::json!({"secret":"argument"}),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
@@ -186,6 +189,7 @@ mod tests {
|
||||
arguments: JsonValue::String(REDACTED_PAYLOAD.to_string()),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
|
||||
@@ -1087,6 +1087,7 @@ async fn mcp_tool_call_completion_notification_contains_truncated_large_result()
|
||||
arguments: json!({ "message": LARGE_RESPONSE_MESSAGE }),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: Some(result),
|
||||
@@ -1198,6 +1199,7 @@ async fn mcp_tool_call_hint_survives_mid_call_thread_read_and_resume() -> Result
|
||||
arguments: json!({ "message": ELICITATION_TRIGGER_MESSAGE }),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: Some(true),
|
||||
result: None,
|
||||
|
||||
@@ -2770,6 +2770,7 @@ fn append_resume_redaction_history(
|
||||
},
|
||||
connector_id: Some("calendar".to_string()),
|
||||
mcp_app_resource_uri: Some("ui://widget/lookup.html".to_string()),
|
||||
mcp_app_ui: None,
|
||||
link_id: Some("link_calendar".to_string()),
|
||||
app_name: Some("Calendar".to_string()),
|
||||
action_name: Some("lookup".to_string()),
|
||||
|
||||
@@ -46,6 +46,8 @@ use codex_mcp::build_auth_elicitation_plan;
|
||||
use codex_mcp::is_connector_auth_failure_from_tool_result;
|
||||
use codex_mcp::mcp_permission_prompt_is_auto_approved;
|
||||
use codex_protocol::approvals::ElicitationRequest;
|
||||
use codex_protocol::items::McpAppDisplayMode;
|
||||
use codex_protocol::items::McpAppUi;
|
||||
use codex_protocol::items::McpToolCallError;
|
||||
use codex_protocol::items::McpToolCallItem;
|
||||
use codex_protocol::items::McpToolCallStatus;
|
||||
@@ -390,7 +392,7 @@ pub(crate) struct HandledMcpToolCall {
|
||||
struct McpToolCallItemMetadata {
|
||||
connector_id: Option<String>,
|
||||
link_id: Option<String>,
|
||||
mcp_app_resource_uri: Option<String>,
|
||||
mcp_app_ui: Option<McpAppUi>,
|
||||
app_name: Option<String>,
|
||||
action_name: Option<String>,
|
||||
plugin_id: Option<String>,
|
||||
@@ -408,8 +410,7 @@ impl McpToolCallItemMetadata {
|
||||
connector_id: trusted_mcp_app_metadata
|
||||
.and_then(|metadata| metadata.connector_id.clone()),
|
||||
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()),
|
||||
mcp_app_ui: metadata.and_then(|metadata| metadata.mcp_app_ui.clone()),
|
||||
app_name: trusted_mcp_app_metadata.and_then(|metadata| metadata.connector_name.clone()),
|
||||
action_name: trusted_mcp_app_metadata
|
||||
.and_then(|metadata| metadata.codex_apps_meta.as_ref())
|
||||
@@ -1004,7 +1005,11 @@ async fn notify_mcp_tool_call_started(
|
||||
tool,
|
||||
arguments: arguments.unwrap_or(JsonValue::Null),
|
||||
connector_id: item_metadata.connector_id,
|
||||
mcp_app_resource_uri: item_metadata.mcp_app_resource_uri,
|
||||
mcp_app_resource_uri: item_metadata
|
||||
.mcp_app_ui
|
||||
.as_ref()
|
||||
.map(|ui| ui.resource_uri.clone()),
|
||||
mcp_app_ui: item_metadata.mcp_app_ui,
|
||||
link_id: item_metadata.link_id,
|
||||
app_name: item_metadata.app_name,
|
||||
action_name: item_metadata.action_name,
|
||||
@@ -1049,7 +1054,11 @@ async fn notify_mcp_tool_call_completed(
|
||||
tool,
|
||||
arguments: arguments.unwrap_or(JsonValue::Null),
|
||||
connector_id: item_metadata.connector_id,
|
||||
mcp_app_resource_uri: item_metadata.mcp_app_resource_uri,
|
||||
mcp_app_resource_uri: item_metadata
|
||||
.mcp_app_ui
|
||||
.as_ref()
|
||||
.map(|ui| ui.resource_uri.clone()),
|
||||
mcp_app_ui: item_metadata.mcp_app_ui,
|
||||
link_id: item_metadata.link_id,
|
||||
app_name: item_metadata.app_name,
|
||||
action_name: item_metadata.action_name,
|
||||
@@ -1171,7 +1180,7 @@ pub(crate) struct McpToolApprovalMetadata {
|
||||
plugin_id: Option<String>,
|
||||
tool_title: Option<String>,
|
||||
tool_description: Option<String>,
|
||||
mcp_app_resource_uri: Option<String>,
|
||||
mcp_app_ui: Option<McpAppUi>,
|
||||
codex_apps_meta: Option<serde_json::Map<String, serde_json::Value>>,
|
||||
openai_file_input_optional_fields: Option<HashMap<String, Vec<String>>>,
|
||||
}
|
||||
@@ -1633,7 +1642,7 @@ pub(crate) async fn request_mcp_tool_user_approval(
|
||||
plugin_id: None,
|
||||
tool_title: tool_title.clone(),
|
||||
tool_description: tool_description.clone(),
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
@@ -1783,6 +1792,24 @@ fn mcp_tool_metadata(
|
||||
None
|
||||
};
|
||||
|
||||
let mcp_app_ui = get_mcp_app_resource_uri(tool_info.tool.meta.as_deref()).map(|resource_uri| {
|
||||
let preferred_model_display_mode = match tool_info
|
||||
.tool
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|meta| meta.get("openai/ui"))
|
||||
.and_then(|ui| ui.get("preferredModelDisplayMode"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
{
|
||||
Some("fullscreen") => McpAppDisplayMode::Fullscreen,
|
||||
_ => McpAppDisplayMode::Inline,
|
||||
};
|
||||
McpAppUi {
|
||||
resource_uri,
|
||||
preferred_model_display_mode,
|
||||
}
|
||||
});
|
||||
|
||||
Ok(McpToolApprovalMetadata {
|
||||
annotations: tool_info.tool.annotations,
|
||||
connector_id: tool_info.connector_id,
|
||||
@@ -1793,7 +1820,7 @@ fn mcp_tool_metadata(
|
||||
plugin_id: plugin_id.map(str::to_string),
|
||||
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()),
|
||||
mcp_app_ui,
|
||||
codex_apps_meta,
|
||||
// Disallow custom MCPs from uploading files via fileParams.
|
||||
openai_file_input_optional_fields: openai_file_input_optional_fields_for_server(
|
||||
|
||||
@@ -90,7 +90,7 @@ fn approval_metadata(
|
||||
plugin_id: None,
|
||||
tool_title: tool_title.map(str::to_string),
|
||||
tool_description: tool_description.map(str::to_string),
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
}
|
||||
@@ -1436,7 +1436,7 @@ fn mcp_tool_call_item_metadata_only_trusts_codex_apps_identity() {
|
||||
McpToolCallItemMetadata {
|
||||
connector_id: Some("asdk_app_0123456789abcdef0123456789abcdef".to_string()),
|
||||
link_id: Some("link_fedcba9876543210fedcba9876543210".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
app_name: Some("Calendar".to_string()),
|
||||
action_name: Some("create_event".to_string()),
|
||||
plugin_id: None,
|
||||
@@ -1448,7 +1448,7 @@ fn mcp_tool_call_item_metadata_only_trusts_codex_apps_identity() {
|
||||
McpToolCallItemMetadata {
|
||||
connector_id: None,
|
||||
link_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
app_name: None,
|
||||
action_name: None,
|
||||
plugin_id: None,
|
||||
@@ -1473,7 +1473,7 @@ async fn mcp_tool_call_item_includes_app_identity() {
|
||||
McpToolCallItemMetadata {
|
||||
connector_id: Some("asdk_app_0123456789abcdef0123456789abcdef".to_string()),
|
||||
link_id: Some("link_fedcba9876543210fedcba9876543210".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
app_name: Some("Calendar".to_string()),
|
||||
action_name: Some("create_event".to_string()),
|
||||
plugin_id: Some("sample@test".to_string()),
|
||||
@@ -1523,7 +1523,7 @@ async fn codex_apps_tool_call_request_meta_includes_turn_metadata_and_codex_apps
|
||||
plugin_id: None,
|
||||
tool_title: Some("Create Event".to_string()),
|
||||
tool_description: Some("Create a calendar event.".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: Some(
|
||||
serde_json::json!({
|
||||
"resource_uri": "connector://calendar/tools/calendar_create_event",
|
||||
@@ -1974,7 +1974,7 @@ fn guardian_mcp_review_request_includes_annotations_when_present() {
|
||||
plugin_id: None,
|
||||
tool_title: None,
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
@@ -2738,7 +2738,7 @@ async fn approve_mode_skips_when_annotations_do_not_require_approval() {
|
||||
plugin_id: None,
|
||||
tool_title: Some("Read Only Tool".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
@@ -2819,7 +2819,7 @@ async fn guardian_mode_skips_auto_when_annotations_do_not_require_approval() {
|
||||
plugin_id: None,
|
||||
tool_title: Some("Read Only Tool".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
@@ -2882,7 +2882,7 @@ async fn permission_request_hook_allows_mcp_tool_call() {
|
||||
plugin_id: None,
|
||||
tool_title: Some("Create entities".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
@@ -3033,7 +3033,7 @@ async fn permission_request_hook_runs_after_remembered_mcp_approval() {
|
||||
plugin_id: None,
|
||||
tool_title: Some("Create entities".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
@@ -3134,7 +3134,7 @@ async fn strict_auto_review_forces_guardian_for_mcp_policy_skip() {
|
||||
plugin_id: None,
|
||||
tool_title: Some("Dangerous Tool".to_string()),
|
||||
tool_description: Some("Reads calendar data.".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
@@ -3213,7 +3213,7 @@ async fn assert_mcp_user_approval_persistence(
|
||||
plugin_id: None,
|
||||
tool_title: Some("Create entities".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
@@ -3299,7 +3299,7 @@ async fn prompt_mode_waits_for_approval_when_annotations_do_not_require_approval
|
||||
plugin_id: None,
|
||||
tool_title: Some("Read Only Tool".to_string()),
|
||||
tool_description: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
@@ -3364,7 +3364,7 @@ async fn full_access_mode_skips_mcp_tool_approval_for_all_approval_modes() {
|
||||
plugin_id: None,
|
||||
tool_title: Some("Dangerous Tool".to_string()),
|
||||
tool_description: Some("Performs a risky action.".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
@@ -3423,7 +3423,7 @@ async fn approve_mode_skips_guardian_in_every_permission_mode() {
|
||||
plugin_id: None,
|
||||
tool_title: Some("Dangerous Tool".to_string()),
|
||||
tool_description: Some("Performs a risky action.".to_string()),
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
codex_apps_meta: None,
|
||||
openai_file_input_optional_fields: None,
|
||||
};
|
||||
|
||||
@@ -95,6 +95,7 @@ fn mcp_tool_call(id: &str, server: &str, status: McpToolCallStatus) -> TurnItem
|
||||
arguments: serde_json::Value::Null,
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
action_name: None,
|
||||
|
||||
@@ -222,6 +222,7 @@ async fn emit_tool_call_begin(
|
||||
arguments: arguments.unwrap_or(Value::Null),
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
action_name: None,
|
||||
@@ -266,6 +267,7 @@ async fn emit_tool_call_end(
|
||||
arguments: arguments.unwrap_or(Value::Null),
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
action_name: None,
|
||||
|
||||
@@ -14,6 +14,8 @@ use codex_protocol::config_types::ApprovalsReviewer;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::items::McpAppDisplayMode;
|
||||
use codex_protocol::items::McpAppUi;
|
||||
use codex_protocol::items::McpToolCallStatus;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::models::NetworkPermissions;
|
||||
@@ -23,6 +25,8 @@ use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::ElicitationAction;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::ItemCompletedEvent;
|
||||
use codex_protocol::protocol::ItemStartedEvent;
|
||||
use codex_protocol::protocol::Op;
|
||||
use codex_protocol::protocol::ThreadSettingsOverrides;
|
||||
use codex_protocol::protocol::TurnEnvironmentSelections;
|
||||
@@ -71,6 +75,142 @@ use wiremock::matchers::body_partial_json;
|
||||
use wiremock::matchers::method;
|
||||
use wiremock::matchers::path_regex;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
#[test_case(json!({"ui": {"resourceUri": "ui://calendar/widget"}, "openai/ui": {"preferredModelDisplayMode": "fullscreen"}}), Some(McpAppDisplayMode::Fullscreen); "fullscreen")]
|
||||
#[test_case(json!({"ui": {"resourceUri": "ui://calendar/widget"}}), Some(McpAppDisplayMode::Inline); "missing preference")]
|
||||
#[test_case(json!({"ui": {"resourceUri": "ui://calendar/widget"}, "openai/ui": {"preferredModelDisplayMode": "unsupported"}}), Some(McpAppDisplayMode::Inline); "unsupported preference")]
|
||||
#[test_case(json!({"openai/outputTemplate": "ui://calendar/widget"}), Some(McpAppDisplayMode::Inline); "legacy uri")]
|
||||
#[test_case(json!({}), None; "result only ui")]
|
||||
async fn mcp_app_ui_survives_tool_events_and_resume(
|
||||
mut metadata: Value,
|
||||
expected_mode: Option<McpAppDisplayMode>,
|
||||
) -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let apps_server = AppsTestServer::mount(&server).await?;
|
||||
metadata["connector_id"] = json!("calendar");
|
||||
metadata["connector_name"] = json!("Calendar");
|
||||
Mock::given(method("POST"))
|
||||
.and(path_regex("^/api/codex/ps/mcp/?$"))
|
||||
.and(body_partial_json(json!({"method": "tools/list"})))
|
||||
.respond_with(move |request: &Request| {
|
||||
let body: Value = serde_json::from_slice(&request.body).expect("valid tools/list");
|
||||
ResponseTemplate::new(/*status*/ 200).set_body_json(json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": body["id"],
|
||||
"result": {"tools": [{
|
||||
"name": "calendar_create_event",
|
||||
"description": "Create a calendar event.",
|
||||
"inputSchema": {"type": "object", "properties": {}},
|
||||
"annotations": {"readOnlyHint": true},
|
||||
"_meta": metadata
|
||||
}]}
|
||||
}))
|
||||
})
|
||||
.with_priority(/*priority*/ 1)
|
||||
.mount(&server)
|
||||
.await;
|
||||
Mock::given(method("POST"))
|
||||
.and(path_regex("^/api/codex/ps/mcp/?$"))
|
||||
.and(body_partial_json(json!({"method": "tools/call"})))
|
||||
.respond_with(|request: &Request| {
|
||||
let body: Value = serde_json::from_slice(&request.body).expect("valid tools/call");
|
||||
ResponseTemplate::new(/*status*/ 200).set_body_json(json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": body["id"],
|
||||
"result": {
|
||||
"content": [{"type": "text", "text": "Calendar opened."}],
|
||||
"_meta": {"openai/outputTemplate": "ui://calendar/result-widget"},
|
||||
"isError": false
|
||||
}
|
||||
}))
|
||||
})
|
||||
.with_priority(/*priority*/ 1)
|
||||
.mount(&server)
|
||||
.await;
|
||||
let call_id = "calendar-ui";
|
||||
let responses = mount_sse_sequence(
|
||||
&server,
|
||||
vec![
|
||||
sse(vec![
|
||||
ev_response_created("resp-calendar"),
|
||||
ev_function_call_with_namespace(
|
||||
call_id,
|
||||
SEARCH_CALENDAR_NAMESPACE,
|
||||
SEARCH_CALENDAR_CREATE_TOOL,
|
||||
"{}",
|
||||
),
|
||||
ev_completed("resp-calendar"),
|
||||
]),
|
||||
sse(vec![
|
||||
ev_assistant_message("msg-done", "done"),
|
||||
ev_completed("resp-done"),
|
||||
]),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
let mut builder = search_capable_apps_builder(apps_server.chatgpt_base_url);
|
||||
let test = builder.build_with_auto_env(&server).await?;
|
||||
submit_user_turn(
|
||||
&test,
|
||||
"Use [$calendar](app://calendar) to open the calendar widget.",
|
||||
AskForApproval::Never,
|
||||
PermissionProfile::Disabled,
|
||||
/*collaboration_mode*/ None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let expected_ui = expected_mode.map(|preferred_model_display_mode| McpAppUi {
|
||||
resource_uri: "ui://calendar/widget".to_string(),
|
||||
preferred_model_display_mode,
|
||||
});
|
||||
let expected_uri = expected_ui.as_ref().map(|ui| ui.resource_uri.clone());
|
||||
let mut observed = Vec::new();
|
||||
let mut completed = None;
|
||||
wait_for_event(&test.codex, |event| {
|
||||
match event {
|
||||
EventMsg::ItemStarted(ItemStartedEvent {
|
||||
item: TurnItem::McpToolCall(item),
|
||||
..
|
||||
})
|
||||
| EventMsg::ItemCompleted(ItemCompletedEvent {
|
||||
item: TurnItem::McpToolCall(item),
|
||||
..
|
||||
}) => {
|
||||
observed.push((item.mcp_app_ui.clone(), item.mcp_app_resource_uri.clone()));
|
||||
}
|
||||
EventMsg::McpToolCallBegin(begin) => {
|
||||
observed.push((begin.mcp_app_ui.clone(), begin.mcp_app_resource_uri.clone()));
|
||||
}
|
||||
EventMsg::McpToolCallEnd(end) => {
|
||||
assert!(end.result.is_ok());
|
||||
observed.push((end.mcp_app_ui.clone(), end.mcp_app_resource_uri.clone()));
|
||||
completed = Some(event.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
matches!(event, EventMsg::TurnComplete(_))
|
||||
})
|
||||
.await;
|
||||
assert_eq!(observed, vec![(expected_ui, expected_uri); 4]);
|
||||
assert_eq!(responses.requests().len(), 2);
|
||||
|
||||
let resumed = builder.restart(&server, &test).await?;
|
||||
let completed_history = resumed
|
||||
.session_configured
|
||||
.initial_messages
|
||||
.expect("resumed history")
|
||||
.into_iter()
|
||||
.filter(|event| matches!(event, EventMsg::McpToolCallEnd(end) if end.call_id == call_id))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
serde_json::to_value(completed_history)?,
|
||||
serde_json::to_value(vec![completed.expect("completed MCP call")])?
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn auto_session_approval_is_scoped_to_tool_link_id() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
@@ -2529,6 +2529,7 @@ async fn stdio_image_responses_round_trip() -> anyhow::Result<()> {
|
||||
},
|
||||
connector_id: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
link_id: None,
|
||||
app_name: None,
|
||||
action_name: None,
|
||||
|
||||
@@ -103,6 +103,7 @@ fn mcp_tool_call_result_preserves_meta_in_jsonl_event() {
|
||||
arguments: json!({"search_query": [{"q": "OpenAI Codex CLI documentation"}]}),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: Some(Box::new(codex_app_server_protocol::McpToolCallResult {
|
||||
|
||||
@@ -492,6 +492,7 @@ fn mcp_tool_call_begin_and_end_emit_item_events() {
|
||||
arguments: json!({ "key": "value" }),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
@@ -512,6 +513,7 @@ fn mcp_tool_call_begin_and_end_emit_item_events() {
|
||||
arguments: json!({ "key": "value" }),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: Some(Box::new(McpToolCallResult {
|
||||
@@ -586,6 +588,7 @@ fn mcp_tool_call_failure_sets_failed_status() {
|
||||
arguments: json!({ "param": 42 }),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
@@ -637,6 +640,7 @@ fn mcp_tool_call_defaults_arguments_and_preserves_structured_content() {
|
||||
arguments: serde_json::Value::Null,
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
@@ -657,6 +661,7 @@ fn mcp_tool_call_defaults_arguments_and_preserves_structured_content() {
|
||||
arguments: serde_json::Value::Null,
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: Some(Box::new(McpToolCallResult {
|
||||
|
||||
@@ -425,6 +425,25 @@ pub struct FileChangeItem {
|
||||
pub stderr: Option<String>,
|
||||
}
|
||||
|
||||
/// UI resource and display preference for model invocations, captured from the tool descriptor.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct McpAppUi {
|
||||
pub resource_uri: String,
|
||||
pub preferred_model_display_mode: McpAppDisplayMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, TS, JsonSchema, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub enum McpAppDisplayMode {
|
||||
Inline,
|
||||
Fullscreen,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase")]
|
||||
@@ -436,11 +455,15 @@ pub struct McpToolCallItem {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub connector_id: Option<String>,
|
||||
/// Legacy compatibility field; prefer `mcp_app_ui.resource_uri` when available.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub mcp_app_resource_uri: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub mcp_app_ui: Option<McpAppUi>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub link_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
|
||||
@@ -508,6 +508,7 @@ impl McpToolCallItem {
|
||||
},
|
||||
connector_id: self.connector_id.clone(),
|
||||
mcp_app_resource_uri: self.mcp_app_resource_uri.clone(),
|
||||
mcp_app_ui: self.mcp_app_ui.clone(),
|
||||
link_id: self.link_id.clone(),
|
||||
app_name: self.app_name.clone(),
|
||||
action_name: self.action_name.clone(),
|
||||
@@ -532,6 +533,7 @@ impl McpToolCallItem {
|
||||
arguments: (!self.arguments.is_null()).then(|| self.arguments.clone()),
|
||||
},
|
||||
mcp_app_resource_uri: self.mcp_app_resource_uri.clone(),
|
||||
mcp_app_ui: self.mcp_app_ui.clone(),
|
||||
connector_id: self.connector_id.clone(),
|
||||
link_id: self.link_id.clone(),
|
||||
app_name: self.app_name.clone(),
|
||||
|
||||
@@ -2670,6 +2670,9 @@ pub struct McpToolCallBeginEvent {
|
||||
pub mcp_app_resource_uri: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub mcp_app_ui: Option<crate::items::McpAppUi>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub link_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
@@ -2702,6 +2705,9 @@ pub struct McpToolCallEndEvent {
|
||||
pub mcp_app_resource_uri: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub mcp_app_ui: Option<crate::items::McpAppUi>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub link_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
@@ -5392,6 +5398,7 @@ mod tests {
|
||||
arguments: json!({"arg": "value"}),
|
||||
connector_id: Some("connector".into()),
|
||||
mcp_app_resource_uri: Some("app://connector".into()),
|
||||
mcp_app_ui: None,
|
||||
link_id: Some("link_123".into()),
|
||||
app_name: Some("Calendar".into()),
|
||||
action_name: Some("create_event".into()),
|
||||
@@ -5513,6 +5520,7 @@ mod tests {
|
||||
arguments: json!({"arg": "value"}),
|
||||
connector_id: Some("connector".into()),
|
||||
mcp_app_resource_uri: Some("app://connector".into()),
|
||||
mcp_app_ui: None,
|
||||
link_id: Some("link_123".into()),
|
||||
app_name: Some("Calendar".into()),
|
||||
action_name: Some("create_event".into()),
|
||||
|
||||
@@ -185,6 +185,7 @@ pub(super) fn completed_item(
|
||||
.unwrap_or(serde_json::Value::Null),
|
||||
connector_id: event.connector_id.clone(),
|
||||
mcp_app_resource_uri: event.mcp_app_resource_uri.clone(),
|
||||
mcp_app_ui: event.mcp_app_ui.clone(),
|
||||
link_id: event.link_id.clone(),
|
||||
app_name: event.app_name.clone(),
|
||||
action_name: event.action_name.clone(),
|
||||
|
||||
@@ -654,6 +654,7 @@ async fn misalignment_buffered_replay_preserves_input_after_continuation() {
|
||||
arguments: serde_json::json!({}),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
|
||||
@@ -12,6 +12,7 @@ fn computer_item(
|
||||
arguments: json!({"title": format!("Inspect page {id}"), "code": "await cua.getState()"}),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: Some(Box::new(codex_app_server_protocol::McpToolCallResult {
|
||||
|
||||
@@ -1223,6 +1223,7 @@ async fn replayed_in_progress_mcp_tool_call_stays_active() {
|
||||
arguments: json!({"action": "wait"}),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
@@ -1258,6 +1259,7 @@ async fn failed_repl_mcp_tool_call_preserves_status_and_result() {
|
||||
arguments: json!({"title": "Inspect workspace"}),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: Some(Box::new(codex_app_server_protocol::McpToolCallResult {
|
||||
@@ -1326,6 +1328,7 @@ async fn deferred_mcp_lifecycle_events_keep_fifo_after_stream_finishes() {
|
||||
arguments: json!({"action": "wait"}),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: None,
|
||||
@@ -1343,6 +1346,7 @@ async fn deferred_mcp_lifecycle_events_keep_fifo_after_stream_finishes() {
|
||||
arguments: json!({"action": "wait"}),
|
||||
app_context: None,
|
||||
mcp_app_resource_uri: None,
|
||||
mcp_app_ui: None,
|
||||
plugin_id: None,
|
||||
read_only_hint: None,
|
||||
result: Some(Box::new(codex_app_server_protocol::McpToolCallResult {
|
||||
|
||||
@@ -2461,6 +2461,21 @@ class MarketplaceUpgradeResponse(BaseModel):
|
||||
upgraded_roots: Annotated[list[AbsolutePathBuf], Field(alias="upgradedRoots")]
|
||||
|
||||
|
||||
class McpAppDisplayMode(Enum):
|
||||
inline = "inline"
|
||||
fullscreen = "fullscreen"
|
||||
|
||||
|
||||
class McpAppUi(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
)
|
||||
preferred_model_display_mode: Annotated[
|
||||
McpAppDisplayMode, Field(alias="preferredModelDisplayMode")
|
||||
]
|
||||
resource_uri: Annotated[str, Field(alias="resourceUri")]
|
||||
|
||||
|
||||
class McpAuthStatus(Enum):
|
||||
unknown = "unknown"
|
||||
unsupported = "unsupported"
|
||||
@@ -5303,7 +5318,14 @@ class McpToolCallThreadItem(BaseModel):
|
||||
str | None,
|
||||
Field(
|
||||
alias="mcpAppResourceUri",
|
||||
description="Deprecated: use `appContext.resourceUri` instead.",
|
||||
description="Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available.",
|
||||
),
|
||||
] = None
|
||||
mcp_app_ui: Annotated[
|
||||
McpAppUi | None,
|
||||
Field(
|
||||
alias="mcpAppUi",
|
||||
description="Presentation captured from the invoked descriptor; absent in older history.",
|
||||
),
|
||||
] = None
|
||||
plugin_id: Annotated[str | None, Field(alias="pluginId")] = None
|
||||
|
||||
Reference in New Issue
Block a user