MCPサーバをLangChain/LangGraphで使う2種類の方法
1. はじめに
1.1 目的
Anthropic社が2024年12月末に公開したMCP(Model Context Protocol)の仕様を元に、
世に沢山のMCPサーバのプログラムが公開されています。
最近は、Cursorで代表されるAIエディタ(WindsurfやCline)でも利用できるようになり、
エコシステムが拡大していると感じます。
そんな中、LangChain/LangGraphもMCPサーバに対応するためのAdaputerが公開されたので、
2種類の実装方法を試してみました。
1.2. 読者対象と前提知識
- MCPサーバを利用されている方
- LangChain/LangGraphの知識がある方
2. langchain-mcp-adapters
2.1 langchain-mcp-adaptersとは
langchain-mcp-adaptersのgithubを読むと、2つのことができると記述されています。
特徴:
🛠️ MCP ツールをLangGraphエージェントで使用できるLangChain ツールに変換する
📦 複数の MCP サーバーに接続し、そこからツールをロードできるクライアント実装
Github上のREADMEにサンプルコードが記述されているので、簡単に試すことが出来ます。
2.2 利用するMCPサーバ
複数のMCPサーバが動作するか試すのに、以下の2種類のMCPサーバを利用します。
-
現在時刻を取得することができるMCPサーバ
https://github.com/SecretiveShell/MCP-timeserver -
Tavilyを使ってWeb検索できるMCPサーバ
https://github.com/Tomatio13/mcp-server-tavily
2は、自分で作ったMCPサーバなので使い方を理解しているのもありますが。
2.3 動作確認
REAMEを参考に以下のソースファイルを作成し、実行させて見ました。
※実行する前に、MCPサーバへのパスは修正してください。
# Create server parameters for stdio connection
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
from langchain_core.messages import AIMessage
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI
import os
import asyncio
async def main():
x_api_key = os.environ["GROK_API_KEY"]
base_url = 'https://api.x.ai/v1'
model = ChatOpenAI(model="grok-2-latest",
base_url=base_url, api_key=x_api_key)
async with MultiServerMCPClient() as client:
# Tavily検索サーバーに接続
await client.connect_to_server(
"tavily",
command="uv",
args=[
"--directory",
"/path/to/mcp-server-tavily",
"run",
"tavily-search"
],
env={
"TAVILY_API_KEY": os.getenv("TAVILY_API_KEY"),
"PYTHONIOENCODING": "utf-8"
}
)
await client.connect_to_server(
"timeserver",
command="uvx",
args=["--directory","/path/to/mcp-timeserver","mcp-timeserver"],
)
# エージェントの作成と実行
agent = create_react_agent(model, client.get_tools())
# メッセージの履歴を表示
messages = []
agent_response = await agent.ainvoke(
{"messages":
"今日の日付を調べてから、来週末の鎌倉のイベントを調べて詳細を教えてください"})
for message in agent_response["messages"]:
if hasattr(message, 'content') and isinstance(message, AIMessage):
messages.append(message.content)
# すべての回答を表示
print("\n".join(messages))
if __name__ == "__main__":
asyncio.run(main())
実行すると、以下のように検索して調べてくれています。
2025-02-23 11:09:24,065 - tavily-search-server - INFO - Starting Tavily search server
2025-02-23 11:09:24,067 - tavily-search-server - INFO - Server initialized, starting main loop
2025-02-23 11:09:24,071 - mcp.server - INFO - Processing request of type ListToolsRequest
2025-02-23 11:09:24,071 - tavily-search-server - INFO - Listing available tools
2025-02-23 11:09:27,540 - mcp.server - INFO - Processing request of type CallToolRequest
2025-02-23 11:09:27,540 - tavily-search-server - INFO - TOOL_CALL_DEBUG: Tool called - name: search, arguments: {'query': '来週末 鎌倉 イベント', 'search_depth': 'advanced'}
2025-02-23 11:09:27,540 - tavily-search-server - INFO - Executing search with query: '来週末 鎌倉 イベント'
2025-02-23 11:09:31,015 - httpx - INFO - HTTP Request: POST https://api.tavily.com/search "HTTP/1.1 200 OK"
2025-02-23 11:09:31,015 - tavily-search-server - INFO - Search successful - Answer generated
2025-02-23 11:09:31,015 - tavily-search-server - INFO - Search successful - Results available
I am calling the `get-current-time` function to find out today's date.
今日は2025年2月23日です。来週末の鎌倉のイベントについて調べますね。
今日は2025年2月23日です。来週末の鎌倉のイベントについて調べた結果、以下のイベントがあります:
- **デジタルスタンプラリーイベント「#秘密のえのかま」**:2025年2月22日から3月23日まで開催。
- **湘南美術学院「基礎科祭2025」-10代が創る、未来のアート-**:2025年3月1日から2日まで開催。
- **三浦海岸桜まつり**:2025年2月5日から3月2日まで開催。桜の開花状況により変更の可能性あり。
- **桜ウェルカムドーム**:2025年3月1日から4月14日まで開催。
- **横浜ストロベリーフェスティバル**:2025年2月6日から3月2日まで開催。
- **珍奇の世界展**:2025年2月15日から3月30日まで開催。
これらのイベントについて、さらに詳しい情報が必要ですか?
現在時刻を調べた上で、TaivilyでWeb検索を行っています。
langchain-mcp-adaptersのソースを参照した感じ、LangGraphは使わずに実現しているようです。
3. langgraph-supervisorとの融合
3.1 langgraph-supervisorとは
langgraph-supervisorは以下の図のように、中央にスーパーバイザーがおり、複数のAgentを使って、目的を達成するためのアーキテクチャーを実現することができるライブラリです。
Agentは、Toolを利用することができるので、このToolをMCPサーバを利用できれば、
世に公開されているMCPサーバが利用できるのでは?と思いませんか?
3.2 動作確認
以下、ソースコードですが、langchain-mcp-adaptersのREADMEに記述されている
単一のMCPサーバの実装方法で、AgentのToolに設定しています。
# Create server parameters for stdio connection
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
from langchain_core.messages import AIMessage
from langchain_openai import ChatOpenAI
from langgraph_supervisor import create_supervisor
import os
import asyncio
import asyncio.subprocess
async def main():
x_api_key = os.environ["GROK_API_KEY"]
base_url = 'https://api.x.ai/v1'
model = ChatOpenAI(model="grok-2-latest",
base_url=base_url, api_key=x_api_key)
# Tavily検索サーバーのパラメータ
tavily_params = StdioServerParameters(
command="uv",
args=[
"--directory",
"/path/to/mcp-server-tavily",
"run",
"tavily-search"
],
env={
"TAVILY_API_KEY": os.getenv("TAVILY_API_KEY"),
"PYTHONIOENCODING": "utf-8"
}
)
# タイムサーバーのパラメータ
time_params = StdioServerParameters(
command="uvx",
args=["--directory", "/path/to/mcp-timeserver", "mcp-timeserver"],
)
# 両方のサーバーに接続
async with (
stdio_client(tavily_params) as (tavily_read, tavily_write),
stdio_client(time_params) as (time_read, time_write),
):
async with (
ClientSession(tavily_read, tavily_write) as tavily_session,
ClientSession(time_read, time_write) as time_session,
):
# 各セッションを初期化
await tavily_session.initialize()
await time_session.initialize()
# 各エージェントのツールを取得
tavily_tools = await load_mcp_tools(tavily_session)
time_tools = await load_mcp_tools(time_session)
# 専門エージェントを作成
time_agent = create_react_agent(
model=model,
tools=time_tools,
name="time_expert",
prompt="あなたは現在時間を調べる専門家です。"
)
search_agent = create_react_agent(
model=model,
tools=tavily_tools,
name="search_expert",
prompt="あなたはWebサイトを検索する専門家です。"
)
# スーパーバイザーを作成
supervisor = create_supervisor(
agents=[time_agent, search_agent],
model=model,
prompt=(
"あなたはチームスーパーバイザーです。\n"
"- time_expertは現在時間の確認を担当します\n"
"- search_expertはWebサイトを検索する担当します\n"
"質問の内容に応じて適切な専門家に振り分けてください。\n"
"必要に応じて複数の専門家に質問を振り分けることもできます。"
)
).compile()
# メッセージの履歴を表示
response = await supervisor.ainvoke({
"messages": [
{
"role": "user",
"content": "今日の日付を調べてから、
来週末の鎌倉のイベントを調べて詳細を教えてください"
}
]
})
# 応答を表示
for message in response["messages"]:
if isinstance(message, AIMessage):
print(message.content)
if __name__ == "__main__":
asyncio.run(main())
実行すると、以下のように検索して調べてくれています。
2025-02-23 11:31:33,880 - tavily-search-server - INFO - Starting Tavily search server
2025-02-23 11:31:33,882 - tavily-search-server - INFO - Server initialized, starting main loop
2025-02-23 11:31:34,189 - mcp.server - INFO - Processing request of type ListToolsRequest
2025-02-23 11:31:34,189 - tavily-search-server - INFO - Listing available tools
2025-02-23 11:31:39,388 - mcp.server - INFO - Processing request of type CallToolRequest
2025-02-23 11:31:39,388 - tavily-search-server - INFO - TOOL_CALL_DEBUG: Tool called - name: search, arguments: {'query': '鎌倉 次週末 イベント', 'search_depth': 'advanced'}
2025-02-23 11:31:39,388 - tavily-search-server - INFO - Executing search with query: '鎌倉 次週末 イベント'
2025-02-23 11:31:42,004 - httpx - INFO - HTTP Request: POST https://api.tavily.com/search "HTTP/1.1 200 OK"
2025-02-23 11:31:42,004 - tavily-search-server - INFO - Search successful - Answer generated
2025-02-23 11:31:42,004 - tavily-search-server - INFO - Search successful - Results available
I am transferring your request to check today's date to the time expert.
今日の日付は2025年2月23日です。次に、来週末の鎌倉のイベントについて調べます。
Transferring back to supervisor
I am transferring your request to search for events in Kamakura next weekend to the search expert.
鎌倉の来週末のイベントについて調べました。以下のイベントが見つかりました:
- **クリスマスマーケット in 横浜**
- **YOKOHAMA MINATOMIRAI WINTER HOLIDAY**
- **YOKOHAMA STRAWBERRY PARK**
- **長谷寺の桜見物**
- **東慶寺、円覚寺、鎌倉宮などでの紅葉**
他に知りたいことがあれば教えてください。
Transferring back to supervisor
他に知りたいことがあれば教えてください。
動きは、langchain-mcp-adaptersとほぼ同じですね。
4. 考察
4.1 使い分け
今回の例では、langchain-mcp-adaptersとlanggraph-supervisorの実行結果はほぼ同じでした。
使い分けるとすると・・
- langchain-mcp-adapters :
MCPサーバのみを利用することが前提で、素早くAgentを作成する - langgraph-supervisor :
ToolがMCPサーバだけではなく、他の既存Toolと組み合わせて、Agentを作成する
とかでしょうか。
もう少しMCPサーバを利用数を多くしていくと、性能面など差がでてくるかもしれません。
4.2 実行性
両方共そうですが、タイムサーバーを使ったり、使わなかったりと、実行する毎に変わります。
Agentが人間っぽい感じになりますが、なかなか制御難しいです。
Workflowの方が良いのでは、と思ってしまいます。
5. 謝辞
LangChainチームの皆様、公開ありがとうございました。
MCPサーバをAIエディタだけでなく、プログラムからも利用しやすいのは非常助かります、
Discussion