pub mod query;
mod registry;
pub use query::{CapabilitiesResponse, CapabilityEntry};
pub use registry::{
CapabilityRegistration, available_capabilities, build_capabilities, resolve_capabilities,
};
use std::{borrow::Cow, str::FromStr};
use serde::{Deserialize, Serialize};
use crate::Protocol;
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
strum::EnumIter,
strum::EnumString,
strum::Display,
)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CapabilityId {
Sessions,
Streaming,
StructuredOutput,
Hooks,
Builtins,
Shell,
Comms,
MemoryStore,
SessionStore,
SessionCompaction,
Skills,
McpLive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MobpackCapabilityRequirement<'a> {
raw: &'a str,
id: MobpackCapabilityId,
}
impl<'a> MobpackCapabilityRequirement<'a> {
pub fn parse(raw: &'a str) -> Self {
let id = CapabilityId::from_str(raw).map_or_else(
|_| {
HostProcessCapabilityId::parse(raw).map_or(
MobpackCapabilityId::Unknown,
MobpackCapabilityId::HostProcess,
)
},
MobpackCapabilityId::Known,
);
Self { raw, id }
}
pub fn raw(self) -> &'a str {
self.raw
}
pub fn id(self) -> MobpackCapabilityId {
self.id
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MobpackCapabilityId {
Known(CapabilityId),
HostProcess(HostProcessCapabilityId),
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HostProcessCapabilityId {
McpStdio,
ProcessSpawn,
}
impl HostProcessCapabilityId {
pub fn parse(raw: &str) -> Option<Self> {
match raw {
"mcp_stdio" => Some(Self::McpStdio),
"process_spawn" => Some(Self::ProcessSpawn),
_ => None,
}
}
pub fn as_str(self) -> &'static str {
match self {
Self::McpStdio => "mcp_stdio",
Self::ProcessSpawn => "process_spawn",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowserMobpackCapabilityDecision {
Allowed,
Forbidden { capability: MobpackCapabilityId },
}
impl BrowserMobpackCapabilityDecision {
pub fn is_forbidden(self) -> bool {
matches!(self, Self::Forbidden { .. })
}
}
pub fn browser_mobpack_capability_decision(
requirement: MobpackCapabilityRequirement<'_>,
) -> BrowserMobpackCapabilityDecision {
match requirement.id() {
MobpackCapabilityId::Known(CapabilityId::Shell) | MobpackCapabilityId::HostProcess(_) => {
BrowserMobpackCapabilityDecision::Forbidden {
capability: requirement.id(),
}
}
MobpackCapabilityId::Known(_) | MobpackCapabilityId::Unknown => {
BrowserMobpackCapabilityDecision::Allowed
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum CapabilityScope {
Universal,
Extension { protocols: Cow<'static, [Protocol]> },
}
#[cfg(test)]
mod mobpack_policy_tests {
use super::{
BrowserMobpackCapabilityDecision, CapabilityId, HostProcessCapabilityId,
MobpackCapabilityId, MobpackCapabilityRequirement, browser_mobpack_capability_decision,
};
#[test]
fn mobpack_capability_requirement_classifies_known_capabilities() {
let requirement = MobpackCapabilityRequirement::parse("comms");
assert_eq!(
requirement.id(),
MobpackCapabilityId::Known(CapabilityId::Comms)
);
assert_eq!(requirement.raw(), "comms");
}
#[test]
fn mobpack_capability_requirement_classifies_host_process_capabilities() {
assert_eq!(
MobpackCapabilityRequirement::parse("mcp_stdio").id(),
MobpackCapabilityId::HostProcess(HostProcessCapabilityId::McpStdio)
);
assert_eq!(
MobpackCapabilityRequirement::parse("process_spawn").id(),
MobpackCapabilityId::HostProcess(HostProcessCapabilityId::ProcessSpawn)
);
}
#[test]
fn browser_mobpack_policy_forbids_shell_and_host_process_capabilities() {
for raw in ["shell", "mcp_stdio", "process_spawn"] {
assert!(
browser_mobpack_capability_decision(MobpackCapabilityRequirement::parse(raw))
.is_forbidden(),
"{raw} should be forbidden in browser mobpacks"
);
}
}
#[test]
fn browser_mobpack_policy_allows_safe_known_and_unknown_capabilities() {
assert_eq!(
browser_mobpack_capability_decision(MobpackCapabilityRequirement::parse("comms")),
BrowserMobpackCapabilityDecision::Allowed
);
assert_eq!(
browser_mobpack_capability_decision(MobpackCapabilityRequirement::parse(
"vendor.custom"
)),
BrowserMobpackCapabilityDecision::Allowed
);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum CapabilityStatus {
Available,
DisabledByPolicy { description: Cow<'static, str> },
NotCompiled { feature: Cow<'static, str> },
NotSupportedByProtocol { reason: Cow<'static, str> },
}