1use std::io::{self, BufRead, Write};
27use std::sync::Arc;
28
29use serde_json::Value;
30
31use crate::protocol::{
32 JsonRpcId, JsonRpcRequest, JsonRpcResponse, McpCapabilities, McpServerInfo,
33 MCP_PROTOCOL_VERSION,
34};
35use crate::tool::{DynTool, McpTool, ToolCallResult, ToolProvider, ToolRegistry};
36
37#[derive(Debug, Clone)]
39pub enum ServerTransport {
40 None,
42 Stdio,
44 #[cfg(feature = "http-server")]
46 Http {
47 host: String,
49 port: u16,
51 },
52}
53
54impl Default for ServerTransport {
55 fn default() -> Self {
56 Self::None
57 }
58}
59
60pub struct McpServerConfig {
79 pub(crate) name: String,
80 pub(crate) version: String,
81 pub(crate) transport: ServerTransport,
82 pub(crate) registry: ToolRegistry,
83 pub(crate) capabilities: McpCapabilities,
84}
85
86impl McpServerConfig {
87 pub fn builder() -> McpServerConfigBuilder {
89 McpServerConfigBuilder::new()
90 }
91
92 pub fn name(&self) -> &str {
94 &self.name
95 }
96
97 pub fn version(&self) -> &str {
99 &self.version
100 }
101
102 pub fn registry(&self) -> &ToolRegistry {
104 &self.registry
105 }
106}
107
108#[derive(Default)]
110pub struct McpServerConfigBuilder {
111 name: String,
112 version: String,
113 transport: ServerTransport,
114 registry: ToolRegistry,
115 capabilities: McpCapabilities,
116}
117
118impl McpServerConfigBuilder {
119 pub fn new() -> Self {
121 Self {
122 name: "mcp-server".to_string(),
123 version: "0.1.0".to_string(),
124 transport: ServerTransport::default(),
125 registry: ToolRegistry::new(),
126 capabilities: McpCapabilities {
127 tools: Some(serde_json::json!({})),
128 ..Default::default()
129 },
130 }
131 }
132
133 pub fn name(mut self, name: impl Into<String>) -> Self {
135 self.name = name.into();
136 self
137 }
138
139 pub fn version(mut self, version: impl Into<String>) -> Self {
141 self.version = version.into();
142 self
143 }
144
145 pub fn with_stdio_transport(mut self) -> Self {
147 self.transport = ServerTransport::Stdio;
148 self
149 }
150
151 #[cfg(feature = "http-server")]
153 pub fn with_http_transport(mut self, host: impl Into<String>, port: u16) -> Self {
154 self.transport = ServerTransport::Http {
155 host: host.into(),
156 port,
157 };
158 self
159 }
160
161 pub fn with_tool<T: McpTool + 'static>(mut self, tool: T) -> Self {
163 self.registry.register(Arc::new(tool));
164 self
165 }
166
167 pub fn with_dyn_tool(mut self, tool: DynTool) -> Self {
169 self.registry.register(tool);
170 self
171 }
172
173 pub fn with_tools(mut self, tools: Vec<DynTool>) -> Self {
181 for tool in tools {
182 self.registry.register(tool);
183 }
184 self
185 }
186
187 pub fn with_tools_from<P: ToolProvider>(mut self, provider: P) -> Self {
189 self.registry.register_provider(provider);
190 self
191 }
192
193 pub fn register_tools(mut self) -> Self {
213 for tool in crate::tool::all_tools() {
214 self.registry.register(tool);
215 }
216 self
217 }
218
219 pub fn register_tools_in_group(mut self, group: &str) -> Self {
241 for tool in crate::tool::tools_in_group(group) {
242 self.registry.register(tool);
243 }
244 self
245 }
246
247 pub fn with_capabilities(mut self, capabilities: McpCapabilities) -> Self {
249 self.capabilities = capabilities;
250 self
251 }
252
253 pub fn with_resources(mut self) -> Self {
255 self.capabilities.resources = Some(serde_json::json!({}));
256 self
257 }
258
259 pub fn with_prompts(mut self) -> Self {
261 self.capabilities.prompts = Some(serde_json::json!({}));
262 self
263 }
264
265 pub fn build(self) -> McpServerConfig {
267 McpServerConfig {
268 name: self.name,
269 version: self.version,
270 transport: self.transport,
271 registry: self.registry,
272 capabilities: self.capabilities,
273 }
274 }
275}
276
277pub struct McpServer {
281 config: McpServerConfig,
282 registry: Arc<ToolRegistry>,
283}
284
285impl McpServer {
286 pub async fn run(config: McpServerConfig) -> Result<(), ServerError> {
302 let server = Self {
303 registry: Arc::new(config.registry),
304 config: McpServerConfig {
305 name: config.name,
306 version: config.version,
307 transport: config.transport,
308 registry: ToolRegistry::new(), capabilities: config.capabilities,
310 },
311 };
312
313 match server.config.transport.clone() {
314 ServerTransport::None => Err(ServerError::Transport(
315 "No transport configured for server".to_string(),
316 )),
317 #[cfg(feature = "stdio")]
318 ServerTransport::Stdio => server.run_stdio().await,
319 #[cfg(feature = "http-server")]
320 ServerTransport::Http { host, port } => server.run_http(&host, port).await,
321 }
322 }
323
324 pub fn name(&self) -> &str {
326 &self.config.name
327 }
328
329 pub fn version(&self) -> &str {
331 &self.config.version
332 }
333
334 pub fn server_info(&self) -> McpServerInfo {
336 McpServerInfo {
337 name: self.config.name.clone(),
338 version: self.config.version.clone(),
339 }
340 }
341
342 async fn run_stdio(&self) -> Result<(), ServerError> {
344 let stdin = io::stdin();
345 let stdout = io::stdout();
346 let reader = stdin.lock();
347 let mut writer = stdout.lock();
348
349 for line in reader.lines() {
350 let line = line.map_err(ServerError::Io)?;
351 if line.is_empty() {
352 continue;
353 }
354
355 let response = self.handle_request(&line).await;
356 let response_json =
357 serde_json::to_string(&response).map_err(ServerError::Serialization)?;
358
359 writeln!(writer, "{}", response_json).map_err(ServerError::Io)?;
360 writer.flush().map_err(ServerError::Io)?;
361 }
362
363 Ok(())
364 }
365
366 #[cfg(feature = "http-server")]
368 async fn run_http(&self, host: &str, port: u16) -> Result<(), ServerError> {
369 use actix_web::{web, App, HttpResponse, HttpServer};
370
371 let registry = self.registry.clone();
372 let name = self.config.name.clone();
373 let version = self.config.version.clone();
374 let capabilities = self.config.capabilities.clone();
375
376 HttpServer::new(move || {
377 let registry_tools = registry.clone();
378 let registry_call = registry.clone();
379 let registry_rpc = registry.clone();
380 let name_rpc = name.clone();
381 let version_rpc = version.clone();
382 let caps_rpc = capabilities.clone();
383
384 App::new()
385 .route(
386 "/tools",
387 web::get().to(move || {
388 let r = registry_tools.clone();
389 async move {
390 let tools = r.definitions();
391 HttpResponse::Ok().json(tools)
392 }
393 }),
394 )
395 .route(
396 "/call",
397 web::post().to(move |body: web::Json<CallToolRequest>| {
398 let r = registry_call.clone();
399 async move {
400 let result = r.call(&body.name, body.arguments.clone()).await;
401 match result {
402 Ok(content) => HttpResponse::Ok().json(content),
403 Err(e) => {
404 HttpResponse::InternalServerError().json(serde_json::json!({
405 "error": e.to_string()
406 }))
407 }
408 }
409 }
410 }),
411 )
412 .route(
413 "/rpc",
414 web::post().to(move |body: String| {
415 let r = registry_rpc.clone();
416 let n = name_rpc.clone();
417 let v = version_rpc.clone();
418 let c = caps_rpc.clone();
419 async move {
420 let response = handle_rpc_request_static(&body, &r, &n, &v, &c).await;
421 HttpResponse::Ok().json(response)
422 }
423 }),
424 )
425 })
426 .bind((host, port))
427 .map_err(|e| ServerError::Io(io::Error::new(io::ErrorKind::AddrInUse, e)))?
428 .run()
429 .await
430 .map_err(|e| ServerError::Io(io::Error::new(io::ErrorKind::Other, e)))
431 }
432
433 pub async fn handle_request(&self, request_str: &str) -> JsonRpcResponse {
435 let request: JsonRpcRequest = match serde_json::from_str(request_str) {
436 Ok(req) => req,
437 Err(e) => {
438 return JsonRpcResponse::error(
439 JsonRpcId::Null,
440 -32700,
441 format!("Parse error: {}", e),
442 None,
443 );
444 }
445 };
446
447 self.handle_rpc_request(request).await
448 }
449
450 pub async fn handle_rpc_request(&self, request: JsonRpcRequest) -> JsonRpcResponse {
452 match request.method.as_str() {
453 "initialize" => self.handle_initialize(request.id),
454 "tools/list" => self.handle_tools_list(request.id),
455 "tools/call" => self.handle_tools_call(request.id, request.params).await,
456 "ping" => JsonRpcResponse::success(request.id, serde_json::json!({})),
457 _ => JsonRpcResponse::error(
458 request.id,
459 -32601,
460 format!("Method not found: {}", request.method),
461 None,
462 ),
463 }
464 }
465
466 fn handle_initialize(&self, id: JsonRpcId) -> JsonRpcResponse {
467 JsonRpcResponse::success(
468 id,
469 serde_json::json!({
470 "protocolVersion": MCP_PROTOCOL_VERSION,
471 "serverInfo": self.server_info(),
472 "capabilities": self.config.capabilities
473 }),
474 )
475 }
476
477 fn handle_tools_list(&self, id: JsonRpcId) -> JsonRpcResponse {
478 let tools = self.registry.definitions();
479 JsonRpcResponse::success(id, serde_json::json!({ "tools": tools }))
480 }
481
482 async fn handle_tools_call(&self, id: JsonRpcId, params: Option<Value>) -> JsonRpcResponse {
483 let params = match params {
484 Some(p) => p,
485 None => {
486 return JsonRpcResponse::error(id, -32602, "Missing params".to_string(), None);
487 }
488 };
489
490 let name = match params.get("name").and_then(|n| n.as_str()) {
491 Some(n) => n,
492 None => {
493 return JsonRpcResponse::error(id, -32602, "Missing tool name".to_string(), None);
494 }
495 };
496
497 let arguments = params
498 .get("arguments")
499 .cloned()
500 .unwrap_or(serde_json::json!({}));
501
502 let result = self.registry.call(name, arguments).await;
503
504 match result {
505 Ok(content) => JsonRpcResponse::success(
506 id,
507 serde_json::json!({
508 "content": content,
509 "isError": false
510 }),
511 ),
512 Err(e) => JsonRpcResponse::success(
513 id,
514 serde_json::json!({
515 "content": [{ "type": "text", "text": e.to_string() }],
516 "isError": true
517 }),
518 ),
519 }
520 }
521
522 pub async fn call_tool(&self, name: &str, args: Value) -> ToolCallResult {
524 self.registry.call(name, args).await
525 }
526}
527
528#[cfg(feature = "http-server")]
530async fn handle_rpc_request_static(
531 request_str: &str,
532 registry: &ToolRegistry,
533 name: &str,
534 version: &str,
535 capabilities: &McpCapabilities,
536) -> JsonRpcResponse {
537 let request: JsonRpcRequest = match serde_json::from_str(request_str) {
538 Ok(req) => req,
539 Err(e) => {
540 return JsonRpcResponse::error(
541 JsonRpcId::Null,
542 -32700,
543 format!("Parse error: {}", e),
544 None,
545 );
546 }
547 };
548
549 match request.method.as_str() {
550 "initialize" => JsonRpcResponse::success(
551 request.id,
552 serde_json::json!({
553 "protocolVersion": MCP_PROTOCOL_VERSION,
554 "serverInfo": {
555 "name": name,
556 "version": version
557 },
558 "capabilities": capabilities
559 }),
560 ),
561 "tools/list" => {
562 let tools = registry.definitions();
563 JsonRpcResponse::success(request.id, serde_json::json!({ "tools": tools }))
564 }
565 "tools/call" => {
566 let params = match request.params {
567 Some(p) => p,
568 None => {
569 return JsonRpcResponse::error(
570 request.id,
571 -32602,
572 "Missing params".to_string(),
573 None,
574 );
575 }
576 };
577
578 let tool_name = match params.get("name").and_then(|n| n.as_str()) {
579 Some(n) => n,
580 None => {
581 return JsonRpcResponse::error(
582 request.id,
583 -32602,
584 "Missing tool name".to_string(),
585 None,
586 );
587 }
588 };
589
590 let arguments = params
591 .get("arguments")
592 .cloned()
593 .unwrap_or(serde_json::json!({}));
594 let result = registry.call(tool_name, arguments).await;
595
596 match result {
597 Ok(content) => JsonRpcResponse::success(
598 request.id,
599 serde_json::json!({
600 "content": content,
601 "isError": false
602 }),
603 ),
604 Err(e) => JsonRpcResponse::success(
605 request.id,
606 serde_json::json!({
607 "content": [{ "type": "text", "text": e.to_string() }],
608 "isError": true
609 }),
610 ),
611 }
612 }
613 "ping" => JsonRpcResponse::success(request.id, serde_json::json!({})),
614 _ => JsonRpcResponse::error(
615 request.id,
616 -32601,
617 format!("Method not found: {}", request.method),
618 None,
619 ),
620 }
621}
622
623#[cfg(feature = "http-server")]
625#[derive(serde::Deserialize)]
626struct CallToolRequest {
627 name: String,
628 arguments: Value,
629}
630
631#[derive(Debug)]
633pub enum ServerError {
634 Io(io::Error),
636 Serialization(serde_json::Error),
638 Transport(String),
640}
641
642impl std::fmt::Display for ServerError {
643 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
644 match self {
645 ServerError::Io(e) => write!(f, "I/O error: {}", e),
646 ServerError::Serialization(e) => write!(f, "Serialization error: {}", e),
647 ServerError::Transport(e) => write!(f, "Transport error: {}", e),
648 }
649 }
650}
651
652impl std::error::Error for ServerError {
653 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
654 match self {
655 ServerError::Io(e) => Some(e),
656 ServerError::Serialization(e) => Some(e),
657 ServerError::Transport(_) => None,
658 }
659 }
660}
661
662#[macro_export]
682macro_rules! tools {
683 () => {
684 Vec::new()
685 };
686 ($($tool:expr),+ $(,)?) => {
687 vec![
688 $(std::sync::Arc::new($tool) as $crate::DynTool),+
689 ]
690 };
691}
692
693#[cfg(test)]
694mod tests {
695 use super::*;
696 use crate::protocol::{McpToolDef, ToolContent};
697 use crate::tool::BoxFuture;
698
699 struct EchoTool;
700
701 impl McpTool for EchoTool {
702 fn definition(&self) -> McpToolDef {
703 McpToolDef {
704 name: "echo".to_string(),
705 description: Some("Echo the input".to_string()),
706 group: None,
707 input_schema: serde_json::json!({
708 "type": "object",
709 "properties": {
710 "message": { "type": "string" }
711 }
712 }),
713 }
714 }
715
716 fn call<'a>(&'a self, args: Value) -> BoxFuture<'a, ToolCallResult> {
717 Box::pin(async move {
718 let message = args
719 .get("message")
720 .and_then(|m| m.as_str())
721 .unwrap_or("no message");
722 Ok(vec![ToolContent::text(message)])
723 })
724 }
725 }
726
727 #[test]
728 fn test_config_builder() {
729 let config = McpServerConfig::builder()
730 .name("test-server")
731 .version("1.0.0")
732 .with_stdio_transport()
733 .with_tool(EchoTool)
734 .build();
735
736 assert_eq!(config.name(), "test-server");
737 assert_eq!(config.version(), "1.0.0");
738 assert_eq!(config.registry.len(), 1);
739 }
740
741 #[test]
742 fn test_tools_macro() {
743 let tools = tools![EchoTool];
744 assert_eq!(tools.len(), 1);
745 }
746
747 #[test]
748 fn test_config_with_tools() {
749 let config = McpServerConfig::builder()
750 .name("test-server")
751 .version("1.0.0")
752 .with_tools(tools![EchoTool])
753 .build();
754
755 assert_eq!(config.registry.len(), 1);
756 }
757}