From aa4ae1e8a24eccb7d67414a50e5418a7ab13e5e3 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Wed, 8 Jan 2025 18:13:53 +0100 Subject: [PATCH] Stdio: always disable console logging, null system out - also update docs --- .../includes/quarkus-mcp-server-stdio.adoc | 28 +++++ .../quarkus-mcp-server-stdio_quarkus.mcp.adoc | 28 +++++ .../pages/includes/quarkus-mcp-server.adoc | 100 ------------------ .../includes/quarkus-mcp-server_quarkus.adoc | 100 ------------------ ...quarkus-mcp-server_quarkus.mcp-server.adoc | 66 ------------ .../quarkus-mcp-server_quarkus.mcp.adoc | 100 ------------------ docs/modules/ROOT/pages/index.adoc | 37 +++++-- .../deployment/StdioMcpServerProcessor.java | 8 +- .../src/main/resources/application.properties | 2 +- .../runtime/StdioConfigBuilderCustomizer.java | 25 +++++ .../runtime/StdioMcpConnection.java | 2 +- .../runtime/StdioMcpMessageHandler.java | 6 +- .../runtime/StdioMcpServerRecorder.java | 18 +++- .../runtime/config/McpStdioRuntimeConfig.java | 20 ++++ ...rye.config.SmallRyeConfigBuilderCustomizer | 1 + 15 files changed, 150 insertions(+), 391 deletions(-) create mode 100644 docs/modules/ROOT/pages/includes/quarkus-mcp-server-stdio.adoc create mode 100644 docs/modules/ROOT/pages/includes/quarkus-mcp-server-stdio_quarkus.mcp.adoc delete mode 100644 docs/modules/ROOT/pages/includes/quarkus-mcp-server.adoc delete mode 100644 docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.adoc delete mode 100644 docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.mcp-server.adoc delete mode 100644 docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.mcp.adoc create mode 100644 transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioConfigBuilderCustomizer.java rename transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/{sse => stdio}/runtime/StdioMcpConnection.java (79%) rename transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/{sse => stdio}/runtime/StdioMcpMessageHandler.java (94%) rename transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/{sse => stdio}/runtime/StdioMcpServerRecorder.java (61%) create mode 100644 transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/config/McpStdioRuntimeConfig.java create mode 100644 transports/stdio/runtime/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer diff --git a/docs/modules/ROOT/pages/includes/quarkus-mcp-server-stdio.adoc b/docs/modules/ROOT/pages/includes/quarkus-mcp-server-stdio.adoc new file mode 100644 index 0000000..89b41a8 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-mcp-server-stdio.adoc @@ -0,0 +1,28 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a| [[quarkus-mcp-server-stdio_quarkus-mcp-server-stdio-null-system-out]] [.property-path]##link:#quarkus-mcp-server-stdio_quarkus-mcp-server-stdio-null-system-out[`quarkus.mcp.server.stdio.null-system-out`]## + +[.description] +-- +If set to `true` then the standard output stream is set to "null" when the app is started. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_STDIO_NULL_SYSTEM_OUT+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MCP_SERVER_STDIO_NULL_SYSTEM_OUT+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`true` + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-mcp-server-stdio_quarkus.mcp.adoc b/docs/modules/ROOT/pages/includes/quarkus-mcp-server-stdio_quarkus.mcp.adoc new file mode 100644 index 0000000..89b41a8 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-mcp-server-stdio_quarkus.mcp.adoc @@ -0,0 +1,28 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a| [[quarkus-mcp-server-stdio_quarkus-mcp-server-stdio-null-system-out]] [.property-path]##link:#quarkus-mcp-server-stdio_quarkus-mcp-server-stdio-null-system-out[`quarkus.mcp.server.stdio.null-system-out`]## + +[.description] +-- +If set to `true` then the standard output stream is set to "null" when the app is started. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_STDIO_NULL_SYSTEM_OUT+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MCP_SERVER_STDIO_NULL_SYSTEM_OUT+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`true` + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-mcp-server.adoc b/docs/modules/ROOT/pages/includes/quarkus-mcp-server.adoc deleted file mode 100644 index b93c9ce..0000000 --- a/docs/modules/ROOT/pages/includes/quarkus-mcp-server.adoc +++ /dev/null @@ -1,100 +0,0 @@ -[.configuration-legend] -icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime -[.configuration-reference.searchable, cols="80,.^10,.^10"] -|=== - -h|[.header-title]##Configuration property## -h|Type -h|Default - -a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-mcp-server-root-path]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-root-path[`quarkus.mcp.server.root-path`]## - -[.description] --- -The root path. - - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_ROOT_PATH+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_MCP_SERVER_ROOT_PATH+++` -endif::add-copy-button-to-env-var[] --- -|string -|`/mcp` - -a| [[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| [[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 -| - -a| [[quarkus-mcp-server_quarkus-mcp-server-traffic-logging-enabled]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-traffic-logging-enabled[`quarkus.mcp.server.traffic-logging.enabled`]## - -[.description] --- -If set to true then JSON messages received/sent are logged. - - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_TRAFFIC_LOGGING_ENABLED+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_MCP_SERVER_TRAFFIC_LOGGING_ENABLED+++` -endif::add-copy-button-to-env-var[] --- -|boolean -|`false` - -a| [[quarkus-mcp-server_quarkus-mcp-server-traffic-logging-text-limit]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-traffic-logging-text-limit[`quarkus.mcp.server.traffic-logging.text-limit`]## - -[.description] --- -The number of characters of a text message which will be logged if traffic logging is enabled. - - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_TRAFFIC_LOGGING_TEXT_LIMIT+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_MCP_SERVER_TRAFFIC_LOGGING_TEXT_LIMIT+++` -endif::add-copy-button-to-env-var[] --- -|int -|`100` - -|=== - diff --git a/docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.adoc b/docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.adoc deleted file mode 100644 index 862400e..0000000 --- a/docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.adoc +++ /dev/null @@ -1,100 +0,0 @@ -[.configuration-legend] -icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime -[.configuration-reference.searchable, cols="80,.^10,.^10"] -|=== - -h|[.header-title]##Configuration property## -h|Type -h|Default - -a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-server-info-name]] [.property-path]##link:#quarkus-mcp-server_quarkus-server-info-name[`quarkus.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_SERVER_INFO_NAME+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_SERVER_INFO_NAME+++` -endif::add-copy-button-to-env-var[] --- -|string -| - -a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-server-info-version]] [.property-path]##link:#quarkus-mcp-server_quarkus-server-info-version[`quarkus.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_SERVER_INFO_VERSION+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_SERVER_INFO_VERSION+++` -endif::add-copy-button-to-env-var[] --- -|string -| - -a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-traffic-logging-enabled]] [.property-path]##link:#quarkus-mcp-server_quarkus-traffic-logging-enabled[`quarkus.traffic-logging.enabled`]## - -[.description] --- -If set to true then JSON messages received/sent are logged if the `DEBUG` level is enabled for the logger `io.quarkus.mcp.server.traffic`. - - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_TRAFFIC_LOGGING_ENABLED+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_TRAFFIC_LOGGING_ENABLED+++` -endif::add-copy-button-to-env-var[] --- -|boolean -|`false` - -a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-traffic-logging-text-limit]] [.property-path]##link:#quarkus-mcp-server_quarkus-traffic-logging-text-limit[`quarkus.traffic-logging.text-limit`]## - -[.description] --- -The number of characters of a text message which will be logged if traffic logging is enabled. - - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_TRAFFIC_LOGGING_TEXT_LIMIT+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_TRAFFIC_LOGGING_TEXT_LIMIT+++` -endif::add-copy-button-to-env-var[] --- -|int -|`100` - -a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-root-path]] [.property-path]##link:#quarkus-mcp-server_quarkus-root-path[`quarkus.root-path`]## - -[.description] --- -The root path. - - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_ROOT_PATH+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_ROOT_PATH+++` -endif::add-copy-button-to-env-var[] --- -|string -|`/mcp` - -|=== - 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 deleted file mode 100644 index 5daf3cd..0000000 --- a/docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.mcp-server.adoc +++ /dev/null @@ -1,66 +0,0 @@ -[.configuration-legend] -icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime -[.configuration-reference.searchable, cols="80,.^10,.^10"] -|=== - -h|[.header-title]##Configuration property## -h|Type -h|Default - -a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-mcp-server-root-path]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-root-path[`quarkus.mcp-server.root-path`]## - -[.description] --- -The root path. - - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_ROOT_PATH+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_MCP_SERVER_ROOT_PATH+++` -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.adoc b/docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.mcp.adoc deleted file mode 100644 index b93c9ce..0000000 --- a/docs/modules/ROOT/pages/includes/quarkus-mcp-server_quarkus.mcp.adoc +++ /dev/null @@ -1,100 +0,0 @@ -[.configuration-legend] -icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime -[.configuration-reference.searchable, cols="80,.^10,.^10"] -|=== - -h|[.header-title]##Configuration property## -h|Type -h|Default - -a|icon:lock[title=Fixed at build time] [[quarkus-mcp-server_quarkus-mcp-server-root-path]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-root-path[`quarkus.mcp.server.root-path`]## - -[.description] --- -The root path. - - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_ROOT_PATH+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_MCP_SERVER_ROOT_PATH+++` -endif::add-copy-button-to-env-var[] --- -|string -|`/mcp` - -a| [[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| [[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 -| - -a| [[quarkus-mcp-server_quarkus-mcp-server-traffic-logging-enabled]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-traffic-logging-enabled[`quarkus.mcp.server.traffic-logging.enabled`]## - -[.description] --- -If set to true then JSON messages received/sent are logged. - - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_TRAFFIC_LOGGING_ENABLED+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_MCP_SERVER_TRAFFIC_LOGGING_ENABLED+++` -endif::add-copy-button-to-env-var[] --- -|boolean -|`false` - -a| [[quarkus-mcp-server_quarkus-mcp-server-traffic-logging-text-limit]] [.property-path]##link:#quarkus-mcp-server_quarkus-mcp-server-traffic-logging-text-limit[`quarkus.mcp.server.traffic-logging.text-limit`]## - -[.description] --- -The number of characters of a text message which will be logged if traffic logging is enabled. - - -ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_MCP_SERVER_TRAFFIC_LOGGING_TEXT_LIMIT+++[] -endif::add-copy-button-to-env-var[] -ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_MCP_SERVER_TRAFFIC_LOGGING_TEXT_LIMIT+++` -endif::add-copy-button-to-env-var[] --- -|int -|`100` - -|=== - diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 7230cdf..6292558 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -6,31 +6,46 @@ _"https://modelcontextprotocol.io/[Model Context Protocol] (MCP) is an open pro This extension provides a declarative API that enables developers to implement the MCP server features easily. -IMPORTANT: Currently, only the https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse[HTTP/SSE] transport is supported. +== Supported transports -== Installation +MCP currently defines two standard transports for client-server communication. +This extension supports both transports and defines a unified declarative API. +In other words, the server features are defined with the same API but the selected transport determines the way the MCP server communicates with clients. -If you want to use this extension, you need to add the `io.quarkiverse.mcp:quarkus-mcp-server` extension to your build file first. +If you want to use the https://spec.modelcontextprotocol.io/specification/basic/transports/#stdio[stdio] transport you'll need to add the `io.quarkiverse.mcp:quarkus-mcp-server-stdio` extension to your build file first. +For instance, with Maven, add the following dependency to your POM file: +[source,xml,subs=attributes+] +---- + + io.quarkiverse.mcp + quarkus-mcp-server-stdio + {project-version} + +---- + +IMPORTANT: If you use the `stdio` transport then your app should not write anything to the standard output. Logging in the console is automatically disabled. And the standard output stream is set to "null" when the app is started by default. + +If you want to use the https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse[HTTP/SSE] transport you'll need to add the `io.quarkiverse.mcp:quarkus-mcp-server-sse` extension to your build file first. For instance, with Maven, add the following dependency to your POM file: [source,xml,subs=attributes+] ---- io.quarkiverse.mcp - quarkus-mcp-server + quarkus-mcp-server-sse {project-version} ---- -== Supported Server Features +== Supported server features An https://spec.modelcontextprotocol.io/specification/server/[MCP server] provides some building blocks to enrich the context of language models in AI apps. In this extension, a _server feature_ (prompt, resource, tool) is represented by an _annotated business method_ of a CDI bean. The execution model and context handling follow the idiomatic approach used in fundamental Quarkus extensions (such as `quarkus-rest` and `quarkus-scheduler`). For example, when a server feature is executed the CDI request context is activated and a https://quarkus.io/guides/duplicated-context[Vert.x duplicated context] is created. -=== Execution Model +=== Execution model A server feature method may use blocking or non-blocking logic. The execution model is determined by the method signature and additional annotations such as `@Blocking` and `@NonBlocking`. @@ -165,7 +180,7 @@ However, the annotated method can also return other types that are converted acc * If the method returns a `List` of `Content` implementations then the reponse is "success" and contains the list of content objects. * The method may return a `Uni` that wraps any of the type mentioned above. -== Traffic Logging +== Traffic logging The extension can log JSON messages sent and received for debugging purposes. To enable traffic logging, set the `quarkus.mcp.server.traffic-logging.enabled` configuration property to `true`. @@ -182,8 +197,10 @@ quarkus.mcp.server.traffic-logging.text-limit=50 <2> <2> Set the number of characters of a JSON message which will be logged. [[extension-configuration-reference]] -== Extension Configuration Reference +== Extension configuration reference + +include::includes/quarkus-mcp-server-core.adoc[leveloffset=+1, opts=optional] -TIP: Remove this section if you don't have Quarkus configuration properties in your extension. +include::includes/quarkus-mcp-server-stdio.adoc[leveloffset=+1, opts=optional] -include::includes/quarkus-mcp-server.adoc[leveloffset=+1, opts=optional] +include::includes/quarkus-mcp-server-sse.adoc[leveloffset=+1, opts=optional] diff --git a/transports/stdio/deployment/src/main/java/io/quarkiverse/mcp/server/stdio/deployment/StdioMcpServerProcessor.java b/transports/stdio/deployment/src/main/java/io/quarkiverse/mcp/server/stdio/deployment/StdioMcpServerProcessor.java index 6b43943..0891950 100644 --- a/transports/stdio/deployment/src/main/java/io/quarkiverse/mcp/server/stdio/deployment/StdioMcpServerProcessor.java +++ b/transports/stdio/deployment/src/main/java/io/quarkiverse/mcp/server/stdio/deployment/StdioMcpServerProcessor.java @@ -2,13 +2,12 @@ import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; -import io.quarkiverse.mcp.server.sse.runtime.StdioMcpServerRecorder; +import io.quarkiverse.mcp.server.stdio.runtime.StdioMcpServerRecorder; import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.Consume; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.FeatureBuildItem; -import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem; public class StdioMcpServerProcessor { @@ -24,9 +23,4 @@ void initialize(StdioMcpServerRecorder recorder) { recorder.initialize(); } - @BuildStep - RunTimeConfigurationDefaultBuildItem disableConsoleLogging() { - return new RunTimeConfigurationDefaultBuildItem("quarkus.log.console.enable", "false"); - } - } diff --git a/transports/stdio/integration-tests/src/main/resources/application.properties b/transports/stdio/integration-tests/src/main/resources/application.properties index 4e8915d..d74d691 100644 --- a/transports/stdio/integration-tests/src/main/resources/application.properties +++ b/transports/stdio/integration-tests/src/main/resources/application.properties @@ -1 +1 @@ -quarkus.log.console.enable=false \ No newline at end of file +#quarkus.log.console.enable=false \ No newline at end of file diff --git a/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioConfigBuilderCustomizer.java b/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioConfigBuilderCustomizer.java new file mode 100644 index 0000000..f2ef547 --- /dev/null +++ b/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioConfigBuilderCustomizer.java @@ -0,0 +1,25 @@ +package io.quarkiverse.mcp.server.stdio.runtime; + +import java.util.Map; + +import io.smallrye.config.PropertiesConfigSource; +import io.smallrye.config.SmallRyeConfigBuilder; +import io.smallrye.config.SmallRyeConfigBuilderCustomizer; + +/** + * Always disable console logging. + *

+ * Originally, we tried to use the {@code RunTimeConfigurationDefaultBuildItem}. However, this build item has no effect unless a + * Quarkus version with a fix is used. + */ +public class StdioConfigBuilderCustomizer implements SmallRyeConfigBuilderCustomizer { + + @Override + public void configBuilder(SmallRyeConfigBuilder builder) { + builder.withSources( + new PropertiesConfigSource( + Map.of("quarkus.log.console.enable", "false"), + "mcp-stdio-config-source", 500)); + } + +} diff --git a/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/sse/runtime/StdioMcpConnection.java b/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioMcpConnection.java similarity index 79% rename from transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/sse/runtime/StdioMcpConnection.java rename to transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioMcpConnection.java index ea53c3e..41695d4 100644 --- a/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/sse/runtime/StdioMcpConnection.java +++ b/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioMcpConnection.java @@ -1,4 +1,4 @@ -package io.quarkiverse.mcp.server.sse.runtime; +package io.quarkiverse.mcp.server.stdio.runtime; import io.quarkiverse.mcp.server.runtime.McpConnectionBase; diff --git a/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/sse/runtime/StdioMcpMessageHandler.java b/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioMcpMessageHandler.java similarity index 94% rename from transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/sse/runtime/StdioMcpMessageHandler.java rename to transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioMcpMessageHandler.java index 71705e8..73dbf14 100644 --- a/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/sse/runtime/StdioMcpMessageHandler.java +++ b/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioMcpMessageHandler.java @@ -1,4 +1,4 @@ -package io.quarkiverse.mcp.server.sse.runtime; +package io.quarkiverse.mcp.server.stdio.runtime; import java.io.BufferedReader; import java.io.IOException; @@ -34,8 +34,8 @@ protected StdioMcpMessageHandler(McpRuntimeConfig config, ConnectionManager conn this.executor = Executors.newSingleThreadExecutor(); } - void initialize() { - StdioResponder responder = new StdioResponder(System.out); + void initialize(PrintStream stdout) { + StdioResponder responder = new StdioResponder(stdout); StdioMcpConnection connection = new StdioMcpConnection( Base64.getUrlEncoder().encodeToString(UUID.randomUUID().toString().getBytes())); InputStream in = System.in; diff --git a/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/sse/runtime/StdioMcpServerRecorder.java b/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioMcpServerRecorder.java similarity index 61% rename from transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/sse/runtime/StdioMcpServerRecorder.java rename to transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioMcpServerRecorder.java index 675879a..39ca510 100644 --- a/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/sse/runtime/StdioMcpServerRecorder.java +++ b/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/StdioMcpServerRecorder.java @@ -1,10 +1,14 @@ -package io.quarkiverse.mcp.server.sse.runtime; +package io.quarkiverse.mcp.server.stdio.runtime; + +import java.io.OutputStream; +import java.io.PrintStream; import io.quarkiverse.mcp.server.runtime.ConnectionManager; import io.quarkiverse.mcp.server.runtime.PromptManager; import io.quarkiverse.mcp.server.runtime.ResourceManager; import io.quarkiverse.mcp.server.runtime.ToolManager; import io.quarkiverse.mcp.server.runtime.config.McpRuntimeConfig; +import io.quarkiverse.mcp.server.stdio.runtime.config.McpStdioRuntimeConfig; import io.quarkus.arc.Arc; import io.quarkus.arc.ArcContainer; import io.quarkus.runtime.annotations.Recorder; @@ -14,17 +18,25 @@ public class StdioMcpServerRecorder { private final McpRuntimeConfig config; - public StdioMcpServerRecorder(McpRuntimeConfig config) { + private final McpStdioRuntimeConfig stdioConfig; + + public StdioMcpServerRecorder(McpRuntimeConfig config, McpStdioRuntimeConfig stdioConfig) { this.config = config; + this.stdioConfig = stdioConfig; } public void initialize() { + PrintStream stdout = System.out; + if (stdioConfig.nullSystemOut()) { + System.setOut(new PrintStream(OutputStream.nullOutputStream())); + } + ArcContainer container = Arc.container(); StdioMcpMessageHandler messageHandler = new StdioMcpMessageHandler(config, container.instance(ConnectionManager.class).get(), container.instance(PromptManager.class).get(), container.instance(ToolManager.class).get(), container.instance(ResourceManager.class).get()); - messageHandler.initialize(); + messageHandler.initialize(stdout); } } diff --git a/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/config/McpStdioRuntimeConfig.java b/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/config/McpStdioRuntimeConfig.java new file mode 100644 index 0000000..82e3eb3 --- /dev/null +++ b/transports/stdio/runtime/src/main/java/io/quarkiverse/mcp/server/stdio/runtime/config/McpStdioRuntimeConfig.java @@ -0,0 +1,20 @@ +package io.quarkiverse.mcp.server.stdio.runtime.config; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +@ConfigMapping(prefix = "quarkus.mcp.server.stdio") +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +public interface McpStdioRuntimeConfig { + + /** + * If set to `true` then the standard output stream is set to "null" when the app is started. + * + * @asciidoclet + */ + @WithDefault("true") + boolean nullSystemOut(); + +} diff --git a/transports/stdio/runtime/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer b/transports/stdio/runtime/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer new file mode 100644 index 0000000..a8c00c1 --- /dev/null +++ b/transports/stdio/runtime/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer @@ -0,0 +1 @@ +io.quarkiverse.mcp.server.stdio.runtime.StdioConfigBuilderCustomizer \ No newline at end of file