MCP is useful because it lets an AI coding assistant do more than autocomplete text. It can read project files, call external tools, reach databases, and interact with systems your developers use every day.

That is exactly why you should treat MCP as a security boundary, not a convenience setting.

If an MCP-connected agent can touch your repo, shell, tickets, secrets, or internal APIs, the important question is no longer "Is the model smart?" It is:

What can this tool chain do when the prompt is wrong, the output is malicious, or the approval model is too loose?

This guide gives you a practical hardening checklist for MCP servers used in engineering workflows.


What changes when you enable MCP

Without MCP, an AI coding tool mostly produces text.

With MCP, it can:

  • read files and project metadata
  • invoke tools with structured arguments
  • query systems outside the repo
  • receive large tool outputs that may contain sensitive data
  • take actions based on instructions embedded in repo content, tickets, docs, or tool responses

In Anthropic and Cursor, MCP is explicitly designed to connect assistants to external tools and data sources. The official MCP specification also calls out security controls around authorization, tool confirmation, validation, and output handling.

That means your security posture depends on both the model and the tool surface you expose to it.


The short checklist

ControlWhy it matters
Prefer local stdio when possibleSmaller network and auth surface
Limit what the server process can reachMCP roots are informational, not access control; enforce paths in the server config or an OS sandbox
Keep approval on for sensitive toolsPrevents silent high-impact actions
Use environment variables for secretsAvoids hardcoded tokens in checked-in config
Audit and pin MCP serversThird-party server code becomes part of your trust chain
Validate and sanitize tool outputsPrevents accidental leakage and prompt poisoning
Verify all generated changes with static analysisCatches insecure code and removed controls before merge

1. Prefer the smallest transport that solves the problem

If a workflow only needs local repo access, prefer a local stdio server over a remote networked server.

Why:

  • fewer auth flows to maintain
  • less chance of accidental external exposure
  • simpler debugging and incident scope
  • easier local isolation

The MCP specification defines two transports, stdio and Streamable HTTP; Cursor also still accepts SSE, which Claude Code marks as deprecated. Use the more complex transports only when you genuinely need multi-user or remote access.

If you do need remote transport:

  • require HTTPS
  • use short-lived credentials
  • rotate tokens
  • validate redirect URIs and OAuth configuration
  • validate that tokens were issued for your server (audience / resource indicators, RFC 8707) and never pass a client's token through to upstream APIs
  • use PKCE for OAuth
  • publish Protected Resource Metadata (RFC 9728) so clients can discover your authorization server
  • log access to sensitive tools

These requirements come from the MCP authorization specification and its security considerations.


2. Scope file access in the server, not in roots

The MCP specification (2026-07-28) deprecates Roots and says they "are informational guidance rather than an access-control mechanism". A server can read whatever its own process can read, so enforce the boundary where the server runs:

  • start the server with only the project directory it needs, using its own path allowlist or config
  • run servers that execute commands inside an OS-level sandbox or container
  • expose the project root, not your home directory
  • do not mount shared secrets directories into project-scoped tooling
  • separate production infrastructure repos from application repos
  • avoid a single all-powerful server that sees everything

If one agent session can reach several projects, it can also blend instructions and data from multiple contexts. That makes accidental leakage and prompt poisoning easier.

For AI coding, the safe default is:

  1. one repo
  2. one bounded working directory
  3. one job-specific tool set

3. Keep approval on for sensitive operations

Neither Claude Code nor Cursor asks about everything by default. Claude Code's interactive sessions start in auto mode, where a classifier reviews each action (Manual mode asks first). Cursor asks before MCP tool calls by default, and its run modes (Auto-review, Allowlist, Run Everything) decide how much else runs without a prompt. Keep these out of any allowlist and out of fully automatic modes:

  • shell-backed tools
  • database access
  • secret lookups
  • deployment systems
  • ticketing or production admin integrations
  • any tool that writes outside the repo

Do not turn on Cursor's Run Everything mode, or skip Claude Code's permission prompts, until you have a tight allowlist and a clear audit trail.

The common failure mode here is not a Hollywood-style compromise. It is a developer approving too much because the workflow is noisy, repetitive, and feels routine.

If a tool can cause production impact, the approval step is part of the product, not friction to remove.

One gap to know about: in non-interactive runs (claude -p), the Agent SDK and cloud sessions, Claude Code loads servers from a project's .mcp.json without the approval prompt (Claude Code MCP docs). List servers you don't want there in disabledMcpjsonServers.


4. Treat MCP server code as part of your supply chain

An MCP server is not just a connector. It is executable logic with:

  • its own dependencies
  • its own auth behavior
  • its own logging and error handling
  • its own assumptions about safe inputs and outputs

Before adopting a third-party MCP server:

  • review its source
  • pin versions instead of tracking floating main
  • understand what environment variables it expects
  • verify what it logs
  • check whether it enforces auth, rate limits, and path boundaries

If a server has access to customer data, billing systems, infra, or source code, give it the same review bar you would give an internal service with similar privilege.


5. Keep secrets out of checked-in MCP config

Anthropic's project-scoped MCP config and Cursor's server definitions are convenient, but convenience is where teams accidentally commit credentials.

Use environment-variable expansion instead of inline values. Claude Code (.mcp.json):

{
  "mcpServers": {
    "internal-tools": {
      "command": "python",
      "args": ["tools/mcp_server.py"],
      "env": {
        "API_BASE_URL": "${API_BASE_URL}",
        "SERVICE_TOKEN": "${SERVICE_TOKEN}"
      }
    }
  }
}

Cursor's mcp.json uses a different form: "API_BASE_URL": "${env:API_BASE_URL}" (Cursor MCP docs).

Also:

  • scope tokens to the minimum permissions required
  • prefer separate tokens per server, not one shared admin token
  • rotate credentials on server changes or team changes
  • never pass production secrets into experimental servers

6. Plan for prompt injection through tools and tool output

Prompt injection is not limited to user chat messages.

In an MCP workflow, the model can receive instructions from:

  • markdown files in the repo
  • issue tracker tickets
  • pasted logs
  • database records
  • design docs
  • tool responses

That means an MCP server can become a delivery path for malicious or manipulative text, even if the transport itself is perfectly authenticated.

Defensive defaults:

  • sanitize high-risk tool output before returning it
  • truncate oversized responses
  • remove secrets and tokens from logs and tool results
  • require confirmation before executing secondary actions derived from tool output
  • avoid "take whatever the tool returned and immediately act on it" workflows

7. Verify the code after the agent acts

MCP controls help you constrain what the assistant can do. They do not prove the code changes are safe.

That is where Skylos fits.

For repo-level verification:

skylos . -a

For AI-generated PRs or local diffs:

skylos . --diff-base origin/main --danger --quality

For LLM-integrated applications (full guide):

skylos defend .

This gives you three layers:

  1. MCP controls to bound the tool surface
  2. approval controls to review sensitive actions
  3. static analysis and diff analysis to catch insecure or regressive code before merge

If you build the MCP server

Run skylos . --danger on the server's repository: it reports the MCP rules above (SKY-D240–D244) alongside general security findings. To audit which MCP servers are configured on developer machines, use a config scanner such as Snyk Agent Scan (it sends configs to Snyk's API); Skylos scans code, not client configs.

If you use Claude Code, Codex or Cursor, skylos agent install-hooks runs some checks inside the agent loop, locally and without an account: it blocks reads of files that contain a hard-coded secret and installs of packages that don't exist. See agent hooks.


A practical setup for engineering teams

If you want a default setup that is hard to regret, use this:

  1. Local stdio server when possible
  2. Server limited to one project directory (its own config or a sandbox)
  3. Environment variables for credentials
  4. Approval required for high-impact tools
  5. Pinned server versions
  6. Skylos local scan before commit
  7. Skylos diff scan in CI for PRs

That setup will not make MCP risk-free. It will make it understandable, reviewable, and much harder to misuse by accident.

Running this across a team? These checks are free in the CLI. Skylos Cloud keeps the results in one place: the Free plan adds a pass/fail check on pull requests and scan history for one project, and the Workspace plan adds up to 10 projects, approvals for findings you decide not to fix, pull request comments, and audit exports.


Where to go next

References