Skip to main content

Skills Architecture

How MeetLoyd implements the Anthropic Agent Skills specification — from file-based skill packages to runtime agent queries.

Overview

The skills system has two layers:

┌─────────────────────────────────────────────────────────┐
│ Management Layer (Database) │
│ src/routers/skills.ts │
│ ┌─────────┐ ┌────────────────┐ ┌──────────────────┐ │
│ │ Skills │ │ Agent-Skill │ │ Store Listings │ │
│ │ CRUD │ │ Assignments │ │ & Purchases │ │
│ └─────────┘ └────────────────┘ └──────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ Content Layer (Filesystem) │
│ src/skills/ (parser, loader, registry) │
│ ┌─────────┐ ┌────────────────┐ ┌──────────────────┐ │
│ │ SKILL.md│ │ references/ │ │ scripts/ │ │
│ │ Parser │ │ Loader │ │ assets/ │ │
│ └─────────┘ └────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────┘
▲ │
│ skills/ directory │
└────────────────────────────────────┘
  • Content Layer (src/skills/): Parses, validates, and loads SKILL.md files following the Anthropic spec. Handles progressive disclosure.
  • Management Layer (src/routers/skills.ts): Database-backed CRUD for multi-tenant skill ownership, agent assignments, store purchases.

Progressive Disclosure

The core design principle: load only what's needed, when it's needed.

                    ┌──────────────────────────────┐
Level 1 (~100 tok) │ name: ticket-triage │ ← Loaded at startup
System Prompt │ description: ITIL-based... │ for ALL skills
└──────────────────────────────┘

Agent encounters support ticket


┌──────────────────────────────┐
Level 2 (<5k tok) │ # Support Ticket Triage │ ← Loaded on activation
skill_query │ ## Decision Tree │ via skill_query tool
│ ## Priority Definitions │
│ ## Response Templates │
└──────────────────────────────┘

Agent needs escalation details


┌──────────────────────────────┐
Level 3 (variable) │ references/ESCALATION.md │ ← Loaded on demand
skill_query +ref │ scripts/classify-ticket.py │ specific file only
└──────────────────────────────┘

Why Progressive Disclosure?

ApproachTokens UsedAgent Quality
Dump everything in system prompt50,000+Diluted attention
Only names in prompt, load on query~100 + on-demandFocused, efficient

With 80+ skills, dumping all instructions into the system prompt would consume 200k+ tokens and severely degrade agent performance. Progressive disclosure keeps the system prompt lean while giving agents full access to deep knowledge when needed.

Component Architecture

Parser (src/skills/parser.ts)

Handles SKILL.md file parsing and validation.

SKILL.md content


┌─────────────────┐ ┌──────────────────┐
│ extractFrontmatter│ ──→ │ YAML parse │
│ (--- delimiters) │ │ (name, desc, ...) │
└─────────────────┘ └──────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ Markdown body │ │ validateSkill() │
│ (instructions) │ │ - name format │
└─────────────────┘ │ - description len │
│ - dir name match │
│ - instruction len │
└──────────────────┘

Validation rules (per Anthropic spec):

  • name: 1-64 chars, lowercase a-z0-9-, no consecutive hyphens, matches directory
  • description: 1-1024 chars, non-empty
  • compatibility: max 500 chars if present
  • Instructions: warning if > 500 lines (recommends splitting to references)

Key functions:

  • parseSkillMd(content){ frontmatter, instructions, raw }
  • validateSkill(parsed, directoryName){ valid, errors, warnings }
  • extractReferenceLinks(markdown)string[] (finds references/ links)
  • extractScriptReferences(markdown)string[] (finds scripts/ links)

Loader (src/skills/loader.ts)

Implements the progressive disclosure loading strategy.

┌─────────────────────────────────────────────────┐
│ loadSkillSummary(path) [Level 1] │
│ Returns: { id, name, description, category } │
│ Cost: ~100 tokens │
├─────────────────────────────────────────────────┤
│ loadSkill(path) [Level 2] │
│ Returns: { ...summary, instructions, │
│ references[], scripts[], assets[] } │
│ Note: references/scripts discovered but NOT │
│ loaded (loaded: false) │
│ Cost: <5,000 tokens (instructions only) │
├─────────────────────────────────────────────────┤
│ loadReference(skill, filename) [Level 3] │
│ loadScript(skill, filename) │
│ Returns: file content string │
│ Cost: varies per file │
└─────────────────────────────────────────────────┘

Batch loading:

  • loadSkillsFromDirectory(dir)Map<string, Skill> — loads all skills (Level 2)
  • loadSkillSummariesFromDirectory(dir)SkillSummary[] — summaries only (Level 1)
  • loadReferences(skill, filenames) — batch load multiple references

Registry (src/skills/registry.ts)

In-memory index of all loaded skills with lookup and search.

┌──────────────────────────────────────────────────┐
│ SkillRegistry (singleton) │
│ │
│ skills: Map<string, Skill> ← Full skill data │
│ summaries: SkillSummary[] ← Quick lookup │
│ lastRefresh: Date ← Cache control │
├──────────────────────────────────────────────────┤
│ Lookup │
│ getSkill(id) → Skill │
│ getSkillSummary(id) → SkillSummary │
│ getAllSkillSummaries() → SkillSummary[] │
│ searchSkills(query) → SkillSummary[] │
│ getSkillsByCategory(cat) → SkillSummary[] │
├──────────────────────────────────────────────────┤
│ Agent Assignments (in-memory) │
│ assignSkillToAgent(agentId, skillId) │
│ unassignSkillFromAgent(agentId, skillId) │
│ getAgentSkills(agentId) → Skill[] │
│ getAgentSkillSummaries(agentId) → Summary[] │
├──────────────────────────────────────────────────┤
│ System Prompt Generation │
│ generateSkillsPromptSection(agentId) → markdown │
│ generateSkillsXML(agentId) → XML │
└──────────────────────────────────────────────────┘

Initialization flow:

Server startup


initializeRegistry()


loadSkillsFromDirectory('skills/')

├── skills/terraform/SKILL.md → parse → validate → Skill
├── skills/ticket-triage/SKILL.md → parse → validate → Skill
├── skills/sales-objection-handling/SKILL.md → ...
└── ... (80+ skills)


Registry ready (Map<id, Skill> + SkillSummary[])

Data Flow: Agent Uses a Skill

End-to-end flow when an agent encounters a task that matches a skill:

1. Agent starts conversation


2. System prompt includes skill summaries (Level 1)
"Available Skills:
- terraform: Expert Terraform/OpenTofu IaC...
- ticket-triage: ITIL-based ticket triage..."


3. User asks: "How should I triage this production outage?"


4. Agent recognizes skill match → calls skill_query tool
{ skillId: "ticket-triage", query: "production outage triage" }


5. MCP tool handler:
a. getSkill("ticket-triage") from registry
b. buildSkillQueryResult(skill) → Level 2 instructions
c. Return: instructions + available references list


6. Agent reads instructions, applies decision tree
"Is system completely down? → YES → P1 Critical"


7. Agent needs escalation details → calls skill_query again
{ skillId: "ticket-triage", referenceFile: "ESCALATION-MATRIX.md" }


8. loadReference(skill, "ESCALATION-MATRIX.md") → Level 3


9. Agent applies escalation rules, responds to user

System Prompt Integration

When an agent has skills assigned, the registry generates a skills section for the system prompt.

Markdown format (generateSkillsPromptSection):

## Available Skills

You have access to the following skills. Use the `skill_query` tool to load instructions when needed.

- **terraform**: Expert Terraform/OpenTofu Infrastructure as Code...
- **ticket-triage**: Triage and categorize support tickets using ITIL-based framework...
- **sales-objection-handling**: Handle B2B SaaS sales objections with proven frameworks...

XML format (generateSkillsXML):

<available_skills>
<skill name="terraform">
<description>Expert Terraform/OpenTofu Infrastructure as Code...</description>
<category>engineering</category>
<version>1.0.0</version>
</skill>
...
</available_skills>

Database Layer

The management layer stores skill ownership and assignments in PostgreSQL:

skills Table

ColumnTypeDescription
idUUIDPrimary key
tenant_idUUIDFK → tenants
nameVARCHARSkill name
slugVARCHARURL-safe identifier
descriptionTEXTSkill description
categoryVARCHARCategory tag
tool_idsJSONBAssociated MCP tool IDs
configJSONBSkill configuration
listing_refUUIDFK → store_listings (if purchased)
listing_versionVARCHARStore listing version
lockedBOOLEANPrevent edits (store skills)
current_versionVARCHARCurrent version string
statusVARCHARactive / inactive / archived

agent_skill_assignments Table

ColumnTypeDescription
idUUIDPrimary key
tenant_idUUIDFK → tenants
skill_idUUIDFK → skills
agent_idUUIDFK → agents
skill_versionVARCHARVersion at time of assignment
listing_refUUIDStore listing reference
enabledBOOLEANToggle on/off without removing
configJSONBPer-assignment configuration
priorityINTEGERHigher = more likely to use
assigned_atTIMESTAMPWhen assigned
assigned_byUUIDFK → users

Vertical Skills

Verticals (like MFO) can bundle their own skills that reference vertical-specific MCP tools:

mfo/skills/
├── tax-optimization/SKILL.md → references mfo_income_tax_calculate, mfo_ifi_calculate
├── bspce-strategy/SKILL.md → references mfo_bspce_calculate, mfo_bspce_simulate
├── patrimony-review/SKILL.md → references getProfile, listAssets
├── real-estate-timing/SKILL.md → references mfo_cdhr_calculate
└── succession-planning/SKILL.md → references mfo_av_compare_options

Vertical skills are loaded alongside platform skills but scoped to tenants using the vertical.

Key Files

FilePurpose
src/skills/types.tsType definitions (SkillFrontmatter, Skill, SkillSummary, etc.)
src/skills/parser.tsSKILL.md parsing and validation
src/skills/loader.tsProgressive disclosure loading
src/skills/registry.tsIn-memory skill index, search, assignments
src/skills/index.tsPublic API re-exports
src/routers/skills.tsREST API for skill CRUD and assignments
skills/80+ first-party SKILL.md files

Spec Compliance

MeetLoyd implements the full Agent Skills specification:

Spec RequirementImplementation
SKILL.md with YAML frontmatterparser.tsparseSkillMd(), extractFrontmatter()
Name validation (lowercase, hyphens, matches dir)parser.tsSKILL_NAME_PATTERN, validateSkill()
Description (max 1024 chars)types.tsSKILL_DESCRIPTION_MAX_LENGTH
Optional references/, scripts/, assets/loader.tsdiscoverReferences(), discoverScripts(), discoverAssets()
Progressive disclosure (3 levels)loader.tsloadSkillSummary(), loadSkill(), loadReference()
Under 500 lines recommendationtypes.tsSKILL_INSTRUCTIONS_MAX_LINES, warning in validation

Next: See the Skills API Reference for endpoint documentation, or the Creating Skills Guide for a step-by-step tutorial.