Skip to main content

Command Palette

Search for a command to run...

Adopting an MCP-First API Design for the Agentic Era | Beyond REST.

Updated
Adopting an MCP-First API Design for the Agentic Era | Beyond REST.
M

Learner, Love to make things simple, Full Stack Developer, StackOverflower, Passionate about using machine learning, deep learning and AI

Move beyond traditional RESTful thinking. Learn how to design APIs specifically for MCP (Model Context Protocol) servers. This guide covers the shift in mindset, a practical OpenAPI 3.1 example, and a Spring Boot implementation to make your services AI-native.

Introduction:

For the last decade, our mantra as architects has been "API-first." We designed beautiful, resource-oriented RESTful interfaces, meticulously documented with OpenAPI, to decouple frontends from backends and enable a rich ecosystem of integrations. We built our digital empires on HTTP verbs and status codes.

But a new client has entered the building. It’s not a mobile app or a single-page application. It’s an AI Agent.

This agent doesn't navigate hypermedia links or care about the fine print of your application/json schema in the same way a human developer does. It needs context and capability. It needs to know: "What can you do for me, and how do I ask you to do it?"

This is where the Model Context Protocol (MCP) changes the game. MCP provides a standardized way to expose tools, resources, and prompts to AI models. If you want your enterprise systems to be "AI-native," you must shift from "REST-first" to "MCP-first" API design.

The Shift in Mindset: From Resources to Actions

Traditional REST is about managing state (GET, POST, PUT, DELETE on /users or /orders). MCP is about enabling action. An agent doesn't want to just read a user; it wants to "Send a notification to user X" or "Calculate the risk for customer Y."

Therefore, an MCP-first design philosophy treats your APIs as a collection of tools. While these tools may be backed by REST endpoints, their public face to the agent ecosystem is a list of declarative functions.

The Golden Rule: Design the tool first, then implement the API endpoint that supports it.

What is an MCP Tool?

From an API perspective, an MCP server exposes a tool, which is defined by:

  1. Name: A unique identifier for the function (e.g., get_weather).

  2. Description: A natural language explanation of what the tool does. This is crucial for the LLM to decide when to use it.

  3. Input Schema: A JSON Schema describing the parameters the tool accepts.

Practical Example: The Weather Service (A Classic for a Reason)

Let’s say we need to expose an internal weather service to an AI agent so it can answer, "Do I need an umbrella in London tomorrow?"

The MCP-First Thought Process

  1. Identify the Tool: The agent needs a tool called get_forecast.

  2. Describe it: "Retrieves the weather forecast for a specific location for a given number of days."

  3. Define the Input:

    • location (string, required): The city name or coordinates.

    • days (integer, optional): Number of days for the forecast (default: 1).

Now that we know the tool, we design the backing API.

1. The Backing API (OpenAPI 3.1 & Spring Boot)

The backing API is what your backend team builds. It might be REST, gRPC, or GraphQL. For our example, let's design a simple REST controller in Spring Boot.

OpenAPI 3.1 Specification Snippet (weather-service.yaml)

openapi: 3.1.0
info:
  title: Internal Weather API
  version: 1.0.0
servers:
  - url: https://api.internal.company.com/v1
paths:
  /weather/forecast:
    get:
      summary: Get weather forecast
      operationId: getForecast
      parameters:
        - name: location
          in: query
          required: true
          schema:
            type: string
          description: "City name (e.g., London) or lat,long"
        - name: days
          in: query
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
            maximum: 7
          description: "Number of forecast days"
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  location:
                    type: string
                  forecast:
                    type: array
                    items:
                      type: object
                      properties:
                        date:
                          type: string
                          format: date
                        condition:
                          type: string
                        high:
                          type: number
                        low:
                          type: number

Spring Boot Controller Implementation

This is the code that fulfills the OpenAPI contract.

@RestController
@RequestMapping("/api/v1/weather")
public class WeatherController {

    @GetMapping("/forecast")
    public ResponseEntity<ForecastResponse> getForecast(
            @RequestParam String location,
            @RequestParam(defaultValue = "1") @Min(1) @Max(7) int days) {

        // In a real app, you'd call a weather service here
        ForecastResponse response = new ForecastResponse(location, days);
        return ResponseEntity.ok(response);
    }

    // Inner class for response structure
    record ForecastResponse(String location, List<DailyForecast> forecast) {
        ForecastResponse(String location, int days) {
            this(location, generateMockForecast(days));
        }
        private static List<DailyForecast> generateMockForecast(int days) {
            // Mock implementation
            return IntStream.range(0, days)
                    .mapToObj(i -> new DailyForecast(
                            LocalDate.now().plusDays(i),
                            "Sunny",
                            22.5 + i,
                            13.0 + i))
                    .toList();
        }
    }

    record DailyForecast(LocalDate date, String condition, Double high, Double low) {}
}

This is a standard, well-designed REST endpoint. It's ready for mobile apps and web UIs. But it's not yet ready for an AI Agent.

2. The MCP Server Wrapper (The "Adapter")

This is the critical piece of the MCP-first architecture. We build a lightweight MCP server that acts as an adapter. Its job is to expose our getForecast REST endpoint as an MCP Tool.

Here’s how you might implement that adapter using a Spring Boot MCP abstraction (building on the new Spring AI MCP modules).

MCP Server Tool Definition (Spring Boot).

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.util.UriComponentsBuilder;

@Component
public class WeatherTools {

    private final RestClient restClient;

    public WeatherTools() {
        this.restClient = RestClient.builder()
                .baseUrl("https://api.internal.company.com/v1")
                .build();
    }

    @Tool(description = "Retrieves the weather forecast for a specific location for a given number of days.")
    public String getForecast(
            @ToolParam(description = "The city name (e.g., London) or latitude,longitude") String location,
            @ToolParam(description = "Number of forecast days (1-7, default is 1)") Integer days) {

        // The MCP tool calls the internal REST API
        String url = UriComponentsBuilder.fromPath("/weather/forecast")
                .queryParam("location", location)
                .queryParam("days", days != null ? days : 1)
                .toUriString();

        ForecastResponse response = restClient.get()
                .uri(url)
                .retrieve()
                .body(ForecastResponse.class);

        // The tool returns a simplified string result for the LLM
        return String.format("The forecast for %s is: %s", response.location(), response.forecast());
    }

    // You can reuse the ForecastResponse record from the controller
    // or define a simpler one here.
    private record ForecastResponse(String location, List<DailyForecast> forecast) {}
    private record DailyForecast(String date, String condition, Double high, Double low) {}
}

The MCP Server Configuration

To make this work, you need a main application class that scans for these @Tool annotations and starts an MCP server.

import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class McpWeatherServerApplication {

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

    // This crucial bean tells Spring AI to scan your component for @Tool methods
    // and expose them as MCP tools.
    @Bean
    public ToolCallbackProvider weatherTools(WeatherTools weatherTools) {
        return MethodToolCallbackProvider.builder().toolObjects(weatherTools).build();
    }
}

When this MCP server runs, it connects to an MCP client (like Claude Desktop, or a custom agent orchestrator) and advertises the getForecast tool with its description and input schema.

Why This Matters for the Enterprise Architect

  1. Separation of Concerns: The core business logic (the WeatherController) remains clean and unaware of AI. It serves all clients. The MCP layer is a specialized adapter.

  2. Security and Governance: The MCP server can be deployed in a DMZ with stricter security controls. It can handle authentication to the internal API, rate limiting for agent requests, and input sanitization specifically tuned for LLM quirks.

  3. Evolution: You can version your MCP tools independently of your REST APIs. You could have an MCP tool that composes multiple backend REST calls into a single, agent-friendly action.

  4. Standardization: By adopting MCP, you stop writing custom, brittle prompts for every new API. The agent discovers capabilities dynamically.

Conclusion: The Future is Multi-Client

Your API consumers will no longer be just humans with browsers or developers with API keys. They will be autonomous agents making decisions in milliseconds.

An MCP-first design philosophy doesn't mean abandoning REST or OpenAPI. It means treating them as implementation details for a higher-level capability contract. By designing the tool the agent needs and then building the API to support it, you ensure your enterprise is not just "digitally transformed," but "intelligence-ready."

Start small. Pick one legacy API, define the MCP tool it should expose, and build a lightweight server for it. This is the first step toward an architecture that doesn't just serve data, but participates in reasoning.

More such articles:

https://medium.com/techwasti

https://www.youtube.com/@maheshwarligade

https://techwasti.com/series/spring-boot-tutorials

https://techwasti.com/series/go-language