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 model_visible = |t: &&ToolDef| {
619 t.visibility
620 .as_ref()
621 .is_none_or(|v| v.contains(&crate::apps::Visibility::Model))
622 };
623 let tools_list_for_models = if tools.iter().all(|t| model_visible(&t)) {
624 None
625 } else {
626 let list = spec::ListToolsResult {
627 tools: tools
628 .iter()
629 .filter(model_visible)
630 .map(|t| t.tool.clone())
631 .collect(),
632 ..list
633 };
634 Some(to_raw(&list).map_err(|e| BuildError::Serialize("tools/list", e))?)
635 };
636 let list_ttl_ms = millis(self.list_ttl);
637
638 let resources_list = if has_resources {
639 Some(
640 to_raw(&spec::ListResourcesResult {
641 meta: result_meta.clone(),
642 cache_scope: self.cache_scope,
643 next_cursor: None,
644 resources: resources.listed,
645 result_type: COMPLETE.to_owned(),
646 ttl_ms: list_ttl_ms,
647 })
648 .map_err(|e| BuildError::Serialize("resources/list", e))?,
649 )
650 } else {
651 None
652 };
653 let templates_list = if has_resources {
654 Some(
655 to_raw(&spec::ListResourceTemplatesResult {
656 meta: result_meta.clone(),
657 cache_scope: self.cache_scope,
658 next_cursor: None,
659 resource_templates: resources.listed_templates,
660 result_type: COMPLETE.to_owned(),
661 ttl_ms: list_ttl_ms,
662 })
663 .map_err(|e| BuildError::Serialize("resources/templates/list", e))?,
664 )
665 } else {
666 None
667 };
668 let prompts_list = if has_prompts {
669 Some(
670 to_raw(&spec::ListPromptsResult {
671 meta: result_meta.clone(),
672 cache_scope: self.cache_scope,
673 next_cursor: None,
674 prompts: prompts.listed,
675 result_type: COMPLETE.to_owned(),
676 ttl_ms: list_ttl_ms,
677 })
678 .map_err(|e| BuildError::Serialize("prompts/list", e))?,
679 )
680 } else {
681 None
682 };
683
684 let registry = tools
685 .into_iter()
686 .zip(mirrored)
687 .map(|(t, mirrored)| RegisteredTool {
688 name: t.tool.name,
689 call: t.call,
690 mirrored,
691 scopes: t.scopes.into(),
692 app_only: t
693 .visibility
694 .as_ref()
695 .is_some_and(|v| !v.contains(&crate::apps::Visibility::Model)),
696 })
697 .collect::<Box<[RegisteredTool]>>();
698 let has_scopes = registry.iter().any(|t| !t.scopes.is_empty())
699 || resources.statics.iter().any(|r| !r.scopes.is_empty())
700 || resources.templates.iter().any(|t| !t.scopes.is_empty())
701 || prompts.registry.iter().any(|p| !p.scopes.is_empty());
702
703 Ok(Server {
704 inner: Arc::new(Inner {
705 tools: registry,
706 has_scopes,
707 resources: resources.statics,
708 templates: resources.templates,
709 prompts: prompts.registry,
710 has_completions,
711 state_codec,
712 legacy,
713 legacy_hint: Once::new(),
714 state_hint: Once::new(),
715 discover,
716 tools_list,
717 tools_list_for_models,
718 resources_list,
719 templates_list,
720 prompts_list,
721 result_meta,
722 supported_versions,
723 state: Arc::new(self.state),
724 permits: Arc::new(Semaphore::new(self.limits.max_concurrent_calls)),
725 limits: self.limits,
726 }),
727 })
728 }
729}
730
731#[derive(Clone)]
736pub struct Server {
737 inner: Arc<Inner>,
738}
739
740struct Inner {
741 tools: Box<[RegisteredTool]>,
743 resources: Box<[RegisteredResource]>,
745 templates: Box<[RegisteredTemplate]>,
747 prompts: Box<[RegisteredPrompt]>,
749 has_completions: bool,
750 has_scopes: bool,
752 state_codec: StateCodec,
753 legacy: LegacyConfig,
754 legacy_hint: Once,
755 state_hint: Once,
756 discover: Box<RawValue>,
757 tools_list: Box<RawValue>,
758 tools_list_for_models: Option<Box<RawValue>>,
761 resources_list: Option<Box<RawValue>>,
762 templates_list: Option<Box<RawValue>>,
763 prompts_list: Option<Box<RawValue>>,
764 result_meta: Option<spec::ResultMetaObject>,
765 supported_versions: Vec<String>,
766 state: Arc<TypeMap>,
767 permits: Arc<Semaphore>,
768 limits: Limits,
769}
770
771struct RegisteredTool {
772 name: String,
773 call: CallFn,
774 mirrored: Vec<MirroredParam>,
775 scopes: Box<[String]>,
776 app_only: bool,
778}
779
780struct RegisteredResource {
781 uri: String,
782 read: ReadFn,
783 ttl_ms: u64,
784 cache_scope: CacheScope,
785 scopes: Box<[String]>,
786 app: Option<Value>,
788}
789
790struct RegisteredTemplate {
791 template: UriTemplate,
792 read: ReadFn,
793 ttl_ms: u64,
794 cache_scope: CacheScope,
795 completers: Vec<Completer>,
796 scopes: Box<[String]>,
797}
798
799struct RegisteredPrompt {
800 name: String,
801 description: Option<String>,
802 get: GetFn,
803 completers: Vec<Completer>,
804 scopes: Box<[String]>,
805}
806
807struct BuiltResources {
808 statics: Box<[RegisteredResource]>,
809 templates: Box<[RegisteredTemplate]>,
810 listed: Vec<spec::Resource>,
811 listed_templates: Vec<spec::ResourceTemplate>,
812}
813
814struct BuiltPrompts {
815 registry: Box<[RegisteredPrompt]>,
816 listed: Vec<spec::Prompt>,
817}
818
819fn check_state(
820 state: &TypeMap,
821 required: &[StateRequirement],
822 handler: &str,
823) -> Result<(), BuildError> {
824 match required.iter().find(|r| !state.contains(r.type_id)) {
825 Some(missing) => Err(BuildError::MissingState {
826 handler: handler.to_owned(),
827 type_name: missing.type_name,
828 }),
829 None => Ok(()),
830 }
831}
832
833fn check_scopes(scopes: &[String], handler: &str) -> Result<(), BuildError> {
834 match scopes.iter().find(|s| !crate::auth::is_valid_scope(s)) {
835 Some(scope) => Err(BuildError::InvalidScope {
836 handler: handler.to_owned(),
837 scope: scope.clone(),
838 }),
839 None => Ok(()),
840 }
841}
842
843fn check_completers(
844 completers: &[Completer],
845 owner: &str,
846 arguments: &[&str],
847) -> Result<(), BuildError> {
848 match completers
849 .iter()
850 .find(|c| !arguments.contains(&c.argument.as_str()))
851 {
852 Some(unknown) => Err(BuildError::InvalidCompletion {
853 owner: owner.to_owned(),
854 argument: unknown.argument.clone(),
855 }),
856 None => Ok(()),
857 }
858}
859
860fn build_resources(defs: Vec<ResourceDef>, state: &TypeMap) -> Result<BuiltResources, BuildError> {
861 let mut statics = Vec::new();
862 let mut templates = Vec::new();
863 let mut listed = Vec::new();
864 let mut listed_templates = Vec::new();
865 for def in defs {
866 check_state(state, &def.required_state, def.uri())?;
867 check_scopes(&def.scopes, def.uri())?;
868 let ttl_ms = millis(def.ttl);
869 match def.kind {
870 ResourceKind::Static(resource) => {
871 if !uri_template::is_absolute_uri(&resource.uri) {
872 return Err(BuildError::InvalidResource {
873 uri: resource.uri,
874 reason: "expected an absolute URI such as `scheme://path`".to_owned(),
875 });
876 }
877 check_completers(&def.completers, &resource.uri, &[])?;
878 let app = match &def.app {
879 None => None,
880 Some(ui) => {
881 let invalid = |reason: String| BuildError::InvalidApp {
882 uri: resource.uri.clone(),
883 reason,
884 };
885 if !resource.uri.starts_with(crate::apps::URI_SCHEME) {
886 return Err(invalid("the URI must use the `ui://` scheme".to_owned()));
887 }
888 ui.validate().map_err(invalid)?;
889 Some(ui.to_meta())
890 }
891 };
892 statics.push(RegisteredResource {
893 uri: resource.uri.clone(),
894 read: def.read,
895 ttl_ms,
896 cache_scope: def.cache_scope,
897 scopes: def.scopes.into(),
898 app,
899 });
900 listed.push(resource);
901 }
902 ResourceKind::Template(template) => {
903 if def.app.is_some() {
904 return Err(BuildError::InvalidApp {
905 uri: template.uri_template,
906 reason: "an interface is a fixed `ui://` resource, not a template"
907 .to_owned(),
908 });
909 }
910 let parsed = UriTemplate::parse(&template.uri_template).map_err(|e| {
911 BuildError::InvalidResource {
912 uri: template.uri_template.clone(),
913 reason: e.to_string(),
914 }
915 })?;
916 let variables: Vec<&str> = parsed.variables().collect();
917 check_completers(&def.completers, &template.uri_template, &variables)?;
918 templates.push((
919 template.name.clone(),
920 RegisteredTemplate {
921 template: parsed,
922 read: def.read,
923 ttl_ms,
924 cache_scope: def.cache_scope,
925 completers: def.completers,
926 scopes: def.scopes.into(),
927 },
928 ));
929 listed_templates.push(template);
930 }
931 }
932 }
933 statics.sort_by(|a, b| a.uri.cmp(&b.uri));
934 if let Some(pair) = statics
935 .windows(2)
936 .find(|w| matches!(w, [a, b] if a.uri == b.uri))
937 && let Some(first) = pair.first()
938 {
939 return Err(BuildError::DuplicateResource(first.uri.clone()));
940 }
941 listed.sort_by(|a, b| a.uri.cmp(&b.uri));
942 templates.sort_by(|(na, a), (nb, b)| {
943 na.cmp(nb)
944 .then_with(|| a.template.as_str().cmp(b.template.as_str()))
945 });
946 for (i, (_, t)) in templates.iter().enumerate() {
947 if templates
948 .iter()
949 .skip(i + 1)
950 .any(|(_, other)| other.template.as_str() == t.template.as_str())
951 {
952 return Err(BuildError::DuplicateResource(
953 t.template.as_str().to_owned(),
954 ));
955 }
956 }
957 listed_templates.sort_by(|a, b| {
958 a.name
959 .cmp(&b.name)
960 .then_with(|| a.uri_template.cmp(&b.uri_template))
961 });
962 Ok(BuiltResources {
963 statics: statics.into(),
964 templates: templates.into_iter().map(|(_, t)| t).collect(),
965 listed,
966 listed_templates,
967 })
968}
969
970fn build_prompts(mut defs: Vec<PromptDef>, state: &TypeMap) -> Result<BuiltPrompts, BuildError> {
971 defs.sort_by(|a, b| a.prompt.name.cmp(&b.prompt.name));
972 if let Some(pair) = defs
973 .windows(2)
974 .find(|w| matches!(w, [a, b] if a.prompt.name == b.prompt.name))
975 && let Some(first) = pair.first()
976 {
977 return Err(BuildError::DuplicatePrompt(first.prompt.name.clone()));
978 }
979 let mut registry = Vec::with_capacity(defs.len());
980 let mut listed = Vec::with_capacity(defs.len());
981 for def in defs {
982 let name = &def.prompt.name;
983 if name.is_empty() || name.chars().any(char::is_control) {
984 return Err(BuildError::InvalidPromptName(name.clone()));
985 }
986 check_state(state, &def.required_state, name)?;
987 check_scopes(&def.scopes, name)?;
988 let arguments: Vec<&str> = def
989 .prompt
990 .arguments
991 .iter()
992 .flatten()
993 .map(|a| a.name.as_str())
994 .collect();
995 check_completers(&def.completers, name, &arguments)?;
996 registry.push(RegisteredPrompt {
997 name: name.clone(),
998 description: def.prompt.description.clone(),
999 get: def.get,
1000 completers: def.completers,
1001 scopes: def.scopes.into(),
1002 });
1003 listed.push(def.prompt);
1004 }
1005 Ok(BuiltPrompts {
1006 registry: registry.into(),
1007 listed,
1008 })
1009}
1010
1011impl std::fmt::Debug for Server {
1012 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1013 f.debug_struct("Server")
1014 .field(
1015 "tools",
1016 &self.inner.tools.iter().map(|t| &t.name).collect::<Vec<_>>(),
1017 )
1018 .field(
1019 "resources",
1020 &self
1021 .inner
1022 .resources
1023 .iter()
1024 .map(|r| &r.uri)
1025 .collect::<Vec<_>>(),
1026 )
1027 .field(
1028 "prompts",
1029 &self
1030 .inner
1031 .prompts
1032 .iter()
1033 .map(|p| &p.name)
1034 .collect::<Vec<_>>(),
1035 )
1036 .field("limits", &self.inner.limits)
1037 .finish_non_exhaustive()
1038 }
1039}
1040
1041#[derive(Debug)]
1043pub struct McpRequest {
1044 pub message: Message,
1046 pub transport: TransportContext,
1048 pub cancellation: CancellationToken,
1050}
1051
1052impl McpRequest {
1053 #[must_use]
1055 pub fn new(message: Message) -> Self {
1056 Self {
1057 message,
1058 transport: TransportContext::default(),
1059 cancellation: CancellationToken::new(),
1060 }
1061 }
1062}
1063
1064impl Server {
1065 pub fn builder(name: impl Into<String>, version: impl Into<String>) -> ServerBuilder {
1067 ServerBuilder::new(name, version)
1068 }
1069
1070 #[must_use]
1072 pub fn limits(&self) -> &Limits {
1073 &self.inner.limits
1074 }
1075
1076 #[must_use]
1079 pub fn mirrored_params(&self, tool: &str) -> Option<&[MirroredParam]> {
1080 self.find_tool(tool).map(|t| t.mirrored.as_slice())
1081 }
1082
1083 fn find_tool(&self, name: &str) -> Option<&RegisteredTool> {
1084 self.inner
1085 .tools
1086 .binary_search_by(|t| t.name.as_str().cmp(name))
1087 .ok()
1088 .and_then(|i| self.inner.tools.get(i))
1089 }
1090
1091 #[cfg_attr(not(feature = "http"), allow(dead_code))]
1093 pub(crate) fn has_scopes(&self) -> bool {
1094 self.inner.has_scopes
1095 }
1096
1097 pub(crate) fn legacy(&self) -> &LegacyConfig {
1098 &self.inner.legacy
1099 }
1100
1101 pub(crate) fn state_codec(&self) -> &StateCodec {
1102 &self.inner.state_codec
1103 }
1104
1105 #[must_use]
1107 pub fn supported_versions(&self) -> &[String] {
1108 &self.inner.supported_versions
1109 }
1110
1111 pub async fn handle_bytes(
1115 &self,
1116 bytes: &[u8],
1117 transport: TransportContext,
1118 cancellation: CancellationToken,
1119 ) -> Option<Response> {
1120 match jsonrpc::parse(bytes, &self.inner.limits) {
1121 Ok(message) => {
1122 self.handle(McpRequest {
1123 message,
1124 transport,
1125 cancellation,
1126 })
1127 .await
1128 }
1129 Err(Rejection::Error { id, error }) => Some(Response::error(id, error)),
1130 Err(Rejection::UnexpectedResponse) => {
1131 tracing::debug!("ignoring a JSON-RPC response sent by the client");
1132 None
1133 }
1134 }
1135 }
1136
1137 pub async fn handle(&self, request: McpRequest) -> Option<Response> {
1139 let McpRequest {
1140 message,
1141 transport,
1142 cancellation,
1143 } = request;
1144 match message {
1145 Message::Notification(_) => None,
1148 Message::Request(req) => {
1149 let id = req.id.clone();
1150 let dispatched =
1154 std::panic::AssertUnwindSafe(self.dispatch(req, transport, cancellation))
1155 .catch_unwind()
1156 .await;
1157 match dispatched {
1158 Ok(Ok(Some(result))) => Some(Response::Result { id, result }),
1159 Ok(Ok(None)) => None,
1160 Ok(Err(error)) => Some(Response::error(Some(id), error)),
1161 Err(_panic) => {
1162 tracing::error!("request handling panicked");
1163 Some(Response::error(Some(id), McpError::internal()))
1164 }
1165 }
1166 }
1167 }
1168 }
1169
1170 async fn dispatch(
1171 &self,
1172 req: Request,
1173 transport: TransportContext,
1174 cancellation: CancellationToken,
1175 ) -> Result<Option<Box<RawValue>>, McpError> {
1176 let params = req.params.as_deref();
1177 match req.method.as_str() {
1178 methods::SERVER_DISCOVER => {
1179 let p: MetaOnly<'_> = parse_params(params)?;
1180 self.validate_meta(p.meta, &transport)?;
1181 Ok(Some(self.inner.discover.clone()))
1182 }
1183 methods::TOOLS_LIST => self.list_tools(params, &transport),
1184 methods::TOOLS_CALL => {
1185 let p: CallParams<'_> = parse_params(params)?;
1186 let meta = self.validate_meta(p.meta, &transport)?;
1187 let auth = transport.get::<AuthContext>();
1188 let key = RoundKey {
1189 method: methods::TOOLS_CALL,
1190 target: &p.name,
1191 arguments: p.arguments.map_or(Args::None, |a| Args::Raw(a.get())),
1192 principal: auth.as_deref().map(|a| a.digest.as_str()),
1193 };
1194 let round = self.round(&key, p.input_responses, p.request_state.as_deref())?;
1195 let ctx = self.ctx(req.id, meta, &cancellation, transport, &round);
1196 self.call_tool(&p.name, p.arguments, ctx, &cancellation)
1197 .await
1198 .or_else(|e| self.input_required(e, &round, &key))
1199 }
1200 methods::RESOURCES_LIST => self.list(
1201 &req.method,
1202 params,
1203 self.inner.resources_list.as_deref(),
1204 &transport,
1205 ),
1206 methods::RESOURCES_TEMPLATES_LIST => self.list(
1207 &req.method,
1208 params,
1209 self.inner.templates_list.as_deref(),
1210 &transport,
1211 ),
1212 methods::RESOURCES_READ if self.inner.resources_list.is_some() => {
1213 let p: ReadParams<'_> = parse_params(params)?;
1214 let meta = self.validate_meta(p.meta, &transport)?;
1215 let auth = transport.get::<AuthContext>();
1216 let key = RoundKey {
1217 method: methods::RESOURCES_READ,
1218 target: &p.uri,
1219 arguments: Args::None,
1220 principal: auth.as_deref().map(|a| a.digest.as_str()),
1221 };
1222 let round = self.round(&key, p.input_responses, p.request_state.as_deref())?;
1223 let ctx = self.ctx(req.id, meta, &cancellation, transport, &round);
1224 self.read_resource(&p.uri, ctx, &cancellation)
1225 .await
1226 .or_else(|e| self.input_required(e, &round, &key))
1227 }
1228 methods::PROMPTS_LIST => self.list(
1229 &req.method,
1230 params,
1231 self.inner.prompts_list.as_deref(),
1232 &transport,
1233 ),
1234 methods::PROMPTS_GET if self.inner.prompts_list.is_some() => {
1235 let p: GetPromptParams<'_> = parse_params(params)?;
1236 let meta = self.validate_meta(p.meta, &transport)?;
1237 let arguments = PromptArguments::new(p.arguments.unwrap_or_default());
1238 let auth = transport.get::<AuthContext>();
1239 let key = RoundKey {
1240 method: methods::PROMPTS_GET,
1241 target: &p.name,
1242 arguments: Args::Map(arguments.as_map()),
1243 principal: auth.as_deref().map(|a| a.digest.as_str()),
1244 };
1245 let round = self.round(&key, p.input_responses, p.request_state.as_deref())?;
1246 let ctx = self.ctx(req.id, meta, &cancellation, transport, &round);
1247 self.get_prompt(&p.name, &arguments, ctx, &cancellation)
1248 .await
1249 .or_else(|e| self.input_required(e, &round, &key))
1250 }
1251 methods::COMPLETION_COMPLETE if self.inner.has_completions => {
1252 let mut p: CompleteParams<'_> = parse_params(params)?;
1253 self.validate_meta(p.meta.take(), &transport)?;
1254 self.complete(p).map(Some)
1255 }
1256 "initialize" if self.inner.legacy.enabled => self
1257 .legacy_initialize(params)
1258 .map(|(result, _)| Some(result)),
1259 "initialize" => {
1260 hints::warn_once(
1261 &self.inner.legacy_hint,
1262 format_args!(
1263 "a client tried to connect with `initialize` (MCP 2025-11-25 or older), \
1264 but this server only accepts {}",
1265 self.inner.supported_versions.join(", ")
1266 ),
1267 "legacy clients are disabled on this server; enable them with \
1268 ServerBuilder::legacy_clients(true), or use a client that supports MCP \
1269 2026-07-28",
1270 );
1271 Err(McpError::method_not_found_explained(format!(
1272 "This server implements the stateless MCP protocol (versions: {}); \
1273 `initialize` is not supported",
1274 self.inner.supported_versions.join(", ")
1275 )))
1276 }
1277 other => Err(McpError::method_not_found(other)),
1278 }
1279 }
1280
1281 fn list(
1284 &self,
1285 method: &str,
1286 params: Option<&RawValue>,
1287 body: Option<&RawValue>,
1288 transport: &TransportContext,
1289 ) -> Result<Option<Box<RawValue>>, McpError> {
1290 let Some(body) = body else {
1291 return Err(McpError::method_not_found(method));
1292 };
1293 let p: ListParams<'_> = parse_params(params)?;
1294 self.validate_meta(p.meta, transport)?;
1295 if p.cursor.is_some() {
1296 return Err(McpError::invalid_params("Invalid cursor"));
1298 }
1299 Ok(Some(body.to_owned()))
1300 }
1301
1302 fn list_tools(
1305 &self,
1306 params: Option<&RawValue>,
1307 transport: &TransportContext,
1308 ) -> Result<Option<Box<RawValue>>, McpError> {
1309 let p: ListParams<'_> = parse_params(params)?;
1310 let meta = self.validate_meta(p.meta, transport)?;
1311 if p.cursor.is_some() {
1312 return Err(McpError::invalid_params("Invalid cursor"));
1313 }
1314 let body = match &self.inner.tools_list_for_models {
1315 Some(for_models) if !crate::apps::supported_by(&meta.client_capabilities) => for_models,
1316 _ => &self.inner.tools_list,
1317 };
1318 Ok(Some(body.to_owned()))
1319 }
1320
1321 #[allow(deprecated)] fn validate_meta(
1323 &self,
1324 meta: Option<&RawValue>,
1325 transport: &TransportContext,
1326 ) -> Result<RequestMeta, McpError> {
1327 if let Some(session) = transport.get::<LegacySession>() {
1329 let mut request_meta = session.request_meta();
1330 request_meta.progress_token = meta
1332 .and_then(|raw| serde_json::from_str::<LegacyMeta>(raw.get()).ok())
1333 .and_then(|m| m.progress_token);
1334 return Ok(request_meta);
1335 }
1336 let missing = || {
1337 McpError::invalid_params(
1338 "Missing `_meta` with `io.modelcontextprotocol/protocolVersion` and \
1339 `io.modelcontextprotocol/clientCapabilities`",
1340 )
1341 };
1342 let meta: spec::RequestMetaObject = serde_json::from_str(meta.ok_or_else(missing)?.get())
1343 .map_err(|e| {
1344 McpError::invalid_params(format!(
1345 "Invalid `_meta`: {}",
1346 crate::error::sanitize(&e.to_string())
1347 ))
1348 })?;
1349 if !self
1350 .inner
1351 .supported_versions
1352 .contains(&meta.protocol_version)
1353 {
1354 return Err(McpError::unsupported_protocol_version(
1355 &meta.protocol_version,
1356 &self.inner.supported_versions,
1357 ));
1358 }
1359 Ok(RequestMeta {
1360 protocol_version: meta.protocol_version,
1361 client_capabilities: meta.client_capabilities,
1362 client_info: meta.client_info,
1363 progress_token: meta.progress_token,
1364 log_level: meta.log_level,
1365 })
1366 }
1367
1368 fn ctx(
1369 &self,
1370 id: RequestId,
1371 meta: RequestMeta,
1372 cancellation: &CancellationToken,
1373 transport: TransportContext,
1374 round: &Arc<Round>,
1375 ) -> Ctx {
1376 let notifier = transport
1379 .get::<NotifyChannel>()
1380 .and_then(|channel| {
1381 Notifier::new(&channel, meta.progress_token.clone(), meta.log_level)
1382 })
1383 .map(Arc::new);
1384 Ctx::new(
1385 id,
1386 meta,
1387 cancellation.clone(),
1388 transport,
1389 Arc::clone(&self.inner.state),
1390 Some(Arc::clone(round)),
1391 notifier,
1392 )
1393 }
1394
1395 fn round(
1397 &self,
1398 key: &RoundKey<'_>,
1399 input_responses: Option<Value>,
1400 request_state: Option<&str>,
1401 ) -> Result<Arc<Round>, McpError> {
1402 let has_state = request_state.is_some();
1403 Round::start(&self.inner.state_codec, key, input_responses, request_state)
1404 .map(Arc::new)
1405 .inspect_err(|_| {
1406 if has_state {
1407 hints::warn_once(
1408 &self.inner.state_hint,
1409 format_args!("rejected a requestState that failed verification"),
1410 "if this server runs as several instances, give all of them the same \
1411 key with ServerBuilder::request_state_key(..); otherwise the client \
1412 sent an expired or modified state (or retried another request)",
1413 );
1414 }
1415 })
1416 }
1417
1418 fn input_required(
1421 &self,
1422 error: HandlerError,
1423 round: &Round,
1424 key: &RoundKey<'_>,
1425 ) -> Result<Option<Box<RawValue>>, McpError> {
1426 match error {
1427 HandlerError::InputRequired => {
1428 let result = round.input_required(
1429 &self.inner.state_codec,
1430 key,
1431 self.inner.result_meta.as_ref(),
1432 )?;
1433 to_raw(&result).map(Some).map_err(|_| McpError::internal())
1434 }
1435 HandlerError::Rpc(error) => Err(error),
1436 }
1437 }
1438
1439 async fn run<T>(
1442 &self,
1443 future: Pin<Box<dyn Future<Output = Result<T, ToolError>> + Send>>,
1444 cancellation: &CancellationToken,
1445 ) -> Result<Option<Result<T, ToolError>>, McpError> {
1446 let Ok(_permit) = self.inner.permits.acquire().await else {
1448 return Err(McpError::internal());
1449 };
1450 let guarded = std::panic::AssertUnwindSafe(future).catch_unwind();
1451 tokio::select! {
1452 biased;
1453 () = cancellation.cancelled() => Ok(None),
1454 timed = tokio::time::timeout(self.inner.limits.tool_timeout, guarded) => {
1455 Ok(Some(match timed {
1456 Ok(Ok(result)) => result,
1457 Ok(Err(_panic)) => Err(ToolError::panicked()),
1458 Err(_elapsed) => Err(ToolError::timeout()),
1459 }))
1460 }
1461 }
1462 }
1463
1464 fn with_server_info(&self, meta: &mut Option<spec::ResultMetaObject>) {
1465 if let Some(server_meta) = &self.inner.result_meta {
1466 match meta {
1467 Some(meta) => meta.server_info.clone_from(&server_meta.server_info),
1468 None => *meta = Some(server_meta.clone()),
1469 }
1470 }
1471 }
1472
1473 async fn call_tool(
1474 &self,
1475 name: &str,
1476 arguments: Option<&RawValue>,
1477 ctx: Ctx,
1478 cancellation: &CancellationToken,
1479 ) -> Result<Option<Box<RawValue>>, HandlerError> {
1480 let tool = self
1481 .find_tool(name)
1482 .filter(|t| !t.app_only || ctx.client_supports_apps())
1483 .ok_or_else(|| McpError::unknown_tool(name))?;
1484 authorize(&tool.scopes, &ctx)?;
1485
1486 let notifier = ctx.notifier_handle();
1487 let outcome = match (tool.call)(arguments, ctx) {
1488 Err(error) => Err(error),
1489 Ok(future) => match self.run(future, cancellation).await? {
1490 None => return Ok(None),
1491 Some(outcome) => outcome,
1492 },
1493 };
1494 if let Some(notifier) = notifier {
1495 notifier.finish().await;
1496 }
1497
1498 let mut result = match outcome {
1499 Ok(ToolOutput::Complete(result)) => result,
1500 Err(error) if error.is_input_required() => return Err(HandlerError::InputRequired),
1501 Err(error) => match error.into_protocol() {
1502 Ok(protocol_error) => return Err(protocol_error.into()),
1503 Err(error) => error.into_call_result(&tool.name),
1504 },
1505 };
1506 result.result_type = COMPLETE.to_owned();
1507 self.with_server_info(&mut result.meta);
1508 Ok(to_raw(&result).map(Some).map_err(|error| {
1509 tracing::error!(tool = %tool.name, %error, "failed to serialize tool result");
1510 McpError::internal()
1511 })?)
1512 }
1513
1514 #[allow(clippy::type_complexity)]
1517 fn find_resource(
1518 &self,
1519 uri: &str,
1520 ) -> Option<(ReadFn, UriVars, u64, CacheScope, &[String], Option<&Value>)> {
1521 if let Some(r) = self
1522 .inner
1523 .resources
1524 .binary_search_by(|r| r.uri.as_str().cmp(uri))
1525 .ok()
1526 .and_then(|i| self.inner.resources.get(i))
1527 {
1528 return Some((
1529 r.read,
1530 UriVars::default(),
1531 r.ttl_ms,
1532 r.cache_scope,
1533 &r.scopes,
1534 r.app.as_ref(),
1535 ));
1536 }
1537 self.inner.templates.iter().find_map(|t| {
1538 t.template
1539 .matches(uri)
1540 .map(|vars| (t.read, vars, t.ttl_ms, t.cache_scope, &*t.scopes, None))
1541 })
1542 }
1543
1544 async fn read_resource(
1545 &self,
1546 uri: &str,
1547 ctx: Ctx,
1548 cancellation: &CancellationToken,
1549 ) -> Result<Option<Box<RawValue>>, HandlerError> {
1550 let (read, vars, ttl_ms, cache_scope, scopes, app) = self
1551 .find_resource(uri)
1552 .ok_or_else(|| McpError::resource_not_found(uri))?;
1553 authorize(scopes, &ctx)?;
1554 let notifier = ctx.notifier_handle();
1555 let outcome = match read(uri, &vars, ctx) {
1556 Err(error) => Err(error),
1557 Ok(future) => match self.run(future, cancellation).await? {
1558 None => return Ok(None),
1559 Some(outcome) => outcome,
1560 },
1561 };
1562 if let Some(notifier) = notifier {
1563 notifier.finish().await;
1564 }
1565 let mut contents =
1566 outcome.map_err(|e| HandlerError::from_tool(e, "resource", Some(uri)))?;
1567 if let Some(ui) = app {
1568 crate::apps::mark_contents(&mut contents, ui);
1569 }
1570 let mut result =
1571 spec::ReadResourceResult::new(cache_scope, contents, COMPLETE.to_owned(), ttl_ms);
1572 self.with_server_info(&mut result.meta);
1573 Ok(to_raw(&result).map(Some).map_err(|error| {
1574 tracing::error!(uri, %error, "failed to serialize resource contents");
1575 McpError::internal()
1576 })?)
1577 }
1578
1579 fn find_prompt(&self, name: &str) -> Option<&RegisteredPrompt> {
1580 self.inner
1581 .prompts
1582 .binary_search_by(|p| p.name.as_str().cmp(name))
1583 .ok()
1584 .and_then(|i| self.inner.prompts.get(i))
1585 }
1586
1587 async fn get_prompt(
1588 &self,
1589 name: &str,
1590 arguments: &PromptArguments,
1591 ctx: Ctx,
1592 cancellation: &CancellationToken,
1593 ) -> Result<Option<Box<RawValue>>, HandlerError> {
1594 let prompt = self
1595 .find_prompt(name)
1596 .ok_or_else(|| McpError::unknown_prompt(name))?;
1597 authorize(&prompt.scopes, &ctx)?;
1598 let notifier = ctx.notifier_handle();
1599 let outcome = match (prompt.get)(arguments, ctx) {
1600 Err(error) => Err(error),
1601 Ok(future) => match self.run(future, cancellation).await? {
1602 None => return Ok(None),
1603 Some(outcome) => outcome,
1604 },
1605 };
1606 if let Some(notifier) = notifier {
1607 notifier.finish().await;
1608 }
1609 let mut result = outcome.map_err(|e| HandlerError::from_tool(e, &prompt.name, None))?;
1610 result.result_type = COMPLETE.to_owned();
1611 if result.description.is_none() {
1612 result.description.clone_from(&prompt.description);
1613 }
1614 self.with_server_info(&mut result.meta);
1615 Ok(to_raw(&result).map(Some).map_err(|error| {
1616 tracing::error!(prompt = %prompt.name, %error, "failed to serialize prompt");
1617 McpError::internal()
1618 })?)
1619 }
1620
1621 fn complete(&self, p: CompleteParams<'_>) -> Result<Box<RawValue>, McpError> {
1622 let completers = match &p.reference {
1623 CompletionRef::Prompt { name } => {
1624 &self
1625 .find_prompt(name)
1626 .ok_or_else(|| McpError::unknown_prompt(name))?
1627 .completers
1628 }
1629 CompletionRef::Resource { uri } => {
1630 &self
1631 .inner
1632 .templates
1633 .iter()
1634 .find(|t| t.template.as_str() == uri)
1635 .ok_or_else(|| McpError::resource_not_found(uri))?
1636 .completers
1637 }
1638 };
1639 let context = p.context.and_then(|c| c.arguments).unwrap_or_default();
1640 let request = CompletionRequest {
1641 argument: &p.argument.name,
1642 value: &p.argument.value,
1643 context: &context,
1644 };
1645 let completion = match completers.iter().find(|c| c.argument == p.argument.name) {
1646 Some(completer) => {
1647 let complete = completer.complete;
1648 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| complete(&request)))
1649 .map_err(|_panic| {
1650 tracing::error!(argument = %p.argument.name, "completion provider panicked");
1651 McpError::internal()
1652 })?
1653 }
1654 None => crate::completion::Completion::default(),
1655 };
1656 let mut result = spec::CompleteResult::new(completion.into_spec(), COMPLETE.to_owned());
1657 self.with_server_info(&mut result.meta);
1658 to_raw(&result).map_err(|error| {
1659 tracing::error!(%error, "failed to serialize completion");
1660 McpError::internal()
1661 })
1662 }
1663}
1664
1665impl tower_service::Service<McpRequest> for Server {
1666 type Response = Option<Response>;
1667 type Error = Infallible;
1668 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Infallible>> + Send>>;
1669
1670 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1671 Poll::Ready(Ok(()))
1672 }
1673
1674 fn call(&mut self, request: McpRequest) -> Self::Future {
1675 let server = self.clone();
1676 Box::pin(async move { Ok(server.handle(request).await) })
1677 }
1678}
1679
1680#[derive(Deserialize)]
1682struct LegacyMeta {
1683 #[serde(rename = "progressToken", default)]
1684 progress_token: Option<spec::ProgressToken>,
1685}
1686
1687#[derive(Deserialize)]
1688struct MetaOnly<'a> {
1689 #[serde(rename = "_meta", borrow, default)]
1690 meta: Option<&'a RawValue>,
1691}
1692
1693#[derive(Deserialize)]
1694struct ListParams<'a> {
1695 #[serde(rename = "_meta", borrow, default)]
1696 meta: Option<&'a RawValue>,
1697 #[serde(borrow, default)]
1698 cursor: Option<std::borrow::Cow<'a, str>>,
1699}
1700
1701#[derive(Deserialize)]
1702struct CallParams<'a> {
1703 #[serde(rename = "_meta", borrow, default)]
1704 meta: Option<&'a RawValue>,
1705 name: String,
1706 #[serde(borrow, default)]
1707 arguments: Option<&'a RawValue>,
1708 #[serde(rename = "inputResponses", default)]
1709 input_responses: Option<Value>,
1710 #[serde(rename = "requestState", default)]
1711 request_state: Option<String>,
1712}
1713
1714#[derive(Deserialize)]
1715struct ReadParams<'a> {
1716 #[serde(rename = "_meta", borrow, default)]
1717 meta: Option<&'a RawValue>,
1718 #[serde(borrow)]
1719 uri: std::borrow::Cow<'a, str>,
1720 #[serde(rename = "inputResponses", default)]
1721 input_responses: Option<Value>,
1722 #[serde(rename = "requestState", default)]
1723 request_state: Option<String>,
1724}
1725
1726#[derive(Deserialize)]
1727struct GetPromptParams<'a> {
1728 #[serde(rename = "_meta", borrow, default)]
1729 meta: Option<&'a RawValue>,
1730 name: String,
1731 #[serde(default)]
1732 arguments: Option<BTreeMap<String, String>>,
1733 #[serde(rename = "inputResponses", default)]
1734 input_responses: Option<Value>,
1735 #[serde(rename = "requestState", default)]
1736 request_state: Option<String>,
1737}
1738
1739enum HandlerError {
1741 InputRequired,
1743 Rpc(McpError),
1745}
1746
1747impl HandlerError {
1748 fn from_tool(error: ToolError, handler: &str, uri: Option<&str>) -> Self {
1749 if error.is_input_required() {
1750 Self::InputRequired
1751 } else {
1752 Self::Rpc(error.into_rpc_error(handler, uri))
1753 }
1754 }
1755}
1756
1757impl From<McpError> for HandlerError {
1758 fn from(error: McpError) -> Self {
1759 Self::Rpc(error)
1760 }
1761}
1762
1763#[derive(Deserialize)]
1764struct CompleteParams<'a> {
1765 #[serde(rename = "_meta", borrow, default)]
1766 meta: Option<&'a RawValue>,
1767 #[serde(rename = "ref")]
1768 reference: CompletionRef,
1769 argument: CompletionArgument,
1770 #[serde(default)]
1771 context: Option<CompletionContext>,
1772}
1773
1774#[derive(Deserialize)]
1775#[serde(tag = "type")]
1776enum CompletionRef {
1777 #[serde(rename = "ref/prompt")]
1778 Prompt { name: String },
1779 #[serde(rename = "ref/resource")]
1780 Resource { uri: String },
1781}
1782
1783#[derive(Deserialize)]
1784struct CompletionArgument {
1785 name: String,
1786 value: String,
1787}
1788
1789#[derive(Deserialize)]
1790struct CompletionContext {
1791 #[serde(default)]
1792 arguments: Option<BTreeMap<String, String>>,
1793}
1794
1795fn parse_params<'a, T: Deserialize<'a>>(params: Option<&'a RawValue>) -> Result<T, McpError> {
1796 let raw = params.map_or("{}", RawValue::get);
1797 serde_json::from_str(raw).map_err(|e| {
1798 McpError::invalid_params(format!(
1799 "Invalid params: {}",
1800 crate::error::sanitize(&e.to_string())
1801 ))
1802 })
1803}
1804
1805fn to_raw<T: serde::Serialize>(value: &T) -> Result<Box<RawValue>, serde_json::Error> {
1806 serde_json::value::to_raw_value(value)
1807}
1808
1809fn millis(d: Duration) -> u64 {
1810 u64::try_from(d.as_millis()).unwrap_or(u64::MAX)
1811}
1812
1813fn is_valid_tool_name(name: &str) -> bool {
1816 (1..=128).contains(&name.len())
1817 && name
1818 .bytes()
1819 .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'_' | b'-' | b'.'))
1820}
1821
1822fn authorize(scopes: &[String], ctx: &Ctx) -> Result<(), McpError> {
1826 if scopes.is_empty() {
1827 return Ok(());
1828 }
1829 match ctx.transport().get::<AuthContext>() {
1830 Some(auth) => auth.check(scopes),
1831 None => Ok(()),
1832 }
1833}