chio_cross_protocol/
capability_bridge.rs1use chio_core::capability::{scope::ChioScope, token::CapabilityToken};
2use chio_core::{canonical_json_bytes, sha256_hex};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::discovery::DiscoveryProtocol;
7use crate::error::BridgeError;
8
9pub const CROSS_PROTOCOL_AUTHORITY_PATH: &str = "cross_protocol_orchestrator";
10pub const CROSS_PROTOCOL_CAPABILITY_ENVELOPE_SCHEMA: &str = "chio.cross-protocol-cap.v1";
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct CrossProtocolCapabilityRef {
16 pub chio_capability_id: String,
17 pub origin_protocol: DiscoveryProtocol,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub protocol_context: Option<Value>,
20 pub parent_capability_hash: String,
21}
22
23impl CrossProtocolCapabilityRef {
24 pub fn from_capability(
25 capability: &CapabilityToken,
26 origin_protocol: DiscoveryProtocol,
27 protocol_context: Option<Value>,
28 ) -> Result<Self, BridgeError> {
29 Ok(Self {
30 chio_capability_id: capability.id.clone(),
31 origin_protocol,
32 protocol_context,
33 parent_capability_hash: parent_capability_hash(capability)?,
34 })
35 }
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct CrossProtocolCapabilityEnvelope {
42 pub schema: String,
43 pub capability_ref: CrossProtocolCapabilityRef,
44 pub target_protocol: DiscoveryProtocol,
45 pub attenuated_scope: ChioScope,
46 pub bridged_at: u64,
47 pub bridge_id: String,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52#[serde(rename_all = "camelCase")]
53pub struct ProtocolHop {
54 pub protocol: DiscoveryProtocol,
55 pub request_id: String,
56 #[serde(default, skip_serializing_if = "Option::is_none")]
57 pub receipt_id: Option<String>,
58 pub bridge_id: String,
59 pub timestamp: u64,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct CrossProtocolTraceContext {
66 pub trace_id: String,
67 pub hops: Vec<ProtocolHop>,
68 pub session_fingerprint: String,
69}
70
71pub trait CapabilityBridge: Send + Sync {
73 fn source_protocol(&self) -> DiscoveryProtocol;
74
75 fn extract_capability_ref(
76 &self,
77 request: &Value,
78 ) -> Result<Option<CrossProtocolCapabilityRef>, BridgeError>;
79
80 fn inject_capability_ref(
81 &self,
82 envelope: &mut Value,
83 cap_ref: &CrossProtocolCapabilityRef,
84 ) -> Result<(), BridgeError>;
85
86 fn protocol_context(&self, _request: &Value) -> Result<Option<Value>, BridgeError> {
87 Ok(None)
88 }
89}
90
91pub(crate) fn parent_capability_hash(capability: &CapabilityToken) -> Result<String, BridgeError> {
92 Ok(sha256_hex(&canonical_json_bytes(capability).map_err(
93 |error| BridgeError::Canonical(error.to_string()),
94 )?))
95}
96
97pub(crate) fn attenuate_scope_for_tool(
98 parent: &ChioScope,
99 server_id: &str,
100 tool_name: &str,
101) -> ChioScope {
102 let grants = parent
103 .grants
104 .iter()
105 .filter_map(|grant| {
106 let server_matches = grant.server_id == server_id || grant.server_id == "*";
107 let tool_matches = grant.tool_name == tool_name || grant.tool_name == "*";
108 if !server_matches || !tool_matches {
109 return None;
110 }
111 let mut narrowed = grant.clone();
112 narrowed.server_id = server_id.to_string();
113 narrowed.tool_name = tool_name.to_string();
114 Some(narrowed)
115 })
116 .collect();
117
118 ChioScope {
119 grants,
120 resource_grants: vec![],
121 prompt_grants: vec![],
122 }
123}