メインコンテンツまでスキップ

ツール形式アダプター

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>

実行フロー:

  1. fetchMcpTools() を呼び出して生の MCPTool リストを取得します
  2. 利用可能なツールがない場合は undefined を返します
  3. サーバーごとにグループ化してログを出力します
  4. apiFormat に応じた変換関数を呼び出します
  5. 変換済みツール定義と生の MCPTool リストを返します

fetchMcpTools — ツールの取得

async function fetchMcpTools(
mcpService: McpService,
config?: { enabled?: boolean; mode?: 'auto' | 'manual'; selectedServers?: string[] }
): Promise<MCPTool[]>

設定に基づいて読み込み戦略を決定します。

条件動作
enabledfalse または未設定空配列を返す
modeauto または未設定mcpService.listAllActiveServerTools() を呼び出す
modemanualselectedServers を反復し、各サーバーに対して 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' を持つ場合は、propertiesrequireddescription を抽出します
  • それ以外の場合は 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
コンテンツ種別変換
textc.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 との違い

機能McpToolAdapterDirectMcpToolAdapter
接続管理McpService に委譲Client 参照を直接保持
ユースケースユーザー設定の MCP サーバー組み込み/プリセット MCP サーバー
ライフサイクルMcpService に従うセッション単位
ツール名形式mcp__serverName__toolNameserverName__toolName
プロセス追跡不要McpProcessTracker 経由

loadDirectMcpTools — 直接接続の読み込み

async function loadDirectMcpTools(
servers: ResolvedMcpServer[]
): Promise<{
tools: DirectMcpToolAdapter[];
connections: DirectMcpConnection[];
}>

実行フロー:

  1. 各サーバーに対して StdioClientTransport を作成します
  2. MCP Client を作成して接続します
  3. PID 追跡のため McpProcessTracker に登録します
  4. ツールを検出し、DirectMcpToolAdapter インスタンスを作成します
  5. セッション終了時のクリーンアップ用に、ツールリストと接続ハンドルを返します

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);
}

関連ファイル