DEV Community

Cover image for How to Build and Connect an MCP Server with Quarkus and Claude
Praveen Gowthaman
Praveen Gowthaman

Posted on

How to Build and Connect an MCP Server with Quarkus and Claude

Introduction

In this blog, we will walk through how to create a simple MCP server using Quarkus and explore how to integrate it with Claude Desktop.

Prerequisites

Before getting started, make sure you have:

  • A basic understanding of Java and Quarkus
  • Java 17 installed on your system
  • Claude Desktop installed
  • A basic understanding of LLMs (Large Language Models)

That’s it — once these are in place, we’re ready to start building our MCP server.

Creating the Quarkus Project

To get started, we first need to create a Quarkus project.

  1. Navigate to https://code.quarkus.io in your browser.
  2. Configure the project with the following settings:
  • Build Tool: Gradle
  • Java Version: 17
  • Group: <group-id-of-ur-choice>
  • Artifact: mcp-tutorial-app
    1. Generate and download the project.

Once the project is downloaded, extract it and open it in your preferred IDE (IntelliJ IDEA or VS Code).

Adding Required Dependencies

Next, we need to add the dependencies required to build an MCP server and call external REST APIs.

Open the build.gradle file and ensure the following dependencies are present:

dependencies {
    implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
    implementation enforcedPlatform("${quarkusPlatformGroupId}:quarkus-mcp-server-bom:${quarkusPlatformVersion}")

    implementation 'io.quarkus:quarkus-rest'
    implementation 'io.quarkus:quarkus-rest-client'
    implementation 'io.quarkus:quarkus-rest-client-jackson'
    implementation 'io.quarkiverse.mcp:quarkus-mcp-server-stdio'
    implementation 'io.quarkus:quarkus-arc'

    testImplementation 'io.quarkus:quarkus-junit5'
    testImplementation 'io.rest-assured:rest-assured'
}
Enter fullscreen mode Exit fullscreen mode

These dependencies enable the following:

  • quarkus-rest – Allows us to expose REST endpoints.
  • quarkus-rest-client – Helps us call external APIs.
  • quarkus-rest-client-jackson – Provides JSON serialization support.
  • quarkus-mcp-server-stdio – Enables the application to run as an MCP server.
  • quarkus-arc – Provides dependency injection support in Quarkus.

Creating the REST Client

For this step, we will create a REST client to call the Quarkus Extensions API. Instead of creating everything from scratch, we can reuse the starter code that comes with the project generated from code.quarkus.io.

When you download the project from code.quarkus.io, it already includes a sample REST resource (MyRemoteService). We can use this starter project as a base and extend it to implement our REST client.

Quarkus provides support for REST clients using the MicroProfile REST Client specification, which makes it easy to call external APIs using simple Java interfaces.

This interface (MyRemoteService) is annotated with @RegisterRestClient. This interface will be responsible for calling the external Quarkus Extensions API and returning the extension details.

package com.praveen.tutorial;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import java.util.List;
import java.util.Set;

/**
 * To use it via injection.
 *
 * {@code
 *     @Inject
 *     @RestClient
 *     MyRemoteService myRemoteService;
 *
 *     public void doSomething() {
 *         Set<MyRemoteService.Extension> restClientExtensions = myRemoteService.getExtensionsById("io.quarkus:quarkus-hibernate-validator");
 *     }
 * }
 */
@RegisterRestClient(baseUri = "https://stage.code.quarkus.io/api")
public interface MyRemoteService {

    @GET
    @Path("/extensions")
    Set<Extension> getExtensionsById(@QueryParam("id") String id);

    public class Extension {
        public String id;
        public String name;
        public String shortName;
        public List<String> keywords;

        @java.lang.Override
        public java.lang.String toString() {
            return "Extension{" +
                    "id='" + id + '\'' +
                    ", name='" + name + '\'' +
                    ", shortName='" + shortName + '\'' +
                    ", keywords=" + keywords +
                    '}';
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Implementing MCP Tools

Now that we have created the REST client, the next step is to expose functionality to Claude using MCP Tools.

MCP tools allow Claude (or any MCP-compatible client) to call specific methods in our application. These methods can perform actions such as calling APIs, retrieving data, or executing business logic.

Below is the class we will use to implement our MCP tool.

package com.praveen.tutorial;

import java.util.Set;
import jakarta.inject.Inject;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;

import com.praveen.tutorial.MyRemoteService.Extension;
import com.praveen.tutorial.MyRemoteService;

public class ExtensionMCP {

    @RestClient
    @Inject
    MyRemoteService remoteService;

    @Tool(description = "Get the quarkus extension details using the query param called id")
    Set<Extension> getAlerts(
        @ToolArg(description = "String field describing quarkus id name eg:io.quarkus:quarkus-hibernate-validator")
        String id
    ) {
        return remoteService.getExtensionsById(id);
    }
}
Enter fullscreen mode Exit fullscreen mode

Let’s understand each part of this class.

1. Dependency Injection with @Inject

@Inject
Enter fullscreen mode Exit fullscreen mode

@Inject is provided by CDI (Contexts and Dependency Injection) in Quarkus.
It allows Quarkus to automatically create and manage the instance of the required class.

In this case, Quarkus injects the MyRemoteService REST client into our class so we can use it to call external APIs.

2. Using @RestClient

@RestClient
Enter fullscreen mode Exit fullscreen mode

@RestClient tells Quarkus that the injected class is a MicroProfile REST Client.

This annotation ensures that the MyRemoteService interface is treated as a REST client capable of making HTTP calls to external APIs.

So when our MCP tool runs, it can call the external Quarkus extensions API through this client.

3. Declaring the MCP Tool with @Tool

@Tool(description = "Get the quarkus extension details using the query param called id")
Enter fullscreen mode Exit fullscreen mode

@Tool is provided by quarkus-mcp-server and is used to expose a method as an MCP tool.

Once the application runs as an MCP server, Claude can discover and call this method.

The description field is important because it helps the LLM understand what the tool does.

4. Tool Arguments with @ToolArg

@ToolArg(description = "String field describing quarkus id name eg:io.quarkus:quarkus-hibernate-validator")
Enter fullscreen mode Exit fullscreen mode

@ToolArg describes the input parameter that the tool expects.

This metadata helps the LLM understand:

  • what parameter the tool requires
  • the type of input expected
  • an example value

In this example, the tool expects a Quarkus extension ID.

5. Returning the Extension Data

Set<Extension>
Enter fullscreen mode Exit fullscreen mode

The method returns a Set of Extension objects.
The Extension class is defined inside the MyRemoteService interface and represents the structure of the response returned by the Quarkus extensions API.

6. Calling the REST Client

return remoteService.getExtensionsById(id);
Enter fullscreen mode Exit fullscreen mode

Finally, the MCP tool calls the REST client method getExtensionsById() and returns the results.

When Claude invokes this MCP tool, the flow works like this:

  1. Claude calls the MCP tool getExtensionsById
  2. The tool receives the extension id
  3. The REST client calls the external API
  4. The API response is returned back to Claude

Building the Application as an Uber JAR

Once the MCP tool implementation is complete, the next step is to package the application as a single runnable JAR, also known as an Uber JAR.

An Uber JAR bundles the application along with all its dependencies into a single executable file. This makes it easy to distribute and run the application without needing to manage external libraries separately.

To enable Uber JAR packaging in Quarkus, add the following configuration to the application.properties file:

quarkus.package.jar.type=uber-jar
Enter fullscreen mode Exit fullscreen mode

After adding this configuration, build the project using the Gradle command below:

./gradlew build
Enter fullscreen mode Exit fullscreen mode

For Windows users:

gradlew build
Enter fullscreen mode Exit fullscreen mode

Once the build completes successfully, the Uber JAR will be generated inside the build directory.

The generated file will look similar to:

build/mcp-tutorial-app-1.0.0-SNAPSHOT-runner.jar
Enter fullscreen mode Exit fullscreen mode

This JAR file contains the entire application and can be executed using the following command:

java -jar build/mcp-tutorial-app-1.0.0-SNAPSHOT-runner.jar
Enter fullscreen mode Exit fullscreen mode

At this point, our MCP server is packaged and ready to be integrated with Claude Desktop.

Integrating the MCP Server with Claude Desktop

Now that our MCP server has been packaged as an Uber JAR, the next step is to integrate it with Claude Desktop so that Claude can discover and use the MCP tools we created.

Claude Desktop loads MCP servers through a configuration file called claude_desktop_config.json. By adding an entry for our server in this file, we instruct Claude to start our MCP server when the application launches.

Locating the Configuration File

On Windows, the configuration file is located at:

C:\Users\<your-username>\AppData\Roaming\Claude\claude_desktop_config.json
Enter fullscreen mode Exit fullscreen mode

Open this file using any text editor such as VS Code or Notepad.

Adding the MCP Server Entry

Inside the configuration file, add a new entry under the mcpServers section that points to the Uber JAR we built earlier.

{
  "mcpServers": {
    "mcptutorial": {
      "command": "java",
      "args": [
        "-jar",
        "C:\\Users\\<your-username>\\projects\\mcp-tutorial-app\\build\\mcp-tutorial-app-1.0.0-SNAPSHOT-runner.jar"
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Understanding the Configuration

  • mcpServers – This section defines all MCP servers that Claude Desktop should load.
  • mcptutorial – A name used to identify our MCP server.
  • command – The command used to start the server. In this case, we run the JAR using java.
  • args – Arguments passed to the command. Here we provide -jar followed by the path to our Uber JAR.

Applying the Changes

After saving the configuration file:

  1. Close Claude Desktop if it is running.
  2. Restart Claude Desktop.

When Claude starts again, it will automatically launch the MCP server defined in the code

Now let us prompt Claude and see the toll call being made

References

https://quarkus.io/blog/mcp-server/
https://docs.quarkiverse.io/quarkus-mcp-server/dev/index.html

Top comments (0)