---
title: "@Skill"
description: "The @Skill decorator defines a knowledge package that bundles instructions and tool references for specific workflows."
icon: "lightbulb"
keywords: ['frontmcp', 'mcp', 'model context protocol', 'sdk', 'reference', 'api', 'decorators', 'typescript', 'skill']
---

## Basic Usage

```typescript
import { Skill, SkillContext } from '@frontmcp/sdk';

@Skill({
  name: 'code-review',
  description: 'Complete code review workflow',
  instructions: `
# Code Review Workflow

1. Fetch the pull request details
2. Review the code changes
3. Check for common issues
4. Post review comments
  `,
  tools: ['github_get_pr', 'github_post_comment'],
})
class CodeReviewSkill extends SkillContext {}
```

<Info>
The class body is optional. When you use `@Skill`, the runtime loads
`instructions` (and `resources`) from the decorator metadata via
`SkillInstance`, so `loadInstructions()` and `build()` on your class are
never invoked — you only need to override them if you construct a
`SkillContext` subclass manually outside the decorator pipeline. See
[Advanced: overriding loadInstructions / build](#advanced-overriding-loadinstructions--build).
</Info>

### Advanced: overriding loadInstructions / build

You only need to override these methods when you bypass the `@Skill`
decorator and want to drive loading yourself. Otherwise, leave the body
empty — the runtime does the work for you.

```typescript
class CodeReviewSkill extends SkillContext {
  override async loadInstructions() {
    return this.metadata.instructions as string;
  }

  override async build() {
    return {
      id: this.skillId,
      name: this.metadata.name,
      description: this.metadata.description,
      instructions: await this.loadInstructions(),
      tools: this.getToolRefs(),
    };
  }
}
```

## Signature

```typescript
function Skill(providedMetadata: SkillMetadata): ClassDecorator
```

## Configuration Options

### Required Properties

| Property       | Type                                            | Constraints                     | Description             |
| -------------- | ----------------------------------------------- | ------------------------------- | ----------------------- |
| `name`         | `string`                                        | kebab-case, 1-64 chars, no `--` | Unique skill identifier |
| `description`  | `string`                                        | 1-1024 chars, non-empty         | Skill description       |
| `instructions` | `string \| { file: string } \| { url: string }` |                                 | Skill instructions      |

### Optional Properties

| Property            | Type                             | Description                                        |
| ------------------- | -------------------------------- | -------------------------------------------------- |
| `tools`             | `SkillToolInput[]`               | Tools used by the skill                            |
| `id`                | `string`                         | Stable identifier (defaults to `name`)             |
| `parameters`        | `SkillParameter[]`               | Input parameters                                   |
| `examples`          | `SkillExample[]`                 | Usage examples                                     |
| `tags`              | `string[]`                       | Categorization tags                                |
| `priority`          | `number`                         | Search ranking weight (default: `0`)               |
| `hideFromDiscovery` | `boolean`                        | Hide from search results (default: `false`)        |
| `visibility`        | `'mcp' \| 'http' \| 'both'`      | Discovery visibility (default: `'both'`)           |
| `toolValidation`    | `'strict' \| 'warn' \| 'ignore'` | Tool reference validation mode (default: `'warn'`) |

### Agent Skills Spec Properties

These properties align with the [Anthropic Agent Skills specification](https://agentskills.io/specification):

| Property        | Type                     | Description                                                       |
| --------------- | ------------------------ | ----------------------------------------------------------------- |
| `license`       | `string`                 | License name or reference (e.g. `'MIT'`, `'Apache-2.0'`)          |
| `compatibility` | `string`                 | Environment requirements (max 500 chars)                          |
| `specMetadata`  | `Record<string, string>` | Arbitrary key-value metadata (maps to spec `metadata`)            |
| `allowedTools`  | `string`                 | Space-delimited pre-approved tools (maps to spec `allowed-tools`) |
| `resources`     | `SkillResources`         | Bundled resource directories (`scripts`, `references`, `examples`, `assets`)  |

## Instruction Sources

### Inline String

```typescript
@Skill({
  name: 'my-skill',
  description: 'My skill',
  instructions: '# Instructions\n\n1. Step one\n2. Step two',
  tools: [],
})
```

### File Reference

```typescript
@Skill({
  name: 'my-skill',
  description: 'My skill',
  instructions: { file: './skills/my-skill.md' },
  tools: [],
})
```

<Info>
**File path resolution.** Relative paths in `instructions: { file: '…' }`,
`resources.references`, and `resources.examples` resolve against the
directory of the source file that declared the `@Skill` class — **not**
`process.cwd()`. The directory is captured at decoration time by walking
the call stack, and the same rule applies to the inline `skill()` helper.
For bundled CLIs the framework additionally falls back to the build-time
`_skills/manifest.json` so paths keep resolving after `frontmcp build`.
</Info>

### URL Reference

```typescript
@Skill({
  name: 'my-skill',
  description: 'My skill',
  instructions: { url: 'https://example.com/skills/my-skill.md' },
  tools: [],
})
```

## Tool References

### By Name

```typescript
tools: ['tool_name', 'another_tool']
```

### By Class

```typescript
tools: [MyToolClass, AnotherToolClass]
```

### With Purpose

```typescript
tools: [
  { name: 'github_get_pr', purpose: 'Fetch PR details', required: true },
  { name: 'github_comment', purpose: 'Post review feedback', required: false },
]
```

### With Class and Purpose

```typescript
tools: [
  { class: GitHubGetPRTool, purpose: 'Fetch PR details' },
]
```

## Parameters

Define input parameters for skills:

```typescript
@Skill({
  name: 'deploy',
  description: 'Deploy application',
  instructions: '...',
  tools: ['deploy_app'],
  parameters: [
    {
      name: 'environment',
      description: 'Target environment',
      required: true,
      type: 'string',
    },
    {
      name: 'version',
      description: 'Version to deploy',
      required: false,
      type: 'string',
      default: 'latest',
    },
  ],
})
```

## Examples

Provide usage examples:

```typescript
@Skill({
  name: 'data-analysis',
  description: 'Analyze data sets',
  instructions: '...',
  tools: ['query_database', 'generate_chart'],
  examples: [
    {
      scenario: 'Analyze monthly sales',
      parameters: { table: 'sales', period: 'monthly' },
      expectedOutcome: 'Sales trends chart and summary report',
    },
  ],
})
```

## Function-Based Alternative

```typescript
import { skill } from '@frontmcp/sdk';

const codeReviewSkill = skill({
  name: 'code-review',
  description: 'Code review workflow',
  instructions: { file: './code-review.md' },
  tools: ['github_get_pr', 'github_comment'],
  tags: ['github', 'review'],
});
```

## Skill Content Output

Skills build to `SkillContent`:

```typescript
interface SkillContent {
  id: string;
  name: string;
  description: string;
  instructions: string;
  tools: Array<{
    name: string;
    purpose?: string;
    required?: boolean;
  }>;
  parameters?: SkillParameter[];
  examples?: SkillExample[];

  // Agent Skills spec fields
  license?: string;
  compatibility?: string;
  specMetadata?: Record<string, string>;
  allowedTools?: string;
  resources?: SkillResources;
}

interface SkillResources {
  scripts?: string;
  references?: string;
  examples?: string;
  assets?: string;
}
```

## Skill Validation

Skills are validated on server startup. Validation strictness is configured **per skill** via the `toolValidation` field on `@Skill`:

```typescript
@Skill({
  name: 'review-pr',
  description: 'Review a pull request',
  tools: ['list_files', 'read_file', 'comment_on_pr'],
  toolValidation: 'strict', // 'strict' | 'warn' (default) | 'ignore'
})
class ReviewPrSkill extends SkillContext {}
```

- **strict**: Fail if skill references missing tools
- **warn** (default): Log warnings but continue
- **ignore**: Skip tool validation

## Skill Sessions

Skills can be loaded into sessions for focused tool access:

```typescript
// Client-side
const client = await connect(config);

// Search for skills
const results = await client.searchSkills('code review');

// Load skill into session
await client.loadSkills(['code-review'], {
  activateSession: true,
  policyMode: 'strict', // Only allow skill's tools
});

// Now tool calls are restricted to skill's tool allowlist
```

## Full Example

```typescript
import { Skill, SkillContext, Tool, ToolContext, App, FrontMcp } from '@frontmcp/sdk';
import { z } from '@frontmcp/sdk';

// Tools
@Tool({
  name: 'jira_get_issue',
  inputSchema: { issueKey: z.string() },
})
class JiraGetIssueTool extends ToolContext {
  async execute(input) {
    return { key: input.issueKey, summary: 'Issue summary' };
  }
}

@Tool({
  name: 'jira_update_status',
  inputSchema: { issueKey: z.string(), status: z.string() },
})
class JiraUpdateStatusTool extends ToolContext {
  async execute(input) {
    return { success: true };
  }
}

@Tool({
  name: 'slack_notify',
  inputSchema: { channel: z.string(), message: z.string() },
})
class SlackNotifyTool extends ToolContext {
  async execute(input) {
    return { sent: true };
  }
}

// Skill
@Skill({
  name: 'sprint-planning',
  description: 'Assist with Agile sprint planning',
  instructions: `
# Sprint Planning Workflow

## Overview
Help the team plan their next sprint by reviewing backlog items and updating statuses.

## Steps

1. **Review Backlog**
   - Use \`jira_get_issue\` to fetch backlog items
   - Analyze priority and estimates

2. **Assign to Sprint**
   - Use \`jira_update_status\` to move items to sprint
   - Verify capacity constraints

3. **Notify Team**
   - Use \`slack_notify\` to inform the team
   - Share sprint goals and assignments

## Best Practices
- Keep sprint scope realistic
- Balance workload across team members
- Leave buffer for unexpected work
  `,
  tools: [
    { name: 'jira_get_issue', purpose: 'Fetch issue details from backlog', required: true },
    { name: 'jira_update_status', purpose: 'Move issues to sprint', required: true },
    { name: 'slack_notify', purpose: 'Notify team of sprint plan', required: false },
  ],
  parameters: [
    { name: 'sprintName', description: 'Name of the sprint', required: true, type: 'string' },
    { name: 'capacity', description: 'Team capacity in story points', required: false, type: 'number', default: 40 },
  ],
  examples: [
    {
      scenario: 'Plan a two-week sprint',
      parameters: { sprintName: 'Sprint 23', capacity: 40 },
      expectedOutcome: 'Sprint backlog populated with balanced workload',
    },
  ],
  tags: ['agile', 'jira', 'planning'],
  priority: 10,
})
class SprintPlanningSkill extends SkillContext {
  async loadInstructions() {
    return this.metadata.instructions as string;
  }

  async build() {
    return {
      id: this.skillId,
      name: this.metadata.name,
      description: this.metadata.description,
      instructions: await this.loadInstructions(),
      tools: this.getToolRefs().map(ref => ({
        name: ref.name,
        purpose: ref.purpose,
        required: ref.required !== false,
      })),
      parameters: this.metadata.parameters,
      examples: this.metadata.examples,
    };
  }
}

@App({
  name: 'project-management',
  tools: [JiraGetIssueTool, JiraUpdateStatusTool, SlackNotifyTool],
  skills: [SprintPlanningSkill],
})
class ProjectManagementApp {}

@FrontMcp({
  info: { name: 'PM Assistant', version: '1.0.0' },
  apps: [ProjectManagementApp],
  skillsConfig: {
    enabled: true,
  },
})
export default class PMAssistantServer {}
```

## SKILL.md Files

Load skills from SKILL.md files with YAML frontmatter (per Agent Skills spec):

```markdown
---
name: review-pr
description: Review a GitHub pull request
license: MIT
compatibility: Requires git CLI
tags:
  - github
  - code-review
metadata:
  author: platform-team
  version: "1.0"
allowed-tools: Read Edit Bash(git diff)
---
# PR Review Instructions

1. Fetch the PR details using github_get_pr
2. Review each changed file
3. Add review comments
4. Submit the review
```

### Loading SKILL.md Files

```typescript
import { loadSkillMdFile, parseSkillMdFrontmatter } from '@frontmcp/sdk';

// High-level: read + parse in one call
const metadata = await loadSkillMdFile('./skills/review-pr/SKILL.md');

// Low-level: parse content you already have
const { frontmatter, body } = parseSkillMdFrontmatter(rawContent);
```

## Skill Directories

Load entire skill directories with the `skillDir()` helper:

```typescript
import { skillDir } from '@frontmcp/sdk';

const reviewSkill = await skillDir('./skills/review-pr');
// Reads SKILL.md, auto-detects scripts/, references/, assets/

@App({
  name: 'my-app',
  skills: [reviewSkill],
})
class MyApp {}
```

Expected directory structure:

```
skills/review-pr/
  SKILL.md             # Required
  scripts/             # Optional: automation scripts
  references/          # Optional: reference documents
  assets/              # Optional: images, templates
```

## Agent Skills Spec Example

A fully spec-compliant skill:

```typescript
@Skill({
  name: 'security-audit',
  description: 'Perform a security audit on the codebase',
  instructions: { file: './skills/security-audit/SKILL.md' },
  tools: [
    { name: 'code_search', purpose: 'Search for vulnerability patterns', required: true },
    { name: 'file_read', purpose: 'Read source files', required: true },
    { name: 'create_report', purpose: 'Generate audit report' },
  ],
  tags: ['security', 'audit', 'compliance'],
  license: 'Apache-2.0',
  compatibility: 'Requires Node.js 24+ and access to source repository',
  specMetadata: {
    author: 'security-team',
    version: '3.0.0',
    category: 'security',
  },
  allowedTools: 'Read Grep Glob',
  resources: {
    scripts: './skills/security-audit/scripts',
    references: './skills/security-audit/references',
  },
})
class SecurityAuditSkill extends SkillContext {}
```

## Related

<CardGroup cols={2}>
  <Card title="SkillContext" icon="code" href="/frontmcp/sdk-reference/contexts/skill-context">
    Context class details
  </Card>
  <Card title="SkillRegistry" icon="database" href="/frontmcp/sdk-reference/registries/skill-registry">
    Skill registry API
  </Card>
  <Card title="@Tool" icon="wrench" href="/frontmcp/sdk-reference/decorators/tool">
    Define tools
  </Card>
  <Card title="Skills Overview" icon="lightbulb" href="/frontmcp/servers/skills">
    Skills documentation
  </Card>
</CardGroup>
