Even if Claude could write a script, pre-made scripts offer advantages: - More reliable than generated code - Save tokens (no need to include code in context) - Save time (no code generation required) - Ensure consistency across uses Make clear whether Claude should: - **Execute the script** (most common): "Run `analyze_form.py` to extract fields" - **Read it as reference** (for complex logic): "See `analyze_form.py` for the extraction algorithm" For most utility scripts, execution is preferred. When Claude executes a script via bash: 1. Script code never enters context window 2. Only script output consumes tokens 3. Far more efficient than having Claude generate equivalent code **Best practice**: Place all executable scripts in a `scripts/` subdirectory within the skill folder. ``` skill-name/ ├── SKILL.md ├── scripts/ │ ├── main_utility.py │ ├── helper_script.py │ └── validator.py └── references/ └── api-docs.md ``` **Benefits**: - Keeps skill root clean and organized - Clear separation between documentation and executable code - Consistent pattern across all skills - Easy to reference: `python scripts/script_name.py` **Reference pattern**: In SKILL.md, reference scripts using the `scripts/` path: ```bash python ~/.claude/skills/skill-name/scripts/analyze.py input.har ``` ## Utility scripts **analyze_form.py**: Extract all form fields from PDF ```bash python scripts/analyze_form.py input.pdf > fields.json ``` Output format: ```json { "field_name": { "type": "text", "x": 100, "y": 200 }, "signature": { "type": "sig", "x": 150, "y": 500 } } ``` **validate_boxes.py**: Check for overlapping bounding boxes ```bash python scripts/validate_boxes.py fields.json # Returns: "OK" or lists conflicts ``` **fill_form.py**: Apply field values to PDF ```bash python scripts/fill_form.py input.pdf fields.json output.pdf ``` Handle error conditions rather than punting to Claude. ```python def process_file(path): """Process a file, creating it if it doesn't exist.""" try: with open(path) as f: return f.read() except FileNotFoundError: print(f"File {path} not found, creating default") with open(path, 'w') as f: f.write('') return '' except PermissionError: print(f"Cannot access {path}, using default") return '' ``` ```python def process_file(path): # Just fail and let Claude figure it out return open(path).read() ``` Document configuration parameters to avoid "voodoo constants": ```python # HTTP requests typically complete within 30 seconds REQUEST_TIMEOUT = 30 # Three retries balances reliability vs speed MAX_RETRIES = 3 ``` ```python TIMEOUT = 47 # Why 47? RETRIES = 5 # Why 5? ``` Skills run in code execution environment with platform-specific limitations: - **claude.ai**: Can install packages from npm and PyPI - **Anthropic API**: No network access and no runtime package installation List required packages in your SKILL.md and verify they're available. Install required package: `pip install pypdf` Then use it: ```python from pypdf import PdfReader reader = PdfReader("file.pdf") ``` "Use the pdf library to process the file." If your Skill uses MCP (Model Context Protocol) tools, always use fully qualified tool names. ServerName:tool_name - Use the BigQuery:bigquery_schema tool to retrieve table schemas. - Use the GitHub:create_issue tool to create issues. Without the server prefix, Claude may fail to locate the tool, especially when multiple MCP servers are available.