Loading lesson...
Loading lesson...

Industry standard · November 2024
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 tool results back to the model.
Anthropic published the Model Context Protocol (MCP) specification on 25 November 2024 to solve this fragmentation. 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.
With the learning outcomes established, this module begins by examining mcp architecture: hosts, clients, and servers in depth.
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."
With an understanding of mcp architecture: hosts, clients, and servers in place, the discussion can now turn to transport mechanisms: stdio and sse, which builds directly on these foundations.
MCP supports two transport methods. 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 SSE (Server-Sent Events) transport runs the server as an HTTP server. The client connects via HTTP and receives events as a stream. This is the right choice for remote servers, shared team infrastructure, or tools that must remain running continuously. SSE transport requires HTTPS and authentication; stdio transport relies on process isolation.
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 SSE with authentication.
Common misconception
“stdio transport is less secure than SSE 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. SSE transport exposes an HTTP port and requires explicit authentication to be secure. Neither is inherently superior; each is appropriate for its context.
With an understanding of transport mechanisms: stdio and sse in place, the discussion can now turn to building your first mcp server in python, which builds directly on these foundations.
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())“MCP servers MUST validate all inputs before processing. Servers SHOULD implement appropriate rate limiting. Servers MUST NOT expose sensitive information through error messages.”
Model Context Protocol Specification, 2025-11-25 - Security Considerations, Section 7.2
The specification makes security requirements explicit. Validating inputs prevents injection. Rate limiting prevents abuse of shared servers. Sanitising error messages prevents leaking system structure or credentials to the model, which could then relay them to the user.
With an understanding of building your first mcp server in python in place, the discussion can now turn to connecting to claude desktop, which builds directly on these foundations.
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.
With an understanding of connecting to claude desktop in place, the discussion can now turn to security model and least privilege, which builds directly on these foundations.
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 prompt injection, 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.
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 14 of 25 · Practical Building