Create Your First Plugin
This guide walks you through building a complete plugin from scratch — a simple “Greeting” tool that generates personalized greetings.
Create the package
Every plugin is a standalone package in the packages/ directory:
- package.json
- pandora.manifest.json
- index.ts
- greet.ts
- tsconfig.json
Start with package.json. The SDK is a dev dependency — only type-only imports are used at runtime:
{
"name": "@pandorakit/greeting",
"version": "0.1.0",
"type": "module",
"exports": { ".": "./src/index.ts" },
"devDependencies": {
"@pandorakit/sdk": "*"
}
}And a minimal tsconfig.json:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noEmit": true
},
"include": ["src"]
}Write the manifest
The pandora.manifest.json tells Pandora what your plugin is, what it provides, and what it needs:
{
"manifestVersion": 1,
"id": "@pandorakit/greeting",
"name": "Greeting",
"description": "Generate personalized greetings in different styles",
"author": "You",
"version": "0.1.0",
"license": "MIT",
"pandora": ">=0.0.1",
"provides": {
"tools": {
"entry": "./src/index.ts",
"sandbox": "compartment",
"permissions": {
"time": true
}
}
},
"configFields": [
{
"key": "defaultStyle",
"label": "Default Style",
"type": "enum",
"description": "The greeting style to use when none is specified",
"options": [
{ "value": "formal", "label": "Formal" },
{ "value": "casual", "label": "Casual" },
{ "value": "enthusiastic", "label": "Enthusiastic" }
]
}
]
}idmatches the npm package name — becomes the namespace prefix for all toolsprovides.toolspoints to the entry module and declares the sandbox modepermissions.timegrants access toDatefor time-appropriate greetingsconfigFieldsdefines settings the user can configure on the Plugins page
See the full manifest reference for all available fields.
Define the tool
A tool is a plain object — metadata, JSON Schema parameters, and an execute function:
// src/greet.ts
import type { Tool } from '@pandorakit/sdk/tools'
export const greetTool: Tool = {
id: 'greet',
name: 'Greet',
description: 'Generate a personalized greeting for someone',
parameters: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'The name of the person to greet',
},
style: {
type: 'string',
enum: ['formal', 'casual', 'enthusiastic'],
description: 'Greeting style (defaults to plugin config)',
},
},
required: ['name'],
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
},
execute: async (input: { name: string; style?: string }, { logger }) => {
const style = input.style || 'casual'
const hour = new Date().getHours()
let timeOfDay = 'Hello'
if (hour < 12) timeOfDay = 'Good morning'
else if (hour < 18) timeOfDay = 'Good afternoon'
else timeOfDay = 'Good evening'
logger.log(`Generating ${style} greeting for ${input.name}`)
const greetings = {
formal: `${timeOfDay}, ${input.name}. How may I assist you today?`,
casual: `Hey ${input.name}! ${timeOfDay}.`,
enthusiastic: `${timeOfDay}, ${input.name}!!! So great to see you! 🎉`,
}
return { greeting: greetings[style as keyof typeof greetings] }
},
}Export the tools
The entry point exports a named tools array:
// src/index.ts
import { greetTool } from './greet'
export const tools = [greetTool]Pandora registers each tool with a namespaced ID — in this case, @pandorakit/greeting:greet.
Install and run
bun install
bun run devOpen the web UI, go to the Plugins page, and toggle your “Greeting” plugin on.
Try it
Go to Chat and ask:
“Greet Alice in a formal style”
The agent will call your greet tool and return the personalized greeting.
What’s Next
- Tools — sandbox, dynamic resolution, and full type reference
- Agents — create specialist sub-agents
- Channels — connect to messaging platforms
- Plugin Manifest — full manifest reference