Skip to main content

systemprompt_models/a2a/agent_card/
extension.rs

1//! Agent capability flags and the named extension catalogue.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use serde::{Deserialize, Serialize};
7
8pub const ARTIFACT_RENDERING_URI: &str = "https://systemprompt.io/extensions/artifact-rendering/v1";
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
11#[serde(rename_all = "camelCase")]
12pub struct AgentCapabilities {
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub streaming: Option<bool>,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub push_notifications: Option<bool>,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub state_transition_history: Option<bool>,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub extensions: Option<Vec<AgentExtension>>,
21}
22
23impl Default for AgentCapabilities {
24    fn default() -> Self {
25        Self {
26            streaming: Some(true),
27            push_notifications: Some(true),
28            state_transition_history: Some(true),
29            extensions: None,
30        }
31    }
32}
33
34impl AgentCapabilities {
35    #[must_use]
36    pub const fn normalize(mut self) -> Self {
37        if self.streaming.is_none() {
38            self.streaming = Some(true);
39        }
40        if self.push_notifications.is_none() {
41            self.push_notifications = Some(false);
42        }
43        if self.state_transition_history.is_none() {
44            self.state_transition_history = Some(true);
45        }
46        self
47    }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
51pub struct AgentExtension {
52    pub uri: String,
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub description: Option<String>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub required: Option<bool>,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    // JSON: A2A `AgentExtension.params` is spec-defined as a free-form object.
59    pub params: Option<serde_json::Value>,
60}
61
62impl AgentExtension {
63    #[must_use]
64    pub fn mcp_tools_extension() -> Self {
65        Self {
66            uri: "systemprompt:mcp-tools".to_owned(),
67            description: Some("MCP tool execution capabilities".to_owned()),
68            required: Some(false),
69            params: Some(serde_json::json!({
70                "supported_protocols": ["mcp-1.0"]
71            })),
72        }
73    }
74
75    #[must_use]
76    pub fn agent_identity(agent_name: &str) -> Self {
77        Self {
78            uri: "systemprompt:agent-identity".to_owned(),
79            description: Some("systemprompt.io platform agent name".to_owned()),
80            required: Some(true),
81            params: Some(serde_json::json!({
82                "name": agent_name
83            })),
84        }
85    }
86
87    #[must_use]
88    pub fn system_instructions(system_prompt: &str) -> Self {
89        Self {
90            uri: "systemprompt:system-instructions".to_owned(),
91            description: Some("Agent system prompt and behavioral guidelines".to_owned()),
92            required: Some(true),
93            params: Some(serde_json::json!({
94                "systemPrompt": system_prompt,
95                "format": "text/plain"
96            })),
97        }
98    }
99
100    #[must_use]
101    pub fn system_instructions_opt(system_prompt: Option<&str>) -> Option<Self> {
102        system_prompt.map(Self::system_instructions)
103    }
104
105    #[must_use]
106    pub fn service_status(
107        status: &str,
108        port: Option<u16>,
109        pid: Option<u32>,
110        default: bool,
111    ) -> Self {
112        let mut params = serde_json::json!({
113            "status": status,
114            "default": default
115        });
116
117        if let Some(p) = port {
118            params["port"] = serde_json::json!(p);
119        }
120        if let Some(p) = pid {
121            params["pid"] = serde_json::json!(p);
122        }
123
124        Self {
125            uri: "systemprompt:service-status".to_owned(),
126            description: Some("Runtime service status from orchestrator".to_owned()),
127            required: Some(true),
128            params: Some(params),
129        }
130    }
131}