# How Claude Code Skill Metadata Fields Are Parsed and Used: A Complete Technical Guide

> Learn how Claude Code skill metadata fields are parsed from YAML frontmatter and used for validation and documentation rendering. A complete technical guide.

- Repository: [Jeffallan/claude-skills](https://github.com/jeffallan/claude-skills)
- Tags: deep-dive
- Published: 2026-02-16

---

**Claude Code skill metadata fields are extracted from YAML frontmatter in [`SKILL.md`](https://github.com/Jeffallan/claude-skills/blob/main/SKILL.md) files, parsed into Python dictionaries via `BaseChecker._extract_frontmatter`, validated against required schemas using checker classes, and consumed by both the validation scripts and the Astro documentation site to enforce constraints and render skill cards.**

The `Jeffallan/claude-skills` repository manages Claude Code capabilities through structured markdown files. Each skill's metadata—including triggers, role definitions, and output formats—lives in a standardized YAML block that powers both automated validation and documentation generation. Understanding how these fields are parsed and used is essential for contributing new skills or extending the validation framework.

## Where Skill Metadata Lives

Every skill resides in its own directory under `skills/<skill-name>/`, containing a mandatory [`SKILL.md`](https://github.com/Jeffallan/claude-skills/blob/main/SKILL.md) file. This file must begin with a YAML frontmatter block delimited by triple dashes:

```yaml
---
metadata:
  triggers: ["react", "component"]
  role: "frontend expert"
  scope: "file"
  output-format: "typescript"
  domain: "web development"
  related-skills: ["typescript-basics", "testing-react"]
---

```

The `metadata` map is the central data structure that subsequent parsing and validation stages consume.

## Extracting and Parsing Metadata

The [`scripts/validate-skills.py`](https://github.com/Jeffallan/claude-skills/blob/main/scripts/validate-skills.py) file contains the core extraction logic in the `BaseChecker` class. The process follows a strict pipeline from raw markdown to structured data.

### Locating the SKILL.md File

The validator first resolves the skill path and locates the markdown file:

```python
skill_md = skill_path / "SKILL.md"

```

This logic resides in `BaseChecker._extract_frontmatter` at line 44 of [`validate-skills.py`](https://github.com/Jeffallan/claude-skills/blob/main/validate-skills.py). If the file is missing, the checker immediately flags a validation error.

### Splitting Frontmatter from Body

Once loaded, the raw content undergoes delimiter checking and splitting:

```python
if not content.startswith("---"):
    # Error: Missing frontmatter delimiter

    return None

parts = content.split("---", 2)

# parts[0] = empty prefix

# parts[1] = YAML content

# parts[2] = Markdown body

```

This three-way split preserves the YAML metadata separately from the instructional content that follows.

### YAML Parsing Implementation

The extracted YAML string passes through a compatibility layer that prefers PyYAML but falls back to a custom parser:

```python
def parse_yaml(yaml_str: str) -> dict:
    if HAS_PYYAML:
        return yaml.safe_load(yaml_str) or {}
    return simple_yaml_parse(yaml_str)

```

This function, defined around line 24 in [`validate-skills.py`](https://github.com/Jeffallan/claude-skills/blob/main/validate-skills.py), ensures the metadata block converts to a Python dictionary regardless of environment dependencies.

The final extraction wraps everything in a dataclass:

```python
return FrontmatterResult(frontmatter, parts[2], skill_md)

```

Where `frontmatter` contains the parsed metadata dictionary ready for validation.

## Validating Metadata Fields

After parsing, specialized checker classes validate the metadata dictionary against the schema defined in [`CLAUDE.md`](https://github.com/Jeffallan/claude-skills/blob/main/CLAUDE.md). Each checker inherits from `BaseChecker` and targets specific constraints.

### Required Field Validation

The `MetadataFieldsChecker` ensures the metadata map contains all mandatory fields:

```python
REQUIRED_METADATA_FIELDS = [
    "triggers",
    "role", 
    "scope",
    "output-format",
    "domain",
    "related-skills"
]

def check(self, result: FrontmatterResult) -> List[ValidationIssue]:
    metadata = result.frontmatter.get("metadata", {})
    for field in REQUIRED_METADATA_FIELDS:
        if field not in metadata:
            yield ValidationIssue(
                file=result.path,
                message=f"Missing required metadata field: {field}"
            )

```

This validation occurs in `MetadataFieldsChecker.check` and prevents incomplete skill definitions from passing CI.

### Enum Constraint Enforcement

Two specialized checkers validate that certain fields contain only allowed values:

- **`ScopeEnumChecker`**: Validates `metadata.scope` against `VALID_SCOPES`
- **`OutputFormatEnumChecker`**: Validates `metadata.output-format` against `VALID_OUTPUT_FORMATS`

Both inherit from `MetadataEnumChecker`, which reads the allowed values from constants defined around line 150 in [`validate-skills.py`](https://github.com/Jeffallan/claude-skills/blob/main/validate-skills.py). If a skill declares `scope: invalid-value`, the checker emits a validation error before the documentation site builds.

### Cross-Skill Reference Checking

The `CrossRefChecker` uses the `related-skills` field to build a dependency graph:

```python
def _check_orphans(self, all_skills: Dict[str, FrontmatterResult]):
    for skill_name, result in all_skills.items():
        related = result.frontmatter.get("metadata", {}).get("related-skills", [])
        for ref in related:
            if ref not in all_skills:
                yield ValidationIssue(
                    file=result.path,
                    message=f"Related skill '{ref}' does not exist"
                )

```

This ensures that `metadata.related-skills` only references valid, existing skills within the repository.

## Consuming Metadata in the Documentation Site

While Python scripts handle validation, the Astro documentation site consumes the same metadata at build time. The configuration in [`site/src/content.config.ts`](https://github.com/Jeffallan/claude-skills/blob/main/site/src/content.config.ts) defines the skills collection:

```typescript
import { defineCollection, z } from 'astro:content';

const skills = defineCollection({
  schema: z.object({
    metadata: z.object({
      triggers: z.array(z.string()),
      role: z.string(),
      scope: z.enum(['file', 'project', 'global']),
      'output-format': z.string(),
      domain: z.string(),
      'related-skills': z.array(z.string())
    }).optional()
  })
});

```

Astro automatically parses the YAML frontmatter when loading `skills/*/SKILL.md` files. The TypeScript definitions in [`.astro/content.d.ts`](https://github.com/Jeffallan/claude-skills/blob/main/.astro/content.d.ts) expose this metadata to components:

```typescript
export interface RenderedContent {
  html: string;
  metadata?: {
    imagePaths: Array<string>;
    [key: string]: unknown;
  };
}

```

This enables the site to render skill cards showing the domain badge, filter search results by `scope` or `output-format`, and generate navigation links based on `related-skills`.

## Practical Implementation Examples

### Running the Validator for a Single Skill

Validate a specific skill from the command line:

```bash
python -m scripts.validate-skills --skill react-expert

```

This executes `BaseChecker._extract_frontmatter` to load [`skills/react-expert/SKILL.md`](https://github.com/Jeffallan/claude-skills/blob/main/skills/react-expert/SKILL.md), then runs `MetadataFieldsChecker`, `ScopeEnumChecker`, and `CrossRefChecker` to ensure compliance with the schema defined in [`CLAUDE.md`](https://github.com/Jeffallan/claude-skills/blob/main/CLAUDE.md).

### Programmatically Reading Skill Metadata

Access parsed metadata in Python scripts:

```python
from pathlib import Path
from scripts.validate_skills import BaseChecker

skill_path = Path("skills/react-expert")
result = BaseChecker._extract_frontmatter(skill_path)

if result:
    metadata = result.frontmatter.get("metadata", {})
    print("Domain:", metadata.get("domain"))
    print("Triggers:", metadata.get("triggers"))
    print("Related Skills:", metadata.get("related-skills"))

```

The `BaseChecker._extract_frontmatter` method is static and reusable, returning a `FrontmatterResult` dataclass containing the parsed dictionary, markdown body, and file path.

### Accessing Metadata in Astro Components

Consume metadata in the documentation frontend:

```tsx
---
import { getEntry } from 'astro:content';

const skill = await getEntry({ collection: 'skills', slug: 'react-expert' });
const { metadata } = skill?.data ?? {};
---

<h2>{skill?.title}</h2>

{metadata?.domain && (
  <span class="badge">{metadata.domain}</span>
)}

{metadata?.triggers && (
  <ul>
    {metadata.triggers.split(", ").map(trigger => (
      <li>{trigger}</li>
    ))}
  </ul>
)}

{metadata?.['related-skills'] && (
  <nav>
    <h3>Related Skills</h3>
    {metadata['related-skills'].map(related => (
      <a href={`/skills/${related}`}>{related}</a>
    ))}
  </nav>
)}

```

This TypeScript/Astro template renders the same `metadata` dictionary that the Python validator processes, ensuring consistency between validation and presentation layers.

## Summary

- **Skill metadata resides in YAML frontmatter** within `skills/<skill-name>/SKILL.md` files, nested under a top-level `metadata` key containing fields like `triggers`, `role`, `scope`, `output-format`, `domain`, and `related-skills`.
- **Extraction uses `BaseChecker._extract_frontmatter`** in [`scripts/validate-skills.py`](https://github.com/Jeffallan/claude-skills/blob/main/scripts/validate-skills.py), which splits the markdown on `---` delimiters and parses the YAML content into a Python dictionary using `parse_yaml` (preferring PyYAML with a fallback to a custom parser).
- **Validation enforces schema compliance** through specialized checkers: `MetadataFieldsChecker` ensures required fields exist, `ScopeEnumChecker` and `OutputFormatEnumChecker` validate against allowed values, and `CrossRefChecker` verifies that `related-skills` references point to existing skills.
- **Documentation consumption happens via Astro**, which reads the same YAML frontmatter through [`site/src/content.config.ts`](https://github.com/Jeffallan/claude-skills/blob/main/site/src/content.config.ts) and exposes the metadata to TypeScript components for rendering skill cards, search filters, and navigation links.

## Frequently Asked Questions

### What happens if a SKILL.md file is missing the metadata block?

The `MetadataFieldsChecker` emits a `ValidationIssue` error indicating that the `metadata` key is missing or incomplete. If the file lacks the `---` delimiter entirely, `BaseChecker._extract_frontmatter` returns `None`, causing the validation pipeline to flag the file as having no parseable frontmatter.

### Can I use custom fields in the metadata section?

While the `MetadataFieldsChecker` enforces a specific set of `REQUIRED_METADATA_FIELDS`, the YAML parser itself preserves all key-value pairs in the frontmatter dictionary. The Astro documentation site receives the complete metadata object, so additional fields can be accessed in TypeScript components, though they won't be validated by the Python checkers unless explicitly added to the schema in [`CLAUDE.md`](https://github.com/Jeffallan/claude-skills/blob/main/CLAUDE.md).

### How does the validator handle YAML syntax errors?

The `parse_yaml` function in [`scripts/validate-skills.py`](https://github.com/Jeffallan/claude-skills/blob/main/scripts/validate-skills.py) attempts to use `yaml.safe_load` from PyYAML first. If PyYAML is unavailable, it falls back to `simple_yaml_parse`, a minimal custom parser. Syntax errors in the YAML block cause the parser to raise an exception, which the validation pipeline catches and reports as a `ValidationIssue` pointing to the specific file and error description.

### What is the relationship between metadata validation and the Astro documentation site?

The Python validation scripts and the Astro site operate independently but consume the same source data. The Python checkers ensure that `metadata` fields conform to the schema before content is committed, while Astro's content collection (defined in [`site/src/content.config.ts`](https://github.com/Jeffallan/claude-skills/blob/main/site/src/content.config.ts)) reads the valid YAML at build time to generate static pages. This separation ensures that only schema-compliant skills appear in the documentation, with the Python layer acting as a gatekeeper for the frontend.