A developer is using a coding assistant inside their IDE. They want their coding assistant to read a GitHub issue, query the staging database for related records, and post a short status to Slack, all without leaving the editor. Each of those services has its own API, its own auth, and its own quirks. Without a shared standard, developers building AI-powered IDEs have to write a custom adapter for every tool, and every other AI product has to write the same adapters all over again.
This is the gap MCP closes. It gives AI applications one common way to talk to external tools and data, so the same GitHub or Slack integration can plug into any compliant agent.
In this article, we’ll cover what MCP is, the problem it solves, and how to expose a small tool through an MCP server.
What is MCP in plain terms?
MCP, short for Model Context Protocol, is an open standard for connecting AI applications to external tools, data, and prompts. It was introduced by Anthropic in late 2024 and has since been adopted across major AI products, frameworks, and IDEs.
A useful loose analogy is USB. Before USB, every peripheral shipped with its own connector and its own driver story. USB gave everyone a shared port, and the ecosystem grew around it. MCP plays a similar role for AI tools. A tool author exposes their capability once through an MCP server, and any MCP-compatible agent can use it.
The N by M integration problem
Imagine you have 5 agent frameworks and 10 tools. Without a standard protocol, each framework needs a custom connector for each tool. That is 5 x 10 = 50 custom integrations.
Now add 5 more tools. You need 25 more integrations. Add another framework and you need 15 more. The cost grows multiplicatively.

A shared protocol collapses the above problem to N + M integrations. Each application learns to
speak MCP once, and each tool exposes itself through MCP once.
How MCP works
Host, client, server
MCP defines three roles that talk over JSON-RPC.
- The host is the AI application the user actually interacts with, such as an IDE, a chat product, or a custom agent.
- The client lives inside the host and manages a single connection to one MCP server. A host can run several clients, one per connected server.
- The server is the process that wraps a specific capability, like a GitHub integration, a filesystem, or a database, and exposes it through MCP.

Hosts decide which servers to launch and which tools to expose to the model. Servers stay focused on doing one job well behind a standard interface.
The three primitives
Servers can expose three kinds of capabilities to a host.
| Primitive | What it is | Example |
|---|---|---|
| Tools | Functions the model can call to take an action | get_current_weather(city), create_issue(title, body) |
| Resources | Read-only context the model or host can fetch | A file, a database row, a wiki page |
| Prompts | Reusable prompt templates the server offers | “Summarize this PR”, “Draft a release note” |
Tools are the most commonly used primitive today and the one we’ll focus on. Resources cover the read-only side, where a server hands the model context without asking it to call a function. Prompts let a server ship opinionated, reusable interactions that a host can surface to the user.
A typical handshake
An MCP session follows a small, predictable sequence of JSON-RPC messages. The first three calls are the ones every session does.
First, the client sends initialize to announce itself and negotiate protocol versions and
capabilities.
{ "method": "initialize", "params": { "protocolVersion": "2024-11-05", "clientInfo": { "name": "ide", "version": "0.1.0" } } }
Next, the client asks the server what it offers with tools/list. The server replies with each
tool’s name, description, and input schema.
{ "method": "tools/list" }
Finally, when the model decides to use a tool, the client sends tools/call with the chosen name
and arguments. The server runs the tool and returns the result.
{ "method": "tools/call", "params": { "name": "get_forecast", "arguments": { "city": "Tokyo", "days": 1 } } }
Building a small MCP server
The clearest way to understand MCP is to expose one tool through it. We’ll build a tiny weather server using the official TypeScript SDK, and then call it from a client the way an agent host would.
Exposing a tool from a server
First, we create the server and give it some metadata. The name and version show up during the
initialize handshake.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({
name: "weather-server",
version: "0.1.0",
});
Next, we register a tool. We give it a name, a short description, an input schema, and a handler. The schema is what the host will pass to the model so it knows how to call the tool.
import { z } from "zod";
server.tool(
"get_current_weather",
"Look up the current weather for a city.",
{ city: z.string() },
async ({ city }) => {
const reading = await fetchWeather(city);
return {
content: [
{ type: "text", text: `${city}: ${reading.summary}` },
],
};
}
);
Finally, we connect a transport. Stdio is the simplest option, and it works well for local servers launched by an IDE or another agent host.
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const transport = new StdioServerTransport();
await server.connect(transport);
That is a complete MCP server. Any compliant client can now discover get_current_weather and call
it.
Calling a tool from a client
On the client side, we wire the same SDK to a transport, list the tools the server offers, and call one of them.
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["./weather-server.js"],
});
const client = new Client({ name: "weather-client", version: "0.1.0" });
await client.connect(transport);
const { tools } = await client.listTools();
const result = await client.callTool({
name: "get_current_weather",
arguments: { city: "Tokyo" },
});
In a real agent, we would feed tools into the model as function definitions and let the model
choose which one to call based on the user’s prompt. The demo below shows that whole loop end to
end.
Interactive example
Ask the agent a question and watch the MCP handshake run end to end.
On connect
Handshake status
- 1Initialize
- 2Tools list
- 3Tools call
Negotiated MCP 2024-11-05 with weather-server v0.1.0.
Discovered 2 tools (get_current_weather, get_forecast).
tools/call
Submit a prompt and the host will pick a tool to call.
Tool picked
The selected weather tool will appear here after you run a prompt.
Where MCP shows up in practice
MCP servers you can use today
A growing set of MCP servers is already available for the products developers reach for every day:
- GitHub for issues, pull requests, and repo search.
- Notion for pages, databases, and search across a workspace.
- Cloudflare for managing workers, DNS, and edge configuration.
- Zapier for fanning out to thousands of existing app integrations.
- Figma for reading designs and components.
- Filesystem for reading and writing local files inside a sandboxed root.
SDKs and implementation support
The official SDKs cover most of the languages teams build agents in, including TypeScript, Python, Java, Kotlin, and C#.
When MCP is the right tool
MCP earns its place when an agent needs to call discrete, well-defined capabilities owned by other systems. It gets in the way when the work is internal, ad hoc, or genuinely conversational.
Use MCP when
- We want our agent to use tools and data sources built by others without writing custom adapters.
- We want a tool we built to be reachable from any MCP-compatible host with no per-host work.
- We need dynamic tool discovery at runtime instead of a fixed, hardcoded list.
- We are wiring an agent into an IDE, chat product, or other host that already speaks MCP.
Reach for something else when
- The tool is a single private function inside our own app and will never be reused elsewhere. A direct function call is simpler.
- We need fine-grained permission and policy enforcement that MCP does not yet standardize.
- We are coordinating reasoning between two agents rather than calling a specific function. That is what A2A is for.
MCP or just a CLI?
A fair question once an agent can run shell commands: if our coding assistant can already shell out
to gh, docker, or kubectl, why does it need an MCP server in front of them?
For well-known developer tools, a CLI (Command-Line Interface) is often the better default. The model already knows the syntax from training, there is nothing to install or schema to load into the context window, and the output is just text. Many MCP servers are thin wrappers around CLIs that the model already uses fluently.
MCP earns its keep when the qualities that make it heavier are also the qualities we need: per-user authentication, structured inputs and outputs, runtime tool discovery, and a permission boundary that does not depend on the agent’s ambient shell access. Those matter most for multi-user, multi-tenant, or audited deployments, exactly the cases where “the agent just runs commands as me” stops being acceptable.
A useful default: start with the CLI, and reach for MCP when we hit a specific limitation it solves, usually auth, multi-tenancy, or dynamic tool discovery. Most production agents use both, picking per integration. There are also bridging tools like mcp2cli that expose an MCP server as a CLI, so the agent only pays the schema cost on demand.
Pitfalls to avoid
MCP is straightforward to adopt, but a few specific failure modes show up once a host connects to more than a handful of servers.
- Tool shadowing across servers. Two servers can ship tools with similar names or descriptions, and the model can pick the wrong one. We audit tool names and descriptions whenever we add a server.
- Context window bloat from many tool definitions. Every connected server adds tool schemas to the prompt. A dozen servers can quietly eat thousands of tokens, so we only connect the ones the current task actually needs.
- Supply-chain risk with community servers. A server is third-party code that runs alongside our agent. We review the source, pin versions, and sandbox where we can, the same way we treat any other dependency.
- No native scope limits, so auth lives at the server. MCP does not define fine-grained permissions. If a server exposes a database, anything the server can read is reachable. We keep credentials narrow and enforce scopes inside the server.
- Network latency on every tool call. Each call is a JSON-RPC round trip, plus whatever the underlying tool does. For chatty tools we prefer local transports, batched calls, or caching.
MCP vs. agent-to-agent protocols
MCP solves the agent-to-tool problem. A separate protocol, Agent-to-Agent (A2A), introduced by Google, solves the agent-to-agent problem. Where MCP exposes specific functions for a model to call, A2A lets one agent hand an open-ended goal to another agent that has its own reasoning, its own tools, and its own way of working.
A useful rule of thumb is that MCP is for “do this specific thing” and A2A is for “achieve this goal.” A travel agent might use MCP to call a flight search API and A2A to delegate hotel selection to a specialized booking agent. The two protocols sit at different layers and complement each other.
Key takeaways
- MCP is an open protocol that connects AI applications to external tools, data, and prompts through a small JSON-RPC surface.
- It collapses the N by M integration problem by giving hosts and tool authors one shared interface instead of a custom adapter per pairing.
- A session is built from three roles and a tiny handshake. Hosts run clients, clients connect
to servers, and the conversation starts with
initialize,tools/list, andtools/call. - Tools, resources, and prompts are the primitives servers expose, with tools being the most commonly used today.
- MCP is for tools, A2A is for agents. Use MCP when our agent needs to call a specific capability, and reach for A2A when it needs to delegate an open-ended goal to another reasoning agent.
- Start with the CLI, add MCP when we need what MCP adds. Auth, multi-tenancy, and structured tool discovery are the usual reasons. Most production agents use both.