Introduction
Plugin System allows third-party to extend the platform. MCP (Model Context Protocol) is the new standard for AI tool interoperability — xClaw implements both MCP server (expose tools) and MCP client (consume external tools).
1. Plugin Architecture
// packages/core/src/plugins/types.ts
export interface Plugin {
id: string;
name: string;
version: string;
description: string;
// Lifecycle hooks
onLoad(context: PluginContext): Promise<void>;
onUnload(): Promise<void>;
// What plugin provides
tools?: AdditionalTool[];
skills?: SkillDefinition[];
middleware?: MiddlewareFunction[];
routes?: RouteDefinition[];
}
export interface PluginContext {
config: Record<string, unknown>;
logger: Logger;
eventBus: EventBus;
registerTool(def: ToolDefinition, handler: ToolHandler): void;
registerSkill(skill: SkillDefinition): void;
}
Plugin Manager
// packages/core/src/plugins/plugin-manager.ts
export class PluginManager {
private plugins = new Map<string, Plugin>();
private context: PluginContext;
async loadPlugin(plugin: Plugin): Promise<void> {
if (this.plugins.has(plugin.id)) {
throw new Error(`Plugin ${plugin.id} already loaded`);
}
// Initialize plugin
await plugin.onLoad(this.context);
// Register tools
if (plugin.tools) {
for (const tool of plugin.tools) {
this.context.registerTool(tool.definition, tool.handler);
}
}
// Register skills
if (plugin.skills) {
for (const skill of plugin.skills) {
this.context.registerSkill(skill);
}
}
this.plugins.set(plugin.id, plugin);
console.log(`Plugin loaded: ${plugin.name} v${plugin.version}`);
}
async unloadPlugin(pluginId: string): Promise<void> {
const plugin = this.plugins.get(pluginId);
if (!plugin) return;
await plugin.onUnload();
this.plugins.delete(pluginId);
console.log(`Plugin unloaded: ${plugin.name}`);
}
listPlugins(): { id: string; name: string; version: string }[] {
return Array.from(this.plugins.values()).map(p => ({
id: p.id,
name: p.name,
version: p.version,
}));
}
}
2. Plugin example: GitHub Integration
// packages/integrations/src/github-plugin.ts
export const githubPlugin: Plugin = {
id: 'github',
name: 'GitHub Integration',
version: '1.0.0',
description: 'GitHub issues, PRs, and repository management',
async onLoad(ctx: PluginContext) {
const token = ctx.config.githubToken as string;
if (!token) throw new Error('GitHub token required');
},
async onUnload() {},
tools: [
{
definition: {
name: 'github_list_issues',
description: 'List issues in a GitHub repository',
parameters: {
type: 'object',
properties: {
repo: { type: 'string', description: 'owner/repo format' },
state: { type: 'string', enum: ['open', 'closed', 'all'] },
},
required: ['repo'],
},
},
handler: async (args) => {
const { repo, state = 'open' } = args as { repo: string; state?: string };
const response = await fetch(
`https://api.github.com/repos/${repo}/issues?state=${state}`,
{ headers: { Authorization: `token ${process.env.GITHUB_TOKEN}` } },
);
return response.json();
},
},
{
definition: {
name: 'github_create_issue',
description: 'Create a new issue',
parameters: {
type: 'object',
properties: {
repo: { type: 'string', description: 'owner/repo' },
title: { type: 'string', description: 'Issue title' },
body: { type: 'string', description: 'Issue body' },
},
required: ['repo', 'title'],
},
},
handler: async (args) => {
const { repo, title, body } = args as { repo: string; title: string; body?: string };
const response = await fetch(
`https://api.github.com/repos/${repo}/issues`,
{
method: 'POST',
headers: {
Authorization: `token ${process.env.GITHUB_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ title, body }),
},
);
return response.json();
},
},
],
};
3. MCP Server Implementation
// packages/core/src/mcp/mcp-server.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
export class MCPServer {
private server: Server;
private toolRegistry: ToolRegistry;
constructor(toolRegistry: ToolRegistry) {
this.toolRegistry = toolRegistry;
this.server = new Server(
{ name: 'xclaw-mcp', version: '1.0.0' },
{ capabilities: { tools: {} } },
);
this.setupHandlers();
}
private setupHandlers() {
// List available tools
this.server.setRequestHandler('tools/list', async () => {
const tools = this.toolRegistry.getDefinitions();
return {
tools: tools.map(t => ({
name: t.name,
description: t.description,
inputSchema: t.parameters,
})),
};
});
// Execute a tool
this.server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params;
const result = await this.toolRegistry.execute(
name,
args as Record<string, unknown>,
{ tenantId: 'mcp', userId: 'mcp', sessionId: 'mcp' },
);
return {
content: [{
type: 'text',
text: JSON.stringify(result.result),
}],
isError: !result.success,
};
});
}
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.log('MCP Server started on stdio');
}
}
4. MCP Client — Consume External Tools
// packages/core/src/mcp/mcp-client.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
export class MCPClient {
private client: Client;
async connect(command: string, args: string[]) {
const transport = new StdioClientTransport({ command, args });
this.client = new Client({ name: 'xclaw', version: '1.0.0' });
await this.client.connect(transport);
}
// Import external MCP tools into xClaw's ToolRegistry
async importTools(registry: ToolRegistry) {
const { tools } = await this.client.listTools();
for (const tool of tools) {
registry.register(
{
name: `mcp_${tool.name}`,
description: tool.description || '',
parameters: tool.inputSchema as ToolDefinition['parameters'],
},
async (args) => {
const result = await this.client.callTool({
name: tool.name,
arguments: args,
});
return result.content;
},
);
}
console.log(`Imported ${tools.length} MCP tools`);
}
}
5. Summary
- Plugin System — lifecycle hooks (load/unload), provides tools + skills + routes
- MCP Server — expose xClaw tools to Claude Desktop, Cursor, etc.
- MCP Client — import external MCP tools into xClaw
- Bidirectional — xClaw is both an MCP server and an MCP client
Next article: Multi-tenant RBAC — Isolated tenants with granular permissions.