Autonnel v0.1.0
Back to blog

Writing a SKILL.md an agent actually follows

A skill file is procedural knowledge the model cannot infer, not documentation. The trigger description matters more than everything below it.

· 10 min read

Update, 2026-08-05. The standalone skills repository this post describes is no longer published. Once the MCP server shipped, tool names, descriptions and input schemas are read by the client on connect, so the per-domain skill files were duplication that drifted - the component catalog singled out below had gone stale enough to name components that no longer exist. What introspection cannot carry (the error envelope, the write rules, the build order) now lives on the MCP server page and in a single funnel-building skill. The argument below stands; the artifact it describes does not.

There are two ways a skill file fails, and they look nothing alike.

The first is that it never loads. The agent works the whole task without it, and you find out because the output is wrong in a way the file specifically warned about. The second is worse: it loads, sits in context, and the model works around it, because what you wrote was a description of the system rather than an instruction about what to do.

Autonnel shipped eleven Claude skills alongside its MCP server, in a separate repo. Writing them taught me more than any guidance I could find, most of which is about file structure. File structure is not the hard part.

This post is the companion to Funnels an agent can operate, which covers the permission model and what the MCP server exposes. This one is about the instructions on top.

A skill file is for what the schema cannot say

Start by deleting everything the agent already gets for free.

The Autonnel MCP server is self-describing. A client calls listTools on connect and receives the full input schema and output shape for every tool. If your skill file explains that list_orders takes a page and a limit, you have burned context re-stating something the transport already delivered, more reliably than your Markdown does.

What the schema cannot carry is the shape of the domain. Real examples from our repo, none of which are inferable from a tool signature:

  • A landing page can belong to exactly one funnel. Assigning a page already used as a landing page elsewhere is rejected (autonnel-funnels/SKILL.md).
  • update_page replaces the entire draftData. Send a partial content array and you silently delete components (autonnel-pages/SKILL.md).
  • Customer email is always masked and the address stripped to country only, and this cannot be bypassed via the API (autonnel-orders/SKILL.md). An agent that does not know this keeps retrying, convinced it passed a wrong parameter.
  • Only SHIPPED orders can be marked delivered. Anything else returns a 409 (autonnel-orders/SKILL.md).

That is the test I now apply to every line: could a competent model have guessed this from the tool schema alone? If yes, cut it. A skill file is the set of facts that live only in someone’s head or in a validator, written down.

The corollary is that most of what people put in skill files is documentation, and documentation belongs in docs. Our MCP page has the tool table. The skill files do not repeat it.

The description is the entire product

Every SKILL.md in the repo has exactly two frontmatter fields, name and description. That is the whole header. The body can be 100 lines or 560, and it does not matter at all if the description does not fire.

The body is what the model reads after deciding to read it. The description is the only thing that makes the decision. It is a routing key wearing the clothes of a summary.

The pattern that worked, from autonnel-orders/SKILL.md:

description: Use to list and search orders with pagination, or mark
shipped orders as delivered. Use when querying order data, analyzing
sales, or cross-referencing orders with ads/funnel data.

Two sentences doing two different jobs. The first says what the skill does, in verbs. The second says when to reach for it, in the vocabulary of the task the user will actually describe. Nobody types “I need the orders domain.” They type “why did revenue drop last week,” and analyzing sales is the hook that catches it.

Both sentences start with “Use”, consistently across all eleven files. A description written as a noun phrase (“Order management reference”) describes a document. An imperative describes a decision.

Name the tools in the description too. autonnel-stats ends with “comparing ad spend to conversions via get_stats”. When an agent is already staring at a tool list, the literal tool name is the strongest match signal available.

Instructions must be testable

Here is a line I would reject in review:

Be careful when updating page data, as the draft structure is important.

And here is what is actually in autonnel-pages/SKILL.md:

Always send the complete draftData - update_page replaces the entire draftData, so partial content arrays will lose components.

The difference is not tone. The difference is that the second one can be checked. Read the tool call, look at whether draftData.content is the full array, and you know whether the instruction was followed. The first one cannot be violated, because it does not say to do anything.

The same file has “Don’t invent new component types” and “Component IDs must be unique within a page.” Both are binary. autonnel-components/SKILL.md specifies that zone children live under a key of <parentId>:<zoneName>, which is either right or wrong in the payload.

Three rules of thumb:

Write the failure mode next to the rule. Not “always send complete draftData” alone, but the consequence. Models weigh instructions by consequence, and so do the humans maintaining the file.

Prefer a worked example over a paragraph. autonnel-stats/SKILL.md does not explain conversion analysis, it gives the arithmetic: checkout / lp1 for landing to checkout, thankyou / lp1 for overall. Four lines, zero ambiguity.

Order the body by decision, not by resource. Our page skill leads with the four-step loop (get the page, locate the component, modify props, put the whole thing back) before any field types, because the loop is what the agent is choosing.

Guardrails do not go in the text

This is the same argument I made about prompts in the last post, and it applies in full to skill files, which are just prompts with a filename.

A skill file cannot stop anything. If you write “never issue a refund without confirming with the user,” you have expressed a preference. The tool is still on the list, the key still authorizes it, and nothing in the runtime consults your Markdown before executing.

Enforcement lives one layer down. Every Autonnel API key carries an immutable writeAccess boolean, fixed at creation: false allows GET only, true allows GET, POST, PUT and DELETE. To change it you revoke the key and mint a new one. Below that, a key’s effective permissions are the union of the creating user’s role features. There are no per-key scopes.

Which means the only real way to constrain an agent is to create a dedicated restricted user and mint the key as that user. Not to ask the skill file nicely. If you mint from your owner account, the agent has owner powers no matter what your Markdown says about caution.

So what does belong in the skill file? The failure, not the prevention. autonnel-orders/SKILL.md marks deliver_order with [WRITE], states that it requires writeAccess, and tables the 403 with the message Write access required. When the wall stops the agent, the agent knows why and stops retrying instead of thrashing. The file documents the boundary. It never pretends to be one.

Finding out you are being ignored

Nothing tells you a skill was skipped. There is no load event you can subscribe to. What I use instead:

Read the tool trace for errors the skill exists to prevent. These are the tell. A 409 on deliver_order means the agent did not know the SHIPPED precondition. A 400 for an unknown component type means it never read the catalog. A rejected funnel assignment means it missed the one-landing-page rule. Each maps to a specific line in a specific file, and a cluster of them is a trigger failure, not a model failure.

Use the API key logs. Every request authenticated with a key is logged with the key ID, the HTTP status, and latency, visible in the key detail view. Not a skill metric, but a rising 4xx rate on an agent key is the cheapest proxy for “the instructions are not landing” that you already have.

Watch for reconstruction. An agent calling get_page three times to work out the update semantics that are written down in plain English is telling you the file did not load.

Then fix the description, not the body. When a skill is skipped, the instinct is to make the instructions louder. Almost always the problem is upstream: the description did not match the words the task was phrased in. Add the vocabulary. Add the tool names.

Where this is honestly weak

There is no test harness. I cannot assert that a description change improved trigger rate, because I have no measurement of trigger rate. Everything above is a pattern that stopped producing visible failures, which is weaker evidence than I would like and I am not going to dress it up.

Size discipline is a guess. autonnel-components/SKILL.md is 564 lines, by far the largest in the repo, and it earns it: it is a component catalog with props and page recipes. I have no evidence that it loads as reliably as the 106-line stats file. I suspect it does not.

Our own docs were behind the repo. The SKILL.md reference page still described an older workflow where an agent fetches raw Markdown from a URL at session start, long after the repo had moved to an installable skill plugin layered on the MCP server. That gap was on us, and it is the reason the whole artifact was eventually folded into the MCP server docs.

Skill files do not auto-update from your deploy. They are a snapshot. After upgrading Autonnel, refetch them, and pin to a tag if you need stable context across releases.

The short version

Write down only what the schema cannot say. Spend your effort on the description, because it decides everything. Make every instruction something you could grade. Put the actual guardrail in a restricted user and an immutable writeAccess flag, and let the skill file explain the wall rather than impersonate it.

The tool surface is on the MCP page and the key model is in API keys. All of it runs in the self-hosted edition, and paid plugins and template packs live in the marketplace.

If you write a skill for Autonnel that I did not anticipate, open a PR. The failure modes are more interesting than the successes.