Skills and Toolchain Integration
By integrating with external toolchains, Skills can accomplish tasks that Claude alone cannot.
This article introduces how to call command-line tools, system tools, and third-party libraries in a Skill, and provides a complete integration example.
Tool Types That Skills Can Call
| Tool Type | Example | Invocation Method |
|---|---|---|
| System Commands | ls、cp、find、wc | subprocess.run() |
| Language Runtime | python、node、bash | subprocess.run() |
| Document Processing Tools | pandoc、pdflatex | subprocess.run() |
| Media Processing Tools | ffmpeg、imagemagick | subprocess.run() |
| Python Libraries | pandas、openpyxl、pypdf | Call directly after import |
| Node.js Tools | prettier、eslint | Invoke via npx |
Before calling external tools, confirm that the tools are installed in the runtime environment. Common tools (python3, pip, node, npm) are preinstalled in the Claude sandbox, but pandoc, ffmpeg, etc. need to be installed separately.
Calling External Commands in Scripts
Python'ssubprocess`subprocess` module is the standard way to call external commands. It is recommended to wrap it uniformly into reusable utility functions.
Example
import subprocess
import sys
import json
def run_command(cmd: list, timeout: int = 60) -> dict:
"""
Run an external command and capture the output
Parameters:
cmd: command list, e.g., ["pandoc", "--version"]
timeout: timeout in seconds, default 60
Returns:
a dict containing success, stdout, stderr, returncode
"""
try:
result = subprocess.run(
cmd,
capture_output=True, # Capture both stdout and stderr
text=True, # Return as a string, not bytes
timeout=timeout
)
return {
"success": result.returncode == 0,
"returncode": result.returncode,
"stdout": result.stdout.strip(),
"stderr": result.stderr.strip()
}
except FileNotFoundError:
return {
"success": False,
"returncode": -1,
"stdout": "",
"stderr": f"Command not found: {cmd
}
except subprocess.TimeoutExpired:
return {
"success": False,
"returncode": -1,
"stdout": "",
"stderr": f"Command execution timed out (exceeded {timeout} seconds)"
}
# Example: use pandoc to convert Markdown to HTML
if __name__ == "__main__":
result = run_command([
"pandoc",
"/mnt/user-data/uploads/readme.md",
"-o", "/mnt/user-data/outputs/readme.html",
"--standalone"
])
if result["success"]:
print("Conversion successful")
else:
print(f"Conversion failed: {result['stderr']}")
sys.exit(1)
Checking Whether a Tool Is Available
Checking whether the required tools are installed before executing a Skill provides clear feedback before the task fails midway.
Example
import shutil
import sys
REQUIRED_TOOLS = [
("pandoc", "sudo apt-get install pandoc"),
("ffmpeg", "sudo apt-get install ffmpeg"),
("convert", "sudo apt-get install imagemagick"),
]
def check_tools(tools: list) -> bool:
"""Check the tool list, return True if all are ready"""
all_ok = True
for tool, install_cmd in tools:
path = shutil.which(tool) # Find the tool path in PATH
if path:
print(f" Installed: {tool} → {path}")
else:
print(f" Not installed: {tool} Install command: {install_cmd}")
all_ok = False
return all_ok
if __name__ == "__main__":
print("Checking tool dependencies...")
if not check_tools(REQUIRED_TOOLS):
print("\n"Please install the missing tools first.")
sys.exit(1)
print("\n"All tools are ready, continue execution.")
检查工具依赖... 已安装:pandoc → /usr/bin/pandoc 未安装:ffmpeg 安装命令:sudo apt-get install ffmpeg 未安装:convert 安装命令:sudo apt-get install imagemagick 请先安装缺少的工具。
Declaring Tool Dependencies in SKILL.md
Use YAML frontmatter'scompatibility`dependencies` field to list all external dependencies, so users know what to prepare before installing the Skill.
---
name: doc-converter
description: >
将文档在不同格式间转换,支持 Markdown、HTML、PDF、Word。
当用户需要格式转换、导出文档时触发。
compatibility:
tools:
- pandoc >= 2.14 # 文档格式转换
- python >= 3.8 # 运行辅助脚本
python_packages:
- pypdf >= 3.0 # PDF 处理
- python-docx >= 1.0
---
Integrating Node.js Tools
Throughnpx`npx`, you can directly call CLI tools from npm packages without global installation.
Example
import subprocess
import sys
def run_prettier(file_path: str) -> bool:
"""Format code files with prettier"""
result = subprocess.run(
["npx", "--yes", "prettier", "--write", file_path],
capture_output=True,
text=True
)
if result.returncode == 0:
print(f"Formatting successful: {file_path}")
return True
print(f"Formatting failed: {result.stderr}")
return False
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python use_node_tool.py <file path>")
sys.exit(1)
sys.exit(0 if run_prettier(sys.argv[1]) else 1)
npx --yesOn first run, the tool package is downloaded automatically, requiring a network connection to the npm registry. In network-restricted environments, install it globally in advance:npm install -g prettier。
Standardizing Tool Call Results
Different tools have different output formats. It is recommended to wrap them uniformly at the Skill layer, exposing only a consistent structure externally.
| Handling Method | Applicable Scenario | Typical Tools |
|---|---|---|
| Capture stdout | The tool outputs the result to standard output | pandoc、wc、cat |
| Check the return code | The tool returns 0 on success and non-zero on failure | Almost all CLI tools |
| Read the output file | The tool writes the result to a specified file | ffmpeg、pandoc -o |
| Parse JSON output | The tool supports the --json parameter | eslint --format json |
Complete Integration Example: Markdown to PDF Report
The following example combines pandoc with a Python script to implement a complete Markdown → PDF generation Skill script.
Example
# Complete workflow: check tool → validate input → call pandoc → confirm output
import subprocess
import shutil
import sys
import os
import json
from datetime import datetime
def check_pandoc() -> bool:
"""Check whether pandoc is available"""
if not shutil.which("pandoc"):
print("Error: pandoc not found, please run: sudo apt-get install pandoc")
return False
return True
def validate_input(md_path: str) -> bool:
"""Validate the input Markdown file"""
if not os.path.exists(md_path):
print(f"Error: file does not exist → {md_path}")
return False
if not md_path.lower().endswith(".md"):
print(f"Error: file must be in .md format → {md_path}")
return False
return True
def convert_to_pdf(md_path: str, output_dir: str) -> dict:
"""Call pandoc to convert Markdown to PDF"""
os.makedirs(output_dir, exist_ok=True)
# Generate an output filename with a timestamp to avoid overwriting old files
base_name = os.path.splitext(os.path.basename(md_path))[0]
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_path = os.path.join(output_dir, f"{base_name}_{timestamp}.pdf")
result = subprocess.run(
[
"pandoc", md_path,
"-o", output_path,
"--pdf-engine=xelatex", # Support Chinese
"-V", "CJKmainfont=SimSun", # Chinese font (adjust according to the environment)
"-V", "geometry:margin=2cm" # Page margin
],
capture_output=True,
text=True,
timeout=120
)
if result.returncode == 0 and os.path.exists(output_path):
size_kb = os.path.getsize(output_path) // 1024
return {
"status": "success",
"output": output_path,
"size_kb": size_kb
}
return {
"status": "error",
"message": result.stderr or "pandoc did not generate an output file"
}
def main():
if len(sys.argv) < 2:
print("Usage: python md_to_pdf.py <Markdown file path>")
sys.exit(1)
md_path = sys.argv[1]
output_dir = "/mnt/user-data/outputs"
# Check step by step; exit immediately if any step fails
if not check_pandoc():
sys.exit(1)
if not validate_input(md_path):
sys.exit(1)
print(f"Converting: {md_path}")
result = convert_to_pdf(md_path, output_dir)
print(json.dumps(result, ensure_ascii=False, indent=2))
sys.exit(0 if result["status"] == "success" else 1)
if __name__ == "__main__":
main()
正在转换:/mnt/user-data/uploads/example_report.md
{
"status": "success",
"output": "/mnt/user-data/outputs/example_report_20260518_102305.pdf",
"size_kb": 248
}
Call this script in SKILL.md:
## 执行转换 获取用户上传的 .md 文件路径后,运行: ```bash python scripts/md_to_pdf.py <文件路径> ``` 解析 JSON 输出: - status 为 "success":调用 present_files 展示 output 路径的文件 - status 为 "error":将 message 内容展示给用户,说明可能原因Other Extensions