GitMCPから見たCloudflareリモートMCPサーバーの構築

## 初めに

`GitHub Projects Community`というGitHubの非公式アカウントが先日(2025年4月12日)に
面白そうなリポジトリを紹介しました。

https://x.com/GithubProjects/status/1910533317172015227

ドキュメントを見ると`あらゆるGitHubリポジトリをリモートMCPエンドポイント`に変換できるらしい。
興味深いので、ちょっと実装を見てみました。

## 機能紹介
まずは軽くリポジトリの検証からです。

私は自分のPublicのGitHubリポジトリを使って検証をしてみました、
自分のリポジトリを使ったのは、`GitMCP`の回答精度を正確に判断しやすいからです。

https://github.com/huanshenyi/nextjs-mastra

`https://github.com/huanshenyi/nextjs-mastra`は該当リポジトリのURLですが、
ホストネームを少し修正して、`https://gitmcp.io/huanshenyi/nextjs-mastra`にした状態でブラウザから開けば、下記のような画面を開けます。

![screencapture-gitmcp-io-huanshenyi-nextjs-mastra-2025-04-13-13_06_53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/dc932ef2-66d8-403b-b49c-789a423eedf5.png)

各MCPクライアントで利用する場合の設定ファイルを一覧できます。

今回はCursor用の設定ファイルをコピーして、
`Cursor Settings`からMCPの`+ Add new global MCP server`を開いて、設定を追加します。

```json:mcp.json
{
  "mcpServers": {
    "nextjs-mastra Docs": {
      "url": "https://gitmcp.io/huanshenyi/nextjs-mastra"
    }
  }
}
```

追加完了したら、サーバーが利用可能な状態になってることが確認できます。

![C14619B7-366E-419F-8A16-4DE47973E510_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/084a62b3-989a-453f-85ae-b6b43273106e.jpeg)

チャット画面から、`huanshenyi/nextjs-mastra`リポジトリの中、Langfuseにトレースを送信する内容を聞いてみました。


![B2AE4B09-1184-4B1F-A8B7-0E66D8A8FB1D_1_201_a.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/7ee976fd-c676-4d9a-aa28-2ec8a73943cc.jpeg)


うまく取得できませんでした:frowning2:

それはLangfuseにトレースを送れることが`READMEに書いてないから`です:point_up_tone1:

質問された際に、最初は`fetch_reponame_documentation`ツールが呼び出され、
READMEに書いてる内容が返されます、それを元に回答を生成してるからです。

ドキュメントにLangfuseとの連携の内容を追加し、コミットしてから再度聞いてみると、回答が返ってきました。

![92A2E174-A6F3-4FB8-BD1F-CF6DB3DD39B5_1_201_a.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/98ca2a3a-cc68-44c7-a648-477e66f9b4d1.jpeg)

GitMCPの実装では、Cloudflare の tiered cacheがリポジトリ単位で機能してるため、リポジトリのドキュメント更新後、しばらく待たないとキャッシュにだけヒットします。

ここまででハンズオン完了です。

実装を見ると、リポジトリ内の`ドキュメント`、`llms.txt`、`リポジトリのメタデータ`をうまく取得して、回答してるそうです。

## 簡易なGitMCPを作ってみよう

この`GitMCP`はCloudflareの`workers mcp`を使って構築されてることがわかります。

CloudflareのリモートMCPの詳細説明は下記の記事を読んでください。

https://blog.cloudflare.com/remote-model-context-protocol-servers-mcp/

同じ`workers mcp`を使って、リポジトリのURLを渡せば、
リポジトリのことを聞けるリモートMCPサーバーを作ってみます。


### プロジェクト初期化

任意のディレクトリでアプリを初期化します。

```bash
npm create cloudflare@latest -- my-mcp-server-github-auth --template=cloudflare/ai/demos/remote-mcp-github-oauth
```

初期化プロセス中に質問されますが、下記のよに回答しましょう。

> You're in an existing git repository. Do you want to use git for version control?
- yes

> Do you want to deploy your application?

- No

初期化完了したら、リポジトリに入って、`OAUTH_KV`を作ります。

```bash
cd my-mcp-server-github-auth
wrangler kv namespace create "test-remoto-mcp(任意)"
```

実行したら、Cloudflareの認証画面が開かれます、事前にアカウント作っておくことをおすすめです。

Cloudflareはクレジットカードの登録なしでアカウントを作成でき、無料プランでも十分な機能が提供されているため、気軽に利用を始めることができます。

```
🌀 Creating namespace with title "worker-test-remoto-mcp"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
[[kv_namespaces]]
binding = "test_remoto_mcp"
id = "xxxxxxxxx"
```

実行完了したら、できた値(id)をリポジトリ配下のwrangler.jsoncのOAUTH_KVに書き換えます。
```diff_json: wrangler.jsonc

	"kv_namespaces": [
		{
			"binding": "OAUTH_KV",
+			"id": "{OAUTH_KV}"
		}
	]

```

ここまでできたら一度デプロイしましょう。

```
npm run deploy
```

デプロイ完了したら、Cloudflareワーカーのurlが表示されます、
`https://your-worker-name.your-account.workers.dev`
それをコピーしておいてください、後でまた使います。


### GitHub OAuth Appを作る

[GitHub OAuth Appドキュメント](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app)

GitHubにログインして、`Settings` -> `Developer Settings` -> `New OAuth App`

![EBB3B313-1E08-4E5A-A356-CF1CA4FA6B6A.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/5591e3d0-8766-4d4f-8540-c632b5d4821e.jpeg)

> Application name

覚えやすい名前を入力しましょう。

> Homepage URL

先コピーしたCloudflareワーカーのurlを入力しましょう、`https://your-worker-name.your-account.workers.dev`。

> Authorization callback URL

Cloudflareワーカーのurl+/callback, `https://your-worker-name.your-account.workers.dev/callback`。

完了したら、クライアントIDとクライアントシークレットが発行されます、
それらをWorkerのシークレットとして追加します。

namespaceに入る値は`OAUTH_KV`を作った際に使った値になります。

```bash
wrangler secret put GITHUB_CLIENT_ID --name {namespace}
wrangler secret put GITHUB_CLIENT_SECRET --name {namespace}
```

最後、COOKIE_ENCRYPTION_KEYの値も作ります。

```bash
openssl rand -hex 32
```

できた値をメモして、次のコマンド実行後、入力します。

```bash
wrangler secret put COOKIE_ENCRYPTION_KEY --name {namespace}
```

ここまででセットアップは完了しました、実際の動作を確認してみましょう。

[Workers AI LLM Playground](https://playground.ai.cloudflare.com/)を開いてください。


![96F43FD7-4749-4DA0-8BFF-99B102B1D6FA.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/ec223340-7bfb-4733-83fd-04024bb79a73.jpeg)


MCP Servers URLに Cloudflareワーカーのurl+`/sse`を入力しましょう、
`https://your-worker-name.your-account.workers.dev/sse`。

Connectを押すと、認証画面にリダイレクトされます。

![A33B26BC-9E36-483A-A711-33CFE0BF5F12.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/578ec737-e022-4790-a413-650e97475627.jpeg)

接続完了したら、デフォルトで実装されてる`add`ツールを呼び出してみます。

![83E43ED7-36C1-4DF6-A5BD-27DD49088A62_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/5fc3a4c1-2e90-49f1-97af-c53d278632b6.jpeg)

環境変数のセットやデプロイが完了していれば、問題なく呼び出せるはずです。

### githubドキュメント取得ツールを作る

簡易的なGitHubリポジトリのREADME.mdの内容を取得するツールを作ります、
`addツール`の下に下記のコードを追記します。

```typescript:src/index.ts
...

    this.server.tool(
      'getGithubReadme',
      'GitHubリポジトリのREADMEファイルを取得する',
      { repositoryUrl: z.string() },
      async ({ repositoryUrl }) => {
        try {
          // repositoryUrlからオーナー名とリポジトリ名を抽出
          const urlParts = repositoryUrl.replace('https://github.com/', '').split('/')
          const owner = urlParts[0]
          const repo = urlParts[1]

          // READMEのraw URLを構築
          const readmeUrl = `https://raw.githubusercontent.com/${owner}/${repo}/refs/heads/main/README.md`

          // READMEの内容を取得
          const response = await fetch(readmeUrl)

          if (!response.ok) {
            throw new Error(`README取得エラー: ${response.status} ${response.statusText}`)
          }

          const readmeContent = await response.text()

          return {
            content: [
              {
                type: 'text',
                text: readmeContent,
              },
            ],
          }
        } catch (error) {
          return {
            content: [
              {
                type: 'text',
                text: `エラーが発生しました: ${error.message}`,
              },
            ],
          }
        }
      },
    )
```

このツールはユーザーからGithubリポジトリのurl渡された際に、そのリポジトリにある`README.md`の中身を返すだけのシンプルなツールです。

修正完了したら、再度デプロイします。
```
npm run deploy
```

[Workers AI LLM Playground](https://playground.ai.cloudflare.com/)を使って検証してみよう。

同じくLangfuseとの連携の話を聞いてみます。

![9D8ED49D-79EF-480E-8A20-31970B4F1F46_1_105_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/68b4d7cf-ffe8-4c49-891b-0fa544bc473b.jpeg)

それぽい結果が返ってきました:relaxed:

## その他のクライアントで使用

### CLINE

cline_mcp_settings.jsonを下記のように修正してください

```json: cline_mcp_settings.json
{
  "mcpServers": {
    "math": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://your-worker-name.your-account.workers.dev/sse"
      ],
      "timeout": 10
    }
  }
}
```

![C24C7DAA-1B41-4B43-837C-E82C56EDE78D.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/5dad806c-eb70-4acc-8275-103cafc48676.jpeg)

### Claude Desktop

```json: claude_desktop_config.json
{
  "mcpServers": {
    "math": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://your-worker-name.your-account.workers.dev/sse"
      ],
      "timeout": 10
    }
  }
}
```

![462676CC-9867-43B2-85C7-923A07FB2F28.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/972820f0-bed4-4f70-8806-f9450b04d6d1.jpeg)

## 課題

### 無限ループ

自分の実装ミスなのか、仕様なのか不明ですが、ローカルのMCPクライアントを使ってリモートMCPサーバーに接続する際に、認証の無限ループがたまに発生します。

Claude Desktop、Cline、Cursorからも再現できます。
もしかするとmcp-remoteに関連する問題かもしれませんが、さらに検証が必要です。

### リクエストの数がエグい

最初はループに気づかずに検証していたため、メインアカウントのリクエスト数が5000件以上まで膨れ上がってしまいました。
その後、気をつけて使っていても、ご覧いただいた数になってしまいました。これまではMCPをローカルで動かしていたのでリクエスト数にあまり注意を払っていませんでしたが、リモートMCPをサービスとして提供する場合は、適切なキャッシュ機構の構築が必須だと実感しました。

![BC842A98-124A-4386-A5FB-0EE059BA669A_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/d9605cf9-b600-40ec-843d-92ca5f56807a.jpeg)


## 参考資料

https://github.com/cloudflare/ai/blob/main/demos/remote-mcp-github-oauth/README.md

https://developers.cloudflare.com/agents/guides/remote-mcp-server/