⏱️ Reading time: 14 min
Earendil, the company behind the Pi code assistant, spent months writing on its blog and repeating on podcasts that Pi would not support MCP. In the product’s most recent version, MCP became part of the core.
📑 En este artículo
- TL;DR
- What Is Model Context Protocol?
- Why Pi’s Reversal Matters
- How MCP Works Under the Hood
- Practical Examples
- Getting Started
- Real-World Use Cases
- Common Mistakes and Best Practices
- Going Deeper: How MCP Scales with Codemode
- Frequently Asked Questions
- What sets MCP apart from a traditional REST API?
- Do I need to know JavaScript to use Codemode with MCP?
- Is MCP exclusive to Claude?
- Can an MCP server access my file system without permission?
- Why did Pi reject MCP at first, and what changed its mind?
- What is Codemode, and how is it different from a regular MCP server?
- References
The reversal isn’t cosmetic: it reveals what the protocol actually is, how it changed over the past year, and why even its loudest critics end up integrating it.
TL;DR
- MCP is Anthropic’s open protocol that standardizes how a model connects to external tools and data.
- An MCP server exposes tools through JSON-RPC; any compatible client discovers them without custom integration code.
- Codemode adds a JavaScript sandbox to combine multiple MCP calls without spending context on every step.
- Pi, Earendil’s assistant, went from rejecting MCP on its blog to building it into the product’s core.
- Setting up your own MCP server takes one JSON file and an npx command, with no extra infrastructure.
What Is Model Context Protocol?
MCP (Model Context Protocol) is an open protocol published by Anthropic that standardizes how a language model connects to external tools, files, and services. Instead of coding a separate integration for every data source, an MCP client and server share the same language: JSON-RPC over a common transport.
Before MCP, every AI assistant (an editor, a CLI, a chatbot) needed its own connector for each service: one for Slack, another for a database, another for the file system. MCP splits those into two roles. An MCP server exposes a set of capabilities (tools to run actions, resources to read data, reusable prompts). An MCP client, which lives inside the assistant, discovers those capabilities on connection and decides which ones to show the model on each turn.
Under the hood, calling an MCP tool isn’t very different from the function calling that model APIs already support: the server announces the function’s name, its description, and its parameter schema in JSON Schema, and the model chooses when to invoke it. The difference is that MCP standardizes that announcement so it doesn’t depend on the model provider or the application using it.
Why Pi’s Reversal Matters
Pi’s case is instructive because it isn’t the only one: several agent tools started out distrusting MCP and ended up adopting it. Earendil even published, according to its own account on the company blog, an explicit statement that Pi would not support the protocol, and one of its engineers wrote an entire post explaining why.
The original rejection wasn’t ideological. The real technical problem was composition: chaining the output of one MCP tool as input to another forced the model to read, copy, and rewrite entire data payloads within the conversation’s context, whether the data was a hundred lines or a hundred thousand. Every intermediate step cost tokens, and many MCP servers returned plain text meant for a human to read, not structured data meant for another program to process.
What changed, according to Earendil, wasn’t just the protocol: it was realizing that the infrastructure MCP needed (lazy tool loading, mid-conversation system messages, per-tool metadata for models with variable reasoning levels) was useful on its own, beyond MCP. Instead of treating the protocol as a bolted-on addition, Earendil redesigned Pi so those primitives became part of the core, and only then did MCP fit without friction.
Earendil isn’t the only case. Codex, OpenAI’s assistant, exposes an equivalent mechanism for composing tool calls with code, and several agent harnesses converge on the same idea: let the model write the combination logic instead of executing a fixed plan of calls.
💭 Key point: Codemode doesn’t replace MCP, it complements it. MCP defines how a client discovers and calls tools; Codemode defines how that client combines multiple calls with its own logic without spending context on every intermediate step.
How MCP Works Under the Hood
Model Context Protocol has three primitives: tools (actions the model can invoke, like reading a file or calling an API), resources (data the client can expose to the model, like a document’s contents), and prompts (reusable templates the server suggests for common tasks). A server can expose any combination of the three.
The connection follows a classic client-server pattern over JSON-RPC 2.0. On startup, the client sends an initialize message with the capabilities it supports; the server responds with its own and the protocol version it speaks. That capability negotiation is why a new client can still talk to an old server: if a feature isn’t in the other side’s capability list, it simply doesn’t get used.
The transport is also part of the standard. For a server running on the same machine as the client, like the file-system example in this article, MCP uses stdio: the server is a child process and messages travel over its standard input and output. For a remote server, the spec defines an HTTP transport with streaming (Server-Sent Events or streamable HTTP), letting the server live on another machine without changing a single line of the messaging protocol.
sequenceDiagram
participant C as MCP Client
participant S as MCP Server
C->>S: initialize
S-->>C: supported capabilities
C->>S: tools/list
S-->>C: list of tools
C->>S: tools/call clima_actual
S-->>C: structured result
Note over C,S: the connection persists for the whole session
Once connected, the client requests tools/list to find out what the server can do, and uses that response to build the list of functions it shows the model. When the model decides to use one, the client translates that decision into a tools/call message and returns the server’s response to the model.
Practical Examples
Here’s what a minimal MCP server looks like in Node.js, using the official SDK:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "clima-latam", version: "1.0.0" });
server.registerTool(
"clima_actual",
{
title: "Current weather",
description: "Returns the current temperature for a Latin American city",
inputSchema: { ciudad: z.string() },
},
async ({ ciudad }) => ({
content: [{ type: "text", text: `The weather in ${ciudad} is 24 degrees and clear skies.` }],
})
);
const transport = new StdioServerTransport();
await server.connect(transport);
This code registers a tool called clima_actual that any MCP client (Pi, Claude Desktop, Zed) automatically discovers on connecting. When the client requests tools/list, the server responds with something like this:
{
"tools": [
{
"name": "clima_actual",
"description": "Returns the current temperature for a Latin American city",
"inputSchema": { "type": "object", "properties": { "ciudad": { "type": "string" } } }
}
]
}
From that response, the model already knows clima_actual exists, what parameter it expects, and what it does, without anyone writing a prompt describing it by hand. On the client side, calling it is a JSON-RPC message like this:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "clima_actual", "arguments": { "ciudad": "Bogotá" } }
}
The server responds with the result inside content, ready for the client to show the model as if it were the output of any other function.
Getting Started
To try MCP without writing your own server, the official SDK includes reference servers. You need Node.js 18 or higher installed; the following command runs the same on Windows (PowerShell or cmd), macOS, and Linux because it uses npx:
npx -y @modelcontextprotocol/server-filesystem /path/to/your/project
For Claude Desktop to use it, add this to its configuration file (claude_desktop_config.json):
{
"mcpServers": {
"files": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/your/project"]
}
}
}
To confirm the server loaded, open Claude Desktop and look for the tools icon in the message box: clicking it should list files with the file-reading tool available. If it doesn’t appear, check the logs (~/Library/Logs/Claude/ on macOS, %APPDATA%\Claude\logs on Windows) to see the server’s exact startup error.
Real-World Use Cases
The clearest example of why Pi integrated MCP and Codemode together is combining them with the company’s own other tools. Earendil describes, in the same post, a workflow where Pi uses Codemode to combine a Linear MCP server with Jev, its classification engine, to find the most frustrated comments in an issue tracker, all within a single session and without any intermediate step consuming context.
Outside Pi, the pattern repeats: editors like Zed and Cursor expose MCP connectors so the model can read the open repository, environments like Claude Desktop use it to connect Google Drive or a local Postgres database, and internal teams write their own servers to expose legacy systems (a CRM, an internal ticketing tool) without building a custom chatbot for each one.
The list of already published MCP servers includes connectors for GitHub, Google Drive, Slack, Postgres, and Sentry, among dozens more cataloged in the official repository. A team that already has one of those services integrated doesn’t need to write anything: it installs the corresponding server and the client discovers its tools on connecting.
Comparison Table: MCP vs. Other Ways to Connect Tools
| Option | When to Use It | Advantage | Limitation |
|---|---|---|---|
| MCP | Connecting an agent to multiple external tools or data sources | One client talks to multiple servers without rewriting integrations | Each server must implement the protocol; it doesn’t solve the context cost by itself |
| Native API function calling | One-off integration within a single application | Full control over the format, no intermediate protocol | Not portable across providers or reusable in another client |
| Classic REST/OpenAPI API | Exposing a service for traditional HTTP calls | Mature documentation and tooling for over a decade | Designed for humans and deterministic clients, not for a model to discover tools in real time |
| Codemode (execution sandbox) | Orchestrating multiple tool calls with custom logic | Combines results without spending context on every intermediate step | Needs a JavaScript runtime embedded in the client |
Common Mistakes and Best Practices
The most common mistake is connecting too many MCP servers at once and dumping all their tools into the model’s context unfiltered. Every tool adds its description and parameter schema to the system prompt, so ten servers with ten tools each add up to a hundred definitions competing for the model’s attention before it answers a single word. The fix is lazy tool loading: showing the model only the tools from servers relevant to the current task.
The second mistake is about trust: an MCP server runs with whatever permissions the client grants it, and not every published server is audited. Installing a server from an unknown repository and giving it write access to the entire file system is the same decision as installing a browser extension without checking what permissions it asks for.
The third mistake is returning plain text where a data structure would be more appropriate. Many MCP servers were written with harnesses in mind that just insert text into the prompt, so they optimize for that text being short instead of structured. Earendil sums it up by comparing the goal to OpenAPI: tools should return structured data and be discoverable through their documentation, not text the model has to reinterpret.
The fourth mistake is not versioning the server itself. If you change a parameter’s name or a tool’s response format, any client that already memorized it within a long conversation can fail silently. Treating a tool’s schema as a public contract, with additive changes instead of breaking ones, avoids that problem.
Going Deeper: How MCP Scales with Codemode
The Model Context Protocol specification defines capabilities that client and server negotiate on connecting, which allows the protocol to be versioned without breaking existing integrations. But capability negotiation doesn’t solve the composition problem: if a model needs to combine the output of three tools into a calculation, traditionally every intermediate result passes through the conversation’s context.
Codemode attacks that problem from a different angle: instead of the model orchestrating call by call, it writes JavaScript code that combines them, and that code runs in a sandbox on the client side, not on the side where the tools run, which tends to be less trustworthy. Because Codemode runs in the same process as the harness, its state stays in the session transcript instead of being written to disk, and because the sandbox can compile to WASM, it provides isolation without needing a full virtual machine.
flowchart TD
A["Agent or language model"] --> B["Codemode: JavaScript sandbox"]
B --> C["MCP Server: Linear"]
B --> D["MCP Server: Jev"]
C --> E[("Combined result")]
D --> E
subgraph Client["Pi or another agent harness"]
B
C
D
end
Another recent change to the spec is lazy tool loading: instead of announcing all tools on connecting, a server can expose only an initial subset and reveal the rest based on the conversation’s context. It’s the same idea that solved the context problem behind Pi’s original rejection, now built into the protocol itself.
The practical result is that an agent can ask Codemode to use several MCP servers at once without every intermediate step costing context tokens: the code that combines them lives outside the conversation, and only the final result comes back to the model.
Your next step: install the reference server @modelcontextprotocol/server-filesystem using the command from the previous section and connect it to an MCP client you already have installed to watch the list of tools discover itself.
Frequently Asked Questions
What sets MCP apart from a traditional REST API?
A REST API is designed for a developer to integrate by hand, reading its documentation once. MCP is designed for a client to discover a server’s tools at runtime and show them to the model without anyone writing integration code ahead of time.
Do I need to know JavaScript to use Codemode with MCP?
To use it from a client like Pi, no: the model itself writes the code that combines the tools. To write your own MCP server, it does help to know at least one language supported by the official SDK, like JavaScript, Python, or Java.
Is MCP exclusive to Claude?
No. It’s an open protocol: any client, like Pi, Zed, Cursor, or VS Code, can implement it, and any MCP server works with any of those clients without changes.
Can an MCP server access my file system without permission?
Only if the client allows it. The reference file-system server, for example, is limited to the folder you pass it as an argument on startup; that limit is set by whoever configures the client, not by the protocol itself.
Why did Pi reject MCP at first, and what changed its mind?
According to Earendil, the original rejection pointed to MCP being hard to compose and many servers returning text meant for humans. The change came when they noticed that the infrastructure needed to fix that, like lazy tool loading and per-tool metadata, also improved the rest of Pi, so they integrated MCP directly into the core instead of leaving it as a separate extension.
What is Codemode, and how is it different from a regular MCP server?
An MCP server exposes individual tools. Codemode is a sandbox that runs on the client side and lets the model write code that combines several of those tools into a single operation, without spending context on every intermediate step.
References
- Earendil: “You Said No MCP!”: the original post where the company explains why it integrated MCP and Codemode into Pi.
- Model Context Protocol: official documentation: the full protocol specification, primitives, and SDKs.
- Model Context Protocol on GitHub: repositories for the official SDK and reference servers.
- Anthropic: official Model Context Protocol announcement: the original post where Anthropic introduced the protocol as an open standard.
📱 Like this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day.
Featured image: Foto de BoliviaInteligente en Unsplash
Did it work for you? Got a different error? Say so below: questions get answered and help the next reader.
Leave a comment
0 Comments