Skip to main content

Availability

EditionDeployment Type
Community & EnterpriseSelf-Managed, Hybrid
Plugin manifests define plugin metadata, capabilities, permissions, and UI integration. Understanding manifests is essential for building secure, well-integrated plugins.

Manifest Structure

Edge Gateway Plugins

Edge Gateway plugins don’t use JSON manifests. Configuration is provided via the API when creating the plugin:
{
  "name": "Custom Auth Plugin",
  "slug": "custom-auth",
  "description": "Custom authentication logic",
  "command": "file:///path/to/plugin",
  "hook_type": "auth",
  "plugin_type": "gateway",
  "config": {
    "valid_token": "secret123"
  },
  "is_active": true
}
Hook types for Edge Gateway plugins:
  • pre_auth - Before authentication
  • auth - Custom authentication
  • post_auth - After authentication
  • on_response - Response modification
  • data_collection - Data export

AI Studio UI Plugins

Complete manifest structure for UI plugins:
Expandable
{
  "id": "com.example.plugin",
  "name": "My Plugin",
  "version": "1.0.0",
  "description": "Plugin description",
  "plugin_type": "ai_studio",
  "permissions": {
    "services": [
      "llms.read",
      "llms.write",
      "llms.proxy",
      "tools.read",
      "tools.execute",
      "tools.write",
      "datasources.read",
      "datasources.query",
      "apps.read",
      "apps.write",
      "plugins.read",
      "analytics.read",
      "kv.read",
      "kv.readwrite"
    ]
  },
  "ui": {
    "slots": [
      {
        "slot": "sidebar.section",
        "label": "My Plugin",
        "icon": "/assets/icon.svg",
        "items": [
          {
            "type": "route",
            "path": "/admin/my-plugin",
            "title": "Dashboard",
            "mount": {
              "kind": "webc",
              "tag": "my-plugin-dashboard",
              "entry": "/ui/webc/dashboard.js",
              "props": {
                "apiBase": "/plugin/com.example.plugin/rpc"
              }
            }
          }
        ]
      }
    ]
  },
  "rpc": {
    "basePath": "/plugin/com.example.plugin/rpc"
  },
  "assets": [
    "/assets/icon.svg",
    "/ui/webc/dashboard.js"
  ],
  "compat": {
    "app": ">=2.6 <3.0",
    "api": ["ui-v1", "kv-v1", "rpc-v1"]
  },
  "security": {
    "csp": "script-src 'self'; object-src 'none'"
  }
}

AI Studio Agent Plugins

Agent plugin manifests are simpler (no UI):
Expandable
{
  "id": "com.example.agent",
  "name": "My Agent",
  "version": "1.0.0",
  "description": "Custom conversational agent",
  "plugin_type": "agent",
  "permissions": {
    "services": [
      "llms.proxy",
      "tools.execute",
      "datasources.query",
      "kv.readwrite"
    ]
  },
  "ui": {
    "slots": []
  }
}

Object Hooks Plugins

Object hooks plugins intercept CRUD operations on platform objects. These plugins use the unified SDK and register hooks programmatically, so the manifest doesn’t need special hook configuration.

Basic Object Hooks Manifest

Expandable
{
  "id": "com.example.validator",
  "name": "LLM Validator",
  "version": "1.0.0",
  "description": "Validates LLM configurations before saving",
  "plugin_type": "ai_studio",
  "permissions": {
    "services": [
      "llms.read",
      "kv.readwrite"
    ]
  },
  "ui": {
    "slots": []
  }
}
Note: Object hooks are registered via the GetObjectHookRegistrations() method in the plugin code, not in the manifest. The plugin_type is "ai_studio" since object hooks are Studio-only.

Hook Registration (Code, Not Manifest)

Object hooks are registered programmatically:
Expandable
func (p *ValidatorPlugin) GetObjectHookRegistrations() ([]*pb.ObjectHookRegistration, error) {
    return []*pb.ObjectHookRegistration{
        {
            ObjectType: "llm",               // llm, datasource, tool, user
            HookTypes:  []string{            // before_create, after_create, etc.
                "before_create",
                "before_update",
            },
            Priority: 10,                    // Lower runs first
        },
        {
            ObjectType: "datasource",
            HookTypes:  []string{"before_create"},
            Priority:   5,                   // Runs before priority 10
        },
    }, nil
}
Supported Objects:
  • llm - LLM provider configurations
  • datasource - Data source connections
  • tool - External tool definitions
  • user - User accounts
Supported Hook Types (per object):
  • before_create - Before object creation (can block)
  • after_create - After object creation (notification only)
  • before_update - Before object update (can block)
  • after_update - After object update (notification only)
  • before_delete - Before object deletion (can block)
  • after_delete - After object deletion (notification only)
Priority: Lower numbers run first (e.g., priority 5 runs before priority 10). Use priority to control hook execution order when multiple plugins register hooks for the same object/event.

Multi-Capability with Hooks

Object hooks plugins can combine hooks with UI:
Expandable
{
  "id": "com.example.approval-system",
  "name": "Approval System",
  "version": "1.0.0",
  "description": "Requires approval for datasource creation",
  "plugin_type": "ai_studio",
  "permissions": {
    "services": [
      "datasources.read",
      "kv.readwrite"
    ]
  },
  "ui": {
    "slots": [
      {
        "slot": "sidebar.section",
        "label": "Approvals",
        "items": [
          {
            "type": "route",
            "path": "/admin/approvals",
            "title": "Pending Approvals",
            "mount": {
              "kind": "webc",
              "tag": "approval-dashboard",
              "entry": "/ui/webc/approvals.js"
            }
          }
        ]
      }
    ]
  },
  "rpc": {
    "basePath": "/plugin/com.example.approval-system/rpc"
  }
}
This plugin would:
  1. Register before_create hooks for datasources (via code)
  2. Block creation and add to pending approvals
  3. Provide UI dashboard to approve/reject
  4. Use RPC methods for approval actions
See examples/plugins/studio/llm-validator/ and examples/plugins/studio/hook-test-plugin/ for complete examples.

Resource Provider Plugins

Resource Provider plugins register custom resource types that integrate into the App creation flow. Declare resource types in the manifest’s resource_types section:
Expandable
{
  "id": "com.example.mcp-registry",
  "name": "MCP Registry",
  "version": "1.0.0",
  "description": "Manages MCP server resources",
  "capabilities": {
    "hooks": ["resource_provider", "studio_ui"]
  },
  "resource_types": [
    {
      "slug": "mcp_servers",
      "name": "MCP Servers",
      "description": "Model Context Protocol servers",
      "icon": "Hub",
      "has_privacy_score": true,
      "supports_submissions": true,
      "form_component": {
        "tag": "mcp-server-selector",
        "entry_point": "ui/webc/mcp-selector.js"
      }
    }
  ],
  "permissions": {
    "services": ["apps.read", "kv.readwrite"]
  }
}
Resource types declared in the manifest are automatically registered when the plugin loads. The plugin also implements ResourceProvider methods programmatically for runtime behavior.
FieldRequiredDescription
slugYesMachine-readable identifier, unique per plugin
nameYesDisplay name shown in the App form
has_privacy_scoreNoWhether instances carry privacy scores (default: false)
supports_submissionsNoWhether community users can submit instances (default: false)
form_componentNoCustom Web Component for the App form selector
See Resource Provider Plugins for the full guide.

Service Scopes Reference

LLM Scopes

ScopeDescriptionOperations
llms.readRead LLM configurationsList LLMs, Get LLM details, Get LLM counts
llms.writeCreate/update LLMsCreate LLM, Update LLM, Delete LLM
llms.proxyCall LLMs via proxyCallLLM (streaming/non-streaming)

Tool Scopes

ScopeDescriptionOperations
tools.readRead tool configurationsList Tools, Get Tool details
tools.executeExecute toolsExecuteTool with operation and parameters
tools.writeCreate/update toolsCreate Tool, Update Tool, Delete Tool
tools.operationsManage tool operationsAdd/remove operations

Datasource Scopes

ScopeDescriptionOperations
datasources.readRead datasource configurationsList Datasources, Get Datasource details
datasources.queryQuery datasourcesQueryDatasource with SQL/query DSL
datasources.writeCreate/update datasourcesCreate, Update, Delete Datasources

App Scopes

ScopeDescriptionOperations
apps.readRead app configurationsList Apps, Get App details
apps.writeCreate/update appsCreate App, Update App, Delete App

Plugin Scopes

ScopeDescriptionOperations
plugins.readRead plugin configurationsList Plugins, Get Plugin details, Get counts
plugins.writeCreate/update pluginsCreate Plugin, Update Plugin

KV Storage Scopes

ScopeDescriptionOperations
kv.readRead plugin KV storageReadPluginKV, ListPluginKVKeys
kv.readwriteRead/write plugin KV storageRead + WritePluginKV, DeletePluginKV

Analytics Scopes

ScopeDescriptionOperations
analytics.readRead analytics dataGetUsageStats, GetCostAnalytics
analytics.writeWrite analytics dataRecordCustomMetric

UI Slot System

Available Slots

Add a collapsible section to the sidebar with nested items:
Expandable
{
  "slot": "sidebar.section",
  "label": "My Plugin",
  "icon": "/assets/icon.svg",
  "items": [
    {
      "type": "route",
      "path": "/admin/my-plugin/dashboard",
      "title": "Dashboard",
      "mount": { }
    },
    {
      "type": "route",
      "path": "/admin/my-plugin/settings",
      "title": "Settings",
      "mount": { }
    }
  ]
}
Add a single link to the sidebar:
{
  "slot": "sidebar.link",
  "label": "Quick Action",
  "icon": "/assets/icon.svg",
  "path": "/admin/quick-action"
}

settings.section

Add a section to the Settings page:
{
  "slot": "settings.section",
  "label": "Plugin Settings",
  "path": "/admin/settings/plugin",
  "mount": {
    "kind": "webc",
    "tag": "plugin-settings",
    "entry": "/ui/webc/settings.js"
  }
}

app.detail.tab

Add a tab to App detail pages:
{
  "slot": "app.detail.tab",
  "label": "Custom View",
  "mount": {
    "kind": "webc",
    "tag": "app-custom-view",
    "entry": "/ui/webc/app-view.js",
    "props": {
      "appId": "{{app.id}}"
    }
  }
}

llm.detail.tab

Add a tab to LLM detail pages:
{
  "slot": "llm.detail.tab",
  "label": "Analytics",
  "mount": {
    "kind": "webc",
    "tag": "llm-analytics",
    "entry": "/ui/webc/llm-analytics.js",
    "props": {
      "llmId": "{{llm.id}}"
    }
  }
}

Mount Configuration

WebComponent Mount

{
  "kind": "webc",
  "tag": "my-component",
  "entry": "/ui/webc/component.js",
  "props": {
    "apiBase": "/plugin/com.example/rpc",
    "theme": "dark"
  }
}
  • kind: Must be "webc" for WebComponents
  • tag: Custom element tag name
  • entry: Path to JavaScript file (relative to plugin)
  • props: Properties passed to the component

Permission Validation

Permissions are validated when plugins call the Service API:
// This call requires "llms.read" scope
llmsResp, err := ai_studio_sdk.ListLLMs(ctx, 1, 10)
if err != nil {
    // Error: insufficient permissions
    log.Printf("Permission denied: %v", err)
}
If your plugin doesn’t declare the required scope in its manifest, Service API calls will fail with permission errors.

Configuration Schema

Plugins can provide JSON Schema for their configuration:
Expandable
func (p *MyPlugin) GetConfigSchema() ([]byte, error) {
    schema := map[string]interface{}{
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type":    "object",
        "title":   "My Plugin Configuration",
        "properties": map[string]interface{}{
            "api_key": map[string]interface{}{
                "type":        "string",
                "description": "API key for external service",
                "minLength":   1,
            },
            "endpoint": map[string]interface{}{
                "type":        "string",
                "format":      "uri",
                "description": "Service endpoint URL",
                "default":     "https://api.example.com",
            },
            "rate_limit": map[string]interface{}{
                "type":        "integer",
                "description": "Requests per minute",
                "minimum":     1,
                "maximum":     1000,
                "default":     100,
            },
            "enabled": map[string]interface{}{
                "type":        "boolean",
                "description": "Enable plugin",
                "default":     true,
            },
        },
        "required": []string{"api_key"},
    }

    return json.Marshal(schema)
}
The platform uses this schema to:
  • Validate configuration on save
  • Generate UI forms
  • Provide inline documentation
  • Set default values

Security Best Practices

Principle of Least Privilege

Only request scopes your plugin actually needs:
{
  "permissions": {
    "services": [
      "llms.read",      // ✅ Need to list LLMs
      "kv.readwrite"    // ✅ Need to store settings
      // ❌ Don't add "llms.write" if not creating LLMs
      // ❌ Don't add "llms.proxy" if not calling LLMs
    ]
  }
}

Content Security Policy

Define CSP headers for UI plugins:
{
  "security": {
    "csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; object-src 'none'; frame-ancestors 'none'"
  }
}

Input Validation

Always validate inputs in RPC methods:
Expandable
func (p *MyPlugin) HandleCall(method string, payload []byte) ([]byte, error) {
    // Validate method
    allowedMethods := map[string]bool{
        "get_data":    true,
        "save_config": true,
    }

    if !allowedMethods[method] {
        return nil, fmt.Errorf("invalid method: %s", method)
    }

    // Validate payload
    var data map[string]interface{}
    if err := json.Unmarshal(payload, &data); err != nil {
        return nil, fmt.Errorf("invalid payload: %w", err)
    }

    // Additional validation...
    return p.processMethod(method, data)
}

Secrets Management

Never hardcode secrets in manifests or code:
// ❌ Bad: Hardcoded secret
func (p *MyPlugin) OnInitialize(...) error {
    p.apiKey = "secret123"  // Don't do this!
}

// ✅ Good: Configuration from secure storage
func (p *MyPlugin) OnInitialize(...) error {
    // Read from config (stored securely by platform)
    config, _ := ai_studio_sdk.ReadPluginKV(ctx, "config")
    var cfg Config
    json.Unmarshal(config, &cfg)
    p.apiKey = cfg.APIKey
}

Versioning and Compatibility

Semantic Versioning

Use semantic versioning for plugin versions:
{
  "version": "1.2.3"  // MAJOR.MINOR.PATCH
}
  • MAJOR: Breaking changes
  • MINOR: New features, backward compatible
  • PATCH: Bug fixes, backward compatible

Compatibility Declaration

Declare platform compatibility:
{
  "compat": {
    "app": ">=2.6 <3.0",
    "api": ["ui-v1", "kv-v1", "rpc-v1"]
  }
}

Testing Manifests

Validation

Validate your manifest before deployment:
# Check JSON syntax
cat plugin.manifest.json | jq .

# Validate required fields
jq '.id, .name, .version, .plugin_type' plugin.manifest.json

Common Errors

ErrorCauseSolution
”Invalid plugin_type”Wrong plugin typeUse "ai_studio" or "agent"
”Missing required field”Missing manifest fieldAdd id, name, version
”Invalid scope”Unknown service scopeCheck scope name spelling
”Duplicate UI slot”Slot registered twiceRemove duplicate slot
”Asset not found”Missing embedded fileVerify //go:embed directive

Complete Examples

Minimal UI Plugin

Expandable
{
  "id": "com.example.minimal",
  "name": "Minimal Plugin",
  "version": "1.0.0",
  "plugin_type": "ai_studio",
  "permissions": {
    "services": ["kv.readwrite"]
  },
  "ui": {
    "slots": [
      {
        "slot": "sidebar.link",
        "label": "Minimal",
        "path": "/admin/minimal"
      }
    ]
  }
}
See plugins-studio-ui.md for complete rate-limiting-ui example.

Minimal Agent Plugin

{
  "id": "com.example.simple-agent",
  "name": "Simple Agent",
  "version": "1.0.0",
  "plugin_type": "agent",
  "permissions": {
    "services": ["llms.proxy"]
  },
  "ui": {
    "slots": []
  }
}

Advanced Agent Plugin

Expandable
{
  "id": "com.example.rag-agent",
  "name": "RAG Agent",
  "version": "1.0.0",
  "description": "Agent with retrieval-augmented generation",
  "plugin_type": "agent",
  "permissions": {
    "services": [
      "llms.proxy",
      "datasources.query",
      "tools.execute",
      "kv.readwrite",
      "analytics.read"
    ]
  },
  "ui": {
    "slots": []
  }
}