1use serde::Serialize;
2
3pub const UI_DEFAULT_PROTOCOL: &str = "adk_ui";
5
6pub const TOOL_ENVELOPE_VERSION: &str = "1.0";
8
9pub const SUPPORTED_UI_PROTOCOLS: &[&str] = &["adk_ui", "a2ui", "ag_ui", "mcp_apps"];
11
12#[derive(Debug, Clone, Serialize)]
14#[serde(rename_all = "camelCase")]
15pub struct UiProtocolDeprecationSpec {
16 pub stage: &'static str,
17 pub announced_on: &'static str,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub sunset_target_on: Option<&'static str>,
20 pub replacement_protocols: &'static [&'static str],
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub note: Option<&'static str>,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
27#[serde(rename_all = "snake_case")]
28pub enum UiProtocolImplementationTier {
29 Legacy,
30 NativeSubset,
31 HybridSubset,
32 CompatibilitySubset,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
37#[serde(rename_all = "snake_case")]
38pub enum UiProtocolSpecTrack {
39 Internal,
40 Stable,
41 Draft,
42}
43
44#[derive(Debug, Clone, Serialize)]
46#[serde(rename_all = "camelCase")]
47pub struct UiProtocolCapabilitySpec {
48 pub protocol: &'static str,
49 pub versions: &'static [&'static str],
50 pub implementation_tier: UiProtocolImplementationTier,
51 pub spec_track: UiProtocolSpecTrack,
52 pub summary: &'static str,
53 pub features: &'static [&'static str],
54 pub limitations: &'static [&'static str],
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub deprecation: Option<&'static UiProtocolDeprecationSpec>,
57}
58
59pub const ADK_UI_LEGACY_DEPRECATION: UiProtocolDeprecationSpec = UiProtocolDeprecationSpec {
60 stage: "planned",
61 announced_on: "2026-02-07",
62 sunset_target_on: Some("2026-12-31"),
63 replacement_protocols: &["a2ui", "ag_ui", "mcp_apps"],
64 note: Some("Legacy adk_ui profile remains supported during migration."),
65};
66
67pub const UI_PROTOCOL_CAPABILITIES: &[UiProtocolCapabilitySpec] = &[
68 UiProtocolCapabilitySpec {
69 protocol: "adk_ui",
70 versions: &["1.0"],
71 implementation_tier: UiProtocolImplementationTier::Legacy,
72 spec_track: UiProtocolSpecTrack::Internal,
73 summary: "Legacy internal runtime profile retained for backward compatibility during migration.",
74 features: &["legacy_components", "theme", "events"],
75 limitations: &[
76 "Deprecated for new integrations.",
77 "Does not represent a standard external protocol surface.",
78 ],
79 deprecation: Some(&ADK_UI_LEGACY_DEPRECATION),
80 },
81 UiProtocolCapabilitySpec {
82 protocol: "a2ui",
83 versions: &["0.9"],
84 implementation_tier: UiProtocolImplementationTier::HybridSubset,
85 spec_track: UiProtocolSpecTrack::Draft,
86 summary: "Core A2UI surface transport with flat-component alignment, metadata-aware client envelopes, and validation-capable renderer behavior.",
87 features: &[
88 "jsonl",
89 "flat_components",
90 "createSurface",
91 "updateComponents",
92 "updateDataModel",
93 "client_metadata",
94 "validation_feedback",
95 "basic_catalog_functions",
96 "local_actions",
97 ],
98 limitations: &[
99 "Treat current support as a v0.9-aligned subset while the upstream v0.9 spec remains draft.",
100 "The package now covers the practical metadata, validation, and basic catalog flows used by this repo, but it still does not claim every draft-only renderer/runtime feature.",
101 ],
102 deprecation: None,
103 },
104 UiProtocolCapabilitySpec {
105 protocol: "ag_ui",
106 versions: &["0.1"],
107 implementation_tier: UiProtocolImplementationTier::CompatibilitySubset,
108 spec_track: UiProtocolSpecTrack::Stable,
109 summary: "Compatibility-oriented AG-UI subset with native run-input ingestion, lifecycle events, custom surface transport, and stable text/tool event ingestion.",
110 features: &[
111 "event_stream",
112 "native_run_input_ingest",
113 "run_lifecycle",
114 "custom_surface_event",
115 "stable_text_events",
116 "stable_tool_events",
117 "messages_snapshot_ingest",
118 "run_error",
119 ],
120 limitations: &[
121 "The example client now prefers protocol-native AG-UI request input, but the bundled example server still serializes wrapped runtime events rather than a fully native AG-UI SSE envelope.",
122 "Activity snapshots/deltas, reasoning streams, and chunk aggregation are accepted as compatibility inputs but are not yet produced end-to-end by the example server.",
123 ],
124 deprecation: None,
125 },
126 UiProtocolCapabilitySpec {
127 protocol: "mcp_apps",
128 versions: &["sep-1865"],
129 implementation_tier: UiProtocolImplementationTier::CompatibilitySubset,
130 spec_track: UiProtocolSpecTrack::Stable,
131 summary: "Compatibility-oriented MCP Apps subset with initialize requests, bridge-aware structured tool results, ui:// resources, and inline HTML fallback surfaces.",
132 features: &[
133 "ui_resource_uri",
134 "tool_meta",
135 "structured_content",
136 "initialize_request",
137 "initialize_bridge_endpoint",
138 "message_bridge_endpoint",
139 "update_model_context_bridge_endpoint",
140 "bridge_host_context",
141 "bridge_app_capabilities",
142 "inline_html_resource",
143 ],
144 limitations: &[
145 "The example host/client path now supports initialize, message, and model-context bridge endpoints plus bridge-aware response rendering, but the library still exposes MCP Apps primarily through compatibility adapters rather than a standalone embedded app bridge API.",
146 "Static HTML resource fallback remains part of the public MCP Apps surface while broader host negotiation, resource notifications, and fully app-native bridge semantics are still partial.",
147 ],
148 deprecation: None,
149 },
150];
151
152#[cfg(feature = "awp")]
157pub const AWP_PROTOCOL_CAPABILITY: UiProtocolCapabilitySpec = UiProtocolCapabilitySpec {
158 protocol: "awp",
159 versions: &["1.0"],
160 implementation_tier: UiProtocolImplementationTier::CompatibilitySubset,
161 spec_track: UiProtocolSpecTrack::Draft,
162 summary: "AWP-aligned HTML rendering with capability manifest export and bandwidth-adaptive output.",
163 features: &[
164 "html_rendering",
165 "capability_manifest_export",
166 "bandwidth_adaptive",
167 "tool_envelope_bridge",
168 ],
169 limitations: &[
170 "HTML renderer produces static markup; interactive behaviors require client-side hydration.",
171 "Chart components render as data-attribute placeholders, not visual charts.",
172 ],
173 deprecation: None,
174};
175
176pub fn normalize_runtime_ui_protocol(raw: &str) -> Option<&'static str> {
178 match raw.trim().to_ascii_lowercase().as_str() {
179 "adk_ui" => Some("adk_ui"),
180 "a2ui" => Some("a2ui"),
181 "ag_ui" | "ag-ui" => Some("ag_ui"),
182 "mcp_apps" | "mcp-apps" => Some("mcp_apps"),
183 #[cfg(feature = "awp")]
184 "awp" => Some("awp"),
185 _ => None,
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn normalize_runtime_protocol_accepts_aliases() {
195 assert_eq!(normalize_runtime_ui_protocol("adk_ui"), Some("adk_ui"));
196 assert_eq!(normalize_runtime_ui_protocol("A2UI"), Some("a2ui"));
197 assert_eq!(normalize_runtime_ui_protocol("ag-ui"), Some("ag_ui"));
198 assert_eq!(normalize_runtime_ui_protocol("mcp-apps"), Some("mcp_apps"));
199 assert_eq!(normalize_runtime_ui_protocol("unknown"), None);
200 }
201
202 #[test]
203 fn capability_specs_cover_supported_protocols() {
204 let protocols: Vec<&str> = UI_PROTOCOL_CAPABILITIES
205 .iter()
206 .map(|spec| spec.protocol)
207 .collect();
208 assert_eq!(protocols, SUPPORTED_UI_PROTOCOLS);
209 }
210
211 #[test]
212 fn capability_specs_include_versions() {
213 for spec in UI_PROTOCOL_CAPABILITIES {
214 assert!(
215 !spec.versions.is_empty(),
216 "missing versions for {}",
217 spec.protocol
218 );
219 assert!(
220 !spec.features.is_empty(),
221 "missing features for {}",
222 spec.protocol
223 );
224 assert!(
225 !spec.summary.trim().is_empty(),
226 "missing summary for {}",
227 spec.protocol
228 );
229 assert!(
230 !spec.limitations.is_empty(),
231 "missing limitations for {}",
232 spec.protocol
233 );
234 }
235 }
236
237 #[test]
238 fn legacy_profile_has_deprecation_metadata() {
239 let legacy = UI_PROTOCOL_CAPABILITIES
240 .iter()
241 .find(|spec| spec.protocol == "adk_ui")
242 .expect("adk_ui capability");
243 let deprecation = legacy.deprecation.expect("adk_ui deprecation metadata");
244 assert_eq!(deprecation.announced_on, "2026-02-07");
245 assert_eq!(deprecation.sunset_target_on, Some("2026-12-31"));
246 }
247
248 #[test]
249 fn capability_specs_capture_support_boundaries() {
250 let a2ui = UI_PROTOCOL_CAPABILITIES
251 .iter()
252 .find(|spec| spec.protocol == "a2ui")
253 .expect("a2ui capability");
254 assert_eq!(
255 a2ui.implementation_tier,
256 UiProtocolImplementationTier::HybridSubset
257 );
258 assert_eq!(a2ui.spec_track, UiProtocolSpecTrack::Draft);
259
260 let ag_ui = UI_PROTOCOL_CAPABILITIES
261 .iter()
262 .find(|spec| spec.protocol == "ag_ui")
263 .expect("ag_ui capability");
264 assert_eq!(
265 ag_ui.implementation_tier,
266 UiProtocolImplementationTier::CompatibilitySubset
267 );
268
269 let mcp_apps = UI_PROTOCOL_CAPABILITIES
270 .iter()
271 .find(|spec| spec.protocol == "mcp_apps")
272 .expect("mcp_apps capability");
273 assert_eq!(
274 mcp_apps.implementation_tier,
275 UiProtocolImplementationTier::CompatibilitySubset
276 );
277 assert_eq!(mcp_apps.spec_track, UiProtocolSpecTrack::Stable);
278 assert!(
279 mcp_apps.features.contains(&"initialize_request"),
280 "mcp_apps feature list should include initialize support"
281 );
282 }
283
284 #[test]
285 fn capability_specs_serialize_support_metadata_in_camel_case() {
286 let value = serde_json::to_value(UI_PROTOCOL_CAPABILITIES).expect("serialize capabilities");
287 let protocols = value.as_array().expect("capabilities array");
288 let a2ui = protocols
289 .iter()
290 .find(|spec| spec["protocol"] == "a2ui")
291 .expect("a2ui json capability");
292
293 assert_eq!(a2ui["implementationTier"], "hybrid_subset");
294 assert_eq!(a2ui["specTrack"], "draft");
295 assert!(a2ui["summary"].as_str().is_some());
296 assert!(
297 a2ui["limitations"]
298 .as_array()
299 .is_some_and(|limitations| !limitations.is_empty())
300 );
301 }
302}