Build a plugin
A Ghast plugin is a declarative directory; it does not need an arbitrary JavaScript activation entry. New plugins should use an Agent Plugins 1.0.0 root manifest and place each contribution in its declared or conventional location.
Minimal plugin
release-tools/
├── plugin.json
└── skills/
└── release-review/
└── SKILL.md
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "release-tools",
"version": "1.0.0",
"description": "Reusable release review workflows for Ghast.",
"author": {
"name": "Example Team"
},
"license": "MIT"
}
---
name: release-review
description: Review a release candidate and report scope, checks, risks, and rollback readiness.
---
# Release review
Review the requested release scope. Report what passed, failed, or remains unverified. Do not publish or push unless the user explicitly asks.
A Plugin must contain at least one runnable contribution: a Skill, slash command, MCP Server, or Hook. A directory containing only plugin.json can parse as a manifest, but installation rolls back because there is nothing to activate.
name is at most 64 characters and must start with a lowercase letter or digit. Remaining characters may be lowercase letters, digits, hyphens, or dots. It cannot end with a dot or hyphen or contain consecutive -- or ... A root plugin.json must declare the schema shown above.
Ghast also reads compatible manifests at .ghast-plugin/plugin.json, .codex-plugin/plugin.json, and .claude-plugin/plugin.json. Prefer the Agent Plugin root format for new work so one metadata file can serve compatible hosts.
Recommended layout
release-tools/
├── plugin.json
├── skills/
│ └── release-review/
│ └── SKILL.md
├── commands/
│ └── release-check.md
├── mcp.json
├── hooks.json
└── scripts/
├── session-context.sh
├── prompt-context.sh
└── reviewer-context.sh
Include only what the plugin provides. A skills-only plugin does not need MCP, hooks, or a commands directory.
Declare Ghast contribution paths
An Agent Plugin uses extensions.ai.trapezohe.ghast for Ghast-specific paths:
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "release-tools",
"version": "1.0.0",
"description": "Reusable release review workflows for Ghast.",
"author": { "name": "Example Team" },
"extensions": {
"ai.trapezohe.ghast": {
"commands": "commands",
"mcpServers": "mcp.json",
"hooks": "hooks.json"
}
}
}
Declared values must be relative paths inside the plugin root. Absolute paths and .. escapes are rejected. Skills are discovered from skills/ by convention, with one skills/<name>/SKILL.md per skill.
Add skills
Every skill needs valid YAML front matter:
---
name: release-review
description: Review a release candidate and report scope, validation, risks, and rollback readiness.
allowed-tools:
- Glob
- Grep
- Read
- Bash
---
# Release review
Follow the requested scope. Stop before publish, push, or deployment unless the user explicitly authorizes it.
After the plugin is installed and enabled, these appear as Marketplace skills in the library. Users can turn off individual skills. Runtime does not load a plugin that is already disabled, but the current public detail page cannot switch an enabled plugin to disabled. Remove a removable plugin when the complete contribution set must stop.
Add slash commands
Store one command per commands/*.md file. The filename becomes the command name, the body becomes its workflow, and simple front matter can provide a description and argument hint.
---
description: Review the current release candidate
argument-hint: <release or branch>
allowed-tools: Glob, Grep, Read, Bash
---
Review $ARGUMENTS. Report scope, checks, blockers, and rollback readiness. Do not publish.
After enabling the plugin, type / in the composer to find the command. allowed-tools becomes the command run's strict active tool set, replacing the user's previous selection and disabling automatic tool selection. It still cannot bypass Ask/Full access, system permissions, or remote-account permissions. A command should produce an inspectable task, not conceal high-risk actions.
Add an MCP server
Agent Plugin mcp.json uses its own schema:
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
"mcpServers": {
"release-api": {
"type": "streamable-http",
"url": "https://example.com/mcp"
}
}
}
For a local stdio server:
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
"mcpServers": {
"release-local": {
"type": "stdio",
"command": "./bin/release-server",
"args": ["--data", "${PLUGIN_DATA}"],
"cwd": "${PLUGIN_DATA}"
}
}
}
command must be a bare executable such as node or a plugin-relative path beginning with ./. Variable expansion is available in path-bearing fields such as args, env, and cwd:
${PLUGIN_ROOT}is the installed plugin root;${PLUGIN_DATA}is the writable data directory Ghast prepares for the plugin.
Do not embed secrets in mcp.json. OAuth and credential declarations belong under extensions.ai.trapezohe.ghast.mcpServerExtensions.<server> in plugin.json; they are rejected if added directly to mcp.json:
{
"extensions": {
"ai.trapezohe.ghast": {
"mcpServerExtensions": {
"release-api": {
"oauthClientRequired": true,
"oauthRequired": true,
"credentialHeaders": {
"Authorization": "Bearer $VAULT:release-token"
}
},
"release-local": {
"credentialEnv": {
"API_KEY": "$VAULT:release-key"
},
"optionalCredentials": ["release-key"]
}
}
}
}
}
oauthClientRequired and oauthRequired are remote-only. credentialHeaders is also remote-only and each value must be $VAULT:key or Bearer $VAULT:key. credentialEnv is stdio-only, each value must be $VAULT:key, and it cannot override PLUGIN_ROOT or PLUGIN_DATA. optionalCredentials can list only credential keys declared for that server. Also provide privacy, terms, and revocation information on the plugin detail page.
Add lifecycle hooks
Point extensions.ai.trapezohe.ghast.hooks to a JSON configuration file. Plugin hooks support SessionStart, UserPromptSubmit, and SubagentStart, allowing a plugin to provide additional context or block an unsafe flow. See Plugin lifecycle hooks for the complete protocol.
Hooks run local commands and are executable plugin content. Disclose them on the install surface, bound their output, use timeouts, and never treat hook output as a way to grant tool permissions.
Validate a local build
- Prepare the plugin in a clean directory.
- Choose Plugins → Install local.
- Select the plugin root.
- Verify the name, author, version, and source that are actually shown. A local Plugin has extended details only when it includes
details.json. - Inspect Skills, commands, MCP, and Hooks in the package itself. Details can show Overview, Starter Prompts, Links, Repository, Skills, and Apps, but not a complete command list, individual MCP Tools, or Hook definitions.
- Use Try now to create a draft and run a small read-only task.
- If the plugin is already disabled, choose Enable and confirm its contributions appear. The current public detail page cannot switch an enabled plugin to disabled.
- If removable, uninstall it and confirm skills, commands, hooks, and connection entries disappear.
Do not stop at an “installed” toast. Discovery, installation or enablement, connection, invocation, and removal are separate acceptance checks. An individual Skill switch does not stop the complete Plugin.
Publish a catalog entry
Before installation, the catalog entry should expose:
- stable plugin ID, name, version, and author;
- English and Chinese descriptions;
- category and icon;
- package URL and integrity digest;
- compatibility;
- included skills, apps, and starter prompts;
- website, repository, documentation, privacy policy, and terms.
Update the version and digest whenever the package changes. Call out changes to OAuth scopes, data flow, hooks, or high-risk tools, and require users to recheck the connection and first run.