I added MCP (Model Context Protocol) to re:Money — my Quarkus + DynamoDB financial tracking app — in under 30 minutes. One dependency, a few annotations on existing service methods, and my MCP client (Kiro / Claude) could call my actual backend during development. Here's exactly what I did.
New to re:Money? Check out the full tutorial on AWS Community to get started.
The Starting Point
re:Money is a standard three-tier Quarkus app:
| Layer | What It Does |
|---|---|
| Model |
Entry — accountID, category, amount, balance, date |
| Service |
EntryService — business logic and DynamoDB queries |
| Resource | JAX-RS REST endpoints |
The service layer already had methods like findByAccountID(), findByCategory(), listCategories(), getLastBalance(). All I needed was to make them discoverable by AI tools.
Step 1: Add the Dependency
<dependency>
<groupId>io.quarkiverse.mcp</groupId>
<artifactId>quarkus-mcp-server-http</artifactId>
<version>1.10.1</version>
</dependency>
No configuration needed. Quarkus auto-discovers MCP tools and exposes them at /mcp.
Step 2: Annotate Your Service Methods
Add @Tool and @ToolArg to existing methods — no new code:
import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;
@ApplicationScoped
public class EntryService extends AbstractService {
@Tool(description = "Find all financial entries for a specific bank account")
public List<Entry> findByAccountID(
@ToolArg(description = "The account identifier, e.g. ACC001")
String accountID) {
return findAll().stream()
.filter(e -> e.getAccountID().equals(accountID))
.collect(Collectors.toList());
}
@Tool(description = "Find financial entries by spending category")
public List<Entry> findByCategory(
@ToolArg(description = "Category name like Food, Transport, Housing")
String category) {
return findAll().stream()
.filter(e -> category.equals(e.getCategory()))
.collect(Collectors.toList());
}
@Tool(description = "List all available spending categories")
public List<String> listCategories() {
return findAll().stream()
.map(Entry::getCategory)
.distinct().sorted()
.collect(Collectors.toList());
}
@Tool(description = "List all bank accounts")
public List<String> listAccounts() { /* ... */ }
@Tool(description = "Get the last known balance for a bank account")
public BigDecimal getLastBalance(
@ToolArg(description = "The account identifier")
String accountID) { /* ... */ }
@Tool(description = "Find entries for an account within a date range")
public List<Entry> findByAccountIDAndDates(
@ToolArg(description = "Account identifier") String accountID,
@ToolArg(description = "Start date as epoch millis") Long init,
@ToolArg(description = "End date as epoch millis") Long end) { /* ... */ }
}
The description fields matter — they're what the AI reads to decide which tool to call. Be specific to your domain.
Step 3: Run It
./mvnw compile quarkus:dev
MCP endpoint is live at http://localhost:8080/mcp.
Step 4: Configure Kiro as MCP Client
With re:Money running, you tell Kiro where to find the MCP server. Create .kiro/settings/mcp.json in your project root:
{
"mcpServers": {
"remoney": {
"url": "http://localhost:8080/mcp"
}
}
}
This is a workspace-scoped configuration — it only applies when you're working in this project. You can also set it globally at ~/.kiro/settings/mcp.json if you want it available everywhere.
Another option is to create a dedicated Kiro agent for working with re:Money. Create .kiro/agents/finance-dev.json:
{
"name": "finance-dev",
"description": "Development assistant with live access to re:Money",
"mcpServers": {
"remoney": {
"url": "http://localhost:8080/mcp"
}
}
}
Once configured, start a Kiro chat session and run /mcp to verify the server is connected and the tools are loaded:
You should see remoney listed with all your @Tool-annotated methods as available tools.
Demo: Kiro Calling re:Money
Once connected, Kiro chat now can reach your financial data and I love it because I can keep coding and using the chat to input data and query my re:Money app in a single place.
One Catch: MCP and Lambda Don't Mix
re:Money deploys to AWS Lambda using Quarkus's Lambda HTTP extension. But MCP requires a persistent HTTP connection — the server needs to stay alive to handle tool calls from the AI client. Lambda's request-response model doesn't support that.
So if your app runs on Lambda (or any short-lived serverless compute), you can't just add MCP to the same module and deploy it. You need to separate the MCP server into its own module and deploy it on a service that keeps a long-running process: ECS, App Runner, EC2, or even a local dev server.
In practice, this means:
- Your Lambda module stays as-is — REST API, UI, the production workload
-
A new MCP module imports your service layer and exposes it via
quarkus-mcp-server-http, deployed on ECS/App Runner/EC2
The service layer stays shared. The only difference is how it's exposed and where it runs. This is another reason to keep your business logic in the service layer and out of your REST controllers — it makes splitting modules painless.
For local development, this isn't an issue. ./mvnw quarkus:dev runs a long-lived process and MCP works fine. The separation only matters when you deploy to production.
What I Learned
Your service layer is the API. If your business logic lives in the service layer and not in your REST controllers, adding MCP is almost free.
Descriptions are the real work. The AI picks tools based on descriptions alone. Writing clear, domain-specific descriptions took more thought than any code change.
Start read-only. Expose queries first. Open up writes when you're ready.
Quarkus makes this trivial. One dependency, a few annotations, no config. The Quarkiverse MCP extension handles protocol, discovery, and transport. If you have a Quarkus app with a clean service layer, try it.

Top comments (0)