aether-mcp-servers 0.8.9

Built-in MCP tool servers for the Aether AI agent framework
Documentation
use rmcp::ErrorData;
use rmcp::handler::server::tool::IntoCallToolResult;
use rmcp::model::{CallToolResponse, CallToolResult};
use serde::Serialize;
use utils::display_meta::ToolResultMeta;

#[derive(Debug)]
pub struct Displayed<T> {
    pub output: T,
    pub meta: Option<ToolResultMeta>,
}

impl<T> Displayed<T> {
    pub fn new(output: T, meta: impl Into<ToolResultMeta>) -> Self {
        Self { output, meta: Some(meta.into()) }
    }
}

impl<T: Serialize> Displayed<T> {
    pub fn into_result(self) -> Result<CallToolResult, serde_json::Error> {
        let mut result = CallToolResult::structured(serde_json::to_value(self.output)?);
        result.meta = self.meta.map(|meta| serde_json::to_value(meta).and_then(serde_json::from_value)).transpose()?;
        Ok(result)
    }
}

impl<T: Serialize> IntoCallToolResult for Displayed<T> {
    fn into_call_tool_result(self) -> Result<CallToolResponse, ErrorData> {
        self.into_result()
            .map(Into::into)
            .map_err(|error| ErrorData::internal_error(format!("Failed to serialize tool result: {error}"), None))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;
    use utils::display_meta::ToolDisplayMeta;

    #[derive(Serialize)]
    struct Output {
        lines: u32,
    }

    #[test]
    fn output_is_structured_content_and_meta_is_the_result_meta() {
        let result = Displayed::new(Output { lines: 3 }, ToolDisplayMeta::new("Read file", "main.rs")).into_result();

        let result = result.unwrap();
        assert_eq!(result.structured_content, Some(json!({ "lines": 3 })));
        let meta = result.meta.expect("result meta");
        assert_eq!(meta.0["display"], json!({ "title": "Read file", "value": "main.rs" }));
    }
}