Heym - Build agentic systems. Run them with confidence | Product Hunt
Back to blog

August 8, 2026Mehmet Burak Akgün

Agent Skills: What Breaks When Nobody Is Watching

Agent skills assume a human at a terminal. What changes when a cron trigger starts the run: activation, sandboxing, versioning, and proof. →

agent-skillsskill-mdai-agentsagentic-aiworkflow-automationself-hostedsandboxing
Agent Skills: What Breaks When Nobody Is Watching

The best sentence written about agent skills this year is on the Cisco learning blog, and it gives the whole game away without meaning to. The post is a genuinely good explanation of why a skill beats a prompt, and it ends like this: "You bring your skill, your terminal, and your judgment, and the agent works alongside you."

Your terminal. Your judgment. Alongside you.

That is not a criticism of the post. It is an accurate description of how every serious piece of writing on this topic frames the problem, because it is an accurate description of where the format grew up. I have read the top results for this query and the framing is remarkably consistent: a person, sitting in front of an agent, in a session they started.

An agent skill is a folder containing a SKILL.md file plus any scripts and resources that file references, written so an agent can load the procedure on demand instead of being told it again every time. The format is portable, it is now an open standard, and as of early August 2026 it is supported by 46 client products. That part is settled and well covered.

What is not covered anywhere is what happens when you take the human out. Not the human who wrote the skill, the human who is sitting there when it runs. Because the moment a skill is started by a cron job at 07:00 rather than by someone typing, four assumptions quietly break, and none of the guides mention them.

TL;DR: Agent skills are a good format built on a session-shaped assumption. Progressive disclosure makes activation probabilistic, which is fine when you can nudge the agent and fatal when nobody is there. A skill that bundles code is untrusted code running on your infrastructure, and scanning it before installation is only half the answer. Nobody is writing about versioning, rollback, or how to prove a skill actually ran. If you want skills in production, bind them to a node instead of a session, sandbox the code at runtime, and record both what was in context and what actually executed.

The same skill, two very different runs
1. Trigger
Cron fires at 07:00
No person is present to phrase a request or notice a bad answer
2. Activation
Skill is already in context
Attached to the node rather than discovered by the model at runtime
3. Execution
Python runs in a sandbox
Throwaway container, no secrets, no Docker socket, fails closed
4. Evidence
Trace records both facts
Which skills were in context, and which ones actually fired
What a chat session does instead
Session run
Human nudges it
If the model misses the skill you just say use the invoice one, and the miss never gets recorded
The format is identical. The operational requirements are not.

What an Agent Skill Actually Is

Definition: An agent skill is a directory containing a SKILL.md file whose YAML frontmatter carries at minimum a name and a description, followed by instructions the agent reads when the task matches, plus optional scripts, references, and assets the file points to.

The layout published on agentskills.io is deliberately boring:

my-skill/
├── SKILL.md          # Required: metadata + instructions
├── scripts/          # Optional: executable code
├── references/       # Optional: documentation
├── assets/           # Optional: templates, resources
└── ...               # Any additional files or directories

Anthropic published the format on 16 October 2025 and released it as an open standard that December. The framing in their engineering post is that building a skill is "like putting together an onboarding guide for a new hire", which is the right instinct: you are not writing a prompt, you are writing documentation for a colleague who will read it repeatedly and never remember the last time.

Loading happens through what the spec calls progressive disclosure, in three stages:

StageWhat loadsWhen
Discoveryname and description onlyAt startup, for every available skill
ActivationThe full SKILL.md bodyWhen the agent judges the task to match
ExecutionReferenced files and bundled codeOnly as the instructions call for them

Anthropic calls this "the core design principle that makes Agent Skills flexible and scalable", and as a context-budget decision it is obviously right. Google's ADK team put numbers on it: about 100 tokens of metadata per skill at the discovery level, under 5,000 tokens for a full instruction file, so "an agent with 10 skills starts each call with roughly 1,000 tokens of L1 metadata instead of 10,000 tokens". A 90 percent reduction in standing context is not a small thing.

Hold on to that mechanism, because everything difficult in this article comes out of it.

The Two Things People Mean by "AI Agent Skills"

Before going further, a disambiguation that the search results genuinely muddle. "AI agent skills" resolves to two unrelated topics.

SenseWhat it meansWho is asking
The formatA SKILL.md folder that extends an agent's capabilityEngineers deciding whether to adopt the standard
The competencyWhich abilities a person needs in order to build agentsPeople planning a career or a hiring bar

This article is entirely about the first one. If you landed here looking for the second, the honest short answer is that the scarce competency in 2026 is not model knowledge, it is the operational judgment covered in how to build an AI agent: knowing which steps should be deterministic, which need a model, and which need a person.

Skill, MCP Server, or Tool

Three primitives keep getting compared, and the comparison itself is not new. Several pages have a version of this table already. What they do not have is the operational half, because you can only fill those columns if all three primitives live in one runtime you can inspect.

Agent skillMCP serverCanvas tool
What it carriesA written procedure, optionally with codeLive access to an external systemOne capability wired into the graph
FormatSKILL.md folder, portable across clientsJSON-RPC over stdio, SSE, or HTTPNode configuration in the workflow
Where the work happensLocally, in the agent's own runtimeOn the server, wherever that isIn the workflow engine
Who holds the credentialNobody. It inherits the host agent's accessThe server, usually via OAuthThe platform credential store
Fails whenThe instructions are wrongThe server is down or the token expiredThe upstream API rejects the call
CostContext tokens, every run it is loadedA round trip, plus tool-listing overheadWhatever the node does
Right choice forProcedures and formatting rules you repeatAnything live, authenticated, or sharedAnything the graph should control directly

The credential row is the one worth pausing on, and it is the sharpest live question on this topic. A skill has no authentication model of its own. It runs with whatever access the host agent already has, which is exactly why "just install this skill" deserves more suspicion than it usually gets. If you want the protocol side of this in depth, what MCP is covers it properly and I will not repeat it here.

The practical rule I use: if the thing you need is a fact from a system, that is MCP or a node. If the thing you need is a way of working, that is a skill.

What Breaks When Nobody Is Watching

Here is the section the rest of the internet has not written yet. Take the exact same skill, keep the format identical, and change only who started the run.

1. Activation becomes a coin flip you never see land

Progressive disclosure means the model reads a one-line description and decides whether your skill applies. In a chat session that is a fine bet, because the failure is instantly correctable. The agent ignores your invoice skill, you say "use the invoice one", it loads, you move on. The miss cost you four seconds and left no trace.

At 07:00 on a cron trigger, the same miss produces a run that completes successfully with a plausible answer generated from general knowledge instead of your procedure. Nothing errors. Nothing alerts. You find out three weeks later when the numbers do not reconcile.

This is the honest core of an objection that has been sitting on r/Anthropic under the title "Agent Skills - Am I missing something or is it just conditional context loading?", with 31 upvotes and 27 comments.

Is a skill just conditional context loading? Largely, yes. The interesting question is not whether the loading is conditional but what the condition is. When the condition is a model's judgment about a one-line description, activation is probabilistic, and probabilistic activation is only safe when somebody is present to notice a miss.

Which means you have introduced a coin flip into a pipeline you probably wanted to be deterministic, and the flip is invisible because a missed skill and a used skill produce the same green tick.

2. Failure has nowhere to go

Interactive agents fail into a conversation. The script throws, the agent explains, you decide. An unattended run has no conversation to fail into, so every failure mode needs a pre-decided answer: retry, skip, alert, or stop the workflow. The skill format says nothing about this, correctly, because it is a runtime concern. But that means the runtime has to have an answer, and a terminal does not.

3. There is no evidence

After an interactive session you know the skill ran, because you watched it. After 400 unattended runs you know nothing unless something recorded it. And the thing you need recorded is subtler than a log line, which brings us to the two facts nobody tracks. More on that below.

4. Credentials stop being ambient

In a terminal the skill inherits your shell, your SSH agent, your cloud profile, your everything. That is convenient and it is also why the same skill in a server process is a different security proposition entirely. A skill that quietly reads an environment variable is harmless on your laptop and a credential leak on a shared runner.

Attachment Beats Discovery, and It Costs You

The fix for the first problem is not clever. It is to stop making availability a decision.

In Heym, a skill is not installed into a session. It is attached to a specific Agent node on the canvas, and at run time the content of every attached SKILL.md is prepended to that node's system instruction, joined by a separator, before the model sees anything else. There is no discovery stage and no relevance check that can go the wrong way. The node has the procedure because you put it there, the same way it has its model and its temperature.

Be precise about what that does and does not fix. It removes the failure where the model never sees your procedure at all. It does not make the model obey it. Once the instructions are in context, choosing whether to call a skill's bundled code is still the model's call, made from the input in front of it and the description you wrote. Attachment guarantees the instruction is present. It does not guarantee the code runs, which is exactly why the two facts have to be traced separately, and why the description in your frontmatter is the highest-leverage sentence in the whole file.

There is no cap on how many skills a node can carry. You can attach a hundred, and the selection genuinely works: the model reads the descriptions and picks. What you cannot avoid is the bill.

So here is the honest part, because this is a trade and not a free win.

Loading everything unconditionally costs exactly what progressive disclosure was invented to save. Using Google's published figures, a full instruction file runs under 5,000 tokens. Three attached skills can therefore add something in the order of 10,000 to 15,000 tokens of standing context to every single call on that node, forever, whether or not any of them get used. Progressive disclosure would have charged you roughly 300.

That is a budget question rather than a rule, and it is worth doing the arithmetic before it surprises you. A node with twenty skills is a node paying for twenty instruction files on every run, including the nineteen that were irrelevant to this particular input. Splitting a broad agent into a few narrower nodes, each carrying the skills its job actually needs, is usually cheaper and easier to reason about, and it moves part of the routing from the model into the graph, which is the same principle behind most useful agentic design patterns. If your standing context is getting heavy either way, the levers in context engineering apply here exactly as they do anywhere else.

Key principle: Progressive disclosure trades determinism for context budget. That is the correct trade when a human can correct a miss in four seconds, and the wrong one when nobody is there to notice.

Skill Code Is Code

A skill can bundle executable scripts. That single sentence in the specification is the most under-discussed line in this whole topic, and it is why agent skills security is not the same conversation as prompt safety.

NVIDIA is the only organization publishing seriously about it. Their verified agent skills work, announced on 19 May 2026, catalogs, scans, signs and documents skills, and their scanner's threat list is the best short summary of the risk anyone has written. SkillSpector checks "conventional software risks such as vulnerable dependencies, suspicious scripts, dangerous code patterns, credential access, and data exfiltration paths", and also "agent-specific risks, such as hidden instructions, prompt injection, trigger abuse, excessive agency, tool poisoning, and mismatches between a skill's declared purpose, requested access, and bundled behavior".

That is supply-chain governance, and it is genuinely valuable. It is also only half the problem, and the other half has nobody writing about it at all.

Key principle: Scanning tells you what a skill looked like when you installed it. Containment decides what it can do when it runs. Supply-chain verification and runtime sandboxing are different guarantees, and a skill that executes unattended needs both.

A workflow platform needs the second one in particular, because skills arrive inside shared workflows and community templates where the person running the skill is not the person who wrote it.

We shipped this exact bug

I would rather not have a first-hand example here, but we do. In Heym, skill Python originally ran in a local backend subprocess, while user-defined Python tools were already required to run inside Docker. Same untrusted code, two different answers, and only one of them was right. It was published as CVE-2026-67543 on 11 July 2026, rated High at CVSS 8.8, affecting every version up to 0.0.65.

The exposure list in that advisory is the generic one for this class of mistake, which is why it is worth reading if you are building anything similar. A skill could read host files including .env, it had unrestricted network egress where tools ran with --network none, provider keys and OAuth secrets slipped past a seven-entry environment denylist, and file handling had no validation against .. traversal. The line that matters most for any platform with shared templates is this one: "Shared templates and workflows containing malicious skills execute in importers' backend contexts." That is the whole risk of a portable skill format stated in one sentence.

The fix was parity rather than cleverness. Skill Python now runs through the same fail-closed sandbox as Python tools. The generalizable lesson: if your platform already has a hardened path for untrusted code, a second untrusted-code path has to use that one, not a lighter version of it. Nobody sets out to build the lighter version. It happens because the second path arrives later, looks smaller, and nobody re-asks the question.

Heym's answer starts from an assumption stated bluntly in the source file itself: skill Python is untrusted, because it can arrive verbatim inside a shared workflow. So it goes through the same path as user-defined Python tools:

ControlWhat it does
Throwaway sibling containerNon-root, all Linux capabilities dropped, no-new-privileges, read-only root filesystem
No Docker socketSkill code cannot reach the host daemon or the backend's secrets
Environment allowlistOnly PATH, HOME, LANG, TZ and a short prefix list survive. Database URLs, encryption keys and provider API keys are dropped
Memory and PID caps512 MB virtual memory ceiling, strict CPU and process limits
Fails closedIf Docker is unavailable the run raises instead of quietly executing untrusted code in the backend process
Per-skill timeout30 seconds by default, applied to the Python execution

Two design decisions in there are worth stealing regardless of what platform you use.

The first is the allowlist rather than a denylist for environment variables. A denylist leaks by default: add a new secret to the backend six months from now and it is exposed until somebody remembers to extend the list. An allowlist withholds by default, and the failure mode is a skill that cannot find a variable rather than a skill that reads your encryption key.

The second is failing closed on a missing sandbox. It is very tempting to let the code run in-process when the container will not start, because otherwise the workflow breaks. Resist it. A sandbox that silently degrades to no sandbox is worse than no sandbox, because you stop thinking about it.

One deliberate difference from the stricter tool sandbox: skills keep network egress and a writable workspace, because skills legitimately fetch things and generate files. That widens the blast radius and it is a conscious trade, which is why the same care that applies to prompt injection applies to anything a skill reads off the internet. Content a skill pulls in is input, never instruction.

Versioning, Rollback, and the Audit Trail

agent skills versioning autocompletes in Google. There is no page dedicated to it anywhere in the results. That is a strange hole for a format whose entire selling point is that you write the procedure down once and reuse it, because a written procedure that changes is a procedure that needs history.

The question is concrete. Someone edits a skill on Tuesday, the Thursday night run produces different output, and you need to know what changed and get back to the version that worked.

In Heym, skill history is not a separate store. Every saved workflow version snapshots the full skills array on the Agent node, so history comes out of Edit History for free, with the same 7-day retention as the rest of the workflow. From the history dialog you can preview a past snapshot, load it into the editor without reverting anything else, or revert only that one skill while every other node stays where it is. That last one matters more than it sounds: rolling back a whole workflow to fix one prompt is how you undo three other people's work by accident.

Two more things belong in the same category. Every skill can be exported as a zip and dropped onto another node or another instance, which is the standard's portability promise honored in the least clever possible way. And because a skill is part of the workflow document, "who changed this procedure and when" is answered by the same mechanism that answers it for every other node.

Building One, End to End

If you came here looking for how to build an agent skill rather than for the argument, this is the short version: two files and a node. Here is a full skill, small enough to read in one sitting, doing the thing a skill is genuinely best at, which is turning something a model is bad at into something deterministic.

First the SKILL.md format itself. Note that the frontmatter carries parameters and outputs beyond the two fields the base standard requires. Those are Heym extensions and they are documentation for the model, not an enforced schema. The wire-level tool signature the model actually sees is a single string input, so the frontmatter is how you tell it what to put in that string.

---
name: weekly_status_pdf
description: Turn a JSON list of shipped items into a formatted weekly PDF report. Use whenever a weekly status report needs to be produced as a file.
parameters:
  - name: input
    type: string
    description: JSON array of objects with title, owner and status keys
    required: true
outputs:
  - name: file_path
    type: string
    description: Path to the generated PDF
timeout: 30
---
 
## Description
 
Render a weekly status report as a PDF. The layout is fixed so the
report looks the same every week regardless of which model produced
the summary text.
 
## Parameters
 
- **input** (string, required): a JSON array. Each object needs
  `title`, `owner` and `status`.
 
## Returns
 
- **file_path**: where the PDF was written.

The main.py. The boilerplate is not optional: without the __main__ block the script produces no output and the skill fails silently, which is a genuinely nasty way to lose an hour.

#!/usr/bin/env python3
import json
import os
import pathlib
import sys
 
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
 
 
def execute(params: dict, files: dict) -> dict:
    rows = json.loads(params.get("input", "[]"))
    out_dir = pathlib.Path(os.environ["_OUTPUT_DIR"])
    pdf_path = out_dir / "weekly-status.pdf"
 
    page = canvas.Canvas(str(pdf_path), pagesize=A4)
    page.setFont("Helvetica-Bold", 16)
    page.drawString(50, 790, "Weekly Status")
    page.setFont("Helvetica", 10)
 
    y = 760
    for row in rows:
        line = "{} ({}) - {}".format(
            row.get("title", ""), row.get("owner", ""), row.get("status", "")
        )
        page.drawString(50, y, line)
        y -= 16
 
    page.save()
    return {"file_path": str(pdf_path)}
 
 
if __name__ == "__main__":
    try:
        raw = sys.stdin.read().strip()
        params = json.loads(raw) if raw else {}
        if not isinstance(params, dict):
            params = {"input": params}
        print(json.dumps(execute(params, {}), default=str))
    except Exception as exc:
        print(json.dumps({"error": str(exc)}, default=str))

reportlab is available in the skill runtime, alongside python-docx, Pillow, pypandoc, pypdf and requests. Files written to _OUTPUT_DIR are collected after the run and attached automatically, so the skill never returns file bytes in its JSON.

When a skill contains a .py file, it stops being only instructions. It is registered as a named tool on that agent, skill_weekly_status_pdf, with its own timeout, and the model calls it the way it calls anything else. That is the part that makes a skill useful in an unattended run: it is not a suggestion the model may follow, it is a function it can invoke, and the invocation is recorded.

Here is the whole thing wired to a trigger. Four nodes, no human.

Weekly status PDF, on a schedule
View template JSON
{
  "nodes": [
    {
      "id": "cron_1",
      "type": "cron",
      "position": {
        "x": 40,
        "y": 200
      },
      "data": {
        "label": "MondayMorning",
        "cronExpression": "0 7 * * 1"
      }
    },
    {
      "id": "http_1",
      "type": "http",
      "position": {
        "x": 300,
        "y": 200
      },
      "data": {
        "label": "FetchShipped",
        "curl": "curl -s -X GET \"https://api.example.com/shipped?range=7d\""
      }
    },
    {
      "id": "agent_1",
      "type": "agent",
      "position": {
        "x": 580,
        "y": 200
      },
      "data": {
        "label": "BuildReport",
        "model": "gpt-5.5",
        "temperature": 0,
        "outputType": "text",
        "systemInstruction": "You produce the weekly status report. Normalize the payload into a JSON array where every object has title, owner and status. Then call the skill_weekly_status_pdf tool with that JSON array as the input string. Return the tool result unchanged. Do not describe the report in prose.",
        "userMessage": "$FetchShipped.text",
        "tools": [],
        "mcpConnections": [],
        "skills": [
          {
            "id": "weekly-status-pdf",
            "name": "weekly_status_pdf",
            "timeoutSeconds": 30,
            "driveFilesEnabled": false,
            "content": "---\nname: weekly_status_pdf\ndescription: Turn a JSON list of shipped items into a formatted weekly PDF report. Use whenever a weekly status report needs to be produced as a file.\nparameters:\n  - name: input\n    type: string\n    description: JSON array of objects with title, owner and status keys\n    required: true\noutputs:\n  - name: file_path\n    type: string\n    description: Path to the generated PDF\ntimeout: 30\n---\n\n## Description\n\nRender a weekly status report as a PDF. The layout is fixed so the report looks the same every week regardless of which model produced the summary text.\n\n## Parameters\n\n- input (string, required): a JSON array. Each object needs title, owner and status.\n\n## Returns\n\n- file_path: where the PDF was written.\n",
            "files": [
              {
                "path": "main.py",
                "encoding": "text",
                "mimeType": "text/plain",
                "content": "#!/usr/bin/env python3\nimport json\nimport os\nimport pathlib\nimport sys\n\nfrom reportlab.lib.pagesizes import A4\nfrom reportlab.pdfgen import canvas\n\n\ndef execute(params: dict, files: dict) -> dict:\n    rows = json.loads(params.get(\"input\", \"[]\"))\n    out_dir = pathlib.Path(os.environ[\"_OUTPUT_DIR\"])\n    pdf_path = out_dir / \"weekly-status.pdf\"\n\n    page = canvas.Canvas(str(pdf_path), pagesize=A4)\n    page.setFont(\"Helvetica-Bold\", 16)\n    page.drawString(50, 790, \"Weekly Status\")\n    page.setFont(\"Helvetica\", 10)\n\n    y = 760\n    for row in rows:\n        line = \"{} ({}) - {}\".format(row.get(\"title\", \"\"), row.get(\"owner\", \"\"), row.get(\"status\", \"\"))\n        page.drawString(50, y, line)\n        y -= 16\n\n    page.save()\n    return {\"file_path\": str(pdf_path)}\n\n\nif __name__ == \"__main__\":\n    try:\n        raw = sys.stdin.read().strip()\n        params = json.loads(raw) if raw else {}\n        if not isinstance(params, dict):\n            params = {\"input\": params}\n        print(json.dumps(execute(params, {}), default=str))\n    except Exception as exc:\n        print(json.dumps({\"error\": str(exc)}, default=str))\n"
              }
            ]
          }
        ]
      }
    },
    {
      "id": "out_1",
      "type": "output",
      "position": {
        "x": 860,
        "y": 200
      },
      "data": {
        "label": "ReportFile",
        "outputSchema": [
          {
            "key": "report",
            "value": "$BuildReport.text"
          }
        ]
      }
    }
  ],
  "edges": [
    {
      "id": "e1",
      "source": "cron_1",
      "target": "http_1"
    },
    {
      "id": "e2",
      "source": "http_1",
      "target": "agent_1"
    },
    {
      "id": "e3",
      "source": "agent_1",
      "target": "out_1"
    }
  ]
}
A cron trigger, an HTTP fetch, an Agent node carrying one skill, and an output. Nobody types anything.

If you want a version that is already live rather than one you assemble, the YouTube RSS to CSV template is a published Heym template built on exactly this pattern: an Agent node whose bundled skill parses XML deterministically and writes the CSV to Drive.

For non-developers there is also an AI Build path. You describe the skill in a chat prompt, the builder streams a live preview of the generated SKILL.md and .py files, and you either save it onto the node or download the zip. Drop files into the prompt and they are attached as bundled assets. It does not remove the need to read what it wrote, but it removes the blank page.

In Context Is Not the Same as Executed

This is the smallest idea in the article and possibly the most useful.

There are two different facts about any run involving skills, and almost every tool records at most one of them:

  1. Which skills were in the model's context. A property of the configuration.
  2. Which skills the model actually invoked. A property of what happened.

A skill sitting in context and never firing looks exactly like a working skill from the outside. The run succeeds, the output is plausible, the logs are clean. The only difference is that your careful deterministic procedure did not run and a language model improvised the same job.

Heym writes both to the trace as separate fields. skills_included lists what was attached and loaded. skills_used is computed after the fact by matching the skills whose generated tool name actually appears in the invoked tool calls, so it lists only what genuinely ran. Two fields, and the gap between them is the diagnostic.

If you take one operational habit from this piece, take that one, and implement it however your stack allows. It slots into the same set of signals covered in AI agent observability, and it answers a question the rest of that dashboard cannot: not "did the run work", but "did it work the way I designed it to".

Notable fact: Of the 46 client products listed as supporting Agent Skills on agentskills.io on 8 August 2026, 33 are coding agents, IDEs or terminals, 4 are frameworks a developer imports, and the remaining 9 are chat apps, personal assistants, and domain products. Not one of them is a workflow canvas where a trigger starts the run. The list grows most weeks, so treat the exact number as a snapshot and the shape as the point.

For what it is worth, we have submitted Heym to that showcase. At the time of writing the pull request is open and unreviewed, so the count above still describes a list we are not on.

When a Skill Should Stop and Ask

There is one more thing a skill can do in an unattended run that has no equivalent in a terminal, and it is the resolution to the "failure has nowhere to go" problem from earlier.

A skill's Python can pause the workflow and request a human review. The script writes a sentinel file, the runtime turns it into a pause rather than a result, and the run stops with a review link instead of committing to an action. The person who eventually looks at it sees the draft the skill produced and decides.

That is a different shape from an interactive agent asking a question, because the run is not waiting on a conversation. It is suspended, with state, and it will resume from where it stopped whenever the answer arrives. The design rules for when to use it are the same ones in human in the loop AI agents, and they compress to one question: is this action irreversible or externally visible? If yes, a skill that pauses beats a skill that is confident.

When a Skill Is the Wrong Answer

Skills are being over-applied right now, in the same way MCP servers were over-applied last year. Three cases where something else is better:

You wantUse insteadWhy
Live data from an authenticated systemAn MCP server or a nodeA skill has no credential model of its own
A step that must run every single timeA node in the graphAnything mediated by a model is a step that can be skipped
Behavior shared by every agent you runThe system instruction, or a sub-workflowA skill attached to twelve nodes is twelve copies to keep in sync

And one non-obvious case. If the procedure is short enough to state in three sentences, it is not a skill, it is a prompt. The overhead of a skill only pays off when the procedure is long enough that repeating it is a real cost, or exact enough that drift is a real risk. Cisco's framing is the right test: a prompt is disposable and a skill is institutional knowledge. If closing the window would not lose anything worth keeping, you do not have a skill yet.

What Is Heym?

Heym is an open-source, self-hosted AI workflow automation platform. You build workflows on a visual canvas from nodes: triggers, LLM and Agent nodes, HTTP calls, conditions, loops, and integrations. Agent nodes carry Skills in the standard SKILL.md format, run their Python in a hardened sandbox, and record what happened in built-in traces. It is MIT licensed with a Commons Clause, and it runs on your own infrastructure. Browse the template library or read the Agent node docs.

Frequently Asked Questions

What are agent skills?

An agent skill is a folder containing a SKILL.md file, plus any scripts, references, or assets that file points to. The SKILL.md begins with YAML frontmatter that must carry at least a name and a description, followed by instructions written for the agent rather than for a person. Anthropic published the format in October 2025 and released it as an open standard that December, and it is now supported by 46 client products listed on agentskills.io. The point of the format is portability: you write the procedure once and any skills-compatible agent can load it.

Can an agent skill run on a schedule with no human present?

Only if the runtime is built for it, and most skills-compatible clients are not. Of the 46 clients listed on agentskills.io in early August 2026, 33 are coding agents, IDEs, or terminals, 4 are frameworks a developer imports into their own code, and the rest are chat apps, personal assistants, or domain products. All of them assume a session that a person starts. Running a skill unattended needs four things the standard does not specify: an activation rule that does not depend on a model guessing right, a failure path that does something other than wait for a reply, a record of what happened, and a credential model, since a skill on a server cannot inherit the ambient access it would have had on your laptop. In Heym a skill is attached to an Agent node on a canvas, so a cron, webhook, or email trigger starts the run and the skill is already in context.

Are agent skills the same thing as MCP?

No, and they are not competing either. MCP is a client-server protocol that gives an agent live, authenticated access to an external system. A skill is a file format that gives an agent a written procedure and optional local code. The clearest split is that MCP answers what the agent can reach and a skill answers how the agent should proceed. They also fail differently: an MCP server can be down, while a skill can only be wrong. Most production setups use both.

Is it safe to run an agent skill someone else wrote?

Treat it as untrusted code, because that is what it is. A skill can bundle executable scripts, and NVIDIA's SkillSpector work names the specific risks well: hidden instructions, prompt injection, trigger abuse, excessive agency, tool poisoning, and mismatches between a skill's declared purpose and its bundled behavior. Scanning before installation covers the supply chain. It does not cover runtime. A skill you scanned last week still executes on your infrastructure today, so the second half of the answer is containment: run the code in a throwaway sandbox with no access to your secrets, and make the sandbox fail closed when it cannot start. We learned this one first-hand: Heym shipped skill execution without the Docker isolation its Python tools already required, published as CVE-2026-67543 in July 2026, and the fix was to route skill code through the identical fail-closed sandbox.

How do I know whether the agent actually used my skill?

You need two separate facts, and most tooling records neither. The first is which skills were in the model's context for that call. The second is which skills the model actually invoked. A skill can sit in context all day and never fire, which looks identical to a working skill until you check the output. Heym records both as distinct fields on the trace, skills_included and skills_used, so a run where the skill was available but ignored is visibly different from a run where it executed.

What does an agent skill cost me in context?

It depends entirely on whether the runtime uses progressive disclosure. Google's ADK team published useful figures for the disclosure case: roughly 100 tokens of metadata per skill at the discovery level and under 5,000 tokens for a full instruction file, so an agent with ten skills starts each call with about 1,000 tokens instead of 10,000. A runtime that loads every attached skill in full pays the larger number every run. There is usually no hard cap on how many you can attach, and the model's selection does work, but the cost is linear in the number attached and you pay it whether or not a skill is used. Treat it as a budget question: a node carrying twenty skills is paying for twenty instruction files on every single run, including the nineteen that were irrelevant to this input.

Where This Goes

The format is good. It is the first packaging unit for agent behavior that is small enough to write by hand, portable enough to share, and boring enough to standardize on. Forty-six clients in under a year, with new ones landing most weeks, is not an accident.

What is missing is not in the specification, and it should not be. It is in the runtimes. A standard that says "here is a folder" is doing its job; it is the thing loading the folder that has to decide whether activation is a guess, whether the code is contained, whether last week's version is recoverable, and whether anyone can prove what ran.

Right now almost every runtime answering those questions is answering them for a person at a keyboard. That is a reasonable place to start and a strange place to stop, because the runs that most need a written-down, version-controlled, deterministic procedure are exactly the ones with nobody in the room.

If you want to try skills under a trigger rather than in a session, the template library is the fastest way in, and what AI workflow automation is covers the surrounding model if this is your first canvas.

Steps at a glance

  1. Write the SKILL.md before the code. Start with the frontmatter name and description, then write the instructions as if you were onboarding someone who is competent but has never seen this task. The description is the part the model reads when deciding whether the skill applies, so it should say when to use the skill, not just what it does.
  2. Add main.py only if the task needs determinism. If the procedure is judgment, prose is enough. Add Python when you need the same input to produce the same output every time: parsing, arithmetic, formatting, file generation. In Heym main.py must expose a top-level execute function and print JSON to stdout, or the skill silently returns nothing.
  3. Attach the skill to an Agent node. Drop the folder onto the Skills area of an Agent node as a zip, or build it from a prompt with AI Build. Attachment is the activation rule: the SKILL.md content is prepended to that node's system instruction on every run, so there is no discovery step that can go the wrong way.
  4. Give the skill a trigger, not a chat box. Wire a Cron, webhook, or email trigger into the node so the run starts without a person. This is the step that separates a skill you demo from a skill that works, because everything downstream now has to survive with nobody watching.
  5. Set the timeout and decide on Drive access. Each skill carries its own timeout, 30 seconds by default, applied to the Python execution rather than the whole run. Leave Drive files off unless the code genuinely has to read stored files, because that switch is what generates the heym_drive helper for the sandbox.
  6. Run it once and read both trace fields. Check that the skill appears in skills_included, then confirm it also appears in skills_used. If the first is present and the second is missing, the model had your procedure and chose not to run it, which is a prompt problem rather than a code problem.
Mehmet Burak Akgün
Mehmet Burak Akgün

Founding Engineer

Burak is a founding engineer at Heym, focused on backend infrastructure, the execution engine, and self-hosted deployment. He builds the systems that make Heym's AI workflows run reliably in production.

Enjoyed this post? Get the next one in your inbox.

A monthly note with practical ideas for building AI workflows that hold up in production. No noise, and you can unsubscribe anytime.

No spam, no marketing fluff