1. はじめに
Playwright MCP を用いて ブラウザの自動操作エージェント を LangChain を用いて実装するサンプルを作成したところ、ブラウザがアクション毎にクローズしてしまい、複数ステップ(検索→クリック→入力…)に対応できなかった。複数ステップに対応する方法を示す。
対象読者
- LangChain / Playwright でブラウザ自動化エージェントを作りたいエンジニア
- とりあえず動いた サンプルが連続操作でコケて困っている人
問題点と解決方法
LangChainの公式ページのサンプルコードではセッション管理には言及がないため、"langchain playwright mcp" で Google検索した場合にもセッション管理に言及していないサンプルコードばかりが見つかり落とし穴にハマってしまう。
LangGraphの公式ページのサンプルコードやGitHub Issue [#178] にはセッション管理する方法について記載があった。
ソースコード
2. なぜセッション管理が必要なのか
Playwright MCP は リクエスト=ブラウザインスタンス という極端にシンプルなモデルで動きます。そのため 同じブラウザ状態をまたいで複数の操作を行う 場合は、LangChain 側でセッションを張っておかないと
- browser_navigate だけ成功
- 既にブラウザが終了 → DOM が無い → browser_click 失敗
という “途中で力尽きる” 事象に遭遇します。GitHub Issue [#178] で報告されているのはまさにこのパターンでした。
3. とりあえず動くけど危ういコード
問題点
セッション未使用のためツール呼び出しごとにブラウザが立ち上がり即終了する。
ブラウザの状態は毎回リセットされるためブラウザ状態を保存しておくことができず、複数ステップ(検索→クリック→入力…)に対応できない。
問題のある挙動
ソースコード
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
# Playwright MCPサーバーの設定
# npxコマンドを使用して@playwright/mcpパッケージを実行
client = MultiServerMCPClient(
{
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest",
],
"transport": "stdio",
}
}
)
async def run_agent():
"""
エージェントを実行する関数
直接client.get_tools()でツールを取得し、Google Gemini 2.5 Flashモデルを使用
"""
# クライアントから直接ツールを取得
tools = await client.get_tools()
# ReAct エージェントを作成(Google Geminiモデルを使用)
agent = create_react_agent("google_genai:gemini-2.5-flash", tools)
# YouTubeでサンドイッチマンの漫才動画を検索・再生するタスクを実行
response = await agent.ainvoke(
{"messages": [{"role": "user", "content": "youtubeでサンドイッチマンの漫才の動画を検索して動画を再生して"}]}
)
# レスポンスメッセージの内容を出力
print("Agent response:", response["messages"][-1].content)
if __name__ == "__main__":
# メイン実行部:非同期関数を実行
asyncio.run(run_agent())
4. セッション管理対応版
問題解決方法
- セッション開始:client.session("playwright")
- セッションに紐づけてツール取得:load_mcp_tools(session)
- 以降は同じセッションを保持するので、ブラウザ状態を跨いで操作できる
期待する挙動
ソースコード
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
# Playwright MCPサーバーの設定
# npxコマンドを使用して@playwright/mcpパッケージを実行
client = MultiServerMCPClient(
{
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest",
],
"transport": "stdio",
}
}
)
async def run_agent():
"""
セッション管理を使用してエージェントを実行する関数
"""
# セッションを使用してMCPクライアントとの接続を管理
async with client.session("playwright") as session:
# セッションからツールを読み込み
tools = await load_mcp_tools(session)
# ReAct エージェントを作成(Google Geminiモデルを使用)
agent = create_react_agent("google_genai:gemini-2.5-flash", tools)
# YouTubeでサンドイッチマンの漫才動画を検索・再生するタスクを実行
response = await agent.ainvoke(
{"messages": [{"role": "user", "content": "youtubeでサンドイッチマンの漫才の動画を検索して動画を再生して"}]}
)
# レスポンスメッセージの内容を出力
print("Agent response:", response["messages"][-1].content)
if __name__ == "__main__":
# メイン実行部:非同期関数を実行
asyncio.run(run_agent())
5. セッション管理のポイント
- 必ず async with client.session() を使う
- セッション名は MCP サーバー ID と一致させる(今回なら "playwright")
- ツールは load_mcp_tools(session) から取得
- client.get_tools() だけだと “セッションなし” ツールになってしまう
6. まとめ
Playwright MCP を LangChain エージェントから活用する際、「ブラウザ操作を連続して行いたい」場合にはセッション管理が必須です。セッションを張らずに client.get_tools() を使うと、各アクションごとにブラウザインスタンスが新たに立ち上がって即終了してしまい、途中の操作で DOM が失われて失敗する原因になります。
本記事では、
- なぜセッションが必要なのか
- セッション未使用時の問題点と再現コード
- セッションを使った正しい実装例
- セッション使用時の要点(async with client.session() / load_mcp_tools(session))
を通じて、「とりあえず動いたけど途中で止まる」問題の原因と対処法を解説しました。
LangChain × Playwright MCP でブラウザ操作エージェントを構築する方は、セッションの扱いに注意しながら開発を進めることが安定動作への鍵となります。今回の知見が、同様のハマりポイントで悩む方の助けになれば幸いです。