Loading lesson...
Loading lesson...
A host-owned MCP client uses JSON-RPC over stdio or Streamable HTTP to initialise, discover, call and read, returning structured results or errors. Transport changes where credentials come from, not who approves the call, so the host can refuse a capability the server offers.
MCP standardises the host/client/transport/server exchange. JSON-RPC over stdio or Streamable HTTP, capability negotiation at init, OAuth-style authorisation for HTTP. Spec dated 2025-11-25.
Before November 2024, building a Jira integration for Claude required completely different code than building the same integration for GPT-4. Tools were not portable. Every AI provider had its own function-calling format, its own authentication pattern, and its own way of passing results back to the model.
Anthropic published the first (MCP) specification on 25 November 2024 to solve this fragmentation. The protocol has been revised since, and the quotations in this module are taken from the 2025-11-25 revision. MCP is an open standard: any tool exposed as an MCP server works with any MCP-compatible host, regardless of which AI provider powers the host. Claude Desktop, Cursor, and other clients adopted MCP within weeks of the announcement. By early 2025, hundreds of community-built MCP servers were publicly available for databases, productivity tools, developer platforms, and cloud services.
The standard did not eliminate all integration work. It redirected that work into a defined format with known security properties. Understanding those properties, especially what an MCP server can access by default, is essential before you connect one to a production system.
Before MCP, every AI tool integration required custom code per provider. What does a shared standard actually change in practice, and what security responsibilities does it create?
Module 13 connected agents to services visually. MCP provides a standardised protocol for the same integration at code level. This module teaches you to build and consume MCP servers - the emerging standard for giving agents structured access to external tools.
Next: MCP Architecture: Hosts, Clients, and Servers.
MCP separates tool provision from tool consumption using three roles. A host is the application that runs the AI model, such as Claude Desktop, Claude Code, or a custom application. The host contains an MCP client, which is the protocol layer that connects to MCP servers and makes their tools available to the model. MCP servers are separate processes (or remote services) that expose capabilities without knowing anything about which model or host is using them.
This separation is the key innovation. The model does not call your PostgreSQL database directly. It calls the MCP client, which calls the MCP server, which executes the query with its own credentials. The model never sees the database password.
“MCP provides a standardized way to connect AI assistants to the systems where data lives, including content repositories, business tools, and development environments. The protocol defines how hosts, clients, and servers interact.”
Model Context Protocol Specification, 2025-11-25 - Introduction: What is MCP?
The architecture is deliberately layered. Hosts manage the user interface and model calls. Clients handle protocol negotiation and message routing. Servers implement tool logic. You can update a server without touching the host, and connect a server to any compatible host without rewriting it.
MCP exposes three primitive types to the model. Tools are callable functions that may have side effects, such as creating a GitHub issue or running a database query. Resources are read-only data sources the model can access for context, such as company documentation or a user profile. Prompts are reusable templates that the model or user can invoke, such as "Summarise this pull request" or "Review this code for security issues."
Next: Transport Mechanisms: Stdio and Streamable HTTP.
MCP defines two standard transport mechanisms. The stdio (standard input/output) transport runs the server as a subprocess. Communication happens via the process's stdin and stdout streams. This is the right choice for local tools running on the same machine as the host application: developer tools, local file access, CLI integrations.
The Streamable HTTP transport runs the server as an independent process that can handle multiple client connections. It uses HTTP POST and GET against a single endpoint, and it may use (SSE) to stream several messages back to the client. This is the right choice for remote servers, shared team infrastructure, or tools that must remain running continuously. The specification tells servers to implement proper authentication for all connections and to validate the Origin header; stdio transport relies on process isolation instead.
An earlier HTTP+SSE transport, from protocol version 2024-11-05, still appears in the specification, but only as a deprecated transport that Streamable HTTP replaces and that implementations keep for backwards compatibility. If you read older MCP material that names SSE as the remote transport, this is what it is describing.
If your MCP server is for personal developer use only, use stdio. If ten engineers in your team need to share the same server, use Streamable HTTP with authentication.
Common misconception
“stdio transport is less secure than the remote transport because there is no authentication.”
stdio transport uses process isolation for security: the server runs as a subprocess with the same operating system user permissions as the host application. No network port is exposed. Streamable HTTP exposes an HTTP endpoint, requires servers to validate the Origin header, and needs explicit authentication to be secure. Neither is inherently superior; each is appropriate for its context.
Next: Building Your First MCP Server in Python.
Install the MCP Python SDK with pip install mcp. A minimal server requires three things: declaring which tools it exposes via a list_toolshandler, executing tool calls via a call_tool handler, and running the server via the stdio transport. The structure below is a complete, runnable MCP server that exposes two tools to any MCP-compatible host.
# my_server.py
from mcp.server import Server
from mcp.server.models import InitializationOptions
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import asyncio, json
from datetime import datetime
app = Server("company-tools-v1")
@app.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="get_business_hours",
description="Return business hours for a given office location. Use when users ask about opening times.",
inputSchema={
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "Office location: 'london', 'new_york', or 'singapore'",
"enum": ["london", "new_york", "singapore"]
}
},
"required": ["location"]
}
)
]
BUSINESS_HOURS = {
"london": {"open": "09:00", "close": "17:30", "timezone": "Europe/London"},
"new_york": {"open": "09:00", "close": "18:00", "timezone": "America/New_York"},
"singapore": {"open": "09:00", "close": "18:00", "timezone": "Asia/Singapore"}
}
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "get_business_hours":
location = arguments.get("location", "london")
hours = BUSINESS_HOURS.get(location)
result = hours if hours else {"error": f"Unknown location: {location}"}
return [TextContent(type="text", text=json.dumps(result))]
return [TextContent(type="text", text=json.dumps({"error": f"Unknown tool: {name}"}))]
async def main():
async with stdio_server() as (read_stream, write_stream):
await app.run(read_stream, write_stream,
InitializationOptions(server_name="company-tools", server_version="1.0.0",
capabilities=app.get_capabilities(notification_options=None, experimental_capabilities={})))
if __name__ == "__main__":
asyncio.run(main())Next: Connecting to Claude Desktop.
Claude Desktop reads MCP server configuration from a JSON file. On macOS, this file is at ~/Library/Application Support/Claude/claude_desktop_config.json. Add a key under mcpServers with the server name, the command to launch it, and any required arguments or environment variables.
{
"mcpServers": {
"company-tools": {
"command": "python3",
"args": ["/path/to/my_server.py"],
"env": {
"PYTHONPATH": "/path/to/my_project"
}
}
}
}Restart Claude Desktop after saving the file. The tools declared in your server will appear in Claude's tool list and can be called directly in conversation. Check the Claude Desktop log file at ~/Library/Logs/Claude/mcp-server-company-tools.log if the server fails to start.
Next: Security Model and Least Privilege.
An MCP server configured with stdio transport runs with the same file system and network access as the user who launched the host application. A server that exposes a file-reading tool and accepts an arbitrary path argument could be manipulated, through , into reading /etc/passwd, SSH private keys, or.env files containing API credentials.
Apply the principle of least privilege: expose only the tools that are explicitly needed, scope file access to a specific directory and validate that paths do not traverse outside it using os.path.realpath(), and store all credentials in environment variables rather than hardcoded in the server source code. For SSE transport, require authentication on every endpoint.
The protocol band settles which capabilities are offered and approved; the process band beneath it is everything a locally launched server already holds, and an injected instruction only steers arguments inside that reach, so the narrowing has to happen at launch or not at all.
Capability negotiation bounds the conversation, not the process. A locally launched server holds whatever the account that started it holds, so scope is a launch decision and only the launch can shrink it.
Common misconception
“Because the model calls MCP tools, the model controls what the MCP server can access.”
The MCP server controls its own access. The model only decides which tool to call and what arguments to pass. If the server is configured with broad file system access, that access exists regardless of what the model does or does not intend. Scope and permission decisions belong to the server implementation, not to the model.
Your team wants to expose your company's internal PostgreSQL database to Claude via MCP. The initial plan is a single run_sql tool that accepts a raw SQL string and executes it. What is the primary security risk, and how would you redesign it?
Which transport method is more appropriate for an MCP server that your entire 10-person engineering team will share, and why?
What is the difference between an MCP Tool and an MCP Resource?
You have built an MCP server with a read_file tool that accepts any file path. During testing you notice that when given the path /etc/passwd, the server returns the file contents. What is the correct fix?
Model Context Protocol Specification
modelcontextprotocol.io/specification: Architecture, Primitives, Security
Authoritative protocol specification. Quoted in Sections 14.1 and 14.3 for architecture definitions and required security behaviours.
github.com/modelcontextprotocol/python-sdk
Official Python implementation. Source for all code patterns in Section 14.3, including the Server, Tool, TextContent, and stdio_server classes.
Anthropic MCP announcement, November 2024
anthropic.com/news/model-context-protocol
Context for why MCP was created, the fragmentation problem it addresses, and Anthropic's design goals. Used as the basis for the real-world story in the opening section.
github.com/modelcontextprotocol/servers
Community-maintained list of publicly available MCP servers. Cited to show the breadth of available integrations without building from scratch.
OWASP Top 10 for Large Language Model Applications 2025
LLM01: Prompt Injection
Security risk cited in Section 14.5 for the path traversal and SQL injection risks that arise when MCP tools accept unvalidated inputs from model-generated arguments.
Module 18 of 40 · Practical Building