はじめに
何かと話題なMCPですが、せっかくのなので試してみました。
MCP
Model Context Protocolの略でMCP
私の理解では生成AIエージェントなどが利用するインターフェースです。
標準化したことで外部システムなどとの連携を加速させるものになるといった認識です。
こちらの記事の説明が私のイメージと近かったので紹介させていただきます。
zenn.dev
手順
1. インストール
uvを利用して環境を作成します
$ mkdir fastmcp_project $ cd fastmcp_project/ $ uv init . $ uv add fastmcp $ uv add netmiko
mcpサーバはfastmcpを選択しました
Pythonらしいコードで様々なことができることが特徴のようです。
ほかの選択としてmcp公式のPythonSDKもあります。
こちらもサーバFastMCPというモジュールになっています。
使用感はほぼ同じです。
ただ、内容は別のようで微妙に動きが変わる可能性もあります。
2. サーバーの準備
MCPサーバを準備します。
今回作成するツールは過去作成したものをFastMCP用に微調整したものです
過去の記事
ツールとして利用するには@mcp.toolデコレータを指定します。
スクリプト内にmcp.run()を書くことでpython <スクリプト名>.pyで起動しても動くようになります。
サーバはデフォルトではSTDIOトランスポートモードで起動します。
STDIOモードCaludeDesktopなどのツールで利用する際に選択するモードのようです。
今回はPythonスクリプトで書いたクライアントから起動をしたいのでSSEトランスポートモードを選択します。
mcp.runの引数transportでsseを指定します。
# server.py from typing import List, Optional from netmiko import ConnectHandler from netmiko.exceptions import NetmikoTimeoutException from pydantic import BaseModel from fastmcp import FastMCP class NetworkDeviceInfo(BaseModel): device_type: str host: str username: str password: str secret: Optional[str] mcp = FastMCP("NetmikoTool") @mcp.tool() def run_netmiko_get(device_type: str, host: str, username: str, password: str, commands: List[str], secret: Optional[str] = None) -> List[str]: """Access NW devices and execute commands with Netmiko""" # 辞書からNetworkDeviceInputを作成 output = [] network_device = NetworkDeviceInfo( device_type=device_type, host=host, username=username, password=password, secret=secret ) device_data = network_device.model_dump() try: with ConnectHandler(**device_data) as net_connect: if device_data.get('secret'): net_connect.enable() for command in commands: output.append(str(net_connect.send_command(command))) except NetmikoTimeoutException as e: output = f"{e}" return output if __name__ == "__main__": mcp.run(transport="sse")
サーバの起動は、mcp.run()に起動の定義をしているので以下で行います。
uv run server.py
mcp.run()を書いていない場合もfastmcp runコマンドで起動が可能です。
uv run fastmcp run server.py:mcp --transport sse

デフォルトではポート8000番で起動するようです。
ドキュメントにはポートの指定はmcp.runの引数で可能と記載があるんですが、エラーになってしまいました。。。
raceback (most recent call last):
File "/home/usaen/fastmcp_project/server.py", line 55, in <module>
mcp.run(transport="sse",port=8888)
TypeError: FastMCP.run() got an unexpected keyword argument 'port'
3. クライアントの準備
続いてクライアント側を作成していきます。
以下を参考に作成しています。
サンプルとの違いとして、
Client("http://localhost:8000/sse")で接続先を定義します。
サーバのスクリプトを直接定義する場合は、クライアントのスクリプト内でサーバを起動して処理を実行。完了するとサーバを閉じるといった動きになるようです。
この挙動からの推測ですが、
さまざまなサーバのスクリプトをLLMアプリ経由で呼び出す、といった動きなるのかな。
であればMCPサーバのインストールはスクリプトのインストールってことになるのか。
サーバのトランスポートモードのデフォルトがSTDINになってるのもこの利用方法が前提になっているからでしょうか。
import asyncio from fastmcp import Client async def call_tool(): async with client: result = await client.call_tool( "run_netmiko_get", { "device_type": "nokia_srl", "host": "clab-bgp-lab-leaf1", "username": "admin", "password":"NokiaSrl1!", "commands":["info network-instance mgmt"], "secret": None } ) print(result) if __name__ == "__main__": client = Client("http://localhost:8000/sse") asyncio.run(call_tool())
処理内容としては、コンテナラボにデプロイしたSRLinuxにNetmikoでログインしてコマンドを実行するものになります。
4. 実行
実行をしてみます。
$ uv run client.py
[TextContent(type='text', text='[" type ip-vrf\\n admin-state enable\\n description \\"Management network instance\\"\\n interface mgmt0.0 {\\n }\\n protocols {\\n linux {\\n import-routes true\\n export-routes true\\n export-neighbors true\\n }\\n }\\n"]', annotations=None)]
わかりにくいですが、出力が帰ってきているのが確認できます。
サーバ側のログを見るとアクセスログも残っているのでうまくいったようです

感想
FastMCPで簡単なサーバとクライアントを作成してみました。
正直まだ、MCPについては知らなすぎるので何とも言えないなぁという感想を持っています。
ただ、外部ツール利用をしたい際にREST APIを作成するよりかは簡単なのかなと思いました。 今回は利用していませんが、ToolのほかにもResouces(RAGで使うっぽいもの)やPrompt(プロンプトのテンプレート)なども使用できるようで、便利なのはなんとなくわかりました。