techne_mcp/server/
tool.rs1use 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}