fix: use mimeType instead of mime_type for MCP specification compliance (#339)

- Fix ResourceContents enum to properly serialize with camelCase field names
- Add comprehensive tests to verify JSON serialization compliance
- Ensure all resource-related structs use mimeType as per MCP spec
- Add tests for RawResource, ResourceContents, RawImageContent, RawAudioContent
- Add test for prompt message image content serialization
- Update message schema files to reflect mimeType changes
- Fix import ordering to match project formatting standards

This change ensures the Rust SDK is fully compliant with the MCP specification
which requires mimeType (camelCase) for all resource content types.
This commit is contained in:
Andrew Harvard 2025-07-29 12:42:56 -04:00 • committed by GitHub
commit 7ba1ac4959
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 107 additions and 5 deletions

View file

@ -171,3 +171,40 @@ impl IntoContents for () {
vec![]
}
}
#[cfg(test)]
mod tests {
use serde_json;
use super::*;
#[test]
fn test_image_content_serialization() {
let image_content = RawImageContent {
data: "base64data".to_string(),
mime_type: "image/png".to_string(),
};
let json = serde_json::to_string(&image_content).unwrap();
println!("ImageContent JSON: {}", json);
// Verify it contains mimeType (camelCase) not mime_type (snake_case)
assert!(json.contains("mimeType"));
assert!(!json.contains("mime_type"));
}
#[test]
fn test_audio_content_serialization() {
let audio_content = RawAudioContent {
data: "base64audiodata".to_string(),
mime_type: "audio/wav".to_string(),
};
let json = serde_json::to_string(&audio_content).unwrap();
println!("AudioContent JSON: {}", json);
// Verify it contains mimeType (camelCase) not mime_type (snake_case)
assert!(json.contains("mimeType"));
assert!(!json.contains("mime_type"));
}
}

View file

@ -168,3 +168,25 @@ pub struct PromptArgumentTemplate {
pub description: Option<String>,
pub required: Option<bool>,
}
#[cfg(test)]
mod tests {
use serde_json;
use super::*;
#[test]
fn test_prompt_message_image_serialization() {
let image_content = RawImageContent {
data: "base64data".to_string(),
mime_type: "image/png".to_string(),
};
let json = serde_json::to_string(&image_content).unwrap();
println!("PromptMessage ImageContent JSON: {}", json);
// Verify it contains mimeType (camelCase) not mime_type (snake_case)
assert!(json.contains("mimeType"));
assert!(!json.contains("mime_type"));
}
}

View file

@ -42,15 +42,17 @@ pub struct RawResourceTemplate {
pub type ResourceTemplate = Annotated<RawResourceTemplate>;
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "camelCase", untagged)]
#[serde(untagged)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum ResourceContents {
#[serde(rename_all = "camelCase")]
TextResourceContents {
uri: String,
#[serde(skip_serializing_if = "Option::is_none")]
mime_type: Option<String>,
text: String,
},
#[serde(rename_all = "camelCase")]
BlobResourceContents {
uri: String,
#[serde(skip_serializing_if = "Option::is_none")]
@ -81,3 +83,44 @@ impl RawResource {
}
}
}
#[cfg(test)]
mod tests {
use serde_json;
use super::*;
#[test]
fn test_resource_serialization() {
let resource = RawResource {
uri: "file:///test.txt".to_string(),
name: "test".to_string(),
description: Some("Test resource".to_string()),
mime_type: Some("text/plain".to_string()),
size: Some(100),
};
let json = serde_json::to_string(&resource).unwrap();
println!("Serialized JSON: {}", json);
// Verify it contains mimeType (camelCase) not mime_type (snake_case)
assert!(json.contains("mimeType"));
assert!(!json.contains("mime_type"));
}
#[test]
fn test_resource_contents_serialization() {
let text_contents = ResourceContents::TextResourceContents {
uri: "file:///test.txt".to_string(),
mime_type: Some("text/plain".to_string()),
text: "Hello world".to_string(),
};
let json = serde_json::to_string(&text_contents).unwrap();
println!("ResourceContents JSON: {}", json);
// Verify it contains mimeType (camelCase) not mime_type (snake_case)
assert!(json.contains("mimeType"));
assert!(!json.contains("mime_type"));
}
}

View file

@ -1129,7 +1129,7 @@
{
"type": "object",
"properties": {
"mime_type": {
"mimeType": {
"type": [
"string",
"null"
@ -1153,7 +1153,7 @@
"blob": {
"type": "string"
},
"mime_type": {
"mimeType": {
"type": [
"string",
"null"

View file

@ -1333,7 +1333,7 @@
{
"type": "object",
"properties": {
"mime_type": {
"mimeType": {
"type": [
"string",
"null"
@ -1357,7 +1357,7 @@
"blob": {
"type": "string"
},
"mime_type": {
"mimeType": {
"type": [
"string",
"null"