MCP 터널
MCP 터널 (MCP tunnels)
MCP 터널을 사용하면 사설 네트워크 안에서 실행되는 Model Context Protocol(MCP) 서버에 Claude를 연결할 수 있어요. 트래픽은 아웃바운드 전용 연결로 흐르기 때문에 인바운드 방화벽 포트를 열거나 서비스를 공용 인터넷에 노출하거나 오리진에서 Anthropic의 IP 범위를 허용 목록에 넣을 필요가 없어요.
출처: 문서
본문
MCP 터널을 사용하면 사설 네트워크 안에서 실행되는 Model Context Protocol(MCP) 서버에 Claude를 연결할 수 있어요. 트래픽은 아웃바운드 전용 연결로 흐르므로 인바운드 방화벽 포트를 열거나 서비스를 공용 인터넷에 노출하거나 오리진에서 Anthropic의 IP 범위를 허용 목록에 넣을 필요가 없어요.
참고 (Note) MCP 터널은 연구 프리뷰 상태예요. 사용해보려면 액세스를 요청하세요. 업타임·지원·지속성에 대한 약속 없이 "있는 그대로" 제공되며, 기본 전송에 대한 가용성 약속을 하지 않는 제3자 네트워크 제공자(Cloudflare)에 의존해요. Anthropic은 언제든 MCP 터널을 수정하거나 중단할 수 있어요.
Zero Data Retention과 HIPAA BAA 자격에 대해서는 API 및 데이터 보존을 참고하세요.
작동 방식 (How it works)
터널 스택은 네트워크 안에서 실행되는 두 구성 요소예요:
- cloudflared: Cloudflare의 오픈 소스 터널 커넥터. 터널 엣지로 아웃바운드 전용 연결을 시작하고 Anthropic에서 프록시로 가는 암호화된 트래픽을 운반해요.
- 프록시 (Proxy): Anthropic의 라우팅 구성 요소. 내부 TLS를 종료하고, 업스트림 IP가 허용 범위 안에 드는지 검증하며, 호스트 이름에 따라 각 요청을 올바른 업스트림 MCP 서버로 라우팅해요.
노출하는 각 MCP 서버는 터널 도메인 아래의 호스트 이름(예: docs.<your-tunnel-domain>)을 받아요. 이 호스트 이름을 Claude Console의 Managed Agent 세션에 연결하거나, MCP 커넥터를 통해 Messages API에 전달할 수 있어요.
사전 준비 (Prerequisites)
배포하기 전에 다음이 있는지 확인하세요:
- 배포 대상: Kubernetes 클러스터, 또는 Docker와 Docker Compose가 있는 VM.
- 터널. Claude Console( 터널 만들기 참고) 또는 API로 만드세요. Helm 차트의 설정 훅이 설치 중에 터널을 만들어줄 수도 있어요.
- 스택이 Tunnels API에 인증하는 방법. 하나를 선택하세요:
- 프로그래매틱 액세스 (권장). 터널을 만들 때 Workload Identity Federation을 설정하세요. 스택이 ID 공급자에서 단기 API 토큰을 만들어 터널 토큰을 가져오고 CA 인증서를 자동으로 생성·등록해요. 페더레이션 규칙을 관리할 권한, 등록된 OIDC 발급자,
workspace:manage_tunnels스코프가 있는 페더레이션 규칙이 필요해요. - 수동. 정적 자격 증명을 직접 제공해요. Console의 터널 토큰과 여러분이 등록한 CA가 서명한 서버 인증서. 연결 세부 정보 가져오기와 CA 인증서 추가를 참고하세요.
- 프로그래매틱 액세스 (권장). 터널을 만들 때 Workload Identity Federation을 설정하세요. 스택이 ID 공급자에서 단기 API 토큰을 만들어 터널 토큰을 가져오고 CA 인증서를 자동으로 생성·등록해요. 페더레이션 규칙을 관리할 권한, 등록된 OIDC 발급자,
- 사설 네트워크에서 실행되는 하나 이상의 MCP 서버. 예시는 원격 MCP 서버를 참고하세요.
- 네트워크 요구 사항에 나열된 대로의 아웃바운드 연결.
네트워크 요구 사항 (Network requirements)
| 구성 요소 | 대상 | 포트 / 프로토콜 | 사용 시기 |
|---|---|---|---|
| 설정 구성 요소 | api.anthropic.com |
443 TCP | 프로비저닝 및 토큰 순환 |
| cloudflared | 터널 엣지 (198.41.192.0/19, 2606:4700:a0::/44) |
7844 TCP 및 UDP | 런타임 |
| 프록시 | 업스트림 MCP 서버 | 설정에 따름 | 런타임 |
보안 모델 (Security model)
보안 계층 (Security layers)
세 개의 독립적인 계층이 모든 요청을 보호해요:
| 계층 | 보호 대상 |
|---|---|
| Anthropic과 전송 공급자 사이의 외부 mTLS, IP 검증 포함 | 터널에 도달하는 무단 클라이언트 |
| Anthropic 백엔드에서 프록시까지의 내부 TLS | 전송 공급자 또는 어떤 네트워크 중간자의 페이로드 검사 |
| 각 MCP 서버의 OAuth | 인증된 터널 트래픽에 의한 MCP 도구의 무단 사용 |
터널 전송은 Cloudflare 네트워크에서 실행돼요. 프록시가 여러분만 보유한 인증서로 내부 TLS를 종료하기 때문에 Cloudflare는 요청·응답 페이로드를 읽을 수 없어요. Anthropic은 CA 인증서가 등록된 후에만 터널에 연결하므로 페이로드는 Cloudflare 네트워크를 지날 때 항상 암호화돼요. Cloudflare는 연결 메타데이터를 받긴 하는데, 전송 공급자가 관찰할 수 있는 것을 참고하세요.
공동 책임 모델 (Shared responsibility model)
| Anthropic이 처리 | 여러분의 조직이 처리 |
|---|---|
| 터널 접근 제어 | 터널을 통과하는 모든 콘텐츠와 트래픽, 그리고 적용 가능한 제3자 허용 사용 정책(Cloudflare 포함) 준수 |
| 프록시에 연결하기 전에 CA 인증서 검증 | 이 페이지들의 배포 지침 준수 |
| Claude가 조직이 소유한 터널에만 요청을 보내도록 보장 | 터널 토큰과 TLS 개인 키 보안 |
| 서버 인증서 관리 및 만료 전 갱신 | |
| 각 MCP 서버에 OAuth 구성 | |
| 프록시와 MCP 서버의 네트워크 접근 제한 | |
| 침해가 의심되면 Anthropic에 알림 |
경고 (Warning) 공격자가 터널 토큰 과 TLS 개인 키 중 하나를 얻으면 여러분의 프록시를 사칭해서 MCP 요청 페이로드를 읽을 수 있어요. 두 가지 모두 고가치 비밀로 취급하세요. 강화 지침은 MCP 터널 보안을 참고해요.
전송 공급자가 관찰할 수 있는 것 (What the transport provider can observe)
Cloudflare는 아웃바운드 전송을 제공해요. MCP 요청·응답 페이로드는 읽을 수 없지만, 다음 연결 메타데이터는 받아요:
- cloudflared를 실행하는 호스트의 이그레스 IP 주소
- cloudflared 호스트 지문
- 연결 타이밍과 바이트 양
- 터널에 할당된
*.tunnel.anthropic.com하위 도메인
Anthropic과 Cloudflare의 계약은 이 원격 측정(telemetry)의 Cloudflare 사용을 제한해요. Cloudflare는 이 연구 프리뷰의 하위 처리자로 작동해요.
터널 배포하기 (Deploy a tunnel)
MCP 터널이 처음이라면 프로덕션 배포를 구성하기 전에 퀵스타트부터 시작해서 로컬에서 동작하는 터널을 만들어보세요.
- 퀵스타트 (Quickstart) — 샘플 MCP 서버가 있는 Docker Compose로 동작하는 터널까지의 가장 빠른 경로
- Helm으로 배포 (Deploy with Helm) — Anthropic Helm 차트로 Kubernetes 클러스터에 설치
- Docker Compose로 배포 (Deploy with Docker Compose) — Docker Compose로 VM에 설치
둘 중 선택하기:
- 배포 대상
- Helm: Kubernetes에 배포할 때.
- Docker Compose: 단일 호스트 또는 로컬 테스트용.
- 설정 인증
- 프로그래매틱 액세스 (Workload Identity Federation 경유): Kubernetes 클러스터, 클라우드 IAM, SPIFFE 같은 OIDC ID 공급자가 있을 때.
- 수동 자격 증명: 그런 것이 없거나 테스트 중일 때.
터널링된 MCP 서버 사용하기 (Use the tunneled MCP servers)
터널이 활성 상태(활성 CA 인증서가 있고 터널 스택이 연결됨)가 되면 업스트림 MCP 서버는 Claude Managed Agents와 Messages API에서 접근할 수 있어요.
참고 (Note) Console을 통해 만든 MCP 터널은 claude.ai의 커넥터로는 사용할 수 없어요.
두 경우 모두 터널은 MCP 서버로 암호화된 트래픽을 운반하지만 서버에 인증하지는 않아요. 업스트림 MCP 서버가 자체 인증(OAuth, 베어러 토큰)을 요구하면 다른 MCP 서버와 같은 방식으로 제공하세요. 터널과는 독립적이에요.
Managed Agents (Console)
- Managed Agents > Sessions에서 세션을 만들고 Create new agent를 선택해 MCP 서버 목록을 편집할 수 있게 해요.
- + MCP Server를 클릭하고 드롭다운을 열어요. 세션의 워크스페이스에 있는, 활성 인증서가 하나 이상 있는 터널이 공용 커넥터 카탈로그보다 목록 상단에 나타나요.
- 터널을 선택하고 프록시가 특정 MCP 서버로 라우팅하는 Subdomain과 업스트림 MCP 서버가 기대하는 Path를 제공해요. Resolves to 줄이 정확한 URL을 보여줘요.
Messages API
업스트림 MCP 서버의 URL을 다른 원격 MCP 서버와 같은 방식으로 mcp_servers 배열에 전달해요. 요청 본문과 anthropic-beta 헤더는 표준 MCP 커넥터 형식을 따르고, url만 터널 특유해요. 다음 예시는 Tunnels API가 사용하는 mcp-tunnels 베타와는 별개인 MCP 커넥터의 mcp-client 베타 헤더를 사용해요. 터널이 생성된 워크스페이스에서 그 워크스페이스용 API 키를 사용하거나, 키가 여러 워크스페이스에 접근할 수 있다면 anthropic-workspace-id 헤더를 그 워크스페이스로 설정해서 요청을 보내세요.
URL의 호스트는 <subdomain>.<your-tunnel-domain>이에요. 경로는 터널이 아니라 업스트림 MCP 서버에 달려 있어요. FastMCP의 streamable-http 전송은 /mcp에서 서빙하고, 다른 서버는 /나 커스텀 경로를 쓸 수 있어요 (서버 문서를 확인하세요). 프록시는 경로를 그대로 전달해요.
ant beta:messages create --beta mcp-client-2025-11-20 <<'YAML'
model: claude-opus-5-5
max_tokens: 1000
messages:
- role: user
content: Use the hello tool to greet tunnel.
mcp_servers:
- type: url
url: https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp
name: echo
tools:
- type: mcp_toolset
mcp_server_name: echo
YAML
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1000,
messages=[{"role": "user", "content": "Use the hello tool to greet tunnel."}],
mcp_servers=[
{
"type": "url",
"url": "https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp",
"name": "echo",
}
],
tools=[{"type": "mcp_toolset", "mcp_server_name": "echo"}],
betas=["mcp-client-2025-11-20"],
)
print(response)
const anthropic = new Anthropic();
const response = await anthropic.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 1000,
messages: [
{
role: "user",
content: "Use the hello tool to greet tunnel."
}
],
mcp_servers: [
{
type: "url",
url: "https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp",
name: "echo"
}
],
tools: [
{
type: "mcp_toolset",
mcp_server_name: "echo"
}
],
betas: ["mcp-client-2025-11-20"]
});
console.log(response);
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Messages::Model.ClaudeOpus5_5,
MaxTokens = 1000,
Messages = new List<BetaMessageParam>
{
new() { Role = Role.User, Content = "Use the hello tool to greet tunnel." }
},
McpServers = new List<BetaRequestMcpServerUrlDefinition>
{
new()
{
Url = "https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp",
Name = "echo"
}
},
Tools = new List<BetaToolUnion>
{
new BetaMcpToolset("echo")
},
Betas = ["mcp-client-2025-11-20"]
};
var message = await client.Beta.Messages.Create(parameters);
Console.WriteLine(message);
client := anthropic.NewClient()
response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1000,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Use the hello tool to greet tunnel.")),
},
MCPServers: []anthropic.BetaRequestMCPServerURLDefinitionParam{
{
URL: "https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp",
Name: "echo",
},
},
Tools: []anthropic.BetaToolUnionParam{
{OfMCPToolset: &anthropic.BetaMCPToolsetParam{
MCPServerName: "echo",
}},
},
Betas: []anthropic.AnthropicBeta{
anthropic.AnthropicBetaMCPClient2025_11_20,
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
import com.anthropic.models.beta.messages.BetaMcpToolset;
// ...
import com.anthropic.models.beta.messages.BetaRequestMcpServerUrlDefinition;
// ...
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1000L)
.addUserMessage("Use the hello tool to greet tunnel.")
.addMcpServer(BetaRequestMcpServerUrlDefinition.builder()
.url("https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp")
.name("echo")
.build())
.addTool(BetaMcpToolset.builder()
.mcpServerName("echo")
.build())
.addBeta("mcp-client-2025-11-20")
.build();
BetaMessage response = client.beta().messages().create(params);
IO.println(response);
}
$client = new Client();
$message = $client->beta->messages->create(
maxTokens: 1000,
messages: [
['role' => 'user', 'content' => 'Use the hello tool to greet tunnel.']
],
model: 'claude-opus-5-5',
mcpServers: [
[
'type' => 'url',
'url' => 'https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp',
'name' => 'echo',
],
],
tools: [
[
'type' => 'mcp_toolset',
'mcpServerName' => 'echo',
],
],
betas: ['mcp-client-2025-11-20'],
);
echo $message;
client = Anthropic::Client.new
response = client.beta.messages.create(
model: "claude-opus-5-5",
max_tokens: 1000,
messages: [
{ role: "user", content: "Use the hello tool to greet tunnel." }
],
mcp_servers: [
{
type: "url",
url: "https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp",
name: "echo"
}
],
tools: [
{
type: "mcp_toolset",
mcp_server_name: "echo"
}
],
betas: ["mcp-client-2025-11-20"]
)
puts response
업스트림 MCP 서버에 인증(authorization_token)하는 방법과 기타 mcp_servers 옵션은 MCP 커넥터를 참고하세요.
더 알아보기 (Learn more)
- 보안 (Security) — 강화 지침, 자격 증명 순환, 침해 대응
- 문제 해결 (Troubleshooting) — 연결, TLS, 라우팅 문제 진단
- 레퍼런스 (Reference) — 프록시 설정 필드, Tunnels API, 인증서 요구 사항, 설정 구성 요소
- MCP 커넥터 (MCP connector) — Messages API에서 터널링된 서버 사용하기