techne-mcp 0.1.0

Messages and data structures of the Model Context Protocol
Documentation
use crate::Schema;
use crate::server::content::{self, Content};

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Tool {
    pub name: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    pub description: String,
    pub input_schema: Schema,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub output_schema: Option<Schema>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Response<T = serde_json::Value> {
    #[serde(flatten)]
    pub content: Content<T>,
    pub is_error: bool,
}

impl<T> Response<T> {
    pub async fn serialize(self) -> serde_json::Result<Response>
    where
        T: Serialize,
    {
        Ok(Response {
            content: match self.content {
                Content::Unstructured(content) => Content::Unstructured(content),
                Content::Structured(content) => {
                    Content::Structured(serde_json::to_value(&content)?)
                }
            },
            is_error: self.is_error,
        })
    }
}

pub trait IntoResponse {
    type Content;

    fn into_outcome(self) -> Response<Self::Content>;
}

impl<T> IntoResponse for T
where
    T: Into<Content<T>>,
{
    type Content = T;

    fn into_outcome(self) -> Response<Self::Content> {
        Response {
            content: self.into(),
            is_error: false,
        }
    }
}

impl<T, E> IntoResponse for Result<T, E>
where
    T: Into<Content<T>>,
    E: std::error::Error,
{
    type Content = T;

    fn into_outcome(self) -> Response<T> {
        match self {
            Ok(value) => Response {
                content: value.into(),
                is_error: false,
            },
            Err(error) => Response {
                content: Content::Unstructured(vec![content::Unstructured::Text {
                    text: error.to_string(),
                }]),
                is_error: true,
            },
        }
    }
}