【MCPサーバーを作ろう】FastMCPをお試ししてみる(＠Roo Code)

# 0. はじめに

お手軽に自分でMCPサーバーを構築できるライブラリを試したメモです。

https://github.com/jlowin/fastmcp

- 動作環境
・ OS : Windows11 pro
・ python : 3.12.8(uv環境)
・ fastmcp : 2.2.7
・ Roo Code :3.15.5

# 1. MCPとは？

## 1-1. MCPとは？
Model Context Protocolの略称です。

**最近色々なところで解説がなされていますが「LLM用USB」という説明が一番みんなにとって納得できる回答になっていますね。**

LLMは基本的に「次にくるであろう単語を予測する機能」を持っているわけですが、これだけではLLMでできることは限られます

もちろん次の単語を予測させるだけでも我々は感動したし、それだけでも十分に役に立ちますが、それだとLLMに「作業」をさせることができませんよね？

そんな時に役に立つのがMCPであり、具体的には**言語モデルの予測プロセスの途中に「外部の計算リソース」や「プラグイン」を挟み込む仕組み**を入れることで、その情報を元に次に来る単語を予測し、最終的な出力テキストを生成することができます。

## 1-2. Function Callingとの違いは？
MCPが出る前に「Function Calling」という概念もありました。

これとなにが違うの？と気になったので調べると、以下の画像がわかりやすかったです。
Function Callingは「一方通行」でLLM任せにしている。でもMCPは複数ツールの発見・呼び出し・状態管理をMCPクライアント主導でできるってことなんですね。
要するに主導がLLMなのかMCPクライアントなのかの違いみたいですね（さらにFunction Callingは前後に何が起こったかを認識しないので1回限りの仕事で終わり）

![GovgCf6XQAEjGPP.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373060/9064aad6-0ddc-4da0-8c67-6524ffd4cf77.jpeg)



https://medium.com/@genai.works/%EF%B8%8F-function-calling-vs-mcp-what-smart-ai-teams-need-to-know-7c319267b6db

# 2. FastMCPとは？

FastMCPは、そのMCPサーバーを構築するためのPythonライブラリであり、比較的簡単にMCPサーバーを自作することができます。

https://gofastmcp.com/getting-started/welcome


# 3. 実際にMCPサーバーを設定してみる

さて、以下では実際にMCPサーバーを作成してみます。
**今回MCPの有無で比較するタスクは以下のように「大文字のカウント」という簡単なタスクです（正解は9個）**

```CMD:チャットで質問するタスク
以下の文字で大文字の数をカウントしなさい
AAaaAAaaAAAAA
```

## 3-0. Roo Codeの準備とモデル(gemini 1.5-flash)の設定

今回はRoo CodeというVSCodeで利用可能な、AIによるコーディングまたは周辺作業のアシスタントを行う拡張機能を利用します。

<font color="Red">Roo Codeの導入はこの記事の本質ではないので割愛します。</font>
※VSCodeの拡張機能からRoo Codeと検索して入れるだけです。

その後、使用するLLMプロバイダを選ぶわけですが、最近のLLMは賢いのでこの程度のタスクをAPIの裏側でできてしまいます。よって、少し古いGoogleのAPIである「gemini 1.5-flash」をサンプルとして以下のように設定します

![Roo設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373060/2722f819-b238-49b9-9157-c350ec986e5f.png)

## 3-1. 素のgemini 1.5-flashにチャットで聞いてみる

実行した結果は以下です。
<font color="Red">回答は「大文字の数は6です」となっており、不正解なことがわかります</font>

![gemini non mcp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373060/177926f6-0223-418f-9ec3-f11e71d1b12d.png)


## 3-2. uvでプロジェクトの作成とライブラリ導入

FastMCPライブラリの導入方法は公式で「uv」というPythonパッケージ管理を推奨していますので、uvでプロジェクトを作成します（以下は参考記事）

https://qiita.com/kuk_a_i_ai/items/be8854553486ee1a640a


### 3-2-1. fastmcp_sampleプロジェクトを立ち上げる
Pythonのバージョンですが、今回は適当に3.12.8を使用してfastmcp_sampleというプロジェクトを立ち上げます
`uv init -p 3.12.8 fastmcp_sample && cd fastmcp_sample`

### 3-2-2. 仮想環境(venv)を作成し、FastMCPを導入する
仮想環境venvを作成
`uv venv`
fastmcpライブラリを導入する
`uv add fastmcp`

## 3-3. LLMに与えるプログラム：character_stats.pyの作成
今回はGeminiに大文字をカウントする能力を付与しますが、せっかく作るなら小文字のカウント機能も一緒に盛り込んだプログラム「character_stats.py」としてみます

:::note info
ざっくりとポイントは以下
① 最初にサーバー名入りのインスタンスを作成
② @mcp.tool()の中にMCPとして実行させたい関数を定義する
③ 型ヒントやdocstring(コードが何をやるのかの説明)は無くてもいいが、あったほうがいい（LLMが検証してくれる為）
④ mcp.run()メソッドでクライアント(今回はRoo Code)と通信する
:::


```python:character_stats.py
from fastmcp import FastMCP

# 「Character Counter」という名前のMCPサーバー名インスタンスを作成
mcp = FastMCP("Character Counter")

@mcp.tool()
def uppercase_counter(text: str) -> int:
    """
    テキスト内の英語の大文字の数をカウントする

    Args:
        text: 分析するテキスト（例: "AAaaa"）

    Returns:
        テキスト内の大文字の数（例: "AAaaa" → 2）
    """
    # 大文字をカウント
    uppercase_count = sum(1 for char in text if char.isupper())
    return uppercase_count


@mcp.tool()
def lowercase_counter(text: str) -> int:
    """
    テキスト内の英語の小文字の数をカウントする
    
    Args:
        text: 分析するテキスト（例: "AAaaa"）
    
    Returns:
        テキスト内の小文字の数（例: "AAaaa" → 3）
    """
    # 小文字をカウント
    lowercase_count = sum(1 for char in text if char.islower())
    return lowercase_count

if __name__ == "__main__":
    """
    mcp.run()は実際にMCPサーバーを起動
    サーバーとクライアントが同じマシンの場合にはstudioを指定
    """
    mcp.run(transport="stdio")
```

## 3-4. プロジェクトMCP(Roo Code)の作成と確認

MCPは「グローバル(他のプロジェクトでも有効)」と「プロジェクト限定」の両方のMCPを設定できるので、今回はプロジェクト限定を選んで以下のように設定する

:::note info
ざっくりとポイントは以下
① Character Counterというサーバーの名前を設定 ※FastMCP("Character Counter")の中身と同じ
② 使用するコマンドとしてuvを指定する
③ argsの中にはコマンド(uv)に渡す引数を指定する（uvがcharacter_stats.pyをrunする為のディレクトリのフルpath※fastmcp_sampleデイレクトリを渡して実行させる）
④ disabled以下はお決まりみたいなものでサーバー有効とかを示す
:::

```CMD:mcp.json
{
  "mcpServers": {
    "Character Counter": {
      "command": "uv",
      "args": [
        "--directory",
        "C:\\Users\\*****\\Desktop\\code\\fastmcp_sample",
        "run",
        "character_stats.py"
      ],
      "disabled": false,
      "alwaysAllow": []
    }
  }
}
```
すると、以下のようにMCPサーバーとして「Character Counter」の機能をRoo Codeが得られていることがわかる（緑色のランプがついていればMCP有効です）


![MCP設定_roo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373060/8525f684-d937-435e-93ac-e94a8e89ff99.png)

# 4. GeminiにMCPを使用させて大文字と小文字をカウントさせてみる

さて、[3-1. 素のgemini 1.5-flashにチャットで聞いてみる](#3-1-素のgemini-15-flashにチャットで聞いてみる)でやった結果と比較してみましょう。

**MCPを設定した上でgemini 1.5-flashに同じ質問をすると、<font color="Red">「RooはMCPサーバーUppercase Counterでツールを使用したい」という箇所でMCPを使いたいと申請されるので、承認してあげましょう。</font>**
すると以下図のように「uppercase_counter」関数を使用して大文字をカウントしている様子がわかります。

![Roo_MCP_大文字.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373060/0fa846cb-f7f6-47c8-97e3-9d7d1e5790ae.png)


ついでに以下のように小文字もカウントさせてみましょう。
lowercase_counter関数を使用して、正しく4と回答できていることがわかります。

![Roo_小文字.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373060/7c1e2961-a2cf-4b6c-8ce6-ecccefb6a3fe.png)


# 5. おわりに

こんな感じで素のLLMでは(基本)次の単語予測しかできないのに対して、外部のリソースをUSBのようにドッキングすることで色んなタスクを簡単にこなせるようになります。
これにより、LLMが具体的にこなせるタスクもグッと広がると思うので、皆さんもぜひお気軽に試してみてください！