sbx mcp add — MCP 서버 등록
sbx mcp add — MCP 서버 등록
sandbox mcp의 하위 명령인 sbx mcp add는 이름으로 MCP 서버를 등록해요. 서버를 검증하고 그 명세를 sbx create/run --static-mcp에서 쓸 수 있도록 저장해요. URL 형식 자동 감지, SSRF 가드, OAuth 설정, 사용자 지정 헤더, 로컬 stdio 명령 등 폭넓은 등록 방식을 지원해요.
출처: 문서
본문
설명 (Description)
이름으로 MCP 서버를 등록해요. 서버는 검증되고 그 명세는 sbx create/run --static-mcp에서 사용하도록 저장돼요.
이 명령은 서버를 등록만 해요. 이미 등록된 서버를 실행 중인 샌드박스에 붙이려면 sbx mcp load를 사용해요.
--url 플래그는 네 가지 입력 형식을 받아들이고, 유형은 자동으로 감지돼요:
-
원격 MCP 엔드포인트 URL (
https://host/mcp— 해당 URL에서 MCP를 말하며, OAuth 메타데이터는 RFC 9728/8414를 통해 발견됨) -
MCP 커뮤니티 레지스트리 URL (
https://registry.modelcontextprotocol.io/v0/servers/...— 레지스트리 인벨로프를 가져와 OCI 이미지를 해석함) -
서버 매니페스트 URL (MCP 커뮤니티 레지스트리 스키마 모양의
server.json또는server.yaml본문을 반환하는 아무 URL — GitHub raw URL, 내부 HTTP 서버, ad-hoc CDN 링크 모두 동작) -
Docker Hardened Images (DHI) 이미지 참조 (
dhi.io/:또는dhi.io/@sha256:...—server.json매니페스트가 이미지의 in-toto attestation에서 OCI Referrers API를 통해 추출됨)
그 밖의 이미지 참조(://가 없고 dhi.io가 아닌 입력, 예: docker.io/foo:tag)는 더 이상 허용되지 않아요. 서버 매니페스트를 대신 사용하세요.
SSRF 가드와 --skip-ssrf-check:
호스트가 private/RFC1918, 루프백, link-local, 또는 클라우드 메타데이터 주소로 해석되는 --url은 그래도 가져오지만 플래그가 붙어요: add가 진행되고 해석된 주소를 명명하는 경고가 출력돼요(내부 서비스, 클라우드 메타데이터, DNS-rebinding 대상을 향하는 매니페스트 URL을 차단하지 않고 보이게 만들어 보호). 일부 정당한 서버는 사설 네트워크(split-horizon DNS, 내부 로드 밸런서, VPN 전용 엔드포인트, PrivateLink)에 살아서 공개 호스트명이 사설 주소로 해석되고, 그런 서버에게 경고는 예상된 노이즈예요. OAuth authorization-server 메타데이터에도 같은 warn-and-proceed 자세의 별도 SSRF 가드가 있어요: 금지된 주소는 차단되지 않고 로그로 기록돼요. --skip-ssrf-check를 전달하면 OAuth 메타데이터 리다이렉트를 포함해 이 add에서 두 검사를 모두 비활성화하고(경고도 조용히 없앰) 제공자와 그 발견 대상을 신뢰할 때만 사용하세요. 통제하는 URL에만 쓰세요.
원격 엔드포인트용 OAuth (--oauth-authorization-server / --client-id):
두 가지 관련 옵션이 원격 --url 서버용 OAuth를 구성해요 (둘 다 --url에서만 유효해요):
--oauth-authorization-server는 well-known RFC 9728/8414 메타데이터를 게시하지 않는 서버(예: Gmail)에 authorization-server 메타데이터를 직접 제공해요. RFC 8414 oauth-authorization-server 모양(authorization_endpoint와 token_endpoint 필수)을 따르는 JSON 문서의 로컬 파일 경로 또는 http(s) URL이에요. 메타데이터 문서 자체가 registration_endpoint를 광고하지 않는 한 --client-id가 함께 필수이며, 광고하면 클라이언트가 동적으로 등록되고(RFC 7591) --client-id는 생략해도 돼요.
--client-id는 사전 등록된 OAuth 클라이언트를 제공해요. --oauth-authorization-server 없이 줄 수도 있어요: 그러면 서버의 authorization 메타데이터는 정상적으로 발견되고 제공된 클라이언트가 여기에 붙어요. 이는 발견 가능한 메타데이터가 registration_endpoint를 노출하지 않아(따라서 Dynamic Client Registration이 불가능하지만) 운영자가 미리 등록한 클라이언트 id를 받아들이는 서버에 맞는 모드예요.
클라이언트 시크릿 (confidential clients):
--client-secret 플래그는 없어요. confidential 클라이언트의 시크릿은 전역 범위의 시크릿 저장소에서 서비스 이름 mcp::client_secret 아래 살고, 서버를 사용할 때마다 거기서 읽혀요:
sbx secret set mcp:<server>:client_secret
값이 셸 히스토리에 남지 않도록 -t 없이 실행해 stdin에서 읽게 하세요. 시크릿은 디스크의 MCP 등록에 절대 기록되지 않아요. 나중에 sbx secret rm mcp:<server>:client_secret로 제거해요.
저장된 시크릿은 처음 사용한 OAuth 신원(client id, issuer, token endpoint)에 바인딩돼요. 따라서 같은 서버 이름을 다른 클라이언트나 authorization server에 다시 등록하면 재사용되지 않아요 — 새 클라이언트를 위해 시크릿을 다시 저장하세요. 기존 시크릿을 새 신원이 주장하도록 하려면 sbx secret rm mcp:<server>:client_secret:identity로 기록된 바인딩을 지우세요.
발견 경로에는 두 규칙이 적용돼요(registration_endpoint를 광고하는 서버, 직접 제공한 --oauth-authorization-server 경로, 또는 --command 서버에는 영향 없음):
- 발견된 authorization 메타데이터에 registration_endpoint가 없으면 Dynamic Client Registration이 불가능하므로 --client-id가 필수이고, 없으면 add가 실패해요. 이는 Slack 형태예요(발견 가능한 메타데이터, DCR 없음, 사전 등록된 클라이언트).
- 서버가 광고한 token_endpoint_auth_methods_supported(RFC 8414)에 "none"이 포함되지 않으면 — 즉 confidential 클라이언트(client_secret_basic / client_secret_post)만 받아들이면 — 저장된 클라이언트 시크릿이 필수예요. 없어도 등록은 성공하지만 add-시간 인증은 건너뛰어요; 시크릿을 저장하고 'sbx mcp auth <server>'를 실행해 마무리하세요. 목록에 "none"이 포함되면 public/PKCE 클라이언트가 허용되어 --client-id만으로 충분해요. 서버가 token_endpoint_auth_methods_supported를 전혀 광고하지 않으면(필드가 RFC 8414에서 선택 사항) 요구 사항을 결정할 수 없고 add는 평소처럼 진행돼요.
기본 OAuth 스코프 (--scope / --no-scope):
--scope는 원격 --url OAuth 서버의 동의(consent) 시점에 요청할 기본 스코프 세트를 기록해요(반복 가능). 인가 시점의 우선순위는 --no-scope > 명시적 'sbx mcp auth --scope' > 여기 기록된 세트 > 리소스 자체가 요구한다고 말하는 스코프 세트(RFC 9728 protected-resource 메타데이터 또는 WWW-Authenticate 챌린지에서) > 광고된 openid, email, profile, offline_access 중 해당하는 것. 다른 광고된 스코프는 이 폴백에서 제외돼요. 선택된 스코프가 없으면 scope 매개변수가 생략되어 authorization server가 자체 기본 grant를 적용할 수 있어요(RFC 6749 §3.3).
필수 세트를 게시하는 리소스는 플래그 없이도 그 세트가 요청되고, 동의 블록은 그 세트를 선택된 것이 아니라 파생된 것으로 표시해요. --no-scope는 모든 스코프 폴백을 억제하고 서버의 기본 grant를 요청해요.
이름을 지은 스코프는 서버가 게시할 수 있는 두 문서의 합집합에 대해 확인돼요: authorization server의 RFC 8414 scopes_supported와 리소스 자신의 RFC 9728 protected-resource 메타데이터(일부 서버, 예: GitHub, 실제 스코프를 리소스에 문서화하고 RFC 8414로는 거의 광고하지 않아요). 어느 쪽에도 없는 스코프는 범인을 명명하는 경고를 출력하지만 여전히 요청돼요 — 불일치는 종종 서버 자체 문서화 공백이지 오타가 아니며, 어느 문서든 비완전해도 허용되기 때문이에요. 이는 인식 검사이지 약속이 아니에요: 인식된 스코프도 동의 시점에 거부될 수 있어요.
스코프 값은 URN 모양(urn:ietf:params:oauth:scope:mail)이거나 URL 모양(https://www.fastmail.com/dev/mcp)일 수 있고, 둘 다 따옴표가 필요 없어요 — 스코프 토큰은 공백이나 따옴표를 포함할 수 없거든요 — 그리고 둘 다 와이어 상에서 정상적으로 percent-encode돼요. --scope는 직접 제공한 오버라이드와 OAuth 메타데이터가 발견되는 일반 --url 서버 양쪽에 적용돼요.
RFC 8707 리소스 표시자 (--resource):
모든 인가 요청, 코드 교환, 토큰 갱신은 MCP 인가 명세가 요구하는 대로 토큰이 대상인 서버를 resource 매개변수(RFC 8707)에 이름을 붙여요. 그 값은 보통 파생되며 이 플래그가 필요 없어요: 서버가 게시하면 자체 RFC 9728 protected-resource 메타데이터에서, 아니면 --url 엔드포인트에서(소문자 scheme/host, 경로 유지, fragment 제거) 와요.
--resource는 그 파생을 사용자가 제공한 값으로 대체해요. 파생된 값이 틀릴 때 쓰세요 — 가장 흔한 경우는 게시된 식별자가 베어 origin(https://api.example.com)인데 엔드포인트가 경로(https://api.example.com/mcp)를 가진 서버로, 아무것도 게시하지 않은 것과 구별할 수 없어서 엔드포인트 URL이 전송되는 경우예요. 알 수 없는 대상을 거부할 권한이 있는 authorization server는 invalid_target으로 답해요.
값은 scheme과 host가 있는 절대 URI여야 하고, RFC 8707 §2는 fragment를 금지해요. 잘못된 값은 나중에 고치거나 버리지 않고 add를 실패시켜요. 값은 그대로(VERBATIM) 전송돼요 — 아무것도 소문자화하지 않고 경로나 끝 슬래시를 조정하지 않아요. 문자열이 서버를 이름 짓는 것이고 바꾸면 다른 서버를 이름 지을 수 있기 때문이에요. 값은 하나만 허용돼요: 게이트웨이 백엔드는 정확히 하나의 MCP 엔드포인트에 연결하거든요.
게시된 값과 URL 파생, 그리고 resource_indicators_supported=false를 광고하는 authorization server보다도 우선해요(그때 add가 그렇게 말해요). 등록에 기록되므로 sbx mcp auth --resource는 없어요: 하나의 값이 add-시간 인가, 이후 모든 sbx mcp auth, 모든 토큰 갱신에 쓰여요 — 그 사이 달라지는 리소스가 바로 이 매개변수가 막으려는 audience 불일치예요. 바꾸려면 서버를 sbx mcp rm하고 다시 add하세요; 기존 토큰은 어차피 옛 리소스용으로 발급됐거든요.
'audience'가 아님: RFC 8707의 resource는 MCP 명세가 요구하는 것이고, authorization-code 요청의 audience 매개변수는 sbx가 보내지 않는 Auth0/Okta 벤더 확장이에요. 유효 값과 그 출처는 sbx mcp inspect에서 확인해요.
스코프 제한: 이는 sbx 자신이 실행하는 OAuth 흐름을 규제해요 — 로컬 데이터 플레인(SBX_MCP_URL=none)과 --oauth-authorization-server/--client-id로 등록된 아무 서버. 일반 원격 호스팅 모드에서는 control-plane 게이트웨이가 OAuth 클라이언트이고 아직 그 필드를 안 갖기 때문에, 값은 기록되고 add는 그 서버로 보내지지 않는다고 경고해요.
원격 엔드포인트용 사용자 지정 요청 헤더 (--header):
--header는 curl 관례 'Header-Name: header value'로 작성된 HTTP 헤더를 원격 --url 엔드포인트로 보내는 모든 요청에 추가해요. 더 많은 헤더를 위해 플래그를 반복하고, 각 헤더 이름은 한 번만 줄 수 있어요. 전송이 소유한 헤더(Host, Content-Length, Connection, Proxy-*, …)는 거부돼요.
원격 엔드포인트만 헤더를 담을 수 있어요: --header는 --command와 --local에서 거부되고, --url이 stdio 서버(레지스트리 또는 OCI 이미지를 명명하는 매니페스트 URL)로 해석되는 add는 헤더를 버리지 않고 실패해요.
헤더 값은 ${placeholder}로 시크릿을 참조할 수 있어요. 플레이스홀더는 입력한 그대로 등록에 저장되고 시크릿 자체는 절대 저장되지 않아요; 값은 암호화된 시크릿 저장소에서 읽혀 샌드박스가 서버에 연결할 때 대체돼요. 다음과 같이 저장하세요:
sbx secret set mcp:<server>:<placeholder>
예를 들어:
sbx mcp add acme --url https://mcp.acme.com/mcp --header 'Authorization: Bearer ***'
sbx secret set mcp:acme:api-key
OAuth로 보호되는 서버에서 명시적 Authorization 헤더는 OAuth 액세스 토큰보다 우선해요.
헤더 시크릿은 로컬 시크릿 저장소에서 읽히므로, 헤더를 담은 서버는 이 호스트가 연결하는 곳에서만 동작해요. MCP 게이트웨이가 호스팅(SaaS)인 동안 하나를 추가하면 조용히 버려질 헤더로 등록되지 않고 거부돼요 — 서버가 직접 제공한 OAuth 오버라이드(--oauth-authorization-server)를 담아 모든 게이트웨이 모드에서 이 호스트가 직접 연결하는 경우가 아니라면요.
대체 입력 — 로컬 stdio 명령 (--command + --args):
명령은 샌드박스 밖 호스트에서 하위 프로세스로 실행돼요.
경고: 로컬 서버는 임시 개발 전용이에요. 신원(identity), 검증 가능한 공급망, 샌드박싱이 없어요. 프로세스는 호스트 사용자의 전체 권한으로 실행돼요 — 파일시스템을 읽고, 네트워크에 접근하고, 사용자가 호출할 수 있는 어떤 API든 호출할 수 있어요. 신뢰할 수 없는 실행 파일에
--command를 쓰지 마세요.
옵션 (Options)
| 옵션 | 기본값 | 설명 |
| --args | | 명령의 커맨드라인 인자 |
| --callback-port | 0 | add-시간 인가 중 OAuth 콜백 리스너를 바인딩할 로컬 포트 (기본: OS 할당 임시 포트). OAuth 앱의 허용 redirect URI에 포트를 미리 등록해야 할 때 유용. --url 원격 OAuth 서버에 적용; 서버가 OAuth를 필요로 하지 않는 것으로 밝혀지면 경고와 함께 무시 |
| --client-id | | 사전 등록된 클라이언트의 OAuth client id (--url과 함께; --oauth-authorization-server와 함께 또는 없이 사용 가능). confidential 클라이언트의 시크릿은 'sbx secret set mcp:
전역 옵션 (Global options)
| 옵션 | 기본값 | 설명 | | --cloud | | 로컬 sandboxd 대신 Docker Cloud Sandboxes API로 디스패치 (점점 더 많은 동사 지원 — 현재 목록은 'sbx --cloud --help' 실행) | | -D, --debug | | 디버그 로깅 활성화 |
예시 (Examples)
# 원격 MCP 엔드포인트 (OAuth 자동 감지)
sbx mcp add notion --url https://mcp.notion.com/mcp
sbx mcp add linear --url https://mcp.linear.app/mcp
# MCP 커뮤니티 레지스트리 URL
sbx mcp add fetch --url https://registry.modelcontextprotocol.io/v0/servers/fetch-mcp/versions/latest
# 일반 서버 매니페스트 URL (server.json / server.yaml)
sbx mcp add opine --url https://example.com/mcp/opine/server.yaml
# Docker Hardened Image (매니페스트가 이미지 attestation에서 추출됨)
sbx mcp add fetch --url dhi.io/fetch-mcp:latest
# 레지스트리 URL, 로컬 모드 (docker run으로 호스트에서 실행; stdio 패키지만)
sbx mcp add fetch --local --url https://registry.modelcontextprotocol.io/v0/servers/fetch-mcp/versions/latest
# 사설 네트워크 엔드포인트 (호스트가 사설 주소로 해석) — SSRF 가드 옵트아웃
sbx mcp add internal --url https://private.example.com/mcp --skip-ssrf-check
# well-known OAuth 메타데이터를 게시하지 않는 원격 엔드포인트 + 직접 제공한 OAuth 오버라이드:
# --oauth-authorization-server는 RFC 8414 메타데이터 문서의 경로 또는 http(s) URL,
# --client-id는 OAuth client id
sbx mcp add acme --url https://mcp.acme.com/mcp --oauth-authorization-server ./acme-as.json --client-id my-client
# registration 엔드포인트가 없는 DISCOVERABLE 서버의 사전 등록 클라이언트
# — --oauth-authorization-server 불필요 (메타데이터가 발견됨)
sbx mcp add slack --url https://slack.example.com/mcp --client-id my-preregistered-client
# Confidential 클라이언트 — 먼저 시크릿 저장 (프롬프트, argv나 셸 히스토리에 절대 없음),
# 그다음 등록; 시크릿은 시크릿 저장소에서 읽힘
sbx secret set mcp:slack:client_secret
sbx mcp add slack --url https://slack.example.com/mcp --client-id my-preregistered-client
# 동의 시점에 요청할 기본 OAuth 스코프 기록 (서버의 authorization 메타데이터가
# 광고하지 않는 스코프는 경고하지만 여전히 요청됨; 각각 --scope 반복)
sbx mcp add acme --url https://mcp.acme.com/mcp --scope read --scope write
# URN·URL 모양 스코프 값은 보통 스코프이고 따옴표 불필요
sbx mcp add fastmail --url https://api.fastmail.com/mcp --scope https://www.fastmail.com/dev/mcp --scope offline_access
# 파생된 값이 틀렸을 때 RFC 8707 'resource' 표시자 정정
# (게시된 식별자가 엔드포인트 URL이 아닌 서버)
sbx mcp add acme --url https://mcp.acme.com/mcp --resource https://api.acme.com/mcp
# 사용자 지정 헤더가 있는 원격 엔드포인트 (curl 관례; 반복 가능).
# ${api-key} 값은 샌드박스가 연결할 때 시크릿 저장소에서 읽힘
sbx mcp add acme --url https://mcp.acme.com/mcp --header 'Authorization: Bearer ***' --header 'Accept: application/json, text/event-stream'
sbx secret set mcp:acme:api-key
# 로컬 stdio 명령 (호스트에서 실행 — 개발 전용)
sbx mcp add github --command npx --args @modelcontextprotocol/server-github
sbx mcp add postgres --command docker --args "run,-i,--rm,mcp/postgres"
# 작업 디렉토리(cwd)가 있는 로컬 stdio 명령 (호스트 프로세스용)
sbx mcp add local-fs --command node --args server.js --dir /srv/data
더 알아보기 (Learn more)
- 등록된 서버를 샌드박스에 연결하려면
sbx mcp load, 인증은sbx mcp auth를 참고해요. - MCP 서버 인가 프로토콜은 RFC 9728(보호 리소스)과 RFC 8414(인가 서버 메타데이터), RFC 8707(리소스 표시자)을 참고해요.