1use collections::HashMap;
2use serde::de::DeserializeOwned;
3use serde::{Deserialize, Serialize};
4use url::Url;
5
6pub const LATEST_PROTOCOL_VERSION: &str = "2024-11-05";
7
8pub mod request {
9 use super::*;
10
11 macro_rules! request {
12 ($method:expr, $name:ident, $params:ty, $response:ty) => {
13 pub struct $name;
14
15 impl Request for $name {
16 type Params = $params;
17 type Response = $response;
18 const METHOD: &'static str = $method;
19 }
20 };
21 }
22
23 request!(
24 "initialize",
25 Initialize,
26 InitializeParams,
27 InitializeResponse
28 );
29 request!("tools/call", CallTool, CallToolParams, CallToolResponse);
30 request!(
31 "resources/unsubscribe",
32 ResourcesUnsubscribe,
33 ResourcesUnsubscribeParams,
34 ()
35 );
36 request!(
37 "resources/subscribe",
38 ResourcesSubscribe,
39 ResourcesSubscribeParams,
40 ()
41 );
42 request!(
43 "resources/read",
44 ResourcesRead,
45 ResourcesReadParams,
46 ResourcesReadResponse
47 );
48 request!("resources/list", ResourcesList, (), ResourcesListResponse);
49 request!(
50 "logging/setLevel",
51 LoggingSetLevel,
52 LoggingSetLevelParams,
53 ()
54 );
55 request!(
56 "prompts/get",
57 PromptsGet,
58 PromptsGetParams,
59 PromptsGetResponse
60 );
61 request!("prompts/list", PromptsList, (), PromptsListResponse);
62 request!(
63 "completion/complete",
64 CompletionComplete,
65 CompletionCompleteParams,
66 CompletionCompleteResponse
67 );
68 request!("ping", Ping, (), ());
69 request!("tools/list", ListTools, (), ListToolsResponse);
70 request!(
71 "resources/templates/list",
72 ListResourceTemplates,
73 (),
74 ListResourceTemplatesResponse
75 );
76 request!("roots/list", ListRoots, (), ListRootsResponse);
77}
78
79pub trait Request {
80 type Params: DeserializeOwned + Serialize + Send + Sync + 'static;
81 type Response: DeserializeOwned + Serialize + Send + Sync + 'static;
82 const METHOD: &'static str;
83}
84
85#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
86#[serde(transparent)]
87pub struct ProtocolVersion(pub String);
88
89#[derive(Debug, Serialize, Deserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct InitializeParams {
92 pub protocol_version: ProtocolVersion,
93 pub capabilities: ClientCapabilities,
94 pub client_info: Implementation,
95 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
96 pub meta: Option<HashMap<String, serde_json::Value>>,
97}
98
99#[derive(Debug, Serialize, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub struct CallToolParams {
102 pub name: String,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub arguments: Option<HashMap<String, serde_json::Value>>,
105 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
106 pub meta: Option<HashMap<String, serde_json::Value>>,
107}
108
109#[derive(Debug, Serialize, Deserialize)]
110#[serde(rename_all = "camelCase")]
111pub struct ResourcesUnsubscribeParams {
112 pub uri: Url,
113 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
114 pub meta: Option<HashMap<String, serde_json::Value>>,
115}
116
117#[derive(Debug, Serialize, Deserialize)]
118#[serde(rename_all = "camelCase")]
119pub struct ResourcesSubscribeParams {
120 pub uri: Url,
121 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
122 pub meta: Option<HashMap<String, serde_json::Value>>,
123}
124
125#[derive(Debug, Serialize, Deserialize)]
126#[serde(rename_all = "camelCase")]
127pub struct ResourcesReadParams {
128 pub uri: Url,
129 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
130 pub meta: Option<HashMap<String, serde_json::Value>>,
131}
132
133#[derive(Debug, Serialize, Deserialize)]
134#[serde(rename_all = "camelCase")]
135pub struct LoggingSetLevelParams {
136 pub level: LoggingLevel,
137 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
138 pub meta: Option<HashMap<String, serde_json::Value>>,
139}
140
141#[derive(Debug, Serialize, Deserialize)]
142#[serde(rename_all = "camelCase")]
143pub struct PromptsGetParams {
144 pub name: String,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub arguments: Option<HashMap<String, String>>,
147 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
148 pub meta: Option<HashMap<String, serde_json::Value>>,
149}
150
151#[derive(Debug, Serialize, Deserialize)]
152#[serde(rename_all = "camelCase")]
153pub struct CompletionCompleteParams {
154 #[serde(rename = "ref")]
155 pub reference: CompletionReference,
156 pub argument: CompletionArgument,
157 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
158 pub meta: Option<HashMap<String, serde_json::Value>>,
159}
160
161#[derive(Debug, Serialize, Deserialize)]
162#[serde(untagged)]
163pub enum CompletionReference {
164 Prompt(PromptReference),
165 Resource(ResourceReference),
166}
167
168#[derive(Debug, Serialize, Deserialize)]
169#[serde(rename_all = "camelCase")]
170pub struct PromptReference {
171 #[serde(rename = "type")]
172 pub ty: PromptReferenceType,
173 pub name: String,
174}
175
176#[derive(Debug, Serialize, Deserialize)]
177#[serde(rename_all = "camelCase")]
178pub struct ResourceReference {
179 #[serde(rename = "type")]
180 pub ty: PromptReferenceType,
181 pub uri: Url,
182}
183
184#[derive(Debug, Serialize, Deserialize)]
185#[serde(rename_all = "snake_case")]
186pub enum PromptReferenceType {
187 #[serde(rename = "ref/prompt")]
188 Prompt,
189 #[serde(rename = "ref/resource")]
190 Resource,
191}
192
193#[derive(Debug, Serialize, Deserialize)]
194#[serde(rename_all = "camelCase")]
195pub struct CompletionArgument {
196 pub name: String,
197 pub value: String,
198}
199
200#[derive(Debug, Serialize, Deserialize)]
201#[serde(rename_all = "camelCase")]
202pub struct InitializeResponse {
203 pub protocol_version: ProtocolVersion,
204 pub capabilities: ServerCapabilities,
205 pub server_info: Implementation,
206 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
207 pub meta: Option<HashMap<String, serde_json::Value>>,
208}
209
210#[derive(Debug, Serialize, Deserialize)]
211#[serde(rename_all = "camelCase")]
212pub struct ResourcesReadResponse {
213 pub contents: Vec<ResourceContentsType>,
214 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
215 pub meta: Option<HashMap<String, serde_json::Value>>,
216}
217
218#[derive(Debug, Serialize, Deserialize)]
219#[serde(untagged)]
220pub enum ResourceContentsType {
221 Text(TextResourceContents),
222 Blob(BlobResourceContents),
223}
224
225#[derive(Debug, Serialize, Deserialize)]
226#[serde(rename_all = "camelCase")]
227pub struct ResourcesListResponse {
228 pub resources: Vec<Resource>,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub next_cursor: Option<String>,
231 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
232 pub meta: Option<HashMap<String, serde_json::Value>>,
233}
234
235#[derive(Debug, Serialize, Deserialize)]
236#[serde(rename_all = "camelCase")]
237pub struct SamplingMessage {
238 pub role: Role,
239 pub content: MessageContent,
240}
241
242#[derive(Debug, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct CreateMessageRequest {
245 pub messages: Vec<SamplingMessage>,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub model_preferences: Option<ModelPreferences>,
248 #[serde(skip_serializing_if = "Option::is_none")]
249 pub system_prompt: Option<String>,
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub include_context: Option<String>,
252 #[serde(skip_serializing_if = "Option::is_none")]
253 pub temperature: Option<f64>,
254 pub max_tokens: u32,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub stop_sequences: Option<Vec<String>>,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub metadata: Option<serde_json::Value>,
259}
260
261#[derive(Debug, Deserialize)]
262#[serde(rename_all = "camelCase")]
263pub struct CreateMessageResult {
264 pub role: Role,
265 pub content: MessageContent,
266 pub model: String,
267 #[serde(skip_serializing_if = "Option::is_none")]
268 pub stop_reason: Option<String>,
269}
270
271#[derive(Debug, Serialize, Deserialize)]
272#[serde(rename_all = "camelCase")]
273pub struct PromptMessage {
274 pub role: Role,
275 pub content: MessageContent,
276}
277
278#[derive(Debug, Serialize, Deserialize)]
279#[serde(rename_all = "lowercase")]
280pub enum Role {
281 User,
282 Assistant,
283}
284
285#[derive(Debug, Serialize, Deserialize)]
286#[serde(tag = "type")]
287pub enum MessageContent {
288 #[serde(rename = "text")]
289 Text {
290 text: String,
291 #[serde(skip_serializing_if = "Option::is_none")]
292 annotations: Option<MessageAnnotations>,
293 },
294 #[serde(rename = "image")]
295 Image {
296 data: String,
297 mime_type: String,
298 #[serde(skip_serializing_if = "Option::is_none")]
299 annotations: Option<MessageAnnotations>,
300 },
301 #[serde(rename = "resource")]
302 Resource {
303 resource: ResourceContents,
304 #[serde(skip_serializing_if = "Option::is_none")]
305 annotations: Option<MessageAnnotations>,
306 },
307}
308
309#[derive(Debug, Serialize, Deserialize)]
310#[serde(rename_all = "camelCase")]
311pub struct MessageAnnotations {
312 #[serde(skip_serializing_if = "Option::is_none")]
313 pub audience: Option<Vec<Role>>,
314 #[serde(skip_serializing_if = "Option::is_none")]
315 pub priority: Option<f64>,
316}
317
318#[derive(Debug, Serialize, Deserialize)]
319#[serde(rename_all = "camelCase")]
320pub struct PromptsGetResponse {
321 #[serde(skip_serializing_if = "Option::is_none")]
322 pub description: Option<String>,
323 pub messages: Vec<PromptMessage>,
324 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
325 pub meta: Option<HashMap<String, serde_json::Value>>,
326}
327
328#[derive(Debug, Serialize, Deserialize)]
329#[serde(rename_all = "camelCase")]
330pub struct PromptsListResponse {
331 pub prompts: Vec<Prompt>,
332 #[serde(skip_serializing_if = "Option::is_none")]
333 pub next_cursor: Option<String>,
334 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
335 pub meta: Option<HashMap<String, serde_json::Value>>,
336}
337
338#[derive(Debug, Serialize, Deserialize)]
339#[serde(rename_all = "camelCase")]
340pub struct CompletionCompleteResponse {
341 pub completion: CompletionResult,
342 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
343 pub meta: Option<HashMap<String, serde_json::Value>>,
344}
345
346#[derive(Debug, Serialize, Deserialize)]
347#[serde(rename_all = "camelCase")]
348pub struct CompletionResult {
349 pub values: Vec<String>,
350 #[serde(skip_serializing_if = "Option::is_none")]
351 pub total: Option<u32>,
352 #[serde(skip_serializing_if = "Option::is_none")]
353 pub has_more: Option<bool>,
354 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
355 pub meta: Option<HashMap<String, serde_json::Value>>,
356}
357
358#[derive(Debug, Serialize, Deserialize)]
359#[serde(rename_all = "camelCase")]
360pub struct Prompt {
361 pub name: String,
362 #[serde(skip_serializing_if = "Option::is_none")]
363 pub description: Option<String>,
364 #[serde(skip_serializing_if = "Option::is_none")]
365 pub arguments: Option<Vec<PromptArgument>>,
366}
367
368#[derive(Debug, Serialize, Deserialize)]
369#[serde(rename_all = "camelCase")]
370pub struct PromptArgument {
371 pub name: String,
372 #[serde(skip_serializing_if = "Option::is_none")]
373 pub description: Option<String>,
374 #[serde(skip_serializing_if = "Option::is_none")]
375 pub required: Option<bool>,
376}
377
378#[derive(Debug, Serialize, Deserialize)]
379#[serde(rename_all = "camelCase")]
380pub struct ClientCapabilities {
381 #[serde(skip_serializing_if = "Option::is_none")]
382 pub experimental: Option<HashMap<String, serde_json::Value>>,
383 #[serde(skip_serializing_if = "Option::is_none")]
384 pub sampling: Option<serde_json::Value>,
385 #[serde(skip_serializing_if = "Option::is_none")]
386 pub roots: Option<RootsCapabilities>,
387}
388
389#[derive(Default, Debug, Serialize, Deserialize)]
390#[serde(rename_all = "camelCase")]
391pub struct ServerCapabilities {
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub experimental: Option<HashMap<String, serde_json::Value>>,
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub logging: Option<serde_json::Value>,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub prompts: Option<PromptsCapabilities>,
398 #[serde(skip_serializing_if = "Option::is_none")]
399 pub resources: Option<ResourcesCapabilities>,
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub tools: Option<ToolsCapabilities>,
402}
403
404#[derive(Debug, Serialize, Deserialize)]
405#[serde(rename_all = "camelCase")]
406pub struct PromptsCapabilities {
407 #[serde(skip_serializing_if = "Option::is_none")]
408 pub list_changed: Option<bool>,
409}
410
411#[derive(Debug, Serialize, Deserialize)]
412#[serde(rename_all = "camelCase")]
413pub struct ResourcesCapabilities {
414 #[serde(skip_serializing_if = "Option::is_none")]
415 pub subscribe: Option<bool>,
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub list_changed: Option<bool>,
418}
419
420#[derive(Debug, Serialize, Deserialize)]
421#[serde(rename_all = "camelCase")]
422pub struct ToolsCapabilities {
423 #[serde(skip_serializing_if = "Option::is_none")]
424 pub list_changed: Option<bool>,
425}
426
427#[derive(Debug, Serialize, Deserialize)]
428#[serde(rename_all = "camelCase")]
429pub struct RootsCapabilities {
430 #[serde(skip_serializing_if = "Option::is_none")]
431 pub list_changed: Option<bool>,
432}
433
434#[derive(Debug, Serialize, Deserialize)]
435#[serde(rename_all = "camelCase")]
436pub struct Tool {
437 pub name: String,
438 #[serde(skip_serializing_if = "Option::is_none")]
439 pub description: Option<String>,
440 pub input_schema: serde_json::Value,
441}
442
443#[derive(Debug, Serialize, Deserialize)]
444#[serde(rename_all = "camelCase")]
445pub struct Implementation {
446 pub name: String,
447 pub version: String,
448}
449
450#[derive(Debug, Serialize, Deserialize)]
451#[serde(rename_all = "camelCase")]
452pub struct Resource {
453 pub uri: Url,
454 pub name: String,
455 #[serde(skip_serializing_if = "Option::is_none")]
456 pub description: Option<String>,
457 #[serde(skip_serializing_if = "Option::is_none")]
458 pub mime_type: Option<String>,
459}
460
461#[derive(Debug, Serialize, Deserialize)]
462#[serde(rename_all = "camelCase")]
463pub struct ResourceContents {
464 pub uri: Url,
465 #[serde(skip_serializing_if = "Option::is_none")]
466 pub mime_type: Option<String>,
467}
468
469#[derive(Debug, Serialize, Deserialize)]
470#[serde(rename_all = "camelCase")]
471pub struct TextResourceContents {
472 pub uri: Url,
473 #[serde(skip_serializing_if = "Option::is_none")]
474 pub mime_type: Option<String>,
475 pub text: String,
476}
477
478#[derive(Debug, Serialize, Deserialize)]
479#[serde(rename_all = "camelCase")]
480pub struct BlobResourceContents {
481 pub uri: Url,
482 #[serde(skip_serializing_if = "Option::is_none")]
483 pub mime_type: Option<String>,
484 pub blob: String,
485}
486
487#[derive(Debug, Serialize, Deserialize)]
488#[serde(rename_all = "camelCase")]
489pub struct ResourceTemplate {
490 pub uri_template: String,
491 pub name: String,
492 #[serde(skip_serializing_if = "Option::is_none")]
493 pub description: Option<String>,
494 #[serde(skip_serializing_if = "Option::is_none")]
495 pub mime_type: Option<String>,
496}
497
498#[derive(Debug, Serialize, Deserialize)]
499#[serde(rename_all = "lowercase")]
500pub enum LoggingLevel {
501 Debug,
502 Info,
503 Notice,
504 Warning,
505 Error,
506 Critical,
507 Alert,
508 Emergency,
509}
510
511#[derive(Debug, Serialize, Deserialize)]
512#[serde(rename_all = "camelCase")]
513pub struct ModelPreferences {
514 #[serde(skip_serializing_if = "Option::is_none")]
515 pub hints: Option<Vec<ModelHint>>,
516 #[serde(skip_serializing_if = "Option::is_none")]
517 pub cost_priority: Option<f64>,
518 #[serde(skip_serializing_if = "Option::is_none")]
519 pub speed_priority: Option<f64>,
520 #[serde(skip_serializing_if = "Option::is_none")]
521 pub intelligence_priority: Option<f64>,
522}
523
524#[derive(Debug, Serialize, Deserialize)]
525#[serde(rename_all = "camelCase")]
526pub struct ModelHint {
527 #[serde(skip_serializing_if = "Option::is_none")]
528 pub name: Option<String>,
529}
530
531#[derive(Debug, Serialize, Deserialize)]
532#[serde(rename_all = "camelCase")]
533pub enum NotificationType {
534 Initialized,
535 Progress,
536 Message,
537 ResourcesUpdated,
538 ResourcesListChanged,
539 ToolsListChanged,
540 PromptsListChanged,
541 RootsListChanged,
542}
543
544impl NotificationType {
545 pub fn as_str(&self) -> &'static str {
546 match self {
547 NotificationType::Initialized => "notifications/initialized",
548 NotificationType::Progress => "notifications/progress",
549 NotificationType::Message => "notifications/message",
550 NotificationType::ResourcesUpdated => "notifications/resources/updated",
551 NotificationType::ResourcesListChanged => "notifications/resources/list_changed",
552 NotificationType::ToolsListChanged => "notifications/tools/list_changed",
553 NotificationType::PromptsListChanged => "notifications/prompts/list_changed",
554 NotificationType::RootsListChanged => "notifications/roots/list_changed",
555 }
556 }
557}
558
559#[derive(Debug, Serialize)]
560#[serde(untagged)]
561pub enum ClientNotification {
562 Initialized,
563 Progress(ProgressParams),
564 RootsListChanged,
565 Cancelled {
566 request_id: String,
567 #[serde(skip_serializing_if = "Option::is_none")]
568 reason: Option<String>,
569 },
570}
571
572#[derive(Debug, Serialize, Deserialize)]
573#[serde(untagged)]
574pub enum ProgressToken {
575 String(String),
576 Number(f64),
577}
578
579#[derive(Debug, Serialize)]
580#[serde(rename_all = "camelCase")]
581pub struct ProgressParams {
582 pub progress_token: ProgressToken,
583 pub progress: f64,
584 #[serde(skip_serializing_if = "Option::is_none")]
585 pub total: Option<f64>,
586 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
587 pub meta: Option<HashMap<String, serde_json::Value>>,
588}
589
590pub enum CompletionTotal {
591 Exact(u32),
592 HasMore,
593 Unknown,
594}
595
596impl CompletionTotal {
597 pub fn from_options(has_more: Option<bool>, total: Option<u32>) -> Self {
598 match (has_more, total) {
599 (_, Some(count)) => CompletionTotal::Exact(count),
600 (Some(true), _) => CompletionTotal::HasMore,
601 _ => CompletionTotal::Unknown,
602 }
603 }
604}
605
606pub struct Completion {
607 pub values: Vec<String>,
608 pub total: CompletionTotal,
609}
610
611#[derive(Debug, Serialize, Deserialize)]
612#[serde(rename_all = "camelCase")]
613pub struct CallToolResponse {
614 pub content: Vec<ToolResponseContent>,
615 #[serde(skip_serializing_if = "Option::is_none")]
616 pub is_error: Option<bool>,
617 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
618 pub meta: Option<HashMap<String, serde_json::Value>>,
619}
620
621#[derive(Debug, Serialize, Deserialize)]
622#[serde(tag = "type")]
623pub enum ToolResponseContent {
624 #[serde(rename = "text")]
625 Text { text: String },
626 #[serde(rename = "image", rename_all = "camelCase")]
627 Image { data: String, mime_type: String },
628 #[serde(rename = "resource")]
629 Resource { resource: ResourceContents },
630}
631
632#[derive(Debug, Serialize, Deserialize)]
633#[serde(rename_all = "camelCase")]
634pub struct ListToolsResponse {
635 pub tools: Vec<Tool>,
636 #[serde(skip_serializing_if = "Option::is_none")]
637 pub next_cursor: Option<String>,
638 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
639 pub meta: Option<HashMap<String, serde_json::Value>>,
640}
641
642#[derive(Debug, Serialize, Deserialize)]
643#[serde(rename_all = "camelCase")]
644pub struct ListResourceTemplatesResponse {
645 pub resource_templates: Vec<ResourceTemplate>,
646 #[serde(skip_serializing_if = "Option::is_none")]
647 pub next_cursor: Option<String>,
648 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
649 pub meta: Option<HashMap<String, serde_json::Value>>,
650}
651
652#[derive(Debug, Serialize, Deserialize)]
653#[serde(rename_all = "camelCase")]
654pub struct ListRootsResponse {
655 pub roots: Vec<Root>,
656 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
657 pub meta: Option<HashMap<String, serde_json::Value>>,
658}
659
660#[derive(Debug, Serialize, Deserialize)]
661#[serde(rename_all = "camelCase")]
662pub struct Root {
663 pub uri: Url,
664 #[serde(skip_serializing_if = "Option::is_none")]
665 pub name: Option<String>,
666}