1use std::collections::BTreeMap;
4use std::convert::Infallible;
5use std::future::Future;
6use std::pin::Pin;
7use std::sync::Arc;
8use std::task::{Context, Poll};
9use std::time::Duration;
10
11use futures_util::FutureExt as _;
12use serde::Deserialize;
13use serde_json::value::RawValue;
14use serde_json::{Map, Value};
15use tokio::sync::Semaphore;
16use tokio_util::sync::CancellationToken;
17
18use crate::auth::AuthContext;
19use crate::completion::{Completer, CompletionRequest};
20use crate::context::{Ctx, RequestMeta, TransportContext, TypeMap};
21use crate::error::McpError;
22use crate::extension::{self, Extension};
23use crate::hints::{self, Once};
24use crate::jsonrpc::{self, Message, Rejection, Request, Response};
25use crate::legacy::{LegacyConfig, LegacySession};
26use crate::limits::Limits;
27use crate::mirror::{self, MirroredParam};
28use crate::mrtr::{Args, MIN_REQUEST_STATE_KEY_BYTES, Round, RoundKey, StateCodec};
29use crate::notify::{Notifier, NotifyChannel};
30use crate::prompt::{GetFn, Prompt, PromptArguments, PromptDef};
31use crate::resource::{ReadFn, Resource, ResourceDef, ResourceKind};
32use crate::spec::{self, CacheScope, RequestId, methods};
33use crate::tool::{CallFn, StateRequirement, Tool, ToolDef, ToolError, ToolOutput};
34use crate::uri_template::{self, UriTemplate, UriVars};
35
36const COMPLETE: &str = "complete";
38
39#[derive(thiserror::Error)]
45#[non_exhaustive]
46pub enum BuildError {
47 #[error("duplicate tool name `{0}`")]
49 DuplicateTool(String),
50 #[error("invalid tool name `{0}`: use 1-128 characters from A-Z, a-z, 0-9, `_`, `-` and `.`")]
52 InvalidToolName(String),
53 #[error("`{handler}` needs shared state of type `{type_name}`, which is not registered")]
55 MissingState {
56 handler: String,
58 type_name: &'static str,
60 },
61 #[error("invalid resource `{uri}`: {reason}")]
63 InvalidResource {
64 uri: String,
66 reason: String,
68 },
69 #[error("duplicate resource `{0}`")]
71 DuplicateResource(String),
72 #[error("duplicate prompt name `{0}`")]
74 DuplicatePrompt(String),
75 #[error("invalid prompt name `{0}`")]
77 InvalidPromptName(String),
78 #[error("`{owner}` has a completion provider for unknown argument `{argument}`")]
80 InvalidCompletion {
81 owner: String,
83 argument: String,
85 },
86 #[error("tool `{tool}` has an invalid `x-mcp-header` annotation: {reason}")]
88 InvalidHeaderAnnotation {
89 tool: String,
91 reason: String,
93 },
94 #[error("invalid or duplicate extension identifier `{0}`")]
96 InvalidExtension(String),
97 #[error("invalid limit: {0}")]
99 InvalidLimit(&'static str),
100 #[error("the requestState key must be at least {MIN_REQUEST_STATE_KEY_BYTES} bytes")]
103 WeakRequestStateKey,
104 #[error("could not generate a random requestState key: {0}")]
106 RandomUnavailable(String),
107 #[error("invalid MCP Apps interface `{uri}`: {reason}")]
109 InvalidApp {
110 uri: String,
112 reason: String,
114 },
115 #[error("`{handler}` declares an invalid scope `{scope}`")]
117 InvalidScope {
118 handler: String,
120 scope: String,
122 },
123 #[error("could not serialize {0}")]
125 Serialize(&'static str, #[source] serde_json::Error),
126}
127
128impl BuildError {
129 #[must_use]
131 pub fn help(&self) -> String {
132 match self {
133 Self::DuplicateTool(name) => format!(
134 "rename one of the tools with #[tool(name = \"...\")], or check that `{name}` \
135 is not registered twice (for example with `.tool::<..>()` and `.auto_tools()`)"
136 ),
137 Self::InvalidToolName(_) => "tool names travel in HTTP headers; rename the tool with \
138 #[tool(name = \"...\")] using only those characters"
139 .to_owned(),
140 Self::MissingState { type_name, .. } => format!(
141 "register a value before `.build()`: `Server::builder(..).state(value)`, \
142 where `value: {type_name}`"
143 ),
144 Self::InvalidResource { .. } => "fixed URIs need a scheme, such as `config://app` or \
145 `file:///notes.txt`; templates support `{var}`, `{+var}`, `{/var}` and a final \
146 `{?query}` (see the `UriTemplate` docs)"
147 .to_owned(),
148 Self::DuplicateResource(_) => {
149 "each URI and each URI template can be registered only once".to_owned()
150 }
151 Self::DuplicatePrompt(_) => {
152 "rename one of the prompts with #[prompt(name = \"...\")]".to_owned()
153 }
154 Self::InvalidPromptName(_) => {
155 "use a non-empty name without control characters".to_owned()
156 }
157 Self::InvalidCompletion { owner, .. } => format!(
158 "#[complete(..)] and `.complete(..)` must name an argument (prompts) or a \
159 template variable (resources) of `{owner}`"
160 ),
161 Self::InvalidHeaderAnnotation { .. } => "`x-mcp-header` takes an HTTP header name \
162 (letters, digits and `-`) on a string, integer or boolean property reached \
163 only through `properties`"
164 .to_owned(),
165 Self::InvalidExtension(_) => "extension identifiers look like \
166 `com.example/my-extension` and can be registered only once"
167 .to_owned(),
168 Self::InvalidLimit(_) => "limits must be positive; see `max_message_bytes`, \
169 `max_json_depth`, `tool_timeout` and `max_concurrent_calls` on the builder"
170 .to_owned(),
171 Self::WeakRequestStateKey => format!(
172 "use at least {MIN_REQUEST_STATE_KEY_BYTES} random bytes from your secret store, \
173 and the same key on every instance"
174 ),
175 Self::RandomUnavailable(_) => "the operating system did not provide randomness; set \
176 a key explicitly with `.request_state_key(..)`"
177 .to_owned(),
178 Self::InvalidApp { .. } => "MCP Apps interfaces are fixed `ui://` resources marked \
179 with #[resource(\"ui://..\", app)] (or `ResourceDef::app`), and \
180 #[tool(ui = \"ui://..\")] must name one of them; CSP origins look like \
181 `https://api.example.com`"
182 .to_owned(),
183 Self::InvalidScope { .. } => "scopes are non-empty and use visible ASCII except `\"` \
184 and `\\`, without spaces (for example `files:read`)"
185 .to_owned(),
186 Self::Serialize(..) => "a definition built by hand contains a value JSON cannot \
187 represent (such as a NaN number); if it came from a macro, please report a bug"
188 .to_owned(),
189 }
190 }
191}
192
193impl std::fmt::Debug for BuildError {
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 write!(f, "{self}\n = help: {}", self.help())
196 }
197}
198
199#[must_use = "call `.build()` to create the server"]
201pub struct ServerBuilder {
202 info: spec::Implementation,
203 instructions: Option<String>,
204 tools: Vec<ToolDef>,
205 resources: Vec<ResourceDef>,
206 prompts: Vec<PromptDef>,
207 state: TypeMap,
208 extensions: Vec<Box<dyn Extension>>,
209 limits: Limits,
210 list_ttl: Duration,
211 discover_ttl: Duration,
212 cache_scope: CacheScope,
213 include_server_info: bool,
214 request_state_key: Option<Vec<u8>>,
215 request_state_ttl: Duration,
216 legacy_clients: bool,
217 legacy_session_ttl: Duration,
218}
219
220impl std::fmt::Debug for ServerBuilder {
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 f.debug_struct("ServerBuilder")
223 .field("info", &self.info)
224 .field("tools", &self.tools.len())
225 .field("resources", &self.resources.len())
226 .field("prompts", &self.prompts.len())
227 .field("limits", &self.limits)
228 .finish_non_exhaustive()
229 }
230}
231
232impl ServerBuilder {
233 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
235 Self {
236 info: spec::Implementation::new(name.into(), version.into()),
237 instructions: None,
238 tools: Vec::new(),
239 resources: Vec::new(),
240 prompts: Vec::new(),
241 state: TypeMap::default(),
242 extensions: Vec::new(),
243 limits: Limits::default(),
244 list_ttl: Duration::from_secs(300),
245 discover_ttl: Duration::from_secs(3600),
246 cache_scope: CacheScope::Public,
247 include_server_info: true,
248 request_state_key: None,
249 request_state_ttl: Duration::from_secs(600),
250 legacy_clients: true,
251 legacy_session_ttl: Duration::from_secs(24 * 3600),
252 }
253 }
254
255 pub fn title(mut self, title: impl Into<String>) -> Self {
257 self.info.title = Some(title.into());
258 self
259 }
260
261 pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
263 self.instructions = Some(instructions.into());
264 self
265 }
266
267 pub fn tool<T: Tool>(mut self) -> Self {
269 self.tools.push(T::definition());
270 self
271 }
272
273 #[cfg(feature = "auto-register")]
277 pub fn auto_tools(mut self) -> Self {
278 for tool in inventory::iter::<crate::auto::AutoTool> {
279 self.tools.push(tool.definition());
280 }
281 self
282 }
283
284 pub fn tool_def(mut self, def: ToolDef) -> Self {
286 self.tools.push(def);
287 self
288 }
289
290 pub fn resource<T: Resource>(mut self) -> Self {
293 self.resources.push(T::definition());
294 self
295 }
296
297 #[cfg(feature = "auto-register")]
300 pub fn auto_resources(mut self) -> Self {
301 for resource in inventory::iter::<crate::auto::AutoResource> {
302 self.resources.push(resource.definition());
303 }
304 self
305 }
306
307 pub fn resource_def(mut self, def: ResourceDef) -> Self {
309 self.resources.push(def);
310 self
311 }
312
313 pub fn prompt<T: Prompt>(mut self) -> Self {
316 self.prompts.push(T::definition());
317 self
318 }
319
320 #[cfg(feature = "auto-register")]
323 pub fn auto_prompts(mut self) -> Self {
324 for prompt in inventory::iter::<crate::auto::AutoPrompt> {
325 self.prompts.push(prompt.definition());
326 }
327 self
328 }
329
330 pub fn prompt_def(mut self, def: PromptDef) -> Self {
332 self.prompts.push(def);
333 self
334 }
335
336 pub fn state<T: Send + Sync + 'static>(mut self, value: T) -> Self {
338 self.state.insert(Arc::new(value));
339 self
340 }
341
342 pub fn extension(mut self, extension: impl Extension) -> Self {
344 self.extensions.push(Box::new(extension));
345 self
346 }
347
348 pub fn limits(mut self, limits: Limits) -> Self {
350 self.limits = limits;
351 self
352 }
353
354 pub fn max_message_bytes(mut self, bytes: usize) -> Self {
356 self.limits.max_message_bytes = bytes;
357 self
358 }
359
360 pub fn max_json_depth(mut self, depth: usize) -> Self {
362 self.limits.max_json_depth = depth;
363 self
364 }
365
366 pub fn tool_timeout(mut self, timeout: Duration) -> Self {
369 self.limits.tool_timeout = timeout;
370 self
371 }
372
373 pub fn max_concurrent_calls(mut self, calls: usize) -> Self {
376 self.limits.max_concurrent_calls = calls;
377 self
378 }
379
380 pub fn list_ttl(mut self, ttl: Duration) -> Self {
383 self.list_ttl = ttl;
384 self
385 }
386
387 pub fn discover_ttl(mut self, ttl: Duration) -> Self {
389 self.discover_ttl = ttl;
390 self
391 }
392
393 pub fn cache_scope(mut self, scope: CacheScope) -> Self {
396 self.cache_scope = scope;
397 self
398 }
399
400 pub fn request_state_key(mut self, key: impl Into<Vec<u8>>) -> Self {
406 self.request_state_key = Some(key.into());
407 self
408 }
409
410 pub fn request_state_ttl(mut self, ttl: Duration) -> Self {
413 self.request_state_ttl = ttl;
414 self
415 }
416
417 pub fn legacy_clients(mut self, enabled: bool) -> Self {
422 self.legacy_clients = enabled;
423 self
424 }
425
426 pub fn legacy_session_ttl(mut self, ttl: Duration) -> Self {
429 self.legacy_session_ttl = ttl;
430 self
431 }
432
433 pub fn include_server_info(mut self, include: bool) -> Self {
436 self.include_server_info = include;
437 self
438 }
439
440 pub fn build(self) -> Result<Server, BuildError> {
449 if self.limits.max_message_bytes == 0 {
450 return Err(BuildError::InvalidLimit(
451 "max_message_bytes must be positive",
452 ));
453 }
454 if self.limits.max_json_depth == 0 {
455 return Err(BuildError::InvalidLimit("max_json_depth must be positive"));
456 }
457 if self.limits.max_concurrent_calls == 0 {
458 return Err(BuildError::InvalidLimit(
459 "max_concurrent_calls must be positive",
460 ));
461 }
462 if self.limits.tool_timeout.is_zero() {
463 return Err(BuildError::InvalidLimit("tool_timeout must be positive"));
464 }
465
466 let mut tools = self.tools;
467 tools.sort_by(|a, b| a.tool.name.cmp(&b.tool.name));
469 for pair in tools.windows(2) {
470 if let [a, b] = pair
471 && a.tool.name == b.tool.name
472 {
473 return Err(BuildError::DuplicateTool(a.tool.name.clone()));
474 }
475 }
476 for def in &mut tools {
477 if def.ui.is_some() || def.visibility.is_some() {
478 if def.visibility.as_ref().is_some_and(Vec::is_empty) {
479 return Err(BuildError::InvalidApp {
480 uri: def.tool.name.clone(),
481 reason: "`visibility` must name `model`, `app` or both".to_owned(),
482 });
483 }
484 crate::apps::tool_meta(
485 &mut def.tool.meta,
486 def.ui.as_deref(),
487 def.visibility.as_deref(),
488 );
489 }
490 }
491 let mut mirrored = Vec::with_capacity(tools.len());
492 for def in &tools {
493 if !is_valid_tool_name(&def.tool.name) {
494 return Err(BuildError::InvalidToolName(def.tool.name.clone()));
495 }
496 check_state(&self.state, &def.required_state, &def.tool.name)?;
497 check_scopes(&def.scopes, &def.tool.name)?;
498 let params = mirror::collect(&def.tool.input_schema.extra).map_err(|reason| {
499 BuildError::InvalidHeaderAnnotation {
500 tool: def.tool.name.clone(),
501 reason,
502 }
503 })?;
504 mirrored.push(params);
505 }
506
507 let state_key = match self.request_state_key {
508 Some(key) if key.len() < MIN_REQUEST_STATE_KEY_BYTES => {
509 return Err(BuildError::WeakRequestStateKey);
510 }
511 Some(key) => key,
512 None => {
513 let mut key = vec![0; MIN_REQUEST_STATE_KEY_BYTES];
514 getrandom::fill(&mut key)
515 .map_err(|e| BuildError::RandomUnavailable(e.to_string()))?;
516 key
517 }
518 };
519 let state_codec = StateCodec::new(state_key, self.request_state_ttl);
520
521 let resources = build_resources(self.resources, &self.state)?;
522 let prompts = build_prompts(self.prompts, &self.state)?;
523
524 for def in &tools {
526 if let Some(uri) = &def.ui {
527 let found = resources
528 .statics
529 .iter()
530 .any(|r| r.uri == *uri && r.app.is_some());
531 if !found {
532 return Err(BuildError::InvalidApp {
533 uri: uri.clone(),
534 reason: format!(
535 "tool `{}` shows it, but no resource with this URI is marked as an \
536 app",
537 def.tool.name
538 ),
539 });
540 }
541 }
542 }
543 let uses_apps = tools
544 .iter()
545 .any(|t| t.ui.is_some() || t.visibility.is_some())
546 || resources.statics.iter().any(|r| r.app.is_some());
547
548 let mut extensions = BTreeMap::new();
549 for ext in &self.extensions {
550 let id = ext.id();
551 if !extension::is_valid_id(id) || extensions.contains_key(id) {
552 return Err(BuildError::InvalidExtension(id.to_owned()));
553 }
554 extensions.insert(id.to_owned(), ext.settings());
555 }
556 if uses_apps {
557 extensions
559 .entry(crate::apps::EXTENSION_ID.to_owned())
560 .or_insert_with(Map::new);
561 }
562
563 let result_meta = self.include_server_info.then(|| spec::ResultMetaObject {
564 server_info: Some(self.info.clone()),
565 extra: Map::new(),
566 });
567
568 let has_completions = resources.templates.iter().any(|t| !t.completers.is_empty())
569 || prompts.registry.iter().any(|p| !p.completers.is_empty());
570 let has_resources = !resources.statics.is_empty() || !resources.templates.is_empty();
571 let has_prompts = !prompts.registry.is_empty();
572 #[allow(deprecated)] let capabilities = spec::ServerCapabilities {
574 tools: Some(spec::ServerCapabilitiesTools { list_changed: None }),
575 resources: has_resources.then_some(spec::ServerCapabilitiesResources {
576 list_changed: None,
577 subscribe: None,
578 }),
579 prompts: has_prompts.then_some(spec::ServerCapabilitiesPrompts { list_changed: None }),
580 completions: has_completions.then(Map::new),
581 logging: Some(Map::new()),
583 extensions: (!extensions.is_empty()).then_some(extensions),
584 ..spec::ServerCapabilities::default()
585 };
586
587 let supported_versions = vec![spec::PROTOCOL_VERSION.to_owned()];
588
589 let legacy = LegacyConfig::new(
590 self.legacy_clients,
591 self.legacy_session_ttl,
592 &capabilities,
593 &self.info,
594 self.instructions.as_deref(),
595 );
596
597 let discover = spec::DiscoverResult {
598 meta: result_meta.clone(),
599 cache_scope: self.cache_scope,
600 capabilities,
601 instructions: self.instructions,
602 result_type: COMPLETE.to_owned(),
603 supported_versions: supported_versions.clone(),
604 ttl_ms: millis(self.discover_ttl),
605 };
606 let discover =
607 to_raw(&discover).map_err(|e| BuildError::Serialize("server/discover", e))?;
608
609 let list = spec::ListToolsResult {
610 meta: result_meta.clone(),
611 cache_scope: self.cache_scope,
612 next_cursor: None,
613 result_type: COMPLETE.to_owned(),
614 tools: tools.iter().map(|t| t.tool.clone()).collect(),
615 ttl_ms: millis(self.list_ttl),
616 };
617 let tools_list = to_raw(&list).map_err(|e| BuildError::Serialize("tools/list", e))?;
618 let list_ttl_ms = millis(self.list_ttl);
619
620 let resources_list = if has_resources {
621 Some(
622 to_raw(&spec::ListResourcesResult {
623 meta: result_meta.clone(),
624 cache_scope: self.cache_scope,
625 next_cursor: None,
626 resources: resources.listed,
627 result_type: COMPLETE.to_owned(),
628 ttl_ms: list_ttl_ms,
629 })
630 .map_err(|e| BuildError::Serialize("resources/list", e))?,
631 )
632 } else {
633 None
634 };
635 let templates_list = if has_resources {
636 Some(
637 to_raw(&spec::ListResourceTemplatesResult {
638 meta: result_meta.clone(),
639 cache_scope: self.cache_scope,
640 next_cursor: None,
641 resource_templates: resources.listed_templates,
642 result_type: COMPLETE.to_owned(),
643 ttl_ms: list_ttl_ms,
644 })
645 .map_err(|e| BuildError::Serialize("resources/templates/list", e))?,
646 )
647 } else {
648 None
649 };
650 let prompts_list = if has_prompts {
651 Some(
652 to_raw(&spec::ListPromptsResult {
653 meta: result_meta.clone(),
654 cache_scope: self.cache_scope,
655 next_cursor: None,
656 prompts: prompts.listed,
657 result_type: COMPLETE.to_owned(),
658 ttl_ms: list_ttl_ms,
659 })
660 .map_err(|e| BuildError::Serialize("prompts/list", e))?,
661 )
662 } else {
663 None
664 };
665
666 let registry = tools
667 .into_iter()
668 .zip(mirrored)
669 .map(|(t, mirrored)| RegisteredTool {
670 name: t.tool.name,
671 call: t.call,
672 mirrored,
673 scopes: t.scopes.into(),
674 })
675 .collect::<Box<[RegisteredTool]>>();
676 let has_scopes = registry.iter().any(|t| !t.scopes.is_empty())
677 || resources.statics.iter().any(|r| !r.scopes.is_empty())
678 || resources.templates.iter().any(|t| !t.scopes.is_empty())
679 || prompts.registry.iter().any(|p| !p.scopes.is_empty());
680
681 Ok(Server {
682 inner: Arc::new(Inner {
683 tools: registry,
684 has_scopes,
685 resources: resources.statics,
686 templates: resources.templates,
687 prompts: prompts.registry,
688 has_completions,
689 state_codec,
690 legacy,
691 legacy_hint: Once::new(),
692 state_hint: Once::new(),
693 discover,
694 tools_list,
695 resources_list,
696 templates_list,
697 prompts_list,
698 result_meta,
699 supported_versions,
700 state: Arc::new(self.state),
701 permits: Arc::new(Semaphore::new(self.limits.max_concurrent_calls)),
702 limits: self.limits,
703 }),
704 })
705 }
706}
707
708#[derive(Clone)]
713pub struct Server {
714 inner: Arc<Inner>,
715}
716
717struct Inner {
718 tools: Box<[RegisteredTool]>,
720 resources: Box<[RegisteredResource]>,
722 templates: Box<[RegisteredTemplate]>,
724 prompts: Box<[RegisteredPrompt]>,
726 has_completions: bool,
727 has_scopes: bool,
729 state_codec: StateCodec,
730 legacy: LegacyConfig,
731 legacy_hint: Once,
732 state_hint: Once,
733 discover: Box<RawValue>,
734 tools_list: Box<RawValue>,
735 resources_list: Option<Box<RawValue>>,
736 templates_list: Option<Box<RawValue>>,
737 prompts_list: Option<Box<RawValue>>,
738 result_meta: Option<spec::ResultMetaObject>,
739 supported_versions: Vec<String>,
740 state: Arc<TypeMap>,
741 permits: Arc<Semaphore>,
742 limits: Limits,
743}
744
745struct RegisteredTool {
746 name: String,
747 call: CallFn,
748 mirrored: Vec<MirroredParam>,
749 scopes: Box<[String]>,
750}
751
752struct RegisteredResource {
753 uri: String,
754 read: ReadFn,
755 ttl_ms: u64,
756 cache_scope: CacheScope,
757 scopes: Box<[String]>,
758 app: Option<Value>,
760}
761
762struct RegisteredTemplate {
763 template: UriTemplate,
764 read: ReadFn,
765 ttl_ms: u64,
766 cache_scope: CacheScope,
767 completers: Vec<Completer>,
768 scopes: Box<[String]>,
769}
770
771struct RegisteredPrompt {
772 name: String,
773 description: Option<String>,
774 get: GetFn,
775 completers: Vec<Completer>,
776 scopes: Box<[String]>,
777}
778
779struct BuiltResources {
780 statics: Box<[RegisteredResource]>,
781 templates: Box<[RegisteredTemplate]>,
782 listed: Vec<spec::Resource>,
783 listed_templates: Vec<spec::ResourceTemplate>,
784}
785
786struct BuiltPrompts {
787 registry: Box<[RegisteredPrompt]>,
788 listed: Vec<spec::Prompt>,
789}
790
791fn check_state(
792 state: &TypeMap,
793 required: &[StateRequirement],
794 handler: &str,
795) -> Result<(), BuildError> {
796 match required.iter().find(|r| !state.contains(r.type_id)) {
797 Some(missing) => Err(BuildError::MissingState {
798 handler: handler.to_owned(),
799 type_name: missing.type_name,
800 }),
801 None => Ok(()),
802 }
803}
804
805fn check_scopes(scopes: &[String], handler: &str) -> Result<(), BuildError> {
806 match scopes.iter().find(|s| !crate::auth::is_valid_scope(s)) {
807 Some(scope) => Err(BuildError::InvalidScope {
808 handler: handler.to_owned(),
809 scope: scope.clone(),
810 }),
811 None => Ok(()),
812 }
813}
814
815fn check_completers(
816 completers: &[Completer],
817 owner: &str,
818 arguments: &[&str],
819) -> Result<(), BuildError> {
820 match completers
821 .iter()
822 .find(|c| !arguments.contains(&c.argument.as_str()))
823 {
824 Some(unknown) => Err(BuildError::InvalidCompletion {
825 owner: owner.to_owned(),
826 argument: unknown.argument.clone(),
827 }),
828 None => Ok(()),
829 }
830}
831
832fn build_resources(defs: Vec<ResourceDef>, state: &TypeMap) -> Result<BuiltResources, BuildError> {
833 let mut statics = Vec::new();
834 let mut templates = Vec::new();
835 let mut listed = Vec::new();
836 let mut listed_templates = Vec::new();
837 for def in defs {
838 check_state(state, &def.required_state, def.uri())?;
839 check_scopes(&def.scopes, def.uri())?;
840 let ttl_ms = millis(def.ttl);
841 match def.kind {
842 ResourceKind::Static(resource) => {
843 if !uri_template::is_absolute_uri(&resource.uri) {
844 return Err(BuildError::InvalidResource {
845 uri: resource.uri,
846 reason: "expected an absolute URI such as `scheme://path`".to_owned(),
847 });
848 }
849 check_completers(&def.completers, &resource.uri, &[])?;
850 let app = match &def.app {
851 None => None,
852 Some(ui) => {
853 let invalid = |reason: String| BuildError::InvalidApp {
854 uri: resource.uri.clone(),
855 reason,
856 };
857 if !resource.uri.starts_with(crate::apps::URI_SCHEME) {
858 return Err(invalid("the URI must use the `ui://` scheme".to_owned()));
859 }
860 ui.validate().map_err(invalid)?;
861 Some(ui.to_meta())
862 }
863 };
864 statics.push(RegisteredResource {
865 uri: resource.uri.clone(),
866 read: def.read,
867 ttl_ms,
868 cache_scope: def.cache_scope,
869 scopes: def.scopes.into(),
870 app,
871 });
872 listed.push(resource);
873 }
874 ResourceKind::Template(template) => {
875 if def.app.is_some() {
876 return Err(BuildError::InvalidApp {
877 uri: template.uri_template,
878 reason: "an interface is a fixed `ui://` resource, not a template"
879 .to_owned(),
880 });
881 }
882 let parsed = UriTemplate::parse(&template.uri_template).map_err(|e| {
883 BuildError::InvalidResource {
884 uri: template.uri_template.clone(),
885 reason: e.to_string(),
886 }
887 })?;
888 let variables: Vec<&str> = parsed.variables().collect();
889 check_completers(&def.completers, &template.uri_template, &variables)?;
890 templates.push((
891 template.name.clone(),
892 RegisteredTemplate {
893 template: parsed,
894 read: def.read,
895 ttl_ms,
896 cache_scope: def.cache_scope,
897 completers: def.completers,
898 scopes: def.scopes.into(),
899 },
900 ));
901 listed_templates.push(template);
902 }
903 }
904 }
905 statics.sort_by(|a, b| a.uri.cmp(&b.uri));
906 if let Some(pair) = statics
907 .windows(2)
908 .find(|w| matches!(w, [a, b] if a.uri == b.uri))
909 && let Some(first) = pair.first()
910 {
911 return Err(BuildError::DuplicateResource(first.uri.clone()));
912 }
913 listed.sort_by(|a, b| a.uri.cmp(&b.uri));
914 templates.sort_by(|(na, a), (nb, b)| {
915 na.cmp(nb)
916 .then_with(|| a.template.as_str().cmp(b.template.as_str()))
917 });
918 for (i, (_, t)) in templates.iter().enumerate() {
919 if templates
920 .iter()
921 .skip(i + 1)
922 .any(|(_, other)| other.template.as_str() == t.template.as_str())
923 {
924 return Err(BuildError::DuplicateResource(
925 t.template.as_str().to_owned(),
926 ));
927 }
928 }
929 listed_templates.sort_by(|a, b| {
930 a.name
931 .cmp(&b.name)
932 .then_with(|| a.uri_template.cmp(&b.uri_template))
933 });
934 Ok(BuiltResources {
935 statics: statics.into(),
936 templates: templates.into_iter().map(|(_, t)| t).collect(),
937 listed,
938 listed_templates,
939 })
940}
941
942fn build_prompts(mut defs: Vec<PromptDef>, state: &TypeMap) -> Result<BuiltPrompts, BuildError> {
943 defs.sort_by(|a, b| a.prompt.name.cmp(&b.prompt.name));
944 if let Some(pair) = defs
945 .windows(2)
946 .find(|w| matches!(w, [a, b] if a.prompt.name == b.prompt.name))
947 && let Some(first) = pair.first()
948 {
949 return Err(BuildError::DuplicatePrompt(first.prompt.name.clone()));
950 }
951 let mut registry = Vec::with_capacity(defs.len());
952 let mut listed = Vec::with_capacity(defs.len());
953 for def in defs {
954 let name = &def.prompt.name;
955 if name.is_empty() || name.chars().any(char::is_control) {
956 return Err(BuildError::InvalidPromptName(name.clone()));
957 }
958 check_state(state, &def.required_state, name)?;
959 check_scopes(&def.scopes, name)?;
960 let arguments: Vec<&str> = def
961 .prompt
962 .arguments
963 .iter()
964 .flatten()
965 .map(|a| a.name.as_str())
966 .collect();
967 check_completers(&def.completers, name, &arguments)?;
968 registry.push(RegisteredPrompt {
969 name: name.clone(),
970 description: def.prompt.description.clone(),
971 get: def.get,
972 completers: def.completers,
973 scopes: def.scopes.into(),
974 });
975 listed.push(def.prompt);
976 }
977 Ok(BuiltPrompts {
978 registry: registry.into(),
979 listed,
980 })
981}
982
983impl std::fmt::Debug for Server {
984 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
985 f.debug_struct("Server")
986 .field(
987 "tools",
988 &self.inner.tools.iter().map(|t| &t.name).collect::<Vec<_>>(),
989 )
990 .field(
991 "resources",
992 &self
993 .inner
994 .resources
995 .iter()
996 .map(|r| &r.uri)
997 .collect::<Vec<_>>(),
998 )
999 .field(
1000 "prompts",
1001 &self
1002 .inner
1003 .prompts
1004 .iter()
1005 .map(|p| &p.name)
1006 .collect::<Vec<_>>(),
1007 )
1008 .field("limits", &self.inner.limits)
1009 .finish_non_exhaustive()
1010 }
1011}
1012
1013#[derive(Debug)]
1015pub struct McpRequest {
1016 pub message: Message,
1018 pub transport: TransportContext,
1020 pub cancellation: CancellationToken,
1022}
1023
1024impl McpRequest {
1025 #[must_use]
1027 pub fn new(message: Message) -> Self {
1028 Self {
1029 message,
1030 transport: TransportContext::default(),
1031 cancellation: CancellationToken::new(),
1032 }
1033 }
1034}
1035
1036impl Server {
1037 pub fn builder(name: impl Into<String>, version: impl Into<String>) -> ServerBuilder {
1039 ServerBuilder::new(name, version)
1040 }
1041
1042 #[must_use]
1044 pub fn limits(&self) -> &Limits {
1045 &self.inner.limits
1046 }
1047
1048 #[must_use]
1051 pub fn mirrored_params(&self, tool: &str) -> Option<&[MirroredParam]> {
1052 self.find_tool(tool).map(|t| t.mirrored.as_slice())
1053 }
1054
1055 fn find_tool(&self, name: &str) -> Option<&RegisteredTool> {
1056 self.inner
1057 .tools
1058 .binary_search_by(|t| t.name.as_str().cmp(name))
1059 .ok()
1060 .and_then(|i| self.inner.tools.get(i))
1061 }
1062
1063 #[cfg_attr(not(feature = "http"), allow(dead_code))]
1065 pub(crate) fn has_scopes(&self) -> bool {
1066 self.inner.has_scopes
1067 }
1068
1069 pub(crate) fn legacy(&self) -> &LegacyConfig {
1070 &self.inner.legacy
1071 }
1072
1073 pub(crate) fn state_codec(&self) -> &StateCodec {
1074 &self.inner.state_codec
1075 }
1076
1077 #[must_use]
1079 pub fn supported_versions(&self) -> &[String] {
1080 &self.inner.supported_versions
1081 }
1082
1083 pub async fn handle_bytes(
1087 &self,
1088 bytes: &[u8],
1089 transport: TransportContext,
1090 cancellation: CancellationToken,
1091 ) -> Option<Response> {
1092 match jsonrpc::parse(bytes, &self.inner.limits) {
1093 Ok(message) => {
1094 self.handle(McpRequest {
1095 message,
1096 transport,
1097 cancellation,
1098 })
1099 .await
1100 }
1101 Err(Rejection::Error { id, error }) => Some(Response::error(id, error)),
1102 Err(Rejection::UnexpectedResponse) => {
1103 tracing::debug!("ignoring a JSON-RPC response sent by the client");
1104 None
1105 }
1106 }
1107 }
1108
1109 pub async fn handle(&self, request: McpRequest) -> Option<Response> {
1111 let McpRequest {
1112 message,
1113 transport,
1114 cancellation,
1115 } = request;
1116 match message {
1117 Message::Notification(_) => None,
1120 Message::Request(req) => {
1121 let id = req.id.clone();
1122 let dispatched =
1126 std::panic::AssertUnwindSafe(self.dispatch(req, transport, cancellation))
1127 .catch_unwind()
1128 .await;
1129 match dispatched {
1130 Ok(Ok(Some(result))) => Some(Response::Result { id, result }),
1131 Ok(Ok(None)) => None,
1132 Ok(Err(error)) => Some(Response::error(Some(id), error)),
1133 Err(_panic) => {
1134 tracing::error!("request handling panicked");
1135 Some(Response::error(Some(id), McpError::internal()))
1136 }
1137 }
1138 }
1139 }
1140 }
1141
1142 async fn dispatch(
1143 &self,
1144 req: Request,
1145 transport: TransportContext,
1146 cancellation: CancellationToken,
1147 ) -> Result<Option<Box<RawValue>>, McpError> {
1148 let params = req.params.as_deref();
1149 match req.method.as_str() {
1150 methods::SERVER_DISCOVER => {
1151 let p: MetaOnly<'_> = parse_params(params)?;
1152 self.validate_meta(p.meta, &transport)?;
1153 Ok(Some(self.inner.discover.clone()))
1154 }
1155 methods::TOOLS_LIST => self.list(
1160 &req.method,
1161 params,
1162 Some(&*self.inner.tools_list),
1163 &transport,
1164 ),
1165 methods::TOOLS_CALL => {
1166 let p: CallParams<'_> = parse_params(params)?;
1167 let meta = self.validate_meta(p.meta, &transport)?;
1168 let auth = transport.get::<AuthContext>();
1169 let key = RoundKey {
1170 method: methods::TOOLS_CALL,
1171 target: &p.name,
1172 arguments: p.arguments.map_or(Args::None, |a| Args::Raw(a.get())),
1173 principal: auth.as_deref().map(|a| a.digest.as_str()),
1174 };
1175 let round = self.round(&key, p.input_responses, p.request_state.as_deref())?;
1176 let ctx = self.ctx(req.id, meta, &cancellation, transport, &round);
1177 self.call_tool(&p.name, p.arguments, ctx, &cancellation)
1178 .await
1179 .or_else(|e| self.input_required(e, &round, &key))
1180 }
1181 methods::RESOURCES_LIST => self.list(
1182 &req.method,
1183 params,
1184 self.inner.resources_list.as_deref(),
1185 &transport,
1186 ),
1187 methods::RESOURCES_TEMPLATES_LIST => self.list(
1188 &req.method,
1189 params,
1190 self.inner.templates_list.as_deref(),
1191 &transport,
1192 ),
1193 methods::RESOURCES_READ if self.inner.resources_list.is_some() => {
1194 let p: ReadParams<'_> = parse_params(params)?;
1195 let meta = self.validate_meta(p.meta, &transport)?;
1196 let auth = transport.get::<AuthContext>();
1197 let key = RoundKey {
1198 method: methods::RESOURCES_READ,
1199 target: &p.uri,
1200 arguments: Args::None,
1201 principal: auth.as_deref().map(|a| a.digest.as_str()),
1202 };
1203 let round = self.round(&key, p.input_responses, p.request_state.as_deref())?;
1204 let ctx = self.ctx(req.id, meta, &cancellation, transport, &round);
1205 self.read_resource(&p.uri, ctx, &cancellation)
1206 .await
1207 .or_else(|e| self.input_required(e, &round, &key))
1208 }
1209 methods::PROMPTS_LIST => self.list(
1210 &req.method,
1211 params,
1212 self.inner.prompts_list.as_deref(),
1213 &transport,
1214 ),
1215 methods::PROMPTS_GET if self.inner.prompts_list.is_some() => {
1216 let p: GetPromptParams<'_> = parse_params(params)?;
1217 let meta = self.validate_meta(p.meta, &transport)?;
1218 let arguments = PromptArguments::new(p.arguments.unwrap_or_default());
1219 let auth = transport.get::<AuthContext>();
1220 let key = RoundKey {
1221 method: methods::PROMPTS_GET,
1222 target: &p.name,
1223 arguments: Args::Map(arguments.as_map()),
1224 principal: auth.as_deref().map(|a| a.digest.as_str()),
1225 };
1226 let round = self.round(&key, p.input_responses, p.request_state.as_deref())?;
1227 let ctx = self.ctx(req.id, meta, &cancellation, transport, &round);
1228 self.get_prompt(&p.name, &arguments, ctx, &cancellation)
1229 .await
1230 .or_else(|e| self.input_required(e, &round, &key))
1231 }
1232 methods::COMPLETION_COMPLETE if self.inner.has_completions => {
1233 let mut p: CompleteParams<'_> = parse_params(params)?;
1234 self.validate_meta(p.meta.take(), &transport)?;
1235 self.complete(p).map(Some)
1236 }
1237 "initialize" if self.inner.legacy.enabled => self
1238 .legacy_initialize(params)
1239 .map(|(result, _)| Some(result)),
1240 "initialize" => {
1241 hints::warn_once(
1242 &self.inner.legacy_hint,
1243 format_args!(
1244 "a client tried to connect with `initialize` (MCP 2025-11-25 or older), \
1245 but this server only accepts {}",
1246 self.inner.supported_versions.join(", ")
1247 ),
1248 "legacy clients are disabled on this server; enable them with \
1249 ServerBuilder::legacy_clients(true), or use a client that supports MCP \
1250 2026-07-28",
1251 );
1252 Err(McpError::method_not_found_explained(format!(
1253 "This server implements the stateless MCP protocol (versions: {}); \
1254 `initialize` is not supported",
1255 self.inner.supported_versions.join(", ")
1256 )))
1257 }
1258 other => Err(McpError::method_not_found(other)),
1259 }
1260 }
1261
1262 fn list(
1265 &self,
1266 method: &str,
1267 params: Option<&RawValue>,
1268 body: Option<&RawValue>,
1269 transport: &TransportContext,
1270 ) -> Result<Option<Box<RawValue>>, McpError> {
1271 let Some(body) = body else {
1272 return Err(McpError::method_not_found(method));
1273 };
1274 let p: ListParams<'_> = parse_params(params)?;
1275 self.validate_meta(p.meta, transport)?;
1276 if p.cursor.is_some() {
1277 return Err(McpError::invalid_params("Invalid cursor"));
1279 }
1280 Ok(Some(body.to_owned()))
1281 }
1282
1283 #[allow(deprecated)] fn validate_meta(
1285 &self,
1286 meta: Option<&RawValue>,
1287 transport: &TransportContext,
1288 ) -> Result<RequestMeta, McpError> {
1289 if let Some(session) = transport.get::<LegacySession>() {
1291 let mut request_meta = session.request_meta();
1292 request_meta.progress_token = meta
1294 .and_then(|raw| serde_json::from_str::<LegacyMeta>(raw.get()).ok())
1295 .and_then(|m| m.progress_token);
1296 return Ok(request_meta);
1297 }
1298 let missing = || {
1299 McpError::invalid_params(
1300 "Missing `_meta` with `io.modelcontextprotocol/protocolVersion` and \
1301 `io.modelcontextprotocol/clientCapabilities`",
1302 )
1303 };
1304 let meta: spec::RequestMetaObject = serde_json::from_str(meta.ok_or_else(missing)?.get())
1305 .map_err(|e| {
1306 McpError::invalid_params(format!(
1307 "Invalid `_meta`: {}",
1308 crate::error::sanitize(&e.to_string())
1309 ))
1310 })?;
1311 if !self
1312 .inner
1313 .supported_versions
1314 .contains(&meta.protocol_version)
1315 {
1316 return Err(McpError::unsupported_protocol_version(
1317 &meta.protocol_version,
1318 &self.inner.supported_versions,
1319 ));
1320 }
1321 Ok(RequestMeta {
1322 protocol_version: meta.protocol_version,
1323 client_capabilities: meta.client_capabilities,
1324 client_info: meta.client_info,
1325 progress_token: meta.progress_token,
1326 log_level: meta.log_level,
1327 })
1328 }
1329
1330 fn ctx(
1331 &self,
1332 id: RequestId,
1333 meta: RequestMeta,
1334 cancellation: &CancellationToken,
1335 transport: TransportContext,
1336 round: &Arc<Round>,
1337 ) -> Ctx {
1338 let notifier = transport
1341 .get::<NotifyChannel>()
1342 .and_then(|channel| {
1343 Notifier::new(&channel, meta.progress_token.clone(), meta.log_level)
1344 })
1345 .map(Arc::new);
1346 Ctx::new(
1347 id,
1348 meta,
1349 cancellation.clone(),
1350 transport,
1351 Arc::clone(&self.inner.state),
1352 Some(Arc::clone(round)),
1353 notifier,
1354 )
1355 }
1356
1357 fn round(
1359 &self,
1360 key: &RoundKey<'_>,
1361 input_responses: Option<Value>,
1362 request_state: Option<&str>,
1363 ) -> Result<Arc<Round>, McpError> {
1364 let has_state = request_state.is_some();
1365 Round::start(&self.inner.state_codec, key, input_responses, request_state)
1366 .map(Arc::new)
1367 .inspect_err(|_| {
1368 if has_state {
1369 hints::warn_once(
1370 &self.inner.state_hint,
1371 format_args!("rejected a requestState that failed verification"),
1372 "if this server runs as several instances, give all of them the same \
1373 key with ServerBuilder::request_state_key(..); otherwise the client \
1374 sent an expired or modified state (or retried another request)",
1375 );
1376 }
1377 })
1378 }
1379
1380 fn input_required(
1383 &self,
1384 error: HandlerError,
1385 round: &Round,
1386 key: &RoundKey<'_>,
1387 ) -> Result<Option<Box<RawValue>>, McpError> {
1388 match error {
1389 HandlerError::InputRequired => {
1390 let result = round.input_required(
1391 &self.inner.state_codec,
1392 key,
1393 self.inner.result_meta.as_ref(),
1394 )?;
1395 to_raw(&result).map(Some).map_err(|_| McpError::internal())
1396 }
1397 HandlerError::Rpc(error) => Err(error),
1398 }
1399 }
1400
1401 async fn run<T>(
1404 &self,
1405 future: Pin<Box<dyn Future<Output = Result<T, ToolError>> + Send>>,
1406 cancellation: &CancellationToken,
1407 ) -> Result<Option<Result<T, ToolError>>, McpError> {
1408 let Ok(_permit) = self.inner.permits.acquire().await else {
1410 return Err(McpError::internal());
1411 };
1412 let guarded = std::panic::AssertUnwindSafe(future).catch_unwind();
1413 tokio::select! {
1414 biased;
1415 () = cancellation.cancelled() => Ok(None),
1416 timed = tokio::time::timeout(self.inner.limits.tool_timeout, guarded) => {
1417 Ok(Some(match timed {
1418 Ok(Ok(result)) => result,
1419 Ok(Err(_panic)) => Err(ToolError::panicked()),
1420 Err(_elapsed) => Err(ToolError::timeout()),
1421 }))
1422 }
1423 }
1424 }
1425
1426 fn with_server_info(&self, meta: &mut Option<spec::ResultMetaObject>) {
1427 if let Some(server_meta) = &self.inner.result_meta {
1428 match meta {
1429 Some(meta) => meta.server_info.clone_from(&server_meta.server_info),
1430 None => *meta = Some(server_meta.clone()),
1431 }
1432 }
1433 }
1434
1435 async fn call_tool(
1436 &self,
1437 name: &str,
1438 arguments: Option<&RawValue>,
1439 ctx: Ctx,
1440 cancellation: &CancellationToken,
1441 ) -> Result<Option<Box<RawValue>>, HandlerError> {
1442 let tool = self
1443 .find_tool(name)
1444 .ok_or_else(|| McpError::unknown_tool(name))?;
1445 authorize(&tool.scopes, &ctx)?;
1446
1447 let notifier = ctx.notifier_handle();
1448 let outcome = match (tool.call)(arguments, ctx) {
1449 Err(error) => Err(error),
1450 Ok(future) => match self.run(future, cancellation).await? {
1451 None => return Ok(None),
1452 Some(outcome) => outcome,
1453 },
1454 };
1455 if let Some(notifier) = notifier {
1456 notifier.finish().await;
1457 }
1458
1459 let mut result = match outcome {
1460 Ok(ToolOutput::Complete(result)) => result,
1461 Err(error) if error.is_input_required() => return Err(HandlerError::InputRequired),
1462 Err(error) => match error.into_protocol() {
1463 Ok(protocol_error) => return Err(protocol_error.into()),
1464 Err(error) => error.into_call_result(&tool.name),
1465 },
1466 };
1467 result.result_type = COMPLETE.to_owned();
1468 self.with_server_info(&mut result.meta);
1469 Ok(to_raw(&result).map(Some).map_err(|error| {
1470 tracing::error!(tool = %tool.name, %error, "failed to serialize tool result");
1471 McpError::internal()
1472 })?)
1473 }
1474
1475 #[allow(clippy::type_complexity)]
1478 fn find_resource(
1479 &self,
1480 uri: &str,
1481 ) -> Option<(ReadFn, UriVars, u64, CacheScope, &[String], Option<&Value>)> {
1482 if let Some(r) = self
1483 .inner
1484 .resources
1485 .binary_search_by(|r| r.uri.as_str().cmp(uri))
1486 .ok()
1487 .and_then(|i| self.inner.resources.get(i))
1488 {
1489 return Some((
1490 r.read,
1491 UriVars::default(),
1492 r.ttl_ms,
1493 r.cache_scope,
1494 &r.scopes,
1495 r.app.as_ref(),
1496 ));
1497 }
1498 self.inner.templates.iter().find_map(|t| {
1499 t.template
1500 .matches(uri)
1501 .map(|vars| (t.read, vars, t.ttl_ms, t.cache_scope, &*t.scopes, None))
1502 })
1503 }
1504
1505 async fn read_resource(
1506 &self,
1507 uri: &str,
1508 ctx: Ctx,
1509 cancellation: &CancellationToken,
1510 ) -> Result<Option<Box<RawValue>>, HandlerError> {
1511 let (read, vars, ttl_ms, cache_scope, scopes, app) = self
1512 .find_resource(uri)
1513 .ok_or_else(|| McpError::resource_not_found(uri))?;
1514 authorize(scopes, &ctx)?;
1515 let notifier = ctx.notifier_handle();
1516 let outcome = match read(uri, &vars, ctx) {
1517 Err(error) => Err(error),
1518 Ok(future) => match self.run(future, cancellation).await? {
1519 None => return Ok(None),
1520 Some(outcome) => outcome,
1521 },
1522 };
1523 if let Some(notifier) = notifier {
1524 notifier.finish().await;
1525 }
1526 let mut contents =
1527 outcome.map_err(|e| HandlerError::from_tool(e, "resource", Some(uri)))?;
1528 if let Some(ui) = app {
1529 crate::apps::mark_contents(&mut contents, ui);
1530 }
1531 let mut result =
1532 spec::ReadResourceResult::new(cache_scope, contents, COMPLETE.to_owned(), ttl_ms);
1533 self.with_server_info(&mut result.meta);
1534 Ok(to_raw(&result).map(Some).map_err(|error| {
1535 tracing::error!(uri, %error, "failed to serialize resource contents");
1536 McpError::internal()
1537 })?)
1538 }
1539
1540 fn find_prompt(&self, name: &str) -> Option<&RegisteredPrompt> {
1541 self.inner
1542 .prompts
1543 .binary_search_by(|p| p.name.as_str().cmp(name))
1544 .ok()
1545 .and_then(|i| self.inner.prompts.get(i))
1546 }
1547
1548 async fn get_prompt(
1549 &self,
1550 name: &str,
1551 arguments: &PromptArguments,
1552 ctx: Ctx,
1553 cancellation: &CancellationToken,
1554 ) -> Result<Option<Box<RawValue>>, HandlerError> {
1555 let prompt = self
1556 .find_prompt(name)
1557 .ok_or_else(|| McpError::unknown_prompt(name))?;
1558 authorize(&prompt.scopes, &ctx)?;
1559 let notifier = ctx.notifier_handle();
1560 let outcome = match (prompt.get)(arguments, ctx) {
1561 Err(error) => Err(error),
1562 Ok(future) => match self.run(future, cancellation).await? {
1563 None => return Ok(None),
1564 Some(outcome) => outcome,
1565 },
1566 };
1567 if let Some(notifier) = notifier {
1568 notifier.finish().await;
1569 }
1570 let mut result = outcome.map_err(|e| HandlerError::from_tool(e, &prompt.name, None))?;
1571 result.result_type = COMPLETE.to_owned();
1572 if result.description.is_none() {
1573 result.description.clone_from(&prompt.description);
1574 }
1575 self.with_server_info(&mut result.meta);
1576 Ok(to_raw(&result).map(Some).map_err(|error| {
1577 tracing::error!(prompt = %prompt.name, %error, "failed to serialize prompt");
1578 McpError::internal()
1579 })?)
1580 }
1581
1582 fn complete(&self, p: CompleteParams<'_>) -> Result<Box<RawValue>, McpError> {
1583 let completers = match &p.reference {
1584 CompletionRef::Prompt { name } => {
1585 &self
1586 .find_prompt(name)
1587 .ok_or_else(|| McpError::unknown_prompt(name))?
1588 .completers
1589 }
1590 CompletionRef::Resource { uri } => {
1591 &self
1592 .inner
1593 .templates
1594 .iter()
1595 .find(|t| t.template.as_str() == uri)
1596 .ok_or_else(|| McpError::resource_not_found(uri))?
1597 .completers
1598 }
1599 };
1600 let context = p.context.and_then(|c| c.arguments).unwrap_or_default();
1601 let request = CompletionRequest {
1602 argument: &p.argument.name,
1603 value: &p.argument.value,
1604 context: &context,
1605 };
1606 let completion = match completers.iter().find(|c| c.argument == p.argument.name) {
1607 Some(completer) => {
1608 let complete = completer.complete;
1609 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| complete(&request)))
1610 .map_err(|_panic| {
1611 tracing::error!(argument = %p.argument.name, "completion provider panicked");
1612 McpError::internal()
1613 })?
1614 }
1615 None => crate::completion::Completion::default(),
1616 };
1617 let mut result = spec::CompleteResult::new(completion.into_spec(), COMPLETE.to_owned());
1618 self.with_server_info(&mut result.meta);
1619 to_raw(&result).map_err(|error| {
1620 tracing::error!(%error, "failed to serialize completion");
1621 McpError::internal()
1622 })
1623 }
1624}
1625
1626impl tower_service::Service<McpRequest> for Server {
1627 type Response = Option<Response>;
1628 type Error = Infallible;
1629 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Infallible>> + Send>>;
1630
1631 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1632 Poll::Ready(Ok(()))
1633 }
1634
1635 fn call(&mut self, request: McpRequest) -> Self::Future {
1636 let server = self.clone();
1637 Box::pin(async move { Ok(server.handle(request).await) })
1638 }
1639}
1640
1641#[derive(Deserialize)]
1643struct LegacyMeta {
1644 #[serde(rename = "progressToken", default)]
1645 progress_token: Option<spec::ProgressToken>,
1646}
1647
1648#[derive(Deserialize)]
1649struct MetaOnly<'a> {
1650 #[serde(rename = "_meta", borrow, default)]
1651 meta: Option<&'a RawValue>,
1652}
1653
1654#[derive(Deserialize)]
1655struct ListParams<'a> {
1656 #[serde(rename = "_meta", borrow, default)]
1657 meta: Option<&'a RawValue>,
1658 #[serde(borrow, default)]
1659 cursor: Option<std::borrow::Cow<'a, str>>,
1660}
1661
1662#[derive(Deserialize)]
1663struct CallParams<'a> {
1664 #[serde(rename = "_meta", borrow, default)]
1665 meta: Option<&'a RawValue>,
1666 name: String,
1667 #[serde(borrow, default)]
1668 arguments: Option<&'a RawValue>,
1669 #[serde(rename = "inputResponses", default)]
1670 input_responses: Option<Value>,
1671 #[serde(rename = "requestState", default)]
1672 request_state: Option<String>,
1673}
1674
1675#[derive(Deserialize)]
1676struct ReadParams<'a> {
1677 #[serde(rename = "_meta", borrow, default)]
1678 meta: Option<&'a RawValue>,
1679 #[serde(borrow)]
1680 uri: std::borrow::Cow<'a, str>,
1681 #[serde(rename = "inputResponses", default)]
1682 input_responses: Option<Value>,
1683 #[serde(rename = "requestState", default)]
1684 request_state: Option<String>,
1685}
1686
1687#[derive(Deserialize)]
1688struct GetPromptParams<'a> {
1689 #[serde(rename = "_meta", borrow, default)]
1690 meta: Option<&'a RawValue>,
1691 name: String,
1692 #[serde(default)]
1693 arguments: Option<BTreeMap<String, String>>,
1694 #[serde(rename = "inputResponses", default)]
1695 input_responses: Option<Value>,
1696 #[serde(rename = "requestState", default)]
1697 request_state: Option<String>,
1698}
1699
1700enum HandlerError {
1702 InputRequired,
1704 Rpc(McpError),
1706}
1707
1708impl HandlerError {
1709 fn from_tool(error: ToolError, handler: &str, uri: Option<&str>) -> Self {
1710 if error.is_input_required() {
1711 Self::InputRequired
1712 } else {
1713 Self::Rpc(error.into_rpc_error(handler, uri))
1714 }
1715 }
1716}
1717
1718impl From<McpError> for HandlerError {
1719 fn from(error: McpError) -> Self {
1720 Self::Rpc(error)
1721 }
1722}
1723
1724#[derive(Deserialize)]
1725struct CompleteParams<'a> {
1726 #[serde(rename = "_meta", borrow, default)]
1727 meta: Option<&'a RawValue>,
1728 #[serde(rename = "ref")]
1729 reference: CompletionRef,
1730 argument: CompletionArgument,
1731 #[serde(default)]
1732 context: Option<CompletionContext>,
1733}
1734
1735#[derive(Deserialize)]
1736#[serde(tag = "type")]
1737enum CompletionRef {
1738 #[serde(rename = "ref/prompt")]
1739 Prompt { name: String },
1740 #[serde(rename = "ref/resource")]
1741 Resource { uri: String },
1742}
1743
1744#[derive(Deserialize)]
1745struct CompletionArgument {
1746 name: String,
1747 value: String,
1748}
1749
1750#[derive(Deserialize)]
1751struct CompletionContext {
1752 #[serde(default)]
1753 arguments: Option<BTreeMap<String, String>>,
1754}
1755
1756fn parse_params<'a, T: Deserialize<'a>>(params: Option<&'a RawValue>) -> Result<T, McpError> {
1757 let raw = params.map_or("{}", RawValue::get);
1758 serde_json::from_str(raw).map_err(|e| {
1759 McpError::invalid_params(format!(
1760 "Invalid params: {}",
1761 crate::error::sanitize(&e.to_string())
1762 ))
1763 })
1764}
1765
1766fn to_raw<T: serde::Serialize>(value: &T) -> Result<Box<RawValue>, serde_json::Error> {
1767 serde_json::value::to_raw_value(value)
1768}
1769
1770fn millis(d: Duration) -> u64 {
1771 u64::try_from(d.as_millis()).unwrap_or(u64::MAX)
1772}
1773
1774fn is_valid_tool_name(name: &str) -> bool {
1777 (1..=128).contains(&name.len())
1778 && name
1779 .bytes()
1780 .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'_' | b'-' | b'.'))
1781}
1782
1783fn authorize(scopes: &[String], ctx: &Ctx) -> Result<(), McpError> {
1787 if scopes.is_empty() {
1788 return Ok(());
1789 }
1790 match ctx.transport().get::<AuthContext>() {
1791 Some(auth) => auth.check(scopes),
1792 None => Ok(()),
1793 }
1794}