Skip to main content

systemprompt_models/a2a/agent_card/
extension.rs

1//! Agent capability flags and the named extension catalogue.
2
3use serde::{Deserialize, Serialize};
4
5pub const ARTIFACT_RENDERING_URI: &str = "https://systemprompt.io/extensions/artifact-rendering/v1";
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
8#[serde(rename_all = "camelCase")]
9pub struct AgentCapabilities {
10    #[serde(skip_serializing_if = "Option::is_none")]
11    pub streaming: Option<bool>,
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub push_notifications: Option<bool>,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub state_transition_history: Option<bool>,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub extensions: Option<Vec<AgentExtension>>,
18}
19
20impl Default for AgentCapabilities {
21    fn default() -> Self {
22        Self {
23            streaming: Some(true),
24            push_notifications: Some(true),
25            state_transition_history: Some(true),
26            extensions: None,
27        }
28    }
29}
30
31impl AgentCapabilities {
32    #[must_use]
33    pub const fn normalize(mut self) -> Self {
34        if self.streaming.is_none() {
35            self.streaming = Some(true);
36        }
37        if self.push_notifications.is_none() {
38            self.push_notifications = Some(false);
39        }
40        if self.state_transition_history.is_none() {
41            self.state_transition_history = Some(true);
42        }
43        self
44    }
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
48pub struct AgentExtension {
49    pub uri: String,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub description: Option<String>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub required: Option<bool>,
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub params: Option<serde_json::Value>,
56}
57
58impl AgentExtension {
59    #[must_use]
60    pub fn mcp_tools_extension() -> Self {
61        Self {
62            uri: "systemprompt:mcp-tools".to_string(),
63            description: Some("MCP tool execution capabilities".to_string()),
64            required: Some(false),
65            params: Some(serde_json::json!({
66                "supported_protocols": ["mcp-1.0"]
67            })),
68        }
69    }
70
71    #[must_use]
72    pub fn mcp_tools_extension_with_servers(servers: &[serde_json::Value]) -> Self {
73        Self {
74            uri: "systemprompt:mcp-tools".to_string(),
75            description: Some("MCP tool execution capabilities with server endpoints".to_string()),
76            required: Some(false),
77            params: Some(serde_json::json!({
78                "supported_protocols": ["mcp-1.0"],
79                "servers": servers
80            })),
81        }
82    }
83
84    #[must_use]
85    pub fn opencode_integration_extension() -> Self {
86        Self {
87            uri: "systemprompt:opencode-integration".to_string(),
88            description: Some("OpenCode AI reasoning integration".to_string()),
89            required: Some(false),
90            params: Some(serde_json::json!({
91                "reasoning_model": "claude-3-5-sonnet",
92                "execution_mode": "structured_planning"
93            })),
94        }
95    }
96
97    #[must_use]
98    pub fn artifact_rendering_extension() -> Self {
99        Self {
100            uri: ARTIFACT_RENDERING_URI.to_string(),
101            description: Some(
102                "MCP tool results rendered as typed artifacts with UI hints".to_string(),
103            ),
104            required: Some(false),
105            params: Some(serde_json::json!({
106                "supported_types": ["table", "form", "chart", "tree", "code", "json", "markdown"],
107                "version": "1.0.0"
108            })),
109        }
110    }
111
112    #[must_use]
113    pub fn agent_identity(agent_name: &str) -> Self {
114        Self {
115            uri: "systemprompt:agent-identity".to_string(),
116            description: Some("systemprompt.io platform agent name".to_string()),
117            required: Some(true),
118            params: Some(serde_json::json!({
119                "name": agent_name
120            })),
121        }
122    }
123
124    #[must_use]
125    pub fn system_instructions(system_prompt: &str) -> Self {
126        Self {
127            uri: "systemprompt:system-instructions".to_string(),
128            description: Some("Agent system prompt and behavioral guidelines".to_string()),
129            required: Some(true),
130            params: Some(serde_json::json!({
131                "systemPrompt": system_prompt,
132                "format": "text/plain"
133            })),
134        }
135    }
136
137    #[must_use]
138    pub fn system_instructions_opt(system_prompt: Option<&str>) -> Option<Self> {
139        system_prompt.map(Self::system_instructions)
140    }
141
142    #[must_use]
143    pub fn service_status(
144        status: &str,
145        port: Option<u16>,
146        pid: Option<u32>,
147        default: bool,
148    ) -> Self {
149        let mut params = serde_json::json!({
150            "status": status,
151            "default": default
152        });
153
154        if let Some(p) = port {
155            params["port"] = serde_json::json!(p);
156        }
157        if let Some(p) = pid {
158            params["pid"] = serde_json::json!(p);
159        }
160
161        Self {
162            uri: "systemprompt:service-status".to_string(),
163            description: Some("Runtime service status from orchestrator".to_string()),
164            required: Some(true),
165            params: Some(params),
166        }
167    }
168}