How to Create a Claude Code Skill: A Web Scraping Example with Firecrawl

TL;DR
- What skills are: markdown files (
SKILL.md) that give Claude new capabilities and trigger automatically based on context, using progressive disclosure to keep token costs low. - What you'll build: a complete web-access skill with Firecrawl covering markdown extraction, screenshots, structured data, web search, branding extraction, documentation crawling, and recurring monitoring for competitor pages.
- What you'll learn: the
SKILL.mdformat, how to write descriptions that trigger reliably (with a forced eval hook for ~84% activation), and how to organize multi-feature skills. - Where skills work: the same
SKILL.mdfile runs across Claude Code, the Claude.ai web app, the Claude API, and other coding agents like Codex CLI and Gemini CLI. - No terminal? No problem: you can also create a skill from the Claude.ai web interface via Settings, Capabilities, Skills, without writing a single file by hand.
Skills are how you extend Claude Code. They're markdown files that teach it new capabilities, trigger automatically based on context, and work across every project once you set them up.
This tutorial covers how to build one from scratch. You'll learn the file structure, how to write descriptions that trigger reliably, and how to organize multi-feature skills. The example we'll build handles web access: markdown extraction, screenshots, structured data extraction, web search, and documentation crawling. Web access is a good teaching example because it involves external API calls, multiple use cases in a single skill, and addresses limitations in Claude Code's default capabilities.
By the end, you'll have a working skill and the knowledge to build others for whatever workflows you need.
What are Claude Code skills?
A skill is a folder containing a SKILL.md file that gives Claude Code new abilities. The file contains instructions that Claude follows when the skill activates.
Skills can call external APIs, run scripts, read files, and execute code. They're how you integrate third-party services into Claude Code in a controlled, repeatable way. You define the behavior once, and it works the same every time.
Claude Code also has slash commands, which you invoke explicitly by typing /command. Skills work differently. They trigger through semantic matching: Claude reads your request, compares it against all available skill descriptions, and activates the right one automatically. Ask "get me the markdown from this URL" and a web scraping skill activates. Ask "what's in this PDF" and a PDF skill activates. You don't memorize commands or check documentation.
Two kinds of skills
Before you start building, it helps to know which kind of skill you're actually making. In his breakdown of Claude Code skills, Nate Herk draws a distinction that changes how you scope and write them.
Capability Uplift skills give Claude abilities it doesn't have on its own. Before the skill, Claude can't do the task. After installing it, it can. The Firecrawl web skill we're building in this tutorial is a Capability Uplift skill: Claude Code can't reliably scrape modern websites, take screenshots, or crawl docs without external infrastructure, and the skill bolts those abilities on.
Encoded Preference skills are different. Claude already knows how to do the underlying task. The skill encodes your specific way of doing it. Commit message formatting, code review checklists, weekly status reports, NDA reviews: Claude can do all of these, but an Encoded Preference skill captures the exact process and defaults you want so it doesn't reinvent them each time.
Both kinds load progressively and trigger contextually. But the distinction shapes how you write the description and structure the instructions. Capability Uplift skills lean heavily on bundled scripts and clear trigger phrases ("scrape this page", "take a screenshot"). Encoded Preference skills lean on examples and rules ("here's how we write release notes", "always include a rollback section"). For a curated list of both kinds worth installing, see the best Claude Code skills to try in 2026.
MCP vs. skills
MCP servers are another extension mechanism in Claude Code. They connect Claude to external tools, databases, and APIs. Anthropic's own framing captures the split neatly: MCP provides the professional kitchen (access to tools, ingredients, and equipment), and skills provide the recipes (the step-by-step instructions for turning those tools into something useful). MCP tells Claude what it can do; skills tell Claude how to do it well.
The difference: MCP servers provide tools, skills teach Claude how to use them. An MCP server gives Claude raw access to an API with all its endpoints and parameters. A skill encodes your preferences: which endpoints to call, what defaults to use, how to format output, and how to handle errors. You get consistent behavior instead of Claude figuring out usage from scratch each time. The underlying reason this works so well: CLIs tend to outperform direct API integrations for agents. A model can discover behavior on demand rather than needing the full schema preloaded.
There's also a context window difference. MCP servers load all tool definitions upfront before any conversation starts. A typical multi-server setup can consume 50K+ tokens before you ask anything. Skills use progressive disclosure: only names and descriptions load at startup, full instructions load when activated, and reference files load on-demand. You can have dozens of skills with minimal overhead. Skills also aren't tied to Claude Code specifically. Any coding agent with filesystem access, including Codex CLI and Gemini CLI, can read the same SKILL.md files without modification. Write a skill once; it works wherever the model can read files and run commands. For Codex users looking for the top skills to install, see the best Codex skills guide. If you're deciding between agents, see the Claude Code vs Codex comparison for a side-by-side on harness depth and sandboxing, or Claude Code vs OpenCode if you're evaluating the open-source model-agnostic alternative. If you're evaluating MCP against newer agent communication standards, see our breakdown of MCP vs. A2A protocols.
| MCP servers | Skills | |
|---|---|---|
| What they provide | Raw tool access to an API | Encoded workflow with your defaults baked in |
| Context cost | 50K+ tokens loaded upfront | Name + description only; full content loads on activation |
| Setup | Server process, protocol, transports | Markdown file in a folder |
| Behavior | Claude figures out usage from scratch each time | Consistent, predictable output |
| Portability | Tied to a specific client | Works with any agent that has filesystem access |
| Best for | Maximum API flexibility | Repeatable, opinionated workflows |

Skills live in two places:
- User-level (
~/.claude/skills/): Available across all your projects. Good for personal tools you use everywhere. - Project-level (
.claude/skills/): Committed to git, shared with your team. Good for project-specific workflows.
This tutorial focuses on user-level skills since we're building something you'd want available everywhere, not tied to a single codebase.
Before building your own, it's worth knowing what's already available. Anthropic maintains an official skills repository with production-ready skills for document creation (PDF, DOCX, XLSX, PPTX), Slack GIF creation, and more. The Claude Skills Cookbook has patterns you can adapt. Community collections like Superpowers go further with structured workflow skills for brainstorming, planning, systematic debugging, and verification. These skills enforce process discipline so Claude doesn't skip planning and jump straight to code. You can install any of these with one command:
/plugin add marketplace https://github.com/anthropics/skillsFor a curated overview of what's worth installing, see the best Claude Code skills to try in 2026.
Claude Code also ships with a set of bundled skills out of the box. These are ready-made slash commands you can run without installing anything:
/simplify— rewrites selected code to be more concise and readable/batch— runs a task across multiple files or inputs in a single pass/debug— walks through debugging a problem step-by-step with structured reasoning/loop— iterates over a list of items and applies a task to each/claude-api— provides example code for calling the Anthropic API in various languages
Run /skills in any Claude Code session to see the full list of what's loaded, including both bundled and installed skills.
That said, building from scratch is the best way to understand how the system actually works. The rest of this tutorial does exactly that.
Now let's look at what goes inside that SKILL.md file.
How is a SKILL.md file structured?
Every skill starts with a SKILL.md file. The structure is straightforward: YAML frontmatter at the top, markdown instructions below.
---
name: processing-pdfs
description: Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction.
---
# PDF Processing
Instructions go here...The frontmatter has one required field and several optional ones:
description (required): Maximum 1024 characters. This field determines when your skill activates, so it matters more than anything else in the file. Write it in third person and answer two questions: what does this skill do, and when should Claude use it? Include specific terms users would say. "Extract text and tables" and "PDF files" will match requests better than "helps with documents."
name (optional): Lowercase letters, numbers, and hyphens only. Maximum 64 characters. If you omit this field, Claude Code uses the directory name automatically. Gerund forms work well (processing-pdfs, analyzing-data) but aren't required.
Optional fields give you more control:
allowed-tools: Restricts which tools Claude can use when the skill is active. Set allowed-tools: Read, Grep, Glob for a read-only skill. Set allowed-tools: Read, Bash(python:*) to allow only Python execution. You can scope Bash to specific commands using the Bash(command:*) syntax — for example, Bash(date:*) only allows running date, not arbitrary shell commands. Without this field, Claude can use any available tool.
model: Forces a specific model when the skill runs. Use model: claude-sonnet-4-20250514 for faster responses on simple tasks, or specify Opus for complex reasoning.
disable-model-invocation: Set to true to run the skill without making any LLM calls. Useful for deterministic scripts or shell commands where you don't want Claude reasoning over the output — just execute and return.
user-invocable: Set to false to hide the skill from the /skills list and prevent users from calling it by name. Useful for internal helper skills that other skills reference but that you don't want exposed directly.
argument-hint: A short string shown next to the skill name in /skills output. Use it to describe the expected input format, for example argument-hint: "<url> [--main-only]" so users know what to pass when invoking the skill explicitly.
context: Set to fork to run the skill in an isolated context that doesn't share conversation history with the main session. Good for self-contained tasks like code formatting or data transformation where you don't want the skill to see or modify your current conversation state.
The markdown body contains your actual instructions. Keep it under 500 lines. If you need more space, split content into separate files and link to them:
For API details, see [reference.md](reference.md)
For examples, see [examples.md](examples.md)Claude loads linked files only when needed. This progressive disclosure pattern lets you bundle detailed documentation without paying the token cost upfront. A skill with 2,000 lines of reference material loads the same as a 50-line skill until Claude actually needs that reference.
For multi-file skills, organize your directory like this:
skill-name/
├── SKILL.md # Required - main instructions
├── reference.md # Optional - API details
├── examples.md # Optional - usage examples
└── scripts/
└── validate.py # Optional - utility scriptsScripts execute without loading their contents into context. A 500-line Python validation script consumes zero tokens until it runs, and even then only the output counts.
Rules that silently break a skill
Most skill failures aren't "the script errored". They're "Claude never loaded the skill, and there was no error to tell you why". The platform enforces a handful of strict rules, and violating any of them makes the skill invisible:
SKILL.mdis case-sensitive. The file must be named exactlySKILL.md.skill.md,Skill.md, orSKILL.MDwill not be recognized. No warning, no log line, the skill just won't appear.- Folder names must be kebab-case. Lowercase letters and hyphens only. No spaces (
firecrawl web), no underscores (firecrawl_web), no capitals (FirecrawlWeb). The folder name should match thenamefield in your frontmatter exactly. - Reserved names. Skill names cannot contain
claudeoranthropic. These are reserved and the skill will be rejected. - No XML angle brackets in frontmatter. Frontmatter is injected directly into Claude's system prompt, so anything that looks like an XML tag could inject unintended instructions. The platform blocks it.
- No
README.mdinside the skill folder. All documentation Claude should see goes inSKILL.mdor under areferences/subdirectory. If you're distributing on GitHub, put the human-readable README at the repository root, not inside the skill folder.
If a skill doesn't show up in /skills after a restart, walk through this list first before debugging anything else.
For larger skills, Anthropic's canonical folder layout uses three optional subdirectories:
your-skill-name/
├── SKILL.md # Required: main instructions
├── scripts/ # Optional: executable code (Python, shell, Node)
├── references/ # Optional: docs Claude loads only when needed
└── assets/ # Optional: templates, fonts, icons, brand filesAnything in references/ or assets/ follows the progressive disclosure pattern: it doesn't load until the SKILL.md body tells Claude to read it, so you can ship 10,000 lines of reference material without paying the token cost upfront.
A few optional frontmatter fields are worth knowing about beyond the ones covered above:
license: matters if you're publishing the skill as open source.compatibility: a 1–500 character string describing environment requirements (e.g., "Requires Claude Code with Python 3.9+ and the Firecrawl SDK installed").metadata: any custom key-value pairs.author,version, andmcp-serverare the most common entries.
String substitutions in SKILL.md
Claude Code supports a set of built-in substitutions you can use anywhere in the markdown body of your skill. These get replaced at runtime before Claude reads the content.
$ARGUMENTS — expands to everything the user typed after the skill invocation. If someone calls /firecrawl-web https://example.com --main-only, then $ARGUMENTS becomes https://example.com --main-only. Use this to pass user input directly to a script without Claude having to parse and re-format it.
$ARGUMENTS[N] / $N — expands to a single positional argument. $ARGUMENTS[0] (or $0) is the first word, $ARGUMENTS[1] (or $1) is the second, and so on. Useful when your skill expects a fixed number of inputs and you want to reference them individually.
${CLAUDE_SKILL_DIR} — expands to the absolute path of the folder containing the skill. This is particularly useful for referencing bundled scripts without hardcoding a path:
# Instead of:
python3 ~/.claude/skills/firecrawl-web/fc.py markdown $ARGUMENTS
# Use:
python3 ${CLAUDE_SKILL_DIR}/fc.py markdown $ARGUMENTSThis makes your skill portable — it works whether it's installed in ~/.claude/skills/ (user-level) or .claude/skills/ (project-level) without any changes.
${CLAUDE_SESSION_ID} — expands to a unique identifier for the current Claude Code session. Use this when your skill writes temporary files or logs and you want to avoid collisions across concurrent sessions.
You can also inject dynamic content using the ! backtick syntax. Any line starting with ! followed by a shell command runs that command at skill load time and injects its output into the skill body:
Current date: !`date +%Y-%m-%d`
Git branch: !`git branch --show-current`This is useful for context that changes frequently but is cheap to compute — like the current date, working directory, or active branch name.

Building a Claude Code skill using Firecrawl
Let's create a skill that uses Firecrawl, a web scraping API designed for LLM workflows. It handles JavaScript rendering and outputs clean markdown instead of raw HTML. Firecrawl also has an official web scraping plugin for Claude Code if you'd rather have MCP-style access out of the box; building it as a skill gives you control over which endpoints to expose, what defaults to use, and how output is formatted.
We'll build incrementally, and each feature targets a specific limitation in Claude Code's default capabilities:
-
Blocked web requests: Claude Code can't directly access most websites due to network restrictions and complex site requirements. Firecrawl handles these with dedicated scraping infrastructure.
-
Missing visual context: Claude can't see rendered webpages or JavaScript-loaded content. Firecrawl executes JavaScript and extracts the final rendered content.
-
Unstructured page content: Raw HTML is full of navigation, ads, and scripts that obscure the main content. Firecrawl returns clean markdown optimized for LLMs.
-
Outdated knowledge: Claude's training data has a cutoff date and can't answer questions about recent events. Firecrawl fetches current web content in real-time.
-
Unfamiliar frameworks: Claude needs up-to-date documentation for tools it wasn't trained on. Firecrawl scrapes API docs and technical resources on-demand.
Five problems, one skill. By the end, you'll have something that works and a pattern you can apply to other APIs.
P.S: On February 5, 2026, Anthropic released Claude Opus 4.6, a major upgrade to their smartest model. The major update was Agent teams (research preview) using which one can spin up multiple Claude Code agents that work in parallel. We wrote a detailed guide on Building Apps with Claude Opus 4.6 Agent Teams & Firecrawl Agent.
Prefer no terminal? Build it from the Claude.ai web interface
If you'd rather skip the CLI entirely, Claude has a built-in skill builder in the web app. Go to Settings → Capabilities → Skills, click Add, and you get three options:
- Create with Claude: the lowest-friction path. Claude opens a new chat with a pre-filled prompt and walks you through the skill conversationally, asking clarifying questions as needed. Good when you're not sure how to phrase the description or what to include.
- Write skill instructions: a structured form where you fill in the skill name, description, and instructions yourself. Best when you already know the workflow and want precise control over what Claude can and can't do.
- Upload a skill: if you already have a
SKILL.mdfile (for example, one you built locally or pulled from GitHub), upload it directly. The skill becomes available across your Claude.ai sessions immediately.
The web-interface flow is the fastest way to get a working skill if you're not building scripts. But for anything that calls an external API, runs Python, or needs deterministic behavior, the file-based approach below gives you more control. The rest of this tutorial uses that path.
Creating the skill directory
To get started, let's set up the directory structure and configure Firecrawl authentication.
Want the complete skill? Clone it directly from GitHub and skip to Testing the Firecrawl skill.
First, create the skill folder:
mkdir -p ~/.claude/skills/firecrawl-webYou can try Firecrawl's endpoints without an API key to start. When you're ready to go further, go to firecrawl.dev, create an account, and navigate to the API Keys section in your dashboard for higher rate limits and more credits. The free tier includes 1,000 credits per month, enough to follow this tutorial and test your skill.
Store the key in a .env file in your home directory:
echo 'FIRECRAWL_API_KEY=fc-your-key-here' >> ~/.envOr export it directly in your shell profile (~/.zshrc or ~/.bashrc):
export FIRECRAWL_API_KEY=fc-your-key-hereNow create the two files your skill needs. First, the SKILL.md:
touch ~/.claude/skills/firecrawl-web/SKILL.mdAdd the frontmatter and initial content:
---
name: firecrawl-web
description: "Fetch web content, take screenshots, extract structured data, search the web, and crawl documentation sites. Use when the user needs current web information, asks to scrape a URL, wants a screenshot, needs to extract specific data from a page, or wants to learn about a framework or library."
allowed-tools: ["Bash", "Read", "Write"]
---Below the frontmatter, add:
# Firecrawl Web Skill
This skill provides web access through Firecrawl's API.
## Script Location
All commands use the bundled script:
~/.claude/skills/firecrawl-web/fc.pyThen create the Python script:
touch ~/.claude/skills/firecrawl-web/fc.pyAdd the base structure:
#!/usr/bin/env python3
"""Firecrawl web skill for Claude Code."""
import argparse
import json
import sys
import urllib.request
from pathlib import Path
from dotenv import load_dotenv
from firecrawl import Firecrawl
def main():
load_dotenv()
load_dotenv(Path.home() / ".env")
parser = argparse.ArgumentParser(description="Firecrawl web tools")
subparsers = parser.add_subparsers(dest="command", required=True)
# Subcommands added as we build each feature
args = parser.parse_args()
if __name__ == "__main__":
main()The script uses subcommands, so each feature becomes python fc.py markdown, python fc.py screenshot, etc. We'll add these one at a time.
Feature 1: Markdown extraction
Claude Code's built-in web fetch runs into 403 errors on many sites. Each request carries a clear signature of where it's coming from, and websites block it. Firecrawl's scrape endpoint routes requests through infrastructure designed to handle these access challenges and converts the response to clean markdown.
There's a second reason this matters for a Claude Code skill: token cost. We benchmarked 200 real URLs and found that Firecrawl returns 94% fewer input tokens than raw HTML, about 35,980 fewer tokens per page on average. You stop paying Claude to read nav bars, ad scripts, and footers. On Claude Sonnet 4.6 ($3/M input), that's roughly $1,079 saved per 10,000 scrapes; on Opus 4.7 it's closer to $1,799. The $19/mo Hobby plan pays for itself after 176 scrapes. For more on why this compounds inside an agent loop, see our writeup on Claude Code token efficiency.
Adding the markdown scraping command
Add the following code to fc.py:
def scrape_markdown(url: str, only_main: bool = False) -> str:
"""Scrape a URL and return markdown content."""
app = Firecrawl()
result = app.scrape(
url,
formats=["markdown"],
only_main_content=only_main if only_main else None
)
return result.markdown
# Inside main(), add to subparsers:
md_parser = subparsers.add_parser("markdown", help="Get page as markdown")
md_parser.add_argument("url", help="URL to scrape")
md_parser.add_argument("--main-only", action="store_true", help="Exclude nav/footer")
# And handle the command:
if args.command == "markdown":
content = scrape_markdown(args.url, args.main_only)
print(content)Then add a "Getting Page Content" section to SKILL.md with these usage examples:
# Fetch any webpage as clean markdown
python3 ~/.claude/skills/firecrawl-web/fc.py markdown "https://example.com"
# Cleaner output without navigation and footers
python3 ~/.claude/skills/firecrawl-web/fc.py markdown "https://example.com" --main-onlyFeature 2: Taking screenshots
Screenshots are useful for automation. If you're a technical writer, you can capture homepage screenshots to include as visuals in articles (like this one!). If you're building reports, you can grab the current state of dashboards or landing pages. Markdown can't capture layout, branding, or visual hierarchy.
Firecrawl returns screenshots as temporary URLs hosted on cloud storage rather than inline base64 data. The code checks for this URL response first and downloads the image directly, with base64 handling as a fallback for compatibility.
Adding the screenshot command
Add the following code to fc.py:
import base64
def take_screenshot(url: str, output_path: str = None) -> str:
"""Take a screenshot of a URL."""
app = Firecrawl()
result = app.scrape(url, formats=["screenshot"])
screenshot_data = result.screenshot
# Handle URL response (Firecrawl returns a GCS URL)
if screenshot_data.startswith("http://") or screenshot_data.startswith("https://"):
if output_path:
urllib.request.urlretrieve(screenshot_data, output_path)
return f"Screenshot saved to {output_path}"
return f"[Screenshot URL: {screenshot_data}]"
# Handle base64 data URI response (fallback)
if screenshot_data.startswith("data:image"):
screenshot_data = screenshot_data.split(",", 1)[1]
if output_path:
with open(output_path, "wb") as f:
f.write(base64.b64decode(screenshot_data))
return f"Screenshot saved to {output_path}"
return f"[Screenshot: {len(screenshot_data)} bytes base64]"
# Add subparser:
ss_parser = subparsers.add_parser("screenshot", help="Screenshot a webpage")
ss_parser.add_argument("url", help="URL to capture")
ss_parser.add_argument("--output", "-o", help="Save to file (PNG)")
# Handle command:
if args.command == "screenshot":
result = take_screenshot(args.url, args.output)
print(result)Add a "Taking Screenshots" section to SKILL.md:
# Capture a full-page screenshot
python3 ~/.claude/skills/firecrawl-web/fc.py screenshot "https://example.com" -o page.pngFeature 3: Structured data extraction
Markdown gives you everything on the page, but sometimes you only need specific fields, like a product price, an article title, or a list of features. Parsing that out with string manipulation is fragile and error-prone. A schema tells Firecrawl exactly what to return, and you get consistent JSON every time regardless of how the page layout changes.
Adding the structured data extraction command
Add the following code to fc.py:
def extract_data(url: str, schema: dict, prompt: str = None) -> dict:
"""Extract structured data from a URL using a schema."""
app = Firecrawl()
format_spec = {"type": "json", "schema": schema}
if prompt:
format_spec["prompt"] = prompt
result = app.scrape(url, formats=[format_spec])
return result.json
# Add subparser:
ex_parser = subparsers.add_parser("extract", help="Extract structured data")
ex_parser.add_argument("url", help="URL to extract from")
ex_parser.add_argument("--schema", required=True, help="Path to JSON schema file")
ex_parser.add_argument("--prompt", help="Extraction guidance")
# Handle command:
if args.command == "extract":
with open(args.schema) as f:
schema = json.load(f)
data = extract_data(args.url, schema, args.prompt)
print(json.dumps(data, indent=2))Add an "Extracting Structured Data" section to SKILL.md. First, provide an example schema file:
{
"type": "object",
"properties": {
"title": { "type": "string" },
"price": { "type": "number" },
"features": { "type": "array", "items": { "type": "string" } }
}
}Then the usage examples, which tells Claude where to find the method and schema files:
# Extract using a schema
python3 ~/.claude/skills/firecrawl-web/fc.py extract "https://example.com/product" --schema schema.json
# Add a prompt for better accuracy
python3 ~/.claude/skills/firecrawl-web/fc.py extract "https://example.com/product" --schema schema.json --prompt "Extract the main product details"Feature 4: Web search
Instead of scraping a URL you already know, Firecrawl searches the web and returns markdown content from the results. It's faster than scraping each result individually and offers flexibility through search parameters. We're only adding a results limit here, but the search endpoint docs cover other options like location filtering and content formats. For a deeper look at building search-powered applications, see our guide on combining web search and content extraction with Firecrawl.
This builds on the markdown extraction feature from earlier.
Adding the web search command
Add the following code to fc.py:
def search_web(query: str, limit: int = 5) -> list:
"""Search the web and return results with content."""
app = Firecrawl()
results = app.search(query, limit=limit)
return results.web or []
# Add subparser:
search_parser = subparsers.add_parser("search", help="Search the web")
search_parser.add_argument("query", help="Search query")
search_parser.add_argument("--limit", type=int, default=5, help="Number of results")
# Handle command:
if args.command == "search":
results = search_web(args.query, args.limit)
for r in results:
print(f"## {r.title}")
print(f"URL: {r.url}")
print(r.description or "No description")
print("\n---\n")Add a "Searching the Web" section to SKILL.md:
# Search for current information
python3 ~/.claude/skills/firecrawl-web/fc.py search "Python 3.13 new features"
# Limit results
python3 ~/.claude/skills/firecrawl-web/fc.py search "latest React documentation" --limit 3Feature 5: Documentation crawling
Anthropic models have a cutoff. When you're working with a framework released after that cutoff, or one that's updated frequently, Claude might not know the current API. Crawling the docs gives Claude accurate, up-to-date reference material. Ask it to learn a new library, and it actually can.
Firecrawl's crawl feature automatically discovers and scrapes all accessible pages from a starting URL, rather than just a single page. Point it at a documentation homepage, and it returns the full site content as clean markdown. This gives Claude accurate, up-to-date reference material. For advanced configuration (URL filtering, depth control, and LangChain integration), see our guide on web crawling with Firecrawl's crawl endpoint.
Add the documentation crawling command
Add the following code to fc.py:
def crawl_docs(url: str, limit: int = 50) -> list:
"""Crawl a documentation site."""
app = Firecrawl()
result = app.crawl(
url,
limit=limit,
scrape_options={"formats": ["markdown"], "onlyMainContent": True}
)
return result.data
# Add subparser:
crawl_parser = subparsers.add_parser("crawl", help="Crawl a docs site")
crawl_parser.add_argument("url", help="Starting URL")
crawl_parser.add_argument("--limit", type=int, default=50, help="Max pages")
crawl_parser.add_argument("--output", "-o", help="Save to directory")
# Handle command:
if args.command == "crawl":
pages = crawl_docs(args.url, args.limit)
if args.output:
Path(args.output).mkdir(parents=True, exist_ok=True)
for i, page in enumerate(pages):
filename = f"{args.output}/page_{i:03d}.md"
with open(filename, "w") as f:
f.write(page.markdown or "")
print(f"Saved {len(pages)} pages to {args.output}/")
else:
for page in pages:
title = page.metadata.title if page.metadata else "Untitled"
print(f"## {title}")
print(page.markdown[:1000] if page.markdown else "")
print("\n---\n")Add a "Crawling Documentation" section to SKILL.md:
# Crawl a documentation site
python3 ~/.claude/skills/firecrawl-web/fc.py crawl "https://docs.newframework.dev" --limit 30
# Save pages to a directory
python3 ~/.claude/skills/firecrawl-web/fc.py crawl "https://docs.example.com" --limit 50 --output ./docsEach page costs one credit. Set a reasonable limit to avoid burning through your quota.
Feature 6: Branding extraction
Firecrawl's Branding Format v2 extracts a site's visual identity in one call: logo, colors, typography, spacing, and UI component styles. It's built for AI agents that need to generate on-brand assets or UI without manually reverse-engineering a design system. The v2 update improves logo detection across tricky cases like background-image logos and modern site builders like Wix and Framer. Branding Format v2: Improved Logo Extraction
Add the branding extraction command
Add the following code to fc.py:
def extract_branding(url: str) -> dict:
"""Extract brand identity data from a URL."""
app = Firecrawl()
result = app.scrape(url, formats=["branding"])
return result.branding
# Add subparser:
brand_parser = subparsers.add_parser("branding", help="Extract brand identity data")
brand_parser.add_argument("url", help="URL to extract branding from")
# Handle command:
if args.command == "branding":
branding = extract_branding(args.url)
print(json.dumps(branding, indent=2))Add a "Branding Extraction" section to SKILL.md:
# Extract logo, colors, typography, and UI style tokens
python3 ~/.claude/skills/firecrawl-web/fc.py branding "https://example.com"Feature 7: Monitoring competitor pages
The features above all run on demand: you ask Claude to scrape something, it scrapes. But a lot of the real value of giving Claude web access is ambient, not on-demand: you want to know when a competitor changes their pricing page, ships a new changelog entry, or quietly rewords their homepage. Firecrawl's monitoring feature handles exactly this: it runs recurring scrapes or crawls on a schedule, diffs each result against the last snapshot, and notifies you by webhook or email when something meaningful changes. You can write a plain-English goal and let Firecrawl's judge filter out noise so you only get pinged on changes that actually matter.
Adding the monitoring command
Add the following code to fc.py:
def create_monitor(name: str, urls: list, schedule: str, goal: str, email: str = None) -> dict:
"""Create a Firecrawl monitor for one or more URLs."""
app = Firecrawl()
config = {
"name": name,
"schedule": {"text": schedule, "timezone": "UTC"},
"goal": goal,
"targets": [{"type": "scrape", "urls": urls}],
}
if email:
config["notification"] = {
"email": {
"enabled": True,
"recipients": [email],
"includeDiffs": True,
}
}
monitor = app.create_monitor(**config)
return monitor
# Add subparser:
mon_parser = subparsers.add_parser("monitor", help="Create a recurring monitor")
mon_parser.add_argument("--name", required=True, help="Monitor name")
mon_parser.add_argument("--url", action="append", required=True, help="URL to monitor (repeatable)")
mon_parser.add_argument("--schedule", default="daily", help="e.g. 'daily', 'hourly', 'every 30 minutes'")
mon_parser.add_argument("--goal", required=True, help="Plain-English alert condition")
mon_parser.add_argument("--email", help="Recipient for change alerts")
# Handle command:
if args.command == "monitor":
monitor = create_monitor(args.name, args.url, args.schedule, args.goal, args.email)
print(f"Created monitor {monitor.id}")
print(f"Next run: {monitor.next_run_at}")Add a "Monitoring Pages" section to SKILL.md:
# Track a competitor's changelog daily
python3 ~/.claude/skills/firecrawl-web/fc.py monitor \
--name "Competitor changelog" \
--url "https://competitor.com/changelog" \
--schedule "daily" \
--goal "Notify me when a new changelog entry is published" \
--email "you@example.com"
# Monitor multiple competitor pages on the same schedule
python3 ~/.claude/skills/firecrawl-web/fc.py monitor \
--name "Competitor messaging" \
--url "https://competitor.com" \
--url "https://competitor.com/pricing" \
--url "https://competitor.com/blog" \
--schedule "every 30 minutes" \
--goal "Alert me when headlines, taglines, pricing tiers, or feature copy change" \
--email "you@example.com"Pro tip: Hiba Fathima, Growth Marketing Lead at Firecrawl, uses this skill to set up remote Claude Code routines that track competitors end-to-end: new changelog entries, product updates, fresh blog posts, and messaging changes on landing pages. She points monitors at each competitor's marketing surface, writes the goal in plain English (e.g. "alert me when the hero headline, pricing, or any feature copy changes"). Combined with Claude Code's scheduled routines, the workflow runs in the background and pings her only when something actually moves.
How do you make your Claude Code skill trigger reliably?
Skills activate through LLM reasoning, not keyword matching or embeddings. Claude reads your request, compares it against all available skill descriptions, and picks the best match. The description field determines whether your skill activates.
A good description has two parts: what the skill does, and when Claude should use it. The first part lists capabilities. The second part lists triggers. Both matter. Capabilities alone tell Claude what's possible but not when to reach for it. Triggers alone might activate the skill for wrong reasons.
Here's the pattern in our Firecrawl skill:
description: "Fetch web content, take screenshots, extract structured data, search the web, and crawl documentation sites. Use when the user needs current web information, asks to scrape a URL, wants a screenshot, needs to extract specific data from a page, or wants to learn about a framework or library."Three rules for writing Claude Code skill descriptions
-
Use third person. First person ("I can help you...") and second person ("You can use this to...") clash with Claude's system prompt structure and cause discovery problems. Stick to declarative statements. For example, use "Scrapes websites and returns clean markdown content."
-
Use the words users actually say. "Scrape a URL" matches requests better than "retrieve web content." "Take a screenshot" beats "capture visual representation." Think about how you'd phrase the request, then put those phrases in.
-
Use the full 1024 characters. A description that's too short leaves Claude guessing. Be thorough. Include what the skill does, when to use it, and its outputs and limitations.
After installing your skill, test with varied phrasing. Ask Claude to "get the markdown from this page" and "fetch this URL as text" and "what does this website say." If some phrases don't trigger the skill, add those terms to your description.
Build a trigger test matrix
Before you ship a skill (especially one you'll rely on daily), write a small test matrix and run it deliberately. Two lists:
Should trigger:
"Grab the markdown from https://example.com"
"Scrape this page for me"
"Take a screenshot of stripe.com and save it to ~/Downloads"
"Crawl the Optuna docs"
"Search the web with Firecrawl for the latest on DeepSeek OCR"
Should NOT trigger:
"Read the README in this repo"
"Summarize the file I just opened"
"Help me debug this Python script"
"Write a SQL query for our analytics table"Run 10–20 should-trigger queries and track how many activate the skill automatically versus requiring you to type "with Firecrawl" or invoke it by name. Anthropic's recommended bar is 90%+ automatic activation on relevant requests. If you're below that, the description is the lever to fix it: add the missing trigger phrases, not extra capabilities.
The most common regression: a description edit that narrows triggers too aggressively and breaks previously working queries. After any frontmatter change, rerun the full should-trigger list before considering it shipped. The same matrix doubles as a regression suite when you refactor the skill later.
The specificity matters more than you'd expect. Scott Spence tested 200+ prompts across different description styles and found that vague descriptions like "helps with documents" triggered around 20% of the time. Specific descriptions with explicit triggers hit 80-84%.
Pro tip: you don't have to always write skills from scratch. Hiba Fathima, Growth Marketing Lead at Firecrawl, says her go-to is to complete a new task in Claude Code for the first time — writing a blog post, running a research workflow, whatever it is — and then ask Claude to encapsulate that session into a skill. Every time she reuses the skill and makes tweaks, she asks Claude to update the skill based on what changed in that session. The skill improves automatically as you use it. Read more about her Claude Code workflow here.
Using a forced eval hook to improve skill activation
Even with a well-written description, skills can miss. Claude evaluates descriptions passively, and if the conversation has a lot of context in it, the skill check can get deprioritized. A forced eval hook solves this by explicitly prompting Claude to check its loaded skills before every response.
Add the following to .claude/settings.json (create the file if it doesn't exist):
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "echo 'IMPORTANT: Before responding, review all loaded skills (run /skills if unsure) and activate any that are relevant to this request.'"
}
]
}
]
}
}This UserPromptSubmit hook runs every time a prompt is submitted, injecting a reminder into the context that pushes Claude to actively check loaded skills rather than passively match them. In Scott Spence's testing, this hook implementation pushed activation rates from around 20% to 84% — a meaningful difference if you're building skills you rely on daily.
The tradeoff: the hook adds a small amount of overhead to every prompt. For most workflows this is negligible. If you only use skills occasionally, you might prefer to rely on good description writing alone and skip the hook.
Testing the Firecrawl skill
With everything in place, restart Claude Code to load the skill. Exit your current session and run claude again. Skills don't hot-reload; Claude Code reads the skills directory at startup.
Verify the skill loaded by running /skills. You should see firecrawl-web in the list:

Now test each feature. The results below come from an actual session, and they show how Claude adapts when one approach doesn't work. We explore each one below.
Web search
Claude Code has a built-in WebSearch tool. To use your Firecrawl skill's search instead, include "with Firecrawl" in your prompt. Otherwise, Claude defaults to its native tool.
Try: "Search the web with Firecrawl for the latest on DeepSeek OCR"

Claude finds several results and fetches the full content from each. It pulls the arXiv paper, extracts technical details, and summarizes the architecture:

Check out our guide on using Claude Code for Marketing.
Screenshots
Screenshots work with straightforward prompts. Ask Claude to capture a page and save it somewhere.
Try: "Take a screenshot of the DeepSeek OCR model card page on HuggingFace and save it to Downloads"

Claude runs the screenshot command and saves the image. The skill handles the URL-based response from Firecrawl and downloads the file locally. Here's the captured screenshot:

Documentation crawling
This one's interesting. Vague prompts like "grab the latest docs on OpenAI's Responses API" might trigger markdown extraction instead of a full crawl:

That works for a single page, but if you want multiple pages, use the word "crawl" explicitly.
Try: "Crawl and grab the latest documentation on Optuna"
Claude does something clever here. It doesn't know the Optuna docs URL, so it uses the search feature first to find it, then crawls with a 30-page limit:

The skill saved 30 markdown files to ~/Downloads/optuna-docs/. Claude chained two features together without being told to.
Structured extraction
For the final test, ask Claude to extract structured data without specifying the source. This shows how Claude adapts when its first approach doesn't work.
Try: "Scrape the most popular repositories on GitHub for the past 30 days. Grab the name, URL, author or org, stars, and the programming language."
Claude first tries markdown extraction on GitHub's trending page. When that doesn't return clean structured data, it switches to the schema-based extraction:

The result is a clean table of trending repositories with exactly the fields requested:

This adaptive behavior comes from how the skill description is written. Claude knows multiple approaches exist and picks the right one based on what works.
Where the same skill works
One of the underrated wins of the SKILL.md format is that it isn't tied to Claude Code. The exact same folder, with the exact same frontmatter and instructions, runs across the Claude ecosystem and a growing number of third-party agents.
| Where it runs | How to install |
|---|---|
| Claude Code (CLI, IDE) | Drop the folder in ~/.claude/skills/ (user-level) or .claude/skills/ (project-level), then restart |
| Claude.ai web app | Settings, Capabilities, Skills, then Upload a skill or paste the SKILL.md contents |
| Claude API | Reference the skill from your agent harness or use the skills feature in the API to load it server-side |
| Codex CLI | The same SKILL.md is read directly; no conversion needed |
| Gemini CLI | Reads SKILL.md as instructions; you may need to adjust shell paths if the skill calls scripts |
| Custom agents (Claude Agent SDK, OpenClaw, etc.) | Point the agent at the skills directory; progressive disclosure works the same way |
Two things make this portability work. First, a skill is just a markdown file with a YAML header. Any model that can read files and run scripts can use it. Second, progressive disclosure is handled by the host agent, not the skill itself, so the same file just gets loaded differently depending on where it's used.
The practical takeaway: write a skill once, version it in a git repo, and you get the same workflow whether you're in the terminal, the Claude desktop app, or building a custom agent with the Claude Agent SDK. If you want to see what's already out there before building your own, browse the official Anthropic skills repo or our roundup of the best Claude Code skills to install in 2026.
Distributing a skill beyond your own machine
Once a skill works locally, there are three common ways to get it onto other people's machines:
-
Manual install for individuals. Zip the folder, share it, and the recipient drops it into
~/.claude/skills/or uploads it via Settings, Capabilities, Skills in the Claude.ai web app. Good for one-off sharing or open-source distribution. -
GitHub via the plugin marketplace. Publish the skill as a repo and have users register it as a Claude Code marketplace. Two commands and they're installed:
# Register the repo as a marketplace /plugin marketplace add your-org/your-skill-repo # Install a specific skill from it /plugin install your-skill-name@your-marketplace-namePut the human-readable
README.mdat the repository root, not inside the skill folder, or Claude will refuse to load the skill. -
Org-level deployment. Since December 18, 2025, Claude admins can deploy skills workspace-wide with automatic updates and centralized management. Once a skill is deployed at the org level, every member's Claude instance loads it without any individual install step. This is how internal-only skills (commit-message formats, security review checklists, brand-voice writers) usually ship inside companies.
Anthropic also published Agent Skills as an open standard at agentskills.io. The format is explicitly portable: the same SKILL.md is designed to work in Claude and any other AI platform that adopts the standard. If you're building a skill you'd want to ship beyond the Claude ecosystem eventually, building to that spec keeps the option open.
Skills vs. plugins
Skills and plugins both extend Claude Code, but they're different tools for different situations.
Skills live in a folder, trigger automatically based on context, and encode how Claude should use a specific tool or workflow, with your preferences baked in. They load progressively: only the description at startup, full content when activated. You create them or copy them from a repo, and they work instantly without installation steps or running server processes.
Plugins are packaged integrations installed through Claude Code's plugin system. They bundle MCP servers, tools, and sometimes skills into a single install, and are designed for sharing and distribution. Installing a plugin gives you capabilities someone else has built and maintains. Firecrawl is available as an official Claude Code plugin, a direct, zero-setup way to give Claude live web access without building anything yourself.
| Skills | Plugins | |
|---|---|---|
| What they are | Markdown files you create or copy | Packaged integrations you install |
| Activation | Automatic, based on context | Available after install |
| Loading | Progressive: description first, full content on demand | Bundled at install |
| Setup | Drop a folder, restart Claude Code | Install via plugin system |
| Who creates them | You, or from a public repo | Maintainers and third parties |
| Best for | Custom workflows, specific API preferences | Pre-built integrations, team distribution |
In practice: build a skill when you want precise control over defaults, output format, and triggering behavior for a specific workflow. Install a plugin when you want a maintained, pre-packaged integration without the setup overhead. For a curated list of the most useful options available today, see the best Claude Code plugins for AI development.
Conclusion
You now have a working skill that handles five types of web access: markdown extraction, screenshots, structured data, search, and documentation crawling. The pattern applies to any API you want to integrate. Pick the endpoints that matter, wrap them in a script, write a description with clear triggers, and Claude Code can use it across every project.
The skill directory at ~/.claude/skills/ is yours to expand. Add more Firecrawl features from the API documentation, or build entirely different skills for other services. Once you understand the SKILL.md structure and how descriptions drive activation, the rest is just implementation. Skills aren't exclusive to Claude Code either. The Firecrawl skill is also available through OpenClaw's ClawHub ecosystem for self-hosted personal agents.
Once you've mastered Claude Code Skills, you should explore Claude Code plugins, it has unlocked a new dimension in how developers build and streamline tasks.
P.S: Check out how we built a Claude Skills generator with Firecrawl's Agent endpoint.
Frequently Asked Questions
My Claude Code skill isn't triggering. What's wrong?
Check three things. First, restart Claude Code. Skills load at startup, so changes won't appear until you exit and run `claude` again. Second, run `/skills` to confirm your skill appears in the list. If it's missing, check that your `SKILL.md` file has valid YAML frontmatter with both `name` and `description` fields. Third, review your description. Vague descriptions like "helps with web stuff" rarely trigger. Add specific phrases users would actually say.
Do I need to restart Claude Code after every change?
Yes. Claude Code reads the skills directory once at startup. Any edits to `SKILL.md` or your scripts require a restart. Exit your session and run `claude` again to load the updated skill.
How do I keep my API key secure?
Store it in `~/.env` or export it in your shell profile. Never hardcode keys in your `SKILL.md` or Python scripts. The `python-dotenv` package loads environment variables automatically, and the key stays outside version control.
Do I need a paid Firecrawl plan to build a Claude Code skill?
No, the free tier includes 1,000 credits per month, which should cover creating your first Claude Code skill. Scraping one page costs one credit. Screenshots, markdown extraction, and structured extraction all cost one credit per URL. Search costs one credit per query. Crawling costs one credit per page crawled. For this tutorial, you'll use around 20-50 credits depending on how much you test.
Can I share this skill with my team?
Yes. Move the skill folder from `~/.claude/skills/` to `.claude/skills/` in your project repository and commit it. Anyone who clones the repo gets the skill automatically. Team members will need their own Firecrawl API keys stored in their local environment.
What's the difference between skills and MCP servers?
MCP servers give Claude raw access to an API's tools. Skills teach Claude how to use those tools with your preferences baked in. A Firecrawl MCP server exposes every endpoint with all parameters. This skill exposes five specific features with sensible defaults. Skills also load progressively, consuming fewer tokens than MCP servers that load everything upfront.
Can I use multiple Claude Code skills together?
Yes, Claude can activate different skills for different parts of a conversation. If you have a Firecrawl skill and a PDF skill, Claude will use Firecrawl for web requests and the PDF skill for document processing. Skills don't conflict as long as their descriptions target different use cases.
What's the difference between skills and slash commands?
Slash commands are explicit. You type `/command` and Claude runs a predefined prompt. Skills, on the other hand, are implicit. Claude reads your request, matches it against skill descriptions, and activates the right one automatically. Use slash commands for actions you repeat verbatim, like `/commit` or `/review`. Use skills for capabilities Claude should discover on its own based on context. A Firecrawl slash command would require you to remember and type it. A Firecrawl skill activates when you say "grab the markdown from this page" without thinking about which tool to invoke.
Can I install pre-built skills instead of building from scratch?
Yes. Anthropic maintains an official skills repository at github.com/anthropics/skills with production-ready skills for document creation (PDF, DOCX, XLSX, PPTX), Slack GIF creation, and more. Community collections like Superpowers add structured workflow skills for brainstorming, planning, systematic debugging, and verification. Install any of them with `/plugin add marketplace https://github.com/anthropics/skills`. Building from scratch is still useful if you need specific behavior, custom defaults, or integration with a private API.
Do Claude Code skills work with other AI coding tools besides Claude Code?
Yes. Skills rely on filesystem access and the ability to run commands, not on Claude Code specifically. Any coding agent with those capabilities, including Codex CLI and Gemini CLI, can read the same SKILL.md files without modification. You write a skill once and it works wherever the model can read files and execute scripts.
Can I create a Claude skill without using the terminal?
Yes. The Claude.ai web app has a built-in skill builder under Settings, Capabilities, Skills. Click Add and you get three choices: Create with Claude (a conversational flow where Claude asks clarifying questions and writes the skill with you), Write skill instructions (a structured form where you fill in the name, description, and instructions directly), and Upload a skill (drop in a SKILL.md file you already have). The web-interface flow is the fastest path if you don't need scripts. For anything that calls an external API or runs custom code, you'll still want the file-based approach.
Does the same SKILL.md work in Claude Code and the Claude web app?
Yes. The SKILL.md format is the same across Claude Code, the Claude.ai web app, and the Claude API. You can build a skill once and use it everywhere. In Claude Code, drop the folder into ~/.claude/skills/ or .claude/skills/. In the Claude web app, upload the same SKILL.md through Settings, Capabilities, Skills. Other agents like Codex CLI and Gemini CLI also read SKILL.md files without modification, so a skill is genuinely portable.
What's the difference between a skill and a plugin?
Skills are markdown files you create or copy into `~/.claude/skills/`. They trigger automatically based on context, load progressively, and encode specific workflows with your preferences baked in. No installation step required. Plugins are packaged integrations installed through Claude Code's plugin system. They bundle MCP servers, tools, and sometimes skills into a single install and are designed for distribution. Use a skill when you want control over a specific workflow. Use a plugin when you want a maintained, pre-packaged integration without the setup overhead.
