지식

MCP 서버에 프록시 레이어가 필요한 이유

실시간 웹 데이터를 가져오는 MCP 서버는 다른 스크레이퍼와 마찬가지로 차단당합니다. 이러한 현상이 발생하는 이유와 레지덴셜 프록시를 통해 요청을 라우팅하는 방법을 알아봅니다.

Chris Collins

Chris Collins

2026년 6월 20일 · 8 분 소요

Model Context Protocol은 문제의 잘못된 절반을 먼저 해결했는데, 이는 비판이 아니라 그저 일이 진행된 순서일 뿐이다. MCP는 언어 모델을 도구와 데이터에 연결하는 깔끔하고 표준화된 방법을 제공했다. 그것이 의도적으로 해결하지 않는 부분은, 그 도구 중 하나가 열린 웹에 접속했을 때 웹이 403으로 응답하면 어떻게 되는가 하는 문제다.

URL을 가져오거나, 페이지를 스크래핑하거나, 검색 결과에 접근하거나, 모델을 위해 실시간 데이터를 가져오는 MCP 서버를 만들거나 운영해 본 적이 있다면, 이미 이 벽을 마주쳤을 가능성이 높다. 서버는 노트북에서는 완벽하게 작동한다. 클라우드 서버에 배포하고 에이전트를 그쪽으로 연결하면, 갑자기 절반의 요청이 CAPTCHA 페이지, “접근 거부”, 또는 엉뚱한 국가의 사이트로의 지역 리디렉션으로 돌아온다. MCP 코드는 전혀 변경되지 않았다. IP가 변경되었을 뿐이다.

이 글은 그 간극에 관한 것이다. 왜 웹을 가져오는 MCP 서버가 차단당하는지, 왜 이것이 MCP가 해결할 문제가 아닌지, 그리고 레지덴셜 프록시 계층이 몇 줄의 코드로 어떻게 이 문제를 해결하는지 다룬다.

웹 접근이 일어나는 지점에 대한 간단한 복습

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 도구 뒤의 레지덴셜 프록시

패턴은 단순하다. MCP 서버의 도구가 데이터센터 IP에서 직접 요청을 만드는 대신, 레지덴셜 프록시 게이트웨이를 통해 요청을 만든다. 대상 사이트는 올바른 국가의 실제 소비자 IP를, 실제 가정 연결의 신뢰 프로필과 함께 보게 되고, 요청은 통과된다.

다음은 Shifter 게이트웨이를 통해 라우팅하는 fetch_url 도구를 가진 최소한의 Python MCP 서버다. MCP 뼈대는 표준 FastMCP이고, 중요한 부분은 HTTP 클라이언트의 proxies 설정이다.

import os
import httpx
from 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을 포함하여 스티키 세션을 추가하면, TTL의 수명 동안 같은 IP가 유지된다:

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를 옮겨 다니며 사이트의 모든 세션 확인을 걸리게 하는 대신, 안정적인 아이덴티티를 유지할 수 있다.

명심해야 할 것들

프록시 계층은 마법의 “절대 차단되지 않음” 스위치가 아니며, 그렇게 취급하는 것이 대역폭을 낭비하는 방법이다. 몇 가지 실용적인 참고 사항이 있다:

적극적으로 캐시하라. 에이전트는 같은 URL을 계속 다시 가져온다. fetch 도구 앞에 작은 캐시를 두면 프록시 대역폭(그리고 비용)을 크게 줄이고, 모델에게도 더 빠르다. 이미 답을 가지고 있는 요청을 프록시하지 마라.

국가를 하드코딩하지 말고 전달하라. 전체 가치는 호출별 지오 제어에 있다. 한 지역을 서버에 굽는 대신, 모델이 설정할 수 있는 도구 인자로 country를 만들되, 합리적인 기본값을 두라.

대상을 존중하라. 프록시는 요청이 어느 IP에서 오는지를 바꿀 뿐, 그 요청을 해야 하는지 여부를 바꾸지 않는다. 의미가 있는 곳에서는 robots.txt를 존중하고, 요청 속도를 적정하게 유지하고, 차단이 사라졌다고 사이트를 무차별적으로 두드리지 마라. 무엇이 허용되는지에 대한 공신력 있는 출처는 우리의 적정 사용 정책이다.

자격 증명을 도구 스키마 밖에 두라. 프록시 사용자명과 비밀번호는 서버의 환경 변수에 있어야 하며, 모델이 보는 도구 정의에 절대 넣지 마라. 모델은 국가를 선택해야지, 게이트웨이 비밀번호를 쥐고 있어서는 안 된다.

보호되지 않은, 지역 중립적인 가져오기에는 데이터센터로 충분하다. 도구가 자체 API나 차단하지 않고 지역화하지 않는 공개 엔드포인트만 호출한다면, 프록시가 필요 없다. 실제로 차단에 직면하거나 지오가 필요한 오픈 웹 가져오기에 레지덴셜 계층을 아껴 두라. (각 IP 유형이 언제 적합한지에 대한 더 자세한 비교는 레지덴셜 대 데이터센터 프록시를 참고하라.)

FAQ

MCP에 내장된 프록시 지원이 있는가? 없으며, 있어서도 안 된다. MCP는 모델과 도구 간 통신을 표준화하는 것이지, 도구가 인터넷에 어떻게 접근하는지를 표준화하는 것이 아니다. 나가는 웹 접근은 서버의 구현 세부사항이다. 위에서 보여준 것처럼 도구 내부의 HTTP 클라이언트 수준에서 프록시를 추가하면 된다.

그냥 레지덴셜 연결에서 MCP 서버를 실행하면 안 되는가? 그렇게 할 수는 있지만, 확장되지 않고, 요청별 지오 제어를 제공하지 않으며, 에이전트의 신뢰성을 하나의 가정 연결의 가동 시간과 단일 IP에 묶어버린다. 레지덴셜 프록시 게이트웨이는 레지덴셜 회선에 아무것도 호스팅하지 않고도 큰 로테이팅 풀과 호출별 국가 타겟팅을 제공한다.

프록시가 에이전트가 차단되는 것을 완전히 막아주는가? 가장 큰 원인(데이터센터 IP)을 제거하고 지오 제어를 제공하지만, 행동은 여전히 중요하다. 급증하는 요청 패턴, 누락되거나 일관되지 않은 헤더, 속도 제한 무시는 여전히 플래그를 유발할 수 있다. 프록시는 필요하지만 충분하지는 않다. 합리적인 요청 행동과 짝을 이루어야 한다.

이것이 TypeScript MCP 서버에서도 작동하는가? 그렇다. 개념은 동일하다. HTTP 클라이언트(에이전트가 있는 fetch, axios, undici, got)를 게이트웨이를 통해 라우팅하도록 설정하면 된다. 프록시 URL 형식과 요청별 타겟팅은 언어에 관계없이 동일하다.

모델이 요청마다 국가를 선택할 수 있는가? 그렇다, 그것이 권장되는 패턴이다. country(그리고 선택적으로 주/도시)를 기본값이 있는 도구 인자로 노출하라. 모델은 작업에 따라 이를 설정하고, 서버는 이를 즉시 프록시 사용자명에 인코딩한다.

프록시를 통한 라우팅이 Shifter의 청구 방식을 바꾸는가? 아니다. 일반적인 GB당 가격의 표준 레지덴셜 게이트웨이일 뿐이다. MCP 서버는 같은 엔드포인트에 연결하는 또 다른 클라이언트일 뿐이다. 대역폭은 대역폭이다.

결론

MCP는 “모델이 어떻게 도구를 호출하는가”에 대한 깔끔한 답이다. “그 도구가 잘못된 국가의 플래그된 데이터센터 IP에서 어떻게 적대적인 오픈 웹에 접근하는가”라는 질문에 답하도록 만들어진 적은 없다. 그 두 번째 질문은 실재하며, 직접 답해야 하고, 그 답은 fetch 도구 뒤의 얇은 레지덴셜 프록시 계층이다.

배선은 몇 줄이다. 그 대가는 웹 도구가 실제로 프로덕션에서 작동하고, 올바른 국가의 데이터를 반환하며, 에이전트가 보호된 사이트로 처음 향할 때 무너지지 않는 MCP 서버다. 웹에 연결된 에이전트를 만들고 있다면, 레지덴셜 게이트웨이로 시작해서 첫날부터 도구 뒤에 두어라. 차단이 시작된 후에 나중에 개조하는 것보다 훨씬 쉽다. 에이전트에게 신뢰할 수 있는 웹 접근을 제공하는 것에 대한 더 자세한 내용은 웹을 탐색하는 AI 에이전트를 위한 최고의 프록시를 참고하라.

시작할 준비가 되셨나요?

205M개 이상의 IP, 195개 이상의 국가를 지원하는 Shifter의 레지덴셜 프록시를 $0.75/GB부터 이용해보세요.

시작하기