DEV Community

Dockfix Labs
Dockfix Labs

Posted on

Securing MCP Servers: A Practical Guide

What is MCP and Why Does Security Matter?

The Model Context Protocol (MCP) is an open standard that lets AI assistants connect to external tools, databases, and APIs. Claude Code, Cursor, and other AI coding assistants use MCP servers to extend their capabilities.

But every MCP server is a potential attack surface. A malicious or misconfigured MCP server can:

  • Execute arbitrary commands on your machine
  • Read sensitive files (SSH keys, .env, credentials)
  • Send your data to external servers
  • Install malicious packages
  • Override safety instructions in the AI assistant

Common MCP Security Risks

1. Command Injection via Tool Arguments

{
  "tools": [{
    "name": "run_shell",
    "description": "Run a shell command",
    "inputSchema": {
      "type": "object",
      "properties": {
        "command": {"type": "string"}
      }
    }
  }]
}
Enter fullscreen mode Exit fullscreen mode

This tool passes user input directly to a shell. An attacker can craft a malicious prompt that makes the AI execute rm -rf / or exfiltrate data via curl.

2. Unrestricted File Access

{
  "tools": [{
    "name": "read_file",
    "inputSchema": {
      "type": "object",
      "properties": {
        "path": {"type": "string"}
      }
    }
  }]
}
Enter fullscreen mode Exit fullscreen mode

No path validation means the AI can read ~/.ssh/id_rsa, ~/.aws/credentials, or /etc/passwd.

3. External Network Calls

An MCP server that makes HTTP requests to arbitrary URLs can be used as a data exfiltration channel:

@mcp.tool()
def fetch_url(url: str) -> str:
    return requests.get(url).text  # No URL validation!
Enter fullscreen mode Exit fullscreen mode

4. Missing Rate Limits

Without rate limits, an attacker can:

  • Exhaust API quotas
  • Generate huge bills
  • DoS the underlying service

5. Unpinned Dependencies

{
  "dependencies": {
    "some-package": "*"  # Latest version, always
  }
}
Enter fullscreen mode Exit fullscreen mode

If a dependency is compromised (supply chain attack), your MCP server automatically installs the malicious version.

How to Audit Your MCP Configuration

Manual Review Checklist

  1. List all tools -- what can each tool do?
  2. Check input validation -- are arguments sanitized?
  3. Check file access -- is access restricted to specific directories?
  4. Check network calls -- are outbound URLs whitelisted?
  5. Check dependencies -- are they pinned to specific versions?
  6. Check permissions -- does the server run as root?
  7. Check logging -- are secrets being logged?

Automated Scanning with MCP Scanner

pip install dfx-mcp-scanner
Enter fullscreen mode Exit fullscreen mode

The MCP Scanner automatically checks MCP server configurations for the risks above:

# Scan a Claude Code config
mcp-scanner ~/.claude/mcp.json

# Scan a Cursor config
mcp-scanner ~/.cursor/mcp.json

# JSON output for CI/CD
mcp-scanner config.json --format json
Enter fullscreen mode Exit fullscreen mode

It detects:

  • Tools with unrestricted shell access
  • Tools with unrestricted file read/write
  • External network calls without URL validation
  • Missing rate limits
  • Unpinned dependencies
  • Hardcoded credentials in config
  • And more (7 security checks total)

Combining with AgentGuard

For full coverage, use both tools together:

# Scan MCP configuration
mcp-scanner ~/.claude/mcp.json

# Scan the MCP server source code
agentguard ./my-mcp-server/ --format sarif
Enter fullscreen mode Exit fullscreen mode

MCP Scanner audits the configuration. AgentGuard audits the implementation.

Best Practices for MCP Server Authors

1. Validate All Inputs

from pathlib import Path

@mcp.tool()
def read_file(path: str) -> str:
    # Restrict to allowed directory
    allowed = Path("/safe/directory")
    target = (allowed / path).resolve()
    if not str(target).startswith(str(allowed)):
        raise ValueError("Path traversal detected")
    return target.read_text()
Enter fullscreen mode Exit fullscreen mode

2. Whitelist External URLs

ALLOWED_DOMAINS = {"api.github.com", "api.openai.com"}

@mcp.tool()
def fetch_url(url: str) -> str:
    from urllib.parse import urlparse
    domain = urlparse(url).hostname
    if domain not in ALLOWED_DOMAINS:
        raise ValueError(f"Domain {domain} not allowed")
    return requests.get(url, timeout=10).text
Enter fullscreen mode Exit fullscreen mode

3. Pin Dependencies

{
  "dependencies": {
    "requests": "2.31.0",
    "pydantic": "2.5.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Run as Non-Root

USER 1000:1000
Enter fullscreen mode Exit fullscreen mode

5. Add Rate Limits

from collections import defaultdict
from time import time

RATE_LIMIT = {}
RATE_WINDOW = 60  # seconds
RATE_MAX = 10  # calls per window

def check_rate_limit(client_id: str):
    now = time()
    if client_id not in RATE_LIMIT:
        RATE_LIMIT[client_id] = []
    RATE_LIMIT[client_id] = [t for t in RATE_LIMIT[client_id] if now - t < RATE_WINDOW]
    if len(RATE_LIMIT[client_id]) >= RATE_MAX:
        raise Exception("Rate limit exceeded")
    RATE_LIMIT[client_id].append(now)
Enter fullscreen mode Exit fullscreen mode

Conclusion

MCP servers are powerful extensions for AI assistants, but they introduce real security risks. Audit your configurations regularly, validate all inputs, restrict file and network access, and use automated tools to catch issues early.

Tools Mentioned


All tools are MIT-licensed and open source. Install with pip install dfx-agentguard dfx-mcp-scanner.

Top comments (0)