# How to Add Custom Commands to an OpenAI Codex Plugin: A Complete Guide

> Learn to add custom commands to your OpenAI Codex plugin. This guide covers manifest configuration, script implementation, and validation for seamless integration.

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

---

**Add custom commands to an OpenAI Codex plugin by defining them in the [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) manifest, implementing executable scripts that read JSON from stdin and write JSON to stdout, and validating the configuration against the plugin-json-spec.**

Custom commands are the primary mechanism for extending OpenAI Codex with external capabilities. In the `openai/plugins` repository, these commands are declared in a JSON manifest and backed by executable scripts that handle the actual logic. This guide walks through the exact structure required to add custom commands to an OpenAI Codex plugin, referencing the official source code and implementation patterns used in production plugins like **Mem** and **Zotero**.

## Defining Commands in the Plugin Manifest

Every Codex plugin centers around a manifest file located at [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) within your plugin directory. This JSON file follows the **plugin-json-spec** and contains a `commands` array where each custom command is declared.

According to the reference schema in [`.agents/skills/plugin-creator/references/plugin-json-spec.md`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/references/plugin-json-spec.md), the manifest structure looks like this:

```json
{
  "name": "my-plugin",
  "version": "0.1.0",
  "description": "Example plugin showing custom commands.",
  "interface": {
    "displayName": "My Plugin",
    "shortDescription": "Demo of custom commands",
    "category": "Productivity"
  },
  "commands": [
    {
      "name": "my-command",
      "description": "Summarize a URL and extract key bullet points.",
      "script": "./scripts/my-command.py",
      "input": {
        "type": "object",
        "properties": {
          "url": { "type": "string", "format": "uri", "description": "Web page to summarize." }
        },
        "required": ["url"]
      },
      "output": {
        "type": "object",
        "properties": {
          "summary": { "type": "string", "description": "Generated summary." }
        }
      }
    }
  ]
}

```

### The Commands Array Schema

Each command object in the `commands` array requires specific fields that define the contract between the model and your code:

- **name** – The unique identifier used by Codex to invoke the command (e.g., `my-command`).
- **description** – Human-readable text shown in the UI and used by the model to decide when to call the command.
- **script** – Relative path from the plugin root to the executable script (e.g., [`./scripts/my-command.py`](https://github.com/openai/plugins/blob/main/./scripts/my-command.py)).
- **input** – JSON Schema defining the arguments the model must provide.
- **output** – JSON Schema describing the data structure returned to the model.
- **auth** (optional) – Authentication requirements such as OAuth scopes.
- **rateLimit** (optional) – Throttling configuration for API calls.

For a concrete implementation example, see the Mem plugin manifest at [`plugins/mem/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/mem/.codex-plugin/plugin.json) in the repository.

## Implementing the Command Script

The runtime contract is strict: scripts receive arguments **via STDIN** as a JSON object conforming to the `input` schema, and must write a JSON object to **STDOUT** matching the `output` schema. Exit status `0` indicates success; any non-zero status is treated as a failure and the error text is reported back to the model.

### Python Implementation Example

Here is a complete implementation that follows the stdin/stdout contract used by the Codex runtime:

```python
#!/usr/bin/env python3
import json
import sys
import requests
from bs4 import BeautifulSoup

def main():
    # Read arguments from stdin

    payload = json.load(sys.stdin)
    url = payload["url"]

    # Do the work (simple HTML extraction)

    resp = requests.get(url, timeout=10)
    resp.raise_for_status()
    soup = BeautifulSoup(resp.text, "html.parser")
    # Grab first three <p> elements as a naive summary

    paragraphs = [p.get_text(strip=True) for p in soup.find_all("p")[:3]]
    summary = "\n\n".join(paragraphs)

    # Emit result to stdout

    result = {"summary": summary}
    json.dump(result, sys.stdout)

if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        # Return a non-zero exit code + error message for Codex handling

        sys.stderr.write(str(e))
        sys.exit(1)

```

Make the script executable before testing:

```bash
chmod +x plugins/my-plugin/scripts/my-command.py

```

For a real-world reference showing multiple sub-commands and error handling, examine [`plugins/zotero/skills/zotero/scripts/zotero.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/zotero.py) lines 730-740 in the source repository.

## Registering Commands in the Codex Marketplace

To make your command appear in a Codex Marketplace listing, add an entry to the marketplace JSON file:

```json
{
  "name": "my-plugin",
  "source": { "source": "local", "path": "./plugins/my-plugin" },
  "policy": {
    "installation": "AVAILABLE",
    "authentication": "ON_INSTALL"
  },
  "category": "Productivity"
}

```

Personal plugins use `~/.agents/plugins/marketplace.json`, while repository-wide plugins use `<repo-root>/.agents/plugins/marketplace.json`. The marketplace workflow is documented in [`.agents/skills/plugin-creator/SKILL.md`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/SKILL.md).

## Testing and Validation

Before deploying, validate your plugin structure and test the command manually.

### Validating the Manifest

Run the quick-validator shipped with the repository:

```bash
python3 .agents/skills/plugin-creator/scripts/quick_validate.py plugins/my-plugin

```

This script checks your [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json) against the official schema and catches common configuration errors.

### Local Command Testing

Test the script manually to confirm JSON I/O works correctly:

```bash
echo '{"url":"https://example.com"}' | \
python3 plugins/my-plugin/scripts/my-command.py

```

Expected output:

```json
{"summary":"First paragraph text...\n\nSecond paragraph text...\n\nThird paragraph text..."}

```

Once manual testing passes, start the Codex runtime pointing at your plugin directory to invoke the command through the model.

## Summary

- **Define commands** in [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) using the `commands` array with strict JSON Schema for inputs and outputs.
- **Implement scripts** that read JSON from stdin and write JSON to stdout, exiting with code 0 on success.
- **Reference production examples** like [`plugins/mem/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/mem/.codex-plugin/plugin.json) and [`plugins/zotero/skills/zotero/scripts/zotero.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/zotero.py) for canonical patterns.
- **Validate** using [`.agents/skills/plugin-creator/scripts/quick_validate.py`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/scripts/quick_validate.py) before testing with the runtime.
- **Optionally register** in the marketplace via the configuration documented in [`.agents/skills/plugin-creator/SKILL.md`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/SKILL.md).

## Frequently Asked Questions

### What file format does the OpenAI Codex plugin manifest use?

The manifest uses standard JSON and must be named [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json) and placed inside a `.codex-plugin/` directory at your plugin root. The file follows the **plugin-json-spec** schema, which defines required fields like `name`, `version`, and the `commands` array.

### How does a Codex plugin script receive arguments from the model?

Scripts receive arguments via **standard input (STDIN)** as a JSON object that conforms to the `input` schema defined in the manifest. The script must parse this JSON, execute the logic, and return results by writing a JSON object to **standard output (STDOUT)**.

### Where can I find the official schema definition for Codex plugin commands?

The official schema reference is located at [`.agents/skills/plugin-creator/references/plugin-json-spec.md`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/references/plugin-json-spec.md) in the `openai/plugins` repository. This document defines all valid fields for the manifest, including command definitions, authentication requirements, and rate limiting options.

### How do I make my custom command appear in the Codex Marketplace?

Add a marketplace entry to either `~/.agents/plugins/marketplace.json` (for personal plugins) or `<repo-root>/.agents/plugins/marketplace.json` (for repository-wide plugins). The entry must reference your plugin's source path and specify installation policies as documented in [`.agents/skills/plugin-creator/SKILL.md`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/SKILL.md).