Skip to main content
Glama
hotaka052

github-mcp-wrapper

by hotaka052
README.md
# github-mcp-wrapper

GitHub の MCP サーバーへの接続を肩代わりする薄いサーバー。

呼ぶ側([tsuzuri](https://github.com/hotaka052/tsuzuri) の runner)は**このサービスの
URL だけ**を知っていればよく、GitHub App の秘密鍵を一切持たない、という形にするための repo。

```
before:  runner ──MCP──> api.githubcopilot.com   (runner が App の秘密鍵を持つ)
after:   runner ──MCP──> このサービス ──MCP──> api.githubcopilot.com
```

[linear-mcp-wrapper](https://github.com/hotaka052/linear-mcp-wrapper) と対になる repo。
中身の作りは揃えてある。

## いまの状態: 置いただけ

**まだサーバーとして動かない。** tsuzuri から認証まわりのコードを退避させただけで、
プロキシ本体(`src/main.py`)はまだ無い。tsuzuri 側も**まだ何も消していない**ので、
そちらは今までどおり直接 GitHub を見ている。

| ファイル | 出どころ |
| --- | --- |
| `src/auth.py` | tsuzuri の `runner/src/github.py` を**そのままコピー**(1 文字も変えていない) |
| `src/config.py` | tsuzuri の `runner/src/config.py` の GitHub の部分だけ |
| `src/logs.py` | tsuzuri の `runner/src/logs.py` から `emit` だけ |
| `env/master.yaml` | tsuzuri の `runner/env/master.yaml` の GitHub の部分だけ |

`GITHUB_REPO` は持ってきていない。あれは「繋ぐのをやめる口」で、呼ぶ側の判断なので
tsuzuri に残っている。

### `src/auth.py` の docstring で、もう合っていない記述

そのままコピーした副作用。**中身の理屈は全部生きているが、参照先の名前だけが古い。**

- 「`linear.py` と対になる場所」— その `linear.py` は linear-mcp-wrapper の
  `src/auth.py` になった
- 「`config.py` の Linear の項と同じ理由」— 同上
- 「MCP サーバーへの接続ヘッダに使う(`mcp_tools.github`)。**唯一の呼び出し元。**」—
  ここを呼ぶのは、これから書くプロキシ本体になる

**「REST を直接叩くのはトークン交換の 1 本だけ。それ以外を足さないこと」は生きている。**
repo が別になって足しやすくなったぶん、むしろここから効く。

## これから書くもの

1. **プロキシ本体(`src/main.py`)。** ここが本番。素通しの HTTP プロキシでは済まない:
   - POST のレスポンスが JSON のことも SSE のこともある
     (クライアントは `Accept: application/json, text/event-stream` で来る)
   - 別に **GET で長寿命の SSE を張る**(サーバー→クライアント方向。切れたら再接続してくる)
   - **`mcp-session-id` と `mcp-protocol-version` を両方向で素通しする**必要がある
   - セッション終了は DELETE
   - **バッファしないこと。** `await response.json()` で受けると SSE がそこで固まる
2. **呼ぶ側の認証。** クライアントから来る `Authorization` は Cloud Run の OIDC ID token
   にして、ここで installation token(`auth.py`)に差し替える。
   Cloud Run は `--no-allow-unauthenticated` で立てる
3. **ツールの絞り込み(あとで)。** いまは tsuzuri 側の `IMPLEMENT_TOOL_FILTER` が
   `get_file_contents,list_commits,get_commit` の 3 本に絞っている。動いてから移す

### 3 をやるときに知っておくこと

**上流のツールを全部そのまま渡すと、呼ぶ側のモデル呼び出しごと落ちる。**
GitHub の MCP は 90 本を超えて公開していて、Vertex の function declaration が
受け付けないスキーマが混ざっていると `400 INVALID_ARGUMENT` になる(tsuzuri で実測)。

**`create_branch` / `push_files` / `create_or_update_file` を許可リストに入れないこと。**
レビューを通る前の実装が GitHub へ出ていく。URL が readonly なので二重に締まっているが、
その readonly がこちら側に移ったことで、呼ぶ側からは外せなくなった。

## 開発

```sh
uv sync
```

ローカルでは `GITHUB_TOKEN`(PAT)を入れれば App の交換を通らずに済む。
Cloud Run では App ID + installation ID + 秘密鍵(Secret Manager)を使う。