Plugin Developer Guide

Main Entry Point

Your plugin's index.ts is the single file Kazibee loads at runtime. What you export from here becomes the tools an AI agent can use.

Quick reminder: How plugins work

Your plugin is not a standalone app. Kazibee loads it, calls your main(env) function, and takes whatever object you return. Each property on that object becomes a tool method that an AI agent can call. Think of it like registering API endpoints — except instead of HTTP routes, you are registering functions that an AI can invoke.

The big picture

Here is exactly what happens when an AI agent runs your plugin:

1.

Kazibee reads your package.json to find "main": "./src/index.ts"

2.

It imports your index.ts and grabs the default export (your main function)

3.

It calls main(env) with the sandboxed environment variables your plugin declared in permissions.json

4.

Whatever object you return becomes tools["your-plugin"] — each property is a method the AI can call

Start here: The simplest plugin

If your plugin does not need any credentials or API keys, you can ignore the env parameter entirely. Just export a default function that returns an object of methods.

my-plugin/src/index.ts

export default function main() {

return {

greet: async (name: string) => {

return `Hello, ${name}!`;

},

add: async (a: number, b: number) => {

return a + b;

}

};

}

That is a complete plugin. When an AI agent loads it, the agent can call:

What the AI agent sees

await tools["my-plugin"].greet("world") // "Hello, world!"

await tools["my-plugin"].add(2, 3) // 5

The plugin name in tools["my-plugin"] comes from the name you used when you installed or linked the plugin with Kazibee. Each method name (greet, add) is whatever key you put on the returned object.

What is the env parameter?

Most real plugins need credentials — an API key, OAuth tokens, or some secret. Your main function receives these as the env parameter.

This is not the same as process.env. Plugins run inside a sandbox. The env object only contains the specific variables you declared in your permissions.json that the user has granted. Nothing more.

Sandbox isolation

Your plugin cannot access process.env, the filesystem, or anything on the host machine. The env parameter is the only way your plugin receives external data. If you declared CLIENT_ID in permissions.json, then env.CLIENT_ID is the only way to get it.

Real-world pattern: Plugin with authentication

In practice, most plugins follow a consistent three-file pattern. Here is how a Gmail plugin does it — this is based on real production code.

Step 1 — Define a typed Env interface in auth.ts

Create an Env interface that matches the variable names in your permissions.json. This gives you type safety — TypeScript will catch misspelled variable names at compile time. The createAuthClient function validates the credentials and creates an authenticated OAuth2 client.

gmail/src/auth.ts

import { auth } from '@googleapis/gmail';

 

export interface Env {

CLIENT_ID: string;

CLIENT_SECRET: string;

REFRESH_TOKEN: string;

}

 

export function createAuthClient(env: Env) {

const oauth2 = new auth.OAuth2(

env.CLIENT_ID,

env.CLIENT_SECRET

);

oauth2.setCredentials({

refresh_token: env.REFRESH_TOKEN

});

return oauth2;

}

Step 2 — Use it in index.ts

Your main(env) imports the Env type for safety, creates an authenticated client, and returns the plugin's methods. The returned object's methods become the tools.

gmail/src/index.ts

import { createAuthClient, type Env } from './auth';

import { createGmailClient } from './gmail-client';

 

// Re-export Env so Kazibee can use it for type generation

export type { Env } from './auth';

 

export default function main(env: Env) {

const auth = createAuthClient(env);

return createGmailClient(auth);

}

That is the entire entry point. The createGmailClient function returns an object like { listMessages, sendMessage, getMessage, ... }, and each of those becomes a tool the AI agent can call via tools["gmail"].listMessages().

What you return matters

The object you return from main() has a direct, concrete effect. Here is how the mapping works:

Your index.ts

export default function main(env: Env) {

return {

getUser: async () => { /* ... */ },

listItems: async (query: string) => { /* ... */ },

createItem: async (data: ItemInput) => { /* ... */ }

};

}

What the AI agent can call

await tools["my-plugin"].getUser()

await tools["my-plugin"].listItems("active")

await tools["my-plugin"].createItem({ name: "New item" })

Keep your method signatures clear and well-typed. The AI agent reads your TypeScript types to understand what arguments each method expects and what it returns.

Trying it out

Once your plugin is linked, you can test it immediately from the terminal:

kazibee-terminal

dev@local:~/workspace $ echo 'return await tools["my-plugin"].greet("world")' | kazibee exec

Hello, world!

The kazibee exec command loads all installed plugins, calls each plugin's main(env), and makes the returned methods available as tools. Your code runs inside that context.

Re-exporting the Env type

You may have noticed this line in the Gmail example:

my-plugin/src/index.ts

export type { Env } from './auth';

This re-exports the Env interface from your entry point so Kazibee can read it for type generation. The pattern is: define Env in auth.ts (so your auth logic has access to it), then re-export it from index.ts (so Kazibee can find it).

The pattern is always the same

Whether your plugin uses an API key, OAuth tokens, or no auth at all, the entry point structure is identical: import auth helper, import client factory, wire them together in main(). The only thing that changes is what variables are in env.

Common mistakes

If your plugin is not working, check these first:

Forgetting export default

Kazibee looks for the default export of your module. A named export like export function main() will not work. It must be export default function main().

Returning a class instead of an object

Kazibee expects a plain object with method properties. If you return new MyClient(), the class instance's methods may not be enumerable. Return a plain object: { getUser: async () => ... }.

Trying to access process.env

This will not work in the sandbox. All credentials must come through the env parameter. If you need a variable, declare it in permissions.json and read it from env.

Forgetting to make methods async

The AI agent calls your methods with await. If your methods are not async and they throw, the error handling may behave unexpectedly. Use async for all tool methods, even if the implementation is synchronous.

Env variable names do not match

The keys in your Env interface must exactly match the keys in permissions.json. If permissions.json says "CLIENT_ID" but your Env interface says clientId, you will get undefined.

Recap

1. Your index.ts must have a export default function main(env)

2. env contains only the variables from permissions.json that the user granted (not process.env)

3. Return a plain object — each property becomes a tool method the AI can call

4. Define your Env type in auth.ts and re-export it from index.ts

5. Use async for all tool methods

Kazibee

Bounded AI execution. Free and open source.