Saltar al contenido principal
Latest writing
openapiJul 27, 20258 min read

You Don't Need to Implement MCP Servers: A Contract-First Approach to AI Tool Integration

Your REST API and its OpenAPI contract are already most of an MCP server. Updated for 2026 with the latest MCP revision, real HAPI CLI output, and the step from MCP to A2A.

:::info [Updated October 2026] We first published this in July 2025. The core idea held up, but almost everything around it moved: the MCP specification has had two new revisions, and the HAPI CLI has a new home and new commands. This version uses the current MCP revision (2026-07-28) and real output from HAPI CLI 1.3.0. :::

The Model Context Protocol (MCP) was introduced as an "open standard for connecting AI assistants to the systems where data lives". Put simply, MCP is a contract: like OpenAPI, it tells a model which tools exist, what inputs they take, and how to call them. OpenAPI describes how machines talk to machines, and MCP describes how AI models talk to applications.

Many developers still assume "MCP server" means writing a new adapter by hand. It doesn't. If you have a REST API with an OpenAPI contract, you already have most of an MCP server. Each API operation becomes an MCP tool, named after its operationId.

One operation, one tool​

Here is a small OpenAPI contract for a fictional order service:

openapi: 3.1.0
paths:
/orders:
get:
operationId: listOrders
summary: List orders
description: Returns recent orders, newest first.
parameters:
- name: status
in: query
schema: { type: string, enum: [pending, shipped, delivered] }
post:
operationId: createOrder
summary: Create an order
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [item, quantity]
properties:
item: { type: string }
quantity: { type: integer, minimum: 1 }
/orders/{orderId}:
get:
operationId: getOrder
summary: Get one order by ID
parameters:
- name: orderId
in: path
required: true
schema: { type: string }

And this is what HAPI actually returns to an MCP client's tools/list request for that contract, unedited except for line breaks:

{
"tools": [
{
"name": "listOrders",
"title": "List orders",
"description": "List orders - Returns recent orders, newest first.",
"inputSchema": {
"type": "object",
"properties": { "status": { "type": "string", "enum": ["pending", "shipped", "delivered"] } }
},
"annotations": { "readOnlyHint": true, "destructiveHint": false }
},
{
"name": "createOrder",
"title": "Create an order",
"description": "Create an order",
"inputSchema": {
"type": "object",
"properties": {
"createOrderBody": {
"type": "object",
"properties": { "item": { "type": "string" }, "quantity": { "type": "integer", "minimum": 1 } },
"required": ["item", "quantity"]
}
},
"required": ["createOrderBody"]
},
"annotations": { "readOnlyHint": false, "destructiveHint": true }
},
{
"name": "getOrder",
"title": "Get one order by ID",
"description": "Get one order by ID",
"inputSchema": {
"type": "object",
"properties": { "orderId": { "type": "string" } },
"required": ["orderId"]
},
"annotations": { "readOnlyHint": true, "destructiveHint": false }
}
]
}

What maps to what:

  • operationId becomes the tool name, and summary becomes its title.
  • Path, query and body parameters become one JSON Schema inputSchema.
  • The HTTP method becomes behavior hints: GET is marked read-only, POST is marked as a write that can change data. MCP clients use these tool annotations to decide when to ask a human before acting.
  • When a tool is called, the result comes back both as text and as structuredContent, the same JSON your REST endpoint returns.

You wrote none of that by hand. The contract already said it.

Common misconceptions​

"You must implement an MCP server from scratch." You don't. An existing API can be served as MCP straight from its contract. Your business logic, security and data stay where they are; you publish the contract over a standard channel.

"MCP tools are different from API operations." In our view, the tools are the operations. The operationId → tool name mapping means the model sees the same operations your developers already know.

"MCP adds a heavy new layer." The latest MCP revision makes this even clearer. In 2026-07-28, MCP became stateless: the initialize handshake and the Mcp-Session-Id header are gone, and every request carries its own protocol version and client capabilities in _meta. Over Streamable HTTP, each MCP message is now one HTTP POST to one endpoint. It looks a lot like the REST call it wraps.

Two ways to serve your API as MCP​

Install the HAPI CLI and its OpenAPI plugin (full instructions):

bun add -g @mcp-com-ai/hapi@latest @mcp-com-ai/plugin-openapi@latest @mcp-com-ai/plugin-arazzo@latest

1. You already have a running API (any language): headless mode. HAPI reads the contract, exposes /mcp, and forwards each tool call to your existing backend:

hapi serve --specs https://example.com/openapi.json --headless --port 3000
# or point it at a local file and a backend URL:
hapi serve --specs ./orders.yaml --headless --url http://localhost:8080 --port 3000

2. You're starting from the contract: scaffold, then serve. HAPI generates a controller with one function per operation, and you plug your services into it:

hapi init orders # writes ~/.hapi/src/orders/Controller.ts from ~/.hapi/specs/orders.yaml
hapi serve orders # the REST API, with docs at /openapi
hapi serve orders --mcp # the same REST API, plus MCP tools at /mcp

Either way, your MCP endpoint is http://localhost:3000/mcp. Add it to any MCP client that supports remote Streamable HTTP servers. For example, in Claude Code:

claude mcp add --transport http orders http://localhost:3000/mcp

Before you serve anything, preview what the model will see, and what it will cost you in context:

hapi serve orders --dry-run
NAME READ-ONLY DESTRUCTIVE OPEN-WORLD DESCRIPTION DESC LEN
listOrders true false ? List orders - Returns recen... 50
createOrder false true ? Create an order 15
getOrder true false ? Get one order by ID 19

3 tools · ~225 tokens (estimate; rule of thumb: ~4 chars/token over the tools/list payload)

That last line matters more in 2026 than it did in 2025. Every tool description travels in the model's context, so a lean contract is a cheaper, more accurate agent.

OpenAPI vs MCP, side by side​

ConceptOpenAPI (OAS)MCP (2026-07-28)
PurposeDescribe an HTTP API for clients and developersLet AI applications discover and call tools, read resources, and use prompts
Unique ID of an actionoperationIdTool name
Inputsparameters and requestBody (JSON Schema)inputSchema (JSON Schema)
Outputsresponses with schemas per status codeOptional outputSchema, with results in structuredContent
BehaviorHTTP method semantics (GET is safe)Annotations: readOnlyHint, destructiveHint, idempotentHint, openWorldHint
SecuritysecuritySchemes (API key, OAuth 2, and more)An Authorization spec for HTTP, based on OAuth 2.1
Versioningopenapi: 3.x plus your own API versionDate-based protocol revisions; the current one is 2026-07-28
DiscoveryPublish the document; Swagger UI and similartools/list, server/discover, and the MCP Registry for publishing servers
TransportHTTPstdio, or Streamable HTTP

operationId ≈ tool name is still the most precise analogy between the two. Both are about contracts, not implementations: they describe how to interact with a service, not how it's built.

Why contract-first pays off​

These are our opinions, from building and running HAPI with teams:

  • Speed. Exposing an API that's already documented takes minutes, not an adapter project.
  • One source of truth. The MCP interface is generated from the same contract as your REST docs, so they can't drift apart.
  • Reuse. The same controller and service answer REST and MCP calls. Security and business rules live in one place.
  • Context discipline. You can see the tool count and token estimate before you ship, and trim descriptions that aren't earning their place.

Beyond tools: from MCP to A2A​

MCP connects an agent to its tools. The Agent2Agent (A2A) protocol, an open-source Linux Foundation project contributed by Google, connects agents to each other, and its documentation describes it as complementary to MCP. Agents discover each other through Agent Cards, which are, again, contracts.

The same idea applies one level up. HAPI 1.3 can publish proven read-only operations, and Arazzo workflows, as A2A skills on the same server, with an Agent Card at /.well-known/agent-card.json:

hapi serve orders --mcp --a2a

A2A in HAPI is an Enterprise plugin, installed separately and licensed (request a license). Write operations are not published as skills. REST and MCP stay in the open-source CLI.

The path forward​

The core insight from 2025 still stands, and the specification has only moved closer to it: if your API is RESTful and documented, it can be an MCP server. You aren't building a new backend; you're opening another door to the one you have.

Start with the contract. Preview it with --dry-run. Serve it with --mcp. Get started at hapi.mcp.com.ai, read the docs, or try runMCP and chatMCP. Questions or ideas? Drop us a line or join us on Discord.

Go Rebels! ✊🏽

Additional resources​

Newsletter

Stay close to the experiments

Get new essays, practical guides, and hands-on field notes from La Rebelion Labs.