Skip to main content

mur_common/
capability.rs

1//! A `Capability`: a standalone-installable bundle of MCP server(s) + skill
2//! refs + external-program requirements + suggested entitlements. Reuses the
3//! existing agent primitives; installed into an agent's profile.
4
5use crate::agent::McpServerEntry;
6use crate::deps::ProgramDep;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct Capability {
11    pub name: String,
12    pub version: String,
13    pub description: String,
14    #[serde(default)]
15    pub mcp_servers: Vec<McpServerEntry>,
16    #[serde(default)]
17    pub skills: Vec<String>,
18    #[serde(default)]
19    pub requires_programs: Vec<ProgramDep>,
20    #[serde(default)]
21    pub entitlements: CapabilityEntitlements,
22}
23
24/// Suggested entitlements a capability requests at install; each list is
25/// unioned into the agent's `Entitlements` after consent.
26#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27pub struct CapabilityEntitlements {
28    #[serde(default)]
29    pub spawn_programs: Vec<String>,
30    #[serde(default)]
31    pub network_hosts: Vec<String>,
32    #[serde(default)]
33    pub filesystem_read: Vec<String>,
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn capability_yaml_round_trips() {
42        let yaml = "\
43name: demo
44version: 1.0.0
45description: a demo capability
46skills:
47  - foo-skill
48requires_programs:
49  - name: vlc
50    detect:
51      command: vlc
52    reason: needed for playback
53entitlements:
54  network_hosts:
55    - 127.0.0.1
56";
57        let c: Capability = serde_yaml_ng::from_str(yaml).unwrap();
58        assert_eq!(c.name, "demo");
59        assert_eq!(c.skills, vec!["foo-skill"]);
60        assert_eq!(c.requires_programs.len(), 1);
61        assert_eq!(c.requires_programs[0].name, "vlc");
62        assert_eq!(c.entitlements.network_hosts, vec!["127.0.0.1"]);
63        let back = serde_yaml_ng::to_string(&c).unwrap();
64        let c2: Capability = serde_yaml_ng::from_str(&back).unwrap();
65        assert_eq!(c, c2);
66    }
67}