MCP Application
Apply the MCP lessons to a client-server integration — discovering and calling tools through the protocol instead of custom code.
What You Will Build
An illustrative MCP integration: an agent with an MCP client that connects to an MCP server, discovers the tools and resources it exposes, and calls them through the protocol — instead of writing custom integration code for each capability. This project applies the MCP, MCP Client, MCP Server, MCP Tools, and MCP Resources lessons, using illustrative examples where actual external execution isn't possible.
Learning Objectives
Understand the request lifecycle between an MCP client and server
Distinguish MCP tools (actions) from MCP resources (read-only content)
Apply tool schemas to a dynamically discovered capability
Understand the permission boundary between a client and a server it connects to
Prerequisites
Concepts Used
Architecture
AI Agent
Decides What It NeedsOnly decides — the client actually executes.
MCP Client
Speaks the ProtocolDiscovers and calls through a standard interface.
MCP Server (Tools + Resources)
Exposes Capability OnceAvailable to any compliant client that connects.
External Capability
Real ActionThe actual system the tool or resource reaches.
Step 1 — Define What the Server Exposes
What are we doing? Defining the tools and resources an MCP server makes available. Why? A server's whole value is exposing a capability once, through a standard protocol, to any client that connects — rather than each consuming application writing its own integration.
{
"tools": [
{ "name": "search_repository", "description": "Search the connected code repository", "inputSchema": { "type": "object", "properties": { "query": { "type": "string" } }, "required": ["query"] } }
],
"resources": [
{ "uri": "repo://readme", "description": "The repository's README file" }
]
}Step 2 — Discover Capabilities from the Client
What are we doing? Having the client ask a connected server what it offers. Why? This is what lets an agent gain new capability just by connecting to a new server, instead of being reconfigured with hardcoded tool definitions.
Client Connects
New SessionEstablishes a link to the server.
Requests Capability List
Asks What's AvailableNo hardcoded tool definitions needed.
Server Responds with Tools + Resources
Full ManifestEverything this server currently exposes.
Client Presents to Model
New CapabilityAvailable the moment the connection is made.
Step 3 — Call a Discovered Tool
What are we doing? Translating the model's decision to use a discovered tool into an actual protocol call. Why? The model just decides what it wants to do — the client is responsible for actually executing that as a real request to the server.
def call_mcp_tool(client, server, tool_name, arguments):
tool = server.get_tool(tool_name)
validate(arguments, tool.input_schema)
result = client.invoke(server, tool_name, arguments)
return mark_as_untrusted_data(result) # still external contentStep 4 — Read a Resource
What are we doing? Pulling read-only content from the server into the agent's context. Why? Not everything the agent needs is an action — resources let it read reference content directly, similar in spirit to retrieval in a RAG system but sourced through MCP.
Warning
Being read-only doesn't make a resource automatically safe — its content still enters the model's context as external, untrusted data.
Step 5 — Enforce the Permission Boundary
What are we doing? Deciding what a given client is actually allowed to do on a server. Why? Not every connecting client should necessarily have the same access — this is enforced server-side, not assumed from the client.
Trusting MCP tool results by default
A tool result from an MCP server is still external content and should be treated as untrusted data.
Exposing more server capability than a use case requires
The same least-privilege principle from agent architecture applies to what a server chooses to expose.
Assuming every connecting client deserves the same access
A server may need to authorize and scope access differently depending on which client connects.
Writing vague tool descriptions on the server
A poorly described tool is just as hard for a connecting model to use correctly as anywhere else.
Challenges
Extend the project yourself. No automated grading — use these to practice reasoning about the architecture.
Challenge 1: Add a second server
Connect the client to a second illustrative MCP server and merge its discovered tools with the first.
Challenge 2: Scope client permissions
Make the server expose different tools depending on which client is connecting.
Challenge 3: Add resource caching
Cache a frequently-read resource on the client side, with a clear invalidation rule.
Design Review
Before moving on, think through these questions the way a reviewer would.
What would happen if the MCP server became unavailable mid-session?
How would you prevent one client from accessing another client's scoped tools?
Where would you add logging to audit what a client actually did through this server?
Interview Questions
How does using MCP change how an agent connects to a new tool provider, compared to custom integration code?
Without MCP, adding a new tool provider means writing custom integration code specific to that provider's API. With MCP, an agent's client can connect to any compliant server and discover its tools and resources dynamically through the same protocol — so gaining a new capability is a matter of connecting to a new server, not reconfiguring the agent.
- Standard protocol vs. custom integration per provider
- Dynamic discovery vs. hardcoded tools
What is the difference between an MCP tool and an MCP resource, and why does it matter for security?
A tool performs an action and may have side effects; a resource is read-only content with no side effects. It matters for security because tools generally need tighter permission checks — since calling one can change something — while resources, though lower-risk, still need access control since their content can be sensitive and still enters the model's context as untrusted external data.
- Tools = actions, resources = read-only
- Both need access control
- Resources still carry injection risk
How would you decide what a connecting MCP client is allowed to do?
I'd enforce that on the server side, scoping which tools and resources are exposed based on the connecting client's identity or role, rather than assuming every client deserves the same access. Following least privilege, a client should only see the capabilities its specific use case actually needs.
- Server-side authorization
- Least privilege per client
- Not every client is equal
Explain It in 30 Seconds
This project applies the MCP lessons to a client-server integration: an MCP client discovers the tools and resources an MCP server exposes, and calls them through the protocol instead of custom integration code — so an agent gains new capability just by connecting to a new server. Tools perform actions and need tight permission checks; resources are read-only but still carry the same untrusted-content risk as any external data. Access is enforced server-side, scoped per connecting client.