The problem MCP solves
Every AI application needs context — files, tickets, databases, calendars — and the ability to act. Before MCP, each pairing was a bespoke integration: the IDE assistant had its own GitHub connector, the chat app another, your internal agent a third. That’s the classic M × N problem: M apps times N tools.
Without MCP
M × N custom integrations. Each app re-implements auth, schemas and error handling for each tool.
With MCP
M + N. Each tool ships one MCP server; each app ships one MCP client. They interoperate.
MCP architecture: hosts, clients and servers
- Host: the application the user interacts with — a desktop chat assistant, a code editor, a custom agent. It manages clients, permissions and the LLM.
- Client: a connector inside the host that maintains a one-to-one session with a single server.
- Server: a program that exposes capabilities from a system — e.g., a Postgres server offering a
querytool and table schemas as resources.
Messages use JSON-RPC 2.0. Two standard transports cover most cases: stdio for local servers launched as a subprocess, and Streamable HTTP for remote servers (with OAuth-based authorisation for protected ones).
The building blocks: tools, resources and prompts
| Primitive | What it is | Controlled by | Example |
|---|---|---|---|
| Tools | Functions the model can call to act or compute | The model (with user approval) | create_issue, run_query, send_email |
| Resources | Read-only data the app can load as context | The application | File contents, DB schema, a wiki page |
| Prompts | Reusable prompt templates with parameters | The user (e.g., slash commands) | “Summarise PR #{number}” |
Clients can offer capabilities back to servers too — for example sampling (a server asks the host’s LLM to generate text) and elicitation (a server asks the user for extra input mid-task).
What happens during an MCP call
MCP vs function calling vs APIs
| REST / GraphQL API | Function (tool) calling | MCP | |
|---|---|---|---|
| What it is | Interface to a service | Model capability to request a tool call | Protocol for serving tools, data and prompts to AI apps |
| Who integrates | Developer, per app | Developer defines tools per app | Server built once; any MCP client discovers it |
| Discovery | Read the docs | Hard-coded in your app | At runtime via tools/list etc. |
| Relationship | Often wrapped by an MCP server | How the model invokes MCP tools | Sits between the two |
They’re layers, not competitors: the API does the work, the MCP server describes and exposes it in a standard way, and function calling is how the model asks to use it.
Build a minimal MCP server in Python
The official Python SDK includes FastMCP, which turns type-hinted functions into MCP tools — the docstring becomes the description the model reads.
server.py# pip install "mcp[cli]"
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("course-catalog")
COURSES = {
"prompt-engineering": "Write reliable prompts: roles, examples, structured output.",
"rag": "Build retrieval-augmented generation apps end to end.",
}
@mcp.tool()
def search_courses(keyword: str) -> list[str]:
"""Search course names and descriptions by keyword. Returns matching course ids."""
k = keyword.lower()
return [cid for cid, desc in COURSES.items() if k in cid or k in desc.lower()]
@mcp.resource("course://{course_id}")
def course_details(course_id: str) -> str:
"""Full description of a course."""
return COURSES.get(course_id, "Course not found")
@mcp.prompt()
def study_plan(course_id: str, weeks: int = 4) -> str:
"""Prompt template that asks for a study plan."""
return f"Create a {weeks}-week study plan for the course '{course_id}'."
if __name__ == "__main__":
mcp.run() # stdio transport by default
Test it with the MCP Inspector (mcp dev server.py), then register it in any MCP-capable host. Many desktop hosts use a JSON config along these lines:
{
"mcpServers": {
"course-catalog": {
"command": "python",
"args": ["/absolute/path/to/server.py"]
}
}
}
Write tool descriptions like prompts — clear, specific, with when-to-use guidance — because they are literally what the model reads when deciding which tool to call. Define precise input schemas (the JSON Schema Builder helps).
MCP security risks
| Risk | What it looks like | Mitigation |
|---|---|---|
| Prompt injection via tool output | A fetched web page or email contains “ignore your instructions and…” | Treat outputs as data; confirm sensitive actions with the user |
| Tool poisoning | Malicious instructions hidden in a tool’s description | Install servers only from trusted sources; review descriptions; pin versions |
| Over-permissioned servers | A “read docs” server holding admin tokens | Least-privilege, scoped credentials per server |
| Data exfiltration | Combining a private-data tool with an outbound tool | Separate sensitive servers; restrict outbound actions; log everything |
When should you use MCP?
- Yes: you want the same tools available across several AI apps, agents or IDEs; you’re publishing an integration for others; you want users to plug in their own servers.
- Maybe not: a single app with two internal functions — plain function calling is simpler.
Discussion