SKILL.md folders, offer the skills to the model, and give it tools to load a skill, read its files and run its scripts. decimalai skills add writes folders all eight accept. This page is the one line that points each framework at them, and what DecimalAI records when the framework loads one.
Each framework has a helper in its decimalai.<framework> module (pip install "decimalai>=0.16"). It points the framework’s own loader at your installed skills and, with DecimalAI’s instrumentation on, every skill the loader hands the model is recorded as an activation of the exact version the model received. DecimalAI’s skill router stays out of an agent whose own loader serves its skills, so the model sees one catalog.
What DecimalAI records
When the framework’s loader delivers a skill to the model, the trace for that run gets an activation carrying the skill’s registry id and version id. The version is decided from the bytes the model received, never from the folder on disk:- the identity stamp in the delivered copy (
metadata.decimalai-*), when the delivered body’s hash matches it; - otherwise the lock, by the delivered body’s hash;
- otherwise any version this process has already seen with that hash.
decimalai skills update. Some loaders keep serving a skill they already loaded, so the folder holds version 4 while the model still reads version 3, and the activation then says version 3:
Also on the trace:
- A skill loaded twice in one run is one activation.
- The skills the loader listed for the model are recorded as offered.
- The router steps aside. An agent whose own loader serves its skills gets no DecimalAI skill list and no DecimalAI
load_skilltool; the SDK logs this once at INFO, or as a warning on an OpenAI Agents, Pydantic AI or Google ADK agent you also gaveagent_config()’s skills. If both still reach the model, because the loader was attached where the adapter could not see it, a WARNING says so once.
Setup at a glance
Each helper lives in its framework’s module; import the module before you use it (
import decimalai.adk): import decimalai alone does not load it. Microsoft Agent Framework, Strands and the Pydantic AI harness need no DecimalAI adapter: with init(otel=True) (and, on Pydantic AI, Agent.instrument_all()) their own OpenTelemetry spans become the trace, and the load is recorded on it. Pass agent_name there, since the exporter names the trace by it. The Pydantic AI harness needed decimalai.pydantic_ai.instrument() as well through SDK 0.18; 0.19 removed it. Run inside decimalai.start_trace() and the load is recorded on that trace instead.
decimalai.skills_dir()
The helpers start from decimalai.skills_dir(); you can use it directly too.
- It finds the project from the working directory, as every
decimalai skillscommand does, so it works from any subdirectory of the project.skills_dir(directory=...)starts the search somewhere else. - It returns the absolute, symlink-resolved path. With
relative_to=, it returns the path relative to that directory instead, for the two loaders that need one (OpenAI Agents and the Pydantic AI harness), and raisesUsageErrorwhen the root is not under it. - It raises
SkillsNotInstalledErrorwhen the project has no lock or no root yet (rundecimalai skills install),LegacyLockErrorfor a lock from SDK 0.14 or older (rundecimalai skills migrate), andIllegalEntryErrorwhen.agentsor.agents/skillsis a symlink.
The eight loaders
Each snippet below is the documented setup that the SDK’s own test suite runs against the real framework. In the suite a scripted model stands in for yours;
model, llm and client in the snippets are your framework’s own model objects.
Claude Agent SDK and Claude Code
The Claude Agent SDK drives the Claude Code CLI, and both load.agents as a plugin named decimalai-skills: its skills/ folder is the root the other seven read.
- The plugin path must be absolute: a relative
--plugin-dir .agentsstarted from a subdirectory loads nothing, with exit status 0 and nothing on stderr.skills_plugin()returns an absolute one. - Skills are listed as
decimalai-skills:<install-name>. The plugin is not nameddecimalai, the marketplace plugin’s name, so the two load side by side. - It reads
name,descriptionand the body, and acts onallowed-toolsanddisable-model-invocation. It pre-approves the tools a skill lists: a skill your organization authored can run its own bundled scripts with no prompt (Bash(python3 ${CLAUDE_SKILL_DIR}/scripts/… *)), any other command needs permission, and a skill your organization did not author lists onlyRead,GrepandGlob, so its scripts always need permission. A user-only skill (disable-model-invocation: true) is listed but never offered to the model. - The frontmatter never reaches your process, so the version comes from the lock, by the hash of the body the CLI delivered.
Google ADK
-
The plugin in the
Apptraces each invocation and records each load (SDK 0.20+; through 0.19,decimalai.init(adk=True)added it to every runner). -
decimalai.adk.skills()loads each folder on its own and skips a bad one with a WARNING that names it. ADK’s ownload_skills_from_dirraises on one bad folder and loads nothing at all. -
It reads each folder’s frontmatter,
metadataincluded, and the body. The model first lists the skills, then loads one. -
ADK parses the skills when
skills()runs, so an agent built beforedecimalai skills updatekeeps serving the old version until you build it again. The activation says which version that was. -
Without a
code_executor, ADK offers norun_skill_script.UnsafeLocalCodeExecutorruns scripts on your machine with no sandbox; use it only for skills you trust.
CrewAI
- CrewAI’s own skill tool is
load_skill, which delivers the body. CrewAI has no resource reader and no script runner of its own, soskill_tools()adds two:load_skill_resourcereads a skill’sreferences/,assets/orscripts/file, andrun_skill_scriptruns one of itsscripts/(Python with your interpreter, shell scripts withbash, no shell expansion, with a timeout). Both accept only installed skills and paths inside them.run_skill_scriptdoes not enforce a skill’sallowed-tools;skill_tools(scripts=False)leaves it out. - It has the strictest
metadataparser of the eight: every value must be a string. A bad folder is skipped with aSkillLoadFailedEventand a log line, not an exception. - CrewAI reads a skill’s body from disk on every load, so it serves the new version right after
decimalai skills update.
OpenAI Agents (SandboxAgent)
sandbox_skills()is the sandbox’sSkills(lazy_from=LocalDirLazySkillSource(...))overskills_dir(relative_to=Path.cwd()). Run from the project root: a source outside the working directory, reached through a symlink or through the macOS/tmpalias, lists zero skills.- It adds two guards the plain capability lacks. The sandbox skips a symlinked skill folder without a word, so
sandbox_skills()warns when it would list nothing. And the sandbox’sload_skillaborts the whole run on a name it cannot find; the helper’s answers the model with the names it can load instead. - Keep
default_manifest=Manifest(); without it the skills vanish. - When the model loads a skill, the sandbox copies its folder to
.agents/<install-name>and drops the exec bit on the way, so run scripts through their interpreter (python3 scripts/compute_refund.py), never as./scripts/.... DecimalAI identifies the load from that copy, the bytes the model reads. - On macOS, commands run under
sandbox-exec.
Deep Agents
decimalai.deepagents.skills()returns the two arguments Deep Agents needs together:skills=[<your skills folder>]and abackendthat can see it. The default backend is aFilesystemBackend(root_dir=<project>, virtual_mode=False), which reads files but has no shell, so a skill’s scripts cannot run. To run them, pass one:decimalai.deepagents.skills(backend=LocalShellBackend(root_dir=project, virtual_mode=False)).LocalShellBackendruns commands on your machine.virtual_mode=False. WithTrue, the prompt advertises virtual/skills/...paths thatexecutecannot open.- The skills middleware lists each skill in the system prompt with the path of its
SKILL.md; the model reads it withread_file. A read that stops short of the whole file delivers part of the body, and the activation then carries no version, because that body is no version. - A namespaced or uppercase
name:is listed under that name, with a warning on thedeepagentslogger, rather than under its folder’s.
Microsoft Agent Framework
skills()isSkillsProvider.from_paths(...)over your skills folder, with three things added on the same object: the observer, a script runner (MAF ships none; without one a script answers that it requires a runner), and an approval rule. MAF makes every skill tool wait for approval by default; the helper lets skill loads and file reads run unasked, and keeps scripts waiting for approval unless you passskills(auto_approve_scripts=True).- It caches skills by default, so a long-running provider keeps serving the old version after
decimalai skills update, and the activation says so. Restart the process to serve the new one. - Without
enable_sensitive_telemetry(), the activation is still recorded, but the trace does not show what each tool call read or returned. - A bad folder is skipped, with an ERROR line on the
agent_frameworklogger.
Pydantic AI harness
decimalai.pydantic_ai_harness.skills(project)is the harness’sSkillsover your skills folder, relative to the workspace as the harness requires: a skills folder outside it still lists, butread_filerefuses its files.- No DecimalAI adapter is involved (SDK 0.19+). Don’t also give this agent
decimalai.pydantic_ai.agent_config()’s capability: the harness’s catalog already serves its skills, so the capability adds neither DecimalAI’s skill list norload_skill, and one warning says so. - The harness keeps a skill’s name, description and body;
metadatais parsed and dropped, so the version comes from the lock, by the hash of the delivered body. - It keeps a user-only skill (
disable-model-invocation: true) out of the model’s catalog. It does not enforceallowed-tools, and says so on every run with aUserWarning: Ignoring unsupported Agent Skill behavioral frontmatter fields. - A bad folder goes into the catalog’s
skippedlist with aUserWarning, never an exception.
Strands Agents
skills()isAgentSkills(skills=<absolute skills folder>, strict=True)with the observer attached, still the one plugin Strands knows asagent_skills. Theskillstool answers with the body and aLocation:line; the model then reads files withfile_editorand runs scripts withshell.file_editorrefuses relative paths, which is why the root is absolute.- Strands puts tool arguments and results in span events, which the exporter does not read. Set
OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental,gen_ai_span_attributes_onlybefore the first agent is built to have them on the trace; the activation is recorded either way. - Strands loads an agent’s skills once, so the same agent keeps serving the old version after
decimalai skills update. strict=Trueon the plugin does not raise: it logs “failed to load skill” and skips that folder.Skill.from_file(path, strict=True)is the call that raises.- Always pass
model=. Strands’ default model is Amazon Bedrock.
Known upstream gaps
Facts about the frameworks, not about the folders. Each is re-checked on every run of the SDK’s test suite, so the line here goes the day the framework changes.What a loader does with a bad folder
Measured one defect at a time, with every loader at the version graded above. Most of them do not raise:- Google ADK raises for a name or a list-typed field that breaks the format, and then loads nothing at all (
decimalai.adk.skills()skips that folder instead). - Microsoft Agent Framework, CrewAI, Strands and the Pydantic AI harness skip the folder and log a warning, which is easy to miss.
- OpenAI Agents and Deep Agents rename: a namespaced or uppercase
name:is listed under that name instead of the folder’s. OpenAI Agents also skips a symlinked skill folder without a word. - Claude Code lists a skill whose
name:differs from its folder under the other name.
decimalai skills verify reports these defects before a loader drops them, and the folders decimalai skills add writes have none of them.
How the SDK checks this
On every commit, the SDK’s test suite starts each of the eight loaders in a child process of its own, points it at a project in the formatdecimalai skills add writes, and drives it with a scripted model.
That the loaders read the folders. One scenario per loader (load a skill, read one of its bundled files, run its script, answer), checking that:
- the loader lists exactly the installed skills, under their install names;
- its own warning channel stays quiet;
- the model receives the skill’s own body, and its hash equals the stamp;
- a bundled file reads back byte for byte, and a bundled script runs and prints what it should, through the loader’s own tools;
- the identity stamp reads back and equals the lock;
- user-only skills are hidden (Claude Code, the Pydantic AI harness), or the upstream gap above holds;
- the script keeps its exec bit in the copy the loader runs.
decimalai skills update moves the skill to a new version, and the same agent loads it again. The suite checks the traces DecimalAI sends for:
- the listing;
- one activation carrying the installed version’s exact ids;
- the resource read and the script run;
- after the update, the version whose body the model actually received.
Without a helper
The DecimalAI adapters for the Claude Agent SDK, Google ADK, CrewAI, OpenAI Agents and Deep Agents watch the framework’s own loader however you set it up, so the plain snippets from those frameworks’ docs are recorded too. For Microsoft Agent Framework, Strands and the Pydantic AI harness, the observer lives on the object the helper returns. Anywhere else, record a load yourself inside a trace. Name the skill as the loader shows it, by its install name,decimalai-skills:<install-name> or its folder path. Before the trace is sent, the SDK looks the name up in the lock and adds the registry name, skill id, version id and body hash. It hashes the folder as it is at that moment, though, so after a skills update a loader that cached the old version is credited with the new one. The helpers above do not have that problem.