Skip to main content

contextvm_sdk/core/
serializers.rs

1//! Serialization utilities for converting between MCP messages and Nostr events.
2
3use nostr_sdk::prelude::*;
4
5use crate::core::types::JsonRpcMessage;
6
7/// Convert an MCP message to a Nostr event template (unsigned).
8///
9/// The message is JSON-serialized into the event's `content` field.
10pub fn mcp_to_nostr_event(
11    message: &JsonRpcMessage,
12    kind: u16,
13    tags: Vec<Tag>,
14) -> Result<EventBuilder, serde_json::Error> {
15    let content = serde_json::to_string(message)?;
16    Ok(EventBuilder::new(Kind::Custom(kind), content).tags(tags))
17}
18
19/// Deserialize a Nostr event's content into an MCP message.
20///
21/// Returns `None` if the content is not valid JSON or not a valid JSON-RPC message.
22pub fn nostr_event_to_mcp_message(content: &str) -> Option<JsonRpcMessage> {
23    serde_json::from_str(content).ok()
24}
25
26/// Extract a tag value from a Nostr event's tags.
27pub fn get_tag_value(tags: &Tags, name: &str) -> Option<String> {
28    tags.iter().find_map(|tag| {
29        let vec = tag.clone().to_vec();
30        if vec.first().map(|s| s.as_str()) == Some(name) {
31            vec.get(1).cloned()
32        } else {
33            None
34        }
35    })
36}
37
38/// Extract a tag value from a slice of tags.
39pub fn get_tag_value_from_slice(tags: &[Tag], name: &str) -> Option<String> {
40    tags.iter().find_map(|tag| {
41        let vec = tag.clone().to_vec();
42        if vec.first().map(|s| s.as_str()) == Some(name) {
43            vec.get(1).cloned()
44        } else {
45            None
46        }
47    })
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use crate::core::types::{JsonRpcMessage, JsonRpcRequest};
54
55    #[test]
56    fn test_roundtrip() {
57        let msg = JsonRpcMessage::Request(JsonRpcRequest {
58            jsonrpc: "2.0".to_string(),
59            id: Value::Number(1.into()),
60            method: "tools/list".to_string(),
61            params: None,
62        });
63
64        let content = serde_json::to_string(&msg).unwrap();
65        let parsed = nostr_event_to_mcp_message(&content).unwrap();
66        assert!(parsed.is_request());
67        assert_eq!(parsed.method(), Some("tools/list"));
68    }
69
70    #[test]
71    fn test_invalid_json() {
72        assert!(nostr_event_to_mcp_message("not json").is_none());
73    }
74}