package io.modelcontextprotocol.conformance.server;

import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import io.modelcontextprotocol.json.McpJsonDefaults;
import io.modelcontextprotocol.json.TypeRef;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.server.transport.DefaultServerTransportSecurityValidator;
import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema.AudioContent;
import io.modelcontextprotocol.spec.McpSchema.BlobResourceContents;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.CompleteResult;
import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest;
import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult;
import io.modelcontextprotocol.spec.McpSchema.ElicitRequest;
import io.modelcontextprotocol.spec.McpSchema.ElicitResult;
import io.modelcontextprotocol.spec.McpSchema.EmbeddedResource;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import io.modelcontextprotocol.spec.McpSchema.ImageContent;
import io.modelcontextprotocol.spec.McpSchema.LoggingLevel;
import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification;
import io.modelcontextprotocol.spec.McpSchema.ProgressNotification;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptArgument;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpSchema.PromptReference;
import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult;
import io.modelcontextprotocol.spec.McpSchema.Resource;
import io.modelcontextprotocol.spec.McpSchema.ResourceTemplate;
import io.modelcontextprotocol.spec.McpSchema.Role;
import io.modelcontextprotocol.spec.McpSchema.SamplingMessage;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
import io.modelcontextprotocol.spec.McpSchema.TextContent;
import io.modelcontextprotocol.spec.McpSchema.TextResourceContents;
import io.modelcontextprotocol.spec.McpSchema.Tool;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static io.modelcontextprotocol.spec.McpSchema.EnumSchemaOption;
import static io.modelcontextprotocol.spec.McpSchema.JSON_SCHEMA_DIALECT_2020_12;
import static io.modelcontextprotocol.spec.McpSchema.LegacyTitledEnumSchema;
import static io.modelcontextprotocol.spec.McpSchema.TitledMultiSelectEnumSchema;
import static io.modelcontextprotocol.spec.McpSchema.TitledMultiSelectItems;
import static io.modelcontextprotocol.spec.McpSchema.TitledSingleSelectEnumSchema;
import static io.modelcontextprotocol.spec.McpSchema.UntitledMultiSelectEnumSchema;
import static io.modelcontextprotocol.spec.McpSchema.UntitledMultiSelectItems;
import static io.modelcontextprotocol.spec.McpSchema.UntitledSingleSelectEnumSchema;

public class ConformanceServlet {

	private static final Logger logger = LoggerFactory.getLogger(ConformanceServlet.class);

	private static final int PORT = 8080;

	private static final String MCP_ENDPOINT = "/mcp";

	private static final Map<String, Object> EMPTY_JSON_SCHEMA = Map.of("type", "object", "properties",
			Collections.emptyMap());

	// Minimal 1x1 red pixel PNG (base64 encoded)
	private static final String RED_PIXEL_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==";

	// Minimal WAV file (base64 encoded) - 1 sample at 8kHz
	private static final String MINIMAL_WAV = "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABAAZGF0YQAAAAA=";

	public static void main(String[] args) throws Exception {
		logger.info("Starting MCP Conformance Tests - Servlet Server");

		HttpServletStreamableServerTransportProvider transportProvider = HttpServletStreamableServerTransportProvider
			.builder()
			.mcpEndpoint(MCP_ENDPOINT)
			.keepAliveInterval(Duration.ofSeconds(30))
			.securityValidator(DefaultServerTransportSecurityValidator.builder()
				.allowedOrigin("http://localhost:*")
				.allowedHost("localhost:*")
				.build())
			.build();

		// Build server with all conformance test features
		var mcpServer = McpServer.sync(transportProvider)
			.serverInfo("mcp-conformance-server", "1.0.0")
			.capabilities(ServerCapabilities.builder()
				.completions()
				.resources(true, false)
				.tools(false)
				.prompts(false)
				.build())
			.tools(createToolSpecs())
			.prompts(createPromptSpecs())
			.resources(createResourceSpecs())
			.resourceTemplates(createResourceTemplateSpecs())
			.completions(createCompletionSpecs())
			.requestTimeout(Duration.ofSeconds(30))
			.build();

		// Set up embedded Tomcat
		Tomcat tomcat = createEmbeddedTomcat(transportProvider);

		try {
			tomcat.start();
			logger.info("Conformance MCP Servlet Server started on port {} with endpoint {}", PORT, MCP_ENDPOINT);
			logger.info("Server URL: http://localhost:{}{}", PORT, MCP_ENDPOINT);

			// Keep the server running
			tomcat.getServer().await();
		}
		catch (LifecycleException e) {
			logger.error("Failed to start Tomcat server", e);
			throw e;
		}
		finally {
			logger.info("Shutting down MCP server...");
			mcpServer.closeGracefully();
			try {
				tomcat.stop();
				tomcat.destroy();
			}
			catch (LifecycleException e) {
				logger.error("Error during Tomcat shutdown", e);
			}
		}
	}

	private static Tomcat createEmbeddedTomcat(HttpServletStreamableServerTransportProvider transportProvider) {
		Tomcat tomcat = new Tomcat();
		tomcat.setPort(PORT);

		String baseDir = System.getProperty("java.io.tmpdir");
		tomcat.setBaseDir(baseDir);

		Context context = tomcat.addContext("", baseDir);

		// Add the MCP servlet to Tomcat
		org.apache.catalina.Wrapper wrapper = context.createWrapper();
		wrapper.setName("mcpServlet");
		wrapper.setServlet(transportProvider);
		wrapper.setLoadOnStartup(1);
		wrapper.setAsyncSupported(true);
		context.addChild(wrapper);
		context.addServletMappingDecoded("/*", "mcpServlet");

		var connector = tomcat.getConnector();
		connector.setAsyncTimeout(30000);
		return tomcat;
	}

	@SuppressWarnings("deprecation")
	private static List<McpServerFeatures.SyncToolSpecification> createToolSpecs() {
		return List.of(
				// test_simple_text - Returns simple text content
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool.builder("test_simple_text", EMPTY_JSON_SCHEMA)
						.description("Returns simple text content for testing")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_simple_text' called");
						return CallToolResult.builder()
							.content(
									List.of(TextContent.builder("This is a simple text response for testing.").build()))
							.isError(false)
							.build();
					})
					.build(),

				// test_image_content - Returns image content
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool.builder("test_image_content", EMPTY_JSON_SCHEMA)
						.description("Returns image content for testing")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_image_content' called");
						return CallToolResult.builder()
							.content(List.of(ImageContent.builder(RED_PIXEL_PNG, "image/png").build()))
							.isError(false)
							.build();
					})
					.build(),

				// test_audio_content - Returns audio content
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool.builder("test_audio_content", EMPTY_JSON_SCHEMA)
						.description("Returns audio content for testing")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_audio_content' called");
						return CallToolResult.builder()
							.content(List.of(AudioContent.builder(MINIMAL_WAV, "audio/wav").build()))
							.isError(false)
							.build();
					})
					.build(),

				// test_embedded_resource - Returns embedded resource content
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool.builder("test_embedded_resource", EMPTY_JSON_SCHEMA)
						.description("Returns embedded resource content for testing")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_embedded_resource' called");
						TextResourceContents resourceContents = TextResourceContents
							.builder("test://embedded-resource", "This is an embedded resource content.")
							.mimeType("text/plain")
							.build();
						EmbeddedResource embeddedResource = EmbeddedResource.builder(resourceContents).build();
						return CallToolResult.builder().content(List.of(embeddedResource)).isError(false).build();
					})
					.build(),

				// test_multiple_content_types - Returns multiple content types
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool.builder("test_multiple_content_types", EMPTY_JSON_SCHEMA)
						.description("Returns multiple content types for testing")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_multiple_content_types' called");
						TextResourceContents resourceContents = TextResourceContents
							.builder("test://mixed-content-resource", "{\"test\":\"data\",\"value\":123}")
							.mimeType("application/json")
							.build();
						EmbeddedResource embeddedResource = EmbeddedResource.builder(resourceContents).build();
						return CallToolResult.builder()
							.content(List.of(TextContent.builder("Multiple content types test:").build(),
									ImageContent.builder(RED_PIXEL_PNG, "image/png").build(), embeddedResource))
							.isError(false)
							.build();
					})
					.build(),

				// test_tool_with_logging - Tool that sends log messages during execution
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool.builder("test_tool_with_logging", EMPTY_JSON_SCHEMA)
						.description("Tool that sends log messages during execution")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_tool_with_logging' called");
						// Send log notifications
						exchange.loggingNotification(
								LoggingMessageNotification.builder(LoggingLevel.INFO, "Tool execution started")
									.build());
						exchange.loggingNotification(
								LoggingMessageNotification.builder(LoggingLevel.INFO, "Tool processing data").build());
						exchange.loggingNotification(
								LoggingMessageNotification.builder(LoggingLevel.INFO, "Tool execution completed")
									.build());
						return CallToolResult.builder()
							.content(List.of(TextContent.builder("Tool execution completed with logging").build()))
							.isError(false)
							.build();
					})
					.build(),

				// test_error_handling - Tool that always returns an error
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool.builder("test_error_handling", EMPTY_JSON_SCHEMA)
						.description("Tool that returns an error for testing error handling")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_error_handling' called");
						return CallToolResult.builder()
							.content(List.of(TextContent.builder("This tool intentionally returns an error for testing")
								.build()))
							.isError(true)
							.build();
					})
					.build(),

				// test_tool_with_progress - Tool that reports progress
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool.builder("test_tool_with_progress", EMPTY_JSON_SCHEMA)
						.description("Tool that reports progress notifications")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_tool_with_progress' called");
						Object progressToken = request.meta().get("progressToken");
						if (progressToken != null) {
							// Send progress notifications sequentially
							exchange.progressNotification(
									ProgressNotification.builder(progressToken, 0.0).total(100.0).build());
							// try {
							// Thread.sleep(50);
							// }
							// catch (InterruptedException e) {
							// Thread.currentThread().interrupt();
							// }
							exchange.progressNotification(
									ProgressNotification.builder(progressToken, 50.0).total(100.0).build());
							// try {
							// Thread.sleep(50);
							// }
							// catch (InterruptedException e) {
							// Thread.currentThread().interrupt();
							// }
							exchange.progressNotification(
									ProgressNotification.builder(progressToken, 100.0).total(100.0).build());
							return CallToolResult.builder()
								.content(List.of(TextContent.builder("Tool execution completed with progress").build()))
								.isError(false)
								.build();
						}
						else {
							// No progress token, just execute with delays
							// try {
							// Thread.sleep(100);
							// }
							// catch (InterruptedException e) {
							// Thread.currentThread().interrupt();
							// }
							return CallToolResult.builder()
								.content(List
									.of(TextContent.builder("Tool execution completed without progress").build()))
								.isError(false)
								.build();
						}
					})
					.build(),

				// test_sampling - Tool that requests LLM sampling from client
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool
						.builder("test_sampling", Map.of("type", "object", "properties",
								Map.of("prompt",
										Map.of("type", "string", "description", "The prompt to send to the LLM")),
								"required", List.of("prompt")))
						.description("Tool that requests LLM sampling from client")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_sampling' called");
						String prompt = (String) request.arguments().get("prompt");

						// Request sampling from client
						CreateMessageRequest samplingRequest = CreateMessageRequest
							.builder(List
								.of(SamplingMessage.builder(Role.USER, TextContent.builder(prompt).build()).build()),
									100)
							.build();

						CreateMessageResult response = exchange.createMessage(samplingRequest);
						String responseText = "LLM response: " + ((TextContent) response.content()).text();
						return CallToolResult.builder()
							.content(List.of(TextContent.builder(responseText).build()))
							.isError(false)
							.build();
					})
					.build(),

				// test_elicitation - Tool that requests user input from client
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool
						.builder("test_elicitation", Map.of("type", "object", "properties",
								Map.of("message",
										Map.of("type", "string", "description", "The message to show the user")),
								"required", List.of("message")))
						.description("Tool that requests user input from client")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_elicitation' called");
						String message = (String) request.arguments().get("message");

						// Request elicitation from client
						Map<String, Object> requestedSchema = Map.of("type", "object", "properties",
								Map.of("username", Map.of("type", "string", "description", "User's response"), "email",
										Map.of("type", "string", "description", "User's email address")),
								"required", List.of("username", "email"));

						ElicitRequest elicitRequest = ElicitRequest.builder(message, requestedSchema).build();

						ElicitResult response = exchange.createElicitation(elicitRequest);
						String responseText = "User response: action=" + response.action() + ", content="
								+ response.content();
						return CallToolResult.builder()
							.content(List.of(TextContent.builder(responseText).build()))
							.isError(false)
							.build();
					})
					.build(),

				// test_elicitation_sep1034_defaults - Tool with default values for all
				// primitive types
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool.builder("test_elicitation_sep1034_defaults", EMPTY_JSON_SCHEMA)
						.description("Tool that requests elicitation with default values for all primitive types")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_elicitation_sep1034_defaults' called");

						// Create schema with default values for all primitive types
						Map<String, Object> requestedSchema = Map.of("type", "object", "properties",
								Map.of("name", Map.of("type", "string", "default", "John Doe"), "age",
										Map.of("type", "integer", "default", 30), "score",
										Map.of("type", "number", "default", 95.5), "status",
										Map.of("type", "string", "enum", List.of("active", "inactive", "pending"),
												"default", "active"),
										"verified", Map.of("type", "boolean", "default", true)),
								"required", List.of("name", "age", "score", "status", "verified"));

						ElicitRequest elicitRequest = ElicitRequest
							.builder("Please provide your information with defaults", requestedSchema)
							.build();

						ElicitResult response = exchange.createElicitation(elicitRequest);
						String responseText = "Elicitation completed: action=" + response.action() + ", content="
								+ response.content();
						return CallToolResult.builder()
							.content(List.of(TextContent.builder(responseText).build()))
							.isError(false)
							.build();
					})
					.build(),

				// json_schema_2020_12_tool - SEP-1613 dialect/keyword preservation
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool
						.builder("json_schema_2020_12_tool", Map.of("$schema", JSON_SCHEMA_DIALECT_2020_12, "type",
								"object", "$defs",
								Map.of("address",
										Map.of("type", "object", "properties",
												Map.of("street", Map.of("type", "string"), "city",
														Map.of("type", "string")))),
								"properties",
								Map.of("name", Map.of("type", "string"), "address", Map.of("$ref", "#/$defs/address")),
								"additionalProperties", false))
						.description("Tool with JSON Schema 2020-12 features (SEP-1613)")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'json_schema_2020_12_tool' called");
						return CallToolResult.builder()
							.content(List.of(TextContent.builder("ok").build()))
							.isError(false)
							.build();
					})
					.build(),

				// test_elicitation_sep1330_enums - Tool with enum schema improvements
				McpServerFeatures.SyncToolSpecification.builder()
					.tool(Tool.builder("test_elicitation_sep1330_enums", EMPTY_JSON_SCHEMA)
						.description("Tool that requests elicitation with enum schema improvements")
						.build())
					.callHandler((exchange, request) -> {
						logger.info("Tool 'test_elicitation_sep1330_enums' called");

						TypeRef<Map<String, Object>> mapType = new TypeRef<>() {
						};
						var mapper = McpJsonDefaults.getMapper();

						// 1. Untitled single-select
						var untitledSingle = UntitledSingleSelectEnumSchema.builder()
							.enumValues("option1", "option2", "option3")
							.build();
						// 2. Titled single-select using oneOf with const/title
						var titledSingle = TitledSingleSelectEnumSchema.builder()
							.oneOf(new EnumSchemaOption("value1", "First Option"),
									new EnumSchemaOption("value2", "Second Option"),
									new EnumSchemaOption("value3", "Third Option"))
							.build();
						// 3. Legacy titled using enumNames (deprecated)
						var legacyEnum = LegacyTitledEnumSchema.builder()
							.enumValues("opt1", "opt2", "opt3")
							.enumNames("Option One", "Option Two", "Option Three")
							.build();
						// 4. Untitled multi-select
						var untitledMulti = UntitledMultiSelectEnumSchema.builder(
								UntitledMultiSelectItems.builder().enumValues("option1", "option2", "option3").build())
							.build();
						// 5. Titled multi-select using items.anyOf with const/title
						var titledMulti = TitledMultiSelectEnumSchema
							.builder(TitledMultiSelectItems.builder()
								.anyOf(new EnumSchemaOption("value1", "First Choice"),
										new EnumSchemaOption("value2", "Second Choice"),
										new EnumSchemaOption("value3", "Third Choice"))
								.build())
							.build();

						Map<String, Object> requestedSchema = Map.of("type", "object", "properties",
								Map.of("untitledSingle", mapper.convertValue(untitledSingle, mapType), "titledSingle",
										mapper.convertValue(titledSingle, mapType), "legacyEnum",
										mapper.convertValue(legacyEnum, mapType), "untitledMulti",
										mapper.convertValue(untitledMulti, mapType), "titledMulti",
										mapper.convertValue(titledMulti, mapType)),
								"required", List.of("untitledSingle", "titledSingle", "legacyEnum", "untitledMulti",
										"titledMulti"));

						ElicitRequest elicitRequest = ElicitRequest.builder("Select your preferences", requestedSchema)
							.build();

						ElicitResult response = exchange.createElicitation(elicitRequest);
						String responseText = "Elicitation completed: action=" + response.action() + ", content="
								+ response.content();
						return CallToolResult.builder()
							.content(List.of(TextContent.builder(responseText).build()))
							.isError(false)
							.build();
					})
					.build());
	}

	private static List<McpServerFeatures.SyncPromptSpecification> createPromptSpecs() {
		return List.of(
				// test_simple_prompt - Simple prompt without arguments
				new McpServerFeatures.SyncPromptSpecification(Prompt.builder("test_simple_prompt")
					.description("A simple prompt for testing")
					.arguments(List.of())
					.build(), (exchange, request) -> {
						logger.info("Prompt 'test_simple_prompt' requested");
						return GetPromptResult.builder(List.of(PromptMessage
							.builder(Role.USER, TextContent.builder("This is a simple prompt for testing.").build())
							.build())).build();
					}),

				// test_prompt_with_arguments - Prompt with arguments
				new McpServerFeatures.SyncPromptSpecification(Prompt.builder("test_prompt_with_arguments")
					.description("A prompt with arguments for testing")
					.arguments(List.of(
							PromptArgument.builder("arg1").description("First test argument").required(true).build(),
							PromptArgument.builder("arg2").description("Second test argument").required(true).build()))
					.build(), (exchange, request) -> {
						logger.info("Prompt 'test_prompt_with_arguments' requested");
						String arg1 = (String) request.arguments().get("arg1");
						String arg2 = (String) request.arguments().get("arg2");
						String text = String.format("Prompt with arguments: arg1='%s', arg2='%s'", arg1, arg2);
						return GetPromptResult
							.builder(List
								.of(PromptMessage.builder(Role.USER, TextContent.builder(text).build()).build()))
							.build();
					}),

				// test_prompt_with_embedded_resource - Prompt with embedded resource
				new McpServerFeatures.SyncPromptSpecification(Prompt.builder("test_prompt_with_embedded_resource")
					.description("A prompt with embedded resource for testing")
					.arguments(List.of(PromptArgument.builder("resourceUri")
						.description("URI of the resource to embed")
						.required(true)
						.build()))
					.build(), (exchange, request) -> {
						logger.info("Prompt 'test_prompt_with_embedded_resource' requested");
						String resourceUri = (String) request.arguments().get("resourceUri");
						TextResourceContents resourceContents = TextResourceContents
							.builder(resourceUri, "Embedded resource content for testing.")
							.mimeType("text/plain")
							.build();
						EmbeddedResource embeddedResource = EmbeddedResource.builder(resourceContents).build();
						return GetPromptResult
							.builder(List.of(PromptMessage.builder(Role.USER, embeddedResource).build(),
									PromptMessage
										.builder(Role.USER,
												TextContent.builder("Please process the embedded resource above.")
													.build())
										.build()))
							.build();
					}),

				// test_prompt_with_image - Prompt with image content
				new McpServerFeatures.SyncPromptSpecification(Prompt.builder("test_prompt_with_image")
					.description("A prompt with image content for testing")
					.arguments(List.of())
					.build(), (exchange, request) -> {
						logger.info("Prompt 'test_prompt_with_image' requested");
						return GetPromptResult.builder(List.of(
								PromptMessage
									.builder(Role.USER, ImageContent.builder(RED_PIXEL_PNG, "image/png").build())
									.build(),
								PromptMessage
									.builder(Role.USER, TextContent.builder("Please analyze the image above.").build())
									.build()))
							.build();
					}));
	}

	private static List<McpServerFeatures.SyncResourceSpecification> createResourceSpecs() {
		return List.of(
				// test://static-text - Static text resource
				new McpServerFeatures.SyncResourceSpecification(
						Resource.builder("test://static-text", "Static Text Resource")
							.description("A static text resource for testing")
							.mimeType("text/plain")
							.build(),
						(exchange, request) -> {
							logger.info("Resource 'test://static-text' requested");
							return ReadResourceResult.builder(List.of(TextResourceContents
								.builder("test://static-text", "This is the content of the static text resource.")
								.mimeType("text/plain")
								.build())).build();
						}),

				// test://static-binary - Static binary resource (image)
				new McpServerFeatures.SyncResourceSpecification(
						Resource.builder("test://static-binary", "Static Binary Resource")
							.description("A static binary resource for testing")
							.mimeType("image/png")
							.build(),
						(exchange, request) -> {
							logger.info("Resource 'test://static-binary' requested");
							return ReadResourceResult
								.builder(List.of(BlobResourceContents.builder("test://static-binary", RED_PIXEL_PNG)
									.mimeType("image/png")
									.build()))
								.build();
						}),

				// test://watched-resource - Resource that can be subscribed to
				new McpServerFeatures.SyncResourceSpecification(
						Resource.builder("test://watched-resource", "Watched Resource")
							.description("A resource that can be subscribed to for updates")
							.mimeType("text/plain")
							.build(),
						(exchange, request) -> {
							logger.info("Resource 'test://watched-resource' requested");
							return ReadResourceResult.builder(List.of(TextResourceContents
								.builder("test://watched-resource", "This is a watched resource content.")
								.mimeType("text/plain")
								.build())).build();
						}));
	}

	private static List<McpServerFeatures.SyncResourceTemplateSpecification> createResourceTemplateSpecs() {
		return List.of(
				// test://template/{id}/data - Resource template with parameter
				// substitution
				new McpServerFeatures.SyncResourceTemplateSpecification(
						ResourceTemplate.builder("test://template/{id}/data", "Template Resource")
							.description("A resource template for testing parameter substitution")
							.mimeType("application/json")
							.build(),
						(exchange, request) -> {
							logger.info("Resource template 'test://template/{{id}}/data' requested for URI: {}",
									request.uri());
							// Extract id from URI
							String uri = request.uri();
							String id = uri.replaceAll("test://template/(.+)/data", "$1");
							String jsonContent = String
								.format("{\"id\":\"%s\",\"templateTest\":true,\"data\":\"Data for ID: %s\"}", id, id);
							return ReadResourceResult.builder(List.of(TextResourceContents.builder(uri, jsonContent)
								.mimeType("application/json")
								.build())).build();
						}));
	}

	private static List<McpServerFeatures.SyncCompletionSpecification> createCompletionSpecs() {
		return List.of(
				// Completion for test_prompt_with_arguments
				new McpServerFeatures.SyncCompletionSpecification(new PromptReference("test_prompt_with_arguments"),
						(exchange, request) -> {
							logger.info("Completion requested for prompt 'test_prompt_with_arguments', argument: {}",
									request.argument().name());
							// Return minimal completion with required fields
							return new CompleteResult(new CompleteResult.CompleteCompletion(List.of(), 0, false));
						}));
	}

}
