DOCS · USER GUIDE
에이전트에 도구 연결하기 — MCP 게이트웨이
격리된 에이전트에 외부 도구를 안전하게 연결하는 실습 가이드이다. 호스트의 MCP 게이트웨이로 여러 백엔드 도구 서버를 하나의 카탈로그로 통합하고 자격증명은 호스트에만 보관하면서 프로파일별로 노출 범위를 제어한다.
최종 수정 · 2026년 7월 28일
1 · 개요
격리된 VM 내부의 에이전트가 웹 검색, 코드 저장소, 데이터베이스 같은 외부 도구를 사용하려면 해당 도구의 자격증명이 필요하다. 하지만 API 키를 VM 내부에 두는 순간 제로트러스트 보안은 불가능해진다. 에이전트가 탈취되면 중요한 보안 키도 함께 유출되기 때문이다. MCP 게이트웨이는 바로 이 보안 문제를 해결하는 핵심 역할을 한다.
게이트웨이는 호스트에서 실행되는 단일 MCP 서버이다. 여러 백엔드 MCP 서버를 하나로 통합해 VM 내부의 에이전트에는 네임스페이스가 적용되고 프로파일별로 필터링된 단일 도구 카탈로그만 노출한다. 백엔드 자격증명은 호스트에만 남으며 어떤 경우에도 VM 내부로 주입되지 않게 관리한다.
동작 구조 한눈에 보기
구조는 세 계층으로 이루어진다. 맨 위에는 각각 격리된 VM 내부의 에이전트가, 가운데에는 호스트의 게이트웨이가, 맨 아래에는 실제 도구를 제공하는 백엔드 서버가 있다. 에이전트는 오직 게이트웨이와만 통신하며 자격증명을 가진 백엔드에는 직접 접근할 수 없다.
격리된 게스트 — 소스 IP로 식별
POST http://vantisso-gw:3001/mcp브리지 전용Bearerstdio게이트웨이는 로그인 토큰을 사용하지 않는다. 호출자의 신원은 요청의 소스 IP이다. 게이트웨이는 소스 IP로 어떤 VM에서 실행되는 에이전트인지, 그리고 그 VM이 어떤 프로파일로 실행 중인지 확인한 뒤 해당 프로파일의 정책에 따라 무엇을 노출할지 결정한다.
이 가이드에서 다루는 것
- 게이트웨이 활성화 및 실행 — 디폴트로 게이트웨이는 비활성화되어 있기 때문에 명시적으로 실행시켜야 한다.
- 공개 HTTP 백엔드 연동 — 인증이 필요 없는 서버를 연결해 동작을 바로 확인한다.
- 자격증명 관리 — 인증이 필요한 백엔드를 연결하되 키는 호스트에만 보관한다.
- 로컬 stdio 백엔드 연동 — 게이트웨이가 호스트에서 MCP 서버 프로세스를 직접 실행한다.
- 프로파일별 도구 제한 — 역할에 따라 어떤 도구를 노출할지 결정하고 조정하는 방법을 다룬다.
- 신원·감사·호출량 제한 — 누가 무엇을 호출했는지 기록하고 남용을 막는 방법을 살펴본다.
- 운영·트러블슈팅 — 변경 사항의 반영 시점과 점검 순서를 살펴본다.
게이트웨이 구성 설정 관련 유의사항
게이트웨이는 별도의 관리 화면 없이 호스트에 있는 설정 파일 두 개(servers.yaml·secrets.yaml)를 편집하고 컨트롤 플레인 API를 호출해 구성한다. 따라서 백엔드 등록과 자격증명 관리는 호스트에 직접 접근할 수 있는 관리자(operator)가 하는 작업이다. 본 가이드에서는 한 사람이 로컬에서 다루는 가장 단순한 환경, 즉 API 인증을 사용하지 않는 상태를 가정하므로 앞으로 나오는 모든 예시의 curl 명령은 별도의 인증 없이 그대로 실행된다. 그러나 API 인증을 적용하는 실운영 환경이라면 이 구성 API를 호출할 때도 앞서 말한 관리자 권한으로 인증해야 한다는 점에 유의한다.
2 · 게이트웨이 활성화 및 실행
먼저 호스트에 Vantisso가 설치되어 실행 중이어야 하며 프로파일이 하나 이상 있어야 한다. 여기서 프로파일은 에이전트가 실행될 때 적용되는 설정 묶음(프로바이더·모델·자원 할당·도구·시스템 프롬프트)으로 체험판에는 default 프로파일이 기본으로 포함되어 있어 이 조건은 별도 준비 없이 충족된다. 아직 설치하지 않았다면 시작하기(Getting Started) 가이드를 따라 설치를 완료한다.
게이트웨이는 두 개의 설정 파일을 읽는다. configs/mcp/servers.yaml은 백엔드 서버 목록이고 configs/mcp/secrets.yaml은 그 서버들의 자격증명이다. 아직 백엔드가 없으므로 비어 있는 서버 목록으로 시작한다.
# /opt/vantisso/configs/mcp/servers.yaml — backends (added in later steps)
servers: []# /opt/vantisso/configs/mcp/secrets.yaml (mode 0600) — credentials, host-only
# key: token (filled in step 4). Both files are gitignored — never commit them.게이트웨이는 환경 변수 VANTISSO_MCP_ENABLED로 활성화한다. 이 설정은 데몬이 시작할 때 한 번만 적용되기 때문에 값을 변경하면 데몬을 (재)시작해야 반영된다.
# systemd service: add a drop-in, then restart.
# under [Service]: Environment=VANTISSO_MCP_ENABLED=1
sudo systemctl edit vantisso
sudo systemctl restart vantisso
# Or, when running the daemon directly:
VANTISSO_MCP_ENABLED=1 sudo ./vantisso-daemon활성화되었는지는 컨트롤 플레인 API로 확인한다. 백엔드가 아직 없으므로 server_count는 0이다.
curl -s http://localhost:3000/config/mcp
# → {"enabled":true,"endpoint":"http://vantisso-gw:3001/mcp","server_count":0}두 주소를 혼동하지 말 것
http://vantisso-gw:3001/mcp는 VM 내부에서만 접근 가능한 게이트웨이 주소이다.(브리지 게이트웨이 IP 10.0.1.1의 별칭이며 외부에서는 접근할 수 없음) 호스트에서는 컨트롤 플레인 API(`localhost:3000`)로 게이트웨이를 확인하고 구성한다. 따라서 이 가이드의 모든 curl 명령은 후자의 주소를 사용한다.
3 · 첫 도구: 공개 HTTP 백엔드
가장 간단한 백엔드부터 연결해 본다. 이를 위해서 인증이 필요 없는 공개 MCP 서버가 적합하다. 본 예시에서 공개 GitHub 저장소에 대한 질문에 답하는 DeepWiki를 사용하며 다음과 같이 servers.yaml에 항목 하나를 추가한다.
# /opt/vantisso/configs/mcp/servers.yaml
servers:
- id: deepwiki # stable identifier and default namespace
namespace: deepwiki # tool-name prefix in the catalog (defaults to id)
transport: http # MCP Streamable HTTP (the default)
url: https://mcp.deepwiki.com/mcp
profiles: [] # empty = every profile may use itservers.yaml은 데몬이 시작할 때 한 번만 읽기 때문에 백엔드를 추가하거나 수정한 뒤에는 반드시 데몬을 재시작해야 한다.
sudo systemctl restart vantisso이제 백엔드가 등록되었고 정상적으로 응답하는지 확인한다. GET /config/mcp/servers는 설정된 백엔드 목록과 함께 실시간 상태 점검 결과(up)를 반환한다. 응답에 자격증명은 절대 포함되지 않으며 has_credential 여부만 표시된다.
curl -s http://localhost:3000/config/mcp/servers
# → [
# {
# "id": "deepwiki", "namespace": "deepwiki", "transport": "http",
# "url": "https://mcp.deepwiki.com/mcp", "command": "",
# "profiles": [], "has_credential": false,
# "up": true, "error": ""
# }
# ]도구의 카탈로그화와 관리 방식
게이트웨이는 모든 백엔드의 도구를 하나의 카탈로그로 통합한다. 이름 충돌을 방지하기 위해 각 도구 이름 앞에 해당 서버의 네임스페이스를 접두사로 붙이며 구분자는 __(밑줄 두 개)이다. 예를 들어 DeepWiki의 ask_question 도구는 카탈로그에 deepwiki__ask_question으로 표시된다.
게이트웨이가 활성화되어 있고 어떤 프로파일이 이 백엔드를 사용할 수 있으면 해당 역할의 VM에는 게이트웨이 접속 정보가 자동으로 주입된다. VM 내부의 런타임은 이 게이트웨이에 MCP 클라이언트로서 연결하여 카탈로그의 도구를 자신의 도구 목록에 추가한다. 에이전트가 그 도구를 호출하면 게이트웨이가 대신 백엔드로 요청을 전달하고 응답을 반환한다.
VM에는 도구 카탈로그만 전달되기 때문에 백엔드의 실제 URL도, 다음 단계에서 추가할 자격증명도 VM 내부에서는 확인할 수 없다. 따라서 에이전트는 도구가 무엇인지만 인지할 뿐 그 도구가 어디에 있으며 어떻게 인증하는지는 알 수 없다.
4 · 호스트의 자격증명 관리
실제 백엔드는 대부분 인증을 요구한다. 자격증명은 servers.yaml이 아니라 secrets.yaml에 저장하고 servers.yaml에서는 키 이름으로만 참조한다. 토큰 값 자체는 서버 설정에도, 어떤 API 응답에도 출력되지 않는다.
# /opt/vantisso/configs/mcp/servers.yaml
servers:
- id: my-api
namespace: myapi
transport: http
url: https://api.example.com/mcp
credential: my_api_token # a key into secrets.yaml (not the token itself)
profiles: []# /opt/vantisso/configs/mcp/secrets.yaml (mode 0600, gitignored)
my_api_token: sk-your-real-token-here게이트웨이는 이 백엔드를 호출할 때마다 Authorization: Bearer <토큰> 헤더를 자동으로 추가한다. 토큰은 호스트에만 존재하며 VM으로 절대 전달되지 않는다.
servers.yaml이 참조한 credential 키가 secrets.yaml에 없거나 비어 있으면 게이트웨이는 해당 오류로 인해 자동 비활성화된다.(안전 실패 — 인증이 누락된 상태로는 실행되지 않음)
VM으로 전달되는 것과 전달되지 않는 것
| 항목 | 호스트 | VM 내부 |
|---|---|---|
| 백엔드 URL | 있음 | 없음 |
| 자격증명 토큰 | 있음 | 없음 |
| 네임스페이스 도구 카탈로그 (이름·설명·스키마) | 있음 | 있음 |
이것이 제로트러스트의 핵심이다. 에이전트가 탈취되더라도 백엔드 자격증명은 그 환경에 처음부터 존재하지 않으므로 유출될 수 없다.
5 · 로컬 도구 직접 구동: stdio 백엔드
이 단계는 선택 사항인 고급 기법에 해당한다. 대부분의 사용자는 앞의 HTTP 백엔드(3·4단계)로 충분하며 호스트에서 도구 서버를 직접 프로세스로 실행해야 할 때만 이 방식을 사용한다.
원격 HTTP 서버 대신 게이트웨이가 호스트에서 MCP 서버 프로세스를 직접 실행하게 할 수도 있다. transport: stdio를 사용하면 게이트웨이가 지정한 명령을 서브프로세스로 실행하고 표준 입출력으로 줄 단위 JSON-RPC를 주고받게 만들 수 있다.
# /opt/vantisso/configs/mcp/servers.yaml
servers:
- id: localtools
namespace: localtools
transport: stdio
command: /usr/local/bin/my-mcp-server # or a bare name on the daemon's PATH
args: ["--flag"] # NEVER put secrets here (cmdline is public)
credential: localtools_token # optional; requires credential_env
credential_env: MY_SERVER_TOKEN # env var the token is injected as
profiles: [leader]stdio 백엔드는 다음과 같이 동작한다.
- 지연 스폰 — 프로세스가 비정상 종료되면 다시 실행하되 크래시 루프를 방지하기 위해 5초의 쿨다운 시간을 둔다.
- 권한 격리 — 데몬이 root로 실행될 때 서브프로세스는 비특권 사용자로 실행된다. 기본값은
nobody이며VANTISSO_MCP_STDIO_USER로 변경할 수 있다. - 자원 상한 — 열 수 있는 파일 디스크립터 수, 생성할 수 있는 프로세스 수 등에 커널이 강제하는 상한이 걸리기 때문에 도구 서버가 호스트 자원을 고갈시키지 못하게 방지한다.
- 전용 작업 디렉터리 — 서버마다
/var/lib/vantisso/mcp-stdio/<id>가 작업 디렉터리 겸 HOME으로 주어진다.(재시작 후에도 캐시 유지 가능) - 최소 환경 — 자식 프로세스는 최소한의 환경 변수(
PATH·HOME·LANG, 그리고 설정한 경우credential_env)만 상속받는다. 데몬 자신의 환경은 상속하지 않는다.
자격증명과 실행 파일 사용 시 주의사항
args에는 비밀 값을 절대 넣어서는 안 된다. 명령줄은 /proc/<pid>/cmdline을 통해 누구나 읽을 수 있기 때문이다. 자격증명이 필요하면 args가 아니라 credential/credential_env로 지정하며 토큰은 자식 프로세스의 환경 변수로만 주입된다. 또한 실행 파일은 비특권 사용자가 읽고 실행할 수 있어야 하므로 /root 하위가 아닌 /usr/local/bin 같은 위치에 배치한다. 첫 실행 시 npx/uvx로 서버를 내려받는 방식이라면 초기화 대기 시간을 초과할 수 있으므로 미리 설치한 뒤 절대 경로를 사용하는 것이 안전하다.
| HTTP 백엔드 | stdio 백엔드 | |
|---|---|---|
transport | http (기본) | stdio |
| 실행 위치 | 원격 서버 | 호스트의 로컬 서브프로세스 |
| 필수 필드 | url | command |
| 자격증명 주입 | Authorization: Bearer 헤더 | 자식 프로세스의 환경 변수(credential_env) |
6 · 프로파일별 도구 제한
모든 에이전트가 모든 도구를 볼 필요는 없다. 역할마다 꼭 필요한 도구만 노출하면 보안에 유리하고 요청과 함께 전달되는 도구 목록도 짧아져 호출 비용 절감에 도움이 된다. 도구 노출 범위는 아래 세 가지 필터를 통해 조정하며 세 개의 필터에 정의된 조건의 교집합이 최종 적용된다. 즉 어떤 도구가 실제로 포함되려면 세 개의 필터 조건 모두에서 허용되어야 한다.
| 필터 | 위치 | 효과 | 반영 |
|---|---|---|---|
profiles: | servers.yaml | 이 서버를 사용할 수 있는 프로파일 목록(빈 값은 전체를 의미함) | 재시작 |
tools_allow / tools_deny | servers.yaml | 서버 내에서 노출할(화이트리스트) 또는 숨길(블랙리스트) 도구 지정 | 재시작 |
| 프로파일 바인딩 | PUT /config/profiles/{name}/mcp | 해당 프로파일이 실제로 사용할 서버 집합 정의 | 즉시 |
먼저 설정 파일 servers.yaml에서 두 가지를 정할 수 있다. 하나는 그 서버를 사용할 수 있는 프로파일을 profiles:로 한정하는 것이고, 다른 하나는 서버가 제공하는 도구 중 노출할 것과 숨길 것을 tools_allow·tools_deny로 고르는 것이다. 둘 다 설정 파일에 반영하는 것이라 변경되면 데몬을 재시작해야 실제로 반영된다.
- id: deepwiki
transport: http
url: https://mcp.deepwiki.com/mcp
profiles: [leader, researcher] # only these profiles may use it
tools_allow: [read_wiki_structure, ask_question] # expose only these tools그다음 특정 프로파일이 실제로 사용할 서버 집합을 런타임에 API를 이용해 지정할 수 있다. 이 바인딩은 servers.yaml의 profiles: 조건과 교집합으로 적용되므로 원래 허용되지 않은 서버를 새로 열 수는 없고 이미 허용된 범위 내에서 추가 필터 조건을 적용하는 것이다. 설정 파일과 달리 API를 통한 요청 기반이기 때문에 데몬 재시작 없이 곧바로 반영된다.
# Restrict the 'researcher' profile to just deepwiki:
curl -s -X PUT http://localhost:3000/config/profiles/researcher/mcp \
-H 'Content-Type: application/json' \
-d '{"servers":["deepwiki"]}'
# → {"servers":["deepwiki"],"bound":true}
# Read it back:
curl -s http://localhost:3000/config/profiles/researcher/mcp
# → {"servers":["deepwiki"],"bound":true}바인딩 규칙 정리
- 바인딩 자체가 없음(
bound: false) — 그 프로파일은servers.yaml설정을 그대로 따른다. - 빈 서버 목록(
{"servers":[]}) — 그 프로파일은 어떤 MCP 서버도 사용하지 않는다. - 없는 서버 지정 — 설정에 없는 서버 id를 넣으면
400으로 거부되고 아무것도 저장되지 않는다. - 사용 가능한 서버 없음 — 프로파일이 쓸 수 있는 서버가 하나도 없으면 그 역할의 VM은 게이트웨이에 연결조차 하지 않는다.
7 · 신원·감사·호출량 제한
호출자 식별 방식
게이트웨이에는 로그인 토큰이 없기 때문에 호출자의 신원은 요청의 소스 IP로 식별한다. 게이트웨이는 소스 IP를 VM 레지스트리에서 조회하여 해당 VM과 프로파일을 확인하고 그 프로파일의 정책에 따라 접근을 결정한다. 이 신원 정보를 신뢰하려면 IP 위조를 방지해야 한다. VANTISSO_NET_ANTISPOOF(기본 활성화)가 각 VM의 브리지 포트를 배정된 MAC과 IP에 고정하여 한 VM이 다른 VM의 IP를 사칭하지 못하게 한다.
감사 로그
게이트웨이를 통과하는 모든 호출(도구·리소스·프롬프트)은 로그로 기록한다. 각 호출은 {workDir}/audit/mcp.jsonl에 한 줄씩 기록되고 메트릭으로도 집계된다. 개인정보 보호 원칙에 따라 실제 전달된 인자와 결과는 기록하지 않으며 메타데이터만 남긴다.
{"ts":"2026-07-21T09:12:03Z","vm":"vm-3f9a","profile":"researcher","server":"deepwiki","kind":"tool","tool":"ask_question","outcome":"ok","ms":412}outcome은 다음 네 가지 값 중 하나를 갖는다: ok(성공), forbidden(정책에 의한 거부), rate_limited(호출량 제한 초과), fail(백엔드 오류).
호출량 제한
폭주나 비용 급증을 방지하기 위해 VM과 백엔드의 조합별로 분당 호출 예산을 설정할 수 있다. VANTISSO_MCP_RATE가 분당 허용 호출 수이고(0은 무제한을 의미하며 기본값임) VANTISSO_MCP_BURST는 토큰 버킷의 버스트 값을 의미한다(미설정 시 RATE와 동일한 값 적용). 예산을 초과한 호출은 JSON-RPC 오류로 반환되고 rate_limited로 관리되며 잠시 후 재시도할 수 있다.
# 60 calls/min per (VM, backend), bursts up to 10.
# Rate settings are read at startup, so set them then restart the daemon.
# Environment=VANTISSO_MCP_RATE=60
# Environment=VANTISSO_MCP_BURST=108 · 운영과 트러블슈팅
변경 사항이 반영되는 시점
| 변경 항목 | 반영 방법 |
|---|---|
servers.yaml · secrets.yaml (백엔드·자격증명 추가·수정) | 데몬 재시작 |
VANTISSO_MCP_* 환경 변수 | 데몬 재시작 |
프로파일 바인딩 (PUT …/mcp) | 즉시 (재시작 불필요) |
도구가 보이지 않을 때 점검 순서
- 게이트웨이 활성화 여부 체크:
GET /config/mcp에서enabled가true이고server_count가 0보다 큰지 확인한다. - 백엔드 정상 동작 여부 체크:
GET /config/mcp/servers에서up이true인지, 아니라면error내용을 확인한다. - 프로파일이 해당 서버를 사용할 수 있는지 확인:
servers.yaml의profiles:에 포함되어 있거나 비어 있는지 확인한다. - 바인딩 결과 해당 서버가 배제된 것인지 확인:
GET /config/profiles/{name}/mcp를 확인한다. 교집합이므로 바인딩이 해당 서버를 제외했을 수 있다. tools_allow/tools_deny가 해당 도구를 숨기고 있지는 않은지 확인한다.- 도구 이름에 네임스페이스 접두사(
<namespace>__<tool>)가 포함되어 있는지 확인한다.
흔한 설정 실수
- 네임스페이스에
__사용 — 구분자와 충돌하여 거부된다. id나namespace중복 — 게이트웨이가 비활성화된다.- http에
command/args또는 stdio에url을 지정 — 설정 검증에서 실패한다. - stdio에서
credential만 지정하고credential_env누락 — 두 값은 반드시 함께 지정해야 한다. servers.yaml만 수정하고 데몬을 재시작하지 않으면 변경 사항이 적용되지 않는다.
데몬을 종료하면 게이트웨이도 순서대로 정리된다. 먼저 리스너가 새 요청을 더 받지 않고 처리 중이던 호출만 끝까지 마무리하며 그다음 게이트웨이가 띄워 두었던 stdio 서브프로세스를 모두 종료한다.
다음 단계
구성 확인에 사용하는 GET 엔드포인트의 자세한 내용은 API 레퍼런스를 참고한다.