>- Best practices for agents managing PostHog skills via the MCP `llma-skill-*` tools — how to discover, read, create, update, and refactor skills efficiently, especially large skills with many bundled files. Use whenever you are about to call any `llma-skill-*` tool, asked to author or edit a shared skill, or troubleshoot why a skill write was rejected. Pairs with `skills-store` (which covers the raw tool surface) by adding the decision-tree, efficiency, and pitfall guidance.
npx skills add https://github.com/PostHog/skills --skill working-with-skills
This skill teaches agents how to use the llma-skill-* MCP tools well — minimum
context, minimum round-trips, minimum mistakes. If you are not yet familiar with
the tool surface itself, read the skills-store skill first for the catalog.
This document is about _how to choose between the tools_ and _how to scale the
workflow_ when skills get big.
returns body + manifest, file-get returns one file. Never preload bundled
files "just in case" — every preloaded script is wasted context for the
actual task.
editsor file_edits is cheaper, safer, and clearer in version history than a
full body or full bundle replacement.
version from llma-skill-get (or from the response of the previous write)
before calling any write tool, and pass it as base_version.
Keep name kebab-case, descriptions trigger-rich, body short, bulky
material in bundled files.
Need to know what's available?
└─► llma-skill-list (names + descriptions only)
Need to use / inspect a specific skill?
└─► llma-skill-get (body + file manifest, NO file contents)
└─► llma-skill-file-get (one file, on demand, only as referenced)
Authoring a brand new skill?
└─► llma-skill-create (body + all initial files in one call)
Editing an existing skill?
├─ Body change?
│ ├─ Substantial rewrite ............. update(body=...)
│ └─ Surgical tweak .................. update(edits=[{old, new}, ...])
├─ Bundled file content change?
│ └─ update(file_edits=[{path, edits:[...]}, ...])
├─ Add / remove / rename a file?
│ ├─ Add ............................. llma-skill-file-create
│ ├─ Delete .......................... llma-skill-file-delete
│ └─ Rename .......................... llma-skill-file-rename
└─ Wholesale bundle reset (rare!) ....... update(files=[...]) # replaces ALL files
Want a fork as the starting point?
└─► llma-skill-duplicate (then update the copy)
Done with a skill entirely?
└─► llma-skill-archive (hides ALL versions; cannot be undone)
If you find yourself reaching for update(body=...) plus a sprawling files=[...]
to change one paragraph and one script, stop — that's two narrower calls
(update(edits=[...]) plus update(file_edits=[...])) or even a single
update carrying both edits and file_edits.
posthog:llma-skill-list
{ "search": "fractal" }
llma-skill-list is the right tool to "find a skill" — it returns names and
descriptions only. Reading the descriptions is the entire point: pick the right
skill before pulling any body. If search doesn't narrow it enough, list
without it and scan, but do not start fetching candidate bodies blindly.
llma-skill-get should be called once per skill per task, not per question.
Cache the body in your working memory; fetch again only if you suspect the
skill changed under you (e.g. a 409 on write — see "Concurrency" below).
Big skills (long body, many bundled files) are the case where lazy loading
matters most.
llma-skill-get(skill_name=...) — read body + files[] manifest.you which file goes with which task — that's why bodies stay short and
reference files by path.
llma-skill-file-get(file_path=...). Skip everything else.
in case Y, do not fetch scripts/X.
When in doubt, fewer files. You can always fetch one more on the next turn.
Use a single llma-skill-create call with body and initial files — the
skill lands at version: 1 complete. Do not create the skill empty and then
make N follow-up llma-skill-file-create calls; that's N extra versions and N
extra round-trips for no benefit.
posthog:llma-skill-create
{
"name": "my-skill",
"description": "What it does AND when to use it. Include trigger keywords.",
"body": "# my-skill\n\n## When to use\n...\n## Workflow\n...",
"license": "MIT",
"compatibility": "Requires Python 3.10+",
"allowed_tools": ["Bash", "Write"],
"metadata": { "author": "me", "category": "..." },
"files": [
{ "path": "scripts/foo.py", "content": "...", "content_type": "text/x-python" },
{ "path": "references/primer.md", "content": "...", "content_type": "text/markdown" }
]
}
description is the discovery surface. It is the only thingllma-skill-list returns. Make it trigger-rich (what the user might say) and
scope-honest (what the skill does and does not do).
name — kebab-case, max 64 chars, no leading/trailing/consecutivehyphens. The spec validator rejects anything else.
and runnable code belong in references/, assets/, or scripts/. The body
should _route_ to those files, not inline them.
scripts/ for executable code, references/for prose docs and examples, assets/ for templates / data. Agents can rely
on this for orientation when they only have the manifest.
allowed_tools lists the MCP / built-in tools the skill expects to becallable. Be honest — under-declaring causes silent failures, over-declaring
is a security smell.
The single most common mistake is using update(body=..., files=[...]) for a
small change. That works, but it round-trips the entire skill, makes the diff
unreadable in version history, and risks dropping files if files was
incomplete. Use the smallest primitive instead.
versionposthog:llma-skill-get
{ "skill_name": "my-skill" }
Note the returned version — pass it as base_version on every write. After a
successful write, the response contains the new version; chain further writes
with that.
Full replacement when you are restructuring the body:
posthog:llma-skill-update
{ "skill_name": "my-skill", "body": "# my-skill\n\nNew body...", "base_version": 7 }
Incremental edits when you are tweaking a few lines (preferred for small
changes — easier to review, lower error surface):
posthog:llma-skill-update
{
"skill_name": "my-skill",
"edits": [
{ "old": "Use Pillow for rendering.", "new": "Use Pillow ≥10.0 for rendering." },
{ "old": "## Old section title", "new": "## New section title" }
],
"base_version": 7
}
Each edits[].old must match exactly once in the current body, and body and
edits are mutually exclusive in one call.
file_edits patches one or more existing files in place — non-targeted files
carry forward unchanged. This is the right primitive when you are tweaking
script logic or fixing a typo in a reference doc:
posthog:llma-skill-update
{
"skill_name": "my-skill",
"file_edits": [
{
"path": "scripts/foo.py",
"edits": [{ "old": "ITERATIONS = 100", "new": "ITERATIONS = 250" }]
},
{
"path": "references/primer.md",
"edits": [{ "old": "## Outdated header", "new": "## Updated header" }]
}
],
"base_version": 7
}
file_edits cannot add, remove, or rename files — only patch
existing ones. For structural changes, use the per-file tools.
You can combine edits (body) and file_edits (existing files) in one
llma-skill-update call to publish a single coherent version when a change
spans both:
posthog:llma-skill-update
{
"skill_name": "my-skill",
"edits": [{ "old": "## Configuration", "new": "## Setup" }],
"file_edits": [
{ "path": "scripts/run.py", "edits": [{ "old": "DEBUG = False", "new": "DEBUG = True" }] }
],
"base_version": 7
}
The same concept — a bundled file's path — is named differently depending on
where it travels in the request, and this trips up agents working from
memory. There is one rule:
file_path — when the path is part of the URL (llma-skill-file-get,llma-skill-file-delete). These read/delete one file addressed by its path.
path — when the path is a body field: llma-skill-file-create, thefiles=[{path, content, content_type}] array, and file_edits=[{path, edits}].
old_path / new_path — body fields on llma-skill-file-rename.Mnemonic: path is the field name on a file _object_ (it sits next to
content), so everything that carries a file object uses path; the two
tools that address a file by URL use file_path. When unsure, check the
tool's input schema rather than guessing — passing path to file-get yields a
/files/undefined/ 404.
Each is its own call, each publishes a new version:
posthog:llma-skill-file-create
{ "skill_name": "my-skill", "path": "scripts/julia.py", "content": "...", "base_version": 7 }
posthog:llma-skill-file-delete
{ "skill_name": "my-skill", "file_path": "scripts/old.py", "base_version": 8 }
posthog:llma-skill-file-rename
{ "skill_name": "my-skill", "old_path": "scripts/julia.py", "new_path": "scripts/julia_set.py", "base_version": 9 }
llma-skill-file-rename is a true move — it carries the existing content
forward without resending it. Always prefer it over delete + create when the
content is unchanged.
update(files=[...]) (rare)Passing files to llma-skill-update replaces the entire bundle —
anything not in the array is dropped. This is the right tool only when you are
intentionally wiping and reseeding the bundle (e.g. importing a fresh local
SKILL.md tree). For almost every other case, prefer file_edits plus per-file
CRUD.
Skills with many files (10+) require extra discipline:
llma-skill-get's files[] is your map.Match each task step to one file and fetch only that one.
three files, do them sequentially: rename → rename → rename, each chained
via the previous response's version. That gives you three small reviewable
versions instead of one giant update(files=[...]) blob.
llma-skill-update with file_editstargeting five files is fine. A single update(files=[...]) carrying ten
full file bodies is almost always a sign you should have used file_edits.
the right next step is usually to split content into new bundled files
before adding more material.
base_versionEvery write tool accepts base_version. Always pass it.
base_version to the current latest version. If theymatch, the write succeeds and the new version is base_version + 1.
Re-run llma-skill-get, reconcile your changes against the new body, and
retry with the fresh version.
version. Chainfurther edits with that — do not re-get between back-to-back writes you
control.
Skipping base_version does _not_ speed things up — it just turns a clean
"someone else won the race" error into a silent overwrite of their work.
llma-skill-list with no search and then fetching every body —defeats progressive disclosure. Read the descriptions first.
llma-skill-get — same mistake onthe inner level. Fetch on demand from the body's directives.
update(body=..., files=[...]) for a one-line fix — round-tripsthe entire skill, makes diffs unreadable, and risks dropping files. Use
edits / file_edits.
update(files=[...]) when you meant to add one file — drops everyfile you didn't include. Use llma-skill-file-create instead.
extra version bump.
base_version after chained writes — read the version from theprevious write's response, not from your initial get.
base_version off — accepts a silent overwrite. Always includeit once you've done a get.
description — the skill becomes effectively undiscoverablevia llma-skill-list search. Treat the description as the trigger contract.
into references/ and scripts/ rather than letting it grow.
body and edits in one update call — they're mutually exclusive.Pick one.
path vs file_path — file-get and file-delete take file_path(it's in the URL); create, rename (old_path/new_path), files, and
file_edits take path (it's a body field). See "File-path parameter
naming" above.
llma-skill-archive hides every active version of a skill by name. It is
not version-scoped and cannot be undone — the skill drops out of
llma-skill-list and llma-skill-get for the whole team.
posthog:llma-skill-archive
{ "skill_name": "my-skill" }
Before archiving, llma-skill-get the skill if you need to inspect or copy it
first. Archiving is the right tool for retiring a skill entirely; to remove a
single bundled file use llma-skill-file-delete, and to roll back content
publish a new version rather than archiving.
When migrating a local skill folder (e.g. my-skill/SKILL.md plus
scripts/, references/, assets/):
SKILL.md. Its frontmatter maps to name, description,license, compatibility, allowed_tools, metadata. The body after the
frontmatter becomes body.
{ path, content, content_type }.
posthog:llma-skill-create once with everything — the skill lands atversion: 1 complete. Do not split this into a create + N file-create
calls.
After the create, the skill is live for everyone via llma-skill-get.
Not every persistent prompt belongs in the skills store:
repo.
A good skill is reusable, discoverable by description, and worth the cost of
keeping it correct over time.
Complete development kit for Microsoft 365 Copilot declarative agents with three comprehensive workflows (basic, advanced, validation), TypeSpec support, and Microsoft 365 Agents Toolkit integration
Format and structurally validate local treatment-plan documentation after clinical decisions have already been supplied and verified by authorized licensed professionals. Use for source traceability, clinician-authored intervention records, goals and checkpoints, shared-decision records, reconciliation handoffs, and release gates—not for clinical decision-making.
> provider/change budget/修改卖家/修改预算/draft/草稿/我的任务/my tasks/what am I working on/关闭/取消任务/决策列表/decision list/指定服务商/browse (sender.role = COUNTERPARTY, not you); (3) literal "Read the okx-ai skill" (or legacy "Read the okx-agent-task skill") in the envelope.
Automate payer review of prior authorization (PA) requests. This skill should be used when users say "Review this PA request", "Process prior authorization for [procedure]", "Assess medical necessity", "Generate PA decision", or when processing clinical documentation for coverage policy validation and authorization decisions.
Expert in designing and building autonomous AI agents. Masters tool use, memory systems, planning strategies, and multi-agent orchestration.
Autonomous agents are AI systems that can independently decompose goals, plan actions, execute tools, and self-correct without constant human guidance. The challenge isn't making them capable - it's making them reliable. Every extra decision multiplies failure probability.
Orchestrates design workflows by routing work through brainstorming, multi-agent review, and execution readiness in the correct order.
Structured persuasion for tech leads, PMs, and founders—not activity logs. Five scenarios (kickoff, status update, wrap-up, investor pitch, solution selling) on one 5-part framework (Hook→Context→Proposal→Evidence→Ask). AI prompts for missing materials and audience context; pre-submit checklist. Claude Code plugin; Cursor, Codex, and chat via prompts.
Take posthog/working-with-skills from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.