diff --git a/deployment/src/test/java/io/quarkiverse/mcp/server/test/serverinfo/CustomServerInfoTest.java b/deployment/src/test/java/io/quarkiverse/mcp/server/test/serverinfo/CustomServerInfoTest.java new file mode 100644 index 0000000..683cbfe --- /dev/null +++ b/deployment/src/test/java/io/quarkiverse/mcp/server/test/serverinfo/CustomServerInfoTest.java @@ -0,0 +1,36 @@ +package io.quarkiverse.mcp.server.test.serverinfo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.net.URISyntaxException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkiverse.mcp.server.test.McpClient; +import io.quarkiverse.mcp.server.test.McpServerTest; +import io.quarkus.test.QuarkusUnitTest; +import io.vertx.core.json.JsonObject; + +public class CustomServerInfoTest extends McpServerTest { + + private static final String NAME = "Foo"; + private static final String VERSION = "1.0"; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(root -> root.addClasses(McpClient.class)) + .overrideConfigKey("quarkus.mcp-server.server-info.name", NAME) + .overrideConfigKey("quarkus.mcp-server.server-info.version", VERSION); + + @Test + public void testServerInfo() throws URISyntaxException { + initClient(result -> { + JsonObject serverInfo = result.getJsonObject("serverInfo"); + assertNotNull(serverInfo); + assertEquals(NAME, serverInfo.getString("name")); + assertEquals(VERSION, serverInfo.getString("version")); + }); + } +} diff --git a/deployment/src/test/java/io/quarkiverse/mcp/server/test/serverinfo/DefaultServerInfoTest.java b/deployment/src/test/java/io/quarkiverse/mcp/server/test/serverinfo/DefaultServerInfoTest.java new file mode 100644 index 0000000..2b041b4 --- /dev/null +++ b/deployment/src/test/java/io/quarkiverse/mcp/server/test/serverinfo/DefaultServerInfoTest.java @@ -0,0 +1,31 @@ +package io.quarkiverse.mcp.server.test.serverinfo; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.net.URISyntaxException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkiverse.mcp.server.test.McpClient; +import io.quarkiverse.mcp.server.test.McpServerTest; +import io.quarkus.test.QuarkusUnitTest; +import io.vertx.core.json.JsonObject; + +public class DefaultServerInfoTest extends McpServerTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(root -> root.addClasses(McpClient.class)); + + @Test + public void testServerInfo() throws URISyntaxException { + initClient(result -> { + JsonObject serverInfo = result.getJsonObject("serverInfo"); + assertNotNull(serverInfo); + assertEquals("quarkus-mcp-server-deployment", serverInfo.getString("name")); + assertEquals("999-SNAPSHOT", serverInfo.getString("version")); + }); + } +} diff --git a/docs/modules/ROOT/pages/includes/quarkus-mcp-server.adoc b/docs/modules/ROOT/pages/includes/quarkus-mcp-server.adoc index dd925a5..5daf3cd 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-mcp-server.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-mcp-server.adoc @@ -24,5 +24,43 @@ endif::add-copy-button-to-env-var[] |string |`/mcp` +a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-mcp-server-server-info-name]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-server-info-name[`quarkus.mcp-server.server-info.name`]## + +[.description] +-- +The name of the server is included in the response to an `initialize` request as defined by the +https://spec.modelcontextprotocol.io/specification/basic/lifecycle/#initialization[spec]. +By default, the value of the `quarkus.application.name` config property is used. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_SERVER_INFO_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MCP_SERVER_SERVER_INFO_NAME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-mcp-server-server-info-version]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-server-info-version[`quarkus.mcp-server.server-info.version`]## + +[.description] +-- +The version of the server is included in the response to an `initialize` request as defined by the +https://spec.modelcontextprotocol.io/specification/basic/lifecycle/#initialization[spec]. +By default, the value of the `quarkus.application.version` config property is used. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_SERVER_INFO_VERSION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MCP_SERVER_SERVER_INFO_VERSION+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + |=== diff --git a/docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.mcp-server.adoc b/docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.mcp-server.adoc index dd925a5..5daf3cd 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.mcp-server.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.mcp-server.adoc @@ -24,5 +24,43 @@ endif::add-copy-button-to-env-var[] |string |`/mcp` +a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-mcp-server-server-info-name]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-server-info-name[`quarkus.mcp-server.server-info.name`]## + +[.description] +-- +The name of the server is included in the response to an `initialize` request as defined by the +https://spec.modelcontextprotocol.io/specification/basic/lifecycle/#initialization[spec]. +By default, the value of the `quarkus.application.name` config property is used. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_SERVER_INFO_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MCP_SERVER_SERVER_INFO_NAME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-mcp-server-server-info-version]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-server-info-version[`quarkus.mcp-server.server-info.version`]## + +[.description] +-- +The version of the server is included in the response to an `initialize` request as defined by the +https://spec.modelcontextprotocol.io/specification/basic/lifecycle/#initialization[spec]. +By default, the value of the `quarkus.application.version` config property is used. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_SERVER_INFO_VERSION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MCP_SERVER_SERVER_INFO_VERSION+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + |=== diff --git a/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpBuildTimeConfig.java b/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpBuildTimeConfig.java index 911bd34..02df5b8 100644 --- a/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpBuildTimeConfig.java +++ b/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpBuildTimeConfig.java @@ -1,5 +1,7 @@ package io.quarkiverse.mcp.server.runtime; +import java.util.Optional; + import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; import io.smallrye.config.ConfigMapping; @@ -17,4 +19,34 @@ public interface McpBuildTimeConfig { @WithDefault("/mcp") String rootPath(); + /** + * The server info is included in the response to an `initialize` request as defined by the + * https://spec.modelcontextprotocol.io/specification/basic/lifecycle/#initialization[spec]. + * + * @asciidoclet + */ + ServerInfo serverInfo(); + + public interface ServerInfo { + + /** + * The name of the server is included in the response to an `initialize` request as defined by the + * https://spec.modelcontextprotocol.io/specification/basic/lifecycle/#initialization[spec]. + * By default, the value of the `quarkus.application.name` config property is used. + * + * @asciidoclet + */ + Optional name(); + + /** + * The version of the server is included in the response to an `initialize` request as defined by the + * https://spec.modelcontextprotocol.io/specification/basic/lifecycle/#initialization[spec]. + * By default, the value of the `quarkus.application.version` config property is used. + * + * @asciidoclet + */ + Optional version(); + + } + } diff --git a/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpMessagesHandler.java b/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpMessagesHandler.java index c4480db..9b9f231 100644 --- a/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpMessagesHandler.java +++ b/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpMessagesHandler.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; +import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; import io.quarkiverse.mcp.server.ClientCapability; @@ -27,10 +28,13 @@ class McpMessagesHandler implements Handler { private final PromptMessageHandler promptHandler; - McpMessagesHandler(ConnectionManager connectionManager) { + private final McpBuildTimeConfig config; + + McpMessagesHandler(ConnectionManager connectionManager, McpBuildTimeConfig config) { this.connectionManager = connectionManager; this.toolHandler = new ToolMessageHandler(); this.promptHandler = new PromptMessageHandler(); + this.config = config; } @Override @@ -165,8 +169,14 @@ static JsonObject newResult(Object id, Object result) { private Map serverInfo() { Map info = new HashMap<>(); info.put("protocolVersion", "2024-11-05"); - info.put("serverInfo", Map.of("name", "Foo", "version", "999-SNAPSHOT")); - info.put("capabilities", Map.of("prompts", Map.of())); + + String serverName = config.serverInfo().name() + .orElse(ConfigProvider.getConfig().getOptionalValue("quarkus.application.name", String.class).orElse("N/A")); + String serverVersion = config.serverInfo().version() + .orElse(ConfigProvider.getConfig().getOptionalValue("quarkus.application.version", String.class).orElse("N/A")); + info.put("serverInfo", Map.of("name", serverName, "version", serverVersion)); + + info.put("capabilities", Map.of("prompts", Map.of(), "tools", Map.of())); return info; } } diff --git a/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpServerRecorder.java b/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpServerRecorder.java index 9e7a76d..6c6c4e7 100644 --- a/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpServerRecorder.java +++ b/runtime/src/main/java/io/quarkiverse/mcp/server/runtime/McpServerRecorder.java @@ -20,6 +20,12 @@ public class McpServerRecorder { private static final Logger LOG = Logger.getLogger(McpServerRecorder.class); + private final McpBuildTimeConfig config; + + public McpServerRecorder(McpBuildTimeConfig config) { + this.config = config; + } + public Handler createSseEndpointHandler(String mcpPath) { ArcContainer container = Arc.container(); @@ -68,7 +74,7 @@ public void accept(Route route) { } public Handler createMessagesEndpointHandler() { - return new McpMessagesHandler(Arc.container().instance(ConnectionManager.class).get()); + return new McpMessagesHandler(Arc.container().instance(ConnectionManager.class).get(), config); } }