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

> Learn crucial security considerations for custom skills. Discover input validation, secret management, and tool annotation best practices for safe MCP development in anthropics/skills.

- Repository: [Anthropic/skills](https://github.com/anthropics/skills)
- Tags: security-considerations
- Published: 2026-02-16

---

**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`](https://github.com/anthropics/skills/blob/main/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:

```python
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`](https://github.com/anthropics/skills/blob/main/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:

```python
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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/SKILL.md) file:

```yaml
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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/skills/skill-creator/scripts/quick_validate.py) to ensure required metadata and the presence of a [`SKILL.md`](https://github.com/anthropics/skills/blob/main/SKILL.md) file.
4. **Package with [`package_skill.py`](https://github.com/anthropics/skills/blob/main/package_skill.py)** – Only after validation passes, use [`skills/skill-creator/scripts/package_skill.py`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/quick_validate.py)) and packaging tools ([`package_skill.py`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/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.