# How to Use and Configure Skills in the Open Agents System: A Complete Guide

> Master Open Agents skills! Learn how to use and configure reusable Markdown modules for custom functionality and automatic discovery in this comprehensive guide.

- Repository: [Vercel Labs/open-agents](https://github.com/vercel-labs/open-agents)
- Tags: how-to-guide
- Published: 2026-04-16

---

**Open Agents skills are reusable Markdown-based modules stored in `.claude/skills/` or `.agents/skills/` directories that expose custom functionality through YAML front-matter configuration and automatic discovery mechanisms.**

The Open Agents framework by Vercel Labs enables developers to extend LLM-powered agents through a flexible skill system. Skills function as self-contained logic units written in Markdown, allowing you to inject custom capabilities without modifying the core agent codebase. This guide covers the complete architecture, configuration options, and implementation patterns based on the actual source code in the `vercel-labs/open-agents` repository.

## Understanding Open Agents Skills Architecture

The Open Agents skills system follows a discover-and-execute pipeline that bridges file-system storage with runtime LLM tool invocation.

### Skill Discovery Process

Skills are discovered at runtime by the `discoverSkills()` function in [`packages/agent/skills/discovery.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/skills/discovery.ts) (lines 13-90). This utility scans directories returned by `getSandboxSkillDirectories()` from [`apps/web/lib/skills/directories.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/skills/directories.ts), searching for [`SKILL.md`](https://github.com/vercel-labs/open-agents/blob/main/SKILL.md) (preferred) or [`skill.md`](https://github.com/vercel-labs/open-agents/blob/main/skill.md) files.

For each discovered file, the system:

1. Parses YAML front-matter using `parseSkillFrontmatter()` (lines 25-74)
2. Builds a `SkillMetadata` object containing name, description, path, and options
3. Excludes built-in commands (`model`, `resume`, `new`) to prevent shadowing
4. Deduplicates skill names case-insensitively

### Caching and Performance

Discovered metadata is cached per-session via `createSkillsCache()` in [`apps/web/lib/skills-cache.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/skills-cache.ts) (lines 59-120). The implementation uses an in-memory LRU map with a 4-hour TTL and optional Redis backing through `createRedisClient`. This prevents redundant file-system operations during active sessions.

### REST API Exposure

Skills become available through the `GET /api/sessions/:sessionId/skills` endpoint defined in `apps/web/app/api/sessions/[sessionId]/skills/route.ts` (lines 30-84). The handler returns cached skills or triggers fresh discovery when `?refresh=1` is passed. Skills marked with `userInvocable: false` are filtered from the response.

### Client-Side Consumption

The React hook `useSessionSkills` in [`apps/web/hooks/use-session-skills.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/hooks/use-session-skills.ts) (lines 14-44) wraps the REST endpoint with SWR caching. It exposes `skills`, `isLoading`, `error`, and a `refresh()` method for UI components.

### Skill Invocation Flow

When an LLM invokes a skill, it calls the **skill tool** defined in [`packages/agent/tools/skill.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/tools/skill.ts) (lines 55-107). The execution flow:

1. Receives `{ skill: string, args?: string }` parameters
2. Locates the skill in `experimental_context.skills`
3. Reads the skill file inside the sandbox
4. Processes the body through `extractSkillBody()` in [`packages/agent/skills/loader.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/skills/loader.ts)
5. Substitutes `$ARGUMENTS` with user input via `substituteArguments()`
6. Injects the directory path via `injectSkillDirectory()` (lines 38-40)
7. Returns the processed script to the LLM

## Configuring Skills in Open Agents

Skills are configured through YAML front-matter and directory placement, supporting both project-local and global scopes.

### Front-Matter Configuration Options

Each [`SKILL.md`](https://github.com/vercel-labs/open-agents/blob/main/SKILL.md) begins with YAML front-matter defining metadata and behavior. The schema is enforced by `skillFrontmatterSchema` in [`packages/agent/skills/types.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/skills/types.ts) (lines 7-34):

```yaml
---
name: pdf
description: Generate a PDF from markdown
version: "1.0"
disable-model-invocation: false   # Prevents automatic LLM invocation if true

user-invocable: true            # Controls slash-command visibility

allowed-tools: "file,network"   # Comma-separated tool whitelist

context: fork                   # Execution mode (optional)

agent: "my-agent"               # Custom agent type (optional)

---

```

The `frontmatterToOptions()` function (lines 72-89) transforms kebab-case YAML keys into camelCase `SkillOptions` for internal use.

### Directory Placement and Scoping

Skills support two placement strategies:

**Project-Local Skills** (single project only):
- `.claude/skills/your-skill/`
- `.agents/skills/your-skill/`

**Global Skills** (available to all projects):
- `~/.agents/skills/your-skill/`

The `getSandboxSkillDirectories()` function in [`apps/web/lib/skills/directories.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/skills/directories.ts) (lines 19-27) assembles both locations:

```typescript
return [
  ...getProjectSkillDirectories(sandbox.workingDirectory),
  getGlobalSkillsDirectory(homeDirectory),
];

```

### Installing Global Skills

Administrators can pre-install community skills using the `installGlobalSkills()` function in [`apps/web/lib/skills/global-skill-installer.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/skills/global-skill-installer.ts) (lines 15-38). This executes `npx skills add <source> --skill <name> -g -y --copy` inside the sandbox:

```typescript
await installGlobalSkills({
  sandbox,
  globalSkillRefs: [
    { source: "vercel-labs/markdown-pdf", skillName: "pdf" }
  ],
});

```

## Practical Implementation Examples

### Creating a Custom Skill

Create a file structure like:

```

.project-root/
 └─ .claude/
     └─ skills/
         └─ pdf/
             ├─ SKILL.md
             └─ render.js

```

**SKILL.md**:

```markdown
---
name: pdf
description: Convert markdown to a PDF document
allowed-tools: "file"
---
Skill directory: $SKILL_DIR

```js
// render.js will be executed by the model
const md = $ARGUMENTS;
const pdf = await convertMarkdownToPdf(md);
await writeFile(`${process.env.SKILL_DIR}/output.pdf`, pdf);

```

```

The `$ARGUMENTS` placeholder receives user input, while `$SKILL_DIR` is injected automatically by `injectSkillDirectory()`.

### Invoking Skills via Tool Calls

When the LLM decides to use a skill, it generates a tool call:

```json
{
  "name": "skill",
  "arguments": {
    "skill": "pdf",
    "args": "# My Report\n\nHello world!"

  }
}

```

The `skillTool.execute` method processes this by reading the file, substituting arguments, and returning the executable content.

### React Component Integration

Consume skills in the frontend using the provided hook:

```tsx
import { useSessionSkills } from "@/hooks/use-session-skills";

function SkillPicker({ sessionId, sandboxConnected }) {
  const { skills, isLoading, error, refresh } = useSessionSkills(
    sessionId,
    sandboxConnected,
  );

  if (isLoading) return <p>Loading skills…</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <select onChange={(e) => console.log(e.target.value)}>
      {skills?.map((s) => (
        <option key={s.name} value={s.name}>
          {s.name} – {s.description}
        </option>
      ))}
    </select>
  );
}

```

### Global Skill Installation via API

Install skills programmatically for CI/CD or admin interfaces:

```typescript
import { installGlobalSkills } from '@/lib/skills/global-skill-installer';

await installGlobalSkills({
  sandbox,
  globalSkillRefs: [
    { source: "owner/repo", skillName: "analyze-csv" }
  ],
});

```

After installation, the skill appears in subsequent calls to `/api/sessions/:id/skills`.

## Summary

- **Skills** in Open Agents are Markdown files with YAML front-matter stored in `.claude/skills/` or `.agents/skills/` directories
- **Discovery** happens via `discoverSkills()` in [`packages/agent/skills/discovery.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/skills/discovery.ts), with results cached through `createSkillsCache()`
- **Configuration** controls invocation behavior through `disable-model-invocation`, `user-invocable`, and `allowed-tools` front-matter keys
- **Invocation** occurs through the skill tool ([`packages/agent/tools/skill.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/tools/skill.ts)), which processes `$ARGUMENTS` and `$SKILL_DIR` placeholders
- **Global installation** supports reusable skills across projects via `installGlobalSkills()` and the `~/.agents/skills/` directory

## Frequently Asked Questions

### What file format should I use for creating Open Agents skills?

Create a [`SKILL.md`](https://github.com/vercel-labs/open-agents/blob/main/SKILL.md) file (preferred) or [`skill.md`](https://github.com/vercel-labs/open-agents/blob/main/skill.md) file containing YAML front-matter between triple dashes followed by Markdown content. The front-matter must include at minimum a `name` and `description` field, with optional configuration flags like `disable-model-invocation` or `user-invocable` to control visibility and automatic execution.

### How does the Open Agents system handle skill arguments and file paths?

The system substitutes the `$ARGUMENTS` placeholder with whatever text the user provides after the slash command. The skill directory path is injected automatically through the `$SKILL_DIR` placeholder (processed by `injectSkillDirectory()` in [`packages/agent/skills/loader.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/skills/loader.ts)), allowing scripts to reference files relative to the skill location using `process.env.SKILL_DIR`.

### Can I restrict which tools a skill is allowed to use?

Yes, use the `allowed-tools` front-matter key with a comma-separated list of permitted tools (e.g., `allowed-tools: "file,network"`). This restricts the skill's capabilities according to the `allowedTools` option defined in [`packages/agent/skills/types.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/skills/types.ts). If omitted, the skill inherits default tool permissions from the agent context.

### Where should I place skills for global access across all projects?

Place skills in the `~/.agents/skills/` directory or install them programmatically using `installGlobalSkills()` from [`apps/web/lib/skills/global-skill-installer.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/skills/global-skill-installer.ts). Global skills appear in all sessions, while project-local skills placed in `.claude/skills/` or `.agents/skills/` only appear for that specific workspace.