Appearance
3.8 — MCP (Model Context Protocol)
1. What is it?
MCP (Model Context Protocol) is an open protocol, originally introduced by Anthropic, that standardizes how AI applications connect to external tools, data sources, and systems. Instead of every AI application writing bespoke integration code for every tool it wants to use (a custom Slack integration, a custom GitHub integration, a custom database integration), MCP defines a common client-server protocol: an "MCP server" exposes a set of tools/resources/prompts in a standard way, and any "MCP client" (an AI application, an agent framework, an IDE) that speaks the protocol can discover and use them, without either side needing to know the other's specific implementation details ahead of time.
2. Why does it exist?
Part 3.3 established tool calling as the mechanism for an LLM to interact with real systems — but that chapter's examples all assumed you write the tool's implementation directly, wired into your specific application. This doesn't scale well across an ecosystem: if every AI application (Claude Desktop, an IDE's AI assistant, a custom agent framework) needs its own bespoke integration code for every tool provider (GitHub, Slack, a company's internal database), you get an M×N integration problem — M applications each needing custom code for N tool providers. MCP exists to collapse this into an M+N problem: a tool provider builds one MCP server, and it becomes usable by any MCP-compatible client, without either side writing integration code specific to the other.
This is precisely analogous to why USB replaced a different proprietary connector for every peripheral, or why REST APIs (Part 1.2) replaced bespoke point-to-point integration protocols — a shared standard interface lets an ecosystem of independently-built pieces interoperate.
3. What problem does it solve?
For an AI FDE specifically, MCP solves a very concrete, recurring problem: enterprise customers already have dozens of internal and SaaS systems (ticketing, CRM, internal wikis, databases, deployment tools) they want an AI system to interact with. Without MCP, each of these integrations is bespoke, custom-built, and non-reusable across projects. With MCP, if a customer already has (or you build once) an MCP server for, say, their internal ticketing system, that same server can be reused across every future AI project that needs ticketing access — a genuine reusability and reduced-integration-cost win that compounds across an FDE's career working with recurring enterprise system categories.
4. How does it work internally?
The client-server architecture
MCP Host/Clientyour AI app, Claude Desktop, an agent framework
MCP Serverwraps a specific tool/data source: GitHub, Postgres, a company's internal ticketing system
An MCP server exposes some combination of three primitive types:
- Tools: functions the model can call, with defined input schemas — structurally, this is exactly Part 3.3's tool calling, just packaged behind the standard protocol instead of hardcoded directly into your application.
- Resources: data the client can read (a file, a database record, a document) — think of these as addressable, readable content the model can pull into context, distinct from an action it takes.
- Prompts: reusable, parameterized prompt templates the server exposes for clients to use — a way for a tool provider to also ship "the recommended way to ask about this data," not just the raw data access itself.
The discovery and invocation flow
When an MCP client connects to a server, it doesn't need any prior knowledge of what that specific server offers — it queries the server (a list_tools style call) and receives back the available tools' names, descriptions, and input schemas, dynamically. This is the direct mechanical reason MCP achieves the M+N reduction from section 2: the client's code doesn't hardcode knowledge of any specific server's tools; it discovers them at connection time and hands them to the LLM exactly the way Part 3.3 described (name, description, JSON Schema), regardless of which server they came from.
Client connects to server
Client calls list_tools()server responds with tool definitions
Client passes tool definitions to the LLMsame mechanism as Part 3.3
LLM decides to call oneclient forwards the call to the MCP server
Server executes the actual action/queryreturns result to client
Client feeds result back to the LLMsame as any tool result (Part 3.3)
Notice that from the LLM's perspective, nothing is different from ordinary tool calling — the model doesn't know or care whether a tool's implementation is a function in your own codebase or a call proxied through an MCP server to a separate system. MCP is entirely a plumbing/integration-architecture concern, not a change to how the model reasons about or requests tool use.
Transport mechanisms
MCP servers can communicate over different transports depending on the deployment context: stdio (the server runs as a local subprocess, communicating over standard input/output — common for local development tools and desktop AI applications accessing local resources) or HTTP with Server-Sent Events (for remote servers accessed over a network — relevant for enterprise deployments where the MCP server wraps a remotely-hosted system). Which transport is appropriate depends entirely on deployment context: a local file-system MCP server naturally uses stdio; a company's centrally-hosted ticketing-system MCP server, accessed by multiple AI applications across an organization, needs a network-accessible transport.
5. Simple mental model
MCP is a standardized power outlet, not a specific appliance. Before a universal outlet standard, every appliance manufacturer might have used a different plug shape, and every building would need custom wiring per appliance. MCP standardizes the "plug shape" for AI-tool integration: any tool provider builds one MCP server (one "appliance" with a standard plug), and it works in any MCP-compatible AI application (any "building" with the standard outlet) — without the appliance manufacturer needing to know which specific buildings will use it, and without the building needing custom wiring for each specific appliance.
6. Real-world example
An enterprise software company builds one MCP server wrapping their internal deployment/CI system (exposing tools like "check deployment status," "trigger a rollback," "view recent build logs"). This single MCP server can then be used by: an internal AI assistant helping engineers via Slack, an AI coding assistant integrated into their IDE, and a customer-facing status-check chatbot (with appropriately scoped permissions for each) — all without rebuilding the deployment-system integration three separate times, and all automatically benefiting from any improvement made to the shared MCP server (a new tool added, a bug fixed) without each consuming application needing its own corresponding update.
7. Architecture diagram
Slack AI assistantMCP client
IDE AI assistantMCP client
Customer-facing chatbotMCP client
Deployment/CI MCP Serverbuilt once, wraps the real CI system
Real internal CI system
8. Production considerations
- Scope each MCP server's exposed tools narrowly and deliberately, exactly as Part 3.3 argued for hand-built tools — an MCP server exposing an overly broad "run arbitrary CI command" tool carries the same excessive-agency risk (Part 9.2) as any hand-built overly-broad tool, and MCP's standardization doesn't change that risk calculus at all.
- Authentication and authorization must be handled explicitly at the MCP server layer — an MCP server is a real network/process boundary with real access to real systems, and needs its own credential management, least-privilege scoping, and audit logging, exactly like any other service integration (Part 9.4, Part 10).
- Version MCP server tool definitions carefully — since multiple independent client applications may depend on a shared server (section 6), a breaking change to a tool's schema affects every consumer simultaneously, which is a stronger backward-compatibility obligation than a single application's own internal tools would carry.
- Treat a third-party MCP server (one you didn't build) as untrusted code/infrastructure until vetted — since it will execute with whatever permissions you grant it and its tool descriptions become part of what your LLM conditions its behavior on (Part 9.1's injection surface extends to untrusted tool descriptions themselves, not just tool results).
9. Common mistakes
- Building an MCP server as a thin, unscoped wrapper around an entire internal API surface ("here's every endpoint our internal system has") instead of a deliberately curated, narrow set of well-described tools appropriate for AI-driven use — this reintroduces the excessive-agency and unreliable-tool-selection problems from Part 3.3 at a larger scale.
- Assuming MCP itself provides security/access-control guarantees — it's a protocol for tool discovery and invocation, not a security boundary; authorization is still entirely the implementer's responsibility.
- Installing/connecting to a third-party MCP server without reviewing what it actually does and what credentials/access it requires — exactly the same due diligence you'd apply to any new dependency with real system access.
- Trusting a previously-approved MCP server's tool definitions as still-safe indefinitely, without re-verifying them on reconnection — the tool-poisoning/rug-pull risk (section 10) means approval isn't a one-time event; a server's tools can legitimately change out from under you between sessions.
10. Security considerations
- Tool descriptions from an MCP server are part of the prompt the LLM sees, exactly as with hand-built tools (Part 3.3) — a malicious or compromised MCP server could supply a deceptively-worded tool description designed to manipulate the model's tool-selection behavior, which is a real, MCP-specific instance of the broader prompt-injection risk category (Part 9.1).
- Tool poisoning / "rug-pull" tool redefinition is a distinct, MCP-specific attack category — not just a variant of the prompt-injection risk above. Because an MCP server's tool definitions (names, descriptions, schemas) are fetched dynamically at connection/discovery time (section 4) rather than hardcoded once into your application, a server can serve a benign, narrowly-scoped tool description the first time a user (or an automated review process) inspects and approves it, then silently change that same tool's description, schema, or underlying behavior on a later connection — after trust has already been granted and the review step has already happened. This is meaningfully worse than a one-time malicious description, because it specifically defeats "we reviewed and approved this tool" as a control: the approval was real and honest at the time it was given, but the tool actually running under that name and ID is no longer the one that was reviewed. Mitigate by pinning/hashing approved tool definitions and re-diffing them on every connection — flagging and re-prompting for approval on any change rather than silently accepting whatever definitions a server returns each time — and by treating any unexpected change to a previously-approved tool's description or schema as a security event worth investigating, not a routine update to wave through.
- Resource access via MCP inherits every access-control consideration from Part 3.4/3.5/9.6 — an MCP server exposing "resources" (readable data) must enforce the same tenant/user-scoped authorization as any other data-access layer; MCP doesn't do this for you automatically.
- Supply-chain risk: installing a third-party MCP server means running code you didn't write with real access to real systems — the same due diligence applied to any third-party dependency (checking the source, understanding what it can access, sandboxing where possible, Part 9.6) applies fully here.
11. Performance considerations
- Each MCP tool call adds the network/process round trip to the MCP server on top of the underlying system call itself — for a remote MCP server over HTTP, this is an additional latency layer worth accounting for in overall pipeline latency (Part 3.3, section 11's tool-call latency considerations compound here).
- Discovery calls (
list_tools, etc.) at connection time add a small amount of startup latency per session — usually negligible, but worth knowing about when reasoning about cold-start latency for a new client session.
12. Cost considerations
- MCP itself introduces no direct LLM-token cost beyond what any tool call already costs (Part 3.3, section 12) — the cost consideration specific to MCP is the engineering investment in building and maintaining a well-scoped server, weighed against the reuse value across multiple consuming applications (section 6) — a real build-vs-reuse ROI calculation an FDE should be explicit about with a customer considering whether to invest in an MCP server versus a one-off integration.
13. When to use it
When a tool/data-source integration is likely to be reused across multiple AI applications or projects (a strong, recurring pattern in enterprise FDE work — the same internal systems tend to come up across multiple engagements), or when adopting an existing, already-built MCP server (from the provider of a SaaS tool the customer uses) saves meaningful integration effort versus building bespoke tool-calling code from scratch.
14. When NOT to use it
- A one-off, single-application integration with no expectation of reuse — the protocol overhead and architectural indirection may not be justified versus simply implementing a tool directly in your application (Part 3.3's plain approach), especially early in a project when speed of iteration matters more than reusability.
- When the deployment context has hard constraints (e.g., no ability to run additional server processes, or a strict no-new-infrastructure security posture) that make standing up a separate MCP server infeasible — direct in-application tool implementation remains valid.
15. Alternatives and trade-offs
| Approach | Good for | Weak point |
|---|---|---|
| MCP server | Reusable across multiple AI applications, standardized discovery | Added architectural layer/infrastructure; needs its own auth/security design |
| Direct in-application tool calling (Part 3.3) | Simplest, fastest to build for a single application | Not reusable across other applications without duplicating the integration |
| Bespoke custom integration protocol | Full control over exact integration details | Reinvents what MCP already standardizes; no ecosystem interoperability |
16. Practical Python/code example
A minimal MCP server exposing one narrowly-scoped tool, illustrating the deliberate-scoping principle from section 8:
python
from mcp.server import Server
from mcp.types import Tool, TextContent
server = Server("ticketing-mcp-server")
@server.list_tools()
async def list_tools() -> list[Tool]:
"""
Advertises the tools this MCP server exposes to any connecting client.
Returns:
list[Tool]: A narrowly-scoped set of tool definitions — read-only status
lookup only, deliberately excluding any destructive ticket operations.
"""
return [
Tool(
name="get_ticket_status",
description="Looks up the current status of a support ticket by its ID.",
inputSchema={
"type": "object",
"properties": {"ticket_id": {"type": "string"}},
"required": ["ticket_id"],
},
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""
Executes a requested tool call against the real ticketing system.
Args:
name (str): The tool name being invoked.
arguments (dict): The arguments supplied for this call.
Returns:
list[TextContent]: The tool's result, formatted for the calling client.
"""
if name == "get_ticket_status":
ticket = await lookup_ticket(arguments["ticket_id"])
return [TextContent(type="text", text=str(ticket))]
raise ValueError(f"unknown tool: {name}")Note: verify the exact current MCP Python SDK API surface against the official MCP documentation/repository before writing production code — this SDK, like the frameworks in Part 4-6, evolves, and exact class/decorator names should be confirmed at write time rather than assumed from memory.
17. Production-quality example
A client-side wrapper that enforces per-user authorization on top of whatever an MCP server itself provides — because, per section 9, MCP does not give you access control for free:
python
import logging
logger = logging.getLogger("mcp_client_wrapper")
class AuthorizedMCPToolProxy:
"""Wraps MCP tool calls with application-level authorization, since MCP itself
provides no access-control guarantees on its own."""
def __init__(self, mcp_session, current_user):
"""
Args:
mcp_session: An active MCP client session connected to a server.
current_user: The authenticated user this proxy's calls are scoped to.
"""
self._session = mcp_session
self._user = current_user
async def call_tool(self, name: str, arguments: dict) -> dict:
"""
Calls an MCP tool only after checking the current user is authorized for it,
and logs every call for audit purposes.
Args:
name (str): The MCP tool name to call.
arguments (dict): Arguments for the call.
Returns:
dict: The tool's result.
Raises:
PermissionError: If the current user isn't authorized for this tool.
"""
if not self._user.is_authorized_for_tool(name):
logger.warning("user=%s denied access to MCP tool=%s", self._user.id, name)
raise PermissionError(f"user not authorized for tool: {name}")
logger.info("user=%s calling MCP tool=%s args=%s", self._user.id, name, arguments)
result = await self._session.call_tool(name, arguments)
return result18. Short exercise
A customer wants to connect their AI assistant to a third-party-provided MCP server for their CRM system, which they didn't build and haven't reviewed the source of. List the specific due-diligence questions you'd want answered (about scope, authentication, data handling) before recommending they connect it to a system with access to real customer data.
19. Interview questions
- Explain the M×N versus M+N integration problem MCP is designed to solve.
- From the LLM's perspective, what's actually different between a tool implemented directly in your application versus one proxied through an MCP server? (Trick question — nothing, mechanically; explain why.)
- Why doesn't adopting MCP for a tool integration automatically solve that integration's authorization/access-control requirements?
20. FDE/customer scenario
Customer: "We've built a few different AI tools internally, and each one has its own custom code to talk to our ticketing system — is there a better way?"
This is close to a textbook MCP adoption case: consolidating those N custom, redundant integrations into one well-scoped, well-secured MCP server that all M internal AI tools can share, reducing both the integration maintenance burden and the number of places ticketing-system credentials and access logic need to be independently gotten right and kept in sync.
Key takeaways
- MCP standardizes tool/data-source integration into a client-server protocol, turning an M×N integration problem into M+N — one server per tool provider, reusable by any compatible client.
- From the LLM's perspective, an MCP-proxied tool call is mechanically identical to a directly-implemented one (Part 3.3) — MCP is purely an integration-architecture layer.
- MCP provides no security/access-control guarantees on its own — authorization must be designed explicitly at the server (and often client) layer.
Things you should be able to explain
- The M×N vs. M+N integration problem MCP solves.
- Why an MCP server needs the same narrow-scoping discipline as any hand-built tool.
Things you should be able to build
- A narrowly-scoped MCP server exposing one tool, plus a client-side authorization wrapper enforcing per-user access control on top of it.
Common mistakes
- Building an unscoped MCP server exposing an entire internal API surface.
- Assuming MCP itself provides authentication/authorization.
- Connecting to an unreviewed third-party MCP server with access to sensitive systems.
Recommended next chapter
09-memory.md