Skills Script Extension
In addition to writing instructions in SKILL.md, you can also add executable code to a skill through the scripts directory.
This section will teach you how to use scripts to extend the functionality of a skill.
Why Use Scripts
Using scripts allows you to:
- Encapsulate complex logic and avoid writing large amounts of code in SKILL.md
- Provide tested, reliable implementations
- Allow agents to reuse the same tools and functions
- Handle more complex input/output operations
Tip: When you notice the agent repeatedly implementing the same logic across multiple executions, that's often a good time to create a script.
Script Directory Structure
Scripts should be placed in the scripts/ subdirectory of the skill directory:
Directory Structure
├── SKILL.md
└── scripts/
├── script1.py
├── script2.sh
└── helper.js
Script Writing Guidelines
1. Self-containment
Scripts should be as self-contained as possible, or clearly document their dependencies.
Self-contained Script Example
"""
PDF Text Extraction Script
Dependency: pip install pdfplumber
Run: python scripts/extract_pdf.py input.pdf
"""
import sys
import pdfplumber
def extract_text_from_pdf(pdf_path):
"""Extract text from a PDF file"""
with pdfplumber.open(pdf_path) as pdf:
text = ""
for page in pdf.pages:
text += page.extract_text() or ""
return text
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python extract_pdf.py <pdf file>")
sys.exit(1)
pdf_path = sys.argv[1]
text = extract_text_from_pdf(pdf_path)
print(text)
2. Clear Error Handling
Scripts should provide useful error messages to help agents understand what went wrong.
Error Handling Example
def main():
if len(sys.argv) < 2:
print("Error: Missing required argument", file=sys.stderr)
print("Usage: python script.py <input file> [output file]", file=sys.stderr)
sys.exit(1)
input_file = sys.argv[1]
try:
# Execute main logic
process_file(input_file)
except FileNotFoundError:
print(f"Error: File {input_file} not found", file=sys.stderr)
sys.exit(1)
except PermissionError:
print(f"Error: No permission to read file {input_file}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Error: Unknown error occurred while processing file: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
3. Handling Edge Cases
Scripts should gracefully handle various edge cases instead of crashing.
Edge Case Handling Example
"""Validate whether the input is valid"""
if not text:
return False, "Input cannot be empty"
if len(text) > 10000:
return False, "Input length exceeds the limit (max 10000 characters)"
return True, ""
# Use validation
is_valid, error_msg = validate_input(user_input)
if not is_valid:
print(f"Validation failed: {error_msg}")
sys.exit(1)
Referencing Scripts in SKILL.md
When a skill needs to execute a script, describe in SKILL.md how to call it.
Referencing scripts:
## PDF 文本提取 使用脚本提取 PDF 中的文本: ```bash python scripts/extract_pdf.py input.pdf ``` 脚本会从 input.pdf 提取所有文本并输出到标准输出。 如果需要将结果保存到文件: ```bash python scripts/extract_pdf.py input.pdf > output.txt ```
Note: Use paths relative to the skill root directory when referencing scripts.
Supported Script Languages
The supported script languages depend on the agent implementation you use. Common options include:
| Language | Description |
|---|---|
| Python | The most commonly used choice, powerful and easy to write |
| Bash | Suitable for system operations and command-line tools |
| JavaScript | Suitable for Web-related tasks |
Tip: When choosing a script language, consider the target user's runtime environment. If unsure, Python is the safest choice.
Complete Example: PDF Processing Skill
Let's look at a complete example showing how to combine SKILL.md and scripts:
Skill Structure
├── SKILL.md
└── scripts/
├── extract.py
├── fill_form.py
└── merge.py
SKILL.md content:
---
name: pdf-tool
description: 处理 PDF 文件,包括提取文本、填写表单、合并文件。使用场景:处理 PDF、提取文本、填写表单、合并 PDF。
---
## 提取 PDF 文本
从 PDF 文件中提取文本内容:
```bash
python scripts/extract.py input.pdf
```
输出是提取的纯文本内容。
## 填写 PDF 表单
使用 JSON 数据填写 PDF 表单:
```bash
python scripts/fill_form.py input.pdf data.json output.pdf
```
data.json 格式:
```json
{
"field_name": "字段值",
"another_field": "另一个值"
}
```
## 合并 PDF
将多个 PDF 文件合并为一个:
```bash
python scripts/merge.py output.pdf input1.pdf input2.pdf input3.pdf
```
## 依赖安装
需要安装 pdfplumber 和 PyPDF2:
```bash
pip install pdfplumber PyPDF2
```
Two Usage Modes
There are two ways to use code in a Skill:
| Method | Applicable Scenario | Whether scripts/ directory is needed |
|---|---|---|
| One-shot Command | Only need to call existing tools, no custom logic required | Not needed |
| Self-contained Script | Complex logic that needs to be reused | Needed |
One-shot Commands
When existing package manager tools already meet the requirements, you can directly reference them in the SKILL.md instructions without creating a scripts/ directory.
The following are common execution methods in each language ecosystem:
uvx(Python)
uvx is the tool-running command that comes with uv. It runs Python packages in an isolated environment, and with aggressive caching, repeated runs are nearly instantaneous.
Example
uvx ruff@0.8.0 check .
# Run the specified version of black to format code
uvx black@24.10.0 .
pipx(Python)
pipx is a mature alternative to uvx and can be installed via system package managers (e.g.,brew install pipx)。
Example
pipx run 'black==24.10.0' .
# Run the specified version of ruff
pipx run 'ruff@0.8.0' check .
npx(Node.js)
npx comes with npm and downloads and runs npm packages on demand.
Example
npx eslint@9 --fix .
# Create a project with Vite
npx create-vite@6 my-app
go run(Go)
Go's built-in tool-running command compiles and runs remote packages directly.
Example
go run golang.org/x/tools/cmd/goimports@v0.28.0 .
# Run golangci-lint check
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0 run
Recommendations for writing one-shot commands: Pin version numbers (e.g.,
npx eslint@9.0.0) to ensure consistent behavior; declare prerequisites in SKILL.md (e.g., "Node.js 18+ required"); when commands become complex, extract them into the scripts/ directory.
Referencing Scripts in SKILL.md
Use paths starting from the Skill directory rootrelative pathsto reference packaged files.
The agent will automatically resolve these paths; there is no need to use absolute paths.
First, list the available scripts in SKILL.md:
Example
- **`scripts/validate.sh`** — Validate configuration files
- **`scripts/process.py`** — Process input data
- **`scripts/report.py`** — Generate analysis reports
Then reference them in the operation steps:
Example
1. Run the validation script:
```bash
bash scripts/validate.sh "$INPUT_FILE"
```
2. Process data:
```bash
python3 scripts/process.py --input results.json
```
Script execution paths are relative to the Skill directory root, because the Agent runs commands from the Skill directory. This also applies to auxiliary files such as references/*.md.
Self-contained Scripts
When you need reusable logic, you can package scripts in thescripts/directory and declare dependencies within the script.
The Agent can run it with a single command, with no additional installation steps.
Python Self-contained Script (PEP 723)
PEP 723 defines a standard format for declaring dependencies inside Python scripts.
Use# ///markers to wrap the TOML-format dependency declaration:
Example: Python Self-contained Script
# dependencies = [
# "beautifulsoup4",
# ]
# ///
from bs4 import BeautifulSoup
# Example HTML content
html = '<html><body><h1>Welcome</h1><p class="info">EXAMPLE Test</p></body></html>'
# Parse HTML with BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
# Extract the paragraph text with class="info"
info_text = soup.select_one("p.info").get_text()
print(info_text)
How to run:
Example
uv run scripts/extract.py
# Run with pipx
pipx run scripts/extract.py
EXAMPLE 测试
Deno Self-contained Script
Deno usesnpm:andjsr:import specifiers to achieve self-containment:
Example: Deno Self-contained Script
import * as cheerio from "npm:cheerio@1.0.0";
const html = `<html><body><h1>Welcome</h1><p class="info">EXAMPLE Test</p></body></html>`;
const $ = cheerio.load(html);
console.log($("p.info").text());
Example
Bun Self-contained Script
When Bun has nonode_modulesdirectory, it automatically installs missing packages:
Example: Bun Self-contained Script
import * as cheerio from "cheerio@1.0.0";
const html = `<html><body><h1>Welcome</h1><p class="info">EXAMPLE Test</p></body></html>`;
const $ = cheerio.load(html);
console.log($("p.info").text());
Example
Ruby Self-contained Script
Ruby usesbundler/inlineto declare gem dependencies in the script:
Example: Ruby Self-contained Script
gemfile do
source 'https://rubygems.org'
gem 'nokogiri'
end
html = '<html><body><h1>Welcome</h1><p class="info">EXAMPLE Test</p></body></html>'
doc = Nokogiri::HTML(html)
puts doc.at_css('p.info').text
Example
Key Points for Designing Scripts for Agents
The Agent determines its next actions by reading stdout and stderr, so script design should take the Agent's usage characteristics into account.
Avoid Interactive Prompts
This is a hard requirement. The Agent runs in a non-interactive shell and cannot respond to TTY prompts, password dialogs, or confirmation menus.
All input should be passed via command-line arguments, environment variables, or stdin.
Example: Wrong Approach vs Correct Approach
$ python scripts/deploy.py
Target environment: _
# Right: clear error message and guidance
$ python scripts/deploy.py
Error: --env is required. Options: development, staging, production.
Usage: python scripts/deploy.py --env staging --tag v1.2.3
Provide --help Documentation
--helpOutput is the primary way for the Agent to understand the script's interface.
Include a brief description, available parameters, and usage examples:
Example: Good --help Output
Process input data and generate a summary report.
Options:
--formatFORMAT Output format: json, csv, table(Default: json)
--outputFILE Write output to a file instead of stdout
--verbosePrint progress information to stderr
Examples:
scripts/process.py data.csv
scripts/process.py --format csv --output report.csv data.csv
Write Useful Error Messages
When the Agent sees an error message, it adjusts its strategy accordingly; the more specific the message, the easier it is for the Agent to self-correct.
Example
Error: invalid input
# Good error message
Error: --format must be one of: json, csv, table.
Received: "xml"
Use Structured Output
Prefer structured formats such as JSON, CSV, and TSV for output, rather than free-form text.
Structured formats can be consumed by both the Agent and standard tools (jq, cut, awk), making them easy to combine.
Example
NAME STATUS CREATED
my-service running 2025-01-15
# Recommended: JSON format with clear field boundaries
{"name": "my-service", "status": "running", "created": "2025-01-15"}
Output structured data to stdout, and progress messages and diagnostic information to stderr. This way the Agent gets clean, parseable output while still being able to view diagnostic information when needed.
More Script Design Suggestions
| Suggestion | Description |
|---|---|
| Idempotency | The Agent may retry commands. "Create if it doesn't exist" is safer than "create and error on duplicates" |
| Input constraints | Reject ambiguous input with a clear error, rather than guessing the user's intent |
| Dry-run support | For destructive or stateful operations, provide a--dry-runflag to let the Agent preview the operation |
| Meaningful exit codes | Use different exit codes for different failure types (not found, invalid argument, authentication failure, etc.) |
| Predictable output size | Output a summary by default and provide--offsetother parameters so the Agent can get more information as needed. |
| Safe defaults | Destructive operations should require an explicit confirmation flag (--confirm、--force) |
Complete Skill Script Example
Below is a complete Skill directory structure containing scripts:
Example
├── SKILL.md # Main instruction file
├── scripts/
│ ├── overview.py # Data overview script
│ ├── summary.py # Statistical summary script
│ └── validate.py # Data validation script
├── references/
│ └── api-errors.md # API error reference
└── assets/
└── report-template.md # Report template
The corresponding SKILL.md content:
Example
name: csv-analyzer
description: >
Analyze CSV and tabular data files. When the user has CSV, TSV, or Excel files
and wants to explore, transform, or visualize data, use this.
---
# CSV data analysis
## Available scripts
- **`scripts/overview.py`** — Displays the data overview (row count, column names, types)
- **`scripts/summary.py`** — Generates a statistical summary
- **`scripts/validate.py`** — Validates data quality
## Workflow
1. First, run the overview script to understand the data structure:
```bash
uv run scripts/overview.py input.csv
```
2. Generate the statistical summary:
```bash
uv run scripts/summary.py input.csv --output summary.json
```
3. Validate the data quality:
```bash
uv run scripts/validate.py input.csv
```
## Notes
- For large files, use the `--chunk-size` parameter to process in chunks
- For Chinese CSV files, try the `--encoding gbk` parameter
Script Distribution and Dependencies
If your scripts need external dependencies, you have several options:
1. Document Dependencies in SKILL.md
The simplest way is to record the required dependencies in SKILL.md.
2. Use Virtual Environments
For complex projects, you can create a virtual environment.
3. Use Docker
If a complete runtime environment is needed, you can use a Docker container.
Note: Make sure the scripts can run in the target environment. Before writing scripts, understand the target user's runtime environment.
Summary
In this section, we learned how to use scripts to extend Skill functionality:
- Place scripts in the scripts/ directory
- Scripts should be self-contained and have clear error handling
- Use relative paths when referencing scripts in SKILL.md
- Make sure the scripts can run in the target environment