【チャットボット】話題のMCPサーバをLangChainで使う

お疲れ様です。

最近話題のMCPサーバ、LangChainで使えるということをこれまた最近知りました。 会社でやっている技術系の勉強会でも最近触れたこともあり興味があったので、今回はこちらを実装してみました。



そもそもMCPサーバとは

MCP(Model Context Protocol)は、大規模言語モデルClaudeを開発しているAnthropic社が提唱したプロトコルです。 AIモデルが外部システムと通信して情報を取得できる仕組みですね。

私も最近ちゃんと詳細を知ったばかりなので、すべてを知っているわけではないですが…。 AIエージェントに事前に「このシステムの情報を使ってほしい」と設定しておくことで、実施にAIエージェントに質問したときに設定されたシステムを使って情報を示してくれるもの、というざっくりとした認識です。

参考:
https://zenn.dev/tomo0108/articles/6b472b4c9cacfa

実装

早速実装していこうと思います。 今回の方針としては公開されているMCPサーバを利用してチャットボットに入力した質問からMCPを使用した返答を生成させるのを目標としました。

事前準備

最初にLangChainで使えるということを話しましたが、LangChainでMCPを使うための専用のライブラリであるlangchain_mcp_adaptersをインストールします。

pip install langchain_mcp_adapters

使用するMCPサーバはfetchとします。fetchはURLからそのサイトのコンテンツを取得するMCPサーバです。 github.com

AIエージェントにこのMCPサーバを使うと事前設定しておくために登録用のJSONファイルを作成します。辞書型の同じ形式でプログラムファイルにハードコーディングでもOKです。
今回はfetchの1つのみを使用しますが、複数指定することも可能です。 また、こちらの設定をそのまま使う場合、"command"の部分で"uvx"を指定していることから実行環境をuvを使用したものに合わせる必要があるので注意してください。他の実行環境での書き方は調べていただければと思います。

{
    "mcpServers": {
        "fetch": {
            "command": "uvx",
            "args": ["mcp-server-fetch"],
            "transport": "stdio"
        }
    }
}

ソースコード

今回作成のソースコードはこちらになります。(記事投稿時点のバージョンです。) github.com

ソース全体を見たい場合は上記を確認ください。
使用したモデルはGemini 2.0 flash、チャットボットのUIはStreamlitを使用しています。 以下ポイントになりそうな部分のみ書いておきます。

# MCPサーバの設定を読み込み
with open(MCP_CONFIG_PATH, mode="r") as f:
    mcp_config = json.load(f)

# ツール化
mcp_client = MultiServerMCPClient(mcp_config["mcpServers"])
tools = await mcp_client.get_tools()

事前に作成していたMCPの設定(jsonファイル)を読み込み、LangChainで使うためにツール化しています。 toolsを取得する際にawaitを使うので関数の定義のタイミングでasyncも付ける必要があります。

ちなみにtoolsを取得する方法は調べた感じいくつかあるのですが、今回の書き方(もしくはライブラリのバージョンかも?)ではこの書き方でしか動作しませんでした…。 他の書き方ではanyio.ClosedResourceErrorが出ました。下記のGitHubのissuesとリリースを参考にこの書き方にたどり着きましたが結局原因がわからずなので、何かご存じの方がいらっしゃれば教えてほしいです…。

# エージェントを用意
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)
    
# 返答を取得
response = await executor.ainvoke({"question": user_input})

続いてエージェントの作成と返答生成の部分。 返答の中身を取り出したい場合はresponse["output"]としましょう。

返答生成の段階でもawaitが必要になります。 Streamlitを使っている場合にasync/awaitによる非同期処理を書こうとすると、色々と工夫が必要になるようですので注意。 この部分のコードには無いですが標準ライブラリのasyncioを利用するとうまくいきました。 今回の本筋とは少し離れるので詳しくはGitHubに上げたソースの方を見ていただければと思います。

実行結果

実際に実行してみましょう。
チャットボットに下記を入力しました。fetchのみの使用なのでURLを直接書く必要があります。

入力

こちらを入力したときの出力がこちら。

出力

実際のエレファントカシマシの公式サイト(https://www.elephantkashimashi.com/)にある記事投稿時点での最新情報が以下になります。ちゃんとここから情報を取れていますね。 公式サイト

参考サイト