From 24536b0eeaf02d6ccff9b290119579c75179bd57 Mon Sep 17 00:00:00 2001 From: Dale Seo <5466341+DaleSeo@users.noreply.github.com> Date: Sun, 8 Nov 2026 10:43:41 -0500 Subject: [PATCH] fix(server): send skill text with NUL bytes as a blob --- .../rmcp/src/handler/server/skill_catalog.rs | 3 ++- crates/rmcp/tests/test_skill_catalog.rs | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/crates/rmcp/src/handler/server/skill_catalog.rs b/crates/rmcp/src/handler/server/skill_catalog.rs index 0201fb26d..7c20a2792 100644 --- a/crates/rmcp/src/handler/server/skill_catalog.rs +++ b/crates/rmcp/src/handler/server/skill_catalog.rs @@ -215,7 +215,8 @@ impl SkillCatalog { .mime_type .is_some_and(is_textual) .then(|| std::str::from_utf8(&file.bytes).ok()) - .flatten(); + .flatten() + .filter(|text| !text.contains('\0')); let contents = match text { Some(text) => ResourceContents::text(text, uri), None => ResourceContents::blob(BASE64_STANDARD.encode(&file.bytes), uri), diff --git a/crates/rmcp/tests/test_skill_catalog.rs b/crates/rmcp/tests/test_skill_catalog.rs index b4c4f8423..fc8831467 100644 --- a/crates/rmcp/tests/test_skill_catalog.rs +++ b/crates/rmcp/tests/test_skill_catalog.rs @@ -332,6 +332,27 @@ mod serving { )); } + #[test] + fn should_serve_a_text_type_containing_nul_as_a_blob() { + let dir = skills_tree(); + let utf16le: Vec = "".bytes().flat_map(|b| [b, 0]).collect(); + write(dir.path(), "pdf-processing/form.xml", &utf16le); + let catalog = load(&dir); + + let result = catalog + .read_resource("skill://pdf-processing/form.xml") + .expect("served"); + + assert!(matches!( + &result.contents[0], + ResourceContents::BlobResourceContents { mime_type: Some(mime), .. } if mime == "application/xml" + )); + assert_eq!( + read_bytes(&catalog, "skill://pdf-processing/form.xml"), + utf16le + ); + } + #[test] fn should_omit_the_mime_type_of_an_unknown_extension() { let dir = skills_tree();