pulseengine_mcp_protocol/
ui.rs

1//! MCP Apps Extension - UI Communication Protocol Types
2//!
3//! This module implements the complete MCP Apps Extension (SEP-1865) protocol
4//! for bidirectional communication between UI iframes and MCP hosts.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Theme preference for UI rendering
10#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
11#[serde(rename_all = "lowercase")]
12pub enum ThemePreference {
13    Light,
14    Dark,
15    System,
16}
17
18/// Display mode for UI rendering
19#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
20#[serde(rename_all = "lowercase")]
21pub enum DisplayMode {
22    Inline,
23    Fullscreen,
24    Pip, // Picture-in-picture
25    Carousel,
26}
27
28/// Platform type
29#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
30#[serde(rename_all = "lowercase")]
31pub enum PlatformType {
32    Desktop,
33    Mobile,
34    Web,
35    Embedded,
36}
37
38/// Viewport dimensions
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct Viewport {
41    pub width: u32,
42    pub height: u32,
43}
44
45/// Device capabilities
46#[derive(Debug, Clone, Serialize, Deserialize, Default)]
47pub struct DeviceCapabilities {
48    /// Touch input support
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub touch: Option<bool>,
51
52    /// Hover support (mouse/trackpad)
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub hover: Option<bool>,
55
56    /// Keyboard availability
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub keyboard: Option<bool>,
59}
60
61/// Tool context provided to UI
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct ToolContext {
64    /// Tool name
65    pub name: String,
66
67    /// Tool input schema
68    #[serde(rename = "inputSchema")]
69    pub input_schema: serde_json::Value,
70
71    /// Tool output schema (optional)
72    #[serde(rename = "outputSchema", skip_serializing_if = "Option::is_none")]
73    pub output_schema: Option<serde_json::Value>,
74
75    /// JSON-RPC request ID for the tool call that triggered this UI
76    #[serde(rename = "requestId", skip_serializing_if = "Option::is_none")]
77    pub request_id: Option<String>,
78
79    /// Arguments passed to the tool (optional)
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub arguments: Option<HashMap<String, serde_json::Value>>,
82}
83
84/// UI initialization request parameters
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct UiInitializeParams {
87    /// Protocol version
88    #[serde(rename = "protocolVersion")]
89    pub protocol_version: String,
90
91    /// UI capabilities
92    pub capabilities: UiCapabilities,
93
94    /// UI client information
95    #[serde(rename = "uiInfo")]
96    pub ui_info: UiInfo,
97}
98
99/// UI capabilities
100#[derive(Debug, Clone, Serialize, Deserialize, Default)]
101pub struct UiCapabilities {
102    /// Can make tool calls
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub tools: Option<bool>,
105
106    /// Can read resources
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub resources: Option<bool>,
109
110    /// Can send notifications
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub notifications: Option<bool>,
113}
114
115/// UI client information
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct UiInfo {
118    /// UI implementation name
119    pub name: String,
120
121    /// UI implementation version
122    pub version: String,
123}
124
125/// UI initialization result (host context)
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct UiInitializeResult {
128    /// Protocol version
129    #[serde(rename = "protocolVersion")]
130    pub protocol_version: String,
131
132    /// Host capabilities
133    pub capabilities: UiHostCapabilities,
134
135    /// Host information
136    #[serde(rename = "hostInfo")]
137    pub host_info: UiHostInfo,
138
139    /// Tool context (why this UI was invoked)
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub tool: Option<ToolContext>,
142
143    /// Theme preference
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub theme: Option<ThemePreference>,
146
147    /// Display mode
148    #[serde(rename = "displayMode", skip_serializing_if = "Option::is_none")]
149    pub display_mode: Option<DisplayMode>,
150
151    /// Viewport dimensions
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub viewport: Option<Viewport>,
154
155    /// User locale
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub locale: Option<String>,
158
159    /// User timezone
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub timezone: Option<String>,
162
163    /// Platform type
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub platform: Option<PlatformType>,
166
167    /// Device capabilities
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub device: Option<DeviceCapabilities>,
170}
171
172/// Host capabilities exposed to UI
173#[derive(Debug, Clone, Serialize, Deserialize, Default)]
174pub struct UiHostCapabilities {
175    /// Supports tool calls from UI
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub tools: Option<bool>,
178
179    /// Supports resource reads from UI
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub resources: Option<bool>,
182
183    /// Supports notifications from UI
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub notifications: Option<bool>,
186}
187
188/// Host information
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct UiHostInfo {
191    /// Host name (e.g., "Claude Desktop", "MCP Inspector")
192    pub name: String,
193
194    /// Host version
195    pub version: String,
196}
197
198/// Sandbox proxy messages (for web hosts)
199#[derive(Debug, Clone, Serialize, Deserialize)]
200#[serde(tag = "method")]
201pub enum SandboxProxyMessage {
202    /// Host notifies UI that sandbox is ready
203    #[serde(rename = "ui/sandbox-ready")]
204    SandboxReady {
205        #[serde(rename = "resourceUri")]
206        resource_uri: String,
207    },
208
209    /// Host provides resource HTML to sandbox
210    #[serde(rename = "ui/sandbox-resource-ready")]
211    SandboxResourceReady {
212        #[serde(rename = "resourceUri")]
213        resource_uri: String,
214        html: String,
215    },
216}
217
218/// UI notification message types
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct UiNotificationMessage {
221    /// Log level (info, warn, error, debug)
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub level: Option<String>,
224
225    /// Log message
226    pub message: String,
227
228    /// Additional data
229    #[serde(skip_serializing_if = "Option::is_none")]
230    pub data: Option<serde_json::Value>,
231}
232
233/// UI initialized notification (sent after ui/initialize completes)
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct UiInitializedNotification {
236    /// UI is ready
237    pub ready: bool,
238}