1use crate::protocol::discovery::*;
8use crate::protocol::types::*;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
18pub struct IntrospectionResult {
19 pub protocol: ProtocolInfo,
21
22 pub methods: MethodSchemas,
24
25 pub types: TypeDefinitions,
27
28 pub capabilities: CapabilitySchemas,
30
31 pub transports: Vec<TransportInfo>,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub extensions: Option<ExtensionInfo>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
41pub struct ProtocolInfo {
42 pub version: String,
44
45 pub min_version: String,
47
48 pub max_version: String,
50
51 pub supported_versions: Vec<String>,
53
54 pub version_features: HashMap<String, Vec<String>>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub struct MethodSchemas {
61 pub requests: Vec<MethodSchema>,
63
64 pub server_requests: Vec<MethodSchema>,
66
67 pub notifications: Vec<MethodSchema>,
69
70 pub subscriptions: Vec<MethodSchema>,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
76pub struct MethodSchema {
77 pub name: String,
79
80 pub title: String,
82
83 pub description: String,
85
86 pub category: String,
88
89 pub params: serde_json::Value,
91
92 pub result: serde_json::Value,
94
95 pub errors: Vec<ErrorSchema>,
97
98 pub examples: Vec<MethodExample>,
100
101 pub metadata: MethodMetadata,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
107pub struct ErrorSchema {
108 pub code: i32,
110
111 pub name: String,
113
114 pub description: String,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub data_schema: Option<serde_json::Value>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
124pub struct MethodExample {
125 pub title: String,
127
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub description: Option<String>,
131
132 pub request: serde_json::Value,
134
135 pub response: serde_json::Value,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
141pub struct MethodMetadata {
142 pub requires_auth: bool,
144
145 pub supports_progress: bool,
147
148 pub supports_cancellation: bool,
150
151 pub supports_batching: bool,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub rate_limit: Option<RateLimitInfo>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
160 pub deprecation: Option<DeprecationInfo>,
161
162 pub since_version: String,
164
165 pub required_capabilities: Vec<String>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
171pub struct DeprecationInfo {
172 pub deprecated: bool,
174
175 pub since: String,
177
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub removal_version: Option<String>,
181
182 #[serde(skip_serializing_if = "Option::is_none")]
184 pub replacement: Option<String>,
185
186 pub message: String,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
192pub struct TypeDefinitions {
193 pub core: HashMap<String, serde_json::Value>,
195
196 pub requests: HashMap<String, serde_json::Value>,
198
199 pub responses: HashMap<String, serde_json::Value>,
201
202 pub notifications: HashMap<String, serde_json::Value>,
204
205 pub capabilities: HashMap<String, serde_json::Value>,
207
208 pub custom: HashMap<String, serde_json::Value>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
214pub struct CapabilitySchemas {
215 pub server: serde_json::Value,
217
218 pub client: serde_json::Value,
220
221 pub capabilities: Vec<CapabilityDetail>,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
227pub struct CapabilityDetail {
228 pub name: String,
230
231 pub capability_type: String,
233
234 pub description: String,
236
237 pub schema: serde_json::Value,
239
240 pub enabled_methods: Vec<String>,
242
243 pub dependencies: Vec<String>,
245
246 pub since_version: String,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
252pub struct TransportInfo {
253 pub name: String,
255
256 pub transport_type: String,
258
259 pub description: String,
261
262 pub config_schema: serde_json::Value,
264
265 pub features: Vec<String>,
267
268 pub performance: TransportPerformance,
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
274pub struct TransportPerformance {
275 pub latency: String,
277
278 pub throughput: String,
280
281 pub streaming: bool,
283
284 pub multiplexing: bool,
286
287 pub compression: bool,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
293pub struct ExtensionInfo {
294 pub extensions: Vec<Extension>,
296
297 pub experimental: Vec<ExperimentalFeature>,
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
303pub struct Extension {
304 pub name: String,
306
307 pub version: String,
309
310 pub description: String,
312
313 pub methods: Vec<String>,
315
316 pub types: Vec<String>,
318
319 pub config_schema: serde_json::Value,
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
325pub struct ExperimentalFeature {
326 pub name: String,
328
329 pub description: String,
331
332 pub stability: String,
334
335 pub flag: String,
337
338 #[serde(skip_serializing_if = "Option::is_none")]
340 pub stable_version: Option<String>,
341}
342
343pub struct SchemaBuilder {
349 protocol: ProtocolInfo,
350 methods: MethodSchemas,
351 types: TypeDefinitions,
352 capabilities: CapabilitySchemas,
353 transports: Vec<TransportInfo>,
354 extensions: Option<ExtensionInfo>,
355}
356
357impl SchemaBuilder {
358 pub fn new() -> Self {
360 Self {
361 protocol: ProtocolInfo {
362 version: LATEST_PROTOCOL_VERSION.to_string(),
363 min_version: crate::protocol::LEGACY_PROTOCOL_VERSION.to_string(),
364 max_version: LATEST_PROTOCOL_VERSION.to_string(),
365 supported_versions: crate::protocol::SUPPORTED_PROTOCOL_VERSIONS
366 .iter()
367 .map(|version| (*version).to_string())
368 .collect(),
369 version_features: Self::build_version_features(),
370 },
371 methods: MethodSchemas {
372 requests: Vec::new(),
373 server_requests: Vec::new(),
374 notifications: Vec::new(),
375 subscriptions: Vec::new(),
376 },
377 types: TypeDefinitions {
378 core: HashMap::new(),
379 requests: HashMap::new(),
380 responses: HashMap::new(),
381 notifications: HashMap::new(),
382 capabilities: HashMap::new(),
383 custom: HashMap::new(),
384 },
385 capabilities: CapabilitySchemas {
386 server: serde_json::json!({}),
387 client: serde_json::json!({}),
388 capabilities: Vec::new(),
389 },
390 transports: Vec::new(),
391 extensions: None,
392 }
393 }
394
395 fn build_version_features() -> HashMap<String, Vec<String>> {
397 let mut features = HashMap::new();
398
399 features.insert(
400 "2024-11-05".to_string(),
401 vec![
402 "core-protocol".to_string(),
403 "tools".to_string(),
404 "resources".to_string(),
405 "prompts".to_string(),
406 "sampling".to_string(),
407 ],
408 );
409
410 features.insert(
411 "2025-03-26".to_string(),
412 vec![
413 "streamable-http".to_string(),
414 "json-rpc-batching".to_string(),
415 "improved-metadata".to_string(),
416 ],
417 );
418
419 features.insert(
420 "2025-11-25".to_string(),
421 vec![
422 "elicitation".to_string(),
423 "audio-content".to_string(),
424 "resource-links".to_string(),
425 "structured-tool-output".to_string(),
426 "oauth-2.1".to_string(),
427 "improved-annotations".to_string(),
428 ],
429 );
430
431 features.insert(
432 "2026-07-28".to_string(),
433 vec![
434 "stateless-core".to_string(),
435 "server-discover".to_string(),
436 "multi-round-trip-requests".to_string(),
437 "header-routing".to_string(),
438 "cacheable-results".to_string(),
439 "extensions".to_string(),
440 ],
441 );
442
443 features
444 }
445
446 pub fn add_method(mut self, schema: MethodSchema, category: &str) -> Self {
448 match category {
449 "request" => self.methods.requests.push(schema),
450 "server_request" => self.methods.server_requests.push(schema),
451 "notification" => self.methods.notifications.push(schema),
452 "subscription" => self.methods.subscriptions.push(schema),
453 _ => {}
454 }
455 self
456 }
457
458 pub fn add_type(mut self, category: &str, name: String, schema: serde_json::Value) -> Self {
460 match category {
461 "core" => {
462 self.types.core.insert(name, schema);
463 }
464 "request" => {
465 self.types.requests.insert(name, schema);
466 }
467 "response" => {
468 self.types.responses.insert(name, schema);
469 }
470 "notification" => {
471 self.types.notifications.insert(name, schema);
472 }
473 "capability" => {
474 self.types.capabilities.insert(name, schema);
475 }
476 "custom" => {
477 self.types.custom.insert(name, schema);
478 }
479 _ => {}
480 }
481 self
482 }
483
484 pub fn add_transport(mut self, transport: TransportInfo) -> Self {
486 self.transports.push(transport);
487 self
488 }
489
490 pub fn add_capability(mut self, capability: CapabilityDetail) -> Self {
492 self.capabilities.capabilities.push(capability);
493 self
494 }
495
496 pub fn build(self) -> IntrospectionResult {
498 IntrospectionResult {
499 protocol: self.protocol,
500 methods: self.methods,
501 types: self.types,
502 capabilities: self.capabilities,
503 transports: self.transports,
504 extensions: self.extensions,
505 }
506 }
507}
508
509impl Default for SchemaBuilder {
510 fn default() -> Self {
511 Self::new()
512 }
513}
514
515pub struct IntrospectionProvider {
521 #[allow(dead_code)]
522 builder: SchemaBuilder,
523}
524
525impl IntrospectionProvider {
526 pub fn new() -> Self {
528 Self {
529 builder: SchemaBuilder::new(),
530 }
531 }
532
533 pub fn build_complete_introspection(&self) -> IntrospectionResult {
535 let mut builder = SchemaBuilder::new();
536
537 builder = builder
539 .add_transport(TransportInfo {
540 name: "stdio".to_string(),
541 transport_type: "stdio".to_string(),
542 description: "Standard input/output transport for local processes".to_string(),
543 config_schema: serde_json::json!({}),
544 features: vec!["bidirectional".to_string(), "low-latency".to_string()],
545 performance: TransportPerformance {
546 latency: "microseconds".to_string(),
547 throughput: "high".to_string(),
548 streaming: true,
549 multiplexing: false,
550 compression: false,
551 },
552 })
553 .add_transport(TransportInfo {
554 name: "http-sse".to_string(),
555 transport_type: "http".to_string(),
556 description: "HTTP with Server-Sent Events for web-based communication".to_string(),
557 config_schema: serde_json::json!({
558 "type": "object",
559 "properties": {
560 "url": {"type": "string"},
561 "headers": {"type": "object"}
562 }
563 }),
564 features: vec![
565 "web-compatible".to_string(),
566 "firewall-friendly".to_string(),
567 ],
568 performance: TransportPerformance {
569 latency: "milliseconds".to_string(),
570 throughput: "medium".to_string(),
571 streaming: true,
572 multiplexing: false,
573 compression: true,
574 },
575 })
576 .add_transport(TransportInfo {
577 name: "websocket".to_string(),
578 transport_type: "websocket".to_string(),
579 description: "WebSocket transport for real-time bidirectional communication"
580 .to_string(),
581 config_schema: serde_json::json!({
582 "type": "object",
583 "properties": {
584 "url": {"type": "string"},
585 "protocols": {"type": "array"}
586 }
587 }),
588 features: vec!["real-time".to_string(), "bidirectional".to_string()],
589 performance: TransportPerformance {
590 latency: "low-milliseconds".to_string(),
591 throughput: "high".to_string(),
592 streaming: true,
593 multiplexing: true,
594 compression: true,
595 },
596 });
597
598 builder = builder
600 .add_capability(CapabilityDetail {
601 name: "tools".to_string(),
602 capability_type: "server".to_string(),
603 description: "Ability to expose and execute tools".to_string(),
604 schema: serde_json::json!({
605 "type": "object",
606 "properties": {
607 "listChanged": {"type": "boolean"}
608 }
609 }),
610 enabled_methods: vec!["tools/list".to_string(), "tools/call".to_string()],
611 dependencies: vec![],
612 since_version: "2024-11-05".to_string(),
613 })
614 .add_capability(CapabilityDetail {
615 name: "elicitation".to_string(),
616 capability_type: "client".to_string(),
617 description: "Ability to collect user input through forms".to_string(),
618 schema: serde_json::json!({}),
619 enabled_methods: vec!["elicitation/create".to_string()],
620 dependencies: vec![],
621 since_version: "2025-11-25".to_string(),
622 });
623
624 builder.build()
625 }
626}
627
628impl Default for IntrospectionProvider {
629 fn default() -> Self {
630 Self::new()
631 }
632}
633
634#[cfg(test)]
639mod tests {
640 use super::*;
641
642 #[test]
643 fn test_schema_builder() {
644 let builder = SchemaBuilder::new();
645 let result = builder.build();
646
647 assert_eq!(result.protocol.version, "2026-07-28");
648 assert!(result
649 .protocol
650 .supported_versions
651 .contains(&"2025-11-25".to_string()));
652 }
653
654 #[test]
655 fn test_introspection_provider() {
656 let provider = IntrospectionProvider::new();
657 let introspection = provider.build_complete_introspection();
658
659 assert!(!introspection.transports.is_empty());
660 assert!(!introspection.capabilities.capabilities.is_empty());
661
662 assert!(introspection.transports.iter().any(|t| t.name == "stdio"));
664 assert!(introspection
665 .transports
666 .iter()
667 .any(|t| t.name == "websocket"));
668
669 assert!(introspection
671 .capabilities
672 .capabilities
673 .iter()
674 .any(|c| c.name == "tools"));
675 assert!(introspection
676 .capabilities
677 .capabilities
678 .iter()
679 .any(|c| c.name == "elicitation"));
680 }
681
682 #[test]
683 fn test_version_features() {
684 let features = SchemaBuilder::build_version_features();
685
686 assert!(features.contains_key("2025-11-25"));
687 let v2025_features = &features["2025-11-25"];
688 assert!(v2025_features.contains(&"elicitation".to_string()));
689 assert!(v2025_features.contains(&"audio-content".to_string()));
690 assert!(v2025_features.contains(&"oauth-2.1".to_string()));
691 }
692}