GitHub MCPプラグインがHTTP 400で失敗する原因と対処法
公式マーケットプレイスのgithubプラグインが「HTTP 400」で接続に失敗する既知issueを、公式docsのMCP設定の仕組みと突き合わせて原因を切り分け、settings.jsonでの対処法をまとめます。
Claude Codeの公式マーケットプレイスからplugin:github:githubを有効にすると、https://api.githubcopilot.com/mcp/への接続が「HTTP 400」で失敗する報告がGitHub Issue #64654として続いています。2026年6月2日に登録され、2026年9月22日の最新コメントの後もopenのままです。
原因は1つではありません。同じ「HTTP 400」の裏に、少なくとも2種類の異なる応答文言が混ざっています。原因が特定されているのは、Authorizationヘッダーのトークンが展開されないケースです。直し方は~/.claude/settings.jsonのenvブロックにトークンを書くことです。
報告はmacOSラベルで登録されましたが、コメントにはLinuxとWindowsでの再現もあり、OS固有ではないようです。
8月19日のコメントには、環境変数が未設定のとき、汎用の400ではなく明示的に失敗させる改善案も添えられています。全21件のコメントに、Anthropic側の回答はありません。
症状 — /mcpに失敗が出る、または表示は正常なのにツールが動かない
典型的な症状は、/mcpパネルかセッション起動時に次のエラーが出て、githubプラグインのツールが呼べなくなるものです。
Failed to reconnect to plugin:github:github: HTTP 400 at https://api.githubcopilot.com/mcp/やっかいなのは、失敗しているのに接続表示が正常に見える場合があることです。公式MCP docsによると、設定の${VAR}が指す環境変数が未設定でデフォルト値もないとき、Claude Codeはclaude mcp listと/mcpに変数名つきの警告を出します。ところが2026年8月19日のコメントでは、Claude Code v2.1.235でclaude mcp listが✔ Connectedと表示し続けたまま、デバッグログには400の失敗が記録されていました。
claude mcp listは、v2.1.289の--helpによれば承認済みのサーバーをヘルスチェックするコマンドです。表示が正常でも、ツールを呼べるとは限りません。状態表示だけを頼りに切り分けると、問題のない箇所を疑って時間を使います。
切り分けの手順 — デバッグログの文言を見る
v2.1.219より前のclaude mcp listとclaude mcp getは、失敗の理由を出さず失敗の状態だけを示していました。v2.1.219以降は、失敗したサーバーの行に、HTTPステータスとサーバーが返したテキストが付きます。次のコマンドでIssue:行を読めます。
claude mcp get plugin:github:githubただし、接続表示が正常なまま現象が起きる報告(8月19日)もあります。2種類の原因を見分ける確実な手がかりは、デバッグログの文言です。
HTTP 400の切り分け
- 1
デバッグログをファイルに出す
--debug-fileを付けて起動すると、デバッグモードが暗黙に有効になり、指定したパスにログが書かれます。 - 2
githubプラグインの行を探す
ログを
plugin:github:githubで検索します。接続に成功した場合はSuccessfully connected (transport: http)、失敗した場合はConnection failed (400)の行が見つかります。 - 3
400の後ろの文言を読む
400の続きに書かれた文言が、後述の早見表の入口になります。
Authorization header is badly formattedなのか、malformed payloadなのかで、直す場所が変わります。
claude --debug-file /tmp/claude-debug.log -p "say ok"
grep -n "plugin:github:github" /tmp/claude-debug.log--debugにはカテゴリの絞り込みも使えます。公式CLIリファレンスでは、絞り込みが効くのは--debug='mcp'のように=でつなぐ書き方だけで、スペースで区切ると絞り込みなしのデバッグモードになります。--debug-fileはCLAUDE_CODE_DEBUG_LOGS_DIRより優先されます。この変数の名前にはDIRとありますが、指定するのはディレクトリではなくファイルのパスです。
--debug-fileを省くと、ログは~/.claude/debug/<セッションID>.txtに書かれます。8月19日の報告に載っている失敗時のログは、次の形でした。
[ERROR] MCP server "plugin:github:github" Connection failed (400):
Streamable HTTP error: Error POSTing to endpoint:
bad request: Authorization header is badly formatted文言がAuthorization header is badly formattedのとき — ヘッダーの環境変数が展開されていない
githubプラグインの.mcp.jsonは、Authorizationヘッダーを次のように組み立てます。8月19日の報告に載っている設定です。
{ "headers": { "Authorization": "Bearer ${GITHUB_PERSONAL_ACCESS_TOKEN}" } }${GITHUB_PERSONAL_ACCESS_TOKEN}は、Claude Code自身のプロセスの環境変数から展開されます。ここに値が入っていないと、公式MCP docsのとおり、変数は展開されないまま${VAR}という文字列で使われます。GitHub側はBearer ${GITHUB_PERSONAL_ACCESS_TOKEN}を不正な形式のヘッダーとして、400で拒否します。
公式docsには別の挙動もあります。ANTHROPIC_API_KEYやNPM_TOKENのように、認証情報としてdocsが例に挙げる名前は、リモートサーバーのurlとheadersでは警告なしに空として読まれます。この場合、サーバーにはBearer だけが届きます。GITHUB_PERSONAL_ACCESS_TOKENはdocsの例に入っておらず、8月19日の報告でも、settings.jsonのenvに入れれば通常どおり展開されました。
同じ報告は、同じアカウント・同じエンドポイントへ4通りのリクエストを送って、壊れている側を切り分けています。
| リクエスト | 結果 |
|---|---|
有効なトークン + 正しいinitialize | 結果200 |
| 有効なトークン + 空のボディ | 結果400 POST requires a non-empty body |
空のトークン + 正しいinitialize | 結果400 Authorization header is badly formatted |
未展開の${GITHUB_PERSONAL_ACCESS_TOKEN} + 正しいinitialize | 結果400 Authorization header is badly formatted |
3行目と4行目は、ログに出た失敗と同じ文言です。エンドポイント・トークン・アカウントは正常で、壊れているのはヘッダーの組み立てだとわかります。
シェルで再現できないときの罠
この不具合は、トークンをexport済みのシェルからは再現しません。子プロセスのclaudeが環境変数を引き継ぐためです。8月19日の報告者は、これで一度は誤った結論に達したと書いています。再現するには、変数を明示的に外して起動します。
env -u GITHUB_PERSONAL_ACCESS_TOKEN claude --debug -p "say ok"同じコマンドで、settings.jsonのenvにトークンを書いた場合はSuccessfully connected (transport: http)が出て、440msで接続できたと報告されています。変数を外した状態で、envの設定だけが効いているかを調べる方法です。
対処 — envブロックにトークンを書く
原因がヘッダーの展開なら、GitHubのPersonal Access Tokenを~/.claude/settings.jsonのenvブロックに書きます。
{
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
}保存後にClaude Codeを起動し直し、先ほどのログでエラーが消えているかを見ます。変数名はGITHUB_PERSONAL_ACCESS_TOKENでなければなりません。プラグインの.mcp.jsonがこの名前を参照しているので、GITHUB_PATやGITHUB_TOKENに値を入れても読まれません。
~/.claude/settings.jsonはホームディレクトリのユーザー設定で、リポジトリには含まれません。トークンは平文で残るため、チームで共有するプロジェクトの設定ファイルには書かない、という構成もあります。トークンの発行や、有効にするtoolsetsの絞り方はGitHub MCPサーバーの使い方で扱っています。
トークンを渡す2つの経路
settings.jsonのenv
7月7日のコメントがこの方法で直ったと報告し、7月9日と7月13日にも「動いた」という追認が続きました。8月19日の報告も、展開を行うプロセスの環境に確実に入る方法として挙げています。
シェルのexport
7月7日のコメントは.zshrcへの追記でも直ったと書いています。一方、8月19日の報告では.zshenvと.zshrcのexportが届かず、起動方法によって結果が分かれるとされています。
envを書いても直らないとき
settings.jsonを直しても同じ400が出る場合の報告は、2つあります。
古いローカル設定が残っている
7月13日のコメントは、GitHub MCPサーバーのインストールガイドに従うよう勧め、過去に誤って設定したローカルのgithubを削除する手順(コメントのclaude removeはclaude mcp removeの誤記)を示しています。Claude Code内ではなく通常のターミナルでclaude mcp add-jsonを実行する、という指定も付いていました。
claude mcp list
claude mcp remove githubclaude mcp removeは、v2.1.289の--helpによれば、--scopeを省くとサーバーが存在するスコープから削除します。スコープはlocal・user・projectのいずれかです。公式MCP docsには、同じ名前を複数のスコープに別のエンドポイントで定義すると、claude mcp listと/mcpに警告が出るという記述があります。警告が出たときは--scopeを付けて、残すもの以外を削除します。
制限を設定した直後に起きた報告
9月22日のコメントは別の経緯です。Claude Coworkを初めて使った直後に400が出たという報告で、投稿者自身が推測だと断っています。
- 新しいリポジトリで作業していたが、GitHub MCPのセッションが許可しているリポジトリの一覧にそのリポジトリは入っていなかった
- Claude Codeにドメイン制限を勧められ、
github.comだけに設定した - 直後から
githubcopilot.comが400を返し、ghコマンドはシェルで動くのに、MCPは接続済みと表示されたまま使えなくなった - 制限を外しても戻らず、Claude CodeからGitHub MCPを完全に削除し、GitHub側の連携を全部外して、GitHubの設定からClaudeのアプリを入れ直して復旧した
認証ヘッダーの文言は示されておらず、上の原因と同じ現象かどうかはわかりません。投稿者の推測どおりなら、ドメイン制限を設定した直後に起きたときは、環境変数より先に許可リストを見直す手がかりになります。
文言がmalformed payloadのとき — 未確定の原因
最初の報告者は、空のペイロードでエンドポイントへ直接curlするとmalformed payload: invalid message version tag ""; expected "2.0"が返るのを確かめました。そこから、プラグインが"jsonrpc": "2.0"を含まないリクエストを送っているのではないかと推測しています。推測の根拠は、手動で送った空ペイロードへの応答です。プラグインが実際に送ったリクエストの中身は示されていません。
8月19日の報告者が送った4通りのリクエストのうち、有効なトークンで空ボディを送った行はPOST requires a non-empty bodyでした。最初の報告者が引用したinvalid message version tagとは文言が違います。同じ「空のリクエスト」でも、エンドポイントの応答は一致していません。原因2を再現条件つきで裏付けるコメントは、Issue上にありません。
エラー文言別の早見表
| デバッグログの文言 | まず試すこと | 次に試すこと |
|---|---|---|
Authorization header is badly formatted | まず試すことsettings.jsonのenvにトークンを設定 | 次に試すこと古いローカルgithub設定の削除(claude mcp remove) |
Incompatible auth server: does not support dynamic client registration | まず試すこと「Authenticate」ボタンを使わず、ヘッダー方式に切り替え | 次に試すことOAuth接続エラーの記事を参照 |
malformed payload: invalid message version tag | まず試すことAuthorization header is badly formattedが別に出ていないか再確認 | 次に試すこと環境変数の展開を疑って同じ対処を試す |
| 接続済み表示のまま400(ドメイン制限の直後) | まず試すこと許可リストやドメイン制限の設定を見直す | 次に試すことClaude CodeからGitHub MCPを削除し、GitHub側の連携を入れ直す(1件の報告に基づく) |
3行目は、原因2の文言に確定した対処がないため、まず原因1と同じ展開ミスを疑う順序です。
githubプラグイン以外のMCPサーバーで接続エラーが出たときの一般的な切り分けは、MCPサーバーに接続できないときの切り分け手順にまとめています。
まとめ
接続済みの表示は、ツールが使える証拠にはなりません。原因はデバッグログで400の後ろに続く文言から決めます。malformed payloadは原因が未確定なので、Issueのコメントを追うことになります。