Introduction
In my previous article, I demonstrated just how easy it is to build an MCP server using Spring AI—with Claude Desktop acting as the MCP client. You saw how Spring’s MCP Server Boot Starter framework makes it simple to expose your own tools to a local or remote LLM.
In this follow-up article, I’m flipping the scenario: this time, we’ll build your own custom MCP client using Spring AI to connect to an MCP server. While existing tools like Claude Desktop are great for experimentation, they don’t always meet enterprise requirements when you need:
- Programmatic access from your own backend applications
- Integration with internal systems (e.g., databases, file servers, APIs)
- Control over security, auditing, and scalability
That’s where building your own MCP client comes in. It allows you to integrate LLM-powered automation into your systems on your terms.
Why Build a Custom MCP Client?
Before jumping into code, let’s understand why building your own MCP client matters in a real-world context.
Tools like Claude Desktop are excellent for experimentation and desktop use, but real enterprise systems often require greater control, integration flexibility, and security guarantees. Building your own MCP client gives you that control—and enables you to tightly couple LLM interactions with your infrastructure in a governed, auditable, and scalable way.
Full Integration with Your Backend Systems
With a custom MCP client, you can:
- Integrate LLM-driven workflows into your existing microservices or monolithic apps.
- Call internal APIs, search private data, or access your file storage securely.
- Trigger domain-specific logic that Claude Desktop—or any generic client—cannot support.
In contrast, generic clients like Claude Desktop are limited to executing tools from the desktop and can’t easily interact with backend services or enterprise databases.
Control Over Core Components
Building your own MCP client allows you to own and configure these key components:
| Component | Role |
|---|---|
| Transport layer | Use STDIO for local dev, SSE/WebFlux for scalable deployments |
| Tool registration | Attach custom tool callback logic to match your domain |
| Security layer | Authenticate to the MCP server; restrict access by identity |
| Monitoring hooks | Log tool invocations, errors, and latencies |
| LLM orchestration | Prompt construction, output filtering, retry handling, etc. |
Enterprise-Level Security and Governance
Most enterprise applications involve sensitive operations, which means you need:
- Authentication to MCP servers using OAuth2, JWTs, or internal token exchange
- Authorization per tool, per role, or per data scope
- Audit logging for each tool invocation to meet compliance requirements
- Rate limiting or resource isolation to prevent abuse
You can enforce these policies through Spring Security or additional wrappers like MCP Gateway or MCP Guardian.
Observability and Performance Monitoring
A production-grade MCP client should give you insight into:
- Which tools are used and how often
- How long each tool takes to respond
- When LLM calls or tool invocations fail
- How input/output flows through the system
With Spring Boot, you can use:
- Actuator endpoints for real-time diagnostics
- Custom logs inside your tool callbacks
- Metrics pipelines that feed Prometheus, Datadog, or AWS CloudWatch
Testability and CI/CD Automation
Building your own client also makes testing much more robust:
- Use mocked MCP servers or Testcontainers during CI pipelines
- Simulate tool responses or LLM output in unit and integration tests
- Validate full prompt-to-tool-call workflows before releasing
Generic clients don’t offer this level of test coverage or flexibility, which can leave gaps in quality assurance.
Code Walk Through
Now that we’ve explored the enterprise use case and the value of owning your client-side logic, let’s dive into how you can actually build one using Spring AI and Spring Boot.
This snippet below shows the required Maven dependencies to build a MCP client using Spring AI.
spring-ai-starter-mcp-clientenables communication with an MCP server.spring-boot-starter-websets up a basic RESTful service.spring-ai-starter-model-openaiallows integration with OpenAI models likegpt-4o-minifor LLM processing.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
The configuration file below sets up both the OpenAI model and the MCP client:
- The OpenAI section specifies the model, temperature, and token settings.
- The MCP client is configured to use the
Claude Desktopserver via STDIO, referencing the local JSON configuration file (claude_desktop_config.json, which is the same JSON to configure the Claude Desktop for my previous article).
spring:
ai:
openai:
chat:
model: gpt-4o-mini
temperature: 0.7
max_tokens: 1000
top_p: 1
frequency_penalty: 0
presence_penalty: 0
api-key: ${OPENAI_API_KEY}
mcp:
client:
stdio:
servers-configuration: "file:///C:/Users/Chung2/AppData/Roaming/Claude/claude_desktop_config.json"
This Spring Boot controller (McpAskController) exposes a POST endpoint /ask where a user can send a question.
- It initializes the
ChatClientwith default tool callbacks. - The
ask()method sends the user’s input to the LLM, which may call tools registered with the MCP server. - The response is mapped back to an
Answerrecord and returned to the caller.
@RestController
public class McpAskController {
private final ChatClient chatClient;
public McpAskController(ChatClient.Builder chatClientBuilder,
ToolCallbackProvider tools) { //
this.chatClient = chatClientBuilder
.defaultToolCallbacks(tools) //
.build();
}
@PostMapping("/ask")
public Answer ask(@RequestBody Question question) {
return chatClient.prompt()
.user(question.question())
.call()
.entity(Answer.class);
}
public record Question(String question) { }
public record Answer(String answer) { }
}
This screenshot below demonstrates a successful API call to the /ask endpoint using Postman.
- The user sends the JSON payload
{"question": "give me the hello world message"}.- Since Hello World MCP server is integrated, when I ask for Hello World message, it responses Hello World with the local current time.
- The MCP-integrated
ChatClientprocesses the input and responds with:

Conclusion
Off-the-shelf MCP clients like Claude Desktop are great for experimentation—but in real-world systems, you need more than just convenience.
By building your own MCP client with Spring AI, you gain:
- Tight integration with your APIs, services, and data sources
- Full control over how, when, and which tools the LLM can invoke
- Enterprise features like authentication, observability, and automated testing
This approach doesn’t just make your system smarter—it makes it secure, extensible, and production-ready.
Whether you’re building internal assistants, automating workflows, or embedding LLMs into your platform, a custom MCP client gives you the foundation to do it safely and at scale.
Leave a comment