Creating a Plugin
This guide walks through creating a plugin from scratch — from scaffolding the directory to registering tools, mounting routes, and subscribing to events.
Scaffold
bash
# 1. Create the plugin directory
mkdir -p plugins/core/my-plugin
# 2. Create the entry point
touch plugins/core/my-plugin/index.ts
# 3. Restart the gateway (or let hot-reload pick it up in dev)Minimal plugin
The simplest possible plugin — just a name, version, and init function.
typescript
import type { GatewayPlugin } from '../../types.js'
const plugin: GatewayPlugin = {
name: 'hello-world',
version: '0.1.0',
init(ctx) {
ctx.logger.info('Hello world plugin loaded')
}
}
export default pluginPlugin that registers a tool
Tools registered by plugins are available to all agents (subject to the agent's tools.allow / tools.deny configuration).
typescript
import type { GatewayPlugin } from '../../types.js'
const plugin: GatewayPlugin = {
name: 'weather',
version: '1.0.0',
init(ctx) {
ctx.registerTool({
name: 'get_weather',
description: 'Get current weather for a city',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: 'City name' }
},
required: ['city']
},
execute: async (args) => {
const response = await fetch(
`https://api.weather.example/v1/current?city=${args.city}`
)
return await response.json()
}
})
}
}
export default pluginTool naming. Use
snake_case for tool names to match the convention used by built-in tools. The agent sees the tool name in its schema, so keep it descriptive and concise. Plugin with custom routes and events
This example tracks agent completions via the event bus and exposes an analytics endpoint.
typescript
import type { GatewayPlugin } from '../../types.js'
const plugin: GatewayPlugin = {
name: 'analytics',
version: '1.0.0',
init(ctx) {
const bus = ctx.getEventBus()
const metrics = { totalCalls: 0, byAgent: {} as Record<string, number> }
bus.on('agent.completed', (event) => {
metrics.totalCalls++
metrics.byAgent[event.agentId] = (metrics.byAgent[event.agentId] || 0) + 1
})
ctx.app.get('/api/analytics/summary', (req, res) => {
res.json(metrics)
})
}
}
export default pluginError handling
text
// Plugin errors during init() are caught and logged
// The gateway continues loading other plugins
// Event: "plugin.error" is emitted with the error details
// Best practices:
// - Validate config in init() and throw early with clear messages
// - Clean up resources in stop() (timers, connections, file handles)
// - Use ctx.logger instead of console.log for consistent formatting
// - Never throw in start() or stop() — wrap in try/catchPlugin checklist
| Requirement | Details |
|---|---|
| Directory | plugins/core/<name>/index.ts |
| Export | Default export implementing GatewayPlugin |
name | Unique string identifier |
version | Semver string |
init(ctx) | Required — receives PluginContext |
start() | Optional — called after HTTP server is ready |
stop() | Optional — called on graceful shutdown (cleanup) |
Related
- Plugin Overview — lifecycle, discovery, and context reference
- Creating a Tool — detailed tool registration reference
- Creating a Skill — skills bundle tools with prompt context
- Event System — all events available for subscription