/*
 * Copyright 2024-2025 the original author or authors.
 */

package io.modelcontextprotocol.server;

import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
import io.modelcontextprotocol.util.Assert;
import io.modelcontextprotocol.util.Utils;
import lombok.var;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;

/**
 * MCP server features specification that a particular server can choose to support.
 *
 * @author Dariusz Jędrzejczyk
 * @author Jihoon Kim
 */
public class McpServerFeatures {

	/**
	 * Asynchronous server features specification.
	 *
	 */
	public static class Async {
		private McpSchema.Implementation serverInfo;
		private McpSchema.ServerCapabilities serverCapabilities;
		private List<AsyncToolSpecification> tools;
		private Map<String, AsyncResourceSpecification> resources;
		private Map<String, AsyncResourceTemplateSpecification> resourceTemplates;
		private Map<String, AsyncPromptSpecification> prompts;
		private Map<McpSchema.CompleteReference, AsyncCompletionSpecification> completions;
		private List<BiFunction<McpAsyncServerExchange, List<McpSchema.Root>, Mono<Void>>> rootsChangeConsumers;
		private String instructions;

		/**
		 * Create an instance and validate the arguments.
		 * @param serverInfo The server implementation details
		 * @param serverCapabilities The server capabilities
		 * @param tools The list of tool specifications
		 * @param resources The map of resource specifications
		 * @param resourceTemplates The list of resource templates
		 * @param prompts The map of prompt specifications
		 * @param rootsChangeConsumers The list of consumers that will be notified when the
		 * roots list changes
		 * @param instructions The server instructions text
		 */
		Async(McpSchema.Implementation serverInfo, McpSchema.ServerCapabilities serverCapabilities,
              List<AsyncToolSpecification> tools, Map<String, AsyncResourceSpecification> resources,
              Map<String, AsyncResourceTemplateSpecification> resourceTemplates,
              Map<String, AsyncPromptSpecification> prompts,
              Map<McpSchema.CompleteReference, AsyncCompletionSpecification> completions,
              List<BiFunction<McpAsyncServerExchange, List<McpSchema.Root>, Mono<Void>>> rootsChangeConsumers,
              String instructions) {

			Assert.notNull(serverInfo, "Server info must not be null");

			this.serverInfo = serverInfo;
			this.serverCapabilities = (serverCapabilities != null) ? serverCapabilities
					: new McpSchema.ServerCapabilities(null, // completions
					null, // experimental
					new McpSchema.ServerCapabilities.LoggingCapabilities(), // Enable
					// logging
					// by
					// default
					!Utils.isEmpty(prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null,
					!Utils.isEmpty(resources)
							? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false) : null,
					!Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null);

			this.tools = (tools != null) ? tools : new ArrayList<>();
			this.resources = (resources != null) ? resources : new HashMap<>();
			this.resourceTemplates = (resourceTemplates != null) ? resourceTemplates : new HashMap<>();
			this.prompts = (prompts != null) ? prompts : new HashMap<>();
			this.completions = (completions != null) ? completions : new HashMap<>();
			this.rootsChangeConsumers = (rootsChangeConsumers != null) ? rootsChangeConsumers : new ArrayList<>();
			this.instructions = instructions;
		}

		public McpSchema.Implementation serverInfo() {
			return serverInfo;
		}

		public McpSchema.ServerCapabilities serverCapabilities() {
			return serverCapabilities;
		}

		public List<AsyncToolSpecification> tools() {
			return tools;
		}

		public Map<String, AsyncResourceSpecification> resources() {
			return resources;
		}

		public Map<String, AsyncResourceTemplateSpecification> resourceTemplates() {
			return resourceTemplates;
		}

		public Map<String, AsyncPromptSpecification> prompts() {
			return prompts;
		}

		public Map<McpSchema.CompleteReference, AsyncCompletionSpecification> completions() {
			return completions;
		}

		public List<BiFunction<McpAsyncServerExchange, List<McpSchema.Root>, Mono<Void>>> rootsChangeConsumers() {
			return rootsChangeConsumers;
		}

		public String instructions() {
			return instructions;
		}

		/**
		 * Convert a synchronous specification into an asynchronous one and provide
		 * blocking code offloading to prevent accidental blocking of the non-blocking
		 * transport.
		 * @param syncSpec a potentially blocking, synchronous specification.
		 * @param immediateExecution when true, do not offload. Do NOT set to true when
		 * using a non-blocking transport.
		 * @return a specification which is protected from blocking calls specified by the
		 * user.
		 */
		static Async fromSync(Sync syncSpec, boolean immediateExecution) {
			List<AsyncToolSpecification> tools = new ArrayList<>();
			for (var tool : syncSpec.tools()) {
				tools.add(AsyncToolSpecification.fromSync(tool, immediateExecution));
			}

			Map<String, AsyncResourceSpecification> resources = new HashMap<>();
			syncSpec.resources().forEach((key, resource) -> {
				resources.put(key, AsyncResourceSpecification.fromSync(resource, immediateExecution));
			});

			Map<String, AsyncResourceTemplateSpecification> resourceTemplates = new HashMap<>();
			syncSpec.resourceTemplates().forEach((key, resource) -> {
				resourceTemplates.put(key, AsyncResourceTemplateSpecification.fromSync(resource, immediateExecution));
			});

			Map<String, AsyncPromptSpecification> prompts = new HashMap<>();
			syncSpec.prompts().forEach((key, prompt) -> {
				prompts.put(key, AsyncPromptSpecification.fromSync(prompt, immediateExecution));
			});

			Map<McpSchema.CompleteReference, AsyncCompletionSpecification> completions = new HashMap<>();
			syncSpec.completions().forEach((key, completion) -> {
				completions.put(key, AsyncCompletionSpecification.fromSync(completion, immediateExecution));
			});

			List<BiFunction<McpAsyncServerExchange, List<McpSchema.Root>, Mono<Void>>> rootChangeConsumers = new ArrayList<>();

			for (var rootChangeConsumer : syncSpec.rootsChangeConsumers()) {
				rootChangeConsumers.add((exchange, list) -> Mono
						.<Void>fromRunnable(() -> rootChangeConsumer.accept(new McpSyncServerExchange(exchange), list))
						.subscribeOn(Schedulers.boundedElastic()));
			}

			return new Async(syncSpec.serverInfo(), syncSpec.serverCapabilities(), tools, resources, resourceTemplates,
					prompts, completions, rootChangeConsumers, syncSpec.instructions());
		}
	}

	/**
	 * Synchronous server features specification.
	 *
	 */
	public static class Sync {
		private McpSchema.Implementation serverInfo;
		private McpSchema.ServerCapabilities serverCapabilities;
		private List<SyncToolSpecification> tools;
		private Map<String, SyncResourceSpecification> resources;
		private Map<String, SyncResourceTemplateSpecification> resourceTemplates;
		private Map<String, SyncPromptSpecification> prompts;
		private Map<McpSchema.CompleteReference, SyncCompletionSpecification> completions;
		private List<BiConsumer<McpSyncServerExchange, List<McpSchema.Root>>> rootsChangeConsumers;
		private String instructions;
		/**
		 * Create an instance and validate the arguments.
		 * @param serverInfo The server implementation details
		 * @param serverCapabilities The server capabilities
		 * @param tools The list of tool specifications
		 * @param resources The map of resource specifications
		 * @param resourceTemplates The list of resource templates
		 * @param prompts The map of prompt specifications
		 * @param rootsChangeConsumers The list of consumers that will be notified when
		 * the roots list changes
		 * @param instructions The server instructions text
		 */
		Sync(McpSchema.Implementation serverInfo, McpSchema.ServerCapabilities serverCapabilities,
			 List<SyncToolSpecification> tools,
			 Map<String, SyncResourceSpecification> resources,
			 Map<String, SyncResourceTemplateSpecification> resourceTemplates,
			 Map<String, SyncPromptSpecification> prompts,
			 Map<McpSchema.CompleteReference, SyncCompletionSpecification> completions,
			 List<BiConsumer<McpSyncServerExchange, List<McpSchema.Root>>> rootsChangeConsumers,
			 String instructions) {

			Assert.notNull(serverInfo, "Server info must not be null");

			this.serverInfo = serverInfo;
			this.serverCapabilities = (serverCapabilities != null) ? serverCapabilities
					: new McpSchema.ServerCapabilities(null, // completions
					null, // experimental
					new McpSchema.ServerCapabilities.LoggingCapabilities(), // Enable
					// logging
					// by
					// default
					!Utils.isEmpty(prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null,
					!Utils.isEmpty(resources)
							? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false) : null,
					!Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null);

			this.tools = (tools != null) ? tools : new ArrayList<>();
			this.resources = (resources != null) ? resources : new HashMap<>();
			this.resourceTemplates = (resourceTemplates != null) ? resourceTemplates : new HashMap<>();
			this.prompts = (prompts != null) ? prompts : new HashMap<>();
			this.completions = (completions != null) ? completions : new HashMap<>();
			this.rootsChangeConsumers = (rootsChangeConsumers != null) ? rootsChangeConsumers : new ArrayList<>();
			this.instructions = instructions;
		}

		public McpSchema.Implementation serverInfo() {
			return serverInfo;
		}

		public McpSchema.ServerCapabilities serverCapabilities() {
			return serverCapabilities;
		}

		public List<SyncToolSpecification> tools() {
			return tools;
		}

		public Map<String, SyncResourceSpecification> resources() {
			return resources;
		}

		public Map<String, SyncResourceTemplateSpecification> resourceTemplates() {
			return resourceTemplates;
		}

		public Map<String, SyncPromptSpecification> prompts() {
			return prompts;
		}

		public Map<McpSchema.CompleteReference, SyncCompletionSpecification> completions() {
			return completions;
		}

		public List<BiConsumer<McpSyncServerExchange, List<McpSchema.Root>>> rootsChangeConsumers() {
			return rootsChangeConsumers;
		}

		public String instructions() {
			return instructions;
		}
	}

	/**
	 * Specification of a tool with its asynchronous handler function. Tools are the
	 * primary way for MCP servers to expose functionality to AI models. Each tool
	 * represents a specific capability.
	 */
	public static class AsyncToolSpecification {
		private McpSchema.Tool tool;
		@Deprecated
		private BiFunction<McpAsyncServerExchange, Map<String, Object>, Mono<McpSchema.CallToolResult>> call;
		private BiFunction<McpAsyncServerExchange, CallToolRequest, Mono<McpSchema.CallToolResult>> callHandler;

		/**
		 * @param tool The tool definition including name, description, and parameter schema
		 * @param call Deprecated. Use the {@link AsyncToolSpecification#callHandler} instead.
		 * @param callHandler The function that implements the tool's logic, receiving a
		 * {@link McpAsyncServerExchange} and a
		 * {@link CallToolRequest} and returning
		 * results. The function's first argument is an {@link McpAsyncServerExchange} upon
		 * which the server can interact with the connected client. The second arguments is a
		 * map of tool arguments.
		 * */
		public AsyncToolSpecification(McpSchema.Tool tool,
									  BiFunction<McpAsyncServerExchange, Map<String, Object>, Mono<McpSchema.CallToolResult>> call,
									  BiFunction<McpAsyncServerExchange, CallToolRequest, Mono<McpSchema.CallToolResult>> callHandler) {
			this.tool = tool;
			this.call = call;
			this.callHandler = callHandler;
		}

		public McpSchema.Tool tool() {
			return tool;
		}

		public BiFunction<McpAsyncServerExchange, Map<String, Object>, Mono<McpSchema.CallToolResult>> call() {
			return call;
		}

		public BiFunction<McpAsyncServerExchange, CallToolRequest, Mono<McpSchema.CallToolResult>> callHandler() {
			return callHandler;
		}

		/**
		 * @deprecated Use {@link AsyncToolSpecification(McpSchema.Tool, null,
		 * BiFunction)} instead.
		 **/
		@Deprecated
		public AsyncToolSpecification(McpSchema.Tool tool,
									  BiFunction<McpAsyncServerExchange, Map<String, Object>, Mono<McpSchema.CallToolResult>> call) {
			this(tool, call, (exchange, toolReq) -> call.apply(exchange, toolReq.arguments()));
		}

		static AsyncToolSpecification fromSync(SyncToolSpecification syncToolSpec) {
			return fromSync(syncToolSpec, false);
		}

		static AsyncToolSpecification fromSync(SyncToolSpecification syncToolSpec, boolean immediate) {

			// FIXME: This is temporary, proper validation should be implemented
			if (syncToolSpec == null) {
				return null;
			}

			BiFunction<McpAsyncServerExchange, Map<String, Object>, Mono<McpSchema.CallToolResult>> deprecatedCall = (syncToolSpec
					.call() != null) ? (exchange, map) -> {
				var toolResult = Mono
						.fromCallable(() -> syncToolSpec.call().apply(new McpSyncServerExchange(exchange), map));
				return immediate ? toolResult : toolResult.subscribeOn(Schedulers.boundedElastic());
			} : null;

			BiFunction<McpAsyncServerExchange, CallToolRequest, Mono<McpSchema.CallToolResult>> callHandler = (
					exchange, req) -> {
				var toolResult = Mono
						.fromCallable(() -> syncToolSpec.callHandler().apply(new McpSyncServerExchange(exchange), req));
				return immediate ? toolResult : toolResult.subscribeOn(Schedulers.boundedElastic());
			};

			return new AsyncToolSpecification(syncToolSpec.tool(), deprecatedCall, callHandler);
		}

		/**
		 * Builder for creating AsyncToolSpecification instances.
		 */
		public static class Builder {

			private McpSchema.Tool tool;

			private BiFunction<McpAsyncServerExchange, CallToolRequest, Mono<McpSchema.CallToolResult>> callHandler;

			/**
			 * Sets the tool definition.
			 * @param tool The tool definition including name, description, and parameter
			 * schema
			 * @return this builder instance
			 */
			public Builder tool(McpSchema.Tool tool) {
				this.tool = tool;
				return this;
			}

			/**
			 * Sets the call tool handler function.
			 * @param callHandler The function that implements the tool's logic
			 * @return this builder instance
			 */
			public Builder callHandler(
					BiFunction<McpAsyncServerExchange, CallToolRequest, Mono<McpSchema.CallToolResult>> callHandler) {
				this.callHandler = callHandler;
				return this;
			}

			/**
			 * Builds the AsyncToolSpecification instance.
			 * @return a new AsyncToolSpecification instance
			 * @throws IllegalArgumentException if required fields are not set
			 */
			public AsyncToolSpecification build() {
				Assert.notNull(tool, "Tool must not be null");
				Assert.notNull(callHandler, "Call handler function must not be null");

				return new AsyncToolSpecification(tool, null, callHandler);
			}

		}

		/**
		 * Creates a new builder instance.
		 * @return a new Builder instance
		 */
		public static Builder builder() {
			return new Builder();
		}
	}

	/**
	 * Specification of a resource with its asynchronous handler function. Resources
	 * provide context to AI models by exposing data such as:
	 * <ul>
	 * <li>File contents
	 * <li>Database records
	 * <li>API responses
	 * <li>System information
	 * <li>Application state
	 * </ul>
	 *
	 * <p>
	 * Example resource specification:
	 *
	 * <pre>{@code
	 * new McpServerFeatures.AsyncResourceSpecification(
	 *     Resource.builder()
	 *         .uri("docs")
	 *         .name("Documentation files")
	 * 		   .title("Documentation files")
	 * 		   .mimeType("text/markdown")
	 * 		   .description("Markdown documentation files")
	 * 		   .build(),
	 * 		(exchange, request) -> Mono.fromSupplier(() -> readFile(request.getPath()))
	 * 				.map(ReadResourceResult::new))
	 * }</pre>
	 *
	 */
	public static class AsyncResourceSpecification {
		private McpSchema.Resource resource;
		private BiFunction<McpAsyncServerExchange, McpSchema.ReadResourceRequest, Mono<McpSchema.ReadResourceResult>> readHandler;

		/**
		 * @param resource The resource definition including name, description, and MIME type
		 * @param readHandler The function that handles resource read requests. The function's
		 * first argument is an {@link McpAsyncServerExchange} upon which the server can
		 * interact with the connected client. The second arguments is a
		 * {@link McpSchema.ReadResourceRequest}.
		 * */
		public AsyncResourceSpecification(McpSchema.Resource resource,
										  BiFunction<McpAsyncServerExchange, McpSchema.ReadResourceRequest, Mono<McpSchema.ReadResourceResult>> readHandler) {
			this.resource = resource;
			this.readHandler = readHandler;
		}

		public McpSchema.Resource resource() {
			return resource;
		}

		public BiFunction<McpAsyncServerExchange, McpSchema.ReadResourceRequest, Mono<McpSchema.ReadResourceResult>> readHandler() {
			return readHandler;
		}

		static AsyncResourceSpecification fromSync(SyncResourceSpecification resource, boolean immediateExecution) {
			// FIXME: This is temporary, proper validation should be implemented
			if (resource == null) {
				return null;
			}
			return new AsyncResourceSpecification(resource.resource(), (exchange, req) -> {
				var resourceResult = Mono
						.fromCallable(() -> resource.readHandler().apply(new McpSyncServerExchange(exchange), req));
				return immediateExecution ? resourceResult : resourceResult.subscribeOn(Schedulers.boundedElastic());
			});
		}
	}

	/**
	 * Specification of a resource template with its synchronous handler function.
	 * Resource templates allow servers to expose parameterized resources using URI
	 * templates: <a href=https://datatracker.ietf.org/doc/html/rfc6570> URI
	 * templates.</a>. Arguments may be auto-completed through <a href=
	 * "https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion">the
	 * completion API</a>.
	 *
	 * Templates support:
	 * <ul>
	 * <li>Parameterized resource definitions
	 * <li>Dynamic content generation
	 * <li>Consistent resource formatting
	 * <li>Contextual data injection
	 * </ul>
	 *
	 */
	public static class AsyncResourceTemplateSpecification {
		private McpSchema.ResourceTemplate resourceTemplate;
		private BiFunction<McpAsyncServerExchange, McpSchema.ReadResourceRequest, Mono<McpSchema.ReadResourceResult>> readHandler;

		/**
		 * @param resourceTemplate The resource template definition including name,
		 * description, and parameter schema
		 * @param readHandler The function that handles resource read requests. The function's
		 * first argument is an {@link McpSyncServerExchange} upon which the server can
		 * interact with the connected client. The second arguments is a
		 * {@link McpSchema.ReadResourceRequest}. {@link McpSchema.ResourceTemplate}
		 * {@link McpSchema.ReadResourceResult}
		 * */
		public AsyncResourceTemplateSpecification(McpSchema.ResourceTemplate resourceTemplate,
												  BiFunction<McpAsyncServerExchange, McpSchema.ReadResourceRequest, Mono<McpSchema.ReadResourceResult>> readHandler) {
			this.resourceTemplate = resourceTemplate;
			this.readHandler = readHandler;
		}

		public McpSchema.ResourceTemplate resourceTemplate() {
			return resourceTemplate;
		}

		public BiFunction<McpAsyncServerExchange, McpSchema.ReadResourceRequest, Mono<McpSchema.ReadResourceResult>> readHandler() {
			return readHandler;
		}

		static AsyncResourceTemplateSpecification fromSync(SyncResourceTemplateSpecification resource,
														   boolean immediateExecution) {
			// FIXME: This is temporary, proper validation should be implemented
			if (resource == null) {
				return null;
			}
			return new AsyncResourceTemplateSpecification(resource.resourceTemplate(), (exchange, req) -> {
				var resourceResult = Mono
						.fromCallable(() -> resource.readHandler().apply(new McpSyncServerExchange(exchange), req));
				return immediateExecution ? resourceResult : resourceResult.subscribeOn(Schedulers.boundedElastic());
			});
		}
	}

	/**
	 * Specification of a prompt template with its asynchronous handler function. Prompts
	 * provide structured templates for AI model interactions, supporting:
	 * <ul>
	 * <li>Consistent message formatting
	 * <li>Parameter substitution
	 * <li>Context injection
	 * <li>Response formatting
	 * <li>Instruction templating
	 * </ul>
	 *
	 * <p>
	 * Example prompt specification:
	 *
	 * <pre>{@code
	 * new McpServerFeatures.AsyncPromptSpecification(
	 * 		new Prompt("analyze", "Code analysis template"),
	 * 		(exchange, request) -> {
	 * 			String code = request.getArguments().get("code");
	 * 			return Mono.just(new GetPromptResult(
	 * 					"Analyze this code:\n\n" + code + "\n\nProvide feedback on:"));
	 * 		})
	 * }</pre>
	 *
	 */
	public static class AsyncPromptSpecification {
		private McpSchema.Prompt prompt;
		private BiFunction<McpAsyncServerExchange, McpSchema.GetPromptRequest, Mono<McpSchema.GetPromptResult>> promptHandler;

		/**
		 * @param prompt The prompt definition including name and description
		 * @param promptHandler The function that processes prompt requests and returns
		 * formatted templates. The function's first argument is an
		 * {@link McpAsyncServerExchange} upon which the server can interact with the
		 * connected client. The second arguments is a
		 * {@link McpSchema.GetPromptRequest}.
		 * */
		public AsyncPromptSpecification(McpSchema.Prompt prompt,
										BiFunction<McpAsyncServerExchange, McpSchema.GetPromptRequest, Mono<McpSchema.GetPromptResult>> promptHandler) {
			this.prompt = prompt;
			this.promptHandler = promptHandler;
		}

		public McpSchema.Prompt prompt() {
			return prompt;
		}

		public BiFunction<McpAsyncServerExchange, McpSchema.GetPromptRequest, Mono<McpSchema.GetPromptResult>> promptHandler() {
			return promptHandler;
		}

		static AsyncPromptSpecification fromSync(SyncPromptSpecification prompt, boolean immediateExecution) {
			// FIXME: This is temporary, proper validation should be implemented
			if (prompt == null) {
				return null;
			}
			return new AsyncPromptSpecification(prompt.prompt(), (exchange, req) -> {
				var promptResult = Mono
						.fromCallable(() -> prompt.promptHandler().apply(new McpSyncServerExchange(exchange), req));
				return immediateExecution ? promptResult : promptResult.subscribeOn(Schedulers.boundedElastic());
			});
		}
	}

	/**
	 * Specification of a completion handler function with asynchronous execution support.
	 * Completions generate AI model outputs based on prompt or resource references and
	 * user-provided arguments. This abstraction enables:
	 * <ul>
	 * <li>Customizable response generation logic
	 * <li>Parameter-driven template expansion
	 * <li>Dynamic interaction with connected clients
	 * </ul>
	 *
	 */
	public static class AsyncCompletionSpecification {
		private McpSchema.CompleteReference referenceKey;
		private BiFunction<McpAsyncServerExchange, McpSchema.CompleteRequest, Mono<McpSchema.CompleteResult>> completionHandler;

		/**
		 * @param referenceKey The unique key representing the completion reference.
		 * @param completionHandler The asynchronous function that processes completion
		 * requests and returns results. The first argument is an
		 * {@link McpAsyncServerExchange} used to interact with the client. The second
		 * argument is a {@link McpSchema.CompleteRequest}.
		 * */
		public AsyncCompletionSpecification(McpSchema.CompleteReference referenceKey,
											BiFunction<McpAsyncServerExchange, McpSchema.CompleteRequest, Mono<McpSchema.CompleteResult>> completionHandler) {
			this.referenceKey = referenceKey;
			this.completionHandler = completionHandler;
		}

		public McpSchema.CompleteReference referenceKey() {
			return referenceKey;
		}

		public BiFunction<McpAsyncServerExchange, McpSchema.CompleteRequest, Mono<McpSchema.CompleteResult>> completionHandler() {
			return completionHandler;
		}

		/**
		 * Converts a synchronous {@link SyncCompletionSpecification} into an
		 * {@link AsyncCompletionSpecification} by wrapping the handler in a bounded
		 * elastic scheduler for safe non-blocking execution.
		 * @param completion the synchronous completion specification
		 * @return an asynchronous wrapper of the provided sync specification, or
		 * {@code null} if input is null
		 */
		static AsyncCompletionSpecification fromSync(SyncCompletionSpecification completion,
													 boolean immediateExecution) {
			if (completion == null) {
				return null;
			}
			return new AsyncCompletionSpecification(completion.referenceKey(), (exchange, request) -> {
				var completionResult = Mono.fromCallable(
						() -> completion.completionHandler().apply(new McpSyncServerExchange(exchange), request));
				return immediateExecution ? completionResult
						: completionResult.subscribeOn(Schedulers.boundedElastic());
			});
		}
	}

	/**
	 * Specification of a tool with its synchronous handler function. Tools are the
	 * primary way for MCP servers to expose functionality to AI models.
	 *
	 * <p>
	 * Example tool specification:
	 *
	 * <pre>{@code
	 * McpServerFeatures.SyncToolSpecification.builder()
	 * 		.tool(Tool.builder()
	 * 				.name("calculator")
	 * 				.title("Performs mathematical calculations")
	 * 				.inputSchema(new JsonSchemaObject()
	 * 						.required("expression")
	 * 						.property("expression", JsonSchemaType.STRING))
	 * 				.build()
	 * 		.toolHandler((exchange, req) -> {
	 * 			String expr = (String) req.arguments().get("expression");
	 * 			return CallToolResult.builder()
	 *                   .content(List.of(new McpSchema.TextContent("Result: " + evaluate(expr))))
	 *                   .isError(false)
	 *                   .build();
	 * 		}))
	 *      .build();
	 * }</pre>
	 *
	 */
	public static class SyncToolSpecification {
		private McpSchema.Tool tool;
		@Deprecated
		private BiFunction<McpSyncServerExchange, Map<String, Object>, McpSchema.CallToolResult> call;
		private BiFunction<McpSyncServerExchange, CallToolRequest, McpSchema.CallToolResult> callHandler;

		/**
		 * @param tool The tool definition including name, description, and parameter schema
		 * @param call (Deprected) The function that implements the tool's logic, receiving
		 * arguments and returning results. The function's first argument is an
		 * {@link McpSyncServerExchange} upon which the server can interact with the connected
		 * @param callHandler The function that implements the tool's logic, receiving a
		 * {@link McpSyncServerExchange} and a
		 * {@link CallToolRequest} and returning
		 * results. The function's first argument is an {@link McpSyncServerExchange} upon
		 * which the server can interact with the client. The second arguments is a map of
		 * arguments passed to the tool.
		 * */
		public SyncToolSpecification(McpSchema.Tool tool,
									 BiFunction<McpSyncServerExchange, Map<String, Object>, McpSchema.CallToolResult> call,
									 BiFunction<McpSyncServerExchange, CallToolRequest, McpSchema.CallToolResult> callHandler) {
			this.tool = tool;
			this.call = call;
			this.callHandler = callHandler;
		}

		public McpSchema.Tool tool() {
			return tool;
		}

		public BiFunction<McpSyncServerExchange, Map<String, Object>, McpSchema.CallToolResult> call() {
			return call;
		}

		public BiFunction<McpSyncServerExchange, CallToolRequest, McpSchema.CallToolResult> callHandler() {
			return callHandler;
		}

		@Deprecated
		public SyncToolSpecification(McpSchema.Tool tool,
									 BiFunction<McpSyncServerExchange, Map<String, Object>, McpSchema.CallToolResult> call) {
			this(tool, call, (exchange, toolReq) -> call.apply(exchange, toolReq.arguments()));
		}

		/**
		 * Builder for creating SyncToolSpecification instances.
		 */
		public static class Builder {

			private McpSchema.Tool tool;

			private BiFunction<McpSyncServerExchange, CallToolRequest, McpSchema.CallToolResult> callHandler;

			/**
			 * Sets the tool definition.
			 * @param tool The tool definition including name, description, and parameter
			 * schema
			 * @return this builder instance
			 */
			public Builder tool(McpSchema.Tool tool) {
				this.tool = tool;
				return this;
			}

			/**
			 * Sets the call tool handler function.
			 * @param callHandler The function that implements the tool's logic
			 * @return this builder instance
			 */
			public Builder callHandler(
					BiFunction<McpSyncServerExchange, CallToolRequest, McpSchema.CallToolResult> callHandler) {
				this.callHandler = callHandler;
				return this;
			}

			/**
			 * Builds the SyncToolSpecification instance.
			 * @return a new SyncToolSpecification instance
			 * @throws IllegalArgumentException if required fields are not set
			 */
			public SyncToolSpecification build() {
				Assert.notNull(tool, "Tool must not be null");
				Assert.notNull(callHandler, "CallTool function must not be null");

				return new SyncToolSpecification(tool, null, callHandler);
			}

		}

		/**
		 * Creates a new builder instance.
		 * @return a new Builder instance
		 */
		public static Builder builder() {
			return new Builder();
		}
	}

	/**
	 * Specification of a resource with its synchronous handler function. Resources
	 * provide context to AI models by exposing data such as:
	 * <ul>
	 * <li>File contents
	 * <li>Database records
	 * <li>API responses
	 * <li>System information
	 * <li>Application state
	 * </ul>
	 *
	 * <p>
	 * Example resource specification:
	 *
	 * <pre>{@code
	 * new McpServerFeatures.SyncResourceSpecification(
	 *     Resource.builder()
	 *         .uri("docs")
	 *         .name("Documentation files")
	 * 		   .title("Documentation files")
	 * 		   .mimeType("text/markdown")
	 * 		   .description("Markdown documentation files")
	 * 		   .build(),
	 * 		(exchange, request) -> {
	 * 			String content = readFile(request.getPath());
	 * 			return new ReadResourceResult(content);
	 * 		})
	 * }</pre>
	 *
	 */
	public static class SyncResourceSpecification {
		private McpSchema.Resource resource;
		private BiFunction<McpSyncServerExchange, McpSchema.ReadResourceRequest, McpSchema.ReadResourceResult> readHandler;

		/**
		 * @param resource The resource definition including name, description, and MIME type
		 * @param readHandler The function that handles resource read requests. The function's
		 * first argument is an {@link McpSyncServerExchange} upon which the server can
		 * interact with the connected client. The second arguments is a
		 * {@link McpSchema.ReadResourceRequest}.
		 * */
		public SyncResourceSpecification(McpSchema.Resource resource,
										 BiFunction<McpSyncServerExchange, McpSchema.ReadResourceRequest, McpSchema.ReadResourceResult> readHandler) {
			this.resource = resource;
			this.readHandler = readHandler;
		}

		public McpSchema.Resource resource() {
			return resource;
		}

		public BiFunction<McpSyncServerExchange, McpSchema.ReadResourceRequest, McpSchema.ReadResourceResult> readHandler() {
			return readHandler;
		}
	}

	/**
	 * Specification of a resource template with its synchronous handler function.
	 * Resource templates allow servers to expose parameterized resources using URI
	 * templates: <a href=https://datatracker.ietf.org/doc/html/rfc6570> URI
	 * templates.</a>. Arguments may be auto-completed through <a href=
	 * "https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion">the
	 * completion API</a>.
	 *
	 * Templates support:
	 * <ul>
	 * <li>Parameterized resource definitions
	 * <li>Dynamic content generation
	 * <li>Consistent resource formatting
	 * <li>Contextual data injection
	 * </ul>
	 *
	 */
	public static class SyncResourceTemplateSpecification {
		private McpSchema.ResourceTemplate resourceTemplate;
		private BiFunction<McpSyncServerExchange, McpSchema.ReadResourceRequest, McpSchema.ReadResourceResult> readHandler;

		/**
		 * @param resourceTemplate The resource template definition including name,
		 * description, and parameter schema
		 * @param readHandler The function that handles resource read requests. The function's
		 * first argument is an {@link McpSyncServerExchange} upon which the server can
		 * interact with the connected client. The second arguments is a
		 * {@link McpSchema.ReadResourceRequest}. {@link McpSchema.ResourceTemplate}
		 * {@link McpSchema.ReadResourceResult}
		 * */
		public SyncResourceTemplateSpecification(McpSchema.ResourceTemplate resourceTemplate,
												 BiFunction<McpSyncServerExchange, McpSchema.ReadResourceRequest, McpSchema.ReadResourceResult> readHandler) {
			this.resourceTemplate = resourceTemplate;
			this.readHandler = readHandler;
		}

		public McpSchema.ResourceTemplate resourceTemplate() {
			return resourceTemplate;
		}

		public BiFunction<McpSyncServerExchange, McpSchema.ReadResourceRequest, McpSchema.ReadResourceResult> readHandler() {
			return readHandler;
		}
	}

	/**
	 * Specification of a prompt template with its synchronous handler function. Prompts
	 * provide structured templates for AI model interactions, supporting:
	 * <ul>
	 * <li>Consistent message formatting
	 * <li>Parameter substitution
	 * <li>Context injection
	 * <li>Response formatting
	 * <li>Instruction templating
	 * </ul>
	 *
	 * <p>
	 * Example prompt specification:
	 *
	 * <pre>{@code
	 * new McpServerFeatures.SyncPromptSpecification(
	 * 		new Prompt("analyze", "Code analysis template"),
	 * 		(exchange, request) -> {
	 * 			String code = request.getArguments().get("code");
	 * 			return new GetPromptResult(
	 * 					"Analyze this code:\n\n" + code + "\n\nProvide feedback on:");
	 * 		})
	 * }</pre>
	 *
	 */
	public static class SyncPromptSpecification {
		private McpSchema.Prompt prompt;
		private BiFunction<McpSyncServerExchange, McpSchema.GetPromptRequest, McpSchema.GetPromptResult> promptHandler;

		/**
		 * @param prompt The prompt definition including name and description
		 * @param promptHandler The function that processes prompt requests and returns
		 * formatted templates. The function's first argument is an
		 * {@link McpSyncServerExchange} upon which the server can interact with the connected
		 * client. The second arguments is a
		 * {@link McpSchema.GetPromptRequest}.
		 * */
		public SyncPromptSpecification(McpSchema.Prompt prompt,
									   BiFunction<McpSyncServerExchange, McpSchema.GetPromptRequest, McpSchema.GetPromptResult> promptHandler) {
			this.prompt = prompt;
			this.promptHandler = promptHandler;
		}

		public McpSchema.Prompt prompt() {
			return prompt;
		}

		public BiFunction<McpSyncServerExchange, McpSchema.GetPromptRequest, McpSchema.GetPromptResult> promptHandler() {
			return promptHandler;
		}
	}

	/**
	 * Specification of a completion handler function with synchronous execution support.
	 *
	 */
	public static class SyncCompletionSpecification {
		private McpSchema.CompleteReference referenceKey;
		private BiFunction<McpSyncServerExchange, McpSchema.CompleteRequest, McpSchema.CompleteResult> completionHandler;

		/**
		 * @param referenceKey The unique key representing the completion reference.
		 * @param completionHandler The synchronous function that processes completion
		 * requests and returns results. The first argument is an
		 * {@link McpSyncServerExchange} used to interact with the client. The second argument
		 * is a {@link McpSchema.CompleteRequest}.
		 * */
		public SyncCompletionSpecification(McpSchema.CompleteReference referenceKey,
										   BiFunction<McpSyncServerExchange, McpSchema.CompleteRequest, McpSchema.CompleteResult> completionHandler) {
			this.referenceKey = referenceKey;
			this.completionHandler = completionHandler;
		}

		public McpSchema.CompleteReference referenceKey() {
			return referenceKey;
		}
		public BiFunction<McpSyncServerExchange, McpSchema.CompleteRequest, McpSchema.CompleteResult> completionHandler() {
			return completionHandler;
		}
	}
}
