Model Context Protocol (MCP)
What MCP is, how servers expose tools and resources to any AI client, and how to write a server in Python with FastMCP.
What it is
The Model Context Protocol is an open standard for connecting AI applications to tools and data. An MCP server exposes capabilities, mainly tools the model can call and resources it can read. An MCP client, such as Claude Desktop, Claude Code, an IDE or your own agent, connects to the server, lists what it offers and makes those tools available to the model.
Before MCP every integration was bespoke: your agent had its own code for Slack, its own for Postgres, its own for the file system, and none of it worked in any other agent. With MCP you write the Slack server once and every MCP-capable client can use it. It is to AI tools what USB was to peripherals.
Under the hood it is JSON-RPC over either standard input and output (for local servers launched as a subprocess) or HTTP (for remote servers). The official Python SDK includes FastMCP, which turns a decorated function into a tool with its schema generated from the type hints.
Why it's needed
Agents are only as useful as the tools they can reach, and hand-wiring every tool into every agent does not scale. MCP separates the two: tool builders ship servers, agent builders pick servers. A company can expose its internal systems once and let every AI assistant in the building use them with the same permissions and logging.
For an AI engineer it is now table stakes. Clients ask for 'an MCP server for our API' the way they used to ask for a REST API, and knowing how tools, resources and transports fit together is the difference between integrating in an afternoon and reinventing a protocol.
How it works
- A server declares tools (functions with a name, description and JSON Schema input), resources (readable data addressed by URI, like files or records) and prompts (reusable templates).
- The client starts or connects to the server, calls list_tools, and converts the returned schemas into function-calling tools for the model.
- When the model requests a tool, the client sends call_tool over JSON-RPC, the server runs the function and returns the result, and the client feeds it back to the model.
- Local servers use stdio: the client launches the server as a subprocess and talks over pipes. Remote servers use streamable HTTP with authentication.
- Permissions live in the client. A good client asks the user before running a tool with side effects and logs every call.
- Write tool descriptions as documentation for the model: when to use the tool, what it returns and what it must not be used for.
Code example
# portfolio_mcp.py: expose the portfolio knowledge base to any MCP client
from mcp.server.fastmcp import FastMCP
import numpy as np
from sentence_transformers import SentenceTransformer
mcp = FastMCP("abdur-portfolio")
model = SentenceTransformer("all-MiniLM-L6-v2")
chunks = [p.strip() for p in open("knowledge_base.txt", encoding="utf-8")
.read().split("\n\n") if p.strip()]
index = model.encode(chunks, normalize_embeddings=True)
@mcp.tool()
def search_portfolio(query: str, k: int = 5) -> list[str]:
"""Search Abdur Rehman Afzal's career knowledge base.
Use this before answering any question about his experience,
projects, skills or certifications. Returns the top-k paragraphs."""
q = model.encode([query], normalize_embeddings=True)[0]
top = np.argsort(index @ q)[::-1][:k]
return [chunks[i] for i in top]
@mcp.resource("portfolio://knowledge-base")
def knowledge_base() -> str:
"""The full knowledge base as plain text."""
return "\n\n".join(chunks)
if __name__ == "__main__":
mcp.run() # stdio transport; a client launches this file as a subprocess
# Claude Desktop config: {"mcpServers": {"portfolio":
# {"command": "python", "args": ["portfolio_mcp.py"]}}}
Use cases
- Giving Claude Desktop or Claude Code access to an internal API, database or knowledge base
- Shipping one integration that works across several AI assistants
- Letting a company's agents share a vetted, logged set of tools
- Exposing a RAG index so any client can search it
- Wrapping legacy systems so models can read from them safely
Common mistakes
- Exposing dangerous operations as tools without the client confirming. Delete, pay and send should require a human in the loop.
- Returning huge payloads. A tool that dumps a whole database table into the context wastes tokens and confuses the model. Paginate and summarise.
- Vague descriptions. The model picks tools by reading them; 'query' is useless, 'search Abdur's career knowledge base, call before answering career questions' works.
- Trusting tool output as instructions. Text returned from a tool is data; a document that says 'ignore your rules' is a prompt injection through the tool channel.
- Running remote servers without authentication. An HTTP MCP server is an API; secure it like one.