mcp-attr 0.0.7

A library for declaratively building Model Context Protocol servers.
Documentation
use std::{collections::BTreeMap, fmt::Display, str::FromStr};

use jsoncall::{ErrorCode, bail_public};
use serde::de::DeserializeOwned;
use serde_json::{Map, Value};
use uri_template_ex::Captures;

use crate::Result;

pub use uri_template_ex;

pub fn parse_prompt_arg_opt<T>(
    arguments: &BTreeMap<String, String>,
    name: &str,
) -> Result<Option<T>>
where
    T: FromStr,
    T::Err: Display,
{
    if let Some(value) = arguments.get(name) {
        match T::from_str(value) {
            Ok(v) => Ok(Some(v)),
            Err(e) => bail_public!(
                ErrorCode::INVALID_PARAMS,
                "argument `{name}` is invalid ({e})"
            ),
        }
    } else {
        Ok(None)
    }
}

pub fn parse_prompt_arg<T>(arguments: &BTreeMap<String, String>, name: &str) -> Result<T>
where
    T: FromStr,
    T::Err: Display,
{
    if let Some(value) = parse_prompt_arg_opt(arguments, name)? {
        Ok(value)
    } else {
        bail_public!(ErrorCode::INVALID_PARAMS, "argument `{name}` is required");
    }
}

pub fn parse_resource_arg_opt<T>(captures: &Captures, index: usize, name: &str) -> Result<Option<T>>
where
    T: FromStr,
    T::Err: Display,
{
    if let Some(value) = captures.get(index) {
        match T::from_str(value.value()?.as_ref()) {
            Ok(v) => Ok(Some(v)),
            Err(e) => bail_public!(
                ErrorCode::INVALID_PARAMS,
                "variable `{name}` is invalid ({e})"
            ),
        }
    } else {
        Ok(None)
    }
}

pub fn parse_resource_arg<T>(captures: &Captures, index: usize, name: &str) -> Result<T>
where
    T: FromStr,
    T::Err: Display,
{
    if let Some(value) = parse_resource_arg_opt(captures, index, name)? {
        Ok(value)
    } else {
        bail_public!(ErrorCode::INVALID_PARAMS, "variable `{name}` is required");
    }
}

pub fn parse_tool_arg_opt<T>(
    arguments: &Option<Map<String, Value>>,
    name: &str,
) -> Result<Option<T>>
where
    T: DeserializeOwned,
{
    if let Some(arguments) = arguments {
        let value = arguments.get(name);
        if let Some(value) = value {
            return match serde_json::from_value(value.clone()) {
                Ok(v) => Ok(Some(v)),
                Err(e) => {
                    if value.is_null() {
                        Ok(None)
                    } else {
                        Err(e.into())
                    }
                }
            };
        }
    }
    Ok(None)
}
pub fn parse_tool_arg<T: DeserializeOwned>(
    arguments: &Option<Map<String, Value>>,
    name: &str,
) -> Result<T> {
    if let Some(value) = parse_tool_arg_opt(arguments, name)? {
        Ok(value)
    } else {
        bail_public!(ErrorCode::INVALID_PARAMS, "argument `{name}` is required");
    }
}