1use std::collections::HashMap;
7use std::sync::Arc;
8
9use turul_http_mcp_server::{ServerConfig, StreamConfig};
10use turul_mcp_protocol::{Implementation, ServerCapabilities};
11use turul_mcp_server::handlers::{McpHandler, *};
12use turul_mcp_server::{
13 McpCompletion, McpElicitation, McpLogger, McpNotification, McpPrompt, McpResource, McpRoot,
14 McpSampling, McpTool,
15};
16use turul_mcp_session_storage::BoxedSessionStorage;
17
18use crate::error::Result;
19
20#[cfg(feature = "dynamodb")]
21use crate::error::LambdaError;
22use crate::server::LambdaMcpServer;
23
24#[cfg(feature = "cors")]
25use crate::cors::CorsConfig;
26
27pub struct LambdaMcpServerBuilder {
70 name: String,
72 version: String,
73 title: Option<String>,
74
75 capabilities: ServerCapabilities,
77
78 tools: HashMap<String, Arc<dyn McpTool>>,
80
81 resources: HashMap<String, Arc<dyn McpResource>>,
83
84 prompts: HashMap<String, Arc<dyn McpPrompt>>,
86
87 elicitations: HashMap<String, Arc<dyn McpElicitation>>,
89
90 sampling: HashMap<String, Arc<dyn McpSampling>>,
92
93 completions: HashMap<String, Arc<dyn McpCompletion>>,
95
96 loggers: HashMap<String, Arc<dyn McpLogger>>,
98
99 root_providers: HashMap<String, Arc<dyn McpRoot>>,
101
102 notifications: HashMap<String, Arc<dyn McpNotification>>,
104
105 handlers: HashMap<String, Arc<dyn McpHandler>>,
107
108 roots: Vec<turul_mcp_protocol::roots::Root>,
110
111 instructions: Option<String>,
113
114 session_timeout_minutes: Option<u64>,
116 session_cleanup_interval_seconds: Option<u64>,
117
118 session_storage: Option<Arc<BoxedSessionStorage>>,
120
121 strict_lifecycle: bool,
123
124 enable_sse: bool,
126 server_config: ServerConfig,
128 stream_config: StreamConfig,
129
130 #[cfg(feature = "cors")]
132 cors_config: Option<CorsConfig>,
133}
134
135impl LambdaMcpServerBuilder {
136 pub fn new() -> Self {
138 let capabilities = ServerCapabilities::default();
141
142 let mut handlers: HashMap<String, Arc<dyn McpHandler>> = HashMap::new();
144 handlers.insert("ping".to_string(), Arc::new(PingHandler));
145 handlers.insert(
146 "completion/complete".to_string(),
147 Arc::new(CompletionHandler),
148 );
149 handlers.insert(
150 "resources/list".to_string(),
151 Arc::new(ResourcesHandler::new()),
152 );
153 handlers.insert(
154 "prompts/list".to_string(),
155 Arc::new(PromptsListHandler::new()),
156 );
157 handlers.insert(
158 "prompts/get".to_string(),
159 Arc::new(PromptsGetHandler::new()),
160 );
161 handlers.insert("logging/setLevel".to_string(), Arc::new(LoggingHandler));
162 handlers.insert("roots/list".to_string(), Arc::new(RootsHandler::new()));
163 handlers.insert(
164 "sampling/createMessage".to_string(),
165 Arc::new(SamplingHandler),
166 );
167 handlers.insert(
168 "resources/templates/list".to_string(),
169 Arc::new(ResourceTemplatesHandler::new()),
170 );
171 handlers.insert(
172 "elicitation/create".to_string(),
173 Arc::new(ElicitationHandler::with_mock_provider()),
174 );
175
176 let notifications_handler = Arc::new(NotificationsHandler);
178 handlers.insert(
179 "notifications/message".to_string(),
180 notifications_handler.clone(),
181 );
182 handlers.insert(
183 "notifications/progress".to_string(),
184 notifications_handler.clone(),
185 );
186 handlers.insert(
187 "notifications/resources/listChanged".to_string(),
188 notifications_handler.clone(),
189 );
190 handlers.insert(
191 "notifications/resources/updated".to_string(),
192 notifications_handler.clone(),
193 );
194 handlers.insert(
195 "notifications/tools/listChanged".to_string(),
196 notifications_handler.clone(),
197 );
198 handlers.insert(
199 "notifications/prompts/listChanged".to_string(),
200 notifications_handler.clone(),
201 );
202 handlers.insert(
203 "notifications/roots/listChanged".to_string(),
204 notifications_handler,
205 );
206
207 Self {
208 name: "turul-mcp-aws-lambda".to_string(),
209 version: env!("CARGO_PKG_VERSION").to_string(),
210 title: None,
211 capabilities,
212 tools: HashMap::new(),
213 resources: HashMap::new(),
214 prompts: HashMap::new(),
215 elicitations: HashMap::new(),
216 sampling: HashMap::new(),
217 completions: HashMap::new(),
218 loggers: HashMap::new(),
219 root_providers: HashMap::new(),
220 notifications: HashMap::new(),
221 handlers,
222 roots: Vec::new(),
223 instructions: None,
224 session_timeout_minutes: None,
225 session_cleanup_interval_seconds: None,
226 session_storage: None,
227 strict_lifecycle: false,
228 enable_sse: cfg!(feature = "sse"),
229 server_config: ServerConfig::default(),
230 stream_config: StreamConfig::default(),
231 #[cfg(feature = "cors")]
232 cors_config: None,
233 }
234 }
235
236 pub fn name(mut self, name: impl Into<String>) -> Self {
238 self.name = name.into();
239 self
240 }
241
242 pub fn version(mut self, version: impl Into<String>) -> Self {
244 self.version = version.into();
245 self
246 }
247
248 pub fn title(mut self, title: impl Into<String>) -> Self {
250 self.title = Some(title.into());
251 self
252 }
253
254 pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
256 self.instructions = Some(instructions.into());
257 self
258 }
259
260 pub fn tool<T: McpTool + 'static>(mut self, tool: T) -> Self {
272 let name = tool.name().to_string();
273 self.tools.insert(name, Arc::new(tool));
274 self
275 }
276
277 pub fn tool_fn<F, T>(self, func: F) -> Self
279 where
280 F: Fn() -> T,
281 T: McpTool + 'static,
282 {
283 self.tool(func())
284 }
285
286 pub fn tools<T: McpTool + 'static, I: IntoIterator<Item = T>>(mut self, tools: I) -> Self {
288 for tool in tools {
289 self = self.tool(tool);
290 }
291 self
292 }
293
294 pub fn resource<R: McpResource + 'static>(mut self, resource: R) -> Self {
296 let uri = resource.uri().to_string();
297 self.resources.insert(uri, Arc::new(resource));
298 self
299 }
300
301 pub fn resources<R: McpResource + 'static, I: IntoIterator<Item = R>>(
303 mut self,
304 resources: I,
305 ) -> Self {
306 for resource in resources {
307 self = self.resource(resource);
308 }
309 self
310 }
311
312 pub fn prompt<P: McpPrompt + 'static>(mut self, prompt: P) -> Self {
314 let name = prompt.name().to_string();
315 self.prompts.insert(name, Arc::new(prompt));
316 self
317 }
318
319 pub fn prompts<P: McpPrompt + 'static, I: IntoIterator<Item = P>>(
321 mut self,
322 prompts: I,
323 ) -> Self {
324 for prompt in prompts {
325 self = self.prompt(prompt);
326 }
327 self
328 }
329
330 pub fn elicitation<E: McpElicitation + 'static>(mut self, elicitation: E) -> Self {
332 let key = format!("elicitation_{}", self.elicitations.len());
333 self.elicitations.insert(key, Arc::new(elicitation));
334 self
335 }
336
337 pub fn elicitations<E: McpElicitation + 'static, I: IntoIterator<Item = E>>(
339 mut self,
340 elicitations: I,
341 ) -> Self {
342 for elicitation in elicitations {
343 self = self.elicitation(elicitation);
344 }
345 self
346 }
347
348 pub fn sampling_provider<S: McpSampling + 'static>(mut self, sampling: S) -> Self {
350 let key = format!("sampling_{}", self.sampling.len());
351 self.sampling.insert(key, Arc::new(sampling));
352 self
353 }
354
355 pub fn sampling_providers<S: McpSampling + 'static, I: IntoIterator<Item = S>>(
357 mut self,
358 sampling: I,
359 ) -> Self {
360 for s in sampling {
361 self = self.sampling_provider(s);
362 }
363 self
364 }
365
366 pub fn completion_provider<C: McpCompletion + 'static>(mut self, completion: C) -> Self {
368 let key = format!("completion_{}", self.completions.len());
369 self.completions.insert(key, Arc::new(completion));
370 self
371 }
372
373 pub fn completion_providers<C: McpCompletion + 'static, I: IntoIterator<Item = C>>(
375 mut self,
376 completions: I,
377 ) -> Self {
378 for completion in completions {
379 self = self.completion_provider(completion);
380 }
381 self
382 }
383
384 pub fn logger<L: McpLogger + 'static>(mut self, logger: L) -> Self {
386 let key = format!("logger_{}", self.loggers.len());
387 self.loggers.insert(key, Arc::new(logger));
388 self
389 }
390
391 pub fn loggers<L: McpLogger + 'static, I: IntoIterator<Item = L>>(
393 mut self,
394 loggers: I,
395 ) -> Self {
396 for logger in loggers {
397 self = self.logger(logger);
398 }
399 self
400 }
401
402 pub fn root_provider<R: McpRoot + 'static>(mut self, root: R) -> Self {
404 let key = format!("root_{}", self.root_providers.len());
405 self.root_providers.insert(key, Arc::new(root));
406 self
407 }
408
409 pub fn root_providers<R: McpRoot + 'static, I: IntoIterator<Item = R>>(
411 mut self,
412 roots: I,
413 ) -> Self {
414 for root in roots {
415 self = self.root_provider(root);
416 }
417 self
418 }
419
420 pub fn notification_provider<N: McpNotification + 'static>(mut self, notification: N) -> Self {
422 let key = format!("notification_{}", self.notifications.len());
423 self.notifications.insert(key, Arc::new(notification));
424 self
425 }
426
427 pub fn notification_providers<N: McpNotification + 'static, I: IntoIterator<Item = N>>(
429 mut self,
430 notifications: I,
431 ) -> Self {
432 for notification in notifications {
433 self = self.notification_provider(notification);
434 }
435 self
436 }
437
438 pub fn sampler<S: McpSampling + 'static>(self, sampling: S) -> Self {
444 self.sampling_provider(sampling)
445 }
446
447 pub fn completer<C: McpCompletion + 'static>(self, completion: C) -> Self {
449 self.completion_provider(completion)
450 }
451
452 pub fn notification_type<N: McpNotification + 'static + Default>(self) -> Self {
454 let notification = N::default();
455 self.notification_provider(notification)
456 }
457
458 pub fn handler<H: McpHandler + 'static>(mut self, handler: H) -> Self {
460 let handler_arc = Arc::new(handler);
461 for method in handler_arc.supported_methods() {
462 self.handlers.insert(method, handler_arc.clone());
463 }
464 self
465 }
466
467 pub fn handlers<H: McpHandler + 'static, I: IntoIterator<Item = H>>(
469 mut self,
470 handlers: I,
471 ) -> Self {
472 for handler in handlers {
473 self = self.handler(handler);
474 }
475 self
476 }
477
478 pub fn root(mut self, root: turul_mcp_protocol::roots::Root) -> Self {
480 self.roots.push(root);
481 self
482 }
483
484 pub fn with_completion(mut self) -> Self {
490 use turul_mcp_protocol::initialize::CompletionsCapabilities;
491 self.capabilities.completions = Some(CompletionsCapabilities {
492 enabled: Some(true),
493 });
494 self.handler(CompletionHandler)
495 }
496
497 pub fn with_prompts(mut self) -> Self {
499 use turul_mcp_protocol::initialize::PromptsCapabilities;
500 self.capabilities.prompts = Some(PromptsCapabilities {
501 list_changed: Some(false),
502 });
503
504 self
507 }
508
509 pub fn with_resources(mut self) -> Self {
511 use turul_mcp_protocol::initialize::ResourcesCapabilities;
512 self.capabilities.resources = Some(ResourcesCapabilities {
513 subscribe: Some(false),
514 list_changed: Some(false),
515 });
516
517 let mut handler = ResourcesHandler::new();
519 for resource in self.resources.values() {
520 handler = handler.add_resource_arc(resource.clone());
521 }
522
523 self.handler(handler)
524 }
525
526 pub fn with_logging(mut self) -> Self {
528 use turul_mcp_protocol::initialize::LoggingCapabilities;
529 self.capabilities.logging = Some(LoggingCapabilities::default());
530 self.handler(LoggingHandler)
531 }
532
533 pub fn with_roots(self) -> Self {
535 self.handler(RootsHandler::new())
536 }
537
538 pub fn with_sampling(self) -> Self {
540 self.handler(SamplingHandler)
541 }
542
543 pub fn with_elicitation(mut self) -> Self {
545 use turul_mcp_protocol::initialize::ElicitationCapabilities;
546 self.capabilities.elicitation = Some(ElicitationCapabilities {
547 enabled: Some(true),
548 });
549 self.handler(ElicitationHandler::with_mock_provider())
550 }
551
552 pub fn with_elicitation_provider<P: ElicitationProvider + 'static>(
554 mut self,
555 provider: P,
556 ) -> Self {
557 use turul_mcp_protocol::initialize::ElicitationCapabilities;
558 self.capabilities.elicitation = Some(ElicitationCapabilities {
559 enabled: Some(true),
560 });
561 self.handler(ElicitationHandler::new(Arc::new(provider)))
562 }
563
564 pub fn with_notifications(self) -> Self {
566 self.handler(NotificationsHandler)
567 }
568
569 pub fn session_timeout_minutes(mut self, minutes: u64) -> Self {
575 self.session_timeout_minutes = Some(minutes);
576 self
577 }
578
579 pub fn session_cleanup_interval_seconds(mut self, seconds: u64) -> Self {
581 self.session_cleanup_interval_seconds = Some(seconds);
582 self
583 }
584
585 pub fn strict_lifecycle(mut self, strict: bool) -> Self {
587 self.strict_lifecycle = strict;
588 self
589 }
590
591 pub fn with_strict_lifecycle(self) -> Self {
593 self.strict_lifecycle(true)
594 }
595
596 pub fn sse(mut self, enable: bool) -> Self {
598 self.enable_sse = enable;
599
600 if enable {
602 self.server_config.enable_get_sse = true;
603 self.server_config.enable_post_sse = true;
604 } else {
605 self.server_config.enable_get_sse = false;
608 self.server_config.enable_post_sse = false;
609 }
610
611 self
612 }
613
614 pub fn with_long_sessions(mut self) -> Self {
616 self.session_timeout_minutes = Some(120); self.session_cleanup_interval_seconds = Some(300); self
619 }
620
621 pub fn with_short_sessions(mut self) -> Self {
623 self.session_timeout_minutes = Some(5); self.session_cleanup_interval_seconds = Some(30); self
626 }
627
628 pub fn storage(mut self, storage: Arc<BoxedSessionStorage>) -> Self {
636 self.session_storage = Some(storage);
637 self
638 }
639
640 #[cfg(feature = "dynamodb")]
647 pub async fn dynamodb_storage(self) -> Result<Self> {
648 use turul_mcp_session_storage::DynamoDbSessionStorage;
649
650 let storage = DynamoDbSessionStorage::new().await.map_err(|e| {
651 LambdaError::Config(format!("Failed to create DynamoDB storage: {}", e))
652 })?;
653
654 Ok(self.storage(Arc::new(storage)))
655 }
656
657 pub fn server_config(mut self, config: ServerConfig) -> Self {
659 self.server_config = config;
660 self
661 }
662
663 pub fn stream_config(mut self, config: StreamConfig) -> Self {
665 self.stream_config = config;
666 self
667 }
668
669 #[cfg(feature = "cors")]
673 pub fn cors(mut self, config: CorsConfig) -> Self {
674 self.cors_config = Some(config);
675 self
676 }
677
678 #[cfg(feature = "cors")]
680 pub fn cors_allow_all_origins(mut self) -> Self {
681 self.cors_config = Some(CorsConfig::allow_all());
682 self
683 }
684
685 #[cfg(feature = "cors")]
687 pub fn cors_allow_origins(mut self, origins: Vec<String>) -> Self {
688 self.cors_config = Some(CorsConfig::for_origins(origins));
689 self
690 }
691
692 #[cfg(feature = "cors")]
699 pub fn cors_from_env(mut self) -> Self {
700 self.cors_config = Some(CorsConfig::from_env());
701 self
702 }
703
704 #[cfg(feature = "cors")]
706 pub fn cors_disabled(self) -> Self {
707 self
709 }
710
711 #[cfg(all(feature = "dynamodb", feature = "cors"))]
717 pub async fn production_config(self) -> Result<Self> {
718 Ok(self.dynamodb_storage().await?.cors_from_env())
719 }
720
721 #[cfg(feature = "cors")]
725 pub fn development_config(self) -> Self {
726 use turul_mcp_session_storage::InMemorySessionStorage;
727
728 self.storage(Arc::new(InMemorySessionStorage::new()))
729 .cors_allow_all_origins()
730 }
731
732 pub async fn build(self) -> Result<LambdaMcpServer> {
736 use turul_mcp_session_storage::InMemorySessionStorage;
737
738 if self.name.is_empty() {
740 return Err(crate::error::LambdaError::Configuration(
741 "Server name cannot be empty".to_string(),
742 ));
743 }
744 if self.version.is_empty() {
745 return Err(crate::error::LambdaError::Configuration(
746 "Server version cannot be empty".to_string(),
747 ));
748 }
749
750 let session_storage = self
756 .session_storage
757 .unwrap_or_else(|| Arc::new(InMemorySessionStorage::new()));
758
759 let implementation = if let Some(title) = self.title {
761 Implementation::new(&self.name, &self.version).with_title(title)
762 } else {
763 Implementation::new(&self.name, &self.version)
764 };
765
766 let mut capabilities = self.capabilities.clone();
768 let has_tools = !self.tools.is_empty();
769 let has_resources = !self.resources.is_empty();
770 let has_prompts = !self.prompts.is_empty();
771 let has_elicitations = !self.elicitations.is_empty();
772 let has_completions = !self.completions.is_empty();
773 let has_logging = !self.loggers.is_empty();
774 tracing::debug!("🔧 Has logging configured: {}", has_logging);
775
776 if has_tools {
778 capabilities.tools = Some(turul_mcp_protocol::initialize::ToolsCapabilities {
779 list_changed: Some(false), });
781 }
782
783 if has_resources {
785 capabilities.resources = Some(turul_mcp_protocol::initialize::ResourcesCapabilities {
786 subscribe: Some(false), list_changed: Some(false), });
789 }
790
791 if has_prompts {
793 capabilities.prompts = Some(turul_mcp_protocol::initialize::PromptsCapabilities {
794 list_changed: Some(false), });
796 }
797
798 if has_elicitations {
800 capabilities.elicitation =
801 Some(turul_mcp_protocol::initialize::ElicitationCapabilities {
802 enabled: Some(true),
803 });
804 }
805
806 if has_completions {
808 capabilities.completions =
809 Some(turul_mcp_protocol::initialize::CompletionsCapabilities {
810 enabled: Some(true),
811 });
812 }
813
814 capabilities.logging = Some(turul_mcp_protocol::initialize::LoggingCapabilities {
817 enabled: Some(true),
818 levels: Some(vec![
819 "debug".to_string(),
820 "info".to_string(),
821 "warning".to_string(),
822 "error".to_string(),
823 ]),
824 });
825
826 let mut handlers = self.handlers;
828 if !self.roots.is_empty() {
829 let mut roots_handler = RootsHandler::new();
830 for root in &self.roots {
831 roots_handler = roots_handler.add_root(root.clone());
832 }
833 handlers.insert("roots/list".to_string(), Arc::new(roots_handler));
834 }
835
836 Ok(LambdaMcpServer::new(
838 implementation,
839 capabilities,
840 self.tools,
841 self.resources,
842 self.prompts,
843 self.elicitations,
844 self.sampling,
845 self.completions,
846 self.loggers,
847 self.root_providers,
848 self.notifications,
849 handlers,
850 self.roots,
851 self.instructions,
852 session_storage,
853 self.strict_lifecycle,
854 self.server_config,
855 self.enable_sse,
856 self.stream_config,
857 #[cfg(feature = "cors")]
858 self.cors_config,
859 ))
860 }
861}
862
863impl Default for LambdaMcpServerBuilder {
864 fn default() -> Self {
865 Self::new()
866 }
867}
868
869pub trait LambdaMcpServerBuilderExt {
871 fn tools<I, T>(self, tools: I) -> Self
873 where
874 I: IntoIterator<Item = T>,
875 T: McpTool + 'static;
876}
877
878impl LambdaMcpServerBuilderExt for LambdaMcpServerBuilder {
879 fn tools<I, T>(mut self, tools: I) -> Self
880 where
881 I: IntoIterator<Item = T>,
882 T: McpTool + 'static,
883 {
884 for tool in tools {
885 self = self.tool(tool);
886 }
887 self
888 }
889}
890
891pub async fn simple_lambda_server<I, T>(tools: I) -> Result<LambdaMcpServer>
896where
897 I: IntoIterator<Item = T>,
898 T: McpTool + 'static,
899{
900 let mut builder = LambdaMcpServerBuilder::new();
901
902 for tool in tools {
903 builder = builder.tool(tool);
904 }
905
906 #[cfg(feature = "cors")]
907 {
908 builder = builder.cors_allow_all_origins();
909 }
910
911 builder.sse(false).build().await
912}
913
914#[cfg(all(feature = "dynamodb", feature = "cors"))]
918pub async fn production_lambda_server<I, T>(tools: I) -> Result<LambdaMcpServer>
919where
920 I: IntoIterator<Item = T>,
921 T: McpTool + 'static,
922{
923 let mut builder = LambdaMcpServerBuilder::new();
924
925 for tool in tools {
926 builder = builder.tool(tool);
927 }
928
929 builder.production_config().await?.build().await
930}
931
932#[cfg(test)]
933mod tests {
934 use super::*;
935 use turul_mcp_session_storage::InMemorySessionStorage;
936
937 #[derive(Clone, Default)]
939 struct TestTool;
940
941 impl turul_mcp_protocol::tools::HasBaseMetadata for TestTool {
942 fn name(&self) -> &str {
943 "test_tool"
944 }
945 }
946
947 impl turul_mcp_protocol::tools::HasDescription for TestTool {
948 fn description(&self) -> Option<&str> {
949 Some("Test tool")
950 }
951 }
952
953 impl turul_mcp_protocol::tools::HasInputSchema for TestTool {
954 fn input_schema(&self) -> &turul_mcp_protocol::ToolSchema {
955 use turul_mcp_protocol::ToolSchema;
956 static SCHEMA: std::sync::OnceLock<ToolSchema> = std::sync::OnceLock::new();
957 SCHEMA.get_or_init(ToolSchema::object)
958 }
959 }
960
961 impl turul_mcp_protocol::tools::HasOutputSchema for TestTool {
962 fn output_schema(&self) -> Option<&turul_mcp_protocol::ToolSchema> {
963 None
964 }
965 }
966
967 impl turul_mcp_protocol::tools::HasAnnotations for TestTool {
968 fn annotations(&self) -> Option<&turul_mcp_protocol::tools::ToolAnnotations> {
969 None
970 }
971 }
972
973 impl turul_mcp_protocol::tools::HasToolMeta for TestTool {
974 fn tool_meta(&self) -> Option<&std::collections::HashMap<String, serde_json::Value>> {
975 None
976 }
977 }
978
979 #[async_trait::async_trait]
980 impl McpTool for TestTool {
981 async fn call(
982 &self,
983 _args: serde_json::Value,
984 _session: Option<turul_mcp_server::SessionContext>,
985 ) -> turul_mcp_server::McpResult<turul_mcp_protocol::tools::CallToolResult> {
986 use turul_mcp_protocol::tools::{CallToolResult, ToolResult};
987 Ok(CallToolResult::success(vec![ToolResult::text(
988 "test result",
989 )]))
990 }
991 }
992
993 #[tokio::test]
994 async fn test_builder_basic() {
995 let server = LambdaMcpServerBuilder::new()
996 .name("test-server")
997 .version("1.0.0")
998 .tool(TestTool)
999 .storage(Arc::new(InMemorySessionStorage::new()))
1000 .sse(false) .build()
1002 .await
1003 .unwrap();
1004
1005 let handler = server.handler().await.unwrap();
1007 assert!(
1009 handler.get_stream_manager().as_ref() as *const _ as usize > 0,
1010 "Stream manager must be initialized"
1011 );
1012 }
1013
1014 #[tokio::test]
1015 async fn test_simple_lambda_server() {
1016 let tools = vec![TestTool];
1017 let server = simple_lambda_server(tools).await.unwrap();
1018
1019 let handler = server.handler().await.unwrap();
1021 assert!(
1024 handler.get_stream_manager().as_ref() as *const _ as usize > 0,
1025 "Stream manager must be initialized"
1026 );
1027 }
1028
1029 #[tokio::test]
1030 async fn test_builder_extension_trait() {
1031 let tools = vec![TestTool, TestTool];
1032
1033 let server = LambdaMcpServerBuilder::new()
1034 .tools(tools)
1035 .storage(Arc::new(InMemorySessionStorage::new()))
1036 .sse(false) .build()
1038 .await
1039 .unwrap();
1040
1041 let handler = server.handler().await.unwrap();
1042 assert!(
1045 handler.get_stream_manager().as_ref() as *const _ as usize > 0,
1046 "Stream manager must be initialized"
1047 );
1048 }
1049
1050 #[cfg(feature = "cors")]
1051 #[tokio::test]
1052 async fn test_cors_configuration() {
1053 let server = LambdaMcpServerBuilder::new()
1054 .cors_allow_all_origins()
1055 .storage(Arc::new(InMemorySessionStorage::new()))
1056 .sse(false) .build()
1058 .await
1059 .unwrap();
1060
1061 let handler = server.handler().await.unwrap();
1062 assert!(
1065 handler.get_stream_manager().as_ref() as *const _ as usize > 0,
1066 "Stream manager must be initialized"
1067 );
1068 }
1069
1070 #[tokio::test]
1071 async fn test_sse_toggle_functionality() {
1072 let mut builder =
1074 LambdaMcpServerBuilder::new().storage(Arc::new(InMemorySessionStorage::new()));
1075
1076 builder = builder.sse(true);
1078 assert!(builder.enable_sse, "SSE should be enabled");
1079 assert!(
1080 builder.server_config.enable_get_sse,
1081 "GET SSE endpoint should be enabled"
1082 );
1083 assert!(
1084 builder.server_config.enable_post_sse,
1085 "POST SSE endpoint should be enabled"
1086 );
1087
1088 builder = builder.sse(false);
1090 assert!(!builder.enable_sse, "SSE should be disabled");
1091 assert!(
1092 !builder.server_config.enable_get_sse,
1093 "GET SSE endpoint should be disabled"
1094 );
1095 assert!(
1096 !builder.server_config.enable_post_sse,
1097 "POST SSE endpoint should be disabled"
1098 );
1099
1100 builder = builder.sse(true);
1102 assert!(builder.enable_sse, "SSE should be re-enabled");
1103 assert!(
1104 builder.server_config.enable_get_sse,
1105 "GET SSE endpoint should be re-enabled"
1106 );
1107 assert!(
1108 builder.server_config.enable_post_sse,
1109 "POST SSE endpoint should be re-enabled"
1110 );
1111
1112 let server = builder.build().await.unwrap();
1114 let handler = server.handler().await.unwrap();
1115 assert!(
1116 handler.get_stream_manager().as_ref() as *const _ as usize > 0,
1117 "Stream manager must be initialized"
1118 );
1119 }
1120}