use rmcp::schemars;
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct ExtractFileParams {
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pdf_password: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<String>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct ExtractBytesParams {
pub data: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pdf_password: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<String>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct BatchExtractFilesParams {
pub paths: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pdf_password: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(schema_with = "file_configs_schema")]
pub file_configs: Option<Vec<Option<serde_json::Value>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<String>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct DetectMimeTypeParams {
pub path: String,
#[serde(default = "default_use_content")]
pub use_content: bool,
}
fn default_use_content() -> bool {
true
}
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct EmptyParams {}
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct CacheWarmParams {
#[serde(default)]
pub all_embeddings: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub embedding_model: Option<String>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct EmbedTextParams {
pub texts: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preset: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub api_key: Option<String>,
}
fn default_schema_name() -> String {
"extraction".to_string()
}
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct ExtractStructuredParams {
pub path: String,
pub schema: serde_json::Value,
pub model: String,
#[serde(default = "default_schema_name")]
pub schema_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub schema_description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub api_key: Option<String>,
#[serde(default)]
pub strict: bool,
}
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct ChunkTextParams {
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_characters: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub overlap: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub chunker_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub topic_threshold: Option<f32>,
}
fn file_configs_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "array",
"items": {
"anyOf": [
{"type": "null"},
{"type": "object"}
]
}
})
}
#[allow(dead_code)]
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct DownloadGrammarsParams {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub languages: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub groups: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub all: Option<bool>,
}
#[allow(dead_code)]
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct ListGrammarsParams {
#[serde(default)]
pub downloaded_only: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extract_file_params_defaults() {
let json = r#"{"path": "/test.pdf"}"#;
let params: ExtractFileParams = serde_json::from_str(json).unwrap();
assert_eq!(params.path, "/test.pdf");
assert_eq!(params.mime_type, None);
assert_eq!(params.config, None);
}
#[test]
fn test_extract_bytes_params_defaults() {
let json = r#"{"data": "SGVsbG8="}"#;
let params: ExtractBytesParams = serde_json::from_str(json).unwrap();
assert_eq!(params.data, "SGVsbG8=");
assert_eq!(params.mime_type, None);
assert_eq!(params.config, None);
}
#[test]
fn test_batch_extract_files_params_defaults() {
let json = r#"{"paths": ["/a.pdf", "/b.pdf"]}"#;
let params: BatchExtractFilesParams = serde_json::from_str(json).unwrap();
assert_eq!(params.paths.len(), 2);
assert_eq!(params.config, None);
}
#[test]
fn test_detect_mime_type_params_defaults() {
let json = r#"{"path": "/test.pdf"}"#;
let params: DetectMimeTypeParams = serde_json::from_str(json).unwrap();
assert_eq!(params.path, "/test.pdf");
assert!(params.use_content);
}
#[test]
fn test_detect_mime_type_params_use_content_false() {
let json = r#"{"path": "/test.pdf", "use_content": false}"#;
let params: DetectMimeTypeParams = serde_json::from_str(json).unwrap();
assert!(!params.use_content);
}
#[test]
fn test_extract_file_params_with_config() {
let json = r#"{"path": "/test.pdf", "config": {"use_cache": false}}"#;
let params: ExtractFileParams = serde_json::from_str(json).unwrap();
assert_eq!(params.path, "/test.pdf");
assert!(params.config.is_some());
}
#[test]
fn test_extract_file_params_serialization() {
let params = ExtractFileParams {
path: "/test.pdf".to_string(),
mime_type: Some("application/pdf".to_string()),
config: Some(serde_json::json!({"use_cache": false})),
pdf_password: None,
response_format: None,
};
let json = serde_json::to_string(¶ms).unwrap();
let deserialized: ExtractFileParams = serde_json::from_str(&json).unwrap();
assert_eq!(params.path, deserialized.path);
assert_eq!(params.mime_type, deserialized.mime_type);
assert_eq!(params.config, deserialized.config);
}
#[test]
fn test_extract_bytes_params_serialization() {
let params = ExtractBytesParams {
data: "SGVsbG8=".to_string(),
mime_type: None,
config: None,
pdf_password: None,
response_format: None,
};
let json = serde_json::to_string(¶ms).unwrap();
let deserialized: ExtractBytesParams = serde_json::from_str(&json).unwrap();
assert_eq!(params.data, deserialized.data);
}
#[test]
fn test_batch_extract_params_serialization() {
let params = BatchExtractFilesParams {
paths: vec!["/a.pdf".to_string(), "/b.pdf".to_string()],
config: Some(serde_json::json!({"use_cache": true})),
pdf_password: None,
file_configs: None,
response_format: None,
};
let json = serde_json::to_string(¶ms).unwrap();
let deserialized: BatchExtractFilesParams = serde_json::from_str(&json).unwrap();
assert_eq!(params.paths, deserialized.paths);
assert_eq!(params.config, deserialized.config);
}
#[test]
fn test_detect_mime_type_params_serialization() {
let params = DetectMimeTypeParams {
path: "/test.pdf".to_string(),
use_content: false,
};
let json = serde_json::to_string(¶ms).unwrap();
let deserialized: DetectMimeTypeParams = serde_json::from_str(&json).unwrap();
assert_eq!(params.path, deserialized.path);
assert_eq!(params.use_content, deserialized.use_content);
}
#[test]
fn test_empty_params_schema_has_type_object() {
let schema = schemars::schema_for!(EmptyParams);
let json = serde_json::to_value(&schema).unwrap();
assert_eq!(json["type"], "object");
}
#[test]
fn test_empty_params_deserializes_from_empty_object() {
let params: EmptyParams = serde_json::from_str("{}").unwrap();
let _ = params;
}
#[test]
fn test_cache_warm_params_defaults() {
let json = r#"{}"#;
let params: CacheWarmParams = serde_json::from_str(json).unwrap();
assert!(!params.all_embeddings);
assert!(params.embedding_model.is_none());
}
#[test]
fn test_cache_warm_params_with_values() {
let json = r#"{"all_embeddings": true, "embedding_model": "balanced"}"#;
let params: CacheWarmParams = serde_json::from_str(json).unwrap();
assert!(params.all_embeddings);
assert_eq!(params.embedding_model.as_deref(), Some("balanced"));
}
#[test]
fn test_embed_text_params_defaults() {
let json = r#"{"texts": ["hello", "world"]}"#;
let params: EmbedTextParams = serde_json::from_str(json).unwrap();
assert_eq!(params.texts.len(), 2);
assert!(params.preset.is_none());
}
#[test]
fn test_embed_text_params_with_preset() {
let json = r#"{"texts": ["hello"], "preset": "quality"}"#;
let params: EmbedTextParams = serde_json::from_str(json).unwrap();
assert_eq!(params.preset.as_deref(), Some("quality"));
}
#[test]
fn test_chunk_text_params_defaults() {
let json = r#"{"text": "some long text"}"#;
let params: ChunkTextParams = serde_json::from_str(json).unwrap();
assert_eq!(params.text, "some long text");
assert!(params.max_characters.is_none());
assert!(params.overlap.is_none());
assert!(params.chunker_type.is_none());
}
#[test]
fn test_chunk_text_params_with_all_fields() {
let json = r#"{"text": "hello", "max_characters": 500, "overlap": 50, "chunker_type": "markdown"}"#;
let params: ChunkTextParams = serde_json::from_str(json).unwrap();
assert_eq!(params.max_characters, Some(500));
assert_eq!(params.overlap, Some(50));
assert_eq!(params.chunker_type.as_deref(), Some("markdown"));
}
#[test]
fn test_chunk_text_params_with_topic_threshold() {
let json = r#"{"text": "hello", "chunker_type": "semantic", "topic_threshold": 0.6}"#;
let params: ChunkTextParams = serde_json::from_str(json).unwrap();
assert_eq!(params.chunker_type.as_deref(), Some("semantic"));
assert_eq!(params.topic_threshold, Some(0.6));
}
#[test]
fn test_chunk_text_params_topic_threshold_defaults_to_none() {
let json = r#"{"text": "hello"}"#;
let params: ChunkTextParams = serde_json::from_str(json).unwrap();
assert!(params.topic_threshold.is_none());
}
}