Hardening MCP servers and clients
The Model Context Protocol makes it easy to plug tools into agents. That ease is the risk. Here is how to run MCP in a company without handing out your data.
In 60 seconds
Hardening MCP servers and clients
The Model Context Protocol makes it easy to plug tools into agents. That ease is the risk. Here is how to run MCP in a company without handing out your data.
Two very different kinds of server
| Local server | Remote server | |
|---|---|---|
| Where it runs | On the user's own machine, started by the host app | On a network service, reached over HTTP |
| Whose rights | Runs as the user: can often read their files, keys and browser data | Has whatever rights its operator gave it |
| Main risks | A malicious or buggy server acts with the user's full local access | Weak or missing authentication; one server serving many users leaks between them |
| Key controls | Install only allow-listed servers; run in a container or sandbox where possible | Strong authentication and per-user authorisation; TLS; logging |
Remote servers: authenticate and authorise
- Require authentication for every remote server. Never leave an internal MCP server open on the network "because it is only internal".
- The MCP specification describes an authorisation approach based on OAuth. Use it, or your company's standard identity system, so each request carries a real user or agent identity.
- Authorise per user, per tool: a server that serves many users must check what this user may see, not just whether the caller is a valid client.
- Do not pass a user's token straight through to other back-end services. Get a token meant for that service (see the delegated access lesson).
A company-wide MCP policy
- 1
Allow-list
Only servers on an approved list may be used. Block the rest in the host's configuration, managed centrally where the tool allows it. - 2
Pin versions
Approve a specific version (and its hash where possible), not "latest". Updates go through review. - 3
Least-privilege tools
Turn off tools a team does not need. A read-only use case gets a read-only server. - 4
Validate input
Every tool checks its arguments like any public API: types, lengths, allowed values, paths inside allowed folders. - 5
Log
Record every tool call with agent, user, arguments and result summary, sent to your central logs.
{
"server": "internal-tickets",
"transport": "https",
"url": "https://mcp.tickets.internal.example/",
"version": "2.3.1",
"sha256": "<hash of the approved package or image>",
"owner": "IT Service Desk platform team",
"auth": "company SSO, per-user tokens",
"allowed_tools": ["search_tickets", "get_ticket", "add_comment"],
"blocked_tools": ["delete_ticket", "change_assignee_bulk"],
"data_class": "internal",
"review_due": "next quarter"
}Input validation still matters
../ paths, a query tool that concatenates SQL. The model is now the one sending the input, and the model can be steered by an attacker. Validate as if every argument came from the internet, because indirectly it did.from pathlib import Path
ALLOWED_ROOT = Path("/srv/agent-docs").resolve()
def read_doc(relative_path: str) -> str:
target = (ALLOWED_ROOT / relative_path).resolve()
# Refuse anything that escapes the allowed folder (e.g. "../../etc/passwd")
if not target.is_relative_to(ALLOWED_ROOT):
raise PermissionError("path outside allowed folder")
if target.suffix not in {".md", ".txt"}:
raise PermissionError("file type not allowed")
return target.read_text(encoding="utf-8")[:20_000] # cap sizePath.is_relative_to needs Python 3.9 or later. The same idea applies in any language: resolve the full path first, then check it is inside the allowed folder.Watch and read more
Lab
A hardened allow-list for three MCP servers and a fixed, safe file tool.
The problem
Part A: your team uses three MCP servers: a local "filesystem" server, a remote internal "tickets" server, and a community "web search" server. Write an allow-list entry for each (use the lesson's format): owner, version, transport, authentication, allowed and blocked tools, data class. For the local server, write two limits on what folders it may reach.
Part B: on your own machine, save the starter code, create a test folder with a few text files, and run it. Then fix the function so the tests at the bottom pass. Use only test files.
Practice lab
Edit the code and run it. Python runs in your browser — nothing is sent to our server.
No output. Add print(...) to see values.
You are done when
Hard questions
Try to answer before you reveal. If you can answer these, you understood the lesson.
Q1Why can an absolute path like "/etc/hosts" escape the folder even though you joined it to ALLOWED_ROOT?Reveal
In Python's pathlib, joining a base path with an absolute path throws away the base: Path("/srv/docs") / "/etc/hosts" becomes /etc/hosts. That is why you must resolve the final path and then check it is inside the root, instead of assuming the join kept you inside.
Q2The community web search server is honest and well written. Why still limit what it can be combined with?Reveal
Because it brings untrusted content (web pages) into the agent's context. If the same agent also has private data and a way to send data out, you have the lethal trifecta, even though every server is honest on its own. Allow-listing is about the combination, not only each server.
Questions people ask
Is MCP itself insecure?
No more than any plug-in system. The protocol connects things; the risks come from which servers you trust, what rights they run with, and how their tools are written. Good policy and good server code make it manageable.
Can we just run all local servers in containers?
That helps a lot: a container can limit which files and networks the server can reach. It does not fix a server that is designed to send your data out through a tool it legitimately has. You still need allow-listing and review.
Who should own an internal MCP server?
The team that owns the system it wraps, with the platform team setting shared standards (authentication, logging, versioning). A server with no owner will not get security fixes.
Do we need to log tool results too?
Log enough to investigate: tool name, arguments, who, when, success or failure, and a summary or hash of results. Full results may contain sensitive data, so store them only where your data rules allow.
Lesson test
5 questions. Get 3 right (60%) to pass and complete this lesson.