use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
pub type ProtocolId = String;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentIdentity {
pub id: Uuid,
pub name: String,
pub agent_card: AgentCard,
}
impl AgentIdentity {
pub fn new(name: impl Into<String>) -> Self {
Self {
id: Uuid::new_v4(),
name: name.into(),
agent_card: AgentCard::default(),
}
}
pub fn with_id(id: Uuid, name: impl Into<String>) -> Self {
Self {
id,
name: name.into(),
agent_card: AgentCard::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AgentCard {
pub capabilities: Vec<String>,
pub supported_protocols: Vec<ProtocolId>,
pub metadata: HashMap<String, serde_json::Value>,
pub endpoint: Option<String>,
pub max_concurrent_tasks: Option<usize>,
pub compute_capacity: Option<f64>,
}
impl AgentCard {
pub fn supports_protocol(&self, protocol: &str) -> bool {
self.supported_protocols
.iter()
.any(|p| p.eq_ignore_ascii_case(protocol))
}
pub fn has_capability(&self, capability: &str) -> bool {
self.capabilities
.iter()
.any(|c| c.eq_ignore_ascii_case(capability))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_identity_has_unique_id() {
let a = AgentIdentity::new("agent-a");
let b = AgentIdentity::new("agent-b");
assert_ne!(a.id, b.id);
assert_eq!(a.name, "agent-a");
}
#[test]
fn with_id_preserves_uuid() {
let id = Uuid::nil();
let identity = AgentIdentity::with_id(id, "test");
assert_eq!(identity.id, Uuid::nil());
}
#[test]
fn agent_card_protocol_check() {
let card = AgentCard {
supported_protocols: vec!["mcp".into(), "a2a".into()],
..Default::default()
};
assert!(card.supports_protocol("MCP"));
assert!(card.supports_protocol("a2a"));
assert!(!card.supports_protocol("ipc"));
}
#[test]
fn agent_card_capability_check() {
let card = AgentCard {
capabilities: vec!["code-review".into(), "file-editing".into()],
..Default::default()
};
assert!(card.has_capability("Code-Review"));
assert!(!card.has_capability("deploy"));
}
#[test]
fn identity_serde_roundtrip() {
let mut identity = AgentIdentity::new("test-agent");
identity.agent_card.capabilities = vec!["search".into()];
identity.agent_card.endpoint = Some("tcp://localhost:9090".into());
let json = serde_json::to_string(&identity).unwrap();
let deserialized: AgentIdentity = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.id, identity.id);
assert_eq!(deserialized.name, "test-agent");
assert_eq!(deserialized.agent_card.capabilities, vec!["search"]);
assert_eq!(
deserialized.agent_card.endpoint.as_deref(),
Some("tcp://localhost:9090")
);
}
#[test]
fn default_agent_card_is_empty() {
let card = AgentCard::default();
assert!(card.capabilities.is_empty());
assert!(card.supported_protocols.is_empty());
assert!(card.metadata.is_empty());
assert!(card.endpoint.is_none());
assert!(card.max_concurrent_tasks.is_none());
assert!(card.compute_capacity.is_none());
}
}