Skip to main content

synapse_application/
binding.rs

1use serde::{Deserialize, Serialize};
2use soma_ops::OperationName;
3
4/// Legacy Synapse MCP tool family.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
6#[serde(rename_all = "snake_case")]
7pub enum LegacyTool {
8    /// Flux Docker and host operations.
9    Flux,
10    /// Scout filesystem and SSH operations.
11    Scout,
12    /// Binding shared by both tools, currently only help.
13    Both,
14}
15
16impl LegacyTool {
17    /// Returns the legacy MCP tool name.
18    #[must_use]
19    pub const fn as_str(self) -> &'static str {
20        match self {
21            Self::Flux => "flux",
22            Self::Scout => "scout",
23            Self::Both => "both",
24        }
25    }
26}
27
28/// Legacy Synapse authorization classification.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum LegacyAccess {
32    /// Public help operation.
33    Public,
34    /// Requires the read scope.
35    Read,
36    /// Requires the write scope.
37    Write,
38}
39
40/// Legacy transport exposure recorded by the donor registry.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
42#[serde(rename_all = "snake_case")]
43pub enum LegacyTransport {
44    /// Exposed through REST and MCP compatibility surfaces.
45    Rest,
46    /// Exposed only through MCP compatibility surfaces.
47    McpOnly,
48}
49
50/// Legacy response presentation requested by Flux or Scout.
51#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
52pub enum LegacyPresentation {
53    /// Human-readable Markdown.
54    #[default]
55    Markdown,
56    /// Structured JSON.
57    Json,
58}
59
60/// Product-owned binding from one legacy route to a canonical operation.
61#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
62pub struct LegacyOperationBinding {
63    legacy_name: String,
64    canonical_name: OperationName,
65    legacy_tool: LegacyTool,
66    legacy_action: String,
67    legacy_subaction: Option<String>,
68    legacy_access: LegacyAccess,
69    legacy_scope: Option<String>,
70    legacy_destructive: bool,
71    legacy_transport: LegacyTransport,
72    required_params: Vec<String>,
73    required_any: Vec<Vec<String>>,
74}
75
76impl LegacyOperationBinding {
77    /// Returns the historical operation name.
78    #[must_use]
79    pub fn legacy_name(&self) -> &str {
80        &self.legacy_name
81    }
82    /// Returns the canonical operation name.
83    #[must_use]
84    pub fn canonical_name(&self) -> &OperationName {
85        &self.canonical_name
86    }
87    /// Returns the legacy tool owner.
88    #[must_use]
89    pub const fn tool(&self) -> LegacyTool {
90        self.legacy_tool
91    }
92    /// Returns the legacy action.
93    #[must_use]
94    pub fn action(&self) -> &str {
95        &self.legacy_action
96    }
97    /// Returns the optional legacy subaction.
98    #[must_use]
99    pub fn subaction(&self) -> Option<&str> {
100        self.legacy_subaction.as_deref()
101    }
102    /// Returns the legacy authorization class.
103    #[must_use]
104    pub const fn access(&self) -> LegacyAccess {
105        self.legacy_access
106    }
107    /// Returns the product authorization scope.
108    #[must_use]
109    pub fn scope(&self) -> Option<&str> {
110        self.legacy_scope.as_deref()
111    }
112    /// Returns the historical destructive flag.
113    #[must_use]
114    pub const fn destructive(&self) -> bool {
115        self.legacy_destructive
116    }
117    /// Returns historical transport exposure.
118    #[must_use]
119    pub const fn transport(&self) -> LegacyTransport {
120        self.legacy_transport
121    }
122    /// Returns required legacy fields.
123    #[must_use]
124    pub fn required_params(&self) -> &[String] {
125        &self.required_params
126    }
127    /// Returns alternative complete legacy field groups.
128    #[must_use]
129    pub fn required_any(&self) -> &[Vec<String>] {
130        &self.required_any
131    }
132}
133
134#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
135pub(crate) struct LegacyBindingKey {
136    pub(crate) tool: LegacyTool,
137    pub(crate) action: String,
138    pub(crate) subaction: Option<String>,
139}
140
141impl LegacyBindingKey {
142    pub(crate) fn new(tool: LegacyTool, action: &str, subaction: Option<&str>) -> Self {
143        Self {
144            tool,
145            action: action.to_owned(),
146            subaction: subaction.map(str::to_owned),
147        }
148    }
149}
150
151#[cfg(test)]
152#[path = "binding_tests.rs"]
153mod tests;