Skip to content

fix(skills): add normalize_skill_files() helper and document path naming convention#1590

Open
ppradyoth wants to merge 2 commits into
anthropics:mainfrom
ppradyoth:fix/skills-create-normalize-file-paths
Open

fix(skills): add normalize_skill_files() helper and document path naming convention#1590
ppradyoth wants to merge 2 commits into
anthropics:mainfrom
ppradyoth:fix/skills-create-normalize-file-paths

Conversation

@ppradyoth
Copy link
Copy Markdown

@ppradyoth ppradyoth commented May 22, 2026

Summary

Closes #1575.

beta.skills.create(files=...) silently requires every path to be prefixed
with a top-level directory whose name exactly matches the name: field in
SKILL.md frontmatter (e.g. my-skill/SKILL.md, my-skill/scripts/tool.py).
When callers pass bare names like "SKILL.md" they receive a cryptic 400 with
no guidance on what went wrong.

What this PR does

Normalization is now automaticSkills.create / AsyncSkills.create
call normalize_skill_files() internally before building the multipart request,
so callers never need to know about the prefix requirement:

# All three are now equivalent — the SDK fixes the paths transparently:
client.beta.skills.create(files=[("SKILL.md",        skill_md, "text/markdown")])
client.beta.skills.create(files=[("wrong/SKILL.md",  skill_md, "text/markdown")])
client.beta.skills.create(files=[("my-skill/SKILL.md", skill_md, "text/markdown")])

This makes upload symmetric with _archive_top_dir on the download side, which
already strips the wrapper directory when extracting — the constraint was always
there in both directions, but only enforced on upload.

Name resolution order

  1. name: field in SKILL.md YAML frontmatter (primary)
  2. display_title normalised to lowercase-with-hyphens (fallback when frontmatter omits name:)
  3. If neither is available the original file list is passed through unchanged and the API will surface a descriptive error

Key advantages over the other PRs addressing #1575

This PR (#1590) #1577 (docs only) #1604 (auto-wire)
Auto-wired into create()
Wrong prefix replaced (not double-prefixed) ❌ bug
display_title fallback
Idempotent (apply twice → same result)
PathLike file content
normalize_skill_files() exported for explicit use

Changes

  • src/anthropic/lib/tools/_skills.pynormalize_skill_files(files, *, display_title=None) with wrong-prefix replacement, display_title fallback, and idempotency; _parse_skill_name_from_frontmatter returns str | None instead of raising
  • src/anthropic/lib/tools/__init__.py — re-exports normalize_skill_files for callers who want to inspect normalized paths before uploading
  • src/anthropic/resources/beta/skills/skills.py — auto-wires normalization into both Skills.create and AsyncSkills.create; updated files= docstring to say normalization is automatic
  • tests/lib/tools/test_skills.py — 27 tests total (10 new: idempotency, display_title fallback, special-char normalization, 2-tuple entries, PathLike content, frontmatter returning None, wrong-prefix replacement)

Test plan

uv run pytest tests/lib/tools/test_skills.py -v  # 27 passed

…ing convention

Adds a `normalize_skill_files()` utility to `anthropic.lib.tools` that
rewrites every file-tuple path in a `beta.skills.create(files=...)` call
so it is correctly prefixed with the skill name read from the SKILL.md
frontmatter.  The `files=` parameter requires all paths to start with
`<name>/`; without the helper callers must do this manually, and the
error they get when they forget is an opaque API 400.

Also updates the `files=` param docstring in the generated skills
resource to describe the naming convention and cross-reference the new
helper.

Fixes anthropics#1575
@ppradyoth ppradyoth marked this pull request as ready for review May 22, 2026 18:55
@ppradyoth ppradyoth requested a review from a team as a code owner May 22, 2026 18:55
…dempotency

- Wire normalize_skill_files() into Skills.create / AsyncSkills.create so
  callers never need to know about the <name>/ prefix requirement — bare
  paths and wrong-prefix paths are fixed transparently before the request
  is sent (symmetric with _archive_top_dir on the download side).

- Add display_title fallback: when SKILL.md frontmatter has no name: field,
  the method's display_title param is normalized to lowercase-with-hyphens
  and used as the prefix instead of surfacing a confusing 400.

- Idempotent: already-correct paths are left unchanged; wrong top-level
  prefix is stripped-and-replaced rather than double-prefixed (bug in anthropics#1604).

- _parse_skill_name_from_frontmatter now returns str | None instead of
  raising, keeping error-path control in normalize_skill_files().

- Updated docstrings in Skills.create / AsyncSkills.create to show that
  normalization is automatic; users no longer need to think about layout.

- 27 tests passing (added 10 new: idempotency, display_title fallback,
  special-char normalization, 2-tuple entries, PathLike content,
  frontmatter parse returning None, wrong-prefix replacement).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

beta.skills.create: undocumented requirement that files path prefix must match SKILL.md name: field

1 participant