Skip to main content

prism_mcp_rs/protocol/
schema_introspection.rs

1//! improved Schema Introspection for MCP Protocol (2025-11-25)
2//!
3//! Module provides complete schema introspection capabilities,
4//! allowing clients to discover the full structure and capabilities of
5//! the MCP server at runtime
6
7use crate::protocol::discovery::*;
8use crate::protocol::types::*;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12// ============================================================================
13// Schema Introspection Types
14// ============================================================================
15
16/// Introspection result with schema information
17#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
18pub struct IntrospectionResult {
19    /// Protocol version and compatibility information
20    pub protocol: ProtocolInfo,
21
22    /// Method schemas
23    pub methods: MethodSchemas,
24
25    /// Type definitions used across the protocol
26    pub types: TypeDefinitions,
27
28    /// Capability schemas
29    pub capabilities: CapabilitySchemas,
30
31    /// Transport information
32    pub transports: Vec<TransportInfo>,
33
34    /// Extensions and experimental features
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub extensions: Option<ExtensionInfo>,
37}
38
39/// Protocol version and compatibility information
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
41pub struct ProtocolInfo {
42    /// Current protocol version
43    pub version: String,
44
45    /// Minimum compatible version
46    pub min_version: String,
47
48    /// Maximum compatible version
49    pub max_version: String,
50
51    /// List of all supported versions
52    pub supported_versions: Vec<String>,
53
54    /// Protocol features by version
55    pub version_features: HashMap<String, Vec<String>>,
56}
57
58/// Method schemas with documentation
59#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub struct MethodSchemas {
61    /// Request methods (client to server)
62    pub requests: Vec<MethodSchema>,
63
64    /// Server-initiated methods (server to client)
65    pub server_requests: Vec<MethodSchema>,
66
67    /// Notification methods
68    pub notifications: Vec<MethodSchema>,
69
70    /// Subscription methods
71    pub subscriptions: Vec<MethodSchema>,
72}
73
74/// Schema for a single method
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
76pub struct MethodSchema {
77    /// Method name
78    pub name: String,
79
80    /// Human-readable title
81    pub title: String,
82
83    /// Detailed description
84    pub description: String,
85
86    /// Method category
87    pub category: String,
88
89    /// JSON Schema for parameters
90    pub params: serde_json::Value,
91
92    /// JSON Schema for result
93    pub result: serde_json::Value,
94
95    /// Error schemas Method can return
96    pub errors: Vec<ErrorSchema>,
97
98    /// Examples of usage
99    pub examples: Vec<MethodExample>,
100
101    /// Method-specific metadata
102    pub metadata: MethodMetadata,
103}
104
105/// Error schema definition
106#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
107pub struct ErrorSchema {
108    /// Error code
109    pub code: i32,
110
111    /// Error name
112    pub name: String,
113
114    /// Error description
115    pub description: String,
116
117    /// Schema for error data field
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub data_schema: Option<serde_json::Value>,
120}
121
122/// Example of method usage
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
124pub struct MethodExample {
125    /// Example title
126    pub title: String,
127
128    /// Example description
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub description: Option<String>,
131
132    /// Example request
133    pub request: serde_json::Value,
134
135    /// Example response
136    pub response: serde_json::Value,
137}
138
139/// Method-specific metadata
140#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
141pub struct MethodMetadata {
142    /// Whether method requires authentication
143    pub requires_auth: bool,
144
145    /// Whether method supports progress notifications
146    pub supports_progress: bool,
147
148    /// Whether method supports cancellation
149    pub supports_cancellation: bool,
150
151    /// Whether method supports batching
152    pub supports_batching: bool,
153
154    /// Rate limiting information
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub rate_limit: Option<RateLimitInfo>,
157
158    /// Deprecation information
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub deprecation: Option<DeprecationInfo>,
161
162    /// Version when method was introduced
163    pub since_version: String,
164
165    /// Required capabilities
166    pub required_capabilities: Vec<String>,
167}
168
169/// Deprecation information
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
171pub struct DeprecationInfo {
172    /// Whether method is deprecated
173    pub deprecated: bool,
174
175    /// Version when deprecated
176    pub since: String,
177
178    /// Version when it will be removed
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub removal_version: Option<String>,
181
182    /// Replacement method or alternative
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub replacement: Option<String>,
185
186    /// Deprecation message
187    pub message: String,
188}
189
190/// Type definitions used across the protocol
191#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
192pub struct TypeDefinitions {
193    /// Core types (ContentBlock, etc.)
194    pub core: HashMap<String, serde_json::Value>,
195
196    /// Request parameter types
197    pub requests: HashMap<String, serde_json::Value>,
198
199    /// Response result types
200    pub responses: HashMap<String, serde_json::Value>,
201
202    /// Notification types
203    pub notifications: HashMap<String, serde_json::Value>,
204
205    /// Capability types
206    pub capabilities: HashMap<String, serde_json::Value>,
207
208    /// Custom/extension types
209    pub custom: HashMap<String, serde_json::Value>,
210}
211
212/// Capability schemas
213#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
214pub struct CapabilitySchemas {
215    /// Server capability schema
216    pub server: serde_json::Value,
217
218    /// Client capability schema
219    pub client: serde_json::Value,
220
221    /// Individual capability details
222    pub capabilities: Vec<CapabilityDetail>,
223}
224
225/// Detailed information about a capability
226#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
227pub struct CapabilityDetail {
228    /// Capability name
229    pub name: String,
230
231    /// Capability type (server/client)
232    pub capability_type: String,
233
234    /// Description
235    pub description: String,
236
237    /// Schema for capability configuration
238    pub schema: serde_json::Value,
239
240    /// Methods enabled by this capability
241    pub enabled_methods: Vec<String>,
242
243    /// Dependencies on other capabilities
244    pub dependencies: Vec<String>,
245
246    /// Version when introduced
247    pub since_version: String,
248}
249
250/// Transport information
251#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
252pub struct TransportInfo {
253    /// Transport name
254    pub name: String,
255
256    /// Transport type (stdio, http, websocket, etc.)
257    pub transport_type: String,
258
259    /// Description
260    pub description: String,
261
262    /// Configuration schema
263    pub config_schema: serde_json::Value,
264
265    /// Supported features
266    pub features: Vec<String>,
267
268    /// Performance characteristics
269    pub performance: TransportPerformance,
270}
271
272/// Transport performance characteristics
273#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
274pub struct TransportPerformance {
275    /// Latency characteristics
276    pub latency: String,
277
278    /// Throughput characteristics
279    pub throughput: String,
280
281    /// Whether transport supports streaming
282    pub streaming: bool,
283
284    /// Whether transport supports multiplexing
285    pub multiplexing: bool,
286
287    /// Whether transport supports compression
288    pub compression: bool,
289}
290
291/// Extension information
292#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
293pub struct ExtensionInfo {
294    /// Available extensions
295    pub extensions: Vec<Extension>,
296
297    /// Experimental features
298    pub experimental: Vec<ExperimentalFeature>,
299}
300
301/// Extension definition
302#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
303pub struct Extension {
304    /// Extension name
305    pub name: String,
306
307    /// Extension version
308    pub version: String,
309
310    /// Description
311    pub description: String,
312
313    /// Methods added by extension
314    pub methods: Vec<String>,
315
316    /// Types added by extension
317    pub types: Vec<String>,
318
319    /// Configuration schema
320    pub config_schema: serde_json::Value,
321}
322
323/// Experimental feature definition
324#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
325pub struct ExperimentalFeature {
326    /// Feature name
327    pub name: String,
328
329    /// Description
330    pub description: String,
331
332    /// Stability level (alpha, beta, rc)
333    pub stability: String,
334
335    /// Feature flag to enable
336    pub flag: String,
337
338    /// Expected stable version
339    #[serde(skip_serializing_if = "Option::is_none")]
340    pub stable_version: Option<String>,
341}
342
343// ============================================================================
344// Schema Builder
345// ============================================================================
346
347/// Builder for creating introspection schemas
348pub 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    /// Create a dual-era schema builder.
359    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    /// Build version features map
396    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    /// Add a method schema
447    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    /// Add a type definition
459    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    /// Add a transport
485    pub fn add_transport(mut self, transport: TransportInfo) -> Self {
486        self.transports.push(transport);
487        self
488    }
489
490    /// Add a capability
491    pub fn add_capability(mut self, capability: CapabilityDetail) -> Self {
492        self.capabilities.capabilities.push(capability);
493        self
494    }
495
496    /// Build the introspection result
497    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
515// ============================================================================
516// Introspection Provider
517// ============================================================================
518
519/// Provider for schema introspection
520pub struct IntrospectionProvider {
521    #[allow(dead_code)]
522    builder: SchemaBuilder,
523}
524
525impl IntrospectionProvider {
526    /// Create a new introspection provider
527    pub fn new() -> Self {
528        Self {
529            builder: SchemaBuilder::new(),
530        }
531    }
532
533    /// Build introspection for MCP 2025-11-25
534    pub fn build_complete_introspection(&self) -> IntrospectionResult {
535        let mut builder = SchemaBuilder::new();
536
537        // Add standard transports
538        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        // Add core capabilities
599        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// ============================================================================
635// Tests
636// ============================================================================
637
638#[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        // Check for specific transports
663        assert!(introspection.transports.iter().any(|t| t.name == "stdio"));
664        assert!(introspection
665            .transports
666            .iter()
667            .any(|t| t.name == "websocket"));
668
669        // Check for specific capabilities
670        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}