「MCP、Function Calling、API直接呼び出し——結局どれを使えばいい?」
AIエージェントの設計を始めると、ほぼ必ずこの問いにぶつかる。そして困ったことに、どれも「使えるケース」があり、どれも「使うべきでないケース」がある。
10社以上のAIエージェント導入を支援してきた経験から言うと、この判断を間違えると後から大きなリファクタリングが発生する。逆に最初に正しく選択できると、開発速度が劇的に上がる。
この記事では、3つのツール連携パターンを実際のコード例とともに比較し、状況別の使い分けを具体的に示す。
3つのパターンの概要
| パターン | 何を解決するか | 主な採用場面 | 難易度 |
|---|---|---|---|
| Function Calling | LLMからAPI呼び出しを構造化する | 小規模・単一プロバイダー | 低 |
| MCP | ツール定義をプロバイダー横断で標準化 | マルチモデル・スケール展開 | 中 |
| API直接呼び出し | AIを介さない確定的な処理 | 固定ワークフロー・高頻度処理 | 低〜中 |
パターン1: Function Calling(関数呼び出し)
何をするパターンか
LLMに「使えるツールの定義(JSON Schema)」を渡し、LLMが必要に応じてそのツールを呼び出す。OpenAIが普及させたアプローチで、AnthropicのTool Use、GoogleのFunction Declarationsもこの概念の実装だ。
最小実装コード
# OpenAI Function Calling 最小実装
# 動作環境: Python 3.11+, openai>=1.30.0
# pip install openai
from openai import OpenAI
import json
client = OpenAI()
# ツール定義(JSON Schema形式)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "指定した都市の現在の天気を取得する",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "都市名(例: Tokyo)"}
},
"required": ["city"]
}
}
}
]
# ツールの実際の処理(自前で実装)
def get_weather(city: str) -> str:
# 実際はAPIを呼ぶ
return f"{city}の天気: 晴れ、気温23°C"
# LLMにメッセージを送信
messages = [{"role": "user", "content": "東京の天気を教えて"}]
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
)
# LLMがツール呼び出しを判断した場合、実行して結果を返す
if response.choices[0].message.tool_calls:
tool_call = response.choices[0].message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
result = get_weather(**args)
# 結果をLLMに戻す
messages.append(response.choices[0].message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
})
final_response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
)
print(final_response.choices[0].message.content)
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
最終確認日: 2026-04-11
Function Callingが適切な場面
- ツールが5個以下で固定されている
- 単一のLLMプロバイダーしか使わない
- プロトタイプや初期実装
- チームがAI統合に不慣れで、最もシンプルな形から始めたい
Function Callingが合わない場面
- 複数のLLMプロバイダーを使う(OpenAI→Claude切り替えなど)
- ツールが10個を超える
- 同じツールを複数のAIアプリケーションで共有したい
パターン2: MCP(Model Context Protocol)
何をするパターンか
ツールの定義と実行をMCPサーバーとして独立させる。AIアプリケーション(MCPクライアント)は統一されたプロトコルでサーバーに接続し、利用可能なツールを動的に発見して使う。プロバイダー間の差異がなくなり、「1つのサーバー → 複数のAIアプリ」が成立する。
MCPサーバー実装コード
# MCP サーバー実装(天気ツールをMCPサーバーとして公開)
# 動作環境: Python 3.11+, mcp>=1.0
# pip install mcp
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import asyncio
server = Server("weather-service")
@server.list_tools()
async def list_tools():
"""利用可能なツールを定義(クライアントが動的に発見できる)"""
return [
Tool(
name="get_weather",
description="指定した都市の現在の天気を取得する",
inputSchema={
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "都市名(例: Tokyo)"
}
},
"required": ["city"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
"""ツールの実際の処理"""
if name == "get_weather":
city = arguments["city"]
# 実際はAPIを呼ぶ
return [TextContent(type="text", text=f"{city}の天気: 晴れ、気温23°C")]
raise ValueError(f"未知のツール: {name}")
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
このMCPサーバーは、Claude Desktop、Claude Code、OpenAI互換クライアントなど、MCP対応の全クライアントから同じように呼び出せる。
MCPの設定例(Claude Desktop向け)
// claude_desktop_config.json
{
"mcpServers": {
"weather-service": {
"command": "python",
"args": ["/path/to/weather_server.py"],
"env": {
"WEATHER_API_KEY": "your_api_key_here"
}
}
}
}
最終確認日: 2026-04-11(MCP仕様 2026-03-26版)
MCPが適切な場面
- 複数のAIアプリから同じツールを使いたい(共有ツールライブラリ)
- 将来的にLLMプロバイダーを変える可能性がある
- 社内ツールを組織全体でAIに繋ぎたい
- 5,800本以上の既存サーバーを活用したい(開発コスト最小化)
MCPが合わない場面
- サーバーセットアップのオーバーヘッドが大きすぎる極小プロジェクト
- 高頻度処理(MCPのプロトコルオーバーヘッドが影響する場合)
パターン3: API直接呼び出し
何をするパターンか
AIを介さず、アプリケーションが直接APIを呼び出す。AIは「いつ、どのAPIを呼ぶか」を判断するが、実際の呼び出しはAIではなくアプリケーションコードが行う。
これはFunction CallingやMCPを否定するパターンではない。「AIの判断が不要な処理」はAPIを直接呼べばいい、というシンプルな考え方だ。
実装コード
# ハイブリッドパターン: LLMが判断、アプリが実行
# 動作環境: Python 3.11+, anthropic>=0.30.0
# pip install anthropic requests
import anthropic
import requests
import json
client = anthropic.Anthropic()
def execute_order(product_id: str, quantity: int, user_id: str) -> dict:
"""注文確定API(確定的処理 — LLMに実行させない)"""
# 実際は内部APIを呼ぶ
return {"order_id": f"ORD-{product_id}-001", "status": "confirmed"}
def get_product_info(product_id: str) -> dict:
"""商品情報取得(LLMが判断に使う情報取得)"""
# 実際はAPIを呼ぶ
return {"id": product_id, "name": "商品A", "price": 1980, "stock": 50}
# LLMには「推薦」だけをさせ、「実行」はアプリが担う
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
tools=[{
"name": "recommend_product",
"description": "ユーザーのニーズに合った商品IDを推薦する",
"input_schema": {
"type": "object",
"properties": {
"product_id": {"type": "string"},
"reason": {"type": "string"}
},
"required": ["product_id", "reason"]
}
}],
messages=[{
"role": "user",
"content": "予算2000円以内で家庭用品を探しています"
}]
)
# LLMの推薦を取得
if response.stop_reason == "tool_use":
tool_use = next(b for b in response.content if b.type == "tool_use")
recommendation = tool_use.input
# 商品情報を直接API取得(AIを介さない)
product = get_product_info(recommendation["product_id"])
# 在庫チェックもAPIで直接確認
if product["stock"] > 0 and product["price"] <= 2000:
# 注文確定はAIに任せず、アプリが直接実行
order = execute_order(
product_id=recommendation["product_id"],
quantity=1,
user_id="user_123"
)
print(f"注文完了: {order}")
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
最終確認日: 2026-04-11
API直接呼び出しが適切な場面
- 確定的な処理(決済、データ更新など)— AIに任せると不確実性が生まれる
- 高頻度処理(プロトコルオーバーヘッドを避けたい)
- AIが判断だけを担い、実行は既存システムに任せるアーキテクチャ
3パターンの使い分け判断フロー
AIがツールを使う場面
└─ 複数のLLMプロバイダーを使うか、または社内共有ツールが必要?
├─ Yes → [MCP] サーバーを構築して標準化する
└─ No
└─ ツール数は5個以下で固定されているか?
├─ Yes → [Function Calling] シンプルに実装する
└─ No → [MCP] 将来のスケールを考えMCPを選ぶ
ただし、確定的処理(決済・データ更新等)は必ずAPI直接呼び出しで。
よくある誤解と正しい理解
誤解1: MCPはFunction Callingの置き換えだ
✕ MCPがあればFunction Callingは不要
⭕ MCPはFunction Callingの概念を使っている。MCPサーバーが提供するツールも、内部的にはFunction Callingの仕組みで呼び出される
MCPはプロトコルの標準化レイヤーであり、Function Callingの代替ではなく、その上位概念だ。
誤解2: 全処理をAIに判断させるべきだ
✕ AIエージェントが全ての処理を判断・実行すべき
⭕ 確定的処理はAPIに任せ、AIは不確実性が価値を生む判断だけを担う
「AIがどこまで自律的に動くか」を意識的に設計することが、安全で予測可能なシステムを作る鍵だ。
誤解3: Function Callingは古い技術だから使うな
✕ MCPが標準になったのでFunction Callingは廃れた
⭕ 小規模プロジェクトや単一プロバイダー構成では、Function Callingがシンプルで正しい選択肢だ
MCPのセットアップコストを払う必要がない場面でMCPを選ぶのは、複雑さを増やすだけだ。
【要注意】設計でよくある失敗パターン
失敗1: 全処理をFunction Callingで実装し後からMCPに移行できない
❌ プロバイダー固有のFunction Calling構文を全コードに散布する
⭕ ツール実行ロジックを抽象化し、プロバイダー交換を可能にしておく
なぜ重要か: OpenAIのFunction CallingとAnthropicのTool Useは仕様が異なる。早期に抽象化しておかないと、プロバイダー変更時に全書き直しになる。
失敗2: MCPサーバーにビジネスロジックを詰め込む
❌ MCPサーバーの中でデータ変換・ルール適用・バリデーションを全部やる
⭕ MCPサーバーは薄いアダプター層として保ち、ビジネスロジックは既存サービスに委ねる
なぜ重要か: MCPサーバーが太くなると、テストが困難になり、保守性が下がる。MCPサーバーは「AIとサービスをつなぐ線」であり、「サービス本体」ではない。
失敗3: エラーハンドリングを後回しにする
❌ ツール呼び出しが失敗したときの処理を実装せずに本番デプロイする
⭕ タイムアウト、レート制限、APIエラーの全ケースを最初から設計する
なぜ重要か: AIエージェントがツール呼び出しで失敗したとき、エラー処理が不十分だとエージェントが無限ループに入ったり、誤った情報をユーザーに返したりする。
参考・出典
- Function Calling vs. MCP vs. A2A: Developer’s Guide — Zilliz Blog(参照日: 2026-04-11)
- Tool Calling Explained: The Core of AI Agents — Composio(参照日: 2026-04-11)
- MCP vs API: When to Use Each for AI Agent Integration — Atlan(参照日: 2026-04-11)
- Model Context Protocol (MCP) — OpenAI Agents SDK公式ドキュメント(参照日: 2026-04-11)
まとめ:今日から始める3つのアクション
- 今日: 現在のプロジェクトで使っているツール連携方法をリストアップし、「Function Calling」「MCP」「API直接呼び出し」のどれで実装しているかを分類する
- 今週中: 最もスケールが難しそうなツール統合を1つ選び、MCPサーバーに移行するプロトタイプを作る
- 今月中: チームのAIエージェント設計ガイドラインとして、3パターンの使い分けルールを文書化してCLAUDE.mdかWikiに追記する
AIエージェントのツール設計全体を学びたい方は、AIエージェント構築完全ガイドで体系的に解説しています。
あわせて読みたい:
- MCP完全ガイド|AIエージェントのツール連携を標準化する — MCPの概念から実装まで
- FastMCPでPython MCPサーバーを作る — より実践的なMCPサーバー構築方法
著者: 佐藤傑(さとう・すぐる)
株式会社Uravation代表取締役。早稲田大学法学部在学中に生成AIの可能性に魅了され、X(旧Twitter)で活用法を発信(@SuguruKun_ai、フォロワー10万人超)。100社以上の企業向けAI研修・導入支援を展開。著書累計3万部突破。