How to Build an MCP Client with Spring AI

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:

ComponentRole
Transport layerUse STDIO for local dev, SSE/WebFlux for scalable deployments
Tool registrationAttach custom tool callback logic to match your domain
Security layerAuthenticate to the MCP server; restrict access by identity
Monitoring hooksLog tool invocations, errors, and latencies
LLM orchestrationPrompt 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-client enables communication with an MCP server.
  • spring-boot-starter-web sets up a basic RESTful service.
  • spring-ai-starter-model-openai allows integration with OpenAI models like gpt-4o-mini for 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 Desktop server 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 ChatClient with 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 Answer record 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 ChatClient processes 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

About the author

Chung is a seasoned IT expert and Solution Architect with extensive experience in designing innovative solutions, leading technical teams, and securing large-scale contracts. With a strong focus on AI, Large Language Models (LLM), and cloud-based architectures, Chung combines technical expertise with strategic vision to deliver impactful solutions. A technology enthusiast, Chung regularly shares insights on emerging tech trends and practical applications, fostering innovation within the tech community.