agentgateway로 MCP 서버와 LLM 트래픽 한곳에서 관리하기

AI 에이전트를 하나 만들어서 노트북에서 돌릴 때는 세상이 참 단순합니다. MCP(Model Context Protocol) 서버 두어 개를 설정 파일에 적어두고, OpenAI 키 하나를 환경 변수에 넣으면 끝이거든요.
그런데 이걸 팀 전체가 쓰게 만들려는 순간 질문이 쏟아지기 시작합니다. 사내 MCP 서버가 다섯 개로 늘어났는데 에이전트마다 다섯 개를 전부 적어줘야 하나요? 누가 실수로 배포 도구를 호출하는 건 어떻게 막죠? 지난달 LLM 요금이 왜 세 배가 됐는지, 누가 어떤 도구를 불렀는지 물어보면 답할 자료가 있나요? 😅
전통적인 웹 서비스였다면 이런 질문은 이미 답이 나와 있습니다. 앞단에 리버스 프록시를 두고 인증과 로깅, 속도 제한을 거기서 처리하면 되니까요. 그럼 에이전트 트래픽에도 같은 자리를 만들어주면 되지 않을까요? 그 자리를 노리고 나온 오픈소스 프로젝트가 바로 agentgateway입니다.
agentgateway란
agentgateway는 Rust로 작성된 데이터 플레인(data plane)입니다. 에이전트와 바깥세상 사이에 끼어 앉아서 MCP 서버 호출, LLM 제공업체 호출, A2A(Agent-to-Agent) 통신, 그리고 평범한 HTTP와 gRPC 트래픽까지 한 프로세스에서 받아냅니다.
원래 Solo.io가 만들었고 2025년 8월에 리눅스 재단에 기부되었는데, 2026년 6월에는 Agentic AI Foundation(AAIF)에 합류해 지금은 그 산하에서 관리되고 있습니다. 2026년 3월에 1.0.0이 나왔고 그 뒤로 매달 릴리스를 내고 있는데, 7월 말에 나온 1.4에서는 MCP 2026-07-28 스펙까지 따라잡았어요. Microsoft, AWS, Cisco, Adobe, Salesforce 같은 회사가 함께 참여하고 있습니다. 2026년 9월에는 Envoy 위에 컨트롤 플레인을 얹은 Agent Router도 같은 재단에 합류했는데, 두 프로젝트를 나란히 놓고 비교한 내용은 그 글에서 다뤘습니다.
이 프로젝트가 스스로를 설명하는 방식이 재미있는데요. 새로운 프로토콜을 하나 더 만들겠다는 게 아니라, 이미 있는 게이트웨이가 하던 일을 에이전트 트래픽에도 그대로 해주겠다는 쪽입니다. 그래서 기능 목록도 라우팅, 보안, 관측, 거버넌스, 비용처럼 인프라 담당자에게 익숙한 단어로 나뉘어 있습니다.
flowchart TB
accTitle: agentgateway가 에이전트와 백엔드 사이에 놓이는 구조
accDescr: 여러 AI 에이전트가 agentgateway 하나에 연결되고, agentgateway는 MCP 서버와 LLM 제공업체, 다른 A2A 에이전트로 요청을 나누어 보낸다. 게이트웨이 안에서 인증, 인가, 관측, 비용 추적이 함께 처리된다.
subgraph clients["클라이언트"]
direction LR
a1["코딩 에이전트"]
a2["사내 챗봇"]
a3["자동화 워크플로"]
end
clients --> gw
gw["agentgateway<br/>인증, 인가, 관측, 비용"]
subgraph backends["백엔드"]
direction LR
m["MCP 서버"]
l["LLM 제공업체"]
p["A2A 에이전트"]
end
gw --> backends
classDef gateway stroke-width:2px
class gw gateway
설치하고 띄워보기
설치는 스크립트 한 줄입니다.
curl -sL https://agentgateway.dev/install | bash
특정 버전을 고정하고 싶다면 인자를 넘겨줍니다.
curl -sL https://agentgateway.dev/install | bash -s -- --version v1.5.0
설정 파일 없이 그냥 실행하면 ~/.config/agentgateway/config.yaml에 기본 설정을 만들어두고 뜹니다.
agentgateway
이제 브라우저에서 http://localhost:4000/ui로 들어가면 관리 화면이 나옵니다. 여기서 MCP 서버를 등록하고, 등록한 도구를 플레이그라운드에서 직접 호출해보고, LLM 모델을 추가할 수 있어요. 설정 파일을 손으로 짜기 전에 UI로 감을 잡아보는 편이 빠릅니다.
프록시 자체도 같은 4000번 포트를 씁니다. 그래서 LLM 요청은 http://localhost:4000/v1/chat/completions로 보내면 됩니다. OpenAI 호환 경로라 기존 클라이언트의 베이스 URL만 바꿔주면 그대로 붙어요.
설정 파일의 뼈대
UI로 만져본 설정은 결국 YAML 한 장으로 떨어집니다. 최상위 키가 몇 개 없어서 구조는 금방 익숙해집니다.
gateways는 트래픽이 들어오는 입구, 그러니까 포트와 리스너(listener)를 정의합니다. routes는 들어온 요청을 어떤 조건으로 매칭해서 어느 백엔드로 넘길지 정하고요. 여기에 LLM 전용 축약형인 llm, MCP 전용 축약형인 mcp, 관리 화면을 켜는 ui가 더해집니다.
가장 작은 설정은 이렇게 생겼습니다.
gateways:
default:
port: 3000
routes:
- backends:
- host: localhost:8000
3000번 포트로 들어온 HTTP 요청을 localhost:8000으로 넘기는, 그냥 평범한 리버스 프록시죠. 여기서 백엔드 자리에 MCP 서버나 LLM 제공업체를 넣기 시작하면 성격이 달라집니다.
에디터에서 자동 완성을 받고 싶다면 파일 맨 위에 스키마 주석을 달아두세요. 최상위 키가 꽤 많고 정책 이름도 길어서 이게 있으면 훨씬 수월합니다.
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
여러 MCP 서버를 하나로 묶기
agentgateway를 쓰는 가장 직관적인 이유는 다중화(multiplexing)입니다. 여러 MCP 서버를 게이트웨이 뒤에 등록해두면, 클라이언트 입장에서는 MCP 서버가 딱 하나만 있는 것처럼 보입니다.
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
gateways:
default:
port: 3000
mcp:
targets:
- name: everything
stdio:
cmd: npx
args:
- "@modelcontextprotocol/server-everything"
- name: docs
mcp:
host: https://docs.example.com/mcp
ui: {}
로컬에서 프로세스로 띄우는 stdio 서버와 원격 HTTP 서버를 섞어 등록했습니다. 클라이언트가 tools/list를 호출하면 두 서버의 도구가 한 목록으로 합쳐져 돌아와요.
이름이 겹치면 곤란하겠죠. 그래서 대상이 둘 이상이면 도구 이름 앞에 대상 이름이 자동으로 붙습니다. 위 설정이라면 everything_echo처럼 나오는 식이죠. 이 동작이 마음에 들지 않으면 prefixMode 필드로 바꿀 수 있습니다.
이게 왜 편하냐면, 서버를 추가하거나 교체할 때 손댈 곳이 게이트웨이 설정 한 군데로 줄어들기 때문입니다. 팀원 스무 명의 Claude Code 설정 파일을 한 명씩 붙잡고 고치는 대신, 게이트웨이 주소 하나만 공유해두면 되니까요. 사내에 쓸 만한 서버가 늘어날 때마다 MCP Registry에서 찾아 게이트웨이에만 등록해주면 됩니다.
LLM 제공업체 앞에 두기
모델 호출도 같은 파일에서 다룹니다. llm.models에 제공업체와 키를 적어두면 그때부터 게이트웨이가 OpenAI 호환 엔드포인트 노릇을 합니다.
llm:
models:
- name: "*"
provider: openAI
params:
apiKey: "$OPENAI_API_KEY"
name이 *이니까 모델 이름을 가리지 않고 전부 OpenAI로 보내겠다는 뜻입니다. 이제 애플리케이션 코드는 게이트웨이만 바라보면 됩니다.
curl http://localhost:4000/v1/chat/completions \
-H 'content-type: application/json' \
-d '{
"model": "claude-haiku-4-5",
"messages": [
{ "role": "user", "content": "안녕하세요" }
]
}'
일급으로 지원하는 제공업체는 OpenAI, Anthropic, Amazon Bedrock, Azure, Gemini, Vertex AI, Ollama, Groq, Mistral AI, DeepSeek, Together AI, OpenRouter, xAI 등으로 꽤 넓습니다. 목록에 없는 곳은 커스텀 제공업체로 붙일 수 있고요.
클라우드 제공업체는 API 키 대신 각자의 인증 체계를 쓰는데, 이것도 설정으로 흡수합니다.
llm:
models:
- name: "*"
provider: vertex
auth:
gcp:
type: accessToken
- name: "*"
provider: bedrock
auth:
aws: {}
여기까지만 보면 Cloudflare AI Gateway와 겹치는 부분이 많습니다. 실제로 캐싱과 폴백(fallback), 비용 추적 같은 목적은 비슷해요. 차이는 운영 방식과 범위입니다. Cloudflare 쪽은 관리형 서비스라 설정만 하면 되지만 Cloudflare 위에서만 돌아갑니다. agentgateway는 직접 띄워야 하는 대신 어디서든 돌릴 수 있고, MCP와 A2A까지 같은 데이터 플레인에서 처리하고요. 그래서 사내망에 있는 MCP 서버까지 함께 다스려야 하는 상황이라면 후자가 맞아떨어지죠.
도구 단위로 권한 걸기
개인적으로 agentgateway에서 가장 눈여겨본 기능은 인가(authorization)입니다. MCP 서버 자체는 대개 인증까지만 신경 쓰고, 어떤 사용자가 어떤 도구를 부를 수 있는지는 서버 구현에 맡겨져 있거든요. 게이트웨이가 이 판단을 대신 해주면 서버 코드를 건드리지 않고도 정책을 바꿀 수 있습니다.
mcpAuthorization 정책은 CEL(Common Expression Language) 표현식으로 규칙을 적습니다.
gateways:
default:
port: 3000
routes:
- policies:
mcpAuthorization:
rules:
- 'mcp.tool.name == "echo"'
- 'jwt.sub == "test-user" && mcp.tool.name == "add"'
- 'mcp.tool.name == "printEnv" && jwt.nested.key == "value"'
backends:
- mcp:
targets:
- name: everything
stdio:
cmd: npx
args: ["@modelcontextprotocol/server-everything"]
echo는 누구나 부를 수 있고, add는 특정 주체(subject)만, printEnv는 중첩된 클레임(claim)까지 맞아야 통과합니다. JWT는 앞단의 jwtAuth 정책이 검증해서 클레임을 넘겨주는 구조라, MCP 인증 스펙에서 다루는 OAuth 흐름과 자연스럽게 이어집니다.
규칙에서 쓸 수 있는 변수는 요청 시점과 사후 시점이 나뉩니다. 인가 판단에는 호출되는 도구 이름인 mcp.tool.name, 어느 대상이 처리하는지 알려주는 mcp.tool.target, 그리고 jwt.sub를 비롯한 JWT 클레임을 씁니다. 클레임이 있는지 확인하려면 has(jwt.roles)처럼 적고요.
주의할 점이 하나 있는데, 도구 인자는 인가 시점에 볼 수 없습니다. mcp.tool.arguments나 mcp.tool.result 같은 값은 액세스 로그에서만 접근할 수 있어요. 인자 값에 따라 막고 싶다면 인가 규칙이 아니라 별도의 가드레일 기능을 써야 합니다.
mcp.tool.target은 앞서 본 다중화와 붙었을 때 특히 쓸모가 있습니다. 서버 단위로 접근 권한을 갈라놓을 수 있거든요.
mcpAuthorization:
rules:
- allow: 'mcp.tool.target == "docs"'
- allow: |
mcp.tool.target == "operations" &&
has(jwt.roles) &&
"sre" in jwt.roles
문서 서버는 모두에게 열어두고, 운영 서버는 SRE 역할을 가진 사람에게만 여는 식이죠. 이런 규칙을 서버마다 따로 구현하려면 꽤 번거로운데, 게이트웨이에 모아두면 정책을 한눈에 검토할 수 있습니다.
어디에 배포할까
agentgateway는 단일 바이너리로도, 도커 컨테이너로도, 쿠버네티스 위에서도 돌아갑니다. 개인 노트북에서 MCP 서버를 정리하는 용도라면 바이너리 하나로 충분하고, 팀 단위로 공유하려면 컨테이너로 띄워두는 편이 관리하기 낫고요.
관측(observability)은 OpenTelemetry로 나갑니다. Prometheus로 지표를 긁고 Jaeger로 추적을 보는 익숙한 조합이 그대로 통해요. 여기에 비용 기능이 붙어서 어떤 팀이 얼마를 썼는지 귀속시키고, 가상 키(virtual key)로 예산 한도를 걸 수도 있습니다. 사내에서 모델 요금을 나눠 정산해야 한다면 이 부분이 도입 근거가 되겠네요.
다만 아직 어린 프로젝트라는 점은 감안하는 게 좋습니다. 1.0.0이 2026년 3월에 나왔고 매달 기능이 붙는 중이라, 설정 스키마도 조금씩 움직입니다. 예전 문서에 나오는 binds 키는 이미 gateways로 대체되었어요. 문서를 볼 때 버전이 맞는지 한 번씩 확인해주세요.
마치며
지금까지 agentgateway가 무엇이고 어떻게 설정하는지 살펴봤습니다. 여러 MCP 서버를 하나로 묶고, LLM 제공업체를 한 엔드포인트 뒤로 감추고, 도구 단위로 권한을 거는 세 가지가 핵심이었는데요. 결국 웹 서비스에서 리버스 프록시가 맡던 자리를 에이전트 트래픽에도 만들어주는 셈입니다.
에이전트를 혼자 쓰는 동안에는 이런 게 없어도 아무 불편이 없습니다. 그런데 쓰는 사람이 늘고 붙는 서버가 늘어나는 순간, 설정이 사람 수만큼 흩어지고 누가 무엇을 호출했는지 알 길이 없어집니다. 그 시점이 오기 전에 앞단에 게이트웨이 한 겹을 마련해두면 나중에 훨씬 수월해요. 우선 노트북에서 바이너리 하나 띄워놓고 평소 쓰던 MCP 서버 두어 개를 등록해보시면 감이 옵니다.
게이트웨이에 붙이기 전에 MCP 서버 자체가 제대로 도는지 확인하고 싶다면 MCP Inspector로 디버깅하기가 도움이 될 거예요.
더 자세한 내용은 agentgateway 공식 문서를 참고하세요.
This work is licensed under CC BY 4.0