{"id":729,"date":"2026-08-09T18:27:27","date_gmt":"2026-08-09T09:27:27","guid":{"rendered":"https:\/\/tako.nakano.net\/blog\/?p=729"},"modified":"2026-08-09T18:30:08","modified_gmt":"2026-08-09T09:30:08","slug":"mcp-oauth2-proxy","status":"publish","type":"post","link":"https:\/\/tako.nakano.net\/blog\/2026\/08\/mcp-oauth2-proxy\/","title":{"rendered":"Adding Authentication to Remote MCP Servers Using a Lightweight Proxy"},"content":{"rendered":"<h1>\u30ea\u30e2\u30fc\u30c8 MCP \u30b5\u30fc\u30d0\u30fc\u306e\u8a8d\u8a3c\u3092\u8efd\u91cf\u30d7\u30ed\u30ad\u30b7\u3067\u884c\u3046\u65b9\u6cd5<\/h1>\n<p>English follows Japanese.<\/p>\n<h2>\u6982\u8981<\/h2>\n<p>\u8a8d\u8a3c\u6a5f\u80fd\u3092\u6301\u305f\u306a\u3044 MCP \u30b5\u30fc\u30d0\u30fc\u306b\u5bfe\u3057\u3066\u3001\u8efd\u91cf\u306a OAuth2 \u306e Proxy \u3092\u4f7f\u3063\u3066\u8a8d\u8a3c\u6a5f\u80fd\u3092\u8ffd\u52a0\u3059\u308b\u3053\u3068\u304c\u53ef\u80fd\u3067\u3059\u3002\u4f8b\u3048\u3070\u3001\u30af\u30e9\u30a6\u30c9\u4e0a\u306b\u7acb\u3061\u4e0a\u3052\u305f MCP \u30b5\u30fc\u30d0\u30fc\u3092\u3001\u5b89\u5168\u306b\u4fdd\u8b77\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<code>\/.well-known\/oauth-protected-resource<\/code> \u306e\u90e8\u5206\u3067\u5de5\u592b\u304c\u5fc5\u8981\u3067\u3059\u3002OAuth2 Proxy \u306f\u6a5f\u80fd\u8c4a\u5bcc\u3067\u3059\u304c\u4e00\u90e8\u6a5f\u80fd\u306e\u4e0d\u8db3\u3067\u76ee\u7684\u3092\u9054\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002<\/p>\n<h2>\u306f\u3058\u3081\u306b<\/h2>\n<p>\u672c\u8a18\u4e8b\u3067\u306f\u3001\u300cMCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8 -> MCP \u30b5\u30fc\u30d0\u30fc -> \u64cd\u4f5c\u5148\u30b5\u30fc\u30d3\u30b9\u300d\u306e\u95a2\u4fc2\u306b\u304a\u3044\u3066\u3001MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3092\u64cd\u4f5c\u3057\u3066\u3044\u308b\u30e6\u30fc\u30b6\u30fc\u306e\u8a8d\u8a3c\u306b\u3064\u3044\u3066\u8b70\u8ad6\u3057\u3066\u3044\u307e\u3059\u3002<br \/>\n\u7279\u306b\u3001\u300cMCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8 -> MCP \u30b5\u30fc\u30d0\u30fc\u300d\u306e\u90e8\u5206\u306b\u304a\u3044\u3066\u3001MCP \u30b5\u30fc\u30d0\u30fc\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u30e6\u30fc\u30b6\u30fc\u306e\u8a8d\u8a3c\u3092\u884c\u3046\u65b9\u6cd5\u3068\u3057\u3066\u3001\u81ea\u4f5c\u306e\u8efd\u91cf\u306a OAuth2 \u306e Proxy \u3092\u4f7f\u3046\u65b9\u6cd5\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n<h2>\u80cc\u666f\u30fb\u72b6\u6cc1\u306e\u6574\u7406<\/h2>\n<h3>\u80cc\u666f\u30fb\u72b6\u6cc1\u306e\u6574\u7406\u306e\u307e\u3068\u3081<\/h3>\n<p>\u300c\u30e6\u30fc\u30b6\u30fc\u6a29\u9650\u578b\u300d\u3068\u300c\u5171\u7528\u578b\u300d\uff08\u5f8c\u8ff0\uff09\u306e MCP \u30b5\u30fc\u30d0\u30fc\u306b\u304a\u3044\u3066\u3001\u300c\u30e6\u30fc\u30b6\u30fc\u6a29\u9650\u578b\u300d\u306f\u8a8d\u8a3c\u6a5f\u80fd\u3092\u6709\u3057\u3066\u3044\u307e\u3059\u3002\u300c\u5171\u7528\u578b\u300d\u306b\u8a8d\u8a3c\u6a5f\u80fd\u3092\u8ffd\u52a0\u3057\u305f\u3044\u3082\u306e\u3067\u3059\u3002<\/p>\n<h3>\u80cc\u666f\u30fb\u72b6\u6cc1\u306e\u6574\u7406\u306e\u8a73\u7d30\uff08\u30e6\u30fc\u30b6\u30fc\u6a29\u9650\u578b\u3068\u5171\u7528\u578b\u306e\u533a\u5225\uff09<\/h3>\n<p>LLM \u304c\u7d44\u307f\u8fbc\u307e\u308c\u305f\u30a8\u30fc\u30b8\u30a7\u30f3\u30c8\u7b49\u306b\u300c\u3042\u308b\u30b5\u30fc\u30d3\u30b9\/\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u306b\u95a2\u3059\u308b\u4f5c\u696d\u300d\u3092\u4f9d\u983c\u3059\u308b\u5834\u5408\u3001MCP \u30b5\u30fc\u30d0\u30fc\u304c\u4fbf\u5229\u3067\u3059\u3002\u30e6\u30fc\u30b6\u30fc\uff08\u5229\u7528\u8005\uff09\u304c\u958b\u767a\u8005\uff08IT \u30a8\u30f3\u30b8\u30cb\u30a2\uff09\u3067\u3042\u308a\u3001\u304b\u3064\u3001\u30ed\u30fc\u30ab\u30eb\u306b\u30b3\u30de\u30f3\u30c9\u5b9f\u884c\u74b0\u5883\u3084\u30b3\u30f3\u30c6\u30ca\u74b0\u5883\u3092\u6301\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u30ed\u30fc\u30ab\u30eb\u3067 MCP \u30b5\u30fc\u30d0\u30fc\u3092\u7acb\u3061\u4e0a\u3052\u3066\u3001\u4f5c\u696d\u3092\u3055\u305b\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n<p>\u4e00\u65b9\u3067\u3001\u975e IT \u30a8\u30f3\u30b8\u30cb\u30a2\u306e\u5229\u7528\u8005\u304c MCP \u30b5\u30fc\u30d0\u30fc\u3092\u4f7f\u3046\u5834\u5408\u3082\u8003\u3048\u308b\u3068\u3001\u30c1\u30fc\u30e0\u5185\u306e\u958b\u767a\u8005\u304c MCP \u30b5\u30fc\u30d0\u30fc\u3092\u7acb\u3061\u4e0a\u3052\u305f\u308a\u3001\u4f1a\u793e\u3067\u8ab0\u304b\u304c\u4ee3\u8868\u3057\u3066 MCP \u30b5\u30fc\u30d0\u30fc\u3092\u7acb\u3061\u4e0a\u3052\u305f\u308a\u3057\u3066\u304a\u304f\u3068\u6709\u7528\u3067\u3059\u3002\u591a\u6570\u306e\u30e6\u30fc\u30b6\u30fc\u304c\u500b\u3005\u306e\u74b0\u5883\u3067 MCP \u30b5\u30fc\u30d0\u30fc\u3092\u7acb\u3061\u4e0a\u3052\u308b\u306e\u3067\u306f\u306a\u304f\u3001\u30ea\u30e2\u30fc\u30c8\u306e MCP \u30b5\u30fc\u30d0\u30fc\u3092\u7acb\u3061\u4e0a\u3052\u3066\u304a\u304d\u3001\u5229\u7528\u8005\u306f\u305d\u3053\u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u4f5c\u696d\u3092\u4f9d\u983c\u3059\u308b\u5f62\u3067\u3059\u3002\u500b\u3005\u306e\u74b0\u5883\u3067 MCP \u30b5\u30fc\u30d0\u30fc\u3092\u7acb\u3061\u4e0a\u3052\u308b\u306e\u306f\u4e0d\u4fbf\u3067\u3059\u306d\u3002<\/p>\n<p>\u305f\u3060\u3057\u3001\u30ea\u30e2\u30fc\u30c8\u306e\uff08\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u306b\u516c\u958b\u3059\u308b\u3088\u3046\u306a\uff09MCP \u30b5\u30fc\u30d0\u30fc\u3092\u7acb\u3061\u4e0a\u3052\u308b\u5834\u5408\u3001\u30a2\u30af\u30bb\u30b9\u5236\u5fa1\u3092\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3064\u307e\u308a\u3001\u8a8d\u8a3c\u30fb\u8a8d\u53ef\u306e\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3059\u3002\u5168\u4e16\u754c\u306e\u8ab0\u304b\u3089\u3082\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u3088\u3046\u306b\u3057\u3066\u3057\u307e\u3046\u3068\u3001\u60aa\u610f\u306e\u3042\u308b\u5229\u7528\u8005\u304c MCP \u30b5\u30fc\u30d0\u30fc\u3092\u4f7f\u3063\u3066\u4e0d\u6b63\u306a\u4f5c\u696d\u3092\u884c\u3046\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u3053\u3067\u3001\u30ea\u30e2\u30fc\u30c8\u306e MCP \u30b5\u30fc\u30d0\u30fc\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u969b\u306b\u306f\u3001\u30e6\u30fc\u30b6\u30fc\u306e\u8a8d\u8a3c\u60c5\u5831\u3092\u78ba\u8a8d\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\uff08\u9650\u5b9a\u7684\u306a\u72b6\u6cc1\u3067\u306f\u3001IP \u5236\u9650\u3092\u4f7f\u3046\u3053\u3068\u3082\u53ef\u80fd\u3067\u3059\u306d\uff09\u3002<\/p>\n<p>MCP \u30b5\u30fc\u30d0\u30fc\u306b\u306f\u3001\u3044\u304f\u3064\u304b\u306e\u52d5\u4f5c\u3084\u8a8d\u8a3c\u306e\u7a2e\u985e\u304c\u3042\u308b\u3068\u601d\u3044\u307e\u3059\u304c\u3001\u3053\u3053\u3067\u306f\u3001\u4e00\u65e6\u4ee5\u4e0b\u306e2\u7a2e\u985e\u306b\u5206\u985e\u3057\u3001\u4eca\u56de\u306f\u300c\u5171\u7528\u578b\u300d\u306b\u8a8d\u8a3c\u3092\u5165\u308c\u308b\u8a71\u306b\u7126\u70b9\u3092\u5f53\u3066\u3066\u8aac\u660e\u3057\u307e\u3059\u3002<\/p>\n<p>\u5206\u985e:<\/p>\n<ul>\n<li>\u30e6\u30fc\u30b6\u30fc\u6a29\u9650\u578b: \u30e6\u30fc\u30b6\u30fc\u306e\u6a29\u9650\u3067\u64cd\u4f5c\u3092\u884c\u3046<\/li>\n<li>\u5171\u7528\u578b: \uff08\u30a2\u30af\u30bb\u30b9\u5148\u306b\u306f API \u30ad\u30fc\u7b49\u3067\u8a8d\u8a3c\u3057\uff09\u5171\u901a\u306e\u6a29\u9650\u3067\u64cd\u4f5c\u3092\u884c\u3046<\/li>\n<\/ul>\n<p>\u300c\u30e6\u30fc\u30b6\u30fc\u6a29\u9650\u578b\u300d\u306e MCP \u30b5\u30fc\u30d0\u30fc\u306f\u3001\u300cMCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8 -> MCP \u30b5\u30fc\u30d0\u30fc -> \u64cd\u4f5c\u5148\u30b5\u30fc\u30d3\u30b9\u300d\u306e\u95a2\u4fc2\u306b\u304a\u3044\u3066\u3001\u64cd\u4f5c\u5148\u30b5\u30fc\u30d3\u30b9\u3042\u308b\u3044\u306f\u7b2c\u4e09\u8005\u304c\u3001\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u6307\u793a\u3057\u3066\u3044\u308b\u30e6\u30fc\u30b6\u30fc\u3092\u8a8d\u8a3c\u3057\u3001\u64cd\u4f5c\u5148\u30b5\u30fc\u30d3\u30b9\u306b\u5bfe\u3057\u3066\u300c\u305d\u306e\u8a8d\u8a3c\u3055\u308c\u305f\u30e6\u30fc\u30b6\u30fc\u306e\u6a29\u9650\u3067\uff08\u30e6\u30fc\u30b6\u30fc\u306b\u6210\u308a\u4ee3\u308f\u3063\u3066\uff09\u64cd\u4f5c\u3092\u884c\u3046\u300d\u3082\u306e\u3067\u3059\u3002\u3064\u307e\u308a\u3001MCP \u30b5\u30fc\u30d0\u30fc\u306f\u3001\u5229\u7528\u8005\u306e\u8a8d\u8a3c\u60c5\u5831\u3092\u4f34\u3063\u3066\u64cd\u4f5c\u5148\u30b5\u30fc\u30d3\u30b9\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n<p>\u3053\u306e\u3088\u3046\u306a\u300c\u30e6\u30fc\u30b6\u30fc\u6a29\u9650\u578b\u300d\u306e MCP \u30b5\u30fc\u30d0\u30fc\u306f\u3001MCP \u30b5\u30fc\u30d0\u30fc\u3001\u3042\u308b\u3044\u306f\u3001\u64cd\u4f5c\u5148\u30b5\u30fc\u30d3\u30b9\u304c OAuth2 \uff08\u7b49\uff09\u306e\u8a8d\u8a3c\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u3059\u3002\u8a8d\u8a3c\u30fb\u8a8d\u53ef\u306e\u6a5f\u80fd\u306f\u4e88\u3081\u642d\u8f09\u3055\u308c\u3066\u3044\u308b\u305f\u3081\u3001\u7279\u5225\u306a\u3053\u3068\u3092\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u305b\u3093\u3002<\/p>\n<p>\u3068\u3053\u308d\u304c\u3001\u300c\u5171\u7528\u578b\u300d\u306e MCP \u30b5\u30fc\u30d0\u30fc\u306f\u3001\u64cd\u4f5c\u5148\u30b5\u30fc\u30d3\u30b9\u306b\u5bfe\u3057\u3066\u5171\u901a\u306e\u6a29\u9650\u3067\u30a2\u30af\u30bb\u30b9\u3059\u308b\u305f\u3081\u3001\u5229\u7528\u8005\u306e\u8a8d\u8a3c\u60c5\u5831\u3092\u4f7f\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u9006\u306b\u3001\u300c\u305d\u306e MCP \u30b5\u30fc\u30d0\u30fc\u3092\u5229\u7528\u3057\u3066\u826f\u3044\u306e\u304c\u8ab0\u304b\u300d\u3092\u8a8d\u53ef\u3059\u308b\u4ed5\u7d44\u307f\u306f\u3001\u901a\u5e38\u642d\u8f09\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u5229\u7528\u8005\u5171\u901a\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\uff08API \u30ad\u30fc\u3068\u8a00\u3063\u3066\u3082\u826f\u3044\u3067\u3057\u3087\u3046\uff09\u3092\u4f7f\u3046\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u304c\u3001\u5171\u901a\u306b\u3057\u3066\u3057\u307e\u3046\u3068\u3001\u8ab0\u304c\u305d\u306e MCP \u30b5\u30fc\u30d0\u30fc\u3092\u4f7f\u3063\u305f\u306e\u304b\u3092\u8ffd\u8de1\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\u3057\u3001\u79d8\u533f\u60c5\u5831\u306e\u30ed\u30fc\u30c6\u30fc\u30b7\u30e7\u30f3\u7b49\u306e\u904b\u7528\u3082\u96e3\u3057\u304f\u306a\u308a\u307e\u3059\u3002\u305d\u3053\u3067\u3001\u30ea\u30e2\u30fc\u30c8\u306e MCP \u30b5\u30fc\u30d0\u30fc\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u969b\u306b\u3001\u30e6\u30fc\u30b6\u30fc\u306e\u8a8d\u8a3c\u30fb\u8a8d\u53ef\u306e\u6a5f\u80fd\u3092\u8ffd\u52a0\u3057\u305f\u304f\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>\u300c\u5171\u7528\u578b\u300d\u3068\u3057\u3066\u306f\u3001\u4f8b\u3048\u3070\u3001\u64cd\u4f5c\u5148\u30b5\u30fc\u30d3\u30b9\u306b\u5bfe\u3059\u308b API \u30ad\u30fc\u306e\u767a\u884c\u3092\u53d7\u3051\u3066\u3001\u305d\u308c\u3092 Secret \u7d4c\u7531\u3067\u767b\u9332\u3057\u3066\u304a\u304f\u30bf\u30a4\u30d7\u306e MCP \u30b5\u30fc\u30d0\u30fc\u3092\u60f3\u5b9a\u3057\u3066\u3044\u307e\u3059\u3002\u78ba\u304b\u306b\u3001\u3053\u3053\u306b\u300cAPI \u30ad\u30fc\u300d\u306f\u5b58\u5728\u3057\u3066\u3057\u307e\u3063\u3066\u3044\u307e\u3059\u3002\u3057\u304b\u3057\u3001\u300c\u793e\u5185\u306b\u3044\u308b\u591a\u6570\u306e\u5229\u7528\u8005\u300d\u306b\u300c\u5171\u901a\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\uff08API \u30ad\u30fc\uff09\u300d\u3092\u6e21\u3059\u2026\u306e\u306f\u3001\u3061\u3087\u3063\u3068\u9055\u3046\u3060\u308d\u3046\u2026\u3068\u3044\u3046\u3053\u3068\u3092\u8a00\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n<p>\u300cMCP \u30b5\u30fc\u30d0\u30fc -> \u64cd\u4f5c\u5148\u30b5\u30fc\u30d3\u30b9\u300d\u306e\u90e8\u5206\u306f\u5229\u7528\u8005\u304b\u3089\u898b\u3048\u307e\u305b\u3093\u3002API \u30ad\u30fc\u3092\u79d8\u533f\u60c5\u5831 (Secret) \u3068\u3057\u3066\u5b89\u5168\u306b\u4fdd\u6301\u3059\u308b\u4ed5\u7d44\u307f\u306f\u78ba\u7acb\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u554f\u984c\u306f\u3001\u300cMCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8 -> MCP \u30b5\u30fc\u30d0\u30fc\u300d\u306e\u90e8\u5206\u3067\u3059\u3002\u3053\u3053\u306b\u3001\u30e6\u30fc\u30b6\u30fc\u5171\u901a\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u306f\u306a\u304f\u3001\u300c\u8ab0\u300d\u304c\u30a2\u30af\u30bb\u30b9\u3057\u3066\u3044\u3066\u3001\u300c\u305d\u306e\u4eba\u304c\u30a2\u30af\u30bb\u30b9\u3057\u3066\u826f\u3044\u306e\u304b\u300d\u3092\u8a8d\u53ef\u3059\u308b\u4ed5\u7d44\u307f\u3092\u8ffd\u52a0\u3057\u305f\u3044\u3001\u3068\u3044\u3046\u3053\u3068\u3067\u3059\u3002<\/p>\n<h2>\u81ea\u4f5c\u306e\u8efd\u91cf OAuth2 Proxy<\/h2>\n<p>\u826f\u304f\u77e5\u3089\u308c\u3066\u3044\u308b &#8220;OAuth2 Proxy&#8221; \u3068\u306f\u7570\u306a\u308a\u307e\u3059\u3002\u672c\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u3092\u65b0\u898f\u3067\u958b\u767a\u3057\u305f\u7406\u7531\u306f\u5f8c\u3067\u8ff0\u3079\u307e\u3059\u304c\u3001OAuth2 Proxy \u3067\u306f\u76ee\u7684\u9054\u6210\u3067\u304d\u306a\u304b\u3063\u305f\u305f\u3081\u3067\u3059\u3002<\/p>\n<h3>\u8a2d\u5b9a\u4f8b\u30fb\u8a2d\u5b9a\u306e\u6d41\u308c<\/h3>\n<h4>OAuth2<\/h4>\n<p>\u307e\u305a\u306f\u3001Client ID \u3068 Client Secret \u3092\u53d6\u5f97\u3057\u307e\u3059\u3002\u3053\u306e\u3001\u81ea\u4f5c\u306e OAuth2 Proxy \u3067\u306f\u3001OAuth2 \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306b\u767b\u9332\u3057\u305f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e Client ID \u3068 Client Secret \u3092\u4f7f\u3063\u3066\u3001\u30e6\u30fc\u30b6\u30fc\u306e\u8a8d\u8a3c\u3092\u884c\u3044\u307e\u3059\u3002<br \/>\n\u79c1\u306f\u3001Google Cloud \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u7528\u610f\u3057\u3001Google Cloud (Google) \u306e OAuth2 \u8a8d\u8a3c\u3092\u4f7f\u3046\u3053\u3068\u306b\u3057\u307e\u3057\u305f\u3002Google Cloud \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4f5c\u6210\u3001OAuth2 \u30af\u30e9\u30a4\u30a2\u30f3\u30c8 ID \u306e\u4f5c\u6210\u3001\u30ea\u30c0\u30a4\u30ec\u30af\u30c8 URI \u306e\u8a2d\u5b9a\u7b49\u304c\u5fc5\u8981\u3067\u3059\u3002<br \/>\n\u8a2d\u5b9a\u3092\u3057\u305f\u3053\u3068\u304c\u4e00\u5ea6\u3082\u306a\u3044\u65b9\u306b\u3068\u3063\u3066\u306f\u96e3\u3057\u3044\u90e8\u5206\u304c\u3042\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u304c\u3001\u3053\u306e\u8a18\u4e8b\u306e\u672c\u984c\u304b\u3089\u306f\u5916\u308c\u308b\u306e\u3067\u3001\u3053\u3053\u3067\u306f\u5fc5\u8981\u306a\u90e8\u5206\u306e\u8aac\u660e\u306b\u3068\u3069\u3081\u3001\u8a73\u7d30\u306a\u624b\u9806\u306f\u7701\u7565\u3057\u307e\u3059\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/app_information.png\" alt=\"Web App \u306e\u8a2d\u5b9a\" \/><\/p>\n<p><img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/web_app_client.png\" alt=\"Web App Client \u753b\u9762\" \/><\/p>\n<p>Client ID \u3068 Client Secret \u3092\u30e1\u30e2\u3057\u3066\u304a\u3044\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<p>Authorized redirect URIs \u306b <code>https:\/\/antigravity.google\/oauth-callback` \u3068<\/code>http:\/\/localhost:8080\/oauth2\/callback` \u3092\u8ffd\u52a0\u3057\u3066\u304a\u3044\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<h4>Docker Compose \u5f62\u5f0f\u306e\u8a2d\u5b9a\u4f8b<\/h4>\n<p>\u53d6\u5f97\u3057\u305f\u3089\u3001\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306b\u8a18\u8ff0\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u3092\u74b0\u5883\u5909\u6570\u3067\u53d7\u3051\u53d6\u308b\u3088\u3046\u306b\u69cb\u6210\u3057\u3066\u3044\u307e\u3059\u306e\u3067\u3001Docker Compose \u306e\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306b YAML \u5f62\u5f0f\u3067\u8a18\u8ff0\u3059\u308b\u3053\u3068\u3068\u3057\u307e\u3057\u305f\u3002\u4ee5\u4e0b\u306f\u3001Google OAuth2 \u8a8d\u8a3c\u3092\u4f7f\u3046\u5834\u5408\u306e\u8a2d\u5b9a\u4f8b\u3067\u3059\u3002<\/p>\n<p>GitHub \u306e\u30ea\u30dd\u30b8\u30c8\u30ea\u306b\u540c\u3058\u30d5\u30a1\u30a4\u30eb\u3092\u7f6e\u3044\u3066\u3044\u307e\u3059 <a href=\"https:\/\/github.com\/takotakot\/misc\/blob\/main\/mcp-oauth2-proxy\/docker-compose.yaml\">docker-compose.yaml<\/a> \u306e\u3067\u3001\u305d\u3061\u3089\u3082\u53c2\u8003\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002<br \/>\n<code>OAUTH2_PROXY_CLIENT_ID<\/code>, <code>OAUTH2_PROXY_CLIENT_SECRET<\/code>, <code>OAUTH2_PROXY_ALLOWED_EMAILS<\/code> \u306e\u90e8\u5206\u306f\u3001OAuth2 \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306b\u767b\u9332\u3057\u305f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e Client ID \u3068 Client Secret\u3001\u8a8d\u8a3c\u3092\u8a31\u53ef\u3059\u308b\u30e6\u30fc\u30b6\u30fc\u306e\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3092\u6307\u5b9a\u3057\u307e\u3059\u3002\u300c\u8a8d\u8a3c\u3092\u8a31\u53ef\u3059\u308b\u30e6\u30fc\u30b6\u30fc\u306e\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u300d\u3068\u306f\u3001\u666e\u901a\u306f\u3001\u81ea\u5206\u81ea\u8eab\u306e Google \u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3067\u3059\u3002\u8907\u6570\u306e\u30e6\u30fc\u30b6\u30fc\u3092\u8a31\u53ef\u3059\u308b\u5834\u5408\u306b\u306f\u3001\u30ab\u30f3\u30de\u533a\u5207\u308a\u3067\u8907\u6570\u6307\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<br \/>\n\u4f01\u696d\u5185\u3067\u5229\u7528\u3059\u308b\u5834\u5408\u306b\u306f\u3001<code>ALLOWED_EMAILS<\/code> \u3067\u306f\u306a\u304f\u3001<code>ALLOWED_DOMAIN<\/code> \u306e\u3088\u3046\u306b\u3001\u793e\u5185\u30c9\u30e1\u30a4\u30f3\u3092\u6307\u5b9a\u3059\u308b\u3088\u3046\u306b\u6539\u9020\u3092\u52a0\u3048\u308b\u3053\u3068\u3067\u3001\u793e\u5185\u30e6\u30fc\u30b6\u30fc\u306e\u307f\u304c\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u3088\u3046\u306b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u306e\u3067\u3001\u691c\u8a0e\u3057\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<pre><code class=\"language-yaml\">services:\n  # Lightweight Go OAuth2 Proxy (small-oauth2-proxy)\n  # \u3059\u3079\u3066\u306e\u30a2\u30af\u30bb\u30b9\u306f\u3053\u306e\u8efd\u91cf\u30d7\u30ed\u30ad\u30b7\u3092\u7d4c\u7531\u3057\u3066\u8a8d\u8a3c\u30fb\u8ee2\u9001\u3055\u308c\u308b\n  # \u8a8d\u8a3c\u3092\u901a\u904e\u3057\u305f\u30e6\u30fc\u30b6\u30fc\u306f\u3001Mock MCP Server \u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\n  oauth2-proxy:\n    build: .\/small-oauth2-proxy\n    ports:\n      - \"8080:4180\"\n    environment:\n      OAUTH2_PROXY_HTTP_ADDRESS: \"0.0.0.0:4180\"\n\n      # Google OAuth2 \/ OIDC \u8a2d\u5b9a\n      OAUTH2_PROXY_CLIENT_ID: \"number-randomstring.apps.googleusercontent.com\"\n      OAUTH2_PROXY_CLIENT_SECRET: \"GOCSPX-randomstring\"\n      OAUTH2_PROXY_REDIRECT_URL: \"http:\/\/localhost:8080\/oauth2\/callback\"\n      OAUTH2_PROXY_ALLOWED_EMAILS: \"email@example.com\"\n\n      RESOURCE_URL: \"http:\/\/localhost:8080\/mcp\"\n\n      # \u30ea\u30c0\u30a4\u30ec\u30af\u30c8\/\u4e2d\u7d99\u5148 Upstream\n      OAUTH2_PROXY_UPSTREAMS: \"http:\/\/mock-mcp-server:5678\"\n    depends_on:\n      - mock-mcp-server\n\n  # Mock MCP Server (everything)\n  # \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u4f55\u3089\u304b\u306e\u300c\u6b63\u3057\u3044\u300d\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u8fd4\u3059\u305f\u3081\u306b\u3001\u516c\u958b\u3055\u308c\u3066\u3044\u308b MCP \u30b5\u30fc\u30d0\u30fc\u306e\u30e2\u30c3\u30af\u3092\u7acb\u3061\u4e0a\u3052\u308b\n  mock-mcp-server:\n    # See: https:\/\/hub.docker.com\/r\/mcp\/everything\n    # See: https:\/\/github.com\/modelcontextprotocol\/servers\/tree\/main\/src\/everything\n    # v x.y.z is tested.\n    image: node:alpine\n    # command: &gt;\n    #   npx -y @modelcontextprotocol\/server-everything streamableHttp\n    # command: &gt;\n    #   sh -c \"mkdir -p \/tmp\/mcp &amp;&amp; cd \/tmp\/mcp &amp;&amp; npm init -y &amp;&amp; npm install @modelcontextprotocol\/server-everything media-typer &amp;&amp; npx @modelcontextprotocol\/server-everything streamableHttp\"\n    command: &gt;\n      npx -y @modelcontextprotocol\/server-everything streamableHttp\n    environment:\n      PORT: 5678\n<\/code><\/pre>\n<h5>\u53c2\u8003<\/h5>\n<p>\u30d8\u30c3\u30c0\u30fc\u306b\u6e21\u3055\u308c\u305f Bearer \u30c8\u30fc\u30af\u30f3\u306e\u5024\u7b49\u306e\u30ed\u30b0\u306e\u51fa\u529b\u304c\u306a\u3044\u3068\u3001\u30c7\u30d0\u30c3\u30b0\u304c\u96e3\u3057\u3044\u3067\u3059\u3002\u524d\u6bb5\u306b nginx \u3092\u914d\u7f6e\u3057\u3001njs \u3068\u3044\u3046\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001\u30d8\u30c3\u30c0\u30fc\u306e\u5024\u3092\u30ed\u30b0\u306b\u51fa\u529b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<a href=\"https:\/\/github.com\/takotakot\/misc\/blob\/main\/mcp-oauth2-proxy\/docker-compose_log_nginx.yaml\">docker-compose_log_nginx.yaml<\/a> \u3068\u5468\u8fba\u30d5\u30a1\u30a4\u30eb\u3092\u53c2\u8003\u306b\u3057\u3066\u304f\u3060\u3055\u3044\uff08\u5f8c\u8ff0\u306e OAuth2 Proxy \u3068\u7d44\u307f\u5408\u308f\u305b\u305f\u8a2d\u5b9a\u4f8b\u3067\u3059\uff09\u3002<\/p>\n<h4>\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u8a2d\u5b9a\u4f8b<\/h4>\n<p>\u691c\u8a3c\u7528\u306e MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3068\u3057\u3066 Antigravity \u3092\u4f7f\u7528\u3057\u307e\u3059\u3002<\/p>\n<p><code>.gemini\/config\/mcp_config.json<\/code> \u306e\u4f8b\u306f\u4ee5\u4e0b\u306e\u901a\u308a\u3067\u3059\u3002<code>serverUrl<\/code> \u306f\u3001\u63a5\u7d9a\u5148\u306e URL \u3092\u6307\u5b9a\u3057\u307e\u3059\u3002<code>http:\/\/localhost:8080\/mcp` \u306e\u3088\u3046\u306b<\/code>\/mcp` \u304c\u3064\u3044\u3066\u3044\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<pre><code class=\"language-json\">{\n  \"mcpServers\": {\n    \"oauth2-proxy-everything\": {\n      \"oauth\": {\n        \"clientId\": \"number-randomstring.apps.googleusercontent.com\",\n        \"clientSecret\": \"GOCSPX-randomstring\"\n      },\n      \"serverUrl\": \"http:\/\/localhost:8080\/mcp\"\n    }\n  }\n}\n<\/code><\/pre>\n<p>Antigravity \u306f\u3001\uff08\u5c11\u306a\u304f\u3068\u3082\u3001clientId, clientSecret \u3092\u624b\u52d5\u3067\u8a2d\u5b9a\u3059\u308b\u5834\u5408\u306b\u306f\uff09\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u5148\u306b <code>https:\/\/antigravity.google\/oauth-callback` \u3092\u4f7f\u3046\u3088\u3046\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u308b\u305f\u3081\u3001OAuth2 \u306e\u8a31\u53ef\u5148\u3092\u8ffd\u52a0\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002Authorized redirect URIs \u306b<\/code>https:\/\/antigravity.google\/oauth-callback` \u3092\u8ffd\u52a0\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u826f\u304f\u4f7f\u3046\u306e\u3067 `http:\/\/localhost:8080\/oauth2\/callback` \u3082\u8ffd\u52a0\u3057\u3066\u304a\u304f\u3068\u4fbf\u5229\u3067\u3059\u3002<\/p>\n<h4>\u8a8d\u8a3c<\/h4>\n<p>\u6b63\u3057\u304f\u8a2d\u5b9a\u304c\u3067\u304d\u3066\u3044\u308c\u3070\u3001Antigravity \u306e\u8a2d\u5b9a\u753b\u9762 Customizations > Installed MCP Servers \u306b &#8220;oauth2-proxy-everything&#8221; \u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002Authenticate \u30ea\u30f3\u30af\u3092\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u3001\u30d6\u30e9\u30a6\u30b6\u304c\u7acb\u3061\u4e0a\u304c\u308a\u3001Google \u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u8a8d\u8a3c\u753b\u9762\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002\u8a8d\u8a3c\u3092\u884c\u3046\u3068\u3001Antigravity \u306e Web \u753b\u9762\u304c\u898b\u3048\u307e\u3059\u3002<\/p>\n<p>\u4ee5\u4e0b\u306e\u3088\u3046\u306a URL \u306b\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u3055\u308c\u3001\u901a\u5e38 <code>4\/<\/code> \u3067\u59cb\u307e\u308b\u30c8\u30fc\u30af\u30f3\u304c\u8fd4\u5374\u3055\u308c\u307e\u3059\u3002\u30b3\u30d4\u30fc\u3057\u3066 Submit \u3059\u308b\u3053\u3068\u3067\u8a8d\u8a3c\u5b8c\u4e86\u3067\u3059\u3002\u30c4\u30fc\u30eb\u304c\u4f7f\u3048\u308b\u3088\u3046\u306b\u306a\u3063\u3066\u3044\u308b\u306f\u305a\u3067\u3059\u3002<\/p>\n<p>`https:\/\/antigravity.google\/oauth-callback?state=_state_value_&#038;iss=https%3A%2F%2Faccounts.google.com&#038;scope=email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid&#038;authuser=0&#038;prompt=consent`<\/p>\n<p><img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/antigravity-authenticate.png\" alt=\"Authenticate \u306e\u30ea\u30f3\u30af\u3092\u62bc\u3059\" \/><br \/>\n<img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/antigravity-redirected.png\" alt=\"\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u5f8c\" \/><br \/>\n<img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/antigravity-submit.png\" alt=\"\u30c6\u30ad\u30b9\u30c8\u30dc\u30c3\u30af\u30b9\u3068 Submit \u30dc\u30bf\u30f3\u304c\u8868\u793a\u3055\u308c\u3066\u3044\u308b\" \/><br \/>\n<img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/antigravity-tools-enabled.png\" alt=\"\u8a8d\u8a3c\u5b8c\u4e86\u5f8c\" \/><\/p>\n<h3>\u4f1a\u8a71\u4f8b (Antigravity)<\/h3>\n<p>\u8a2d\u5b9a\u304c\u5b8c\u4e86\u3057\u3066\u3044\u308c\u3070\u3001MCP \u30b5\u30fc\u30d0\u30fc\u304c\u63d0\u4f9b\u3059\u308b\u6a5f\u80fd\u3092 tool \u3068\u3057\u3066\u547c\u3073\u51fa\u3059\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n<blockquote><p>\n  MCP \u30b5\u30fc\u30d0 oauth2-proxy-everything \u306e echo \u3092 &#8220;\u3053\u3093\u306b\u3061\u306f&#8221; \u3063\u3066\u547c\u3073\u51fa\u3057\u3066\u307b\u3057\u3044\n<\/p><\/blockquote>\n<p>\u306e\u3088\u3046\u306b\u5bfe\u8a71\u753b\u9762\u306b\u5165\u529b\u3059\u308b\u3053\u3068\u3067\u3001&#8221;\u3053\u3093\u306b\u3061\u306f&#8221; \u3068\u3044\u3046\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u8fd4\u3063\u3066\u304d\u307e\u3059\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/antigravity-tool-call.png\" alt=\"\u30c4\u30fc\u30eb\u547c\u3073\u51fa\u3057\u306e\u4f8b\" \/><\/p>\n<h2>\u307e\u3068\u3081<\/h2>\n<p>\u672c\u8a18\u4e8b\u306e\u524d\u534a\u3067\u306f\u3001\u300cMCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8 -> MCP \u30b5\u30fc\u30d0\u30fc\u300d\u9593\u306e\u8a8d\u8a3c\u3001\u7279\u306b\u8a8d\u8a3c\u6a5f\u80fd\u3092\u6301\u305f\u306a\u3044\u300c\u5171\u7528\u578b\u300d\u306e MCP \u30b5\u30fc\u30d0\u30fc\u306b\u5bfe\u3057\u3066\u3001\u30e6\u30fc\u30b6\u30fc\u5358\u4f4d\u306e\u8a8d\u8a3c\u30fb\u8a8d\u53ef\u3092\u5f8c\u4ed8\u3051\u3067\u8ffd\u52a0\u3059\u308b\u65b9\u6cd5\u3092\u6271\u3044\u307e\u3057\u305f\u3002<\/p>\n<p>\u305d\u306e\u305f\u3081\u306b\u3001\u81ea\u4f5c\u306e\u8efd\u91cf\u306a OAuth2 Proxy (small-oauth2-proxy) \u3092 MCP \u30b5\u30fc\u30d0\u30fc\u306e\u524d\u6bb5\u306b\u914d\u7f6e\u3057\u307e\u3059\u3002\u8a8d\u8a3c\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306b\u306f Google \u306e OAuth2 \u3092\u5229\u7528\u3057\u3001Client ID\u30fbClient Secret \u3068\u3001\u30a2\u30af\u30bb\u30b9\u3092\u8a31\u53ef\u3059\u308b\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\uff08\u4f01\u696d\u5185\u3067\u3042\u308c\u3070\u30c9\u30e1\u30a4\u30f3\u5358\u4f4d\u3092\u691c\u8a0e\u3057\u3066\u304f\u3060\u3055\u3044\uff09\u3092\u8a2d\u5b9a\u3059\u308b\u3060\u3051\u3067\u3001\u8a31\u53ef\u3055\u308c\u305f\u30e6\u30fc\u30b6\u30fc\u3060\u3051\u304c MCP \u30b5\u30fc\u30d0\u30fc\u3092\u5229\u7528\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002\u5229\u7528\u8005\u5171\u901a\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\uff08API \u30ad\u30fc\uff09\u3092\u914d\u5e03\u3059\u308b\u65b9\u5f0f\u3068\u7570\u306a\u308a\u3001\u300c\u8ab0\u304c\u30a2\u30af\u30bb\u30b9\u3057\u3066\u3044\u308b\u306e\u304b\u300d\u3092\u8b58\u5225\u3057\u305f\u3046\u3048\u3067\u8a8d\u53ef\u3067\u304d\u308b\u70b9\u304c\u5229\u70b9\u3067\u3059\u3002<\/p>\n<p>\u8a2d\u5b9a\u306f Docker Compose \u306e\u74b0\u5883\u5909\u6570\u3067\u5b8c\u7d50\u3057\u3001MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\uff08\u3053\u3053\u3067\u306f Antigravity\uff09\u5074\u306f <code>serverUrl<\/code> \u306b <code>\/mcp<\/code> \u4ed8\u304d\u306e URL \u3092\u3001<code>oauth<\/code> \u306b Client ID\u30fbClient Secret \u3092\u6307\u5b9a\u3059\u308b\u3060\u3051\u3067\u63a5\u7d9a\u3067\u304d\u307e\u3059\u3002\u5b9f\u969b\u306b\u3001Authenticate \u304b\u3089\u30d6\u30e9\u30a6\u30b6\u3067\u306e Google \u8a8d\u8a3c\u3092\u7d4c\u3066\u30c4\u30fc\u30eb\u304c\u6709\u52b9\u5316\u3055\u308c\u3001<code>echo<\/code> \u30c4\u30fc\u30eb\u306e\u547c\u3073\u51fa\u3057\u304c\u6210\u529f\u3059\u308b\u3053\u3068\u3082\u78ba\u8a8d\u3057\u307e\u3057\u305f\u3002<\/p>\n<p>\u306a\u304a\u3001\u3053\u306e\u69cb\u6210\u3092\u6210\u7acb\u3055\u305b\u308b\u9375\u306f <code>\/.well-known\/oauth-protected-resource<\/code> \u306e\u8fd4\u5374\u90e8\u5206\u306b\u3042\u308a\u3001\u3053\u3053\u306b\u5de5\u592b\u304c\u5fc5\u8981\u3067\u3057\u305f\uff08\u5f8c\u8ff0\uff09\u3002<\/p>\n<p>\u30af\u30e9\u30a6\u30c9\u4e0a\u3067\u306e\u63a5\u7d9a\u306f\u8a66\u3057\u3066\u3044\u307e\u305b\u3093\u304c\u3001\u539f\u7406\u7684\u306b\u3001Cloud Run \u3092\u30b5\u30a4\u30c9\u30ab\u30fc\u30b3\u30f3\u30c6\u30ca\u69cb\u6210\u306b\u3059\u308b\u3053\u3068\u3067\u3001\u30af\u30e9\u30a6\u30c9\u4e0a\u306e MCP \u30b5\u30fc\u30d0\u30fc\u306b\u5bfe\u3057\u3066\u3082\u540c\u69d8\u306e\u69cb\u6210\u304c\u53ef\u80fd\u3067\u3059\u3002<\/p>\n<hr \/>\n<h2><code>\/.well-known\/oauth-protected-resource<\/code> \u306e\u5fdc\u7b54\u5185\u5bb9<\/h2>\n<p><code>serverUrl<\/code> \u7b49\uff08\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u3088\u3063\u3066\u7570\u306a\u308a\u307e\u3059\uff09\u306b\u8a2d\u5b9a\u3057\u305f URL \u304c\u3001<code>\/.well-known\/oauth-protected-resource<\/code> \u306b\u30a2\u30af\u30bb\u30b9\u3057\u305f\u30ec\u30b9\u30dd\u30f3\u30b9\u306e <code>resource<\/code> \u306e\u5024\u3068\u4e00\u81f4\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\uff08\u53b3\u5bc6\u306b\u306f\u3001<code>\/.well-known\/oauth-protected-resource\/mcp<\/code> \u306b\u3082\u30a2\u30af\u30bb\u30b9\u3055\u308c\u307e\u3059\u304c\u3001\u8a71\u304c\u7d30\u304b\u304f\u306a\u308b\u306e\u3067\u7701\u7565\u3057\u307e\u3059\uff09\u3002\u4eca\u56de\u306f\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u306b\u3088\u308b\u30e6\u30fc\u30b6\u30fc\u306e\u540c\u5b9a\u3092\u884c\u3046\u305f\u3081\u3001<code>scopes_supported<\/code> \u306b <code>email<\/code> \u3092\u6307\u5b9a\u3057\u3066\u3044\u307e\u3059\u3002<code>authorization_servers<\/code> \u306b\u306f\u3001OAuth2 \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e URL \u3092\u6307\u5b9a\u3057\u307e\u3059\u3002<\/p>\n<pre><code class=\"language-json\">{\n  \"resource\": \"http:\/\/localhost:8080\/mcp\",\n  \"authorization_servers\": [\n    \"https:\/\/accounts.google.com\"\n  ],\n  \"scopes_supported\": [\n    \"email\"\n  ]\n}\n<\/code><\/pre>\n<hr \/>\n<h2>OAuth2 Proxy\uff08OSS \u5931\u6557\u4f8b\uff09<\/h2>\n<p>OAuth2 Proxy \u306f\u3001OAuth2 \u8a8d\u8a3c\u3092\u30b5\u30dd\u30fc\u30c8\u3059\u308b\u30d7\u30ed\u30ad\u30b7\u30b5\u30fc\u30d0\u30fc\u3067\u3059 <a href=\"#oauth2-proxy_info\">oauth2-proxy_info<\/a>\u3002\u30ea\u30d0\u30fc\u30b9\u30d7\u30ed\u30ad\u30b7\u3068\u3057\u3066\u52d5\u4f5c\u3057\u306a\u304c\u3089\u3001\u8a8d\u53ef\u3055\u308c\u305f\u30e6\u30fc\u30b6\u30fc\u306e\u307f\u306b\u30a2\u30af\u30bb\u30b9\u3092\u8a31\u53ef\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002OAuth2 Proxy \u3092\u5229\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u30ed\u30b0\u30a4\u30f3\u6a5f\u80fd\u3092\u6301\u305f\u306a\u3044 Web \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u3001\u624b\u8efd\u306b OAuth2 \u8a8d\u8a3c\u3092\u8ffd\u52a0\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002OAuth2 Proxy \u306f\u3001Google\u3001GitHub\u3001GitLab\u3001Microsoft Entra ID \u306a\u3069\u306e OAuth2 \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3068\u9023\u643a\u3057\u3066\u8a8d\u8a3c\u3092\u884c\u3046\u3053\u3068\u304c\u3067\u304d\u307e\u3059 <a href=\"#oauth-provider-configuration\">OAuth Provider Configuration<\/a>\u3002<\/p>\n<p>OAuth2 Proxy \u3068\u3044\u3046 OSS \u3067\u306f\u76ee\u7684\u3092\u9054\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u306e\u3067\u3001\u300c\u4e0a\u624b\u304f\u884c\u304f\u65b9\u6cd5\u3060\u3051\u304c\u77e5\u308a\u305f\u3044\u4eba\u300d\u306f\u3001\u672c\u7bc0\u306f\u8aad\u307f\u98db\u3070\u3057\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<h3>\u8a2d\u5b9a\u4f8b\u30fb\u8a2d\u5b9a\u306e\u6d41\u308c<\/h3>\n<h4>OAuth2<\/h4>\n<p>\u307e\u305a\u306f\u3001Client ID \u3068 Client Secret \u3092\u53d6\u5f97\u3057\u307e\u3059\u3002OAuth2 Proxy \u3067\u306f\u3001OAuth2 \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306b\u767b\u9332\u3057\u305f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e Client ID \u3068 Client Secret \u3092\u4f7f\u3063\u3066\u3001\u30e6\u30fc\u30b6\u30fc\u306e\u8a8d\u8a3c\u3092\u884c\u3044\u307e\u3059\u3002<\/p>\n<p>\u524d\u534a\u3068\u540c\u3058\u3067\u3059\u306e\u3067\u3001\u7701\u7565\u3057\u307e\u3059\u3002<\/p>\n<h4>Docker Compose \u5f62\u5f0f\u306e\u8a2d\u5b9a\u4f8b<\/h4>\n<p>\u53d6\u5f97\u3057\u305f\u3089\u3001OAuth2 Proxy \u306e\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306b\u8a18\u8ff0\u3057\u307e\u3059\u3002OAuth2 Proxy \u306e\u8a2d\u5b9a\u306f\u74b0\u5883\u5909\u6570\u3067\u6e21\u3059\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u306e\u3067\u3001Docker Compose \u306e\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306b YAML \u5f62\u5f0f\u3067\u8a18\u8ff0\u3059\u308b\u3053\u3068\u3068\u3057\u307e\u3057\u305f\u3002\u4ee5\u4e0b\u306f\u3001Google OAuth2 \u8a8d\u8a3c\u3092\u4f7f\u3046\u5834\u5408\u306e\u8a2d\u5b9a\u4f8b\u3067\u3059\u3002<\/p>\n<p>GitHub \u306e\u30ea\u30dd\u30b8\u30c8\u30ea\u306b\u540c\u3058\u30d5\u30a1\u30a4\u30eb\u3092\u7f6e\u3044\u3066\u3044\u307e\u3059 <a href=\"https:\/\/github.com\/takotakot\/misc\/blob\/main\/mcp-oauth2-proxy\/docker-compose_log_nginx.yaml\">docker-compose_log_nginx.yaml<\/a> \u306e\u3067\u3001\u305d\u3061\u3089\u3082\u53c2\u8003\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002<br \/>\n\u4f01\u696d\u5185\u3067\u5229\u7528\u3059\u308b\u5834\u5408\u306b\u306f\u3001<code>OAUTH2_PROXY_EMAIL_DOMAINS: \"example.com\"<\/code> \u306e\u3088\u3046\u306b\u3001\u793e\u5185\u30c9\u30e1\u30a4\u30f3\u3092\u6307\u5b9a\u3059\u308b\u3053\u3068\u3067\u3001\u793e\u5185\u30e6\u30fc\u30b6\u30fc\u306e\u307f\u304c\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u3088\u3046\u306b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u306e\u3067\u3001\u5229\u7528\u3059\u308b\u3068\u8a2d\u5b9a\u304c\u7c21\u5358\u306b\u306a\u308a\u307e\u3059\u3002\u500b\u5225\u306e\u30e6\u30fc\u30b6\u30fc\u306e\u8a2d\u5b9a\u304c\u5fc5\u8981\u306a\u5834\u5408\u306b\u306f\u3001<code>OAUTH2_AUTHENTICATED_EMAILS_FILE<\/code> \u3092\u4f7f\u3063\u3066\u3001\u8a8d\u8a3c\u3092\u8a31\u53ef\u3059\u308b\u30e6\u30fc\u30b6\u30fc\u306e\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3092\u30d5\u30a1\u30a4\u30eb\u3067\u6307\u5b9a\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u308b\u3068\u601d\u3044\u307e\u3059\u3002<\/p>\n<p><code>OAUTH2_PROXY_COOKIE_SECRET<\/code> \u306f\u300132 \u30d0\u30a4\u30c8\u306e\u30e9\u30f3\u30c0\u30e0\u306a\u6587\u5b57\u5217\u3092 Base64 \u30a8\u30f3\u30b3\u30fc\u30c9\u3057\u305f\u5024\u3092\u6307\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u300232 \u30d0\u30a4\u30c8\u306e\u30e9\u30f3\u30c0\u30e0\u306a\u6587\u5b57\u5217\u306f\u3001<code>openssl rand -base64 32<\/code> \u306e\u3088\u3046\u306b\u3057\u3066\u751f\u6210\u3067\u304d\u307e\u3059\u3002<\/p>\n<p>\u8a2d\u5b9a\u306e\u8a73\u7d30\u306f <a href=\"#oauth2-proxy-config-overview\">oauth2-proxy-config-overview<\/a> \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<pre><code class=\"language-yaml\">services:\n  # Nginx \u524d\u6bb5\u30d7\u30ed\u30ad\u30b7\n  # \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304b\u3089\u306e\u3059\u3079\u3066\u306e\u30a2\u30af\u30bb\u30b9\u3092\u53d7\u3051\u3001HTTP\u30d8\u30c3\u30c0\u30fc\u3092\u30ed\u30b0\u306b\u8a18\u9332\u3057\u305f\u4e0a\u3067 oauth2-proxy \u306b\u8ee2\u9001\u3059\u308b\n  nginx:\n    image: nginx:alpine\n    ports:\n      - \"8080:80\"\n    volumes:\n      - .\/nginx.conf:\/etc\/nginx\/nginx.conf:ro\n      - .\/headers.js:\/etc\/nginx\/headers.js:ro\n      - .\/static\/.well-known:\/usr\/share\/nginx\/html\/.well-known:ro\n    depends_on:\n      - oauth2-proxy\n\n  # OAuth2 Proxy (oauth2-proxy)\n  # \u3059\u3079\u3066\u306e\u30a2\u30af\u30bb\u30b9\u306f OAuth2 Proxy \u3092\u7d4c\u7531\u3057\u3066\u8a8d\u8a3c\u3055\u308c\u308b\u3088\u3046\u306b\u3059\u308b\n  # \u4f8b\u5916\u3092 `SKIP_AUTH_ROUTES` \u3067\u6307\u5b9a\u3059\u308b\n  # Upstream \u306f2\u3064\u6307\u5b9a\u3057\u3066\u3044\u308b\n  #  # 1. \u9759\u7684\u306a\u30e1\u30bf\u30c7\u30fc\u30bf\u8fd4\u5374\u7528\u306e\u30eb\u30fc\u30c8(\/.well-known\/)\n  #  # 2. Mock MCP Server (http:\/\/mock-mcp-server:5678\/)\n  # \u8a8d\u8a3c\u3092\u901a\u904e\u3057\u305f\u30e6\u30fc\u30b6\u30fc\u306f\u3001Mock MCP Server \u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\n  oauth2-proxy:\n    # v x.y.z is tested.\n    image: quay.io\/oauth2-proxy\/oauth2-proxy:latest\n    environment:\n      # See: https:\/\/oauth2-proxy.github.io\/oauth2-proxy\/configuration\/overview\/\n      OAUTH2_PROXY_HTTP_ADDRESS: \"0.0.0.0:4180\"\n      OAUTH2_PROXY_API_ROUTES: \"^\/mcp\"\n\n      # Google OAuth2 \/ OIDC \u8a2d\u5b9a\n      OAUTH2_PROXY_PROVIDER: \"oidc\"\n      OAUTH2_PROXY_OIDC_ISSUER_URL: \"https:\/\/accounts.google.com\"\n      # OAUTH2_PROXY_PROVIDER: \"google\"\n      OAUTH2_PROXY_CLIENT_ID: \"number-randomstring.apps.googleusercontent.com\"\n      OAUTH2_PROXY_CLIENT_SECRET: \"GOCSPX-randomstring\"\n      OAUTH2_PROXY_REDIRECT_URL: \"http:\/\/localhost:8080\/oauth2\/callback\"\n      OAUTH2_PROXY_EMAIL_DOMAINS: \"*\"\n      OAUTH2_PROXY_ALLOWED_EMAILS: \"email@example.com\"\n\n      # \u30e1\u30bf\u30c7\u30fc\u30bf\u8fd4\u5374\n      # OAUTH2_PROXY_SKIP_AUTH_ROUTES: \"^\/\\\\.well-known\/oauth-protected-resource,^\/mcp,^\/mockserver\"\n      OAUTH2_PROXY_SKIP_AUTH_ROUTES: \"^\/\\\\.well-known\/oauth-protected-resource\"\n      OAUTH2_PROXY_UPSTREAMS: 'file:\/\/\/etc\/static\/.well-known\/#\/.well-known\/,http:\/\/mock-mcp-server:5678\/'\n      # OAUTH2_PROXY_UPSTREAMS: 'http:\/\/mock-mcp-server:5678\/'\n\n      # API\u8a8d\u8a3c\u306e\u8a31\u53ef\uff08\u30c8\u30fc\u30af\u30f3\u691c\u8a3c\uff09\n      OAUTH2_PROXY_SKIP_JWT_BEARER_TOKENS: \"true\"\n      OAUTH2_PROXY_EXTRA_JWT_ISSUERS: \"https:\/\/accounts.google.com=aud\"\n\n      OAUTH2_PROXY_COOKIE_SECRET: \"abcdefghijklmnopqrstuvwxyz123456\"\n    volumes:\n      - .\/static\/.well-known:\/etc\/static\/.well-known\n    # command: [\"\/bin\/oauth2-proxy\", \"--introspect-token=true\", \"--introspect-url=https:\/\/www.googleapis.com\/oauth2\/v3\/tokeninfo\"]\n\n  # Mock MCP Server (everything)\n  # \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u4f55\u3089\u304b\u306e\u300c\u6b63\u3057\u3044\u300d\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u8fd4\u3059\u305f\u3081\u306b\u3001\u516c\u958b\u3055\u308c\u3066\u3044\u308b MCP \u30b5\u30fc\u30d0\u30fc\u306e\u30e2\u30c3\u30af\u3092\u7acb\u3061\u4e0a\u3052\u308b\n  mock-mcp-server:\n    # See: https:\/\/hub.docker.com\/r\/mcp\/everything\n    # See: https:\/\/github.com\/modelcontextprotocol\/servers\/tree\/main\/src\/everything\n    # v x.y.z is tested.\n    image: node:alpine\n    # command: &gt;\n    #   npx -y @modelcontextprotocol\/server-everything streamableHttp\n    # command: &gt;\n    #   sh -c \"mkdir -p \/tmp\/mcp &amp;&amp; cd \/tmp\/mcp &amp;&amp; npm init -y &amp;&amp; npm install @modelcontextprotocol\/server-everything media-typer &amp;&amp; npx @modelcontextprotocol\/server-everything streamableHttp\"\n    command: &gt;\n      npx -y @modelcontextprotocol\/server-everything streamableHttp\n    environment:\n      PORT: 5678\n<\/code><\/pre>\n<h4>\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u8a2d\u5b9a\u4f8b<\/h4>\n<p>\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u306f Antigravity \u3092\u5229\u7528\u3057\u3066\u307f\u307e\u3057\u305f\u3002<\/p>\n<p><code>.gemini\/config\/mcp_config.json<\/code> \u306e\u4f8b\u306f\u4ee5\u4e0b\u306e\u901a\u308a\u3067\u3059\u3002\u3053\u308c\u306f\u524d\u534a\u306e\u4f8b\u3068\u540c\u3058\u3067\u3059\u3002<code>serverUrl<\/code> \u306f\u3001\u63a5\u7d9a\u5148\u306e URL \u3092\u6307\u5b9a\u3057\u307e\u3059\u3002<code>http:\/\/localhost:8080\/mcp` \u306e\u3088\u3046\u306b<\/code>\/mcp` \u304c\u3064\u3044\u3066\u3044\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<pre><code class=\"language-json\">{\n  \"mcpServers\": {\n    \"oauth2-proxy-everything\": {\n      \"serverUrl\": \"http:\/\/localhost:8080\/mcp\",\n      \"oauth\": {\n        \"clientId\": \"number-randomstring.apps.googleusercontent.com\",\n        \"clientSecret\": \"GOCSPX-randomstring\"\n      }\n    }\n  }\n}\n<\/code><\/pre>\n<h4>\u8a8d\u8a3c<\/h4>\n<p>\u7701\u7565\u3057\u307e\u3059\u3002<\/p>\n<h4>\u8a8d\u8a3c\u306e\u7d50\u679c<\/h4>\n<p>\u8a8d\u8a3c\u307e\u3067\u306f\u9032\u307f\u307e\u3057\u305f\u304c\u3001<code>ya29<\/code> \u3067\u59cb\u307e\u308b\u300c\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3\u300d\u304c Antigravity \u304b\u3089\u9001\u4fe1\u3055\u308c\u3066\u3044\u307e\u3057\u305f\u3002\u3053\u308c\u306f\u3001OAuth2 Proxy \u304c\u671f\u5f85\u3059\u308b JWT \u5f62\u5f0f\u306e\u30c8\u30fc\u30af\u30f3\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002OAuth2 Proxy \u3067\u306f\u691c\u8a3c\u3067\u304d\u306a\u3044\u3053\u3068\u304c\u5206\u304b\u308a\u307e\u3057\u305f\u3002<\/p>\n<p>\u7d30\u304b\u3044\u8a71\u306f <a href=\"https:\/\/docs.cloud.google.com\/docs\/authentication\/token-types?hl=ja\">token-types<\/a> \u306b\u66f8\u3044\u3066\u3042\u308a\u307e\u3059\u306d\u2026\u3002JWT \u30c8\u30fc\u30af\u30f3\u3067\u306f\u306a\u304f\u3001\u30e6\u30fc\u30b6\u30fc\u306e Google \u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3\u304c\u8fd4\u5374\u3055\u308c\u3066\u3044\u308b\u3088\u3046\u3067\u3059\u3002OAuth2 Proxy \u306f\u3001JWT \u5f62\u5f0f\u306e\u30c8\u30fc\u30af\u30f3\u3092\u691c\u8a3c\u3059\u308b\u3053\u3068\u3092\u671f\u5f85\u3057\u3066\u3044\u308b\u305f\u3081\u3001Antigravity \u304b\u3089\u9001\u4fe1\u3055\u308c\u308b\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3\u3067\u306f\u691c\u8a3c\u3067\u304d\u307e\u305b\u3093\u3002<\/p>\n<p>OAuth2 Proxy \u306b\u30d1\u30c3\u30c1\u3092\u5f53\u3066\u308b\u3053\u3068\u3067\u3001Antigravity \u304b\u3089\u9001\u4fe1\u3055\u308c\u308b\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3\u3092\u691c\u8a3c\u3059\u308b\u3053\u3068\u3082\u53ef\u80fd\u3067\u3059\u304c\u3001\u4eca\u56de\u306f\u3053\u306e\u691c\u8a3c\u7d50\u679c\u304b\u3089\u3001\u8efd\u91cf\u30d7\u30ed\u30ad\u30b7\u3092\u81ea\u4f5c\u3057\u3066\u307f\u308b\u65b9\u91dd\u306b\u5207\u308a\u66ff\u3048\u307e\u3057\u305f\u3002<\/p>\n<h2>\u5099\u8003<\/h2>\n<h3>\u8a8d\u8a3c\u65b9\u5f0f\u306e\u691c\u8a0e<\/h3>\n<p>Google Cloud \u3060\u3068\u3001HTTPS \u30b5\u30fc\u30d0\u30fc\u306e\u4fdd\u8b77\u306b\u306f IAP (Identity-Aware-Proxy) \u304c\u975e\u5e38\u306b\u4fbf\u5229\u3067\u3059\u3002\u3057\u304b\u3057\u3001IAP \u306f\u3001MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u671f\u5f85\u3059\u308b\u6319\u52d5\u3092\u3057\u306a\u3044\uff08MCP \u30b5\u30fc\u30d0\u30fc\u3068\u3057\u3066\u3001\u898f\u683c\u901a\u308a\u306e\u52d5\u4f5c\u3092\u3057\u306a\u3044\uff09\u90e8\u5206\u304c\u3042\u308b\u305f\u3081\u3001\u305d\u306e\u307e\u307e\u3067\u306f\u4f7f\u3048\u307e\u305b\u3093\u3002Google Gemini CLI \u306f IAP \u3067\u4fdd\u8b77\u3055\u308c\u305f Cloud Run \u7b49\u306b\u30c7\u30d7\u30ed\u30a4\u3055\u308c\u305f MCP \u30b5\u30fc\u30d0\u30fc\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u6a5f\u80fd\u3092\u6709\u3057\u3066\u3044\u307e\u3059 <a href=\"#\u53c2\u8003\u6587\u732e\">Gemini CLI IAP, gemini-cli#8505, service_account_impersonation<\/a>\u3002\u3057\u304b\u3057\u3001\u300c\u3069\u3093\u306a MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3067\u3082\u4f7f\u3048\u308b\u300d\u308f\u3051\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002<\/p>\n<p>SSH \u306b\u3088\u308b\u30c8\u30f3\u30cd\u30eb\u3084\u3001VPN\u3001\u6c4e\u7528\u7684\u306a\u8a8d\u8a3c\u30d7\u30ed\u30ad\u30b7\u3092\u4f7f\u3046\u65b9\u6cd5\u3082\u3042\u308a\u307e\u3059\u304c\u3001\u30e6\u30fc\u30b6\u30fc\u5074\u306b\u8a2d\u5b9a\u3092\u884c\u3063\u305f\u308a\u3001\u30b5\u30fc\u30d3\u30b9\uff08\u30d7\u30ed\u30b0\u30e9\u30e0\uff09\u306e\u52d5\u4f5c\u3092\u5fc5\u8981\u3068\u3059\u308b\u3053\u3068\u304c\u591a\u304f\u3001\u300c\u3042\u3089\u3086\u308b\u30e6\u30fc\u30b6\u30fc\u304c\u6c17\u8efd\u306b\u30fb\u4fbf\u5229\u306b\u4f7f\u3048\u308b\u300d\u308f\u3051\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002<\/p>\n<p>\u4e00\u65b9\u3067\u3001OAuth2 \u306f\u3001\u7279\u306b\u300c\u30e6\u30fc\u30b6\u30fc\u6a29\u9650\u578b\u300d\u306e MCP \u30b5\u30fc\u30d0\u30fc\u306b\u304a\u3044\u3066\u3001\u64cd\u4f5c\u5148\u30b5\u30fc\u30d3\u30b9\u3042\u308b\u3044\u306f\u7b2c\u4e09\u8005\u304c\u3001\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u6307\u793a\u3057\u3066\u3044\u308b\u30e6\u30fc\u30b6\u30fc\u3092\u8a8d\u8a3c\u3059\u308b\u305f\u3081\u306b\u4f7f\u308f\u308c\u3066\u304a\u308a\u3001MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\uff08\u306e\u307b\u3068\u3093\u3069\uff09\u306f OAuth2 \u8a8d\u8a3c\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<p>\uff08IAP \u306f OAuth2 \u8a8d\u8a3c\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u3059\u304c\u3001IAP \u306e\u8a8d\u8a3c\u306f MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u671f\u5f85\u3059\u308b\u6319\u52d5\u3092\u3057\u306a\u3044\u90e8\u5206\u304c\u3042\u308b\u305f\u3081\u3001MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304b\u3089\u306e\u30a2\u30af\u30bb\u30b9\u306b\u4f7f\u3048\u307e\u305b\u3093\u3002\uff09<\/p>\n<p>OAuth2 Proxy \u306f\u30d7\u30ed\u30bb\u30b9\u30fb\u30b3\u30f3\u30c6\u30ca\u3068\u3057\u3066\u3082\u52d5\u4f5c\u3055\u305b\u3089\u308c\u3001\u81ea\u7531\u5ea6\u304c\u9ad8\u3044\u3067\u3059\u3002OAuth2 Proxy \u306b\u4e00\u5de5\u592b\u3059\u308b\u3053\u3068\u3067\u3001MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u671f\u5f85\u3059\u308b\u6319\u52d5\u3092\u3059\u308b MCP \u30b5\u30fc\u30d0\u30fc\u3068\u3057\u3066\u52d5\u4f5c\u3055\u305b\u308b\u3053\u3068\u304c\u3067\u304d\u306a\u3044\u304b\uff1f\u3092\u691c\u8a0e\u3057\u307e\u3057\u305f\u3002<\/p>\n<h3>OAuth2 Proxy + Cloud Run \u3067\u306e\u52d5\u4f5c<\/h3>\n<p><code>\/.well-known\/oauth-protected-resource<\/code> \u306e\u90e8\u5206\u306b\u3064\u3044\u3066\u306f\u3001Cloud Storage \u306b <code>oauth-protected-resource<\/code> \u3068\u3044\u3046\u9759\u7684\u30d5\u30a1\u30a4\u30eb (JSON) \u3092\u7f6e\u304d\u3001GCSfuse \u3067\u30de\u30a6\u30f3\u30c8\u3055\u305b\u308b\u3053\u3068\u3067\u3001MCP \u30b5\u30fc\u30d0\u30fc\u306e\u30b3\u30f3\u30c6\u30ca\u524d\u6bb5\u306b OAuth2 Proxy \u3092\u7f6e\u304f\u3060\u3051\u3067\u3001MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u671f\u5f85\u3059\u308b\u6319\u52d5\u3092\u3059\u308b MCP \u30b5\u30fc\u30d0\u30fc\u3068\u3057\u3066\u52d5\u4f5c\u3055\u305b\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n<p>\u5177\u4f53\u7684\u306b\u306f <code>bucket-name\/.well-known<\/code> \u306b <code>oauth-protected-resource<\/code> \u3068\u3044\u3046 JSON \u30d5\u30a1\u30a4\u30eb\u3092\u7f6e\u3044\u3066\u304a\u304d\u307e\u3059\u3002Cloud Run \u306e\u30b3\u30f3\u30c6\u30ca\u306e\u30de\u30a6\u30f3\u30c8\u8a2d\u5b9a\u3067\u3001GCSfuse \u3092\u4f7f\u3063\u3066 <code>bucket-name<\/code> \u3092 <code>\/etc\/static<\/code> \u306b\u30de\u30a6\u30f3\u30c8\u3057\u307e\u3059\u3002Docker Compose \u3067 volumes \u306b <code>.\/static\/.well-known:\/etc\/static\/.well-known<\/code> \u306e\u3088\u3046\u306b\u8a2d\u5b9a\u3059\u308b\u306e\u3068\u540c\u3058\u3067\u3059\u3002\u5f8c\u306f <code>OAUTH2_PROXY_UPSTREAMS<\/code> \u306e\u8a2d\u5b9a\u3082\u540c\u3058\u306b\u3057\u3066\u304a\u3051\u3070\u3001\u30de\u30a6\u30f3\u30c8\u3055\u308c\u305f\u30d0\u30b1\u30c3\u30c8\u306e\u30d5\u30a1\u30a4\u30eb\u306e\u5185\u5bb9\u3092\u3001\u305d\u306e\u307e\u307e\u30ec\u30b9\u30dd\u30f3\u30b9\u3068\u3057\u3066 MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u8fd4\u3059\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n<p>\u305f\u3060\u3057\u3001\u524d\u8ff0\u306e\u901a\u308a\u3001OAuth2 Proxy \u306f <code>Authorization: Bearer<\/code> \u3068\u3057\u3066\u6e21\u3055\u308c\u308b\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3\u306e\u691c\u8a3c\u306b\u5931\u6557\u3059\u308b\u305f\u3081\u3001MCP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304b\u3089\u306e\u30a2\u30af\u30bb\u30b9\u306f\u5931\u6557\u3057\u307e\u3059\u3002<\/p>\n<h2>\u53c2\u8003\u6587\u732e<\/h2>\n<p><a name=\"oauth2-proxy_info\">[oauth2-proxy_info]<\/a> OAuth2 Proxy \u3068\u306f <a href=\"https:\/\/openstandia.jp\/oss_info\/oauth2-proxy\/\">https:\/\/openstandia.jp\/oss_info\/oauth2-proxy\/<\/a><br \/>\n<a name=\"oauth-provider-configuration\">[OAuth Provider Configuration]<\/a> OAuth Provider Configuration <a href=\"https:\/\/oauth2-proxy.github.io\/oauth2-proxy\/configuration\/providers\/\">https:\/\/oauth2-proxy.github.io\/oauth2-proxy\/configuration\/providers\/<\/a><\/p>\n<p>[Gemini CLI IAP] Gemini CLI \u304b\u3089 Cloud Run \u306b\u30c7\u30d7\u30ed\u30a4\u3057\u305f MCP \u30b5\u30fc\u30d0\u306b\u63a5\u7d9a\u3059\u308b\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9 <a href=\"https:\/\/zenn.dev\/kimitsu\/articles\/gemini-cli-cloud-run-mcp\">https:\/\/zenn.dev\/kimitsu\/articles\/gemini-cli-cloud-run-mcp<\/a><br \/>\n[gemini-cli#8505] feat(iap support): Add service account impersonation provider to MCPServers to support IAP on Cloud Run <a href=\"https:\/\/github.com\/google-gemini\/gemini-cli\/pull\/8505\">https:\/\/github.com\/google-gemini\/gemini-cli\/pull\/8505<\/a><br \/>\n[service_account_impersonation] <a href=\"https:\/\/geminicli.com\/docs\/tools\/mcp-server\/\">https:\/\/geminicli.com\/docs\/tools\/mcp-server\/<\/a><br \/>\n<a name=\"oauth2-proxy-config-overview\">[oauth2-proxy-config-overview]<\/a> Overview <a href=\"https:\/\/oauth2-proxy.github.io\/oauth2-proxy\/configuration\/overview\">https:\/\/oauth2-proxy.github.io\/oauth2-proxy\/configuration\/overview<\/a><\/p>\n<hr \/>\n<h1>Adding Authentication to Remote MCP Servers Using a Lightweight Proxy<\/h1>\n<h2>Overview<\/h2>\n<p>You can add authentication to an MCP server that has no authentication of its own by using a lightweight OAuth2 proxy. For example, you can safely protect an MCP server that you have launched in the cloud. The <code>\/.well-known\/oauth-protected-resource<\/code> part requires some ingenuity. OAuth2 Proxy is feature-rich, but existing tools like OAuth2 Proxy could not achieve this goal due to token verification limitations.<\/p>\n<h2>Introduction<\/h2>\n<p>This article discusses authentication of the user operating the MCP client, within the &#8220;MCP client -> MCP server -> target service&#8221; relationship.<br \/>\nIn particular, for the &#8220;MCP client -> MCP server&#8221; part, it introduces a method to authenticate users who access the MCP server, using a custom, lightweight OAuth2 proxy.<\/p>\n<h2>Background<\/h2>\n<h3>Summary of the background<\/h3>\n<p>Among &#8220;user-privilege type&#8221; and &#8220;shared type&#8221; MCP servers (described below), the &#8220;user-privilege type&#8221; already has authentication. We want to add authentication to the &#8220;shared type.&#8221;<\/p>\n<h3>Details (distinguishing the user-privilege type and the shared type)<\/h3>\n<p>When you ask an agent with an embedded LLM to perform &#8220;work related to a certain service\/software,&#8221; an MCP server is convenient. If the user is a developer (IT engineer) and has a local command-execution or container environment, they can launch an MCP server locally and have it do the work.<\/p>\n<p>On the other hand, if we also consider non-IT-engineer users, it is useful for a developer on the team, or someone representing the company, to launch an MCP server. Rather than many users each launching an MCP server in their own environment, a remote MCP server is launched, and users access it to request work. Launching an MCP server in each individual environment is inconvenient.<\/p>\n<p>However, when you launch a remote MCP server (one exposed to the internet), you need access control. In other words, authentication and authorization issues arise. If you make it accessible to anyone in the world, a malicious user could use the MCP server to perform unauthorized operations. Therefore, when accessing a remote MCP server, you need to verify the user&#8217;s authentication information (in limited situations, IP restrictions are also an option).<\/p>\n<p>There are several kinds of MCP server behavior and authentication, but here we classify them into the following two types and focus on adding authentication to the &#8220;shared type&#8221;:<\/p>\n<p>Classification:<\/p>\n<ul>\n<li>User-privilege type: operates with the user&#8217;s privileges<\/li>\n<li>Shared type: operates with common privileges (authenticating to the target service with an API key, etc.)<\/li>\n<\/ul>\n<p>In a &#8220;user-privilege type&#8221; MCP server, within the &#8220;MCP client -> MCP server -> target service&#8221; relationship, the target service or a third party authenticates the user instructing the client, and performs operations against the target service &#8220;with the privileges of that authenticated user (on behalf of the user).&#8221; That is, the MCP server needs to access the target service carrying the user&#8217;s authentication information.<\/p>\n<p>Such &#8220;user-privilege type&#8221; MCP servers have OAuth2 (or similar) authentication supported by the MCP server or the target service. Because authentication and authorization are built in, nothing special is required.<\/p>\n<p>In contrast, a &#8220;shared type&#8221; MCP server accesses the target service with common privileges, so it does not need to use the user&#8217;s authentication information. Conversely, a mechanism to authorize &#8220;who is allowed to use that MCP server&#8221; is usually not provided. You could use a password common to all users (you might call it an API key), but if it is shared, you cannot track who used the MCP server, and operations such as rotating secrets become difficult. Therefore, you want to add user authentication and authorization when accessing the remote MCP server.<\/p>\n<p>As a &#8220;shared type,&#8221; we assume, for example, an MCP server that receives an issued API key for the target service and registers it via a Secret. Indeed, an &#8220;API key&#8221; does exist here. However, what we are saying is that sharing a single API key among multiple users in an organization is far from ideal for security and auditing purposes.<\/p>\n<p>The &#8220;MCP server -> target service&#8221; part is invisible to users. A mechanism to safely hold the API key as a Secret is well established. The problem is the &#8220;MCP client -> MCP server&#8221; part. Here, instead of a password common to all users, we want to add a mechanism that identifies &#8220;who&#8221; is accessing and authorizes &#8220;whether that person is allowed to access.&#8221;<\/p>\n<h2>Custom Lightweight OAuth2 Proxy<\/h2>\n<p>This is different from the well-known &#8220;OAuth2 Proxy.&#8221; The reason we developed this software from scratch is described later, but it is because OAuth2 Proxy could not achieve the goal.<\/p>\n<h3>Configuration example and flow<\/h3>\n<h4>OAuth2<\/h4>\n<p>First, obtain a Client ID and Client Secret. In this custom OAuth2 proxy, user authentication is performed using the Client ID and Client Secret of the application registered with the OAuth2 provider.<br \/>\nI prepared a Google Cloud project and decided to use Google Cloud (Google) OAuth2 authentication. You need to create a Google Cloud project, create an OAuth2 client ID, set the redirect URIs, and so on.<br \/>\nFor those who have never configured this before, some parts may be difficult, but since it is outside the main topic of this article, I will only explain the necessary parts here and omit the detailed steps.<\/p>\n<p><img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/app_information.png\" alt=\"Web App settings\" \/><\/p>\n<p><img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/web_app_client.png\" alt=\"Web App Client screen\" \/><\/p>\n<p>Make a note of the Client ID and Client Secret.<\/p>\n<p>Add <code>https:\/\/antigravity.google\/oauth-callback` and<\/code>http:\/\/localhost:8080\/oauth2\/callback` to the Authorized redirect URIs.<\/p>\n<h4>Docker Compose configuration example<\/h4>\n<p>Once obtained, write it into the configuration file. Since it is configured to receive settings via environment variables, I decided to write it in YAML in a Docker Compose file. The following is a configuration example using Google OAuth2 authentication.<\/p>\n<p>The same file is available in the GitHub repository <a href=\"https:\/\/github.com\/takotakot\/misc\/blob\/main\/mcp-oauth2-proxy\/docker-compose.yaml\">docker-compose.yaml<\/a>, so please refer to it as well.<br \/>\nFor <code>OAUTH2_PROXY_CLIENT_ID<\/code>, <code>OAUTH2_PROXY_CLIENT_SECRET<\/code>, and <code>OAUTH2_PROXY_ALLOWED_EMAILS<\/code>, specify the Client ID and Client Secret of the application registered with the OAuth2 provider, and the email address of the user allowed to authenticate. The &#8220;email address of the user allowed to authenticate&#8221; is usually your own Google account&#8217;s email address. To allow multiple users, you can specify several separated by commas.<br \/>\nFor enterprise use, instead of <code>ALLOWED_EMAILS<\/code>, consider modifying it to specify an in-house domain, such as <code>ALLOWED_DOMAIN<\/code>, so that only in-house users can access it.<\/p>\n<pre><code class=\"language-yaml\">services:\n  # Lightweight Go OAuth2 Proxy (small-oauth2-proxy)\n  # All access is authenticated and forwarded through this lightweight proxy\n  # Authenticated users can access the Mock MCP Server\n  oauth2-proxy:\n    build: .\/small-oauth2-proxy\n    ports:\n      - \"8080:4180\"\n    environment:\n      OAUTH2_PROXY_HTTP_ADDRESS: \"0.0.0.0:4180\"\n\n      # Google OAuth2 \/ OIDC settings\n      OAUTH2_PROXY_CLIENT_ID: \"number-randomstring.apps.googleusercontent.com\"\n      OAUTH2_PROXY_CLIENT_SECRET: \"GOCSPX-randomstring\"\n      OAUTH2_PROXY_REDIRECT_URL: \"http:\/\/localhost:8080\/oauth2\/callback\"\n      OAUTH2_PROXY_ALLOWED_EMAILS: \"email@example.com\"\n\n      RESOURCE_URL: \"http:\/\/localhost:8080\/mcp\"\n\n      # Redirect \/ relay upstream\n      OAUTH2_PROXY_UPSTREAMS: \"http:\/\/mock-mcp-server:5678\"\n    depends_on:\n      - mock-mcp-server\n\n  # Mock MCP Server (everything)\n  # Launch a mock of a publicly available MCP server to return some \"valid\" response to the client\n  mock-mcp-server:\n    # See: https:\/\/hub.docker.com\/r\/mcp\/everything\n    # See: https:\/\/github.com\/modelcontextprotocol\/servers\/tree\/main\/src\/everything\n    # v x.y.z is tested.\n    image: node:alpine\n    # command: &gt;\n    #   npx -y @modelcontextprotocol\/server-everything streamableHttp\n    # command: &gt;\n    #   sh -c \"mkdir -p \/tmp\/mcp &amp;&amp; cd \/tmp\/mcp &amp;&amp; npm init -y &amp;&amp; npm install @modelcontextprotocol\/server-everything media-typer &amp;&amp; npx @modelcontextprotocol\/server-everything streamableHttp\"\n    command: &gt;\n      npx -y @modelcontextprotocol\/server-everything streamableHttp\n    environment:\n      PORT: 5678\n<\/code><\/pre>\n<h5>Reference<\/h5>\n<p>Without logging the value of the Bearer token passed in the header, debugging is difficult. By placing nginx in front and combining it with a module called njs, you can log header values. Refer to <a href=\"https:\/\/github.com\/takotakot\/misc\/blob\/main\/mcp-oauth2-proxy\/docker-compose_log_nginx.yaml\">docker-compose_log_nginx.yaml<\/a> and the surrounding files (this is a configuration example combined with the OAuth2 Proxy described later).<\/p>\n<h4>Client configuration example<\/h4>\n<p>I tried Antigravity as the client.<\/p>\n<p>An example of <code>.gemini\/config\/mcp_config.json<\/code> is shown below. <code>serverUrl<\/code> specifies the connection URL. Note that <code>\/mcp<\/code> is appended, as in `http:\/\/localhost:8080\/mcp`.<\/p>\n<pre><code class=\"language-json\">{\n  \"mcpServers\": {\n    \"oauth2-proxy-everything\": {\n      \"oauth\": {\n        \"clientId\": \"number-randomstring.apps.googleusercontent.com\",\n        \"clientSecret\": \"GOCSPX-randomstring\"\n      },\n      \"serverUrl\": \"http:\/\/localhost:8080\/mcp\"\n    }\n  }\n}\n<\/code><\/pre>\n<p>Antigravity is configured to use <code>https:\/\/antigravity.google\/oauth-callback` as the redirect destination (at least when you set clientId and clientSecret manually), so you need to add it to the OAuth2 allowed destinations. Add<\/code>https:\/\/antigravity.google\/oauth-callback` to the Authorized redirect URIs. Since it is used often, it is convenient to also add `http:\/\/localhost:8080\/oauth2\/callback`.<\/p>\n<h4>Authentication<\/h4>\n<p>If configured correctly, &#8220;oauth2-proxy-everything&#8221; appears in Antigravity&#8217;s settings screen under Customizations > Installed MCP Servers. When you click the Authenticate link, a browser launches and the Google account authentication screen appears. After authenticating, you will see the Antigravity web screen.<\/p>\n<p>You are redirected to a URL like the following, and a token that usually starts with <code>4\/<\/code> is returned. Copy it and Submit to complete authentication. The tools should now be usable.<\/p>\n<p>`https:\/\/antigravity.google\/oauth-callback?state=_state_value_&#038;iss=https%3A%2F%2Faccounts.google.com&#038;scope=email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid&#038;authuser=0&#038;prompt=consent`<\/p>\n<p><img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/antigravity-authenticate.png\" alt=\"Click the Authenticate link\" \/><br \/>\n&lt;img src=\/blog\/wp-content\/uploads\/2026\/08\/antigravity-redirected.png&#8221; alt=&#8221;After the redirect&#8221; \/><br \/>\n<img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/antigravity-submit.png\" alt=\"A text box and a Submit button are shown\" \/><br \/>\n<img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/antigravity-tools-enabled.png\" alt=\"After authentication completes\" \/><\/p>\n<h3>Conversation example (Antigravity)<\/h3>\n<p>Once configured, you can call the features provided by the MCP server as tools.<\/p>\n<blockquote><p>\n  Please call the <code>echo<\/code> of the MCP server oauth2-proxy-everything with &#8220;\u3053\u3093\u306b\u3061\u306f&#8221;\n<\/p><\/blockquote>\n<p>By entering something like the above into the chat, a response of &#8220;\u3053\u3093\u306b\u3061\u306f&#8221; is returned.<\/p>\n<p><img decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2026\/08\/antigravity-tool-call.png\" alt=\"Example of a tool call\" \/><\/p>\n<h2>Summary<\/h2>\n<p>In the first half of this article, we covered authentication for the &#8220;MCP client -> MCP server&#8221; segment, specifically a way to add per-user authentication and authorization after the fact to a &#8220;shared type&#8221; MCP server that has no authentication of its own.<\/p>\n<p>To do so, we place a custom, lightweight OAuth2 proxy (small-oauth2-proxy) in front of the MCP server. Using Google OAuth2 as the authentication provider, simply by configuring the Client ID \/ Client Secret and the email addresses allowed to access (consider a per-domain setting for enterprises), only permitted users can use the MCP server. Unlike distributing a password (API key) common to all users, the advantage is that you can authorize access after identifying &#8220;who is accessing.&#8221;<\/p>\n<p>The configuration is completed with Docker Compose environment variables, and on the MCP client side (here, Antigravity) you can connect simply by specifying a URL with <code>\/mcp<\/code> in <code>serverUrl<\/code> and the Client ID \/ Client Secret in <code>oauth<\/code>. In fact, we confirmed that after Google authentication in the browser from Authenticate, the tools were enabled and the <code>echo<\/code> tool call succeeded.<\/p>\n<p>Note that the key to making this configuration work is the response of <code>\/.well-known\/oauth-protected-resource<\/code>, which required some ingenuity (described later).<\/p>\n<p>We have not tried connecting in the cloud, but in principle, by using a sidecar container configuration on Cloud Run, the same configuration is possible for an MCP server in the cloud.<\/p>\n<hr \/>\n<h2>Response content of <code>\/.well-known\/oauth-protected-resource<\/code><\/h2>\n<p>The URL set in <code>serverUrl<\/code> etc. (this differs by client) must match the value of <code>resource<\/code> in the response returned when accessing <code>\/.well-known\/oauth-protected-resource<\/code> (strictly speaking, <code>\/.well-known\/oauth-protected-resource\/mcp<\/code> is also accessed, but that gets into the weeds, so it is omitted). Since we identify users by email address this time, we specify <code>email<\/code> in <code>scopes_supported<\/code>. In <code>authorization_servers<\/code>, specify the URL of the OAuth2 provider.<\/p>\n<pre><code class=\"language-json\">{\n  \"resource\": \"http:\/\/localhost:8080\/mcp\",\n  \"authorization_servers\": [\n    \"https:\/\/accounts.google.com\"\n  ],\n  \"scopes_supported\": [\n    \"email\"\n  ]\n}\n<\/code><\/pre>\n<hr \/>\n<h2>OAuth2 Proxy (OSS failure example)<\/h2>\n<p>OAuth2 Proxy is a proxy server that supports OAuth2 authentication <a href=\"#oauth2-proxy_info\">oauth2-proxy_info<\/a>. While operating as a reverse proxy, it can allow access only to authorized users. By using OAuth2 Proxy, you can easily add OAuth2 authentication to a web application that has no login function. OAuth2 Proxy can authenticate in cooperation with OAuth2 providers such as Google, GitHub, GitLab, and Microsoft Entra ID <a href=\"#oauth-provider-configuration\">OAuth Provider Configuration<\/a>.<\/p>\n<p>Since the OSS OAuth2 Proxy could not achieve the goal, those who &#8220;only want to know the method that works&#8221; can skip this section.<\/p>\n<h3>Configuration example and flow<\/h3>\n<h4>OAuth2<\/h4>\n<p>First, obtain a Client ID and Client Secret. In OAuth2 Proxy, user authentication is performed using the Client ID and Client Secret of the application registered with the OAuth2 provider.<\/p>\n<p>This is the same as the first half, so it is omitted.<\/p>\n<h4>Docker Compose configuration example<\/h4>\n<p>Once obtained, write it into the OAuth2 Proxy configuration file. Since OAuth2 Proxy settings can be passed via environment variables, I decided to write them in YAML in a Docker Compose file. The following is a configuration example using Google OAuth2 authentication.<\/p>\n<p>The same file is available in the GitHub repository <a href=\"https:\/\/github.com\/takotakot\/misc\/blob\/main\/mcp-oauth2-proxy\/docker-compose_log_nginx.yaml\">docker-compose_log_nginx.yaml<\/a>, so please refer to it as well.<br \/>\nFor enterprise use, by specifying an in-house domain such as <code>OAUTH2_PROXY_EMAIL_DOMAINS: \"example.com\"<\/code>, only in-house users can access it, which simplifies configuration. If per-user configuration is needed, you can also use <code>OAUTH2_AUTHENTICATED_EMAILS_FILE<\/code> to specify the email addresses allowed to authenticate in a file.<\/p>\n<p><code>OAUTH2_PROXY_COOKIE_SECRET<\/code> must be a Base64-encoded value of a random 32-byte string. A random 32-byte string can be generated with something like <code>openssl rand -base64 32<\/code>.<\/p>\n<p>For configuration details, see [oauth2-proxy-config-overview].<\/p>\n<pre><code class=\"language-yaml\">services:\n  # Nginx front proxy\n  # Receives all access from the client, logs the HTTP headers, and then forwards to oauth2-proxy\n  nginx:\n    image: nginx:alpine\n    ports:\n      - \"8080:80\"\n    volumes:\n      - .\/nginx.conf:\/etc\/nginx\/nginx.conf:ro\n      - .\/headers.js:\/etc\/nginx\/headers.js:ro\n      - .\/static\/.well-known:\/usr\/share\/nginx\/html\/.well-known:ro\n    depends_on:\n      - oauth2-proxy\n\n  # OAuth2 Proxy (oauth2-proxy)\n  # Ensure all access is authenticated through OAuth2 Proxy\n  # Specify exceptions with `SKIP_AUTH_ROUTES`\n  # Two upstreams are specified\n  #  # 1. A route for returning static metadata (\/.well-known\/)\n  #  # 2. Mock MCP Server (http:\/\/mock-mcp-server:5678\/)\n  # Authenticated users can access the Mock MCP Server\n  oauth2-proxy:\n    # v x.y.z is tested.\n    image: quay.io\/oauth2-proxy\/oauth2-proxy:latest\n    environment:\n      # See: https:\/\/oauth2-proxy.github.io\/oauth2-proxy\/configuration\/overview\/\n      OAUTH2_PROXY_HTTP_ADDRESS: \"0.0.0.0:4180\"\n      OAUTH2_PROXY_API_ROUTES: \"^\/mcp\"\n\n      # Google OAuth2 \/ OIDC settings\n      OAUTH2_PROXY_PROVIDER: \"oidc\"\n      OAUTH2_PROXY_OIDC_ISSUER_URL: \"https:\/\/accounts.google.com\"\n      # OAUTH2_PROXY_PROVIDER: \"google\"\n      OAUTH2_PROXY_CLIENT_ID: \"number-randomstring.apps.googleusercontent.com\"\n      OAUTH2_PROXY_CLIENT_SECRET: \"GOCSPX-randomstring\"\n      OAUTH2_PROXY_REDIRECT_URL: \"http:\/\/localhost:8080\/oauth2\/callback\"\n      OAUTH2_PROXY_EMAIL_DOMAINS: \"*\"\n      OAUTH2_PROXY_ALLOWED_EMAILS: \"email@example.com\"\n\n      # Metadata return\n      # OAUTH2_PROXY_SKIP_AUTH_ROUTES: \"^\/\\\\.well-known\/oauth-protected-resource,^\/mcp,^\/mockserver\"\n      OAUTH2_PROXY_SKIP_AUTH_ROUTES: \"^\/\\\\.well-known\/oauth-protected-resource\"\n      OAUTH2_PROXY_UPSTREAMS: 'file:\/\/\/etc\/static\/.well-known\/#\/.well-known\/,http:\/\/mock-mcp-server:5678\/'\n      # OAUTH2_PROXY_UPSTREAMS: 'http:\/\/mock-mcp-server:5678\/'\n\n      # Allow API authentication (token verification)\n      OAUTH2_PROXY_SKIP_JWT_BEARER_TOKENS: \"true\"\n      OAUTH2_PROXY_EXTRA_JWT_ISSUERS: \"https:\/\/accounts.google.com=aud\"\n\n      OAUTH2_PROXY_COOKIE_SECRET: \"abcdefghijklmnopqrstuvwxyz123456\"\n    volumes:\n      - .\/static\/.well-known:\/etc\/static\/.well-known\n    # command: [\"\/bin\/oauth2-proxy\", \"--introspect-token=true\", \"--introspect-url=https:\/\/www.googleapis.com\/oauth2\/v3\/tokeninfo\"]\n\n  # Mock MCP Server (everything)\n  # Launch a mock of a publicly available MCP server to return some \"valid\" response to the client\n  mock-mcp-server:\n    # See: https:\/\/hub.docker.com\/r\/mcp\/everything\n    # See: https:\/\/github.com\/modelcontextprotocol\/servers\/tree\/main\/src\/everything\n    # v x.y.z is tested.\n    image: node:alpine\n    # command: &gt;\n    #   npx -y @modelcontextprotocol\/server-everything streamableHttp\n    # command: &gt;\n    #   sh -c \"mkdir -p \/tmp\/mcp &amp;&amp; cd \/tmp\/mcp &amp;&amp; npm init -y &amp;&amp; npm install @modelcontextprotocol\/server-everything media-typer &amp;&amp; npx @modelcontextprotocol\/server-everything streamableHttp\"\n    command: &gt;\n      npx -y @modelcontextprotocol\/server-everything streamableHttp\n    environment:\n      PORT: 5678\n<\/code><\/pre>\n<h4>Client configuration example<\/h4>\n<p>I tried Antigravity as the client.<\/p>\n<p>An example of <code>.gemini\/config\/mcp_config.json<\/code> is shown below. This is the same as the first-half example. <code>serverUrl<\/code> specifies the connection URL. Note that <code>\/mcp<\/code> is appended, as in `http:\/\/localhost:8080\/mcp`.<\/p>\n<pre><code class=\"language-json\">{\n  \"mcpServers\": {\n    \"oauth2-proxy-everything\": {\n      \"serverUrl\": \"http:\/\/localhost:8080\/mcp\",\n      \"oauth\": {\n        \"clientId\": \"number-randomstring.apps.googleusercontent.com\",\n        \"clientSecret\": \"GOCSPX-randomstring\"\n      }\n    }\n  }\n}\n<\/code><\/pre>\n<h4>Authentication<\/h4>\n<p>Omitted.<\/p>\n<h4>Authentication result<\/h4>\n<p>Authentication proceeded, but an &#8220;access token&#8221; starting with <code>ya29<\/code> was being sent from Antigravity. This is not the JWT-format token that OAuth2 Proxy expects. It turned out that OAuth2 Proxy could not verify it.<\/p>\n<p>The details are described in <a href=\"https:\/\/docs.cloud.google.com\/docs\/authentication\/token-types\">token-types<\/a>. It appears that, rather than a JWT token, the user&#8217;s Google account access token is returned. Because OAuth2 Proxy expects to verify a JWT-format token, it cannot verify the access token sent from Antigravity.<\/p>\n<p>It is also possible to patch OAuth2 Proxy to verify the access token sent from Antigravity, but this time, based on this verification result, we switched to the approach of building a lightweight proxy ourselves.<\/p>\n<h2>Notes<\/h2>\n<h3>Considering authentication methods<\/h3>\n<p>On Google Cloud, IAP (Identity-Aware Proxy) is very convenient for protecting HTTPS servers. However, IAP has parts that do not behave as an MCP client expects (it does not operate according to the MCP server specification), so it cannot be used as-is. Google Gemini CLI has the ability to access an MCP server deployed on IAP-protected Cloud Run, etc. <a href=\"#references\">Gemini CLI IAP, gemini-cli#8505, service_account_impersonation<\/a>. However, it is not the case that &#8220;any MCP client can use it.&#8221;<\/p>\n<p>There are also methods using SSH tunnels, VPNs, or general-purpose authentication proxies, but they often require configuration on the user side or the operation of a service (program), so it is not the case that &#8220;any user can use it easily and conveniently.&#8221;<\/p>\n<p>On the other hand, OAuth2 is used, especially in &#8220;user-privilege type&#8221; MCP servers, for the target service or a third party to authenticate the user instructing the client, and (most) MCP clients support OAuth2 authentication.<\/p>\n<p>(IAP supports OAuth2 authentication, but because IAP&#8217;s authentication has parts that do not behave as an MCP client expects, it cannot be used for access from MCP clients.)<\/p>\n<p>OAuth2 Proxy can be run as a process or a container and is highly flexible. We considered whether, with a little ingenuity, OAuth2 Proxy could be made to operate as an MCP server that behaves as MCP clients expect.<\/p>\n<h3>Operation with OAuth2 Proxy + Cloud Run<\/h3>\n<p>For the <code>\/.well-known\/oauth-protected-resource<\/code> part, by placing a static file (JSON) named <code>oauth-protected-resource<\/code> in Cloud Storage and mounting it with GCSfuse, you can operate as an MCP server that behaves as MCP clients expect simply by placing OAuth2 Proxy in front of the MCP server container.<\/p>\n<p>Specifically, place a JSON file named <code>oauth-protected-resource<\/code> in <code>bucket-name\/.well-known<\/code>. In the Cloud Run container mount settings, use GCSfuse to mount <code>bucket-name<\/code> to <code>\/etc\/static<\/code>. This is the same as setting <code>.\/static\/.well-known:\/etc\/static\/.well-known<\/code> in volumes in Docker Compose. Then, if you keep the <code>OAUTH2_PROXY_UPSTREAMS<\/code> setting the same, you can return the contents of the mounted bucket&#8217;s file as-is as the response to the MCP client.<\/p>\n<p>However, as described above, OAuth2 Proxy fails to verify the access token passed as <code>Authorization: Bearer<\/code>, so access from the MCP client fails.<\/p>\n<h2>References<\/h2>\n<p>[oauth2-proxy_info] What is OAuth2 Proxy <a href=\"https:\/\/openstandia.jp\/oss_info\/oauth2-proxy\/\">https:\/\/openstandia.jp\/oss_info\/oauth2-proxy\/<\/a><br \/>\n[OAuth Provider Configuration] OAuth Provider Configuration <a href=\"https:\/\/oauth2-proxy.github.io\/oauth2-proxy\/configuration\/providers\/\">https:\/\/oauth2-proxy.github.io\/oauth2-proxy\/configuration\/providers\/<\/a><\/p>\n<p>[Gemini CLI IAP] Best practices for connecting from Gemini CLI to an MCP server deployed on Cloud Run <a href=\"https:\/\/zenn.dev\/kimitsu\/articles\/gemini-cli-cloud-run-mcp\">https:\/\/zenn.dev\/kimitsu\/articles\/gemini-cli-cloud-run-mcp<\/a><br \/>\n[gemini-cli#8505] feat(iap support): Add service account impersonation provider to MCPServers to support IAP on Cloud Run <a href=\"https:\/\/github.com\/google-gemini\/gemini-cli\/pull\/8505\">https:\/\/github.com\/google-gemini\/gemini-cli\/pull\/8505<\/a><br \/>\n[service_account_impersonation] <a href=\"https:\/\/geminicli.com\/docs\/tools\/mcp-server\/\">https:\/\/geminicli.com\/docs\/tools\/mcp-server\/<\/a><br \/>\n[oauth2-proxy-config-overview] Overview <a href=\"https:\/\/oauth2-proxy.github.io\/oauth2-proxy\/configuration\/overview\">https:\/\/oauth2-proxy.github.io\/oauth2-proxy\/configuration\/overview<\/a><br \/>\n[token-types] Token types <a href=\"https:\/\/docs.cloud.google.com\/docs\/authentication\/token-types?hl=en\">https:\/\/docs.cloud.google.com\/docs\/authentication\/token-types?hl=en<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u30ea\u30e2\u30fc\u30c8 MCP \u30b5\u30fc\u30d0\u30fc\u306e\u8a8d\u8a3c\u3092\u8efd\u91cf\u30d7\u30ed\u30ad\u30b7\u3067\u884c\u3046\u65b9\u6cd5 English follows Japanese. \u6982\u8981 \u8a8d\u8a3c\u6a5f\u80fd\u3092\u6301\u305f\u306a\u3044 MCP \u30b5\u30fc\u30d0\u30fc\u306b\u5bfe\u3057\u3066\u3001\u8efd\u91cf\u306a OAuth2 \u306e Proxy \u3092\u4f7f\u3063\u3066\u8a8d\u8a3c\u6a5f\u80fd\u3092\u8ffd [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[17,7,3],"tags":[],"class_list":["post-729","post","type-post","status-publish","format-standard","hentry","category-docker","category-google","category-misc"],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4dIdP-bL","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts\/729","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/comments?post=729"}],"version-history":[{"count":16,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts\/729\/revisions"}],"predecessor-version":[{"id":745,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/posts\/729\/revisions\/745"}],"wp:attachment":[{"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/media?parent=729"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/categories?post=729"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tako.nakano.net\/blog\/wp-json\/wp\/v2\/tags?post=729"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}