use crate::Error;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
pub mod mime_types {
pub const HTML_MCP: &str = "text/html+mcp";
pub const HTML: &str = "text/html";
pub const JSON: &str = "application/json";
pub const TEXT: &str = "text/plain";
pub const OCTET_STREAM: &str = "application/octet-stream";
}
pub mod uri_schemes {
pub const UI: &str = "ui://";
pub const FILE: &str = "file://";
pub const HTTP: &str = "http://";
pub const HTTPS: &str = "https://";
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Meta {
#[serde(rename = "progressToken", skip_serializing_if = "Option::is_none")]
pub progress_token: Option<String>,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum NumberOrString {
Number(i64),
String(Arc<str>),
}
impl NumberOrString {
pub fn into_json_value(self) -> serde_json::Value {
match self {
NumberOrString::Number(n) => serde_json::Value::Number(serde_json::Number::from(n)),
NumberOrString::String(s) => serde_json::Value::String(s.to_string()),
}
}
pub fn from_json_value(value: serde_json::Value) -> Option<Self> {
match value {
serde_json::Value::Number(n) => n.as_i64().map(NumberOrString::Number),
serde_json::Value::String(s) => Some(NumberOrString::String(Arc::from(s.as_str()))),
_ => None,
}
}
}
impl std::fmt::Display for NumberOrString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NumberOrString::Number(n) => write!(f, "{n}"),
NumberOrString::String(s) => write!(f, "{s}"),
}
}
}
impl Serialize for NumberOrString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
NumberOrString::Number(n) => serializer.serialize_i64(*n),
NumberOrString::String(s) => serializer.serialize_str(s),
}
}
}
impl<'de> Deserialize<'de> for NumberOrString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct NumberOrStringVisitor;
impl<'de> serde::de::Visitor<'de> for NumberOrStringVisitor {
type Value = NumberOrString;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a number or string")
}
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(NumberOrString::Number(value))
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(NumberOrString::Number(value as i64))
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(NumberOrString::String(Arc::from(value)))
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(NumberOrString::String(Arc::from(value.as_str())))
}
}
deserializer.deserialize_any(NumberOrStringVisitor)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Request {
pub jsonrpc: String,
pub method: String,
#[serde(default = "serde_json::Value::default")]
pub params: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<NumberOrString>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Response {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<Error>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<NumberOrString>,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Serialize, Deserialize)]
pub struct ProtocolVersion(std::borrow::Cow<'static, str>);
impl Default for ProtocolVersion {
fn default() -> Self {
Self::LATEST
}
}
impl std::fmt::Display for ProtocolVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl ProtocolVersion {
pub const V_2025_11_25: Self = Self(std::borrow::Cow::Borrowed("2025-11-25"));
pub const V_2025_06_18: Self = Self(std::borrow::Cow::Borrowed("2025-06-18"));
pub const V_2025_03_26: Self = Self(std::borrow::Cow::Borrowed("2025-03-26"));
pub const V_2024_11_05: Self = Self(std::borrow::Cow::Borrowed("2024-11-05"));
pub const LATEST: Self = Self::V_2025_11_25;
pub fn new(version: impl Into<std::borrow::Cow<'static, str>>) -> Self {
Self(version.into())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Implementation {
pub name: String,
pub version: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub description: Option<String>,
}
impl Implementation {
pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
Self {
name: name.into(),
version: version.into(),
description: None,
}
}
pub fn with_description(
name: impl Into<String>,
version: impl Into<String>,
description: impl Into<String>,
) -> Self {
Self {
name: name.into(),
version: version.into(),
description: Some(description.into()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ServerCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<ToolsCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resources: Option<ResourcesCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompts: Option<PromptsCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logging: Option<LoggingCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sampling: Option<SamplingCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub elicitation: Option<ElicitationCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tasks: Option<TasksCapability>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ToolsCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub list_changed: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResourcesCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub list_changed: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PromptsCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub list_changed: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct LoggingCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub level: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
Emergency,
Alert,
Critical,
Error,
Warning,
Notice,
Info,
Debug,
}
impl LogLevel {
pub fn as_str(&self) -> &'static str {
match self {
LogLevel::Emergency => "emergency",
LogLevel::Alert => "alert",
LogLevel::Critical => "critical",
LogLevel::Error => "error",
LogLevel::Warning => "warning",
LogLevel::Notice => "notice",
LogLevel::Info => "info",
LogLevel::Debug => "debug",
}
}
}
impl std::str::FromStr for LogLevel {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"emergency" => Ok(LogLevel::Emergency),
"alert" => Ok(LogLevel::Alert),
"critical" => Ok(LogLevel::Critical),
"error" => Ok(LogLevel::Error),
"warning" => Ok(LogLevel::Warning),
"notice" => Ok(LogLevel::Notice),
"info" => Ok(LogLevel::Info),
"debug" => Ok(LogLevel::Debug),
_ => Err(format!("Invalid log level: {s}")),
}
}
}
impl std::fmt::Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SamplingCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<SamplingToolsCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<SamplingContextCapability>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SamplingToolsCapability {}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SamplingContextCapability {}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ElicitationCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub form: Option<FormElicitationCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<UrlElicitationCapability>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FormElicitationCapability {}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UrlElicitationCapability {}
impl ServerCapabilities {
pub fn builder() -> ServerCapabilitiesBuilder {
ServerCapabilitiesBuilder::default()
}
}
#[derive(Default)]
pub struct ServerCapabilitiesBuilder {
capabilities: ServerCapabilities,
}
impl ServerCapabilitiesBuilder {
#[must_use]
pub fn enable_tools(mut self) -> Self {
self.capabilities.tools = Some(ToolsCapability {
list_changed: Some(true),
});
self
}
#[must_use]
pub fn enable_resources(mut self) -> Self {
self.capabilities.resources = Some(ResourcesCapability {
subscribe: Some(true),
list_changed: Some(true),
});
self
}
#[must_use]
pub fn enable_prompts(mut self) -> Self {
self.capabilities.prompts = Some(PromptsCapability {
list_changed: Some(true),
});
self
}
#[must_use]
pub fn enable_logging(mut self) -> Self {
self.capabilities.logging = Some(LoggingCapability {
level: Some("info".to_string()),
});
self
}
#[must_use]
pub fn enable_sampling(mut self) -> Self {
self.capabilities.sampling = Some(SamplingCapability::default());
self
}
#[must_use]
pub fn enable_sampling_with_tools(mut self) -> Self {
self.capabilities.sampling = Some(SamplingCapability {
tools: Some(SamplingToolsCapability {}),
context: Some(SamplingContextCapability {}),
});
self
}
#[must_use]
pub fn enable_elicitation(mut self) -> Self {
self.capabilities.elicitation = Some(ElicitationCapability {
form: Some(FormElicitationCapability {}),
url: None,
});
self
}
#[must_use]
pub fn enable_elicitation_modes(mut self, form: bool, url: bool) -> Self {
self.capabilities.elicitation = Some(ElicitationCapability {
form: if form {
Some(FormElicitationCapability {})
} else {
None
},
url: if url {
Some(UrlElicitationCapability {})
} else {
None
},
});
self
}
#[must_use]
pub fn enable_tasks(mut self) -> Self {
self.capabilities.tasks = Some(TasksCapability {
cancel: Some(TaskCancelCapability {}),
list: Some(TaskListCapability {}),
requests: Some(TaskRequestsCapability {
sampling: Some(TaskSamplingCapability {
create_message: Some(TaskMethodCapability {}),
}),
elicitation: Some(TaskElicitationCapability {
create: Some(TaskMethodCapability {}),
}),
tools: Some(TaskToolsCapability {
call: Some(TaskMethodCapability {}),
}),
}),
});
self
}
#[must_use]
pub fn enable_tasks_basic(mut self) -> Self {
self.capabilities.tasks = Some(TasksCapability {
cancel: Some(TaskCancelCapability {}),
list: Some(TaskListCapability {}),
requests: None,
});
self
}
pub fn build(self) -> ServerCapabilities {
self.capabilities
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerInfo {
pub protocol_version: ProtocolVersion,
pub capabilities: ServerCapabilities,
pub server_info: Implementation,
pub instructions: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Tool {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
pub description: String,
pub input_schema: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub output_schema: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub annotations: Option<ToolAnnotations>,
#[serde(skip_serializing_if = "Option::is_none")]
pub icons: Option<Vec<Icon>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub execution: Option<ToolExecution>,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
pub _meta: Option<ToolMeta>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ToolAnnotations {
#[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, Default)]
pub struct ToolMeta {
#[serde(rename = "ui/resourceUri", skip_serializing_if = "Option::is_none")]
pub ui_resource_uri: Option<String>,
}
impl ToolMeta {
pub fn with_ui_resource(uri: impl Into<String>) -> Self {
Self {
ui_resource_uri: Some(uri.into()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Icon {
pub uri: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListToolsResult {
pub tools: Vec<Tool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaginatedRequestParam {
pub cursor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CallToolRequestParam {
pub name: String,
pub arguments: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Content {
#[serde(rename = "text")]
Text {
text: String,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
_meta: Option<Meta>,
},
#[serde(rename = "image")]
Image {
data: String,
#[serde(rename = "mimeType")]
mime_type: String,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
_meta: Option<Meta>,
},
#[serde(rename = "audio")]
Audio {
data: String,
#[serde(rename = "mimeType")]
mime_type: String,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
_meta: Option<Meta>,
},
#[serde(rename = "resource")]
Resource {
resource: EmbeddedResourceContents,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
_meta: Option<Meta>,
},
#[serde(rename = "tool_use")]
ToolUse {
id: String,
name: String,
input: serde_json::Value,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
_meta: Option<Meta>,
},
#[serde(rename = "tool_result")]
ToolResult {
#[serde(rename = "toolUseId")]
tool_use_id: String,
content: Vec<ToolResultContent>,
#[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
is_error: Option<bool>,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
_meta: Option<Meta>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ToolResultContent {
#[serde(rename = "text")]
Text { text: String },
#[serde(rename = "image")]
Image {
data: String,
#[serde(rename = "mimeType")]
mime_type: String,
},
}
impl Content {
pub fn text(text: impl Into<String>) -> Self {
Self::Text {
text: text.into(),
_meta: None,
}
}
pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
Self::Image {
data: data.into(),
mime_type: mime_type.into(),
_meta: None,
}
}
pub fn audio(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
Self::Audio {
data: data.into(),
mime_type: mime_type.into(),
_meta: None,
}
}
pub fn resource(
uri: impl Into<String>,
mime_type: Option<String>,
text: Option<String>,
) -> Self {
Self::Resource {
resource: EmbeddedResourceContents {
uri: uri.into(),
mime_type,
text,
blob: None,
_meta: None,
},
_meta: None,
}
}
pub fn from_resource_contents(contents: ResourceContents) -> Self {
Self::Resource {
resource: contents,
_meta: None,
}
}
pub fn tool_use(
id: impl Into<String>,
name: impl Into<String>,
input: serde_json::Value,
) -> Self {
Self::ToolUse {
id: id.into(),
name: name.into(),
input,
_meta: None,
}
}
pub fn tool_result(
tool_use_id: impl Into<String>,
content: Vec<ToolResultContent>,
is_error: Option<bool>,
) -> Self {
Self::ToolResult {
tool_use_id: tool_use_id.into(),
content,
is_error,
_meta: None,
}
}
pub fn tool_result_text(tool_use_id: impl Into<String>, text: impl Into<String>) -> Self {
Self::tool_result(
tool_use_id,
vec![ToolResultContent::Text { text: text.into() }],
Some(false),
)
}
pub fn tool_result_error(
tool_use_id: impl Into<String>,
error_message: impl Into<String>,
) -> Self {
Self::tool_result(
tool_use_id,
vec![ToolResultContent::Text {
text: error_message.into(),
}],
Some(true),
)
}
pub fn ui_html(uri: impl Into<String>, html: impl Into<String>) -> Self {
Self::resource(uri, Some("text/html".to_string()), Some(html.into()))
}
pub fn ui_resource(
uri: impl Into<String>,
mime_type: impl Into<String>,
content: impl Into<String>,
) -> Self {
Self::resource(uri, Some(mime_type.into()), Some(content.into()))
}
pub fn as_text(&self) -> Option<&Self> {
match self {
Self::Text { .. } => Some(self),
_ => None,
}
}
}
pub struct TextContent {
pub text: String,
}
impl Content {
pub fn as_text_content(&self) -> Option<TextContent> {
match self {
Self::Text { text, .. } => Some(TextContent { text: text.clone() }),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CallToolResult {
pub content: Vec<Content>,
pub is_error: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub structured_content: Option<serde_json::Value>,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
pub _meta: Option<Meta>,
}
impl CallToolResult {
pub fn success(content: Vec<Content>) -> Self {
Self {
content,
is_error: Some(false),
structured_content: None,
_meta: None,
}
}
pub fn error(content: Vec<Content>) -> Self {
Self {
content,
is_error: Some(true),
structured_content: None,
_meta: None,
}
}
pub fn text(text: impl Into<String>) -> Self {
Self::success(vec![Content::text(text)])
}
pub fn error_text(text: impl Into<String>) -> Self {
Self::error(vec![Content::text(text)])
}
pub fn input_validation_error(field: impl Into<String>, message: impl Into<String>) -> Self {
let error_msg = format!(
"Input validation error for '{}': {}",
field.into(),
message.into()
);
Self::error(vec![Content::text(error_msg)])
}
pub fn structured(content: Vec<Content>, structured_content: serde_json::Value) -> Self {
Self {
content,
is_error: Some(false),
structured_content: Some(structured_content),
_meta: None,
}
}
pub fn structured_error(content: Vec<Content>, structured_content: serde_json::Value) -> Self {
Self {
content,
is_error: Some(true),
structured_content: Some(structured_content),
_meta: None,
}
}
pub fn text_with_structured(
text: impl Into<String>,
structured_content: serde_json::Value,
) -> Self {
Self::structured(vec![Content::text(text)], structured_content)
}
pub fn validate_structured_content(
&self,
output_schema: &serde_json::Value,
) -> crate::Result<()> {
use crate::validation::Validator;
if let Some(structured_content) = &self.structured_content {
Validator::validate_structured_content(structured_content, output_schema)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Resource {
pub uri: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
pub description: Option<String>,
pub mime_type: Option<String>,
pub annotations: Option<Annotations>,
#[serde(skip_serializing_if = "Option::is_none")]
pub icons: Option<Vec<Icon>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub raw: Option<RawResource>,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
pub _meta: Option<ResourceMeta>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResourceMeta {
#[serde(rename = "ui", skip_serializing_if = "Option::is_none")]
pub ui: Option<UiResourceMeta>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UiResourceMeta {
#[serde(skip_serializing_if = "Option::is_none")]
pub csp: Option<CspConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<String>,
#[serde(rename = "prefersBorder", skip_serializing_if = "Option::is_none")]
pub prefers_border: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CspConfig {
#[serde(rename = "connectDomains", skip_serializing_if = "Option::is_none")]
pub connect_domains: Option<Vec<String>>,
#[serde(rename = "resourceDomains", skip_serializing_if = "Option::is_none")]
pub resource_domains: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Annotations {
pub audience: Option<Vec<String>>,
pub priority: Option<f32>,
}
impl Resource {
pub fn ui_resource(
uri: impl Into<String>,
name: impl Into<String>,
description: impl Into<String>,
) -> Self {
Self {
uri: uri.into(),
name: name.into(),
title: None,
description: Some(description.into()),
mime_type: Some(mime_types::HTML_MCP.to_string()),
annotations: None,
icons: None,
raw: None,
_meta: None,
}
}
pub fn ui_resource_with_csp(
uri: impl Into<String>,
name: impl Into<String>,
description: impl Into<String>,
csp: CspConfig,
) -> Self {
Self {
uri: uri.into(),
name: name.into(),
title: None,
description: Some(description.into()),
mime_type: Some(mime_types::HTML_MCP.to_string()),
annotations: None,
icons: None,
raw: None,
_meta: Some(ResourceMeta {
ui: Some(UiResourceMeta {
csp: Some(csp),
domain: None,
prefers_border: None,
}),
}),
}
}
pub fn is_ui_resource(&self) -> bool {
self.uri.starts_with(uri_schemes::UI)
}
pub fn uri_scheme(&self) -> Option<&str> {
self.uri.split_once("://").map(|(scheme, _)| scheme)
}
}
impl ResourceContents {
pub fn html_ui(uri: impl Into<String>, html: impl Into<String>) -> Self {
Self {
uri: uri.into(),
mime_type: Some(mime_types::HTML_MCP.to_string()),
text: Some(html.into()),
blob: None,
_meta: None,
}
}
pub fn json(uri: impl Into<String>, json: impl Into<String>) -> Self {
Self {
uri: uri.into(),
mime_type: Some(mime_types::JSON.to_string()),
text: Some(json.into()),
blob: None,
_meta: None,
}
}
pub fn text(uri: impl Into<String>, text: impl Into<String>) -> Self {
Self {
uri: uri.into(),
mime_type: Some(mime_types::TEXT.to_string()),
text: Some(text.into()),
blob: None,
_meta: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListResourcesResult {
pub resources: Vec<Resource>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReadResourceRequestParam {
pub uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceContents {
pub uri: String,
#[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub blob: Option<String>,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
pub _meta: Option<Meta>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReadResourceResult {
pub contents: Vec<ResourceContents>,
}
pub type EmbeddedResourceContents = ResourceContents;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RawResource {
pub uri: String,
pub data: Vec<u8>,
pub mime_type: Option<String>,
pub name: Option<String>,
pub description: Option<String>,
pub size: Option<usize>,
}
impl PromptMessage {
pub fn new_text(role: PromptMessageRole, text: impl Into<String>) -> Self {
Self {
role,
content: PromptMessageContent::Text { text: text.into() },
}
}
pub fn new_image(
role: PromptMessageRole,
data: impl Into<String>,
mime_type: impl Into<String>,
) -> Self {
Self {
role,
content: PromptMessageContent::Image {
data: data.into(),
mime_type: mime_type.into(),
},
}
}
pub fn new_resource(
role: PromptMessageRole,
uri: impl Into<String>,
mime_type: Option<String>,
text: Option<String>,
) -> Self {
Self {
role,
content: PromptMessageContent::Resource {
resource: EmbeddedResourceContents {
uri: uri.into(),
mime_type,
text,
blob: None,
_meta: None,
},
},
}
}
}
impl CompleteResult {
pub fn simple(value: impl Into<String>) -> Self {
Self {
completion: CompletionValues {
values: vec![value.into()],
total: None,
has_more: Some(false),
},
}
}
pub fn with_values(values: Vec<String>) -> Self {
let total = values.len() as u64;
Self {
completion: CompletionValues {
values,
total: Some(total),
has_more: Some(false),
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Prompt {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arguments: Option<Vec<PromptArgument>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub icons: Option<Vec<Icon>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PromptArgument {
pub name: String,
pub description: Option<String>,
pub required: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListPromptsResult {
pub prompts: Vec<Prompt>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetPromptRequestParam {
pub name: String,
pub arguments: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PromptMessageRole {
User,
Assistant,
System,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum PromptMessageContent {
#[serde(rename = "text")]
Text { text: String },
#[serde(rename = "image")]
Image {
data: String,
#[serde(rename = "mimeType")]
mime_type: String,
},
#[serde(rename = "resource")]
Resource {
resource: EmbeddedResourceContents,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PromptMessage {
pub role: PromptMessageRole,
pub content: PromptMessageContent,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetPromptResult {
pub description: Option<String>,
pub messages: Vec<PromptMessage>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitializeRequestParam {
#[serde(rename = "protocolVersion")]
pub protocol_version: String,
pub capabilities: serde_json::Value,
#[serde(rename = "clientInfo")]
pub client_info: Implementation,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitializeResult {
#[serde(rename = "protocolVersion")]
pub protocol_version: String,
pub capabilities: ServerCapabilities,
#[serde(rename = "serverInfo")]
pub server_info: Implementation,
#[serde(skip_serializing_if = "Option::is_none")]
pub instructions: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionContext {
pub argument_names: Vec<String>,
pub values: HashMap<String, serde_json::Value>,
}
impl CompletionContext {
pub fn new(argument_names: Vec<String>, values: HashMap<String, serde_json::Value>) -> Self {
Self {
argument_names,
values,
}
}
pub fn argument_names_iter(&self) -> impl Iterator<Item = &String> {
self.argument_names.iter()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum CompletionRef {
#[serde(rename = "ref/prompt")]
Prompt { name: String },
#[serde(rename = "ref/resource")]
Resource { uri: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompletionArgument {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompleteRequestParam {
#[serde(rename = "ref")]
pub ref_: CompletionRef,
pub argument: CompletionArgument,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<CompletionContext>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompletionValues {
pub values: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total: Option<u64>,
#[serde(rename = "hasMore", skip_serializing_if = "Option::is_none")]
pub has_more: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompleteResult {
pub completion: CompletionValues,
}
#[deprecated(note = "Use CompletionValues instead")]
pub type CompletionInfo = CompletionValues;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SetLevelRequestParam {
pub level: LogLevel,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceTemplate {
#[serde(rename = "uriTemplate")]
pub uri_template: String,
pub name: String,
pub description: Option<String>,
#[serde(rename = "mimeType")]
pub mime_type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListResourceTemplatesResult {
#[serde(rename = "resourceTemplates")]
pub resource_templates: Vec<ResourceTemplate>,
#[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubscribeRequestParam {
pub uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnsubscribeRequestParam {
pub uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceUpdatedNotification {
pub uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ElicitationCompleteNotification {
#[serde(rename = "elicitationId")]
pub elicitation_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UrlElicitationRequiredData {
pub elicitations: Vec<UrlElicitationInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UrlElicitationInfo {
pub mode: ElicitationMode,
#[serde(rename = "elicitationId")]
pub elicitation_id: String,
pub url: String,
pub message: String,
}
impl UrlElicitationInfo {
pub fn new(
elicitation_id: impl Into<String>,
url: impl Into<String>,
message: impl Into<String>,
) -> Self {
Self {
mode: ElicitationMode::Url,
elicitation_id: elicitation_id.into(),
url: url.into(),
message: message.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ElicitationMode {
Form,
Url,
}
impl Default for ElicitationMode {
fn default() -> Self {
Self::Form
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ElicitationRequestParam {
#[serde(default, skip_serializing_if = "is_form_mode")]
pub mode: ElicitationMode,
#[serde(rename = "elicitationId", skip_serializing_if = "Option::is_none")]
pub elicitation_id: Option<String>,
pub message: String,
#[serde(rename = "requestedSchema", skip_serializing_if = "Option::is_none")]
pub requested_schema: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}
fn is_form_mode(mode: &ElicitationMode) -> bool {
*mode == ElicitationMode::Form
}
impl ElicitationRequestParam {
pub fn form(message: impl Into<String>, schema: serde_json::Value) -> Self {
Self {
mode: ElicitationMode::Form,
elicitation_id: None,
message: message.into(),
requested_schema: Some(schema),
url: None,
}
}
pub fn url(
elicitation_id: impl Into<String>,
url: impl Into<String>,
message: impl Into<String>,
) -> Self {
Self {
mode: ElicitationMode::Url,
elicitation_id: Some(elicitation_id.into()),
message: message.into(),
requested_schema: None,
url: Some(url.into()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ElicitationAction {
Accept,
Decline,
Cancel,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ElicitationResponse {
pub action: ElicitationAction,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ElicitationResult {
pub response: ElicitationResponse,
}
impl ElicitationResult {
pub fn accept(data: serde_json::Value) -> Self {
Self {
response: ElicitationResponse {
action: ElicitationAction::Accept,
data: Some(data),
},
}
}
pub fn decline() -> Self {
Self {
response: ElicitationResponse {
action: ElicitationAction::Decline,
data: None,
},
}
}
pub fn cancel() -> Self {
Self {
response: ElicitationResponse {
action: ElicitationAction::Cancel,
data: None,
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum SamplingRole {
User,
Assistant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SamplingMessage {
pub role: SamplingRole,
pub content: SamplingContent,
}
impl SamplingMessage {
pub fn user_text(text: impl Into<String>) -> Self {
Self {
role: SamplingRole::User,
content: SamplingContent::Text { text: text.into() },
}
}
pub fn assistant_text(text: impl Into<String>) -> Self {
Self {
role: SamplingRole::Assistant,
content: SamplingContent::Text { text: text.into() },
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum SamplingContent {
#[serde(rename = "text")]
Text { text: String },
#[serde(rename = "image")]
Image { data: String, mime_type: String },
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ModelPreferences {
#[serde(skip_serializing_if = "Option::is_none")]
pub hints: Option<Vec<ModelHint>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cost_priority: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub speed_priority: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub intelligence_priority: Option<f32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelHint {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ContextInclusion {
AllServers,
ThisServer,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolChoice {
pub mode: ToolChoiceMode,
}
impl ToolChoice {
pub fn auto() -> Self {
Self {
mode: ToolChoiceMode::Auto,
}
}
pub fn required() -> Self {
Self {
mode: ToolChoiceMode::Required,
}
}
pub fn none() -> Self {
Self {
mode: ToolChoiceMode::None,
}
}
}
impl Default for ToolChoice {
fn default() -> Self {
Self::auto()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ToolChoiceMode {
Auto,
Required,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateMessageRequestParam {
pub max_tokens: u32,
pub messages: Vec<SamplingMessage>,
#[serde(skip_serializing_if = "Option::is_none")]
pub system_prompt: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_sequences: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model_preferences: Option<ModelPreferences>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_context: Option<ContextInclusion>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<Tool>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<ToolChoice>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Value>,
}
impl CreateMessageRequestParam {
pub fn simple(max_tokens: u32, user_message: impl Into<String>) -> Self {
Self {
max_tokens,
messages: vec![SamplingMessage::user_text(user_message)],
system_prompt: None,
temperature: None,
stop_sequences: None,
model_preferences: None,
include_context: None,
tools: None,
tool_choice: None,
metadata: None,
}
}
pub fn with_tools(max_tokens: u32, messages: Vec<SamplingMessage>, tools: Vec<Tool>) -> Self {
Self {
max_tokens,
messages,
system_prompt: None,
temperature: None,
stop_sequences: None,
model_preferences: None,
include_context: None,
tools: Some(tools),
tool_choice: Some(ToolChoice::auto()),
metadata: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateMessageResult {
pub model: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_reason: Option<String>,
pub message: SamplingMessage,
}
impl CreateMessageResult {
pub fn is_tool_use(&self) -> bool {
self.stop_reason.as_deref() == Some("tool_use")
}
pub fn is_end_turn(&self) -> bool {
self.stop_reason.as_deref() == Some("end_turn")
}
pub fn is_max_tokens(&self) -> bool {
self.stop_reason.as_deref() == Some("max_tokens")
}
}
pub mod stop_reasons {
pub const END_TURN: &str = "end_turn";
pub const STOP_SEQUENCE: &str = "stop_sequence";
pub const MAX_TOKENS: &str = "max_tokens";
pub const TOOL_USE: &str = "tool_use";
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum TaskStatus {
Working,
InputRequired,
Completed,
Failed,
Cancelled,
}
impl std::fmt::Display for TaskStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
TaskStatus::Working => "working",
TaskStatus::InputRequired => "input-required",
TaskStatus::Completed => "completed",
TaskStatus::Failed => "failed",
TaskStatus::Cancelled => "cancelled",
};
write!(f, "{s}")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Task {
pub task_id: String,
pub status: TaskStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub status_message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_updated_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ttl: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub poll_interval: Option<u64>,
}
impl Task {
pub fn new(task_id: impl Into<String>) -> Self {
Self {
task_id: task_id.into(),
status: TaskStatus::Working,
status_message: None,
created_at: None,
last_updated_at: None,
ttl: None,
poll_interval: None,
}
}
pub fn with_timestamps(task_id: impl Into<String>, created_at: impl Into<String>) -> Self {
let ts = created_at.into();
Self {
task_id: task_id.into(),
status: TaskStatus::Working,
status_message: None,
created_at: Some(ts.clone()),
last_updated_at: Some(ts),
ttl: None,
poll_interval: None,
}
}
pub fn is_terminal(&self) -> bool {
matches!(
self.status,
TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
)
}
pub fn is_running(&self) -> bool {
matches!(self.status, TaskStatus::Working | TaskStatus::InputRequired)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TasksCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub cancel: Option<TaskCancelCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub list: Option<TaskListCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub requests: Option<TaskRequestsCapability>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TaskCancelCapability {}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TaskListCapability {}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TaskRequestsCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub sampling: Option<TaskSamplingCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub elicitation: Option<TaskElicitationCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<TaskToolsCapability>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TaskSamplingCapability {
#[serde(rename = "createMessage", skip_serializing_if = "Option::is_none")]
pub create_message: Option<TaskMethodCapability>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TaskElicitationCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub create: Option<TaskMethodCapability>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TaskToolsCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub call: Option<TaskMethodCapability>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TaskMethodCapability {}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct TaskMetadata {
#[serde(skip_serializing_if = "Option::is_none")]
pub ttl: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub poll_interval: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateTaskResult {
pub task: Task,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetTaskRequestParam {
pub task_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetTaskResult {
pub task: Task,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ListTasksRequestParam {
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListTasksResult {
pub tasks: Vec<Task>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetTaskResultRequestParam {
pub task_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CancelTaskRequestParam {
pub task_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CancelTaskResult {
pub task: Task,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TaskStatusNotification {
pub task_id: String,
pub status: TaskStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub status_message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_updated_at: Option<String>,
}
impl TaskStatusNotification {
pub fn new(task_id: impl Into<String>, status: TaskStatus) -> Self {
Self {
task_id: task_id.into(),
status,
status_message: None,
last_updated_at: None,
}
}
pub fn with_message(
task_id: impl Into<String>,
status: TaskStatus,
message: impl Into<String>,
) -> Self {
Self {
task_id: task_id.into(),
status,
status_message: Some(message.into()),
last_updated_at: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ToolExecution {
#[serde(skip_serializing_if = "Option::is_none")]
pub task_support: Option<TaskSupport>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum TaskSupport {
Forbidden,
Optional,
Required,
}
impl Default for TaskSupport {
fn default() -> Self {
Self::Optional
}
}