/docs / plugins / capabilities

Plugin Capabilities

Plugins can declare structured capabilities in their manifest. These give the agent typed tools with schemas, lifecycle hooks, slash commands, HTTP routes, AI provider adapters, and user-configurable settings. A single plugin can declare any combination of capabilities simultaneously.

Tools

Declare tools in capabilities.tools[]. Each tool becomes a typed, schema-validated tool available to the agent:

{
  "capabilities": {
    "tools": [
      {
        "name": "gws.gmail.triage",
        "description": "Triage Gmail inbox -- categorize, prioritize, and draft responses",
        "command": "gmail +triage",
        "inputSchema": {
          "type": "object",
          "properties": {
            "limit": { "type": "integer", "description": "Max emails to triage" },
            "label": { "type": "string", "description": "Gmail label filter" }
          }
        },
        "approval": true,
        "timeoutSeconds": 120
      }
    ]
  }
}

Tool fields

FieldTypeDefaultDescription
namestringrequiredTool name exposed to the agent (e.g., "gws.gmail.triage")
descriptionstringrequiredDescription for the model to understand when to use this tool
commandstringrequiredCLI args appended to the plugin binary (e.g., "gmail +triage")
inputSchemaobjectgeneric objectJSON Schema for typed input validation
approvalboolfalseWhether this tool requires user approval before execution
timeoutSecondsnumber120Maximum execution time in seconds

How typed tools work

  • Plugin installs -- Nebo reads capabilities.tools[]
  • Tool definitions are routed through the consolidated STRAP PluginTool
  • Agent sees the tool with its schema and description
  • On execution: Nebo resolves the plugin binary, runs <binary> <command> with input as JSON on stdin, returns stdout

The generic plugin(resource, action, command) tool still works alongside structured tools -- it's the fallback for commands not declared in capabilities.

Hooks

Declare lifecycle hooks your plugin wants to subscribe to in capabilities.hooks[]:

{
  "capabilities": {
    "hooks": [
      {
        "hook": "tool.pre_execute",
        "hookType": "filter",
        "priority": 50,
        "command": "hooks tool-pre-execute",
        "timeoutMs": 500
      }
    ]
  }
}

Hook fields

FieldTypeDefaultDescription
hookstringrequiredHook point name (e.g., "tool.pre_execute")
hookTypestring"action""filter" (can modify payload) or "action" (fire-and-forget)
prioritynumber100Lower runs first
commandstringrequiredCLI subcommand for the hook handler
timeoutMsnumber500Timeout in milliseconds

Hook types

  • filter -- can modify or block the operation. The hook receives the payload as JSON on stdin and returns modified JSON on stdout. Returning a non-zero exit code blocks the operation.
  • action -- fire-and-forget side-effect. The return value is ignored.

Priority determines execution order: lower numbers run first. Default timeout is 500ms.

Available hook points

Plugins can subscribe to these lifecycle hooks. Filter hooks can modify payloads; action hooks are fire-and-forget.

HookDefault typeWhen
tool.pre_executefilterBefore a tool runs -- can modify input or block
tool.post_executefilterAfter a tool completes -- can modify output
message.pre_sendfilterBefore user message is sent to the agent
message.post_receivefilterAfter agent message is received
memory.pre_storefilterBefore a memory is persisted
memory.pre_recallfilterBefore memories are retrieved
session.message_appendactionWhen a message is added to the session
prompt.system_sectionsfilterDuring system prompt generation -- can inject sections
steering.generatefilterDuring steering signal generation
response.streamactionDuring response streaming
agent.turnactionWhen an agent turn completes
agent.should_continuefilterDecision point to continue or halt a turn
Circuit breaker: 3 consecutive failures disables the hook, auto-recovery after 5 minutes.

Commands

Declare slash commands or app commands in capabilities.commands[]:

{
  "capabilities": {
    "commands": [
      {
        "name": "/gmail",
        "description": "Quick access to Gmail operations",
        "command": "gmail",
        "slash": true
      }
    ]
  }
}

Command fields

FieldTypeDefaultDescription
namestringrequiredCommand name (e.g., "/gmail")
descriptionstringrequiredHuman-readable description
commandstringrequiredCLI subcommand to execute
slashboolfalseRegister as a slash command in chat

Routes

Declare HTTP routes your plugin handles (e.g., OAuth callbacks) in capabilities.routes[]:

{
  "capabilities": {
    "routes": [
      {
        "path": "/gws/oauth/callback",
        "method": "GET",
        "command": "auth callback",
        "auth": "public"
      }
    ]
  }
}

Route fields

FieldTypeDefaultDescription
pathstringrequiredRoute path
methodstringrequiredHTTP method (GET, POST, etc.)
commandstringrequiredCLI subcommand that handles the request
authstring"jwt""public" or "jwt"

Providers

Declare AI provider adapters for custom model backends in capabilities.providers[]:

{
  "capabilities": {
    "providers": [
      {
        "id": "openrouter",
        "displayName": "OpenRouter",
        "providerType": "model",
        "modelsCommand": "models list",
        "chatCommand": "chat stream",
        "authCommand": "auth setup"
      }
    ]
  }
}

Provider fields

FieldTypeDefaultDescription
idstringrequiredProvider ID
displayNamestringrequiredDisplay name
providerTypestringrequired"model", "speech", "image", etc.
modelsCommandstringrequiredCLI subcommand to list available models (JSON output)
chatCommandstringrequiredCLI subcommand for streaming chat (NDJSON on stdout)
authCommandstring--CLI subcommand for auth setup

Config schema (user settings)

Declare user-configurable settings that render as a form in the UI. Values are stored in plugin_settings and injected as environment variables on every plugin execution.

{
  "capabilities": {
    "configSchema": [
      {
        "key": "WORKSPACE_ID",
        "label": "Workspace ID",
        "description": "Your Asana workspace ID",
        "fieldType": "string",
        "required": true
      },
      {
        "key": "MAX_RESULTS",
        "label": "Max Results",
        "fieldType": "number",
        "default": "50"
      },
      {
        "key": "API_KEY",
        "label": "API Key",
        "fieldType": "string",
        "required": true,
        "secret": true
      },
      {
        "key": "LOG_LEVEL",
        "label": "Log Level",
        "fieldType": "select",
        "options": ["debug", "info", "warn", "error"],
        "default": "info"
      }
    ]
  }
}

Config schema fields

FieldTypeDefaultDescription
keystringrequiredEnv var name injected on execution (e.g., "MAX_RESULTS")
labelstringrequiredDisplay label in the settings form
descriptionstring""Help text
fieldTypestring"string""string", "number", "boolean", or "select"
defaultstring--Default value
requiredboolfalseWhether the user must set this field
secretboolfalseIf true, value is stored with AES-256-GCM encryption
optionsstring[]--Available choices for "select" type
Activation gate: A plugin will not activate until all required: true config fields have been set by the user. The UI auto-generates a settings form from configSchema -- users fill it in under Settings > Plugins.

Permissions

Plugins can declare a permissions manifest that controls environment variable access, network needs, and execution limits. These are enforced at exec time.

{
  "permissions": {
    "envAllow": ["HOME", "PATH", "WORKSPACE_ID"],
    "envDeny": ["AWS_SECRET_ACCESS_KEY", "GITHUB_TOKEN"],
    "network": true,
    "maxTimeoutSeconds": 300
  }
}

Permissions fields

FieldTypeDefaultDescription
envAllowstring[][] (all allowed)Env vars the plugin may read. Empty means all are allowed.
envDenystring[][]Env vars always stripped before execution (security blocklist)
networkboolfalseWhether the plugin needs network access
maxTimeoutSecondsnumber300Hard cap on any single execution in seconds

Common plugin patterns

These patterns show how capabilities combine to form different plugin types.

Connector plugin

Wraps a SaaS API. Declares auth for OAuth/API key login, tools[] for API operations, events[] for webhook-driven watches, and configSchema[] for user settings.

{
  "id": "asana",
  "slug": "asana",
  "capabilities": {
    "tools": [
      { "name": "asana.list-tasks", "description": "List tasks", "command": "tasks list" }
    ],
    "configSchema": [
      { "key": "WORKSPACE_ID", "label": "Workspace", "fieldType": "string", "required": true }
    ]
  },
  "auth": { "type": "oauth_cli", "label": "Asana account", "commands": { "login": "auth login", "status": "doctor" } },
  "events": [
    { "name": "task.created", "description": "New task created", "command": "watch tasks" }
  ]
}

Provider plugin

Adds an AI model backend. Declares providers[] with commands for listing models and streaming chat.

{
  "id": "openrouter",
  "slug": "openrouter",
  "capabilities": {
    "providers": [
      {
        "id": "openrouter",
        "displayName": "OpenRouter",
        "providerType": "model",
        "modelsCommand": "models list",
        "chatCommand": "chat stream",
        "authCommand": "auth setup"
      }
    ]
  }
}

Hook plugin

Intercepts agent lifecycle events. Declares hooks[] to filter or observe tool calls, messages, memory, or prompts.

{
  "id": "content-filter",
  "slug": "content-filter",
  "capabilities": {
    "hooks": [
      {
        "hook": "message.pre_send",
        "hookType": "filter",
        "priority": 10,
        "command": "filter message",
        "timeoutMs": 200
      },
      {
        "hook": "tool.pre_execute",
        "hookType": "filter",
        "priority": 50,
        "command": "filter tool-input",
        "timeoutMs": 500
      }
    ]
  }
}

Utility plugin

Shared binary that other plugins or skills depend on. No capabilities of its own -- just a binary distributed via env var.

{
  "id": "ffmpeg",
  "slug": "ffmpeg",
  "platforms": {
    "darwin-arm64": { "binaryName": "ffmpeg", "sha256": "...", "size": 50000000 }
  }
}