Skip to main content

chio_cross_protocol/
discovery.rs

1use std::collections::BTreeMap;
2use std::fmt;
3
4use chio_manifest::ToolDefinition;
5use serde::{Deserialize, Serialize};
6
7use crate::execution::TargetProtocolExecutor;
8use crate::validation::schema_string_extension;
9
10/// Shared target-protocol registry and default binding policy for
11/// claim-eligible routes.
12pub struct TargetProtocolRegistry<'a> {
13    default_target_protocol: DiscoveryProtocol,
14    executors: BTreeMap<DiscoveryProtocol, &'a dyn TargetProtocolExecutor>,
15}
16
17/// Protocol families Chio can bridge across.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
19#[serde(rename_all = "snake_case")]
20pub enum DiscoveryProtocol {
21    Native,
22    Http,
23    Mcp,
24    A2a,
25    Acp,
26    OpenAi,
27}
28
29impl DiscoveryProtocol {
30    #[must_use]
31    pub fn as_str(self) -> &'static str {
32        match self {
33            Self::Native => "native",
34            Self::Http => "http",
35            Self::Mcp => "mcp",
36            Self::A2a => "a2a",
37            Self::Acp => "acp",
38            Self::OpenAi => "open_ai",
39        }
40    }
41}
42
43impl fmt::Display for DiscoveryProtocol {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        f.write_str(self.as_str())
46    }
47}
48
49impl<'a> TargetProtocolRegistry<'a> {
50    #[must_use]
51    pub fn new(default_target_protocol: DiscoveryProtocol) -> Self {
52        Self {
53            default_target_protocol,
54            executors: BTreeMap::new(),
55        }
56    }
57
58    #[must_use]
59    pub fn with_executor(mut self, executor: &'a dyn TargetProtocolExecutor) -> Self {
60        self.executors.insert(executor.target_protocol(), executor);
61        self
62    }
63
64    #[must_use]
65    pub fn default_target_protocol(&self) -> DiscoveryProtocol {
66        self.default_target_protocol
67    }
68
69    pub fn resolve_target_protocol(
70        &self,
71        tool: &ToolDefinition,
72    ) -> Result<DiscoveryProtocol, String> {
73        let target = match schema_string_extension(&tool.input_schema, "x-chio-target-protocol")? {
74            Some(value) => Some(value),
75            None => match tool.output_schema.as_ref() {
76                Some(schema) => schema_string_extension(schema, "x-chio-target-protocol")?,
77                None => None,
78            },
79        };
80
81        match target {
82            Some(value) => parse_discovery_protocol(&value),
83            None => Ok(self.default_target_protocol),
84        }
85    }
86
87    #[must_use]
88    pub fn supports_target_protocol(&self, protocol: DiscoveryProtocol) -> bool {
89        protocol == DiscoveryProtocol::Native || self.executors.contains_key(&protocol)
90    }
91
92    pub(crate) fn executor_for_target(
93        &self,
94        protocol: DiscoveryProtocol,
95    ) -> Option<&'a dyn TargetProtocolExecutor> {
96        self.executors.get(&protocol).copied()
97    }
98}
99
100/// Resolve the authoritative target protocol advertised for a tool.
101///
102/// `x-chio-target-protocol` follows the same schema-extension pattern as the
103/// other `x-chio-*` bridge hints. When omitted, Chio defaults to `native`.
104pub fn target_protocol_for_tool(tool: &ToolDefinition) -> Result<DiscoveryProtocol, String> {
105    TargetProtocolRegistry::new(DiscoveryProtocol::Native).resolve_target_protocol(tool)
106}
107
108/// Resolve the authoritative target protocol using an explicit registry policy
109/// rather than silent `Native` fallback.
110pub fn target_protocol_for_tool_with_registry(
111    tool: &ToolDefinition,
112    registry: &TargetProtocolRegistry<'_>,
113) -> Result<DiscoveryProtocol, String> {
114    registry.resolve_target_protocol(tool)
115}
116
117/// Parse a protocol-family name used in bridge metadata.
118pub fn parse_discovery_protocol(value: &str) -> Result<DiscoveryProtocol, String> {
119    let normalized = value.trim().to_ascii_lowercase();
120    match normalized.as_str() {
121        "native" => Ok(DiscoveryProtocol::Native),
122        "http" => Ok(DiscoveryProtocol::Http),
123        "mcp" => Ok(DiscoveryProtocol::Mcp),
124        "a2a" => Ok(DiscoveryProtocol::A2a),
125        "acp" => Ok(DiscoveryProtocol::Acp),
126        "open_ai" | "openai" => Ok(DiscoveryProtocol::OpenAi),
127        _ => Err(format!(
128            "unsupported x-chio-target-protocol value `{value}`; expected one of native, http, mcp, a2a, acp, open_ai"
129        )),
130    }
131}