mcp_exec/
describe.rs

1use anyhow::Result;
2use serde_json::Value;
3
4use crate::{ExecConfig, ExecError, ExecRequest, exec};
5
6#[derive(Debug)]
7pub enum Maybe<T> {
8    Data(T),
9    Unsupported,
10}
11
12#[derive(Debug)]
13pub struct ToolDescribe {
14    pub capabilities: Maybe<Vec<String>>,
15    pub secrets: Maybe<Value>,
16    pub config_schema: Maybe<Value>,
17}
18
19pub fn describe_tool(name: &str, cfg: &ExecConfig) -> Result<ToolDescribe> {
20    fn try_action(name: &str, action: &str, cfg: &ExecConfig) -> Result<Maybe<Value>> {
21        let req = ExecRequest {
22            component: name.to_string(),
23            action: action.to_string(),
24            args: Value::Object(Default::default()),
25            tenant: None,
26        };
27
28        match exec(req, cfg) {
29            Ok(v) => Ok(Maybe::Data(v)),
30            Err(ExecError::NotFound { .. }) => Ok(Maybe::Unsupported),
31            Err(ExecError::Tool { code, payload, .. }) if code == "iface-error.not-found" => {
32                let _ = payload;
33                Ok(Maybe::Unsupported)
34            }
35            Err(e) => Err(e.into()),
36        }
37    }
38
39    let capabilities_value = try_action(name, "capabilities", cfg)?;
40    let secrets = try_action(name, "list_secrets", cfg)?;
41    let config_schema = try_action(name, "config_schema", cfg)?;
42
43    let capabilities = match capabilities_value {
44        Maybe::Data(value) => {
45            if let Some(arr) = value.as_array() {
46                let list = arr
47                    .iter()
48                    .filter_map(|v| v.as_str().map(|s| s.to_string()))
49                    .collect::<Vec<_>>();
50                Maybe::Data(list)
51            } else {
52                Maybe::Data(Vec::new())
53            }
54        }
55        Maybe::Unsupported => Maybe::Unsupported,
56    };
57
58    Ok(ToolDescribe {
59        capabilities,
60        secrets,
61        config_schema,
62    })
63}