ツール形式アダプター
MCP モジュールでは、MCP サーバーが提供するツールを、各 LLM プロバイダーが要求する形式に変換する必要があります。この処理は次の 2 つのコンポーネントが担当します。
- ToolsLoader — MCPTool を OpenAI/Anthropic/Gemini などのプロバイダー向けツール定義形式に変換します
- McpToolAdapter — MCPTool を TinyElf エンジンの ITool インターフェース実装としてラップします
ToolsLoader
ファイルパス: packages/desktop/app/main/services/capabilities/tools/mcp-users/ToolsLoader.ts
責務
ToolsLoader は、MCP ツールの読み込みと形式変換の完全なパイプラインを提供します。
graph LR
fetchMcpTools --> MCPTool_Array["MCPTool[]"]
MCPTool_Array --> convertOpenAI["convertMcpToolsToOpenAI"]
MCPTool_Array --> convertAnthropic["convertMcpToolsToAnthropic"]
MCPTool_Array --> convertGemini["convertMcpToolsToGemini"]
convertOpenAI --> OpenAI_Format["OpenAITool[]"]
convertAnthropic --> Anthropic_Format["AnthropicTool[]"]
convertGemini --> Gemini_Format["GeminiTools"]
エントリ関数: setupMcpTools
async function setupMcpTools(
mcpService: McpService,
config?: {
enabled?: boolean;
mode?: 'auto' | 'manual';
selectedServers?: string[];
},
apiFormat?: 'openai' | 'anthropic' | 'google'
): Promise<{ tools: OpenAITool[] | AnthropicTool[] | GeminiTools; mcpTools: MCPTool[] } | undefined>
実行フロー:
fetchMcpTools()を呼び出して生の MCPTool リストを取得します- 利用可能なツールがない場合は
undefinedを返します - サーバーごとにグループ化してログを出力します
apiFormatに応じた変換関数を呼び出します- 変換済みツール定義と生の MCPTool リストを返します
fetchMcpTools — ツールの取得
async function fetchMcpTools(
mcpService: McpService,
config?: { enabled?: boolean; mode?: 'auto' | 'manual'; selectedServers?: string[] }
): Promise<MCPTool[]>
設定に基づいて読み込み戦略を決定します。
| 条件 | 動作 |
|---|---|
enabled が false または未設定 | 空配列を返す |
mode が auto または未設定 | mcpService.listAllActiveServerTools() を呼び出す |
mode が manual | selectedServers を反復し、各サーバーに対して mcpService.listServerTools() を呼び出す |
出力形式の比較
OpenAI 形式
interface OpenAITool {
type: 'function';
function: {
name: string; // MCPTool.id
description: string; // MCPTool.description
parameters: { // 処理済み JSON Schema
type: 'object';
properties: Record<string, any>;
required: string[];
};
};
}
Anthropic 形式
interface AnthropicTool {
name: string; // MCPTool.id
description: string; // MCPTool.description
input_schema: { // 処理済み JSON Schema
type: 'object';
properties: Record<string, any>;
required: string[];
};
}
Gemini 形式
type GeminiTools = Array<{
functionDeclarations: Array<{
name: string; // MCPTool.id
description: string; // MCPTool.description
parameters: { // 処理済み JSON Schema
type: 'object';
properties: Record<string, any>;
required: string[];
};
}>;
}>;
Gemini 形式の特徴: すべてのツールは functionDeclarations 配列にラップされ、その配列がさらに外側の配列でラップされます。
processJsonSchema — Schema 処理
function processJsonSchema(schema: any): any
JSON Schema が各種 LLM API と互換性を持つようにします。
- schema が空、またはオブジェクトでない場合は、デフォルトの空 object schema を返します
- すでに
type: 'object'を持つ場合は、properties、required、descriptionを抽出します - それ以外の場合は object 型としてラップします
McpToolAdapter
ファイルパス: packages/desktop/app/main/services/agent-core/engine/tinyelf/tools/McpToolAdapter.ts
責務
MCPTool を TinyElf エンジン向けの ITool インターフェース実装としてラップし、TinyElf が MCP ツールを直接呼び出せるようにします。
ITool インターフェースのマッピング
class McpToolAdapter implements ITool {
readonly name: string; // ← MCPTool.id
readonly description: string; // ← MCPTool.description
readonly parameters: JsonSchema; // ← MCPTool.inputSchema
constructor(
private mcpTool: MCPTool,
private mcpService: McpService
);
async execute(params: Record<string, unknown>): Promise<string>;
}
execute メソッド
sequenceDiagram
participant TinyElf
participant Adapter as McpToolAdapter
participant Service as McpService
participant Server as MCP Server
TinyElf->>Adapter: execute(params)
Adapter->>Adapter: Generate callId
Adapter->>Service: callTool(serverId, toolName, args, callId)
Note over Adapter,Service: Promise.race([callTool, timeout(120s)])
Service->>Server: client.callTool()
Server-->>Service: MCPCallToolResponse
Service-->>Adapter: { isError, content[] }
Adapter->>Adapter: flattenContent(content)
Adapter-->>TinyElf: String result
タイムアウト処理: Promise.race() を使って 120 秒のタイムアウトを実装します。これは ShellTool のタイムアウトと同じです。
flattenContent — コンテンツのフラット化
MCPToolResponseContent[] を単一の文字列に変換します。
function flattenContent(content: MCPToolResponseContent[]): string
| コンテンツ種別 | 変換 |
|---|---|
text | c.text をそのまま使用 |
image | [Image: ${mimeType}] プレースホルダーを出力 |
audio | [Audio: ${mimeType}] プレースホルダーを出力 |
複数のコンテンツ項目は \n で結合されます。
loadMcpTools — バッチ読み込み
async function loadMcpTools(
mcpService: McpService,
serverIds: string[]
): Promise<McpToolAdapter[]>
サーバー ID のリストを反復し、各サーバーに対して listServerTools() を呼び出し、各 MCPTool を McpToolAdapter としてラップします。個別のサーバーで失敗しても、他のサーバーには影響しません。
DirectMcpToolAdapter
用途: 組み込み MCP サーバー(Agent プリセット内のローカル MCP サービスなど)で使用します。McpService を経由せず、Client 参照を直接保持します。
McpToolAdapter との違い
| 機能 | McpToolAdapter | DirectMcpToolAdapter |
|---|---|---|
| 接続管理 | McpService に委譲 | Client 参照を直接保持 |
| ユースケース | ユーザー設定の MCP サーバー | 組み込み/プリセット MCP サーバー |
| ライフサイクル | McpService に従う | セッション単位 |
| ツール名形式 | mcp__serverName__toolName | serverName__toolName |
| プロセス追跡 | 不要 | McpProcessTracker 経由 |
loadDirectMcpTools — 直接接続の読み込み
async function loadDirectMcpTools(
servers: ResolvedMcpServer[]
): Promise<{
tools: DirectMcpToolAdapter[];
connections: DirectMcpConnection[];
}>
実行フロー:
- 各サーバーに対して
StdioClientTransportを作成します - MCP
Clientを作成して接続します - PID 追跡のため
McpProcessTrackerに登録します - ツールを検出し、
DirectMcpToolAdapterインスタンスを作成します - セッション終了時のクリーンアップ用に、ツールリストと接続ハンドルを返します
McpProcessTracker
ファイルパス: packages/desktop/app/main/services/agent-core/engine/tinyelf/tools/McpProcessTracker.ts
責務
DirectMcpToolAdapter が作成した stdio 子プロセスの PID を追跡するグローバルシングルトンです。異常終了時でもクリーンアップされるようにします。
安全レイヤー
| レイヤー | メソッド | 説明 |
|---|---|---|
| 正常終了時のクリーンアップ | closeConnection() | 正常に閉じる(3 秒タイムアウト)→ 強制 kill |
| バッチクリーンアップ | closeAll() | 追跡中の全接続を閉じる |
| セーフティネット | process.on('exit') | プロセス終了時にすべての PID を同期的に強制 kill |
使い方
// Track
McpProcessTracker.getInstance().track(connection);
// Cleanup single
await McpProcessTracker.getInstance().closeConnection(connection);
// Cleanup all
await McpProcessTracker.getInstance().closeAll();
統合ポイント
CompletionService との統合
CompletionService は各チャットリクエストの前に setupMcpTools() で MCP ツールを読み込みます。
const mcpResult = await setupMcpTools(
this.mcpService,
{ enabled: true, mode: 'auto' },
'openai' // Choose based on current provider
);
if (mcpResult) {
requestPayload.tools = mcpResult.tools;
}
TinyElf エンジンとの統合
TinyElf エンジンは loadMcpTools() と loadDirectMcpTools() でツールを読み込みます。
// User-configured MCP servers
const mcpTools = await loadMcpTools(mcpService, serverIds);
// Built-in MCP servers
const { tools: directTools, connections } = await loadDirectMcpTools(builtinServers);
// Register with ToolRegistry
for (const tool of [...mcpTools, ...directTools]) {
toolRegistry.register(tool);
}
関連ファイル
- 接続プール管理 — McpService の接続詳細
- Worker アーキテクチャ — Worker アーキテクチャ
- モジュール概要 — 全体アーキテクチャ