techne_mcp/server/
tool.rs

1use crate::Schema;
2use crate::server::content::{self, Content};
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(rename_all = "camelCase")]
8pub struct Tool {
9    pub name: String,
10    #[serde(default, skip_serializing_if = "Option::is_none")]
11    pub title: Option<String>,
12    pub description: String,
13    pub input_schema: Schema,
14    #[serde(default, skip_serializing_if = "Option::is_none")]
15    pub output_schema: Option<Schema>,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct Response<T = serde_json::Value> {
21    #[serde(flatten)]
22    pub content: Content<T>,
23    pub is_error: bool,
24}
25
26impl<T> Response<T> {
27    pub async fn serialize(self) -> serde_json::Result<Response>
28    where
29        T: Serialize,
30    {
31        Ok(Response {
32            content: match self.content {
33                Content::Unstructured(content) => Content::Unstructured(content),
34                Content::Structured(content) => {
35                    Content::Structured(serde_json::to_value(&content)?)
36                }
37            },
38            is_error: self.is_error,
39        })
40    }
41}
42
43pub trait IntoResponse {
44    type Content;
45
46    fn into_outcome(self) -> Response<Self::Content>;
47}
48
49impl<T> IntoResponse for T
50where
51    T: Into<Content<T>>,
52{
53    type Content = T;
54
55    fn into_outcome(self) -> Response<Self::Content> {
56        Response {
57            content: self.into(),
58            is_error: false,
59        }
60    }
61}
62
63impl<T, E> IntoResponse for Result<T, E>
64where
65    T: Into<Content<T>>,
66    E: std::error::Error,
67{
68    type Content = T;
69
70    fn into_outcome(self) -> Response<T> {
71        match self {
72            Ok(value) => Response {
73                content: value.into(),
74                is_error: false,
75            },
76            Err(error) => Response {
77                content: Content::Unstructured(vec![content::Unstructured::Text {
78                    text: error.to_string(),
79                }]),
80                is_error: true,
81            },
82        }
83    }
84}