Writing skills
Contents
Skills are job-to-be-done templates that teach agents how to use capabilities to achieve a goal. They should read like how an experienced person would approach a job, not like a rigid script of commands to run. Describe the workflow, the reasoning behind each step, and what to watch out for – then trust the agent to adapt. Overly strict instructions break when context changes; a well-explained approach generalizes.
This page covers what skills are, how to write them, and how the build pipeline works. For adding MCP tools (the capabilities themselves), see Adding tools to the MCP server.
TL;DR
Skills vs tools
Tools are atomic capabilities – CRUD operations and simple actions exposed via the MCP server. They answer "what can I do?" (list feature flags, execute SQL, create a survey).
Skills answer "how do I accomplish X?" They combine tools, domain knowledge, query patterns, and step-by-step workflows into a template that agents follow to solve a class of problems.
A skill might reference multiple tools, include HogQL query examples, explain what data to verify before querying, and describe the desired outcome for the customer.
This separation matters because agents are good at composing simple tools but need guidance on which tools to use, in what order, with what constraints.
Skill structure
Skills live in products/*/skills/ and come in two forms.
If your product hasn't moved to the products/ folder yet,
create a product folder and add skills there –
skills are designed to work from within the products folder structure.
Simple skill (single file)
The skill name is the filename stem.
Directory skill (with references)
The skill name is the directory name.
Only references/ and scripts/ subdirectories are included in the output –
other subdirectories are ignored.
Frontmatter
Every skill entry point must have YAML frontmatter with name and description:
Both fields are required and validated at build time.
Progressive disclosure
SKILL.md serves as an overview that points agents to detailed materials as needed.
Reference files are loaded on demand – only the entry point is read initially.
Keep SKILL.md under 500 lines and split detailed content into references/.
See query-examples/SKILL.md
for how this works in practice –
the entry point links to 30+ reference files
covering model schemas, query patterns, and HogQL extensions.
Naming and description guidelines
Follow Anthropic's skill authoring best practices. Key points are summarized below.
Naming conventions
Use lowercase kebab-case. Prefer gerund form (verb + -ing):
| Pattern | Examples |
|---|---|
| Gerund form (preferred) | analyzing-llm-traces, writing-hogql-queries, managing-feature-flags |
| Noun phrases (acceptable) | query-examples, error-tracking-guide |
Skills must not be prefixed with posthog-*.
The posthog- prefix is added automatically depending on the consumer agent.
Skills also must not contain the reserved words anthropic or claude in the name.
The name field: lowercase letters, numbers, and hyphens only. Max 64 characters.
Writing descriptions
The description field is critical for skill discovery –
agents use it to decide which skill to activate from potentially many available skills.
Max 1024 characters.
Rules:
- Write in third person ("Analyzes LLM traces...", not "I can help you analyze...").
- Include both what the skill does and when to use it.
- Be specific – include key terms agents would match against.
Good descriptions:
Bad descriptions:
Good and bad skill examples
Good: analyzing-llm-traces
A focused skill that guides the agent through a specific workflow:
- Starts by verifying that
$ai_trace,$ai_generation,$ai_feedbackevents exist. - Provides HogQL queries to retrieve trace data with the right properties.
- Explains how to join generations to traces via
$ai_trace_id. - Describes the desired outcome (latency analysis, feedback summary, cost breakdown).
The agent knows what tools to use (execute-sql, read-data-schema), in what order, and what a successful result looks like.
Good: query-examples
A reference skill with a clear entry point and 30+ reference files:
- Entry point (
SKILL.md) links to model schemas, query patterns, and HogQL extensions. - Guidelines file (
references/guidelines.md) explains schema verification workflow, time ranges, joins, and HogQL differences. - Uses progressive disclosure – agents load only the references they need.
Bad: llm-analytics
An umbrella skill that tries to cover everything: traces, experiments, evaluations, cost tracking, prompt management. Too broad to be useful – agents can't determine when to activate it and the instructions are too generic to guide any specific workflow. Break it into focused skills instead.
Bad: poor descriptions
Template engine
Skills support Jinja2 templates.
Any file ending in .j2 is rendered at build time,
and the .j2 suffix is stripped from the output path.
Plain .md files pass through unchanged.
The Jinja2 environment uses StrictUndefined (undefined variables raise errors),
lstrip_blocks, and trim_blocks for clean whitespace handling.
Built-in template functions
Three global functions are available in all .j2 templates:
pydantic_schema(dotted_path, indent=2)
Imports a Pydantic model by its fully-qualified path and returns its JSON Schema:
Changes to the Pydantic model automatically update the skill output on the next build.
render_hogql_example(query_dict)
Takes a PostHog query spec and renders it to HogQL SQL:
Time is frozen to 2025-12-10T00:00:00 for deterministic output.
hogql_functions()
Returns a sorted list of all public HogQL function names:
Extending the template engine
The build pipeline should be extended so the monorepo remains the source of truth for all skill content. When domain knowledge lives in code (Pydantic models, query runners, function registries), add a template function to extract it at build time rather than duplicating it as static markdown that drifts.
To add a new template function:
- Create a module under
products/posthog_ai/scripts/(follow existing patterns likepydantic_schema/,hogql_example/). - Register it in
SkillRenderer.__init__()by adding toself.env.globalsvia_create_jinja_env(**extra_globals).
Build pipeline
The pipeline discovers, renders, and packages skills.
Source of truth: products/posthog_ai/scripts/build_skills.py.
Pipeline steps
CLI commands
lint:skills validates syntax, frontmatter, binary file detection, and duplicate names.
It runs without Django, so it's fast in CI.
build:skills requires the full Python environment
because template functions import Django models and Pydantic schemas.
Output
Built skills are written to products/posthog_ai/dist/skills/ (gitignored)
and packaged into dist/skills.zip with deterministic timestamps for reproducible builds.
Distribution
Distribution is automatic. Built skills are published through the posthog/skills repo and consumed via plugins for coding agents at PostHog/ai-plugin.
PostHog Code already consumes skills automatically. PostHog AI will consume the same set of skills.
Product teams don't need to handle distribution – the pipeline and CI take care of it.
Testing
To test a product skill locally with Claude Code, sync it to .agents/skills/:
Synced skills are automatically gitignored and should not be committed.
Both sync:skill and unsync:skill accept --name to identify the skill
by its source directory name (the folder name under products/*/skills/).
See How to develop and test for instructions on running the MCP server locally and verifying skills end-to-end.
Writing effective skills
The context window is a shared resource. Your skill competes with the system prompt, conversation history, other skills, and the user's request.
Default assumption: the agent is already very smart. Only include context it doesn't already have. Challenge each piece of information: does the agent really need this explanation?
See Anthropic's full best practices guide for detailed advice on progressive disclosure, feedback loops, workflows, and evaluation-driven development.