> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# SkillRegistry

> The SkillRegistry manages all MCP skills within a scope. Skills are reusable prompts/instructions that can be searched, loaded, and optionally synced to external storage.

## Overview

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { SkillRegistry } from '@frontmcp/sdk';

// Access via scope
const skills = scope.skills;

// List all skills
const allSkills = skills.getSkills();

// Search skills
const results = await skills.search('data analysis');

// Load a skill
const skill = await skills.loadSkill('data-pipeline');
```

## Methods

### getSkills()

Get all skills with optional filtering.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
getSkills(options?: GetSkillsOptions): ReadonlyArray<SkillEntry>
```

| Option | Type | Description |
| - | - | - |
| `includeHidden` | `boolean` | Include hidden skills |
| `visibility` | `'mcp' \| 'http' \| 'both'` | Filter by visibility |

**Example:**

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
// Get all visible skills
const skills = registry.getSkills();

// Get skills visible via HTTP
const httpSkills = registry.getSkills({ visibility: 'http' });
```

### search()

Search skills using TF-IDF text matching.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
search(query: string, options?: SearchOptions): Promise<SearchResult[]>
```

| Option | Type | Description |
| - | - | - |
| `limit` | `number` | Maximum results to return |
| `minScore` | `number` | Minimum relevance score |
| `visibility` | `'mcp' \| 'http' \| 'both'` | Filter by visibility |

**Example:**

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const results = await registry.search('data analysis', {
  limit: 10,
  minScore: 0.5,
});

for (const result of results) {
  console.log(`${result.skill.name}: ${result.score}`);
}
```

### loadSkill()

Load skill content by ID/name with tool validation.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
loadSkill(skillId: string): Promise<LoadedSkill>
```

**Example:**

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const skill = await registry.loadSkill('data-pipeline');
console.log(skill.content); // Full skill content
console.log(skill.tools);   // Validated tool references
```

### listSkills()

List skills with pagination support.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
listSkills(options?: ListOptions): Promise<SkillListResult>
```

| Option | Type | Description |
| - | - | - |
| `offset` | `number` | Starting index |
| `limit` | `number` | Maximum results |
| `visibility` | `'mcp' \| 'http' \| 'both'` | Filter by visibility |

**Example:**

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const page = await registry.listSkills({
  offset: 0,
  limit: 20,
});

console.log(`Total: ${page.total}`);
console.log(`Skills: ${page.items.length}`);
```

### count()

Get total skill count.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
count(options?: CountOptions): Promise<number>
```

### findByName()

Find a skill by its base name.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
findByName(name: string): SkillEntry | undefined
```

### findByQualifiedName()

Find a skill by its fully qualified name.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
findByQualifiedName(qualifiedName: string): SkillEntry | undefined
```

### getInlineSkills()

Get skills defined inline (local only).

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
getInlineSkills(): ReadonlyArray<SkillEntry>
```

### validateAllTools()

Validate all skills' tool references against the ToolRegistry.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
validateAllTools(): ValidationResult
```

**Example:**

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const result = registry.validateAllTools();
if (!result.valid) {
  for (const error of result.errors) {
    console.error(`${error.skill}: Missing tool ${error.tool}`);
  }
}
```

### subscribe()

Subscribe to skill change events.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
subscribe(
  opts: SubscribeOptions,
  cb: (event: SkillChangeEvent) => void
): () => void
```

**Returns:** Unsubscribe function.

### getCapabilities()

Get MCP capabilities for skills.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
getCapabilities(): { listChanged: boolean }
```

### hasAny()

Check if any skills exist.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
hasAny(): boolean
```

### adoptFromChild()

Adopt skills from a child registry.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
adoptFromChild(child: SkillRegistry, childOwner: Owner): void
```

When a child's skill is removed, the adopting registry stops serving it at once, including from its storage provider.

### registerSkillContent()

Register a skill at runtime from a fully resolved `SkillContent`. Registering again with the same `id` replaces the skill.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
registerSkillContent(content: SkillContent, opts?: RegisterSkillContentOptions): Promise<RegisteredSkillContent>
```

| Option | Type | Description |
| - | - | - |
| `source` | `string` | Where the skill came from, for logs |
| `supersedes` | `readonly string[]` | Ids of skills registered this way that the same change replaces or removes. The id/path collision check treats them as already gone (see below) |

`RegisteredSkillContent`:

| Field | Type | Description |
| - | - | - |
| `id` | `string` | The registered skill's id |
| `unregister` | `() => Promise<void>` | Removes this registration; a no-op once a later registration of the same id replaced it |
| `removed` | `readonly string[]` | Ids of superseded skills this registration removed, because the new skill collided with them |

A skill whose `id` is another skill's `skill://` path, or whose path is another skill's `id`, is refused with `INVALID_PARAMS` and nothing changes, since `skill://<id>/SKILL.md` would serve one for the other. The check covers every registry that serves this one's skills: the registries that adopt it, and their other children.

A change that registers its new skills before removing the old ones, such as a skill bundle sync, names the old ones in `supersedes`, so a new skill can take the name of one it drops or renames. A superseded skill the new one collides with is removed in the same step, so the two are never served together, and is listed in `removed`. The other superseded skills stay registered until the caller registers them again or unregisters them.

**Example:**

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const handle = await scope.skills.registerSkillContent(
  { id: 'reports', name: 'reports', description: 'Quarterly reports', instructions: '# Reports', tools: [] },
  { source: 'my-plugin', supersedes: ['reports-v1'] },
);
console.log(handle.removed); // ['reports-v1'] when that skill held the path `reports`
await handle.unregister();
```

### unregisterSkill()

Remove a skill registered with `registerSkillContent()`. Returns `false` when no such skill is registered.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
unregisterSkill(id: string): Promise<boolean>
```

The skill leaves the registry at once. If the storage provider fails to remove its copy, the registry still stops serving it from `search()`, `listSkills()`, `count()` and `loadSkill()`.

## External Storage

Skills can be synced to external storage for persistence:

### setExternalProvider()

Configure an external storage provider.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
setExternalProvider(provider: ExternalSkillProviderBase): void
```

### syncToExternal()

Sync local skills to external storage.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
syncToExternal(): Promise<SyncResult>
```

**Example:**

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
// Configure Redis-based storage
registry.setExternalProvider(new RedisSkillProvider(redisClient));

// Sync skills
const result = await registry.syncToExternal();
console.log(`Synced ${result.count} skills`);
```

### hasExternalProvider()

Check if external provider is configured.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
hasExternalProvider(): boolean
```

### getExternalProvider()

Get the configured external provider.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
getExternalProvider(): ExternalSkillProviderBase | undefined
```

## Change Events

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface SkillChangeEvent {
  kind: 'added' | 'updated' | 'removed' | 'reset';
  changeScope: 'global' | 'session';
  version: number;
  snapshot: ReadonlyArray<SkillEntry>;
  sessionId?: string;
  relatedRequestId?: string;
}
```

## SkillEntry Accessors

Each skill in the registry is a `SkillEntry` with these accessor methods:

| Method | Return Type | Description |
| - | - | - |
| `getDescription()` | `string` | Skill description |
| `getToolRefs()` | `SkillToolRef[]` | Normalized tool references |
| `getToolNames()` | `string[]` | Tool name strings |
| `getTags()` | `string[]` | Categorization tags |
| `getPriority()` | `number` | Search ranking priority |
| `isHidden()` | `boolean` | Whether hidden from discovery |
| `getLicense()` | `string \| undefined` | License (per Agent Skills spec) |
| `getCompatibility()` | `string \| undefined` | Compatibility notes (per Agent Skills spec) |
| `getSpecMetadata()` | `Record<string, string> \| undefined` | Arbitrary metadata (per Agent Skills spec) |
| `getAllowedTools()` | `string \| undefined` | Pre-approved tools (per Agent Skills spec) |
| `getResources()` | `SkillResources \| undefined` | Bundled resource directories |

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const skill = registry.findByName('review-pr');
if (skill) {
  console.log(skill.getLicense());       // 'MIT'
  console.log(skill.getCompatibility()); // 'Requires git CLI'
  console.log(skill.getResources());     // { scripts: '...', references: '...' }
}
```

## Skill Visibility

Skills can have different visibility modes:

| Visibility | Description |
| - | - |
| `mcp` | Available only via MCP protocol |
| `http` | Available only via HTTP API |
| `both` | Available via both MCP and HTTP |

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Skill({
  name: 'internal-task',
  visibility: 'mcp', // Only for MCP clients
})
class InternalTask extends SkillContext { }

@Skill({
  name: 'public-api',
  visibility: 'http', // Only for HTTP clients
})
class PublicAPI extends SkillContext { }
```

## Tool Validation

Skills can reference tools that must exist in the registry:

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Skill({
  name: 'data-pipeline',
  tools: ['fetch_data', 'transform', 'save_results'],
})
class DataPipeline extends SkillContext { }
```

Validation modes:

* **strict**: Fail if any tool is missing
* **warn**: Log warning for missing tools
* **ignore**: Skip validation

## Related

* [Skill Decorator](/frontmcp/sdk-reference/decorators/skill)
* [SkillContext](/frontmcp/sdk-reference/contexts/skill-context)
* [Registries Overview](/frontmcp/sdk-reference/registries/overview)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.