Skills Server¶
skills_server (doc_processing_std_tools.skills, MCP name skills-tools) lets an agent discover,
load and run Agent Skills: folders of instructions, reference files and scripts that follow the
Agent Skills specification. Skills work the same with every
model provider. The server needs no extra (PyYAML is a base dependency).
from doc_processing_std_tools import skills_server
How skills are laid out¶
Each skill is a folder under DPTTOOLS_SKILLS_DIR containing a SKILL.md with YAML frontmatter:
.agent_skills/
invoice-extraction/
SKILL.md
scripts/
normalise_totals.py
references/
field-glossary.md
---
name: invoice-extraction
description: Extracts header fields and line items from supplier invoices into JSON.
license: MIT
compatibility: Needs the docs extra.
metadata:
owner: finance-team
---
# Invoice extraction
1. Run `get_file_overview` on the invoice ...
The agent reads skills in three levels, loading only what it needs:
list_skills— the name, description and path of every skill.load_skill— one skill's fullSKILL.md.load_skill_resourceandexecute_skill_script— the files and scripts the skill refers to.
Tools¶
| Tool | Key parameters | Returns |
|---|---|---|
list_skills |
— | JSON: skills, skills_dir, count, and diagnostics when there are warnings (see below) |
load_skill |
skill_name |
The SKILL.md wrapped in <skill_content name="..." directory="...">, with a <skill_resources> list of the skill's files and the skill directory |
load_skill_resource |
skill_name, resource_path (relative to the skill folder) |
The file's text |
execute_skill_script |
skill_name, script_path (a .py file in the skill), args="" |
JSON: stdout, stderr, exit_code |
create_skill |
skill_name, description |
Confirmation with the new skill's folder, which holds a template SKILL.md |
The list_skills payload¶
{
"skills": [
{
"name": "invoice-extraction",
"description": "Extracts header fields and line items from supplier invoices into JSON.",
"path": "/workspace/.agent_skills/invoice-extraction/SKILL.md",
"directory": "/workspace/.agent_skills/invoice-extraction",
"license": "MIT",
"compatibility": "Needs the docs extra.",
"metadata": {"owner": "finance-team"}
}
],
"skills_dir": "/workspace/.agent_skills",
"count": 1,
"diagnostics": [
{"skill": "draft-skill", "level": "error", "code": "missing_description",
"message": "SKILL.md frontmatter is missing a non-empty 'description' field."}
]
}
pathanddirectoryare absolute.license,compatibilityandmetadataappear only when the frontmatter has them;metadatavalues are turned into strings.diagnosticsis present only when something was wrong. Each entry hasskill,level(warningorerror),codeandmessage. Errors skip the skill; warnings do not.- When the skills folder does not exist, the result has an empty
skillslist,count: 0and anotesaying so.
Frontmatter rules¶
| Situation | Result |
|---|---|
| No frontmatter, or YAML that cannot be parsed | Skill skipped, error diagnostic |
Missing or empty description |
Skill skipped, error diagnostic |
An unquoted colon in description (common in skills written for other clients) |
Quoted automatically and loaded, warning diagnostic |
Missing name |
The folder name is used, warning diagnostic |
name differs from the folder, breaks the naming pattern, or is too long |
Loaded, warning diagnostic |
description over 1024 characters, compatibility over 500, metadata not a mapping |
Loaded (a non-mapping metadata is dropped), warning diagnostic |
The allowed-tools field is not surfaced: this stack does not restrict tools per skill.
Environment variables¶
| Variable | Default | Effect here |
|---|---|---|
DPTTOOLS_SKILLS_DIR |
./.agent_skills |
The skills folder, resolved to an absolute path when configuration is first read |
DPTTOOLS_SANDBOX_TIMEOUT |
30 |
Timeout for execute_skill_script, in seconds |
DPTTOOLS_MAX_FILE_SIZE_MB |
50 |
load_skill_resource refuses larger files |
DPTTOOLS_OUTPUT_TRUNCATION_LIMIT |
50000 |
Cuts load_skill, resource and script output |
A server only when skills exist¶
skills_server always registers all five tools, so a skill added during a session is found by the
next list_skills call. For a static deployment that should advertise no skills tools at all when
the folder is empty, use the factory, which returns None in that case:
from doc_processing_std_tools import file_system_server, make_skills_server
skills = make_skills_server() # None if DPTTOOLS_SKILLS_DIR is missing or has no skill
agent = Agent(model="claude-sonnet-5", tools=[s for s in [skills, file_system_server] if s])
The factory checks the folder once, when it is called.
With doc-processing-toolkit¶
The toolkit reads this server automatically (Skills auto-injection):
- It calls
list_skillsand puts each skill'sname,descriptionandpathinto the system prompt, and logs eachdiagnosticsentry as a warning. Diagnostics never reach the model. - When there are no skills, it hides
load_skill,load_skill_resourceandexecute_skill_scriptfrom the model to save tokens, and keepslist_skillsandcreate_skillvisible. - It finds the skills server by name: the name must contain
skill.skills_serveris namedskills-tools, which matches; the composed server,doc-processing-std-tools, does not.
Those three tool names, the naming rule and the path and diagnostics fields are a contract
between the two packages; std-tools has supplied the fields since 0.2.0.
Toolkit 0.5.2 and earlier: connect the skills server through a pre-built session
In those toolkit versions, auto-injection works only when your application starts the server
itself, passes DPTTOOLS_SKILLS_DIR in the session's env, and hands the toolkit
{"session": session, "name": "...skill..."}; DPTTOOLS_SKILLS_DIR must also be set in the
application's own environment. In-process Agent(tools=[skills_server]) and mcp_command
configs return an empty catalog, and the agent never hears of your skills. This is a toolkit
issue; Compatibility
has the working code.
Pitfalls¶
- Skill names are strict in tool calls.
load_skill,load_skill_resource,execute_skill_scriptandcreate_skillaccept only 1–64 lowercase letters, digits and single hyphens, not starting or ending with a hyphen, and the name must be the folder name. - Paths cannot leave the skills folder. Resource and script paths are resolved inside
DPTTOOLS_SKILLS_DIR; one that escapes is refused. - Scripts must be Python.
execute_skill_scriptruns only.pyfiles, with the same Python interpreter as the server, in the script's own folder.argsis split with POSIX shell rules, so quote arguments that contain spaces. - A script that times out returns
exit_code: -1and a timeout message instderr. - Large
SKILL.mdfiles are cut to fit the output limit, but the resource list and the skill directory are always kept, so the agent can read the rest withload_skill_resource. - Resource listings are bounded.
load_skilllists the files in the skill folder and its immediate subfolders (scripts/normalise_totals.py, but notscripts/lib/util.py), at most 50 with a count of the rest, skipping hidden files and folders such as__pycache__andnode_modules. Deeper files can still be read withload_skill_resource. create_skillwrites a template. The newSKILL.mdhas the frontmatter and aTODObody; the agent fills in the instructions with a file tool.
Recipes¶
- Skills Setup: write a skill, check it with
list_skills, and attach the server to a toolkit agent.