name: Carapace Plugin Development description: > Build, modify, or review Carapace plugins. Use when creating new plugins, implementing tool handlers, writing manifest.json files, creating container-side skill files, or debugging plugin loading issues. Covers the full plugin lifecycle: scaffold, implement handler, define manifest schema, write skill markdown, verify, and symlink for runtime discovery.
Carapace Plugin Development
Architecture
Each plugin is a pair spanning the trust boundary:
- Host-side handler (
handler.ts) — TypeScript, holds credentials, executes tool logic. Runs on the host with full trust. - Container-side skill (
skills/{name}.md) — Markdown teaching Claude the available tools. Injected into the read-only container.
Plugins live in a separate repo (carapace-plugins/) and are symlinked into
$CARAPACE_HOME/plugins/ for runtime discovery. The plugin loader scans for
directories containing manifest.json, validates against a JSON Schema,
dynamically imports handler.js (preferred) or handler.ts, and calls
initialize() with a 10-second timeout.
Plugin Directory Layout
{plugin-name}/
manifest.json # Declares tools, channels, config
handler.ts # Host-side PluginHandler implementation
skills/
{plugin-name}.md # Teaches Claude about the tools
package.json # @carapace/core via link:
tsconfig.json # ES2022 / NodeNext / strict
Workflow
- Scaffold — Run
scripts/scaffold-plugin.sh <name> <output-dir>to generate all boilerplate files with TODO placeholders - Define manifest — Fill in tools, schemas, risk levels. See references/manifest-schema.md
- Implement handler — Write tool logic using the PluginHandler interface. See references/handler-api.md
- Write skill file — Teach Claude how to invoke the tools from the container
- Configure project — Set up package.json and tsconfig.json. See references/project-setup.md
- Verify — Type-check, test, symlink, and confirm loading in server logs
Critical Rules
additionalProperties: falseis enforced at every manifest schema level- Tool names must be globally unique across all loaded plugins
- MUST use
as conston theokfield inToolInvocationResult(discriminated union) - Use
link:(notfile:) for@carapace/corein package.json - Do NOT set
outDir: "."in tsconfig (TypeScript excludes its own inputs) - Never log or expose credential values
- Import types from
@carapace/core/pluginsubpath export
Common Mistakes
- Using
file:instead oflink:in package.json (breaks type resolution) - Forgetting
additionalProperties: falsein argument schemas (validation fails) - Using a reserved tool name (
get_diagnostics,list_tools,get_session_info) or reserved plugin name (installer,memory,test-input,hello) - Missing
subscribes: []in manifest (required even if empty) - Missing
provides.channels: [](required even for tool-only plugins) - Returning
{ ok: true }withoutas const— breaks discriminated union type narrowing - Using
export default { ... } satisfies PluginHandlerwithout the type annotation
Reference Plugin
../carapace-plugins/hello-world/ demonstrates a minimal working plugin with
one low-risk tool, default export handler, argument validation, and a skill file.