MCP (Model Context Protocol) Is the New Standard for AI Tools

1. Overview

Large language models (LLMs) are truly useful only when they can read real data and call real systems: look up orders, read tickets, run queries. Previously, each AI application had to write its own integration for each tool. Model Context Protocol (MCP) was created to standardize this connection part.

This article explains what MCP is and why it’s getting attention. Then you’ll write a small MCP server with Spring Boot, configure a client to call it, and understand when not to use MCP.

2. What is MCP?

MCP is an open protocol. It defines how an AI application discovers and calls external capabilities: tools, data, and prompt templates. You can think of MCP like a “USB port” for AI. Any device following the standard can plug into any host that supports it and work seamlessly.

2.1. Three main roles

  • Host: the user-facing application, for example an AI assistant on desktop, an IDE, or an internal chatbot.
  • Client: the component within the host. Each client maintains a one-to-one connection with a server.
  • Server: a program that provides capabilities to AI, for example a server reading order data or accessing the file system.

2.2. Three types of “primitives” on the server side

  • Tools: functions the model can decide to call, like findOrder or cancelOrder.
  • Resources: read-only data, like file contents or configuration records, that are put into context.
  • Prompts: pre-built prompt templates that users select to use.

2.3. The underlying protocol

MCP uses JSON-RPC 2.0. The two most common transport types are stdio, when the host launches the server as a child process, and HTTP, when the server runs remotely. A tool call looks like this:

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "findOrder",
    "arguments": { "orderId": "ORD-1001" }
  }
}

The basic workflow consists of three steps:

  1. Client sends initialize to exchange protocol version and supported capabilities.
  2. Client calls tools/list to get the list of tools with descriptions and JSON Schema of parameters.
  3. When the model decides to use a tool, the client sends tools/call like in the example above and returns the result to the model.

Important point: the model doesn’t call the server directly. The host stands in between, so it can ask the user for confirmation before running a sensitive tool.

3. Why is MCP getting attention?

3.1. The N × M problem

Suppose there are N AI applications and M systems that need to connect. Without a common standard, you must write N × M integrations. With a common protocol, each application only needs to support MCP once, and each system only needs one MCP server. The problem reduces to N + M.

3.2. Separation of concerns

The team that owns the order system writes and operates their MCP server. The team building the chatbot just needs to point to that server. Business logic, access control, and audit logs stay in the right place and aren’t duplicated across applications.

3.3. Not locked into one model provider

Each LLM provider’s function calling has its own format. MCP sits above that layer. The server you write today will still work when you switch models or hosts.

3.4. The ecosystem is growing

More and more AI assistants, IDEs, and frameworks support MCP. SDKs exist for many languages, including an official Java SDK and built-in Spring AI integration. This is why many see MCP as a strong candidate for the standard tool connection protocol. However, the protocol is still evolving, so keep an eye on the spec for long-term deployments.

4. Writing an MCP server with Spring Boot

You’ll build a server providing two tools for looking up and canceling orders, running over stdio.

4.1. Dependencies

Spring AI provides a starter for MCP servers. Import the Spring AI BOM and use the latest version compatible with your Spring Boot 3.x:

<properties>
    <java.version>21</java.version>
    <spring-ai.version><!-- current Spring AI version --></spring-ai.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring-ai.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server</artifactId>
    </dependency>
</dependencies>

The spring-ai-starter-mcp-server starter supports stdio transport. If you want to run the server over HTTP, Spring AI has separate starters for Web MVC and WebFlux.

Note: Spring AI’s artifact names have changed between milestone releases. Check the documentation for your chosen version.

4.2. Domain and tools

public record Order(String id, String customerName, double total, String status) {}
@Service
public class OrderTools {

    private final Map<String, Order> orders = new ConcurrentHashMap<>(Map.of(
            "ORD-1001", new Order("ORD-1001", "Nguyen Van An", 350_000, "SHIPPING"),
            "ORD-1002", new Order("ORD-1002", "Tran Thi Binh", 120_000, "PENDING")
    ));

    @Tool(description = "Look up an order by ID, returns customer, total amount and status")
    public Order findOrder(@ToolParam(description = "Order ID, e.g. ORD-1001") String orderId) {
        Order order = orders.get(orderId);
        if (order == null) {
            throw new IllegalArgumentException("Order not found: " + orderId);
        }
        return order;
    }

    @Tool(description = "Cancel an order. Can only cancel orders with PENDING status")
    public String cancelOrder(@ToolParam(description = "Order ID to cancel") String orderId) {
        Order order = findOrder(orderId);
        if (!"PENDING".equals(order.status())) {
            return "Cannot cancel: order is in " + order.status() + " status";
        }
        orders.put(orderId, new Order(order.id(), order.customerName(), order.total(), "CANCELLED"));
        return "Order " + orderId + " cancelled";
    }
}

Two annotations play the main roles:

  • @Tool (from package org.springframework.ai.tool.annotation) marks the method as a tool. The description part is what the model reads to decide whether to call the tool. The clearer the description, the better the model chooses.
  • @ToolParam describes each parameter. Spring AI generates JSON Schema from the parameter type and this description.

Note that cancelOrder returns a message instead of throwing an exception when violating business rules. The model reads that message and explains it back to the user naturally.

4.3. Register tools with MCP server

@SpringBootApplication
public class OrderMcpServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderMcpServerApplication.class, args);
    }

    @Bean
    ToolCallbackProvider orderToolProvider(OrderTools orderTools) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(orderTools)
                .build();
    }
}

MethodToolCallbackProvider scans for @Tool methods in orderTools. The MCP server auto-configuration will find any ToolCallbackProvider bean and expose them via tools/list.

4.4. Configure for stdio

spring.ai.mcp.server.name=order-mcp-server
spring.ai.mcp.server.version=0.0.1
spring.ai.mcp.server.stdio=true

spring.main.web-application-type=none
spring.main.banner-mode=off
logging.pattern.console=
logging.file.name=./logs/order-mcp-server.log

With stdio, stdout is the JSON-RPC transport channel. Anything else printed to stdout will break the protocol, including banners and logs. That’s why this configuration disables the banner, disables console logging, and redirects logs to a file.

Note: This is the most common mistake when first writing an MCP server over stdio. Even a single System.out.println for debugging will cause the client to report parse errors or disconnect. Use a logger to write to a file.

Build to jar:

./mvnw clean package -DskipTests

5. Connecting from client

Many hosts configure servers through a JSON file with the key mcpServers. The file name and location depends on each host:

{
  "mcpServers": {
    "orders": {
      "command": "java",
      "args": ["-jar", "/opt/mcp/order-mcp-server.jar"]
    }
  }
}

On startup, the host runs the command java -jar ... as a child process, sends initialize, then tools/list. You ask: “Can I still cancel order ORD-1002? If I can, please cancel it for me.” The model will call findOrder then cancelOrder. The result of the second call looks like:

{"content":[{"type":"text","text":"Order ORD-1002 cancelled"}],"isError":false}

Usually the host will show a dialog asking for permission before running the tool. You should maintain this behavior with tools that have side effects like cancelOrder.

If you want your Spring application to play the client role, Spring AI also has an MCP client starter. This starter turns tools from external servers into tools usable with ChatClient.

6. Testing tool behavior

Tools are just ordinary Java methods, so you can test them without starting MCP:

class OrderToolsTest {

    private final OrderTools tools = new OrderTools();

    @Test
    void shouldCancelPendingOrder() {
        String result = tools.cancelOrder("ORD-1002");

        assertThat(result).isEqualTo("Order ORD-1002 cancelled");
        assertThat(tools.findOrder("ORD-1002").status()).isEqualTo("CANCELLED");
    }

    @Test
    void shouldRefuseToCancelShippingOrder() {
        assertThat(tools.cancelOrder("ORD-1001"))
                .contains("Cannot cancel")
                .contains("SHIPPING");
    }

    @Test
    void shouldFailWhenOrderNotFound() {
        assertThatThrownBy(() -> tools.findOrder("ORD-9999"))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("ORD-9999");
    }
}

These three tests cover the happy path, business rules, and the not-found case. The protocol part is handled by the SDK. What you need to ensure most is that business logic returns correct results and messages the model can understand.

If you want end-to-end testing, you can use MCP inspector tools. They let you list tools and call them manually.

7. Practical application: what to watch out for

  • Design tools according to business logic, not database tables. findOrder is better than a universal executeSql tool. Narrower tools are both easier to control permissions and help the model choose correctly.
  • Principle of least privilege. The server should use an account with only the exact permissions needed. Separate read and write tools, and consider enabling write tools only in certain environments.
  • Beware of prompt injection. Data that tools return, like customer notes, may contain instructions aimed at controlling the model. Don’t let a tool’s result automatically trigger dangerous actions without user confirmation.
  • Only use trustworthy servers. An MCP server from a third party running over stdio is a process on your machine with your permissions. Tool descriptions can also be injected with malicious content. Read the source code or only use sources you trust.
  • Authenticate remote servers. When running over HTTP, put the server behind appropriate authentication and log audit trails for all tool calls.
  • Keep the number of tools reasonable. Each tool consumes context for its description. Too many tools make the model more likely to pick wrong ones.

8. When NOT to use MCP

  • Small applications with only one model and a few functions. Direct function calling from the framework, like using @Tool straight with ChatClient, is simpler. You don’t need an extra process and protocol layer.
  • Fixed processing flows that don’t need the model to decide. If step 1 always calls API A then step 2 always calls API B, just write normal code. Don’t hand off to LLM what an if statement does better.
  • Ultra-low latency requirements or high determinism needed. Each round trip from model → client → server adds latency. Model results aren’t completely deterministic either.
  • Irreversible operations without review. Transferring money or bulk deleting data shouldn’t be exposed as automatic tools. If absolutely necessary, always include a confirmation step.
  • You need an API for other systems to call. MCP was created to serve AI. Between services, REST, gRPC, or messaging are still the right choices.

9. Conclusion

MCP solves a very real problem: connecting AI to tools and data in a unified way, instead of each application writing its own. Thanks to the host–client–server model, three primitives (tools/resources/prompts), and JSON-RPC foundation, a server you write can be reused across many different hosts.

With Spring AI, writing an MCP server takes just a few @Tool annotations, a ToolCallbackProvider bean, and correct stdio configuration. The hard part lies elsewhere: designing narrow and clear tools, enforcing strict permissions, guarding against prompt injection. You also need to stay clear-headed and recognize situations where ordinary code is still the better solution.

mcpaispring-aispring-boottech-trends