Skill Plugin Model

Smarter API PluginData Skill Connection Manifest Constants.

Note

Experimental. The SkillPlugin was designed and coded by Claude Code (Anthropic’s Claude Opus 5.5), with Lawrence McDaniel as co-author. It is experimental, and will be documented.

Parsing and validation of SKILL.md documents, per the Agent Skills specification.

An Agent Skill is a directory containing, at minimum, a SKILL.md file: a YAML frontmatter block followed by Markdown instructions. The directory may also contain bundled files, conventionally in scripts/, references/ and assets/, which SKILL.md refers to by paths relative to the skill root.

---
name: pdf-processing
description: Extract PDF text, fill forms, merge files. Use when handling PDFs.
license: Apache-2.0
allowed-tools: Bash(python:*) Read
metadata:
  author: example-org
  version: "1.0"
---

# PDF processing

## Filling forms
Read [the forms guide](references/FORMS.md), then run `scripts/fill_form.py`.

This module is shared by the manifest (Pydantic) layer and the storage (Django ORM) layer, so that both parse and validate SKILL.md text identically.

See also

Note

Experimental. The SkillPlugin was designed and coded by Claude Code (Anthropic’s Claude Opus 5.5), with Lawrence McDaniel as co-author. It is experimental, and will be documented.

smarter.apps.plugin.manifest.models.skill_plugin.document.MAX_RESOURCES = 100

The maximum number of bundled files.

smarter.apps.plugin.manifest.models.skill_plugin.document.MAX_RESOURCE_LENGTH = 500000

The maximum length of a single bundled file, in characters.

smarter.apps.plugin.manifest.models.skill_plugin.document.MAX_SKILL_DOCUMENT_LENGTH = 200000

The maximum length of a SKILL.md document, in characters.

The specification recommends < 500 lines.

class smarter.apps.plugin.manifest.models.skill_plugin.document.SkillDocument(document, frontmatter, body, allowed_tools=<factory>)[source]

Bases: object

A parsed and validated SKILL.md document.

Variables:
  • document – The verbatim SKILL.md text, with line endings normalized to \n.

  • frontmatter – The complete parsed frontmatter, including any keys beyond the standard fields, such as the extensions supported by Claude Code.

  • body – The Markdown instructions that follow the frontmatter.

  • allowed_tools – The allowed-tools frontmatter field, normalized to a list.

__init__(document, frontmatter, body, allowed_tools=<factory>)
allowed_tools: list[str]
body: str
property compatibility: str | None

The environment requirements of the skill, if any.

property description: str

What the skill does and when to use it.

document: str
frontmatter: dict[str, Any]
property license: str | None

The license applied to the skill, if any.

property metadata: dict[str, Any]

additional properties not defined by the specification.

Type:

The metadata frontmatter field

property name: str

The skill name.

exception smarter.apps.plugin.manifest.models.skill_plugin.document.SkillDocumentError(message='')[source]

Bases: SmarterValueError

Raised when a SKILL.md document or a bundled file path is invalid.

smarter.apps.plugin.manifest.models.skill_plugin.document.find_resource_references(body)[source]

Find the bundled files that a SKILL.md body refers to.

Recognizes relative Markdown links, e.g. [guide](./reference/guide.md), and inline code that looks like a path, e.g. `scripts/fill_form.py`. URLs, absolute paths, anchors and paths that escape the skill root are ignored.

Parameters:

body (str) – The Markdown body of a SKILL.md document.

Return type:

list[str]

Returns:

The normalized paths, without duplicates, in order of first reference.

smarter.apps.plugin.manifest.models.skill_plugin.document.normalize_line_endings(text)[source]

Normalize line endings to \n and remove a leading byte order mark.

Return type:

str

smarter.apps.plugin.manifest.models.skill_plugin.document.normalize_resource_path(path)[source]

Normalize the path of a bundled file, relative to the skill root.

Parameters:

path (str) – A relative path, e.g. ./references/FORMS.md.

Return type:

str

Returns:

The normalized path, e.g. references/FORMS.md.

Raises:

SkillDocumentError – If the path is empty, absolute, a URL, contains a backslash or control characters, or escapes the skill root.

smarter.apps.plugin.manifest.models.skill_plugin.document.normalize_resources(resources)[source]

Validate and normalize a mapping of bundled file paths to their contents.

A None value denotes a file whose contents are unavailable, such as a binary asset.

Parameters:

resources (dict[str, str | None] | None) – The bundled files, keyed by path relative to the skill root.

Return type:

dict[str, str | None]

Returns:

The bundled files, keyed by normalized path, in sorted order.

Raises:

SkillDocumentError – If a path is invalid or duplicated after normalization, a file is SKILL.md itself, a file is too long, or there are too many files.

smarter.apps.plugin.manifest.models.skill_plugin.document.parse_allowed_tools(value)[source]

Normalize the allowed-tools frontmatter field to a list of tool names.

The specification defines allowed-tools as a space-separated string, e.g. Bash(git:*) Bash(jq:*) Read. Tool patterns may themselves contain spaces inside parentheses, e.g. Bash(git status:*). Comma-separated strings and YAML lists, both common in practice, are accepted too.

Parameters:

value (Any) – The raw frontmatter value.

Return type:

list[str]

Returns:

The tool names, in order.

Raises:

SkillDocumentError – If the value is neither a string nor a list of strings.

Example:

parse_allowed_tools("Bash(git status:*) Read, Grep")
# ['Bash(git status:*)', 'Read', 'Grep']
smarter.apps.plugin.manifest.models.skill_plugin.document.parse_skill_document(document)[source]

Parse and validate a SKILL.md document.

Parameters:

document (str) – The verbatim contents of a SKILL.md file.

Return type:

SkillDocument

Returns:

The parsed document.

Raises:

SkillDocumentError – If the document is too long, has no frontmatter block, the frontmatter is not a valid YAML mapping, or a standard frontmatter field is invalid.

Example:

skill = parse_skill_document(open("pdf/SKILL.md").read())
skill.name  # 'pdf'
smarter.apps.plugin.manifest.models.skill_plugin.document.validate_frontmatter(frontmatter)[source]

Validate the standard frontmatter fields against the Agent Skills specification.

Keys beyond the standard fields are permitted and preserved, since agent implementations such as Claude Code define their own extensions.

Parameters:

frontmatter (dict[str, Any]) – The parsed frontmatter.

Raises:

SkillDocumentError – If a standard field is missing or invalid.

Return type:

None

Resolution of the remote locations from which a SkillPlugin can source its skill.

A skill can be sourced from a public GitHub repository, such as Anthropic’s https://github.com/anthropics/skills, by referring to either the skill’s directory or its SKILL.md file. Any other https URL of a SKILL.md file is supported too.

# a skill directory in a GitHub repository
https://github.com/anthropics/skills/tree/main/skills/pdf

# a SKILL.md file in a GitHub repository
https://github.com/anthropics/skills/blob/main/skills/pdf/SKILL.md
https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf/SKILL.md

# a GitHub repository whose root is a skill. HEAD is its default branch.
https://github.com/example-org/my-skill

# any other SKILL.md file
https://example.com/skills/my-skill/SKILL.md

Note

GitHub branch names may contain slashes, which makes a URL such as /tree/feature/x/skills/pdf ambiguous. The first path segment after tree or blob is always taken to be the ref, so refer to such branches by commit SHA instead.

Note

Experimental. The SkillPlugin was designed and coded by Claude Code (Anthropic’s Claude Opus 5.5), with Lawrence McDaniel as co-author. It is experimental, and will be documented.

exception smarter.apps.plugin.manifest.models.skill_plugin.source.SkillSourceError(message='')[source]

Bases: SmarterValueError

Raised when a skill source URL is invalid, or a skill cannot be retrieved from it.

class smarter.apps.plugin.manifest.models.skill_plugin.source.SkillSourceLocation(url, skill_url, base_url, owner=None, repo=None, ref=None, path=None)[source]

Bases: object

The resolved location of a remote skill.

Variables:
  • url – The URL as written in the manifest.

  • skill_url – The URL from which to download SKILL.md.

  • base_url – The URL of the skill root directory, ending in /. Bundled files are resolved relative to it.

  • owner – The GitHub repository owner, for GitHub sources.

  • repo – The GitHub repository name, for GitHub sources.

  • ref – The GitHub branch, tag or commit, for GitHub sources.

  • path – The path of the skill root directory within the GitHub repository, for GitHub sources. Empty if the skill is the repository root.

__init__(url, skill_url, base_url, owner=None, repo=None, ref=None, path=None)
base_url: str
property is_github: bool

True if the skill is hosted in a GitHub repository.

owner: str | None = None
path: str | None = None
ref: str | None = None
repo: str | None = None
resource_url(path)[source]

Return the download URL of a bundled file, given its normalized path relative to the skill root.

Return type:

str

property skill_directory_name: str | None

The name of the skill root directory, which the specification requires to match the skill’s name.

skill_url: str
url: str
smarter.apps.plugin.manifest.models.skill_plugin.source.parse_source_url(url)[source]

Resolve a skill source URL to the location of its SKILL.md file and bundled files.

Parameters:

url (str) – A GitHub URL of a skill directory or SKILL.md file, or an https URL of a SKILL.md file.

Return type:

SkillSourceLocation

Returns:

The resolved location.

Raises:

SkillSourceError – If the URL is not a supported https URL.

Example:

location = parse_source_url("https://github.com/anthropics/skills/tree/main/skills/pdf")
location.skill_url
# 'https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf/SKILL.md'

Smarter API Manifest - SkillPlugin.spec.

A SkillPlugin’s spec.skillData contains an Agent Skill, either verbatim or by reference.

Verbatim: paste the unmodified contents of a SKILL.md file into skill, using a YAML literal block scalar (|) so that the Markdown is preserved exactly. Bundled files, which the skill refers to by relative path, can be pasted into resources.

skillData:
  skill: |
    ---
    name: meeting-notes
    description: Turns raw meeting transcripts into structured notes. Use when the user shares a meeting transcript.
    ---

    # Meeting notes
    Follow the template in [the template](assets/template.md).
  resources:
    assets/template.md: |
      ## Decisions
      ...

By reference: refer to a skill in a public GitHub repository, or to any https URL of a SKILL.md file. The skill and its bundled files are retrieved when the manifest is applied, and stored with the plugin. Apply the manifest again to retrieve the latest version.

skillData:
  source:
    url: https://github.com/anthropics/skills/tree/main/skills/pdf

Note

Experimental. The SkillPlugin was designed and coded by Claude Code (Anthropic’s Claude Opus 5.5), with Lawrence McDaniel as co-author. It is experimental, and will be documented.

class smarter.apps.plugin.manifest.models.skill_plugin.spec.SAMSkillPluginSpec(*, selector: SAMPluginCommonSpecSelector, prompt: SAMPluginCommonSpecPrompt, skillData: SkillData)[source]

Bases: SAMPluginCommonSpec

Smarter API Manifest - SkillPlugin.spec.

class_identifier: ClassVar[str] = 'SkillPlugin.spec'
model_config = {'arbitrary_types_allowed': True, 'from_attributes': True, 'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context, /)

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Parameters:
  • self (BaseModel) – The BaseModel instance.

  • context (Any) – The context.

Return type:

None

skillData: SkillData
class smarter.apps.plugin.manifest.models.skill_plugin.spec.SkillData(*, skill: str | None = None, source: SkillSource | None = None, resources: dict[str, str | None] | None = None)[source]

Bases: SmarterBasePydanticModel

Smarter API - SkillPlugin.spec.skillData: an Agent Skill.

Exactly one of skill or source is required.

class_identifier: ClassVar[str] = 'SkillPlugin.spec'
property document: SkillDocument | None

The parsed SKILL.md document of a verbatim skill, or None for a remote skill.

model_config = {'arbitrary_types_allowed': True, 'from_attributes': True, 'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context, /)

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Parameters:
  • self (BaseModel) – The BaseModel instance.

  • context (Any) – The context.

Return type:

None

resources: dict[str, str | None] | None
skill: str | None
source: SkillSource | None
validate_skill_data()[source]

Validate that exactly one of skill or source is provided, and that the skill and its resources are valid.

Return type:

SkillData

class smarter.apps.plugin.manifest.models.skill_plugin.spec.SkillSource(*, url: str)[source]

Bases: SmarterBasePydanticModel

Smarter API - SkillPlugin.spec.skillData.source: the remote location of a skill.

class_identifier: ClassVar[str] = 'SkillPlugin.spec.source'
property location: SkillSourceLocation

The resolved location of the skill.

model_config = {'arbitrary_types_allowed': True, 'from_attributes': True, 'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context, /)

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Parameters:
  • self (BaseModel) – The BaseModel instance.

  • context (Any) – The context.

Return type:

None

url: str
classmethod validate_url(v)[source]

Validate that the url refers to a supported skill location.

Return type:

str

Smarter API Plugin Manifest.

Note

Experimental. The SkillPlugin was designed and coded by Claude Code (Anthropic’s Claude Opus 5.5), with Lawrence McDaniel as co-author. It is experimental, and will be documented.

class smarter.apps.plugin.manifest.models.skill_plugin.model.SAMSkillPlugin(*, apiVersion: str, kind: str, metadata: SAMPluginCommonMetadata, spec: SAMSkillPluginSpec, status: SAMPluginCommonStatus | None = None)[source]

Bases: SAMPluginCommon

Smarter API Manifest - Skill Connection Model.

class_identifier: ClassVar[str] = 'SkillPlugin'
model_config = {'arbitrary_types_allowed': True, 'from_attributes': True, 'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context, /)

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Parameters:
  • self (BaseModel) – The BaseModel instance.

  • context (Any) – The context.

Return type:

None

spec: SAMSkillPluginSpec