ツールは、サーバが実行可能な機能をクライアントに公開することを可能にする、Model Context Protocol (MCP) の強力なプリミティブです。ツールを通して、LLM は外部システムと相互作用し、計算を実行し、そして現実世界でアクションを実行できます。
Model Context Protocol (MCP) : コンセプト : ツール – 実装 & パターン例 / アノテーション
作成 : クラスキャット・セールスインフォメーション
作成日時 : 05/26/2025
* 本記事は github modelcontextprotocol の以下のページを独自に翻訳した上でまとめ直し、補足説明を加えています :
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
◆ お問合せ : 下記までお願いします。
- クラスキャット セールス・インフォメーション
- sales-info@classcat.com
- ClassCatJP
Model Context Protocol (MCP) : コンセプト : ツール – 実装 & パターン例 / アノテーション
LLM がサーバを通してアクションを実行できるようにします
ツールは、サーバが実行可能な機能をクライアントに公開することを可能にする、Model Context Protocol (MCP) の強力なプリミティブです。ツールを通して、LLM は外部システムと相互作用し、計算を実行し、そして現実世界でアクションを実行できます。
Note : ツールは、モデルにより制御可能 (model-controlled) であるように設計されています、つまりツールは、AI モデルが (承認を与えるため人間介在型で) 自動的にツールを呼び出せるような意図をもって、サーバからクライアントに公開されます。
概要
MCP のツールは、サーバが実行可能な関数を公開することを可能にします、これらの関数はクライアントにより呼び出され、LLM により使用されてアクションを実行できます。ツールの主要な観点は :
- 検出 (Discovery) : クライアントは tools/list エンドポイント経由で利用可能なツールをリストアップできます。
- 呼び出し (Invocation) : ツールは tools/call エンドポイントを使用して呼び出されます、そこではサーバはリクエストされた操作を実行して結果を返します。
- 柔軟性 (Flexibility) : ツールは単純な計算から複雑な API インタラクションまで多岐に渡ります。
リソース のように、ツールは一意の名前で識別され、使用方法を案内するための説明を含めることができます。但し、リソースとは異なり、ツールは状態を変更したり外部システムと相互作用可能な動的操作を表します。
ツール定義の構造
各ツールは以下の構造で定義されます :
{
name: string; // Unique identifier for the tool
description?: string; // Human-readable description
inputSchema: { // JSON Schema for the tool's parameters
type: "object",
properties: { ... } // Tool-specific parameters
},
annotations?: { // Optional hints about tool behavior
title?: string; // Human-readable title for the tool
readOnlyHint?: boolean; // If true, the tool does not modify its environment
destructiveHint?: boolean; // If true, the tool may perform destructive updates
idempotentHint?: boolean; // If true, repeated calls with same args have no additional effect
openWorldHint?: boolean; // If true, tool interacts with external entities
}
}
ツールの実装
MCP サーバで基本的なツールを実装するサンプルが以下です :
TypeScript
const server = new Server({
name: "example-server",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: "calculate_sum",
description: "Add two numbers together",
inputSchema: {
type: "object",
properties: {
a: { type: "number" },
b: { type: "number" }
},
required: ["a", "b"]
}
}]
};
});
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "calculate_sum") {
const { a, b } = request.params.arguments;
return {
content: [
{
type: "text",
text: String(a + b)
}
]
};
}
throw new Error("Tool not found");
});
Python
app = Server("example-server")
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="calculate_sum",
description="Add two numbers together",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"}
},
"required": ["a", "b"]
}
)
]
@app.call_tool()
async def call_tool(
name: str,
arguments: dict
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
if name == "calculate_sum":
a = arguments["a"]
b = arguments["b"]
result = a + b
return [types.TextContent(type="text", text=str(result))]
raise ValueError(f"Tool not found: {name}")
ツールパターンの例
サーバが提供できるツールの種類の幾つかの例が以下です :
システム操作
ローカルシステムと相互作用するツール :
{
name: "execute_command",
description: "Run a shell command",
inputSchema: {
type: "object",
properties: {
command: { type: "string" },
args: { type: "array", items: { type: "string" } }
}
}
}
API 統合
外部 API をラップするツール :
{
name: "github_create_issue",
description: "Create a GitHub issue",
inputSchema: {
type: "object",
properties: {
title: { type: "string" },
body: { type: "string" },
labels: { type: "array", items: { type: "string" } }
}
}
}
データ処理
データを変換や分析するツール :
{
name: "analyze_csv",
description: "Analyze a CSV file",
inputSchema: {
type: "object",
properties: {
filepath: { type: "string" },
operations: {
type: "array",
items: {
enum: ["sum", "average", "count"]
}
}
}
}
}
ベスト・プラクティス
ツールを実装する際には :
- 明確で、説明的な名前と説明を提供する
- パラメータ用に詳細な JSON スキーマ定義を使用する
- ツール説明に例を含めてモデルが使用する方法を示す
- 適切なエラー処理と検証を実装する
- 長時間の操作の進捗レポートを使用する
- ツール操作に焦点を当てて単一機能 (atomic) に保つ
- 想定される戻り値の構造を文書化する
- 適切なタイムアウトの実装
- リソース集約的な操作にはレート制限を考慮する
- デバッグと監視用にツール使用量をログ記録する
セキュリティの考慮事項
ツールを公開する際 :
入力の検証
- スキーマに対してすべてのパラメータを検証する
- ファイルパスとシステムコマンドをサニタイズする
- URL と外部識別子を検証する
- パラメータサイズと範囲を確認する
- コマンドインジェクションを防止する
アクセス制御
- 必要に応じて認証を実装する
- 適切な認可 (authorization) チェックを使用する
- ツールの使用状況を監査する (audit)
- レート制限リクエスト
- 不正利用 (abuse) を監視する
エラー処理
- 内部エラーをクライアントに公開しない
- セキュリティ関連エラーをログ記録する
- タイムアウトを適切に処理する
- エラー後にリソースをクリーンアップする
- 戻り値を検証する
ツールの検出と更新
MCP は動的ツール検出をサポートします :
- クライアントは利用可能なツールをいつでもリストアップできます
- サーバは、notifications/tools/list_changed を使用してツールが変更された場合、クライアントに通知できます
- ツールは実行中に追加または削除できます。
- ツール定義は更新できます (注意深く行われる必要がありますが)
エラー処理
ツールのエラーは MCP プロトコルレベルのエラーとしてではなく、結果 (result) オブジェクト内で報告する必要があります。これは LLM がエラーを確認して場合によっては処理することを可能にします。ツールがエラーに遭遇する場合 :
- 結果で isError を true に設定する
- content 配列にエラーの詳細を含める
Here’s an example of proper error handling for tools:
TypeScript
try {
// Tool operation
const result = performOperation();
return {
content: [
{
type: "text",
text: `Operation successful: ${result}`
}
]
};
} catch (error) {
return {
isError: true,
content: [
{
type: "text",
text: `Error: ${error.message}`
}
]
};
}
Python
try:
# Tool operation
result = perform_operation()
return types.CallToolResult(
content=[
types.TextContent(
type="text",
text=f"Operation successful: {result}"
)
]
)
except Exception as error:
return types.CallToolResult(
isError=True,
content=[
types.TextContent(
type="text",
text=f"Error: {str(error)}"
)
]
)
このアプローチは、LLM がエラーが発生したことを確認し、場合によっては是正措置を取ったり人間の介入をリクエストすることを可能にします。
ツール・アノテーション
ツール・アノテーションはツールの動作に関する追加のメタデータを提供し、クライアントがツールを提示して管理する方法を理解するのに役立ちます。これらのアノテーションはツールの性質と影響を説明するヒントですが、セキュリティ上の判断のために依存するべきではありません。
ツール・アノテーションの目的
ツール・アノテーションは幾つかの主要な目的を果たします :
- モデルコンテキストに影響を与えることなく UX 固有の情報を提供します。
- クライアントがツールを適切に分類して提示するのに役立ちます。
- ツールの潜在的な副作用についての情報を伝えます。
- ツール承認のための直感的なインターフェイスを開発することを支援する。
利用可能なツール・アノテーション
MCP 仕様はツール用に以下のアノテーションを定義しています :
アノテーション | タイプ | デフォルト | 説明 |
---|---|---|---|
title | string | – | ツールのための可読なタイトル、UI 表示に有用です |
readOnlyHint | boolean | false | true の場合、ツールは環境を変更しないことを示します |
destructiveHint | boolean | true | true の場合、ツールは破壊的な更新を実行する可能性があります (readOnlyHint が false の場合だけ意味があります) |
idempotentHint | boolean | false | true の場合、同じ引数でツールを繰り返し呼び出しても、追加の効果はありません (readOnlyHint が false の場合だけ意味があります) |
openWorldHint | boolean | true | true の場合、ツールは外部エンティティの “open world” と相互作用する可能性があります |
使用例
様々なシナリオのためにアノテーション付きのツールを定義する方法は以下です :
TypeScript
// A read-only search tool
{
name: "web_search",
description: "Search the web for information",
inputSchema: {
type: "object",
properties: {
query: { type: "string" }
},
required: ["query"]
},
annotations: {
title: "Web Search",
readOnlyHint: true,
openWorldHint: true
}
}
// A destructive file deletion tool
{
name: "delete_file",
description: "Delete a file from the filesystem",
inputSchema: {
type: "object",
properties: {
path: { type: "string" }
},
required: ["path"]
},
annotations: {
title: "Delete File",
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
openWorldHint: false
}
}
// A non-destructive database record creation tool
{
name: "create_record",
description: "Create a new record in the database",
inputSchema: {
type: "object",
properties: {
table: { type: "string" },
data: { type: "object" }
},
required: ["table", "data"]
},
annotations: {
title: "Create Database Record",
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: false
}
}
サーバ実装へのアノテーションの統合
TypeScript
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: "calculate_sum",
description: "Add two numbers together",
inputSchema: {
type: "object",
properties: {
a: { type: "number" },
b: { type: "number" }
},
required: ["a", "b"]
},
annotations: {
title: "Calculate Sum",
readOnlyHint: true,
openWorldHint: false
}
}]
};
});
Python
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("example-server")
@mcp.tool(
annotations={
"title": "Calculate Sum",
"readOnlyHint": True,
"openWorldHint": False
}
)
async def calculate_sum(a: float, b: float) -> str:
"""Add two numbers together.
Args:
a: First number to add
b: Second number to add
"""
result = a + b
return str(result)
以上