Core
Plugin System
Every tool in Conductor is a plugin. 40+ built-in plugins cover developer workflows, communication, productivity, finance, and system monitoring. Write your own in a single JavaScript file.
The Plugin Interface
A plugin is a class implementing the Plugin interface. Plugins are lazily initialized — the first time an AI calls any tool from a plugin, initialize() is called with the full Conductor instance, giving access to config, database, AI manager, and other plugins.
interface Plugin {
name: string;
description: string;
version: string;
// Called once on first use. Receives the Conductor instance for
// access to config, database, AI manager, and other plugins.
initialize(conductor: Conductor): Promise<void>;
// Return false to show as "not_configured" in plugin list.
// Tools from unconfigured plugins are not registered with MCP.
isConfigured(): boolean;
// The tools this plugin exposes to the MCP server.
getTools(): PluginTool[];
// Optional: guided setup via `conductor plugins setup <name>`
configSchema?: PluginConfigSchema;
// Optional: called each reasoning cycle for proactive context.
// Return a string to inject into the AI's system prompt.
getContext?(): Promise<string | null>;
// Optional: cleanup on plugin disable or server shutdown.
onUnload?(): Promise<void>;
}
interface PluginTool {
name: string;
description: string;
inputSchema: JSONSchema7;
// Set true for irreversible operations (delete, send, publish).
// The MCP client will prompt the user before execution.
requiresApproval?: boolean;
handler(
args: unknown,
conductor: Conductor
): Promise<ToolResult>;
}Plugin Lifecycle
Plugins move through these states. A plugin in not_configured or init_failed state does not expose tools to MCP clients.
registeredPlugin class is registered with PluginManager. Not yet initialized.
not_configuredinitialize() succeeded but isConfigured() returns false. Tools are not registered with MCP.
readyInitialized and configured. All tools are active and callable.
init_failedinitialize() threw an error. Plugin is shown in startup summary with the error message.
disabledManually disabled via CLI. Not initialized, not shown to MCP clients.
Run conductor mcp start to see a startup summary showing each plugin's state. Failed plugins are shown with the error message so you can diagnose credential or network issues immediately.
Built-in Plugins
37 built-in plugins ship with Conductor. Plugins marked zero-config work immediately with no API keys or setup. Click any plugin name to see its full tool reference.
Utilities
| Plugin | Tools | Auth | Description |
|---|---|---|---|
| calculator | 3 | zero-config | Math expressions, unit conversions, percentages |
| hash | 5 | zero-config | MD5, SHA-1, SHA-256, SHA-512, bcrypt |
| text-tools | 6 | zero-config | Word count, JSON format, diff, encode/decode |
| colors | 4 | zero-config | Hex/RGB/HSL/name conversion |
| timezone | 3 | zero-config | Convert time between zones, list zones |
| url-tools | 4 | zero-config | Parse URLs, expand short links, encode/decode |
| cron | 2 | zero-config | Parse cron expressions, next run times |
| translate | 2 | zero-config | Text translation via LibreTranslate (free) |
Developer
| Plugin | Tools | Auth | Description |
|---|---|---|---|
| shell | 4 | zero-config | Safe shell command execution (whitelist-based) |
| github | 20 | partial | Issues, PRs, code search, releases, forks, notifications |
| github-actions | 6 | credentials | Trigger workflows, view runs, download artifacts |
| linear | 9 | credentials | Issues, cycles, projects, team management |
| jira | 8 | credentials | Issues, transitions, projects, sprints |
| vercel | 7 | credentials | Deployments, domains, environment variables |
| docker | 8 | zero-config | Containers, images, volumes, compose |
| n8n | 5 | credentials | Trigger workflows, list executions |
| figma | 5 | credentials | Files, components, comments, exports |
Communication
Productivity
| Plugin | Tools | Auth | Description |
|---|---|---|---|
| notion | 8 | credentials | Pages, databases, blocks, search |
| gcal | 6 | credentials | Events, calendars, availability, scheduling |
| gdrive | 6 | credentials | Files, folders, permissions, search |
| airtable | 6 | credentials | Records, tables, views, formulas |
| todoist | 5 | credentials | Tasks, projects, labels, filters |
| notes | 4 | zero-config | Local markdown notes in ~/.conductor/notes/ |
| memory | 3 | zero-config | Local semantic memory stored in SQLite |
Finance
System
Smart Home
| Plugin | Tools | Auth | Description |
|---|---|---|---|
| homekit | 6 | credentials | Control HomeKit devices, scenes, automations |
Media
| Plugin | Tools | Auth | Description |
|---|---|---|---|
| spotify | 7 | credentials | Playback, playlists, search, library |
Social
| Plugin | Tools | Auth | Description |
|---|---|---|---|
| x | 5 | credentials | Post, search, read timeline, DMs |
Managing Plugins
All plugin management is done through the CLI. Changes take effect immediately — no server restart required.
conductor plugins listconductor plugins enable <name>conductor plugins disable <name>conductor plugins setup <name>conductor plugins install <name>conductor plugins uninstall <name>conductor plugins onboardConfiguring Credentials
Plugins that require credentials define a configSchema. Running conductor plugins setup <name> walks through the fields interactively. Fields marked secret: true are stored in an encrypted local credential store (AES-256-GCM) — never in config.json.
$ conductor plugins setup github
? GitHub Personal Access Token: ghp_••••••••••••
? Default organization (optional): myorg
✓ Token stored in encrypted credential store
✓ github plugin configured — 20 tools now activeWriting a Plugin
Drop a .js file into ~/.conductor/plugins/ and enable it. The file must export a default class implementing the Plugin interface. Here is a complete, production-ready example:
// ~/.conductor/plugins/planetscale.js
// A complete plugin example with credentials, error handling, and multiple tools
export default class PlanetScalePlugin {
name = "planetscale";
description = "Query PlanetScale databases via the PS API";
version = "1.0.0";
#apiKey = null;
#orgName = null;
#baseUrl = "https://api.planetscale.com/v1";
configSchema = {
fields: [
{
key: "apiKey",
label: "Service Token",
type: "string",
secret: true, // stored in encrypted credential store
required: true,
hint: "Create at app.planetscale.com/settings/service-tokens",
},
{
key: "orgName",
label: "Organization name",
type: "string",
required: true,
},
],
};
async initialize(conductor) {
this.#apiKey = await conductor.config.getSecret("planetscale.apiKey");
this.#orgName = await conductor.config.get("planetscale.orgName");
this.logger = conductor.logger;
}
isConfigured() {
return !!this.#apiKey && !!this.#orgName;
}
getTools() {
return [
{
name: "planetscale_databases",
description: "List all databases in your organization",
inputSchema: { type: "object", properties: {} },
handler: async () => this.#listDatabases(),
},
{
name: "planetscale_branches",
description: "List branches for a database",
inputSchema: {
type: "object",
properties: {
database: { type: "string", description: "Database name" },
},
required: ["database"],
},
handler: async ({ database }) => this.#listBranches(database),
},
{
name: "planetscale_query",
description: "Run a read-only SQL query",
inputSchema: {
type: "object",
properties: {
database: { type: "string" },
branch: { type: "string", default: "main" },
sql: { type: "string", description: "SELECT statement" },
},
required: ["database", "sql"],
},
handler: async ({ database, branch = "main", sql }) => {
if (!sql.trim().toUpperCase().startsWith("SELECT")) {
throw new Error("Only SELECT queries are permitted");
}
return this.#runQuery(database, branch, sql);
},
},
];
}
async #request(path) {
const res = await fetch(`${this.#baseUrl}/${path}`, {
headers: {
Authorization: `Bearer ${this.#apiKey}`,
Accept: "application/json",
},
});
if (!res.ok) throw new Error(`PlanetScale API error: ${res.status}`);
return res.json();
}
async #listDatabases() {
const data = await this.#request(
`organizations/${this.#orgName}/databases`
);
return {
content: [{ type: "text", text: JSON.stringify(data.data, null, 2) }],
};
}
async #listBranches(database) {
const data = await this.#request(
`organizations/${this.#orgName}/databases/${database}/branches`
);
return {
content: [{ type: "text", text: JSON.stringify(data.data, null, 2) }],
};
}
async #runQuery(database, branch, sql) {
this.logger.info(`Querying ${database}/${branch}`);
// implementation omitted for brevity
return { content: [{ type: "text", text: "..." }] };
}
}Key patterns in the example above:
- configSchema — defines the credentials the user needs to provide; secret fields go to encrypted credential store
- initialize() — reads secrets from keychain; only runs once on first tool call
- isConfigured() — returns false until credentials are set, keeping tools out of MCP
- requiresApproval — set this to true on any tool that mutates state (write, delete, send)
- Private methods — use # private class fields for API request helpers
conductor plugins enable planetscale
conductor plugins setup planetscalePublishing to the Marketplace
Share your plugin with other Conductor users by publishing it to the marketplace. The marketplace is an npm registry namespace — publish your plugin as an npm package named @conductor-plugins/your-plugin-name.
# Publish to npm under the @conductor-plugins scope
npm publish --access public
# Users install your plugin with:
conductor plugins install planetscaleScaffold a new plugin with the correct package.json, tsconfig, and entry point using: conductor plugin-create