Quarkus MCP Server
"Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools."
This extension provides a declarative API that enables developers to implement the MCP server features easily.
Currently, only the HTTP/SSE transport is supported. |
Installation
If you want to use this extension, you need to add the io.quarkiverse.mcp:quarkus-mcp-server
extension to your build file first.
For instance, with Maven, add the following dependency to your POM file:
<dependency>
<groupId>io.quarkiverse.mcp</groupId>
<artifactId>quarkus-mcp-server</artifactId>
<version>1.0.0.Alpha1</version>
</dependency>
Supported Server Features
An 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 Vert.x duplicated context is created.
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
.
-
Methods annotated with
@RunOnVirtualThread
,@Blocking
or@Transactional
are considered blocking. -
Methods declared in a class annotated with
@RunOnVirtualThread
are considered blocking. -
Methods annotated with
@NonBlocking
are considered non-blocking. -
Methods declared in a class annotated with
@Transactional
are considered blocking unless annotated with@NonBlocking
. -
If the method does not declare any of the annotations listed above the execution model is derived from the return type:
-
Methods returning
Uni
andMulti
are considered non-blocking. -
Methods returning any other type are considered blocking.
-
-
Kotlin
suspend
functions are always considered non-blocking and may not be annotated with@Blocking
,@NonBlocking
or@RunOnVirtualThread
and may not be in a class annotated with@RunOnVirtualThread
. -
Non-blocking methods must execute on the connection’s event loop thread.
-
Blocking methods must execute on a worker thread unless annotated with
@RunOnVirtualThread
or in a class annotated with@RunOnVirtualThread
. -
Methods annotated with
@RunOnVirtualThread
or declared in class annotated with@RunOnVirtualThread
must execute on a virtual thread, each invocation spawns a new virtual thread.
Prompts
MCP provides a standardized way for servers to expose prompt templates to clients.
import io.quarkiverse.mcp.server.Prompt;
import io.quarkiverse.mcp.server.PromptArg;
import io.quarkiverse.mcp.server.PromptMessage;
import jakarta.inject.Inject;
// @Singleton (1)
public class MyPrompts {
@Inject (2)
FooService fooService;
@Prompt(description = "Put you description here.") (3)
PromptMessage foo(@PromptArg(description = "The name") String name) { (4)
return PromptMessage.withUserRole(new TextContent(fooService.ping(name));
}
}
1 | The @Singleton scope is added automatically, if needed. |
2 | MyPrompts is an ordinary CDI bean. It can inject other beans, use interceptors, etc. |
3 | @Prompt annotates a business method of a CDI bean that should be exposed as a prompt template. By default, the name of the prompt is derived from the method name. |
4 | The @PromptArg can be used to customize the description of an argument. |
The result of a "prompt get" operation is always represented as a PromptResponse
.
However, the annotated method can also return other types that are converted according to the following rules.
-
If the method returns a
PromptMessage
then the reponse has no description and contains the single message object. -
If the method returns a
List
ofPromptMessage
s then the reponse has no description and contains the list of messages. -
The method may return a
Uni
that wraps any of the type mentioned above.
In other words, the return type must be one of the following list:
-
PromptResponse
-
PromptMessage
-
List<PromptMessage>
-
Uni<PromptResponse>
-
Uni<PromptMessage>
-
Uni<List<PromptMessage>>
Resources
MCP provides a standardized way for servers to expose resources to clients.
import io.quarkiverse.mcp.server.Resource;
import jakarta.inject.Inject;
import java.nio.file.Files;
// @Singleton (1)
public class MyResources {
@Inject (2)
FooService fooService;
@Resource(uri = "file:///project/alpha") (3)
BlobResourceContents alpha(String uri) {
return BlobResourceContents.create(uri, Files.readAllBytes(Paths.ALPHA));
}
}
1 | The @Singleton scope is added automatically, if needed. |
2 | MyResources is an ordinary CDI bean. It can inject other beans, use interceptors, etc. |
3 | @Resource annotates a business method of a CDI bean that should be exposed as a resource. By default, the name of the resource is derived from the method name. |
The result of a "resource read" operation is always represented as a ResourceResponse
.
However, the annotated method can also return other types that are converted according to the following rules.
-
If the method returns an implementation of
ResourceContents
then the reponse contains the single contents object. -
If the method returns a
List
ofResourceContents
implementations then the reponse contains the list of contents objects. -
The method may return a
Uni
that wraps any of the type mentioned above.
Tools
MCP provides a standardized way for servers to expose tools that can be invoked by clients.
import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;
import io.quarkiverse.mcp.server.ToolResponse;
import jakarta.inject.Inject;
// @Singleton (1)
public class MyTools {
@Inject (2)
FooService fooService;
@Tool(description = "Put you description here.") (3)
ToolResponse foo(@ToolArg(description = "The name") String name) {
return ToolResponse.success(
new TextContent(fooService.ping(name)));
}
}
1 | The @Singleton scope is added automatically, if needed. |
2 | MyTools is an ordinary CDI bean. It can inject other beans, use interceptors, etc. |
3 | @Tool annotates a business method of a CDI bean that should be exposed as a tool. By default, the name of the tool is derived from the method name. |
A result of a "tool call" operation is always represented as a ToolResponse
.
However, the annotated method can also return other types that are converted according to the following rules.
-
If the method returns an implementation of
io.quarkiverse.mcp.server.Content
then the reponse is "success" and contains the single content object. -
If the method returns a
List
ofContent
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
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
.
Note that the number of logged characters is limited.
The default limit is 100, but you can change this limit with the quarkus.mcp.server.traffic-logging.text-limit
configuration property.
quarkus.mcp.server.traffic-logging.enabled=true (1)
quarkus.mcp.server.traffic-logging.text-limit=50 (2)
1 | Enables traffic logging. |
2 | Set the number of characters of a JSON message which will be logged. |
Extension Configuration Reference
Remove this section if you don’t have Quarkus configuration properties in your extension. |
Configuration property fixed at build time - All other configuration properties are overridable at runtime
Configuration property |
Type |
Default |
---|---|---|
The root path. Environment variable: |
string |
|
The name of the server is included in the response to an Environment variable: |
string |
|
The version of the server is included in the response to an Environment variable: |
string |
|
If set to true then JSON messages received/sent are logged. Environment variable: |
boolean |
|
The number of characters of a text message which will be logged if traffic logging is enabled. Environment variable: |
int |
|