Skip to content

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:

  1. list_skills — the name, description and path of every skill.
  2. load_skill — one skill's full SKILL.md.
  3. load_skill_resource and execute_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."}
  ]
}
  • path and directory are absolute. license, compatibility and metadata appear only when the frontmatter has them; metadata values are turned into strings.
  • diagnostics is present only when something was wrong. Each entry has skill, level (warning or error), code and message. Errors skip the skill; warnings do not.
  • When the skills folder does not exist, the result has an empty skills list, count: 0 and a note saying 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_skills and puts each skill's name, description and path into the system prompt, and logs each diagnostics entry as a warning. Diagnostics never reach the model.
  • When there are no skills, it hides load_skill, load_skill_resource and execute_skill_script from the model to save tokens, and keeps list_skills and create_skill visible.
  • It finds the skills server by name: the name must contain skill. skills_server is named skills-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_script and create_skill accept 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_script runs only .py files, with the same Python interpreter as the server, in the script's own folder. args is split with POSIX shell rules, so quote arguments that contain spaces.
  • A script that times out returns exit_code: -1 and a timeout message in stderr.
  • Large SKILL.md files 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 with load_skill_resource.
  • Resource listings are bounded. load_skill lists the files in the skill folder and its immediate subfolders (scripts/normalise_totals.py, but not scripts/lib/util.py), at most 50 with a count of the rest, skipping hidden files and folders such as __pycache__ and node_modules. Deeper files can still be read with load_skill_resource.
  • create_skill writes a template. The new SKILL.md has the frontmatter and a TODO body; 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.