# How to Define Skills in an OpenAI Plugin Structure: A Complete Guide

> Learn how to define skills in an OpenAI plugin structure. Follow this guide to create skill directories, SKILL.md files, and UI specifications for your plugin.

- Repository: [OpenAI/plugins](https://github.com/openai/plugins)
- Tags: how-to-guide
- Published: 2026-07-02

---

**To define a skill in an OpenAI plugin structure, create a dedicated directory under `plugins/<plugin-name>/skills/` containing a [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) file with YAML front matter, an [`agents/openai.yaml`](https://github.com/openai/plugins/blob/main/agents/openai.yaml) UI specification, and optional helper scripts that implement the runtime logic.**

OpenAI plugins are organized as collections of reusable capabilities called skills, each residing in the `openai/plugins` repository. Learning how to define skills in an OpenAI plugin structure enables you to extend Codex with custom tools that integrate seamlessly into the agentic workflow. Every skill follows a strict three-part architecture that separates metadata declaration from implementation logic.

## The Three Core Components of an OpenAI Plugin Skill

Each skill lives in its own directory under `plugins/<plugin-name>/skills/<skill-name>/` and requires three mandatory parts to function within the Codex ecosystem.

### SKILL.md: Metadata and Documentation

The [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) file serves as the canonical declaration of the skill's identity. It must begin with a YAML front-matter block that registers the skill with the system:

```yaml
---
name: Zotero
description: Use Zotero Desktop to search your library and add citations
---

```

The `name` field becomes the skill identifier used in prompts (e.g., `$Zotero`), while the `description` appears in the plugin catalogue. Following the front matter, the markdown body contains usage guidelines, command references, workflow documentation, and safety policies. According to the source code in [`plugins/zotero/skills/zotero/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/SKILL.md), this file should remain self-contained so users understand capabilities without referencing external documentation.

### agents/openai.yaml: UI and Prompt Configuration

The [`agents/openai.yaml`](https://github.com/openai/plugins/blob/main/agents/openai.yaml) file, located at `plugins/<plugin-name>/skills/<skill-name>/agents/openai.yaml`, defines how the skill appears in the UI and how the model interacts with it:

```yaml
interface:
  display_name: "Zotero"
  short_description: "Search Zotero and add citations"
  icon_small: "./assets/icon.png"
  default_prompt: "Use $Zotero to search my Zotero library..."

```

This specification dictates which model to invoke, what system prompt to prepend to user queries, and which icons render in the interface. The `default_prompt` is automatically injected when the skill is selected, establishing the context for the LLM.

### Helper Scripts and Assets: Runtime Implementation

The actual logic resides in executable helpers stored under `plugins/<plugin-name>/skills/<skill-name>/scripts/`. These scripts must use **stdlib-only** Python (no external dependencies) and are executed by Codex via the `python3` command line. Static assets such as icons and images belong in the `assets/` directory and are referenced relative to the skill root.

## How Skills Are Wired Together

Understanding the runtime flow clarifies how the three components interact when a user invokes a capability:

1. **Registration** – Codex loads the YAML front matter from [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) to register the skill identifier and description.

2. **Prompt Construction** – When selected, the system merges the `default_prompt` from [`agents/openai.yaml`](https://github.com/openai/plugins/blob/main/agents/openai.yaml) with the user's query, creating a contextualized instruction set for the model.

3. **Execution** – The model identifies the need to call a helper script (e.g., `python3 <plugin-root>/skills/zotero/scripts/zotero.py --json "query"`) and executes it via the command line.

4. **Response Parsing** – The script outputs JSON or plain text, which the model receives and uses to render the final response to the user.

## Creating a New Skill with the Plugin Creator

The repository provides a scaffolding tool to generate the complete directory structure. Run the following command from the repository root:

```bash
python3 .agents/skills/plugin-creator/scripts/create_basic_plugin.py \
    my-plugin \
    --path ./plugins \
    --marketplace-path ./.agents/plugins/marketplace.json \
    --with-skills --with-scripts --with-assets --with-marketplace

```

This generates:

```

plugins/my-plugin/
└─ skills/
   └─ my-skill/
       ├─ SKILL.md
       ├─ agents/
       │   └─ openai.yaml
       ├─ scripts/
       │   └─ my_script.py
       └─ assets/
           └─ icon.png

```

The generated [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) includes template sections for "Fast starts", "Workflow", and "Output standards". Refer to [`.agents/skills/plugin-creator/SKILL.md`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/SKILL.md) for detailed scaffolding options.

## Best Practices for Skill Definition

Follow these patterns established in the `plugins/zotero` and `plugins/github/skills/yeet` implementations:

- **Keep documentation self-contained** – All commands, safety notes, and usage examples must reside in [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) so users understand the skill without external references.

- **Expose JSON output** – Helper scripts should implement a `--json` flag (as demonstrated in the Zotero skill) to return machine-readable data that Codex can parse reliably.

- **Declare safety guards** – For write-heavy actions like commits or pushes, explicitly document required user confirmations in the "Write Safety" section, following the pattern in [`plugins/github/skills/yeet/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/github/skills/yeet/SKILL.md).

- **Version-control assets** – Store icons as PNG or SVG files under `assets/` and reference them with relative paths in [`agents/openai.yaml`](https://github.com/openai/plugins/blob/main/agents/openai.yaml).

## Complete Example: Building an Echo Skill

Here is a minimal, functional skill that echoes user input back unchanged.

### SKILL.md Definition

Create [`plugins/echo/skills/echo/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/echo/skills/echo/SKILL.md):

```yaml
---
name: echo
description: Return the supplied text unchanged – useful for quick tests.
---

# Echo

## Fast start

```bash
python3 <plugin-root>/skills/echo/scripts/echo.py "Hello world"

```

## Workflow

1. The model receives the user request.
2. It injects the request into the default prompt (`Use $echo …`).
3. The helper prints the payload as JSON:

```bash
python3 <plugin-root>/skills/echo/scripts/echo.py --json "Hello world"

# → {"result":"Hello world"}

```

## Output standards

- Always return a JSON object with a single `result` field.
- No side‑effects; safe for any user context.

```

### Agent Configuration

Create [`plugins/echo/skills/echo/agents/openai.yaml`](https://github.com/openai/plugins/blob/main/plugins/echo/skills/echo/agents/openai.yaml):

```yaml
interface:
  display_name: "Echo"
  short_description: "Echo back whatever you give me"
  icon_small: "./assets/icon.png"
  icon_large: "./assets/icon.png"
  default_prompt: "Use $echo to repeat the user’s input exactly."

```

### Helper Script Implementation

Create [`plugins/echo/skills/echo/scripts/echo.py`](https://github.com/openai/plugins/blob/main/plugins/echo/skills/echo/scripts/echo.py):

```python
#!/usr/bin/env python3
import json, sys

if "--json" in sys.argv:
    # Strip the flag and any leading option names

    text = " ".join(arg for arg in sys.argv[1:] if not arg.startswith("--"))
    print(json.dumps({"result": text}))
else:
    print(" ".join(sys.argv[1:]))

```

### Runtime Flow

When a user requests "Please repeat the phrase 'quick brown fox'", Codex constructs the prompt:

```

Use $echo to repeat the user’s input exactly.

User: Please repeat the phrase 'quick brown fox'.

```

Codex executes the helper and receives `{"result":"quick brown fox"}`, then renders the final response:

```

quick brown fox

```

## Summary

- **Three files are mandatory**: [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) (with YAML front matter), [`agents/openai.yaml`](https://github.com/openai/plugins/blob/main/agents/openai.yaml), and the optional but typical helper scripts under `scripts/`.
- **Directory structure matters**: Skills must reside at `plugins/<plugin-name>/skills/<skill-name>/` to be discovered by the Codex runtime.
- **Use stdlib-only Python**: Helper scripts run via `python3` command line and cannot rely on external packages.
- **JSON output preferred**: Implement `--json` flags for reliable machine-readable communication between scripts and the model.
- **Safety documentation is critical**: Document write-operation safeguards explicitly in [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md), as shown in the GitHub `yeet` skill.

## Frequently Asked Questions

### What is the mandatory file structure for an OpenAI plugin skill?

Every skill requires a [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) file (containing YAML front matter with `name` and `description` fields) and an [`agents/openai.yaml`](https://github.com/openai/plugins/blob/main/agents/openai.yaml) file (containing the `interface` configuration). These must reside in `plugins/<plugin-name>/skills/<skill-name>/`. Helper scripts are optional but typically placed in a `scripts/` subdirectory.

### How does the SKILL.md front matter connect to the Codex runtime?

The first three lines of [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) contain a YAML block that Codex reads during skill discovery to register the identifier. The `name` field becomes the invocation token (e.g., `$Zotero`), while the `description` populates the plugin catalogue UI. This registration happens before any code execution occurs.

### Can skills depend on external Python packages?

No. Helper scripts must be **stdlib-only** and execute via the system's `python3` binary without external dependencies. As implemented in the `openai/plugins` repository, scripts like [`zotero.py`](https://github.com/openai/plugins/blob/main/zotero.py) rely solely on standard library modules to ensure portability and security.

### How do I add safety guards to prevent destructive operations?

Document safety requirements in the "Write Safety" section of [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md), explicitly stating required user confirmations (e.g., "Never push without confirming scope"). For write-heavy skills like the GitHub `yeet` skill, include policy notes that Codex can reference to ensure destructive actions receive explicit user approval before execution.