Model Context Protocolは、問題の間違った半分を先に解決した。これは批判ではなく、ただ物事が起きた順序の話だ。MCPは言語モデルをツールやデータに接続するための、明確で標準的な方法を与えてくれた。MCPが意図的にやらないのは、そのツールの一つがオープンウェブに手を伸ばし、オープンウェブが403を返してくるときに何が起きるかを解決することだ。
URLを取得したり、ページをスクレイピングしたり、検索結果にアクセスしたり、モデルのためにライブデータを取得するMCPサーバーを構築または運用したことがあるなら、おそらくこの壁にすでに遭遇しているだろう。サーバーは自分のノートパソコンでは完璧に動く。それをクラウドのマシンにデプロイし、エージェントをそこに向けると、突然フェッチの半分がCAPTCHAページ、「access denied」、あるいは間違った国のサイトへのジオリダイレクトとして返ってくる。あなたのMCPコードは何も変わっていない。変わったのはIPだ。
この記事はそのギャップについて書く。なぜウェブを取得するMCPサーバーがブロックされるのか、なぜそれはMCPが解決すべき問題ではないのか、そしてresidential proxy層がどうやって数行のコードでそれを埋めるのか。
ウェブアクセスがどこで起きるかの簡単なおさらい
MCPには明確な役割分担がある。ホスト(Claude Desktop、IDE、エージェントランタイム)がMCPクライアントを動かし、それが一つ以上のMCPサーバーと通信する。各サーバーはツール、リソース、プロンプトを公開する。モデルがツールを使うことを決めると、呼び出しはホスト → クライアント → サーバーと流れ、サーバーが実際の作業を行い、結果が戻ってくる。
この議論で重要な部分はこれだ。サーバーこそが、実世界での副作用が起きる場所だ。fetchツール、searchツール、scrape_pageツールを構築するとき、送信されるHTTPリクエストはMCPサーバープロセスから発生する。そのプロセスがどこで動いていようと関係ない。モデルがリクエストを発するわけではない。ホストもそうではない。サーバーがそれを行う。
つまり、対象のウェブサイトが見るIPはMCPサーバーのIPだ。そしてそれこそが問題全体なのだ。
なぜウェブを取得するMCPサーバーがブロックされるのか
三つの要因が積み重なる。そしてそれらは、ブラウザを使う人間には積み重ならないような形で、MCPサーバーに対して特異的に積み重なる。
あなたのMCPサーバーはほぼ確実にデータセンターIPにある。 AWS、GCP、Fly、Render、VPS、その他どのクラウドホストにデプロイしたにせよ、その送信IPはホスティングプロバイダーのASNに属している。アンチボットシステム(Cloudflare、Akamai、DataDome)はデータセンターASNを、証明されるまでは有罪として扱う。AWSのIPから保護されたサイトへのリクエストは、サーバーがヘッダーを送る前からフラグが立てられる。これが「ローカルでは動いた」が「本番ではブロックされる」に変わる最大の理由だ。自宅の接続はレジデンシャルだが、クラウドのマシンはそうではない。
エージェントのトラフィックはバースト的で集中している。 エージェントは人間のようにブラウジングしない。狭い時間枠の中で一連のリクエストを、しばしば同じドメインに対して発射し、その後静かになる。一つのIPから見ると、そのパターンは即座に自動化として読み取られる。私たちはAIエージェントのトラフィックの形について別に書いたが、短く言えば、人間が決して引っかからないレート制限や行動検知に、エージェントは常に引っかかる。すべてのトラフィックが一つのアドレスから来るからだ。
地理的なコントロールがない。 あなたのMCPサーバーは一つのリージョンで動いている。モデルがドイツの買い物客として商品ページを見る必要があったり、東京のユーザーとして検索結果を見る必要があったり、米国の顧客として価格を見る必要があったりする場合、単一リージョンのサーバーには物理的にそれができない。サイトはサーバーのIPを地理特定し、静かに間違った国のコンテンツを提供する。そしてモデルは、自分が正しいと考えているが実際は正しくないデータについて推論することになる。
これらはどれも、あなたのMCPサーバーのバグではない。それがどこで動いていて、どういう形をしているかの性質だ。MCPはまさにその仕事、つまりツール呼び出しを運ぶことをしている。IPを洗浄することは、もともと意図されていなかった。
なぜこれはMCPが解決すべき仕事ではないのか
これは明示しておく価値がある。人々は時に、来ることのない機能をプロトコルが備えるのを待ってしまうからだ。MCPはモデルとツール間の通信のためのプロトコルだ。ツールがどう記述され、呼び出され、結果がどう返ってくるかを標準化する。クライアントとサーバー間のトランスポート(stdio、あるいはStreamable HTTP)は、エージェントの配管についてのものであり、サーバーがどうインターネットに到達するかについてのものではない。
あなたのfetchツールが外の世界とどう話すかは、あなたのサーバーの実装の詳細であり、どのHTTPライブラリを使うか、どうレスポンスをパースするかとまったく同じだ。プロトコルはそれを指示すべきではないし、実際に指示していない。つまり、ウェブアクセス層はあなたが追加するものだ。良いニュースは、それが小さく、よく理解された層であることだ。サーバーの送信リクエストをレジデンシャルIP経由にルーティングする。
解決策: fetchツールの背後にあるresidential proxy
パターンはシンプルだ。あなたのMCPサーバーのツールがデータセンターIPから直接リクエストを行う代わりに、residential proxyゲートウェイ経由でリクエストを行う。対象のサイトは、正しい国にある本物の消費者IPを、実際の自宅接続の信頼プロファイルとともに見て、リクエストは通過する。
以下は、Shifterゲートウェイ経由でルーティングするfetch_urlツールを持つ最小限のPython MCPサーバーだ。MCPの足場は標準的なFastMCPで、重要な部分はHTTPクライアントのproxies設定だ。
import osimport httpxfrom mcp.server.fastmcp import FastMCP
mcp = FastMCP("web-fetch")
# One gateway endpoint, targeting encoded in the username.# Keep credentials in env, never in the tool definition.USER = os.environ["SHIFTER_USER"] # e.g. "customer-<id>"PASS = os.environ["SHIFTER_PASS"]GATEWAY = "p.shifter.io:443"
def proxy_url(country: str = "us") -> str: return f"http://{USER}-country-{country}:{PASS}@{GATEWAY}"
@mcp.tool()async def fetch_url(url: str, country: str = "us") -> str: """Fetch a URL through a residential IP in the given country.""" proxies = proxy_url(country) async with httpx.AsyncClient(proxy=proxies, timeout=30) as client: r = await client.get(url, follow_redirects=True) r.raise_for_status() return r.text
if __name__ == "__main__": mcp.run()変更点はこれだけだ。モデルがfetch_url("https://example.com/product", country="de")を呼び出すと、リクエストはドイツのレジデンシャルIPを通って出ていき、サイトはドイツの買い物客に見えるものに対してドイツのページを提供する。countryを切り替えれば、モデルは再デプロイも二つ目のサーバーも必要とせずに、別の市場の視点から同じページを見ることになる。
ジオターゲティングはエージェントが実際に最も必要とする部分だ
ブロックの問題は人々が最初に気づくものだが、ジオの問題は静かに結果を腐敗させるものだ。一つのリージョンに固定されたMCPサーバーは、一つの鍵穴から世界を見ているLLMだ。
Shifterゲートウェイがターゲティングをユーザー名にエンコードするため、あなたのfetchツールはcountry(あるいは州、都市、さらにはASN)引数を取ることができ、モデルが呼び出しごとに選択できる。これにより単一リージョンのサーバーがグローバルなものに変わる。市場を横断して価格を比較する調査エージェント、五カ国でサイトがどう表示されるかを確認するローカライゼーションQAエージェント、ローカルユーザーとしてGoogleの結果を取得するSERP監視エージェント、これらすべてがまさにこれを必要としていて、素のクラウドホストされたfetchツールからはそれを得られない。
エージェントが複数の呼び出しにわたって同じIPを必要とする複数段階のフロー(ログインしてから、認証済みページの連続)のために、ユーザー名にセッションIDとTTLを含めることでスティッキーセッションを追加できる。同じIPがTTLの寿命の間保持される。
def sticky_proxy_url(country: str, session: str, ttl: int = 600) -> str: return f"http://{USER}-country-{country}-sid-{session}-ttl-{ttl}:{PASS}@{GATEWAY}"これでbrowse_sessionツールは、タスクの各ステップにわたって安定したアイデンティティを保持できるようになり、フローの途中でIPを飛び回ってサイトのすべてのセッションチェックに引っかかることがなくなる。
留意すべきこと
proxy層は魔法の「絶対にブロックされない」スイッチではなく、そう扱うことは帯域幅を無駄にする方法だ。実践的な注意点をいくつか挙げる。
積極的にキャッシュする。 エージェントは同じURLを常に再取得する。fetchツールの前段にある小さなキャッシュは、proxyの帯域幅(とあなたのコスト)を大幅に削減し、モデルにとっても速くなる。すでに答えを持っているリクエストをproxyする必要はない。
countryはハードコードせず、渡すようにする。 その価値のすべては、呼び出しごとのジオコントロールにある。countryを、一つのリージョンをサーバーに焼き込むのではなく、モデルが妥当なデフォルトとともに設定できるツール引数にする。
対象を尊重する。 proxyはリクエストがどのIPから来るかを変えるだけで、そのリクエストをすべきかどうかを変えるわけではない。負荷に関わる場合はrobots.txtを尊重し、リクエストレートを妥当に保ち、ブロックがなくなったからといってサイトを叩きすぎないようにする。私たちのacceptable use policyが、何が許可されるかについての正しい情報源だ。
認証情報をツールスキーマの外に保つ。 proxyのユーザー名とパスワードはサーバー上の環境変数に置き、モデルが見るツール定義には決して入れない。モデルは国を選ぶべきであり、あなたのゲートウェイのパスワードを持つべきではない。
保護されていない、リージョン中立なフェッチにはデータセンターで十分だ。 ツールが自分のAPIやブロックもジオロケーションもしない公開エンドポイントだけを叩くのであれば、proxyは必要ない。レジデンシャル層は、実際にブロックに直面する、あるいはジオが必要なオープンウェブのフェッチのために取っておく。(それぞれのIPタイプがいつ適合するかのより詳しい比較については、residential vs datacenter proxiesを参照。)
FAQ
MCPには組み込みのproxyサポートがありますか? ない、そしてそうあるべきではない。MCPはモデルとツール間の通信を標準化するものであり、ツールがどうインターネットに到達するかではない。送信ウェブアクセスはあなたのサーバーの実装の詳細だ。上記で示したように、ツール内のHTTPクライアントレベルでproxyを追加する。
MCPサーバーをレジデンシャル接続から動かせばいいのでは? できるが、それはスケールしないし、リクエストごとのジオコントロールも得られないし、エージェントの信頼性を一つの自宅接続のアップタイムと単一のIPに結びつけてしまう。residential proxyゲートウェイは、レジデンシャル回線上に何もホスティングせずに、大規模なローテーティングプールと呼び出しごとの国別ターゲティングを提供する。
proxyはエージェントが完全にブロックされることを止めてくれますか? 最大の原因(データセンターIP)を取り除き、ジオコントロールを与えてくれるが、振る舞いも依然として重要だ。バースト的なリクエストパターン、欠落した、あるいは一貫性のないヘッダー、レート制限の無視などは、それでもフラグを立てられる可能性がある。proxyは必要だが十分ではない。まともなリクエスト挙動と組み合わせること。
これはTypeScriptのMCPサーバーでも動きますか? はい。コンセプトは同一で、HTTPクライアント(agentを使ったfetch、axios、undici、got)をゲートウェイ経由でルーティングするように設定する。proxy URLの形式と呼び出しごとのターゲティングは、言語にかかわらず同じだ。
モデルはリクエストごとに国を選べますか?
はい、それが推奨されるパターンだ。country(オプションで州/都市)をデフォルト付きのツール引数として公開する。モデルはタスクに基づいてそれを設定し、あなたのサーバーはそれをその場でproxyのユーザー名にエンコードする。
proxy経由でルーティングすると、Shifterの課金方法は変わりますか? 変わらない。通常のGB単位の価格設定による標準的なレジデンシャルゲートウェイだ。MCPサーバーは同じエンドポイントに接続するもう一つのクライアントに過ぎない。帯域幅は帯域幅だ。
結論
MCPは「モデルがどうツールを呼び出すか」という問いに対する明確な答えだ。それは「そのツールが、間違った国のフラグ付きデータセンターIPから敵対的なオープンウェブにどう到達するか」という問いに答えることを、もともと意図していなかった。この二番目の問いは実在し、それに答えるのはあなたの仕事であり、その答えはあなたのfetchツールの背後にある薄いresidential proxy層だ。
配線は数行だ。見返りは、本番で実際に動作し、正しい国のデータを返し、エージェントが保護されたサイトにそれを向けた最初の瞬間に倒れないウェブツールを持つMCPサーバーだ。ウェブに接続するエージェントを構築しているなら、residentialゲートウェイから始めて、最初の日からあなたのツールの背後にそれを置いてほしい。ブロックが始まった後で後付けするよりも、はるかに簡単だ。エージェントに信頼できるウェブアクセスを与えることについてもっと知りたければ、ウェブをブラウズするAIエージェントに最適なproxyを参照。