はじめに
プラグイン システムにより、サードパーティがプラットフォームを拡張できます。 MCP (モデル コンテキスト プロトコル) は、AI ツールの相互運用性の新しい標準です。xClaw は、MCP サーバー (ツールを公開) と MCP クライアント (外部ツールを使用) の両方を実装します。
1. プラグインのアーキテクチャ
// 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;
}
プラグインマネージャー
// 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. プラグインの例: GitHub 統合
// 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 サーバーの実装
// 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 クライアント — 外部ツールの使用
// 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. まとめ
- プラグイン システム — ライフサイクル フック (ロード/アンロード)、ツール + スキル + ルートを提供します
- MCP サーバー — xClaw ツールを Claude Desktop、Cursor などに公開します。
- MCP クライアント — 外部 MCP ツールを xClaw にインポートします
- 双方向 — xClaw は MCP サーバーと MCP クライアントの両方です
次の記事: マルチテナント RBAC — きめ細かい権限を持つ分離されたテナント。