MCP Security: Threat Boundaries for Local Tools
Understand how MCP host, client, and server trust boundaries can expose data, and how to constrain local tools, permissions, and user consent. MCP does not itself grant every server filesystem access: exposure depends on the server’s configured capabilities, process privileges, host behavior, and user approval.
💡 Plain English Explainer (ELI5)
An MCP client is like a receptionist connecting an AI application to a specialist service. The connection does not make every file available automatically: the server can access what its process and configuration permit. But if you install a local server with broad file or command permissions, and the host passes untrusted content to a model that can call its tools, a mistaken or manipulated tool choice can expose data within that granted scope. MCP’s maintainers classify configured STDIO command execution and intended server capabilities as expected behavior; a real vulnerability requires crossing an access boundary the implementation promised to enforce. MCP Security Policy and Trust Model; MCP Security Best Practices.
Core Concepts & Key Terms
- Host, Client, Server
- The host is the AI application, the client is its protocol connector, and the server provides tools or resources. The protocol connection is not itself an operating-system sandbox.
- STDIO transport
- The host launches a configured local server process and exchanges protocol messages over standard input and output. The process normally has the privileges of its execution environment.
- Tool scope
- The specific operations, paths, accounts, and network destinations that a server can reach. Limit this at both the application layer and the operating-system boundary.
- Confused deputy
- A privileged service uses its own authority for a request without correctly binding the operation to the caller’s authorization. MCP guidance documents this risk in authorization flows.
Step-by-Step Attack Flow
1. Untrusted data enters the host
A user asks an AI application to summarize an issue, email, or repository. That content may include instructions, but it remains untrusted data; prompt injection is a threat-model condition, not proof of an MCP protocol vulnerability.
2. The model selects an available tool
The host presents tools from connected servers. The model may choose a tool based on its interpretation of the task; the MCP Security Policy treats unexpected model-driven tool choice as an application-control issue, not a protocol flaw.
3. The server applies its actual authority
A local server performs its documented operation under the permissions of its process and configuration. If a filesystem tool is scoped to a project, that is its intended boundary; if it is granted a wider scope, those wider capabilities are deployment risk.
4. Data reaches the model or another tool
The returned content can influence later model steps. Exposure occurs only if the server can read the data and the host or an additional permitted tool lets it leave the intended boundary. The current specification calls for explicit user consent before invoking any tool; add stronger confirmation for high-impact actions. See the official MCP security attack flows and mitigations.
Source Code: Flaw vs. Secure Implementation
// RISKY: resolves a model-supplied path but never confines it to the workspace.
import * as fs from "node:fs/promises";
import * as path from "node:path";
async function readWorkspaceFile(inputPath: string) {
const target = path.resolve(WORKSPACE_ROOT, inputPath);
return fs.readFile(target, "utf8");
}
// Defense in depth: canonicalize, check the boundary, reject final symlinks,
// and run the process with OS-level read-only access to only this workspace.
import * as fs from "node:fs/promises";
import { constants } from "node:fs";
import * as path from "node:path";
const workspaceSetting = process.env.MCP_WORKSPACE_DIR;
if (!workspaceSetting) throw new Error("MCP_WORKSPACE_DIR is required");
const root = await fs.realpath(workspaceSetting);
const noFollow = constants.O_NOFOLLOW;
if (noFollow === undefined) {
throw new Error("Use an OS sandbox on platforms without O_NOFOLLOW");
}
async function readWorkspaceFile(inputPath: string) {
if (path.isAbsolute(inputPath)) throw new Error("Absolute paths are not allowed");
const candidate = path.resolve(root, inputPath);
const relative = path.relative(root, candidate);
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
throw new Error("Path escapes the workspace");
}
const canonical = await fs.realpath(candidate);
const canonicalRelative = path.relative(root, canonical);
if (canonicalRelative === ".." || canonicalRelative.startsWith(`..${path.sep}`) || path.isAbsolute(canonicalRelative)) {
throw new Error("Resolved path escapes the workspace");
}
const handle = await fs.open(canonical, constants.O_RDONLY | noFollow);
try {
return await handle.readFile("utf8");
} finally {
await handle.close();
}
}
Engineering Hardening Checklist
- Treat MCP STDIO command execution as an installation and process-isolation boundary: review the configured executable and arguments, then run the server with only the privileges it needs.
- Give filesystem tools a dedicated project directory; never expose
$HOME, credentials, or the host root by default. - Enforce read/write scope in the server and with OS controls such as a restricted container, read-only mounts, and a dedicated unprivileged identity.
- Show the exact tool, target, and side effect, and obtain explicit user consent before invoking any tool; add step-up confirmation for destructive or external actions.
- For networked MCP servers, implement the official authorization guidance; validate OAuth resource and redirect destinations, reject token passthrough, and apply least-privilege scopes.
- Log authorization decisions and tool outcomes without recording credentials or sensitive file contents.
Primary sources: MCP Security Policy and Trust Model; MCP Security Best Practices; MCP Specification 2026-07-28: Security and Trust & Safety.