MCP is not REST API
Model Context Protocol (MCP) is a prominent technology in 2025, generating buzz comparable to ChatGPT in 2023 and RAG in 2024. However, many common implementations simply create an MCP wrapper over existing API services.
This is a suboptimal design choice. This blog post will outline the design principles of RESTful APIs, the origins of MCP, Remote Procedure Calls (RPC), and explain why combining these two distinct design philosophies is detrimental to Agent-Computer Interfaces (ACI).
The Essence of API Design
Let’s define the goals of good API design. A well-designed API should be:
- Easy to understand. Other developers (including future you) can grasp each endpoint’s purpose at a glance.
- Consistent. It follows a clear set of conventions, so the mental overhead to learn it stays low.
- Extensible. Versioning and new features can be added without breaking API consumers.
- Efficient. It makes sensible use of network and compute resources.
While “Premature optimization is the root of all evil” is a common adage in Computer Science, it doesn’t fully apply to API design. An API is a contract, and changes become very difficult after implementation. Even with versioning, driving adoption of new versions requires significant effort. This is analogous to data schema design.
REST API Primer
REST API is a popular API style focused on resources and the actions performed on them. It uses HTTP as the transport layer and typically serializes data in JSON format.
Here’s a simple example REST API of a blog service:
- Retrieve all blogs
GET /api/blogs
- Retrieve a specific post
GET /api/blogs/{id}
- Create a blog post
POST /api/blogs Content-Type: application/json { "Title": "MCP is Not REST API", "Content": "...", "authorId": "12345" }
- Update a post
PUT /api/blogs/{id} Content-Type: application/json { "Title": "MCP is Not REST API v2", "Content": "...", }
- Delete a post
DELETE /api/blogs/{id}
These API endpoints center around the “blog” resource. Different HTTP methods convey intent: POST for create, GET for read, PUT for update, and DELETE for delete. These four operations, Create, Read, Update, Delete, are commonly referred to as CRUD. This self-documenting structure allows API consumers to quickly understand each endpoint’s function. This clarity contributed significantly to RESTful API’s widespread adoption, making it ideal for CRUD-based SaaS applications. REST APIs are great fit for human-computer interfaces (HCI).
Model Context Protocol (MCP)
Lineage of MCP
Numerous resources online explain MCP, so we will focus on its foundational technologies.
MCP is inspired by the Language Server Protocol (LSP). LSP enables code editors (IDEs) to interact with language servers. Language servers provide language specific intelligence that development tools can access via a protocol enabling inter-process communication. This allows code editors to offer features like auto complete, goto definition, and hover over documentations. Language servers communicate using JSON-RPC. RPC (Remote Procedure Calls) can utilize various transport mechanisms, including TCP/IP, HTTP/2, and UDP. Practically, VSCode plugins are LSP servers.
Sounds familiar? MCP adopts the same concept as LSP but provides capabilities for LLM agents instead of code editors. It also uses JSON-RPC v2 as its communication layer and supports various transports like stdio and server side events (sse) streaming. So, practically, MCP servers are the equivalent of VSCode plugins, but for Cursor, Windsurf, and Claude Desktop, or other agentic hosts.
Remote Procedure Calls (RPC)
The reason why we brought up the inspirations for MCP is because at the end of the day, MCP is an RPC. A remote function call. RPC fits the LLM tool use capabilities perfectly, but we digressed. This means MCPs should be designed using RPC best practices.
Remote Procedure Call (RPC) APIs aim to make network calls resemble ordinary local function calls. This contrasts with the resource-centric design of REST APIs. RPC emphasizes actions over resources.
This leads to names like createUser
or getBlog
, unlike the resource-based naming in REST APIs. In REST API terms, every RPC is a POST
.
For example, using the modern gRPC
framework:
Define the service
syntax = "proto3";
package blog;
service BlogService {
rpc GetBlog (GetBlogRequest) returns (Blog {}
rpc CreateBlog(CreateBlogRequest) returns (Blog) {}
}
message GetBlogRequest { int32 blog_id = 1; }
message Blog {
int32 id = 1;
string title = 2;
string content = 3;
int32 author_id = 4;
}
message CreateBlogRequest {
string title = 1;
string content = 2;
int32 author_id = 3;
}
Then, after generating the _pb2
files with protoc, we can call the service from Python:
import grpc
import blog_pb2
import blog_pb2_grpc
def main() -> None:
# Connect to the gRPC server
with grpc.insecure_channel("localhost:50051") as channel:
stub = blog_pb2_grpc.BlogServiceStub(channel)
# Get a blog
response = stub.GetBlog(
blog_pb2.GetBlogRequest(blog_id=12345)
)
print("Fetched blog title:", response.title)
# Create a new blog
new_blog = stub.CreateBlog(
blog_pb2.CreateBlogRequest(
title="MCP is NOT REST",
content="MCP is NOT REST...",
author_id=67890,
)
)
print("New blog ID:", new_blog.id)
The client code closely resembles ordinary function calls like stub.GetBlog()
, abstracting away the network layer. There’s no manual HTTP construction. JSON-RPC works similarly but uses JSON instead of Protobuf.
The Pitfalls of Wrapping REST with MCP
Given that MCP is fundamentally an RPC mechanism designed for agents to perform actions, attempting to layer it directly on top of a resource-centric REST API introduces significant friction. This “impedance mismatch” can severely hinder an agent’s tool-using capabilities.
Actions vs. Resources: The Core Conflict
As we’ve established:
- MCP/RPC is action-oriented: Agents think in terms of verbs. “What can I do?” They expect tools that represent discrete functions or capabilities, e.g.
publishBlog
,summarizeText
,scheduleMeeting
. - REST is resource-oriented: It focuses on nouns. “What resources can I manipulate?” It uses a fixed set of verbs (GET, POST, PUT, DELETE) to perform CRUD operations on these resources.
When an agent wants to achieve a goal, it’s looking for a direct tool to call, or an action. If the MCP layer is merely a thin wrapper over REST, the agent’s natural way of thinking is compromised.
Why a Simple REST Wrapper Fails Agents
-
Lost Semantic Meaning and Increased Agent Complexity:
Agents thrive on clarity. An action like
archiveOldBlogPosts(beforeDate="2023-01-01")
is a clear, high-level instruction. If this MCP “tool” is just a facade for a series of REST calls e.g.,GET /api/blogs?status=published\&beforeDate=...
, then for each blog ID,PUT /api/blogs/{id}
with{"status": "archived"}
, the agent (or the MCP developer) is forced to translate its high-level goal into a sequence of low-level CRUD operations. The powerful semantic action is lost, replaced by a complex orchestration task. This defeats the purpose of providing agents with high-level tools -
Transactionality and Error Handling Nightmares:
Many meaningful agent actions are inherently transactional. They should either complete entirely or not at all. Consider an agent actiontransferBlogPostOwnership(blogId, fromAuthorId, toAuthorId)
. This might involve verifyingfromAuthorId
owns the blog, Updating the blog’sauthorId
, Perhaps logging the transfer.If these are separate REST calls, e.g.,
GET /api/blogs/{id}
, thenPUT /api/blogs/{id}
, what happens if thePUT
call fails after the initial checks? The system is left in an inconsistent state. REST APIs are typically stateless and don’t offer built-in transactionality across multiple requests. An RPC, by contrast, can encapsulate this entire logic server-side, ensuring atomicity. Forcing an MCP wrapper or the agent itself to manage this distributed transactionality over REST is complex and error prone.Using our Agents are Workflows mental model, an RPC wrapper MCP has a single state and an API wrapper MCP has two states. Two states is more complex to traverse than one. Make agent’s life easier. Don’t increase number states it needs to manage and transition.
-
Inefficient Operations and Chatty Interactions:
Agents often need to perform specific, targeted operations. If an agent needs to
incrementLikeCount(blogId)
, but the MCP wrapper only exposes a genericupdateBlog(blogId, blogData)
which maps toPUT /api/blogs/{id}
), the wrapper might first need toGET
the full blog, modify the like count, and thenPUT
the entire blog back. This is inefficient. An RPC designed for this action, e.g.,incrementLikeCount
would be far more direct and less data-intensive. Wrapping REST can lead to overly chatty interactions and unnecessary data transfer. -
Tool Brittleness and Maintenance Burden:
If the MCP layer is tightly coupled to the specifics of a REST API, any change in the REST API such as renaming endpoints, changes in request and response schema, changes in authentication, etc, can break the MCP tools. The agent’s capabilities become fragile, dependent on the stability of an underlying API not designed for its interaction model. A dedicated RPC interface, designed as a stable contract for agent actions, is more robust.
Design for Action, Not Data Manipulation
The core issue is that REST APIs are designed for manipulating data states, while agents are designed to execute actions and achieve goals. Forcing MCP to simply be a passthrough or a light translation layer for REST APIs means you are not providing the agent with true “tools” in the sense of capabilities, but rather with a slightly different way to perform CRUD operations.
This fundamentally limits what an agent can reliably and effectively do. Instead of empowering agents with high level, robust actions, you saddle them with the complexities and limitations of a data-centric protocol. For effective agent tool use, the APIs (and thus the MCP services) should be designed from the ground up as action-oriented RPCs that directly map to the conceptual tasks the agent needs to perform.
Conclusion
Both REST and RPC like MCP have their strengths. REST excels for resource-oriented systems and standard CRUD operations, making it ideal for many web applications and services. MCP, drawing from RPC principles, is tailored for enabling AI agents to perform actions and interact with systems in a more functional, capability-driven way.
Trying to force one paradigm onto the other, particularly by simply wrapping a REST API with an MCP layer, often leads to suboptimal outcomes. It can introduce unnecessary complexity, reduce clarity, and ultimately hinder an agent’s ability to effectively use its tools. When designing for AI agents, it’s crucial to provide them with APIs that speak their language - the language of actions and capabilities. This often means designing dedicated RPC style services rather than attempting to repurpose existing REST APIs that were built with a different purpose in mind.
Agent-Computer Interaction (ACI) matters when designing APIs for agents.
References
- Structured Programming with go to Statements
- Google Cloud API Design Guide
- Microsoft REST API Guidelines
- Paypal API Standards
- Model Control Protocol
- Language Server Protocol
- Agents are Workflows
@article{
leehanchung,
author = {Lee, Hanchung},
title = {MCP is not REST API},
year = {2025},
month = {05},
howpublished = {\url{https://leehanchung.github.io}},
url = {https://leehanchung.github.io/blogs/2025/05/17/mcp-is-not-rest-api/}
}