
リモートで動作する MCP Server を実装し、Claude アプリから呼び出してみる
目次
最近話題の(?) MCP ですが、私も試しに実装してみました。ここでは MCP (Model Context Protocol) 自体の説明はしませんので、分からない人は検索してみてください。
MCP Server のチュートリアルや「やってみた」系の記事だと、開発した MCP Server をローカルで 実行する例が多かったのですが、今回はリモートで動作する MCP Server を起動して Claude から呼び出すところまでをやってみます。なお、MCP 関連は新しいツール・ライブラリーなどが次々と出ているため、本記事もすぐに古くなる可能性がある点は事前にお伝えしておきます。
背景
MCP が昨年の11月頃に発表され、「面白そうなのでこれで何か作ってみよう」と思い、その当時作っていた WP RAG という WordPress の投稿で RAG が構築できるというシステムに MCP Server の機能を追加してみようと思いました(※)。なお、WP RAG のソーススコードは以下からダウンロード可能です。
mobalab/wp-rag: A WordPress plugin for building RAG
※: が、その後、色々忙しくて半年くらい経ってしまいました。
WP RAG のシステム構成は大雑把には以下の通りなのですが、WP RAG API と同じような位置で MCP Server を稼働させて、ベクトルデータベースへの問い合わせなどさせてみたら便利かも、と考えました。

実装
方針
機能としては、引数として受け取ったテキストと類似する WordPress 投稿を返す、というのを1つだけ実装しようと思います。WP RAG API 実装の際に作ったコードを使い回すため、Python を使用します。
今回は所謂 PoC 的なものなので色々突っ込みどころなどはあると思いますが予めご了承ください。また、私は Python はあまり得意ではありませんので、Python っぽくないところも多々あるかと思いますが、その辺もご了承ください。
エントリーポイント等
まずはコードから載せます。
from mcp.server.fastmcp import FastMCP
from core.database import init_db
from config import config
import os
def create_app(config_name='default'):
# DB 初期化
engine, session = init_db(config[config_name]) # config, init_db の中身は省略
# MCP サーバーのセットアップ
return FastMCP("wp-rag-mcp-server", debug = True, log_level = "DEBUG"), engine, session
mcp, engine, session = create_app(os.environ.get('APP_ENV'))
@mcp.tool()
async def get_similar_posts(text: str) -> str:
"""Get similar posts to a given text.
Args:
text:
"""
# 詳細は後述
def main():
mcp.run(transport='sse')
if __name__ == "__main__":
main()
コードについても簡単に補足説明します。
まず注目すべき点は mcp.run(transport='sse')
です。これにより、stdio を使ったローカルで起動する MCP Server ではなく、SSE を使った MCP Server を起動する事になります。SSE については後述します。
get_similar_posts
という「ツール」がどのような機能を持つかについては、コメントブロックに記載します。Get similar posts to a given text.
がそれにあたります。この文字列が MCP Server から MCP Client に返され、MCP Client は、必要に応じて適切なツールを選択する事が出来ます。
ツール
こちらもまずはコードを載せます。
@mcp.tool()
async def get_similar_posts(text: str) -> str:
"""Get similar posts to a given text.
Args:
text:
"""
# ベクトルデータベースには複数のサイトのデータがあるが、今回は ID は決め打ちで
site_id = 1
api_key = 'secret key'
# Site は SQLAlchemy のモデル
site = session.query(Site).get(site_id)
if not site:
raise Exception('Site not found')
validate_api_key(site, api_key) # 中身は省略
return tools.get_similar_posts(site, text)
# tools.py
from operator import itemgetter
from langchain_core.runnables import RunnableParallel
from langchain_openai import OpenAIEmbeddings
from langchain_postgres import PGVector
from config import get_config
from core.models import Site
def get_similar_posts(site: Site, text: str):
openai_api_key = site.site_config.openai_api_key
config = get_config() # 中身は省略
connection_string = PGVector.connection_string_from_db_params(
driver="psycopg",
host=config.DB_HOST,
port=config.DB_PORT,
database=config.DB_NAME,
user=config.DB_USER,
password=config.DB_PASSWORD,
)
embedding_model = OpenAIEmbeddings(
model="text-embedding-3-large",
openai_api_key=openai_api_key
)
pg_vector = PGVector(
embeddings=embeddings,
collection_name=str(site.id),
connection=connection,
pre_delete_collection=False,
)
# 実際には設定から取ってきているが、ここでは決め打ちで
num_docs = 4
score_threshold = 0.5
retriever = pg_vector.as_retriever(
search_type='similarity_score_threshold',
search_kwargs={"k": num_docs, "score_threshold": score_threshold}
)
retrieved_docs = RunnableParallel({
"docs": itemgetter("text") | retriever,
"text": lambda x: x["text"]
})
return retrieved_docs.invoke({"text": text})
MCP Server の実装という観点では特筆すべき点はありませんし、特に難しいところも無いかと思います。LangChain の使い方については以下のページが参考になるかもしれません。
- WordPress のデータを使って簡単な RAG を実装する(2) – もばらぶエンジニアブログ -> WP RAG の実装に関する解説
- LangChain – もばらぶエンジニアブログ -> 本エンジニアブログ内の LangChain 関連の記事
MCP Server 起動、及び Claude からの接続
MCP Server の起動
python main.py
で起動できます。今回はとりあえずテストのためにローカルマシンで起動します。デフォルトでは8000番ポートが使われます。
Claude の設定
実は、2025年4月21日現在、Claude アプリではリモートの MCP Server に接続する事が出来ないようです(※)。
Claude Desktop App with HTTP-with-SSE-transport? · modelcontextprotocol · Discussion #16
※: はっきり言ってこれには驚きました。こういう部分もしっかりサポートすれば、MCP 関連のエコシステムが充実して、Anthropic にとっても大きなメリットがあると思うのですが・・・
とりあえず、上のスレッドで紹介されている mcp-remote という npm パッケージを使ってこの問題を回避する事にしました。npm とか npx とかの説明はここではしませんので、必要に応じて検索などしてみてください。
claude_desktop_config.json
は以下の通りです。
{
"mcpServers": {
"wp_rag": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:8000/sse"
]
}
}
}

実際に使用してみた結果
Claude のアプリを立ち上げます。以下のようにトンカチのアイコンが表示されれば OK です。

今回試験的に実装した get_similar_posts
では、 site_id
が 1
で決め打ちされていますが、その場合以下のサイトのデータを使うようになっています。
もばらぶん – ソフトウェアの受託開発などを主に行う「株式会社もばらぶ」のブログです
まずは、Claude に以下のプロンプトを入力してみます。
MoMado とは何か、投稿の中から探してもらえますか。
すると、これは MCP Server の get_similar_posts
を使うべきだろうと Claude が判断し、以下のようなダイアログが出ます。ここで Allow for this chat か Allow once をクリックします。

結果、以下の通り回答がでました(長いので最初の方だけ載せます)。

途中にある View result from get_similar_posts from wp_rag (local)
というところをクリックすると、MCP Server へのリクエストとレスポンスが表示されます。
今回のリクエストは以下の通りでした。
{
`text`: `MoMado`
}
レスポンスは tools.py
の get_smilar_posts
の返り値です。
通信に関しての説明
概要
MCP の通信の仕組みについては以下のドキュメントに記載があります。
Core architecture – Model Context Protocol
ここでは transport layer には以下の2つがあると記載されています。
- Stdio transport
- HTTP with SSE transport
また、 “All transports use JSON-RPC 2.0 to exchange messages.” という記載もあります。
SSE とは
面倒なので Claude に質問して返ってきた答を少し修正して載せます。
SSE(Server-Sent Events)はサーバーからクライアントへ一方向のリアルタイム通信を可能にするHTTPベースの技術です。
- クライアントが一度接続を確立すると、サーバーから継続的にデータをプッシュできる通信技術
- 標準的なHTTP接続を使用し、特殊なプロトコルは不要
- 接続はテキストベースで、UTF-8エンコードされたテキストストリームとして実装
以下にSSEの主要な特徴を説明します。
- 一方向通信: サーバーからクライアントへの通信のみをサポート
- 自動再接続: クライアント側で接続が切れた場合、自動的に再接続を試みる
- イベントID: イベントにIDを付与することで、再接続時に途切れたところから再開できる
- 簡易な実装: WebSocketに比べて実装が簡単
HTTP with SSE transport とは
ここも Claude の回答を修正して載せます。
「HTTP with SSE transport」 という方式は以下のように通信を行います。
- サーバー→クライアント: SSEを使用してサーバーからリアルタイムデータをプッシュ
- クライアント→サーバー: 通常のHTTP POSTリクエストを使用してデータを送信
これは双方向通信を必要とするケースで、WebSocketの代替として使われることがあります。特にファイアウォールやプロキシの制約がある環境で有用です。
JSON-RPC とは
ここも Claude の回答を編集して載せます。
JSON-RPCは、リモートプロシージャコール(RPC)のための軽量なプロトコルで、JSONをデータフォーマットとして使用します。シンプルさと汎用性を重視した設計になっています。基本的な特徴は以下の通りです。
- データフォーマット: JSONを使用
- ステートレス: 各リクエストは独立している
- 双方向通信: クライアント-サーバー間で双方向の通信が可能
- バージョン: 現在主に使われているのは2.0
- 軽量: シンプルな構造でオーバーヘッドが少ない
JSON-RPCはトランスポート層に依存しない設計になっています。つまり:
- HTTP上で実行可能(最も一般的な使用法)
- WebSocket上でも実行可能
- TCPソケット上で直接実行することも可能
- UNIXソケットなど他の通信チャネルでも利用可能
OSIモデルで言えば、JSON-RPCはアプリケーション層のプロトコルで、HTTP/WebSocketなどの上に構築されるケースが多いです。HTTPを使う場合、通常はPOSTメソッドでJSONペイロードを送信します。
詳しくは以下のドキュメントを見てもらえればと思います。
その他、補足
実際にリモートのサーバーに載せる場合のアーキテクチャ
今回の記事では、タイトルに「リモートで」と書きつつ、実際にはローカルで動作させました(通信としては HTTP with SSE transport ですが)。本当に AWS 上などのリモートに載せる場合は、恐らく HTTPS にしないと色々問題だと思います。なので、例えば
ALB (SSL 証明書を割り当てる) <-> リバースプロキシ (nginx 等) <-> MCP Server
のような構成になると思います。この辺は、今後書いてみようと思います。
mcp-remote のプロセスが残る
今回は Claude アプリから mcp-remote 経由で「リモート」の MCP Server を呼び出しました。mcp-remote ですが、Claude アプリを閉じてもプロセスが残ったままになるようなので、適宜 kill コマンドで消す必要がありました。
感想
今回 MCP Client として Claude のデスクトップアプリを使いましたが、MCP Client として使えるものはそれ以外にもあります。
Example Clients – Model Context Protocol
今後も Client が増えていくにつれ、自前のデータやサービスなどを MCP Server にして、AI を使った業務フローに組み込んでいくという流れになるのかもしれません。
とはいえ、現時点ではまだまだ使いにくいところがあります。MCP を提唱した Anthropic が出している Claude アプリでリモートの MCP Server に直接接続できないとか・・・
まとめ
今回、リモートで動作する MCP Server を実装しました。ただ、(現時点では)Claude のデスクトップアプリではリモートにある MCP Server に直接繋ぐ事はできないため、mcp-remote という NPM パッケージを使用しました。
MCP はまだまだ新しい技術で今後も色々変化が多そうなので、今後も動きに注視していこうと思います。
コメントを残す