HEROZ Tech Blog

日本将棋連盟公認「将棋ウォーズ」や、AIを活用したシステム企画・開発を行う、AI企業HEROZの公式テックブログです。

MCP(Model Context Protocol)をLangChainのエージェント機能で動かしてみた

はじめに

先月、Anthropic社よりMCP(Model Context Protocol)というプロトコルが発表されました。 www.anthropic.com MCPAIアシスタント(RAGシステム)と情報源やツール群をつなげるための通信規約(プロトコル)ですが、現時点で何がどこまでできるかが未知数でしたので、調査を行い、LangChainのエージェント機能で動作させました。
とりあえずは、得られた知見についてシステム構成面の切り口で考察していきたいと思います。

MCPとは

MCPの各種情報は以下にあります。

modelcontextprotocol.io

概要

MCPプロトコル仕様のArchitectureにある下記の図のようにアプリケーションがMCPのクライアントとなり、ローカルやリモートに位置するMCPのサーバーと通信して、情報を取得したり、ツールを動作させたりします。
アプリケーションにはデスクトップアプリやSaaSサービスが想定されます。

MCPの概要図

そして、MCP自体は以下のようなプロトコルのフローやメッセージの中身が規定されています。

MCPプロトコルフロー

認証の不在

プロトコルの規定の中で気になったのは、現時点では認証に対応しておらず、今後議論していくように書かれていました。
認証が存在しない以上、インターネットを介しての通信は難しいかもしれません。

Auth

Authentication and authorization are not currently part of the core MCP specification, but we are considering ways to introduce them in future. Join us in GitHub Discussions to help shape the future of the protocol!

Clients and servers MAY negotiate their own custom authentication and authorization strategies.

2つのトランスポート

プロトコル仕様のTransportsによると、MCPには以下の2つのトランスポートがあるそうです。

後者のSSEタイプはhttpsを介した通信なのでMCPのサーバーはローカルとリモートを問わずに適用できます。
一方で、前者のstdioタイプはMCPのクライアントがMCPのサーバーとなるプロセスを起動して、パイプ機能による標準入出力(stdio)を介して通信するため、stdioタイプはローカル内でしか動作しないです。

SDKとリファレンスサーバー群

MCPの公式githubにはPython SDKTypeScript SDKが整備されており、これらを使用したリファレンスやサードパーティサーバー群も提供されています。

リファレンスサーバー群

SDKは前述した2つのトランスポートの両方に対応しているのですが、リファレンスサーバー群のほとんどはstdioタイプで作られているため、リモートで動作するものはないです。

LangChainへの実装

Python SDKを使用してLangChainへ実装したlangchain-mcpというライブラリもありますが、stdioタイプを前提としているため、ローカル内でのみ動作します(=リモートでは動作しません)。
langchain-mcpToolコンポーネントMCPToolもしくはToolkitコンポーネントMCPToolkitという形態で提供されるため、LangChainのエージェント機能で使う形になります。

このlangchain-mcpasync with構文でトランスポートのクライアント(下記の例ではstdio_client)を起動した状態で実行する必要があるため、チェインの構成と実行を近接して行う必要があります。

async with stdio_client(server_params) as (read, write): 
    async with ClientSession(read, write) as session: 
        toolkit = MCPToolkit(session=session) 
        await toolkit.initialize() 
        (toolkitを使用してrunする)

HEROZ ASKはチェインの構成と実行のタイミングが離れているので、langchain-mcpが適用しづらく、組み込みについては一旦は断念することにしました。

システム構成

MCPの動作についてシステム構成面で図を交えて考察していきます。

Claude Desktopでのシステム構成

Anthropic社はMCPの発表とともに応用例としてデスクトップ版Claude(Claude Desktop)からのデスクトップ検索を提示しています。
これが「デスクトップ検索が便利だ」や「デスクトップを覗かれるのは危険だ」みたいな感想とともに強調されてしまい、「MCP=デスクトップ検索」や「MCP=Claude Desktop」みたいな勘違いが発生しています。
MCPはあくまで通信プロトコルであって、Claude Desktopやデスクトップ検索は直接的には関係なく、単なる応用例の一つに過ぎません。

Claude Desktopでのシステム構成を図にすると、以下になります。
Claude DesktopはMCPクライアントとして位置し、必要なMCPサーバーを自ら起動して、stdioタイプのトランスポートで接続します。 そして、起動したMCPサーバーのプロセスからデスクトップのファイルにアクセスしています。

Claude Desktopでのシステム構成

起動するMCPサーバーの種類やアクセス先は設定ファイルのjsonにてClaude Desktopに渡しますが、任意のプログラムが起動できたり、ローカルのリソースに自由にアクセスできるという点で、安全とは言えない代物です。

SaaSシステムでの目指したいシステム構成

HEROZ ASKのようなSaaS型のAIアシスタントサービスにMCPを組み込む場合には、以下のような2つのパターンのシステム構成があると思います。

SaaSシステムで目指したいシステム構成

a.はSaaSサービスがMCPクライアントとなるパターンで、ユーザーからの質問に応じて、AIアシスタントがリモートサーバーにある情報を取得したり、ツールで何か実行したりします。
b.はSaaSサービスがMCPサーバーとなるパターンで、SaaSサービスが持っているリソースに対して、ユーザのAIアシスタントアプリや、別のSaaSサービスのAIアシスタントからのアクセスを受け付けます。 こちらはSaaSサービスが持つリソースが重要となるので、どちらかと言うとコンテンツプロバイダー的な位置付けになります。

HEROZ ASKは独自のリソースを持つわけではないので、a.のパターンを狙うことになります。
また、HEROZ ASKのようなエンタープライズ向けのサービスの場合には、リモートサーバーはお客様の情報やツールが入ったサーバーになることが想定されます。

現状のシステム構成

前述のようにAIアシスタントSaaSサービスはa.のようなMCPクライアントとして、リモートサーバーへのアクセスを望む場合が多いですが、現状はlangchain-mcpがstdioタイプのトランスポートにしか対応していないため、下記のようなシステム構成にならざるをえません。

現状のシステム構成

すなわち、ユーザから質問を受け取ると、MCPサーバーとなるプロセスを起動するとともに、SaaSサービスが動作しているサーバーのリソースにアクセスします。
これは、セキュリティ的にも問題が多いので、HTTP with SSEの対応が待たれます。

SaaSシステムでの問題点

仮にlangchain-mcpがHTTP with SSEタイプのトランスポートに対応したとしても、お客様のサーバーに情報を取りに行ったり、ツールを実行しに行ったりする場合には以下のような問題があります。

  • MCPに認証機能が実装されていないので、非公開情報のアクセスができない
  • お客様のサーバーがイントラネット内に存在する場合には、アクセスするための術がない

これらを図示すると以下となります。

SaaSシステムでの問題点

LangChainのエージェント機能で動かしてみた

langchain-mcpのREADMEにはエージェント機能を使わない例が載っていますが、せっかくなのでエージェント機能を使って動作させてみました。
/docというディレクトリと/doc/test.txtというファイルを作成してから、「/docには何のファイルがありますか?」という質問でディレクトリ内のファイル一覧を取得させています。

コードを見る

!pip install langchain langchain-openai langchain-mcp langchain-community==0.3.12 langchain-core==0.3.25
!mkdir -p /doc
!touch /doc/test.txt

import os
import asyncio
from langchain_openai import ChatOpenAI
from langchain import hub
from langchain.agents import AgentExecutor, create_structured_chat_agent
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp import MCPToolkit

os.environ["OPENAI_API_KEY"] = "(OpenAIのAPIキー)"

server_params = StdioServerParameters(
    command="npx",
    args=["-y", "@modelcontextprotocol/server-filesystem", "/doc"],
)

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents.structured_chat.prompt import FORMAT_INSTRUCTIONS, PREFIX, SUFFIX
HUMAN_MESSAGE_TEMPLATE = "{input}\n\n{agent_scratchpad}"

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "\n\n".join([PREFIX, "{tools}", FORMAT_INSTRUCTIONS, SUFFIX])),
        MessagesPlaceholder("chat_history", optional=True),
        ("human", HUMAN_MESSAGE_TEMPLATE),
    ]
)

async def main(message):
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            toolkit = MCPToolkit(session=session)
            await toolkit.initialize()
            
            model = ChatOpenAI(model_name="gpt-4o")
            tools = toolkit.get_tools()
            
            agent = create_structured_chat_agent(model, tools, prompt)
            agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
            
            await agent_executor.ainvoke({"input": message})

asyncio.run(main("/docには何のファイルがありますか?"))

無事にtest.txtがあるという回答を得ることができましたが、langchain-communitylangchain-coreをバージョン固定にしないとうまく動きませんでした。

実行結果

おわりに

新しく登場したMCPは外部の情報やツールを活用できるので、期待の技術と思っていたのですが、以下の理由によりまだまだ時期尚早のように思いました。

  • プロトコルの仕様に認証機能が搭載されていないため、インターネット経由の接続が難しい
  • リファレンスサーバーやlangchain-mcpのトランスポートがstdioタイプなので、ローカル内でしか実行できない(リモートサーバーへの接続ができない)
  • Python SDKasync with構文の中でしか実行できないので、セッションの確立(チェインの作成)と実際の動作(チェインの実行)を分離することができない

とは言え、これらの技術課題が解決されれば、大規模言語モデル(LLM)を用いたエンタープライズサーチの実現も夢が広がります。こうしたOSSへのコントリビュートについても一緒にやってくれる仲間を募集しています。