どうもこんにちは。
今回は、弊社サービスのAPIを使用してMCPを作成してみました。
今回、MCPを作成した理由は以下です。
自社プロダクトMCPを作成した理由
- MCPに対する理解を深めたかったから
- 業務に役に立つものを作りたかったから
- Amazon Bedrock AgentCoreを使用してMCPの構築を行いたかったから
今回は ローカルMCP編 なので、「 MCPに対する理解を深めた 」点と、「 業務に役に立つものを作りたかった 」という2点について深ぼっていきたいと思います。
MCPについて
まず、MCPについてですが、これはみなさんご存知かと思います。私は、「 AIエージェントが使える ツール を集めたもの 」と認識しています。
ただ、ツールとはちょっと役割が違うのかなと思っています。
私の思うツールとMCPの違い
| ツール | MCP | |
|---|---|---|
| 立場 | AIエージェントがデフォルトで持っている武器 | AIエージェントが アクセス することで使えるようになる武器 |
| 処理の規模 | 小さめ | 大きめ |
| 拡張性 | 低 | 高 |
| 実装のレベル | 低 | 高 |
処理の規模 については、デフォルトの武器をどれだけ大きくするか によって左右されるものかと思います。
拡張性については、ツールは AIエージェントと同じランタイムに実装される のが一般的かと思います。この場合、ツールを増やしたり改修したりする時には、AIエージェントのランタイムを直接編集する必要があります。一方でMCPは、AIエージェントとは別のランタイムに実装される のが一般的です。そのため、AIエージェントのランタイムとは分離してツールを増やしたり改修することができます。
なぜ自社プロダクトのMCP?
弊社では、自社プロダクトを運用している中で、エンジニア以外の社員がハードウェアのログを取得して調査 したり、 コンテンツを放映するスケジュールを組む などの業務が発生しています。
近年のAI社会では、「サーバから取得したログファイルをAIに読ませる」「インターネット上から必要な情報を取得してレポートをまとめる」というようなことが容易にできるようになっています。
これを応用して「調査すべきログファイルを自立的に取得させて読ませる」「スケジューリングをAIに任せる」ということを実現できないかと考え、MCPを作成してみようと考えました。
MCPを作るにあたってのハードルは?
どうやってプロダクトのDBにアクセスする?
MCPを作る時に、「どうやってデータを取ってくるか」「どうやってデータを登録するか」を考える必要があります。例として、以下が挙げられます。
- プロダクトのAPIを経由する
- ライブラリなどを使用してプロダクトのDBにアクセスする
2つ目の方ができることは増えますが、セキュリティ面を考慮するする必要があったり、1から構築を行う必要があるので難易度が高いです。
今回はやりたいことがAPIで実現できそうだったので、難しいことは考えずにAPI経由でMCPサーバからDBにアクセスするようにしました。
どうやって提供する?
提供方法はまだ検討段階ですが、以下の方法が考えられます。
- MCPサーバのソースコードを配布する
- MCPサーバをAWSやGCPなどに公開して、リモートMCPサーバとして公開する
- MCPサーバは非公開にし、MCPが接続されたAIエージェントをアプリケーションの機能として実装する
より柔軟なのは、2つ目かなと思っています。リモートMCPサーバとして公開しておけば、ClaudeやChatGPTなどのあらゆるサービスから簡単に接続することができるようになります。非エンジニアのユーザに使ってもらうためには多少の説明は必要になりそうです。
1つ目は、ローカルMCPサーバとして動作させることを前提としています。コストは抑えることができますが、エンジニア以外のユーザが使用するのは難しくなります。
3つ目の場合は、エンジニアでも非エンジニアのユーザもMCPサーバを意識することなくMCPを使用することができます。ただし、AIチャット機能をアプリケーションに実装する必要があるので、公開するまでに時間がかかります。
今回の私の要件は、「エンジニア, 非エンジニアの業務を効率化すること」なので、2つ目か3つ目が有効であると考えます。
MCPサーバの作り方
参考までに、以下に開発方法を示しておきます。今回は検証でもあったため、ローカルで動作させることを想定しています。
開発環境は?
開発環境は以下です。
- Python3.13
- uv仮想環境
- FastMCPライブラリ
- Docker
Pythonを選んだ理由は、MCPの中でAIエージェント使用するケースを考えた時に、StrandsAgentsが実装しやすいな と考えたからです。
また、Dockerを使用している理由は、Amazon Bedrock AgentCoreへデプロイするために、ECRへデプロイする必要があったからです。
開発方法
1. 環境を用意
ターミナルで以下を実行します。
$ mkdir playground && cd playground
$ mkdir sample_mcp && cd sample_mcp
$ uv init --python 3.13
$ uv add fastapi 'uvicorn[standard]' mcp httpx strands-agent # あと何か追加したけど忘れた...
プロジェクトにmain.pyというようなものが作成されていたら、mcp_server.pyと名前を変えておきましょう。(なんとなく)
2. MCPサーバを構築していく
まず、書き出しは以下としています。
from mcp.server.fastmcp import FastMCP
from starlette.responses import JSONResponse
import httpx
import json
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
from strands import Agent
mcp = FastMCP(host="0.0.0.0", stateless_http=True)
ツール関数を作成していく
次に、AIエージェントが実行できるツールを用意していきます。
今回はサンプルとして、ユーザ一覧情報を取得するAPIを叩くツールを用意します。
# エラーが発生した場合に呼び出す関数
def error_handler(message: str) -> str:
return json.dumps({ 'success': False, 'error': message })
# GETリクエストを送信する関数
def request_get(url: str, headers: dict, params: dict) -> JSONResponse:
try:
with httpx.Client() as client:
response = client.get(url, headers=headers, params=params)
response.raise_for_status()
return api_success_handler(response.json(), response.status_code)
except Exception as e:
return api_error_handler(str(e))
# ユーザ一覧を取得するツール関数
@mcp.tool()
def get_users(x_api_key: str, api_id: str) -> str:
"""APIからユーザの一覧情報を取得します
必須パラメータ:
- x_api_key: APIキー
- api_id: API ID
"""
try:
headers = { "X-Api-Key": x_api_key, "Accept": "application/json", "Content-Type": "application/json" }
params = { "api_id": api_id }
response = request_get(get_users_url, headers, params)
return json.dumps(response.body.decode())
except Exception as e:
return error_handler(str(e))
ここで重要な点は3つです。
1. デコレータ
ツールとして使用する関数には、@mcp.tool()というデコレータを関数の前に記述します。これによって、以下のことができるようになります。
-
get_users関数をツールとして使用できるようになる - 関数の
docstringがツールの説明として使われる - 関数のパラメータと型アノテーションに基づいて入力スキーマを生成する
- パラメータの検証とエラー報告を処理する
ツールとして呼び出さない関数には、デコレータをつける必要はありません。
2. 関数のパラメータと型を定義する
関数のパラメータに型を指定しておくことで、正確なJSONパラメータを関数に送信することができるようになります。
# 入力パラメータがすべて文字列の場合
def get_users(x_api_key: str, api_id: str) -> str:
# 入力パラメータにint型が含まれる場合
def get_user(x_api_key: str, api_id: str, user_id: int) -> str:
# 入力パラメータにboolean型が含まれる場合
def get_users(x_api_key: str, api_id: str, is_admin: bool) -> str:
3. 出力するデータの型を定義する
関数の戻り値の型を指定しておくことで、適切な形式にデータを変換してデータを返してくれます。
- str : TextContentとして送信
- dict、list、Pydantic、BaseModel : JSON文字列にシリアル化され、TextContentとして送信
- bytes : Base64 エンコードされ、BlobResourceContents として送信 (多くの場合、EmbeddedResource内)
- fastmcp.Image : 画像データを簡単に返すためのヘルパークラス。ImageContentとして送信
- None : 空のレスポンス (クライアントにコンテンツは返されない)
3. MCPサーバを起動する
ターミナルで、以下を実行するとPythonサーバが起動します。
$ uv run python mcp_server.py
4. クライアント側で設定を行う
MCPを使用するクライアント側ツールによりますが、以下のようなJSONを定義します。
{
"mcpServers": {
"sample_mcp_servers": {
"url": "http://localhost:8000/mcp",
"enabled": true
}
}
}
正常に接続ができれば、MCPサーバを使用することができるようになります。
参考サイト