Security Considerations for Custom Skills: A Complete Guide to Safe MCP Development

Custom skills run inside Claude's execution environment and require strict input validation, secure secret management via environment variables, and accurate tool annotations to prevent unauthorized access, injection attacks, and information leakage.

When building custom skills for the Model Context Protocol (MCP) in the anthropics/skills repository, understanding security considerations for custom skills is essential to protect both your infrastructure and user data. These skills execute as tools within Claude's environment, making robust security practices critical for safe deployment.

Authentication and Authorization

The MCP Builder best practices documented in skills/mcp-builder/reference/mcp_best_practices.md mandate that skills implement proper authentication mechanisms to prevent unauthorized callers from invoking the skill or accessing protected resources.

OAuth 2.1 and API Key Management

For OAuth 2.1 implementations, use certificates from trusted authorities, validate access tokens rigorously, and accept only tokens explicitly intended for your server.

When using API keys, never hard-code them in source files. Instead, load them from environment variables and validate their presence on startup:

import os
from typing import Optional

def get_api_key() -> Optional[str]:
    """Return the API key from the environment or raise a clear error."""
    key = os.getenv("MY_SKILL_API_KEY")
    if not key:
        raise RuntimeError(
            "API key not configured. Set the MY_SKILL_API_KEY environment variable."
        )
    return key

This pattern prevents accidental secret leakage through Git history or package distribution.

Input Validation and Sanitization

The mcp_best_practices.md file emphasizes comprehensive input validation to block injection attacks and unauthorized file system access.

Preventing Directory Traversal

Sanitize every file-system path to prevent directory traversal attacks using sequences like ../../etc/passwd. Resolve user-provided paths against a base directory and verify the resolved path remains within allowed boundaries:

from pathlib import Path
import os

BASE_DIR = Path("/app/data")   # Directory that the skill is allowed to touch

def safe_join(user_path: str) -> Path:
    """Resolve a user-provided path and ensure it stays inside BASE_DIR."""
    candidate = (BASE_DIR / user_path).resolve()
    if not str(candidate).startswith(str(BASE_DIR)):
        raise ValueError("Attempted directory traversal detected.")
    return candidate

URL and Command Validation

Validate URL formats against whitelisted domains before making external requests. For system calls, reject or escape dangerous characters to prevent command injection. Use schema validators such as Pydantic (Python) or Zod (TypeScript) to enforce type and format compliance on all inputs.

Error Handling and Information Disclosure

According to the error handling guidelines in skills/mcp-builder/reference/mcp_best_practices.md, skills must never expose stack traces or internal diagnostics to clients. Log security-relevant errors server-side only, and return concise, user-friendly messages that guide users without revealing implementation details.

DNS Rebinding Protection

When a skill hosts a local HTTP server for streaming data, bind to 127.0.0.1 instead of 0.0.0.0 to prevent external network exposure. Additionally, validate the Origin header on every request to ensure the request originates from the expected context, preventing cross-site request forgery via DNS rebinding attacks.

Tool Annotations for Safe Execution

The template/SKILL.md file and best practices document recommend declaring tool annotations that inform Claude about a skill's behavioral characteristics. These annotations help the model avoid unsafe patterns by understanding which operations might mutate state or cause side effects.

Annotation Default Meaning
readOnlyHint false Set to true when the skill never mutates external state.
destructiveHint true Set to false for read-only or safe-only operations.
idempotentHint false Set to true if repeated calls with identical arguments are safe.
openWorldHint true Set to false if the skill does not contact external services.

Declare these annotations in your SKILL.md file:

annotations:
  readOnlyHint: false
  destructiveHint: true
  idempotentHint: false
  openWorldHint: true

Secure Development Workflow

The anthropics/skills repository provides tooling to enforce security standards during development. Follow this workflow to maintain security considerations for custom skills throughout the build process:

  1. Start from the template – The template/SKILL.md file outlines required sections, including a security checklist.
  2. Never hard-code secrets – Use os.getenv() (Python) or process.env (Node) and document required environment variables in your skill's README.
  3. Run the quick validator – Execute skills/skill-creator/scripts/quick_validate.py to ensure required metadata and the presence of a SKILL.md file.
  4. Package with package_skill.py – Only after validation passes, use skills/skill-creator/scripts/package_skill.py to create the .skill bundle, ensuring no stray files are included.
  5. Add security tests – Cover input sanitization, authentication failures, and rate-limit handling in your test suite.

Summary

  • Store API keys and secrets in environment variables, never in source code, and validate them on startup using patterns like os.getenv().
  • Sanitize all file paths using the safe_join pattern to prevent directory traversal, validate URLs against whitelists, and use schema validators like Pydantic or Zod for input validation.
  • Never expose stack traces or internal errors to clients; log security issues server-side only and return user-friendly error messages.
  • Bind local servers to 127.0.0.1 instead of 0.0.0.0 and validate Origin headers to prevent DNS rebinding attacks.
  • Declare accurate tool annotations in SKILL.md to help Claude understand your skill's safety characteristics regarding read-only operations, destructiveness, and external service contact.
  • Use the repository's validation scripts (quick_validate.py) and packaging tools (package_skill.py) to enforce security standards before deployment.

Frequently Asked Questions

How should I store API keys in a custom skill?

Store API keys in environment variables using os.getenv() in Python or process.env in Node.js. Never hard-code secrets in your source files, as this risks exposing them through Git history or package distribution. Validate that the environment variable exists on startup and provide clear error messages if it is missing, as shown in the get_api_key() pattern.

What is the best way to prevent directory traversal attacks in file-handling skills?

Use the safe_join pattern implemented in the repository examples: resolve user-provided paths against a base directory using pathlib.Path.resolve(), then verify the resolved path starts with the base directory string. If the path escapes the allowed directory, raise a ValueError. This approach blocks attacks using ../../ sequences to access sensitive system files like /etc/passwd.

Why should I bind local HTTP servers to 127.0.0.1 instead of 0.0.0.0?

Binding to 0.0.0.0 exposes your server on all network interfaces, making it accessible from external machines and vulnerable to DNS rebinding attacks that bypass same-origin policies. Binding to 127.0.0.1 restricts access to localhost only. Combine this with Origin header validation to ensure requests originate from the expected context, preventing cross-site request forgery via DNS rebinding.

What are tool annotations and why do they matter for security?

Tool annotations are metadata declared in your SKILL.md file that inform Claude about a skill's behavioral characteristics, such as whether it is read-only (readOnlyHint), destructive (destructiveHint), idempotent (idempotentHint), or contacts external services (openWorldHint). While these are only hints, they help Claude avoid unsafe patterns by understanding which operations might mutate state or cause side effects, reducing the risk of accidental data loss or unauthorized actions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →