Home > ブログ > MCPサーバー呼び出しに対応した簡易チャットアプリ

ブログ

MCPサーバー呼び出しに対応した簡易チャットアプリ

少し前からAnthropicのAPIを使って簡単なものを作りちょこちょこと遊んでいたのだが、最近はMCP(Model Context Protocol)が話題になることが多い。 VS CodeからMCPサーバーの呼び出しができるようになったことで一気に広まったようだ。

ネット上にもMCPサーバーの実装方法はたくさん出ている。 一方、クライアント側の実装例はサーバーほど多くはないようだ

私もMCPサーバーのサンプル実装をVS Codeで試してみて、どのようなことができるのか理解できたが、AIを使ったアプリを構築するとなるとクライアント側の作り方も把握しておきたいと思い、MCPサーバーの呼び出しに対応した簡単なチャットアプリを作ってみた。

  • LLM
  • MCPサーバー
  • アプリ(MCPクライアント)

のそれぞれが実際にどのようにデータをやり取りしてMCPサーバーを呼び出せばいいのかをつかんでおきたいというのが意図だ。

今回作成したMCPサーバーの呼び出しに対応したチャットアプリ

作ったものは以下に置いた。

https://github.com/kztomita/chat-app-with-mcp-example

AnthropicのAPIを使うので実行にはAPIキーが必要になる。

セットアップ方法など詳細はリンク先のREADMEを参照してもらうとして、

$ python chat.py

のように実行すると、対話形式でやり取りができる。プロンプトの内容に応じてMCPサーバーを呼び出して処理するという形だ。

MCPサーバーについては、複数のMCPサーバーを用意して、LLMがそれらを使い分ける動作を見たかったので二つ用意した。一つはMCPサーバーのサンプルでよく見るUUIDを作成するものと(get-uuid.py)、もう一つは引数指定がある場合の動作も確認したかったので指定タイムゾーンでの現在時刻を取得するものを用意した(get-datetime.py)。

使用言語はPython。APIの処理には公式SDKを使った。 今回の記事ではMCPのアプリケーション/トランスポート層(?)レベルのプロトコル動作については触れない。 あくまでアプリ/LLM/MCPサーバー間でどのようなやり取りが必要なのかをAPI呼び出しより上のレベルみるだけとなる。

MCPサーバーの呼び出しが発生しないケース(単なるチャット)

実際に動作をみてみよう。まずは、MCPサーバーの実行が不要なように適当な挨拶メッセージを送ってみる。

MCPサーバーの呼び出しが発生しないケース

% python chat.py
Please enter the text and press Ctrl + D:
こんにちは。
Sending...
こんにちは!どのようにお手伝いできますか?

私は現在、UUIDを生成したり、現在の日時を取得したりするツールにアクセスできます。これらの機能を使いたい場合は、お知らせください。

例えば、以下のようなことができます:
- 一意のIDを生成する
- 現在の日時を取得する(特定のタイムゾーンも指定可能)

何かお手伝いすることはありますか?

Cache Creation Input Tokens: 0
Cache Read Input Tokens: 0
Input Tokens: 503
Output Tokens: 147

これはMCPサーバーの呼び出しが不要なケースで単なるチャットだ。 使用可能なTool(MCPサーバー)の情報をLLM(Claude)に渡しているので、これらのツールを使用できることをアピールしてきている(邪魔だけど)。

この時の処理の流れは、単純にメッセージを送信して応答を受け取るだけになる(図1)。単なるLLMへのAPI呼び出しだ。

MCPサーバーを呼び出さないケース
図1 MCPサーバーを呼び出さないケース

MCPサーバーの呼び出しが発生するケース

次にMCPサーバーの呼び出しが発生するようなメッセージを送ってみる。まずはUUIDの作成。

MCPサーバーを使ってUUIDを作成した例

Please enter the text and press Ctrl + D:
UUIDを作って
Sending...
UUIDを生成しますね。以下のツールを使用して新しいUUIDを取得します。

Cache Creation Input Tokens: 0
Cache Read Input Tokens: 0
Input Tokens: 503
Output Tokens: 67
Calling get_uuid {} ←MCPサーバーのget_uuid()の呼び出し
Result: 9fb7fd79-9cf7-442b-aebf-1eaf2118ead6 ←MCPサーバーからの応答
生成されたUUIDは以下になります: ←これはClaudeからの応答
`9fb7fd79-9cf7-442b-aebf-1eaf2118ead6`

このUUIDはランダムに生成された一意の識別子で、様々なアプリケーションやシステムで使用できます。

Cache Creation Input Tokens: 0
Cache Read Input Tokens: 0
Input Tokens: 116
Output Tokens: 85

今回のチャットアプリには、MCPサーバーのToolを呼び出したことがわかるようにメッセージを入れてある(青字箇所)。ちゃんとMCPサーバーのget_uuid()というToolを呼び出して、UUIDを受け取っているのがわかる。

次に別のMCPサーバー(時刻取得)を呼び出してみる。

MCPサーバーを使って現在時刻を取得した例(引数:タイムゾーン指定なし)

Please enter the text and press Ctrl + D:
今何時?
Sending...
現在の時刻を取得します。

Cache Creation Input Tokens: 0
Cache Read Input Tokens: 0
Input Tokens: 699
Output Tokens: 49
Calling get_datetime {}
Result: 2025-04-30 16:47:17
現在の時刻は 2025年4月30日 16時47分17秒 です。

Cache Creation Input Tokens: 0
Cache Read Input Tokens: 0
Input Tokens: 281
Output Tokens: 30

時刻を聞くと、今度はget_datetime()を呼び出して現在時刻を返してきている。質問の内容に合わせて、適切なMCPサーバーが選択されているのがわかる。

次は地域情報(シンガポール)を指定して時刻を聞いてみる。

MCPサーバーを使って現在時刻を取得した例(引数:タイムゾーン指定あり)

Please enter the text and press Ctrl + D:
シンガポールは今何時?
Sending...
シンガポールの現在時刻を確認します。シンガポールのタイムゾーンを指定して時刻を取得します。

Cache Creation Input Tokens: 0
Cache Read Input Tokens: 0
Input Tokens: 814
Output Tokens: 97
Calling get_datetime {'timezone': 'Asia/Singapore'} ←引数に'Asia/Singapore'を指定できている
Result: 2025-04-30 15:47:30
シンガポールの現在時刻は 2025年4月30日 15時47分30秒 です。

シンガポールは、協定世界時(UTC)から+8時間のタイムゾーン(SGT: Singapore Time)に位置しています。

Cache Creation Input Tokens: 0
Cache Read Input Tokens: 0
Input Tokens: 444
Output Tokens: 77

ちゃんとtimezone引数に'Asia/Singapore'を指定して、get_datetime()を呼び出している。

MCPサーバーのget_datetime()には引数でタイムゾーンを渡せるようにしてあるのだが(*1)、引数についても、タイムゾーンを明確に指示しなくても地名(上の例ではシンガポール)から引数用のタイムゾーン名を指定してくるあたり、すごいなと思う。

ひととおり動作をみたところで、実際にどのようにアプリ、LLM、MCPサーバー間でデータをやり取りしているかを整理してみよう。

"今何時?"と聞いた場合の動作は以下のような流れになる。

MCPサーバーを呼び出すケース
図2 MCPサーバーを呼び出すケース

まずは図1と同じようにLLMにメッセージを送信する(1)。 その後のLLMからの応答(2)ではTextBlockが返されるのと一緒に ToolUseBlockというMCPサーバーを呼び出すための情報が格納されたブロックが返される(*2)。

アプリケーションがLLMからのレスポンスをチェックしてToolUseBlockが含まれていたら、ToolUseBlockに含まれている指示にしたがって、アプリが自らMCPサーバーのToolを呼び出す(3)。MCPサーバーから結果を受け取ったら(4)、LLMに結果を送信して(5)、LLMに改めて回答の文章を作成させる(6)という流れになる。これでユーザ側にはLLMが現在時刻を教えてくれたように見えるというわけだ。

(3)のMCPサーバーの呼び出し前には、場合によってはユーザに許可を取る必要があるだろうが、今回はファイルを書き換えたりする危険なMCPサーバーはないので考慮しない。

このあたりの公式ドキュメントは「Claudeのツール使用」を参照するとよい。このページはMCPの仕様について記載したものではないが、 Anthropic(Claude)ではMCPより前にあったFUNCTION CALLINGという外部関数を呼び出す仕組みを流用して、MCPサーバーの選択を実現しているようだ。

実際のメッセージの細かいやり取りはgithubのリポジトリのソース(chat.py)を確認してほしい。

実際にひととおり動かしてみた感想

実際に簡単なアプリを作ってみて、動作の流れが理解できたのはよかった。

自然言語でToolの使い方をLLMに渡しておけば、ユーザが送ったプロンプトに対してLLMがどのToolをどういう引数で呼び出せばいいか指示してくるあたりは、最近のLLMの性能を見れば驚くべきことではないのかもしれないが、実際に動作しているのをみると「未来が来てるな」という感じはする。

MCPにより、AI(LLM)と人間が作ったプログラムとの接続が容易になり、LLMにできることも大きく広がるのだろうが、 自然言語でドキュメントを書いておけば、LLMがそれに合わせてToolを呼び出してくれる(厳密にはLLMの選択に従ってアプリが呼び出しているのだが)という新しい手法を正直まだよく飲み込めていない。しばらくは色々作ってみてこのやり方に慣れる必要がありそうだ。

(*1) https://github.com/kztomita/chat-app-with-mcp-example/blob/master/mcp-servers/get-datetime.py

(*2) AnthropicのAPIは他の一般的なWeb APIと同様に応答はJSON形式で返してくる。 Pythonのanthropicパッケージは、このJSONレスポンスをユーザには生の形では見せず、TextBlockとかToolUseBlockというオブジェクトにして返してくれる。TextBlockにはLLM(Claude)からの回答が格納され、ToolUseBlockには呼び出すべきToolの情報が格納されている。 Anthropic APIが実際に返すJSONデータを知りたい場合は https://docs.anthropic.com/ja/docs/build-with-claude/tool-use/overviewを参照するとよい。

投稿日:2025/04/30 23:24

タグ: プログラミング

Top

アーカイブ

タグ

Server (30) 作業実績 (22) PHP (19) ネットワーク (17) プログラミング (16) OpenSSL (10) C (8) C++ (8) PHP関連更新作業 (8) EC-CUBE (7) Webアプリ (7) laravel (6) Linux (6) Nginx (6) 与太話 (5) 書籍 (5) AWS (4) JavaScript (4) Vue.js (4) Rust (3) Golang (2) Symfony (2) お知らせ (2) Apache (1) CreateJS (1) MySQL (1) OSS (1) デモ (1)