1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
7#[serde(rename_all = "snake_case")]
8pub enum ToolEnvelopeProtocol {
9 AdkUi,
10 A2ui,
11 AgUi,
12 McpApps,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
20pub struct ToolEnvelope<P> {
21 pub protocol: ToolEnvelopeProtocol,
22 pub version: String,
23 pub surface_id: String,
24 #[serde(flatten)]
25 pub payload: P,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub meta: Option<Value>,
28}
29
30impl<P> ToolEnvelope<P> {
31 pub fn new(protocol: ToolEnvelopeProtocol, surface_id: impl Into<String>, payload: P) -> Self {
32 Self {
33 protocol,
34 version: "1.0".to_string(),
35 surface_id: surface_id.into(),
36 payload,
37 meta: None,
38 }
39 }
40
41 pub fn with_meta(mut self, meta: Option<Value>) -> Self {
42 self.meta = meta;
43 self
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use serde::Serialize;
51 use serde_json::json;
52
53 #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
54 struct Payload {
55 value: String,
56 }
57
58 #[test]
59 fn envelope_serializes_flattened_payload() {
60 let envelope = ToolEnvelope::new(
61 ToolEnvelopeProtocol::A2ui,
62 "main",
63 Payload { value: "ok".to_string() },
64 )
65 .with_meta(Some(json!({"trace_id": "abc"})));
66
67 let value = serde_json::to_value(envelope).expect("serialize");
68 assert_eq!(value["protocol"], "a2ui");
69 assert_eq!(value["surface_id"], "main");
70 assert_eq!(value["version"], "1.0");
71 assert_eq!(value["value"], "ok");
72 assert_eq!(value["meta"]["trace_id"], "abc");
73 }
74}