Skip to main content

adk_server/
ui_protocol.rs

1//! UI protocol constants, capability specs, and normalization.
2//!
3//! These were originally in `adk-ui` but are inlined here so `adk-server`
4//! can be published independently without a UI-toolkit dependency.
5
6use serde::Serialize;
7
8/// Default runtime protocol profile for server integrations.
9pub const UI_DEFAULT_PROTOCOL: &str = "adk_ui";
10
11/// Tool envelope version used by protocol-aware legacy tool responses.
12pub const TOOL_ENVELOPE_VERSION: &str = "1.0";
13
14/// Supported runtime protocol profile values.
15pub const SUPPORTED_UI_PROTOCOLS: &[&str] = &["adk_ui", "a2ui", "ag_ui", "mcp_apps"];
16
17/// Planned deprecation metadata for runtime/profile consumers.
18#[derive(Debug, Clone, Serialize)]
19#[serde(rename_all = "camelCase")]
20pub struct UiProtocolDeprecationSpec {
21    pub stage: &'static str,
22    pub announced_on: &'static str,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub sunset_target_on: Option<&'static str>,
25    pub replacement_protocols: &'static [&'static str],
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub note: Option<&'static str>,
28}
29
30/// Static capability contract for each supported UI protocol.
31#[derive(Debug, Clone, Serialize)]
32pub struct UiProtocolCapabilitySpec {
33    pub protocol: &'static str,
34    pub versions: &'static [&'static str],
35    pub features: &'static [&'static str],
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub deprecation: Option<&'static UiProtocolDeprecationSpec>,
38}
39
40pub const ADK_UI_LEGACY_DEPRECATION: UiProtocolDeprecationSpec = UiProtocolDeprecationSpec {
41    stage: "planned",
42    announced_on: "2026-02-07",
43    sunset_target_on: Some("2026-12-31"),
44    replacement_protocols: &["a2ui", "ag_ui", "mcp_apps"],
45    note: Some("Legacy adk_ui profile remains supported during migration."),
46};
47
48pub const UI_PROTOCOL_CAPABILITIES: &[UiProtocolCapabilitySpec] = &[
49    UiProtocolCapabilitySpec {
50        protocol: "adk_ui",
51        versions: &["1.0"],
52        features: &["legacy_components", "theme", "events"],
53        deprecation: Some(&ADK_UI_LEGACY_DEPRECATION),
54    },
55    UiProtocolCapabilitySpec {
56        protocol: "a2ui",
57        versions: &["0.9"],
58        features: &["jsonl", "createSurface", "updateComponents", "updateDataModel"],
59        deprecation: None,
60    },
61    UiProtocolCapabilitySpec {
62        protocol: "ag_ui",
63        versions: &["0.1"],
64        features: &["run_lifecycle", "custom_events", "event_stream"],
65        deprecation: None,
66    },
67    UiProtocolCapabilitySpec {
68        protocol: "mcp_apps",
69        versions: &["sep-1865"],
70        features: &["ui_resource_uri", "tool_meta", "html_resource"],
71        deprecation: None,
72    },
73];
74
75/// Normalize runtime UI profile aliases to canonical values.
76pub fn normalize_runtime_ui_protocol(raw: &str) -> Option<&'static str> {
77    match raw.trim().to_ascii_lowercase().as_str() {
78        "adk_ui" => Some("adk_ui"),
79        "a2ui" => Some("a2ui"),
80        "ag_ui" | "ag-ui" => Some("ag_ui"),
81        "mcp_apps" | "mcp-apps" => Some("mcp_apps"),
82        _ => None,
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn normalize_runtime_protocol_accepts_aliases() {
92        assert_eq!(normalize_runtime_ui_protocol("adk_ui"), Some("adk_ui"));
93        assert_eq!(normalize_runtime_ui_protocol("A2UI"), Some("a2ui"));
94        assert_eq!(normalize_runtime_ui_protocol("ag-ui"), Some("ag_ui"));
95        assert_eq!(normalize_runtime_ui_protocol("mcp-apps"), Some("mcp_apps"));
96        assert_eq!(normalize_runtime_ui_protocol("unknown"), None);
97    }
98
99    #[test]
100    fn capability_specs_cover_supported_protocols() {
101        let protocols: Vec<&str> =
102            UI_PROTOCOL_CAPABILITIES.iter().map(|spec| spec.protocol).collect();
103        assert_eq!(protocols, SUPPORTED_UI_PROTOCOLS);
104    }
105
106    #[test]
107    fn capability_specs_include_versions() {
108        for spec in UI_PROTOCOL_CAPABILITIES {
109            assert!(!spec.versions.is_empty(), "missing versions for {}", spec.protocol);
110            assert!(!spec.features.is_empty(), "missing features for {}", spec.protocol);
111        }
112    }
113
114    #[test]
115    fn legacy_profile_has_deprecation_metadata() {
116        let legacy = UI_PROTOCOL_CAPABILITIES
117            .iter()
118            .find(|spec| spec.protocol == "adk_ui")
119            .expect("adk_ui capability");
120        let deprecation = legacy.deprecation.expect("adk_ui deprecation metadata");
121        assert_eq!(deprecation.announced_on, "2026-02-07");
122        assert_eq!(deprecation.sunset_target_on, Some("2026-12-31"));
123    }
124}