use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::protocol::{
Cursor,
Request,
Notification,
sampling::{TextContent, ImageContent, AudioContent},
prompts::EmbeddedResource,
};
use crate::protocol::messages::MessageResult;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Tool {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub input_schema: ToolInputSchema,
#[serde(skip_serializing_if = "Option::is_none")]
pub annotations: Option<ToolAnnotations>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ToolInputSchema {
pub r#type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ToolAnnotations {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub read_only_hint: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub destructive_hint: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub idempotent_hint: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub open_world_hint: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ListToolsRequest {
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<ListToolsParams>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ListToolsParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<Cursor>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ListToolsResult {
pub tools: Vec<Tool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
pub meta: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CallToolRequest {
pub method: String,
pub params: CallToolParams,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CallToolParams {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub arguments: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CallToolResult {
pub content: Vec<ToolCallContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_error: Option<bool>,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
pub meta: Option<serde_json::Value>,
}
impl MessageResult for CallToolResult {}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum ToolCallContent {
Text(TextContent),
Image(ImageContent),
Audio(AudioContent),
Resource(EmbeddedResource),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ToolListChangedNotification {
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<serde_json::Value>,
}
impl Request for ListToolsRequest {
const METHOD: &'static str = "tools/list";
fn method(&self) -> &str {
&self.method
}
fn params(&self) -> Option<&serde_json::Value> {
None
}
}
impl Request for CallToolRequest {
const METHOD: &'static str = "tools/call";
fn method(&self) -> &str {
&self.method
}
fn params(&self) -> Option<&serde_json::Value> {
None
}
}
impl Notification for ToolListChangedNotification {
const METHOD: &'static str = "notifications/tools/list_changed";
fn method(&self) -> &str {
&self.method
}
fn params(&self) -> Option<&serde_json::Value> {
None
}
}
impl MessageResult for ListToolsResult {}
impl ListToolsRequest {
pub fn new() -> Self {
Self {
method: Self::METHOD.to_string(),
params: None,
}
}
pub fn with_cursor(cursor: impl Into<String>) -> Self {
Self {
method: Self::METHOD.to_string(),
params: Some(ListToolsParams {
cursor: Some(cursor.into()),
}),
}
}
}
impl CallToolRequest {
pub fn new(name: impl Into<String>) -> Self {
Self {
method: Self::METHOD.to_string(),
params: CallToolParams {
name: name.into(),
arguments: None,
},
}
}
pub fn with_arguments(
name: impl Into<String>,
arguments: serde_json::Value,
) -> Self {
Self {
method: Self::METHOD.to_string(),
params: CallToolParams {
name: name.into(),
arguments: Some(arguments),
},
}
}
}
impl ToolListChangedNotification {
pub fn new() -> Self {
Self {
method: Self::METHOD.to_string(),
params: None,
}
}
}
impl Tool {
pub fn new(
name: impl Into<String>,
description: impl Into<String>,
) -> Self {
Self {
name: name.into(),
description: Some(description.into()),
input_schema: ToolInputSchema {
r#type: "object".to_string(),
properties: None,
required: None,
},
annotations: None,
}
}
pub fn with_schema(mut self, properties: HashMap<String, serde_json::Value>, required: Vec<String>) -> Self {
self.input_schema = ToolInputSchema {
r#type: "object".to_string(),
properties: Some(properties),
required: Some(required),
};
self
}
pub fn with_annotations(mut self, annotations: ToolAnnotations) -> Self {
self.annotations = Some(annotations);
self
}
}
impl ToolAnnotations {
pub fn new() -> Self {
Self {
title: None,
read_only_hint: None,
destructive_hint: None,
idempotent_hint: None,
open_world_hint: None,
}
}
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
pub fn read_only(mut self) -> Self {
self.read_only_hint = Some(true);
self
}
pub fn destructive(mut self, is_destructive: bool) -> Self {
self.destructive_hint = Some(is_destructive);
self
}
pub fn idempotent(mut self, is_idempotent: bool) -> Self {
self.idempotent_hint = Some(is_idempotent);
self
}
pub fn open_world(mut self, is_open_world: bool) -> Self {
self.open_world_hint = Some(is_open_world);
self
}
}