contextvm_sdk/core/
constants.rs1pub const CTXVM_MESSAGES_KIND: u16 = 25910;
8
9pub const GIFT_WRAP_KIND: u16 = 1059;
11
12pub const EPHEMERAL_GIFT_WRAP_KIND: u16 = 21059;
17
18pub const RELAY_LIST_METADATA_KIND: u16 = 10002;
20
21pub const SERVER_ANNOUNCEMENT_KIND: u16 = 11316;
23
24pub const TOOLS_LIST_KIND: u16 = 11317;
26
27pub const RESOURCES_LIST_KIND: u16 = 11318;
29
30pub const RESOURCETEMPLATES_LIST_KIND: u16 = 11319;
32
33pub const PROMPTS_LIST_KIND: u16 = 11320;
35
36pub mod tags {
38 pub const PUBKEY: &str = "p";
40
41 pub const RELAY: &str = "r";
43
44 pub const EVENT_ID: &str = "e";
46
47 pub const CAPABILITY: &str = "cap";
49
50 pub const NAME: &str = "name";
52
53 pub const WEBSITE: &str = "website";
55
56 pub const PICTURE: &str = "picture";
58
59 pub const ABOUT: &str = "about";
61
62 pub const SUPPORT_ENCRYPTION: &str = "support_encryption";
64
65 pub const SUPPORT_ENCRYPTION_EPHEMERAL: &str = "support_encryption_ephemeral";
67
68 pub const SUPPORT_OVERSIZED_TRANSFER: &str = "support_oversized_transfer";
70
71 pub const SUPPORT_OPEN_STREAM: &str = "support_open_stream";
73}
74
75pub const MAX_MESSAGE_SIZE: usize = 1024 * 1024;
77
78pub const DEFAULT_LRU_SIZE: usize = 5000;
80
81pub const DEFAULT_TIMEOUT_MS: u64 = 30_000;
83
84pub const DEFAULT_BOOTSTRAP_RELAY_URLS: &[&str] = &[
89 "wss://relay.damus.io",
90 "wss://relay.primal.net",
91 "wss://nos.lol",
92 "wss://relay.snort.social/",
93 "wss://nostr.mom/",
94 "wss://nostr.oxtr.dev/",
95];
96
97pub const INITIALIZE_METHOD: &str = "initialize";
99
100pub const NOTIFICATIONS_INITIALIZED_METHOD: &str = "notifications/initialized";
102
103pub const ANNOUNCEMENT_REQUEST_ID: &str = "announcement";
109
110pub const UNENCRYPTED_KINDS: &[u16] = &[
112 SERVER_ANNOUNCEMENT_KIND,
113 TOOLS_LIST_KIND,
114 RESOURCES_LIST_KIND,
115 RESOURCETEMPLATES_LIST_KIND,
116 PROMPTS_LIST_KIND,
117];
118
119#[cfg(feature = "rmcp")]
121pub fn mcp_protocol_version() -> &'static str {
122 use std::sync::OnceLock;
123 static VERSION: OnceLock<String> = OnceLock::new();
124 VERSION
125 .get_or_init(|| rmcp::model::ProtocolVersion::LATEST.to_string())
126 .as_str()
127}
128
129#[cfg(not(feature = "rmcp"))]
131pub const fn mcp_protocol_version() -> &'static str {
132 "2025-11-25"
133}
134
135const _: () = {
138 assert!(EPHEMERAL_GIFT_WRAP_KIND >= 20000);
140 assert!(EPHEMERAL_GIFT_WRAP_KIND < 30000);
141 assert!(CTXVM_MESSAGES_KIND >= 20000);
142 assert!(CTXVM_MESSAGES_KIND < 30000);
143 assert!(RELAY_LIST_METADATA_KIND >= 10000);
145 assert!(RELAY_LIST_METADATA_KIND < 20000);
146};
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_event_kind_values_match_spec() {
154 assert_eq!(CTXVM_MESSAGES_KIND, 25910);
155 assert_eq!(GIFT_WRAP_KIND, 1059);
156 assert_eq!(EPHEMERAL_GIFT_WRAP_KIND, 21059);
157 assert_eq!(RELAY_LIST_METADATA_KIND, 10002);
158 assert_eq!(SERVER_ANNOUNCEMENT_KIND, 11316);
159 assert_eq!(TOOLS_LIST_KIND, 11317);
160 assert_eq!(RESOURCES_LIST_KIND, 11318);
161 assert_eq!(RESOURCETEMPLATES_LIST_KIND, 11319);
162 assert_eq!(PROMPTS_LIST_KIND, 11320);
163 }
164
165 #[test]
166 fn test_tag_values_match_ts_sdk() {
167 assert_eq!(tags::PUBKEY, "p");
168 assert_eq!(tags::RELAY, "r");
169 assert_eq!(tags::EVENT_ID, "e");
170 assert_eq!(tags::CAPABILITY, "cap");
171 assert_eq!(tags::NAME, "name");
172 assert_eq!(tags::WEBSITE, "website");
173 assert_eq!(tags::PICTURE, "picture");
174 assert_eq!(tags::ABOUT, "about");
175 assert_eq!(tags::SUPPORT_ENCRYPTION, "support_encryption");
176 assert_eq!(
177 tags::SUPPORT_ENCRYPTION_EPHEMERAL,
178 "support_encryption_ephemeral"
179 );
180 assert_eq!(
181 tags::SUPPORT_OVERSIZED_TRANSFER,
182 "support_oversized_transfer"
183 );
184 }
185
186 #[test]
187 fn test_announcement_kinds_in_addressable_range() {
188 for &kind in UNENCRYPTED_KINDS {
192 assert!(kind >= 11316);
193 assert!(kind <= 11320);
194 }
195 }
196
197 #[test]
198 fn test_bootstrap_relays_are_wss() {
199 for url in DEFAULT_BOOTSTRAP_RELAY_URLS {
200 assert!(
201 url.starts_with("wss://"),
202 "Bootstrap relay must use wss: {url}"
203 );
204 }
205 }
206
207 #[test]
208 fn test_bootstrap_relays_nonempty() {
209 assert!(
210 !DEFAULT_BOOTSTRAP_RELAY_URLS.is_empty(),
211 "Must have at least one bootstrap relay"
212 );
213 }
214
215 #[test]
216 fn test_mcp_method_constants() {
217 assert_eq!(INITIALIZE_METHOD, "initialize");
218 assert_eq!(
219 NOTIFICATIONS_INITIALIZED_METHOD,
220 "notifications/initialized"
221 );
222 }
223
224 #[test]
225 fn test_announcement_request_id() {
226 assert_eq!(ANNOUNCEMENT_REQUEST_ID, "announcement");
227 assert_ne!(ANNOUNCEMENT_REQUEST_ID, "contextvm-stateless-init");
229 }
230
231 #[test]
232 fn test_unencrypted_kinds_contains_all_announcements() {
233 assert!(UNENCRYPTED_KINDS.contains(&SERVER_ANNOUNCEMENT_KIND));
234 assert!(UNENCRYPTED_KINDS.contains(&TOOLS_LIST_KIND));
235 assert!(UNENCRYPTED_KINDS.contains(&RESOURCES_LIST_KIND));
236 assert!(UNENCRYPTED_KINDS.contains(&RESOURCETEMPLATES_LIST_KIND));
237 assert!(UNENCRYPTED_KINDS.contains(&PROMPTS_LIST_KIND));
238 }
239
240 #[test]
241 fn test_gift_wrap_not_in_unencrypted() {
242 assert!(!UNENCRYPTED_KINDS.contains(&GIFT_WRAP_KIND));
243 assert!(!UNENCRYPTED_KINDS.contains(&EPHEMERAL_GIFT_WRAP_KIND));
244 }
245}