🛡️

GitHub MCPサーバーをPATなしで運用したくてデバイスフローでアクセストークンを取得するCLIを実装した

に公開

はじめに

最近Copilot AgentなどのAIエージェントにGitHubのIssueやPull Requestの情報を読ませるために、GitHubのMCPサーバーを利用しています。しかしながら、GitHub公式のMCPサーバーの利用手順に従うとPAT(Personal Access Token)を利用しなければなりません。
利用が推奨されるFine-Grained PATは有効期限を最短で1日から設定することができますが、GitHubの設定ページにブラウザでアクセスして更新しなければならないため、人によっては手間を避けるために長期間生存するトークンに設定してしまったりします。

{
  "mcpServers": {
    "github": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "GITHUB_PERSONAL_ACCESS_TOKEN",
        "ghcr.io/github/github-mcp-server"
      ],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"
      }
    }
  }
}

https://github.com/github/github-mcp-server?tab=readme-ov-file#usage-with-vs-code

そこで、GitHub MCPサーバーをPATなしで運用することができないか考えてみました。
まず、GitHub MCPサーバーの実装を読むと内部ではgo-githubを利用してクライアントを生成しているようです。さらに、go-githubのコードを読むと、PATをAuthorizationヘッダーにBearerトークンとして設定しているため、PATに限らずGitHubが発行するアクセストークンを利用することができそうなことがわかります。

https://github.com/github/github-mcp-server/blob/853323d74fa41a913079b36ffe38d7314b17f9ab/internal/ghmcp/server.go#L58

https://github.com/google/go-github/blob/ad671c46a896a07c1ac3053a0abca48493acafbe/github/github.go#L350

GitHubにおけるアクセストークンの種類

GitHubには上述したPAT(Classic / Fine-Grained PAT)の他にも、OAuth Appを介して取得できるアクセストークンと、GitHub Appを介して取得できアクセストークンがあります。ユーザーのアクセストークンでなく、GitHub App自身がClient Credentials Grantで取得するアクセストークンなどもありますが、ここでは割愛します。

GitHub Appから取得できるユーザーのアクセストークンは接頭語がghu_で始まり、有効期限が8時間に設定されています。GitHub Appを介してアクセストークンを取得することで、比較的短い期限のアクセストークンを利用して運用できそうです。

https://docs.github.com/ja/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app#about-user-access-tokens

一方で、OAuth Appを介して取得できるアクセストークンは接頭語がgho_で始まり、有効期限がありません。アクセストークンを失効するためにはGitHubの設定ページにある明示的な操作か、1年間利用しなかった場合だけのようです。また、公式ページを読むとGitHub Appとして作成することが推奨されているので、今回はGitHub Appを利用することにしました。

https://docs.github.com/ja/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app

デバイスフローによるアクセストークンの取得

デバイスフローとは

GitHubからアクセストークンを取得するために、どのような認可フローを実装する必要があるでしょうか。GitHubではWebアプリケーションフローと呼んでいる、よく知られた認可コードフローによるものと、RFC 8628で定義されているデバイスフローがサポートされています。
デバイスフローは元来、テレビやゲーム機、IoT機器のようなブラウザを含む入力手段を持たないデバイスで認可を実施するために策定されました。
以下のようなシーケンスで、認可を得たいデバイスはデバイス認可リクエストを認可サーバーに送り、返ってきたユーザーコードとアクセス先をユーザーに提示して認可を得ます。デバイスはトークンリクエストをポーリングして、ユーザーの認可に基づくアクセストークンの返却を待ちます。
このフローを利用することにより、Webアプリケーションフローで必要なローカルでサーバーを立ててコールバックURIへのリダイレクトを待つことがなくなり、純粋にCLIツールとして実装することができます。

https://datatracker.ietf.org/doc/html/rfc8628

CLIツール

CLIツールを以下のリポジトリに作成しました。あらかじめGitHub Appを作成してデバイスフローを有効化した上で、以下のコマンドを実行することでURIとユーザーコードが表示されます。提示されたURIにブラウザでアクセスして、GitHubのアカウントでログインしたのちにユーザーコードを入力することで、CLIからアクセストークンが取得できます。

go run main.go --client_id <GitHub AppのClient ID>

実行後の出力イメージ

device authorization grant is requrested. Please signin from uri and enter user_code as below.
uri: xxxxx
user_code: xxxxx


waiting to authorize...
success to get user access token
ghu_xxxxxxx

https://github.com/manaty226/github-device-authorization

おわりに

GitHubのMCPサーバーをPATなしで運用するために、デバイスフローを利用してアクセストークンを取得するCLIツールを実装しました。
個人的にはDevice Authorization Grantは好きな仕様なので、GitHubがサポートしているのは喜ばしい気持ちですし、実際にこうした場面で利用できるのも嬉しいです。当初はMCPサーバー自身でデバイスフローを実装できないか考えたのですが、2025-03-25バージョンの最新のMCPの仕様ではサーバー側からユーザーに対してアクセス先とユーザーコードを提示するフローを差し込めそうなリクエスト-レスポンスのパターンがありませんでした。MCPサーバーの認可に関する仕様ではHTTPベースのトランスポートのみOAuth 2.1をサポートしていますが、現在存在するMCPサーバーの実装はSTDIOベースのものも多いので、デバイスフローができたら面白いなと思います。

https://modelcontextprotocol.io/specification/2025-03-26/server

Discussion