Claude Desktop
Claude Desktop (Cowork) 통합
Claude Desktop on third-party inference는 Cowork, Chat, Code 세션의 모든 모델 호출을 사용자가 지정한 게이트웨이로 보내요. LiteLLM이 바로 그 게이트웨이예요. 통합 로깅, 사용자 및 팀별 예산, 모델 접근 제어, 그리고 그 뒤에 있는 모든 Claude 배포(Anthropic, Bedrock, Vertex AI, Foundry)를 클라이언트를 다시 건드리지 않고 처리해요. 이 페이지에서는 디바이스가 LiteLLM에 인증하는 두 가지 방식(아이덴티티 제공자를 통한 SSO - 나눠줄 것이나 회전할 것이 없고 지출이 사람에게 귀속돼요, 그리고 가장 빠르게 테스트해볼 수 있는 정적 가상 키)을 다루고, 모델 선택기에서 보이는 것, 같은 아이덴티티가 LiteLLM MCP 게이트웨이를 통해 MCP 서버에 도달하는 방식, 설정을 플릿(fleet)에 배포하는 방법, 그리고 문제가 생겼을 때 확인할 것들을 설명할게요.
출처: 문서
본문
전체 구조
Claude Desktop은 LiteLLM을 Anthropic 자체 API처럼 취급해요. 시작 시 GET /v1/models로 모델을 발견하고, 채팅 트래픽은 스트리밍과 도구 사용을 포함해 POST /v1/messages로 보내며, 매 요청에 Authorization: Bearer ***를 실어 보내요. 자격 증명은 LiteLLM 가상 키이거나, SSO의 경우 로그인한 사용자에게 아이덴티티 제공자가 발급한 ID 토큰이에요. LiteLLM은 매 요청마다 JWT 인증으로 이를 검증하고 LiteLLM 사용자와 팀에 매핑해요.
| 설정 | 값 |
| 게이트웨이 기본 URL | https://your-litellm-proxy.com, /v1 접미사 없음 |
| 자격 증명, SSO | 사용자의 ID 토큰, LiteLLM JWT 인증으로 검증 |
| 자격 증명, 정적 키 | LiteLLM 가상 키 |
| 사용되는 엔드포인트 | GET /v1/models, POST /v1/messages, MCP 서버용 POST /mcp |
| LiteLLM 버전 | 모델 발견용 v1.98.0 이상, issuers JWT 설정용 v1.89.0 이상 |
| Claude Desktop 버전 | SSO용 1.6889.0 이상, claude.ai 가져오기용 1.10628.0 이상 |
옵션 A: 아이덴티티 제공자와의 SSO
inferenceCredentialKind: interactive로 설정하면, Claude Desktop이 시스템 브라우저에서 아이덴티티 제공자를 대상으로 OpenID Connect 로그인(authorization code + PKCE)을 실행해요. 리프레시 토큰은 OS 보안 저장소에 보관하고, ID 토큰은 bearer 자격 증명으로 LiteLLM에 보내요. LiteLLM은 제공자의 서명 키, 발급자(issuer), 수신자(audience)에 대해 서명을 확인한 뒤, 토큰의 클레임을 LiteLLM 사용자로, 그리고 groups 클레임을 통해 LiteLLM 팀으로 매핑해요. 누구도 키를 프로비저닝하거나 회전하지 않아요. 아이덴티티 제공자에서 제거된 사용자는 토큰이 만료되면 접근을 잃고, MFA와 조건부 액세스(conditional access)가 적용되며, 사용자가 보는 것은 Sign in to your organization 버튼뿐이에요. 이 방식은 LiteLLM 뒤에 데이터베이스가 필요해요. 사용자와 지출이 데이터베이스에 기록되기 때문이에요.
1. 아이덴티티 제공자에 Claude Desktop 등록
Claude Desktop은 네이티브 앱이므로 루프백 리다이렉트 URI를 가진 공용 클라이언트(PKCE, 클라이언트 시크릿 없음)로 등록돼요.
- Microsoft Entra ID
- Okta
- AWS Cognito
Entra 관리 센터에서 디렉터리 내 계정만을 위한 앱 등록을 만들고, Authentication 아래에 커스텀 리다이렉트 URI http://127.0.0.1/callback을 가진 Mobile and desktop applications 플랫폼을 추가해요. localhost 대신 127.0.0.1을 사용하고, /callback 경로를 유지하며, 해당 플랫폼 아래에 추가해요. Entra가 로컬 포트를 자유롭게 쓰도록 허용하는 유일한 방식이라서 그런데, 앱이 로그인 시 자유 포트를 고르기 때문에 이게 필요해요. 클라이언트 시크릿이나 API 권한은 필요 없어요. Application (client) ID와 Directory (tenant) ID를 복사해요.
발급자(issuer)는 https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0이고, 서명 키는 https://login.microsoftonline.com/YOUR_TENANT_ID/discovery/v2.0/keys에 있어요. 안정적인 사용자 ID 클레임은 oid예요. 그룹으로 사용자를 LiteLLM 팀에 매핑하려면 Token configuration에서 ID 토큰에 groups 클레임을 추가해요. 그룹 객체 ID를 담고 있어요.
Okta 관리 콘솔에서 OIDC, Native Application 타입의 앱 통합을 Authorization Code와 Refresh Token 그랜트 타입으로 만들어요. Okta는 포트를 포함해 리다이렉트 URI를 정확히 일치시키므로, 53180 같은 고정 포트를 정하고 http://127.0.0.1:53180/callback을 등록한 뒤 아래 Claude Desktop에도 같은 포트를 설정해요. 접근 권한을 줘야 할 사용자 또는 그룹을 할당해요.
발급자는 https://YOUR_ORG.okta.com으로, /.well-known/openid-configuration으로 끝나는 메타데이터 URI가 아닌 일반 org URL이에요(커스텀 인증 서버의 발급자는 https://YOUR_ORG.okta.com/oauth2/AUTH_SERVER_ID). 서명 키는 https://YOUR_ORG.okta.com/oauth2/v1/keys에 있어요. 안정적인 사용자 ID 클레임은 sub예요. 팀 매핑을 위해 ID 토큰에 groups 클레임을 추가해요.
Cognito 콘솔에서 사용자 풀을 열고 Mobile app 애플리케이션 타입으로 앱 클라이언트를 만들어요. 이것이 콘솔의 공용 클라이언트 타입(PKCE, 클라이언트 시크릿 없음)이고 데스크톱 앱이 필요로 하는 것이에요. Traditional web application 타입은 클라이언트 시크릿을 생성하고, Cognito는 이후 모든 토큰 요청에서 그 시크릿을 요구하는데, Claude Desktop은 공용 클라이언트라 시크릿을 절대 보내지 않아요. 시크릿은 생성 후 제거할 수 없으므로 웹 타입 클라이언트로는 로그인이 항상 토큰 교환에서 실패해요. Cognito도 포트를 포함해 리다이렉트 URI를 정확히 일치시키므로, 53180 같은 고정 포트를 정하고 반환 URL로 http://127.0.0.1:53180/callback을 입력해요(Cognito는 127.0.0.1에 대해 일반 http를 허용해요). 그리고 아래 Claude Desktop에도 같은 포트를 설정해요.
생성 후 앱 클라이언트의 Login pages 구성에서 OpenID Connect 스코프 openid, profile, email을 활성화해요. 사용자 풀은 Branding > Domain에서 도메인이 필요해요. Cognito의 authorize와 token 엔드포인트가 그 도메인에 있기 때문이에요. 발급자 URL의 검색(discovery) 문서가 자동으로 그들을 가리켜요.
발급자는 https://cognito-idp.REGION.amazonaws.com/USER_POOL_ID이고, 서명 키는 https://cognito-idp.REGION.amazonaws.com/USER_POOL_ID/.well-known/jwks.json에 있어요. 안정적인 사용자 ID 클레임은 sub이고, 사용자 풀 그룹의 사용자에게는 그룹 멤버십이 cognito:groups 클레임으로 전달돼요.
2. 토큰을 검증하도록 LiteLLM 구성
issuers 엔트리로 아이덴티티 제공자를 LiteLLM에 알려줘요(LiteLLM v1.89.0 이상). 각 엔트리는 하나의 iss 값을 서명 키와 audience에 바인딩하고 해당 제공자의 클레임 이름을 담고 있어서, 여러 제공자를 나란히 둘 수 있어요. audience는 등록한 클라이언트 ID예요. ID 토큰의 aud는 발급된 클라이언트를 가리키는데, 이를 확인하는 것이 같은 테넌트의 다른 앱용으로 발급된 토큰이 게이트웨이에 도달하는 것을 막아줘요.
- Microsoft Entra ID
- Okta
- AWS Cognito
config.yaml
general_settings: enable_jwt_auth: true litellm_jwtauth: user_id_upsert: true issuers: - issuer: https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0 jwks_url: https://login.microsoftonline.com/YOUR_TENANT_ID/discovery/v2.0/keys audience: YOUR_CLIENT_ID user_id_jwt_field: oid user_email_jwt_field: email team_ids_jwt_field: groupsmodel_list: - model_name: claude-sonnet-5 litellm_params: model: anthropic/claude-sonnet-5 api_key: os.environ/ANTHROPIC_API_KEY - model_name: claude-opus-5 litellm_params: model: anthropic/claude-opus-5 api_key: os.environ/ANTHROPIC_API_KEY - model_name: claude-haiku-4-5 litellm_params: model: anthropic/claude-haiku-4-5 api_key: os.environ/ANTHROPIC_API_KEY
config.yaml
general_settings: enable_jwt_auth: true litellm_jwtauth: user_id_upsert: true issuers: - issuer: https://YOUR_ORG.okta.com jwks_url: https://YOUR_ORG.okta.com/oauth2/v1/keys audience: YOUR_CLIENT_ID user_id_jwt_field: sub user_email_jwt_field: email team_ids_jwt_field: groupsmodel_list: - model_name: claude-sonnet-5 litellm_params: model: anthropic/claude-sonnet-5 api_key: os.environ/ANTHROPIC_API_KEY - model_name: claude-opus-5 litellm_params: model: anthropic/claude-opus-5 api_key: os.environ/ANTHROPIC_API_KEY - model_name: claude-haiku-4-5 litellm_params: model: anthropic/claude-haiku-4-5 api_key: os.environ/ANTHROPIC_API_KEY
config.yaml
general_settings: enable_jwt_auth: true litellm_jwtauth: user_id_upsert: true issuers: - issuer: https://cognito-idp.REGION.amazonaws.com/USER_POOL_ID jwks_url: https://cognito-idp.REGION.amazonaws.com/USER_POOL_ID/.well-known/jwks.json audience: YOUR_CLIENT_ID user_id_jwt_field: sub user_email_jwt_field: email team_ids_jwt_field: cognito:groupsmodel_list: - model_name: claude-sonnet-5 litellm_params: model: anthropic/claude-sonnet-5 api_key: os.environ/ANTHROPIC_API_KEY - model_name: claude-opus-5 litellm_params: model: anthropic/claude-opus-5 api_key: os.environ/ANTHROPIC_API_KEY - model_name: claude-haiku-4-5 litellm_params: model: anthropic/claude-haiku-4-5 api_key: os.environ/ANTHROPIC_API_KEY
이것은 기본 bearer인 ID 토큰을 검증해요. Claude Desktop에서 bearerTokenType: access_token으로 설정하면 audience: YOUR_CLIENT_ID를 disable_audience_validation: true로 바꿔요. Cognito 접근 토큰은 client_id 클레임을 담고 aud 클레임은 없기 때문이에요. LiteLLM은 한 엔트리에 두 키를 모두 설정하면 거부해요.
jwks_url은 선택 사항이에요. 없으면 LiteLLM이 {issuer}/.well-known/openid-configuration에서 jwks_uri를 읽어요. 어느 쪽이든 LiteLLM은 네트워크를 통해 아이덴티티 제공자에서 서명 키를 가져오므로, 프록시가 실행되는 곳에서 해당 제공자에 대한 아웃바운드 HTTPS가 필요해요. 잠긴 VPC에서는 그 이그레스(egress)를 허용하지 않으면 첫 인증 요청이 키를 기다리다 실패해요. user_id_upsert: true는 첫 요청 시 LiteLLM 사용자를 만들어내므로 Internal Users 아래에 사람별로 지출이 쌓이고, oid 또는 sub 값이 모든 지출 로그 행에 남아요. user_allowed_email_domain: yourcompany.com은 다른 이메일 도메인의 토큰을 거부해요. team_ids_jwt_field: groups는 토큰의 그룹 멤버십을 LiteLLM 팀 멤버십으로 바꿔요. team_id가 그룹의 id(Entra 그룹 객체 ID, Okta 그룹 이름)와 같은 팀을 만들면, 그 팀의 models, max_budget, 속도 제한, MCP 서버 권한이 그룹의 모든 사람에게 적용돼요. LiteLLM은 토큰이 그 그룹을 담고 있는 첫 순간에 사용자를 팀에 추가하므로 그들은 팀 멤버에도 나타나고, 정확히 한 팀에 속한 사용자는 나중에 토큰이 그 클레임을 생략해도 계속 그 팀으로 해석돼요. 그룹이 어떤 팀과도 일치하지 않는 토큰은 여전히 사용자로는 받아들여져서, 팀 예산도 MCP 서버도 없이 동작해요. 팀이 필요 없다면 그 줄을 빼면 돼요. iss가 어떤 엔트리와도 일치하지 않는 토큰은 JWT_PUBLIC_KEY_URL, JWT_AUDIENCE, JWT_ISSUER 환경 변수로 폴백하는데, 이것이 JWT 인증 문서가 설명하는 단일 제공자 설정이고 여기서도 동작해요.
public_key_url과 audience는 litellm_jwtauth 키가 아닙니다
Anthropic의 게이트웨이 가이드는 litellm_jwtauth 아래에 public_key_url과 audience를 직접 두는 모습을 보여줘요. LiteLLM에는 그런 키가 없고, public_key_url과 audience를 지명하는 ValueError: Invalid arguments provided: ...로 시작을 거부해요. 위처럼 issuers 엔트리에 넣거나, JWT_PUBLIC_KEY_URL과 JWT_AUDIENCE를 환경 변수로 설정해요.
3. Claude Desktop 구성
구성 창을 열어요. Help > Troubleshooting > Enable Developer Mode, 그다음 Developer > Configure Third-Party Inference…. Connection 섹션에서 Inference provider를 Gateway로, Gateway base URL을 프록시 URL로, Credential kind를 Interactive sign-in으로 설정해요. 그러면 API 키 필드가 숨겨지고 **Gateway SSO IdP (OIDC)**가 나타나요. 1단계의 Client ID와 Issuer URL을 입력하고, Scopes는 기본 openid profile email offline_access로 비워 두되(Cognito에서는 openid profile email로 명시적으로 설정해요. Cognito에는 offline_access 스코프가 없어서 요청하면 invalid_scope로 로그인이 실패해요. Cognito는 어쨌든 리프레시 토큰을 발급해요), Okta와 Cognito에서는 Redirect port(53180)를 채워요. Apply locally는 이 디바이스용 구성을 작성하는데 테스트해보기엔 충분해요. Export는 플릿용 관리 구성을 생성해요(Rolling out to a fleet 참고).
macOS .mobileconfig 페이로드에서 내보낸 키:
inferenceProvidergatewayinferenceGatewayBaseUrlhttps://your-litellm-proxy.cominferenceCredentialKindinteractiveinferenceGatewayOidc{"issuer":"https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0","clientId":"YOUR_CLIENT_ID"}
Okta의 경우 JSON은 {"issuer":"https://YOUR_ORG.okta.com","clientId":"YOUR_CLIENT_ID","redirectPort":53180}이고, Cognito는 {"issuer":"https://cognito-idp.REGION.amazonaws.com/USER_POOL_ID","clientId":"YOUR_CLIENT_ID","redirectPort":53180,"scopes":"openid profile email"}이에요. inferenceGatewayOidc는 값이 JSON 문자열인 하나의 키예요(Windows에서는 REG_SZ, Linux의 managed-settings.json에서는 네이티브 객체). inferenceGatewayOidc.clientId 같은 점으로 구분된 키는 읽히지 않아요. bearerTokenType은 issuers 엔트리가 검증하는 기본값 id_token으로 두세요. Google Workspace는 access_token을 사용해야 해요. 리프레시 시 새 ID 토큰을 발급하지 않고 그렇지 않으면 매시간 사용자에게 다시 물어보기 때문이에요.
4. 검증
다음 실행 시 사용자는 Sign in to your organization을 보고, 브라우저에서 로그인한 뒤 프록시에서 채워진 모델 선택기와 함께 앱으로 돌아와요. LiteLLM UI에서 Logs는 사용자 id 아래 각 요청을 보여주고, Internal Users는 upsert된 사용자와 지출을 보여줘요. 제공자가 그 클라이언트 ID에 발급하는 어떤 ID 토큰으로도 같은 엔드포인트를 셸에서 호출해볼 수 있어요.
curl https://your-litellm-proxy.com/v1/models \ -H "Authorization: Bearer ***" -H "anthropic-version: 2023-06-01"curl -N https://your-litellm-proxy.com/v1/messages \ -H "Authorization: Bearer ***" -H "anthropic-version: 2023-06-01" -H "content-type: application/json" \ -d '{"model":"claude-sonnet-5","max_tokens":64,"stream":true,"messages":[{"role":"user","content":"hello"}]}'
첫 번째는 선택기가 만드는 데 사용하는 Anthropic 형태의 모델 목록을 반환하고, 두 번째는 응답을 스트리밍해요. Audience doesn't match와 함께 401은 토큰이 audience의 클라이언트 ID와 다른 클라이언트에 발급됐다는 뜻이에요. Missing JWT Public Key URL from environment.는 토큰의 iss가 어떤 issuers 엔트리와도 일치하지 않는다는 뜻이고, Token Expired는 앱이 토큰을 리프레시하거나 리프레시가 실패하면 Sign in again을 물어 스스로 해결하는 상태예요.
옵션 B: 정적 가상 키
가상 키는 개념 증명(proof of concept), 공유 워크스테이션, 또는 이미 팀별 키를 나눠주는 게이트웨이에 적합한 자격 증명이에요. 같은 관리형 프로필의 모든 사람이 그 키와 그 예산을 공유하고, 회전하려면 새 프로필을 밀어 넣어야 하기 때문에 결국 플릿은 SSO를 쓰게 돼요.
LiteLLM UI의 Virtual Keys > + Create New Key에서 키를 만들고, Claude 모델로 범위를 지정하고 max_budget을 설정한 뒤 복사해요. Claude Desktop에서 Help > Troubleshooting으로 Developer Mode를 활성화하고, **Developer > Configure Third-Party Inference…**를 연 뒤 Inference provider를 Gateway로, Gateway base URL을 입력하고 키를 Gateway API key로 넣고, Credential kind는 Static API key, Gateway auth scheme은 Bearer로 유지하고(LiteLLM은 x-api-key도 받아요) 적용해요.
Troubleshooting" style=max-width:700px;margin-bottom:20px> Configure Third-Party Inference" style=max-width:700px;margin-bottom:20px>
내보낸 형태는 inferenceProvider: gateway, inferenceGatewayBaseUrl, inferenceGatewayApiKey이고, 그 스킴을 선택한 경우에만 inferenceGatewayAuthScheme: x-api-key예요. 그러면 요청이 해당 키 아래 Logs와 Usage에 표시돼요.
선택기의 모델
Claude Desktop은 GET /v1/models에서 선택기를 만들고 claude 또는 anthropic을 포함하는 id만 대소문자 구분 없이 유지해요. 그래서 중요한 것은 그 뒤의 업스트림 id가 아니라 model_list의 model_name 값이에요. Bedrock이나 Vertex AI에서 서빙되는 claude-sonnet-5는 통과하고, smart-router는 통과하지 못해요. LiteLLM은 불투명한 별칭이 필터를 통과하게 하는 anthropic_family_tier 필드를 반환하지 않으므로, 이름에 claude를 넣거나 inferenceModels에 모델을 나열해야 해요. inferenceModels는 발견을 정확히 사용자가 준 엔트리로 대체해요(첫 번째가 기본값):
[ {"name": "claude-sonnet-5", "supports1m": true}, {"name": "claude-opus-5", "labelOverride": "Opus 5 via LiteLLM"}, {"name": "claude-haiku-4-5"}]
supports1m은 해당 모델에 1M-컨텍스트 선택기 항목을 하나 더 추가해요(문자열 축약형 "claude-sonnet-5[1m]"도 같은 뜻). 프록시가 반환하는 id와 정확히 일치하는 name에만 설정하고, 1M-토큰 요청을 받아들이는 배포에만 설정해요. anthropicFamilyTier(sonnet, opus, haiku, fable, mythos)와 isFamilyDefault: true는 앱이 맨 앞의 계층 별칭이 어느 항목으로 해석되는지 알려줘요. Claude for Teams 또는 Enterprise 조직은 각 게이트웨이 모델 이름도 availableModels 허용 목록에 넣어야 하고, 그렇지 않으면 회색으로 표시돼요. Auto Router with Claude Code and Claude Desktop은 허용 목록 규칙과 선택기가 받아들이는 이름 뒤에 오토 라우터를 두는 방법을 다룬다. LiteLLM 뒤의 비-Claude 모델도 model_name에 claude가 들어가면 같은 방식으로 동작해요. 그 엔트리에 drop_params: true를 추가해 Cowork와 Code 세션이 보내는 Anthropic 전용 요청 필드가 제공자에 대응하는 필드가 없을 때 요청을 실패시키는 대신 버려지게 해요.
LiteLLM MCP 게이트웨이를 통한 MCP 서버
Claude Desktop의 관리형 MCP 서버는 managedMcpServers 엔트리인데, 각 업스트림 서버 대신 LiteLLM의 MCP 게이트웨이를 가리키면 MCP 트래픽이 추론(inference)과 같은 로깅, 접근 제어, 아이덴티티 아래에 들어가요. LiteLLM은 구성된 모든 서버를 하나의 스트리밍 HTTP 엔드포인트인 https://your-litellm-proxy.com/mcp로 노출하고, 같은 bearer 자격 증명으로 인증돼요. x-mcp-servers 헤더는 한 엔트리를 특정 서버들로 좁히고(서버별 경로 /mcp/도 같음), 도구는 -로 나타나요.
config.yaml
mcp_servers: deepwiki: url: https://mcp.deepwiki.com/mcp transport: http github: url: https://api.githubcopilot.com/mcp transport: http auth_type: bearer_token auth_value: os.environ/GITHUB_TOKEN
서버 접근은 가정되는 것이 아니라 부여돼요. 로그인한 사용자는 자신의 팀, 키, 조직이 사용하도록 허용된 서버만 보고, 다른 모든 서버는 단순히 tools/list에서 빠져 있어요. SSO에서는 부여가 토큰의 그룹이 매핑되는 팀에 있으므로, 그 팀을 object_permission에 서버와 함께 만들어요(같은 호출이 그룹이 받는 모델과 예산도 설정해요):
curl -X POST https://your-litellm-proxy.com/team/new \ -H "Authorization: Bearer $LITEL..._KEY" -H "content-type: application/json" \ -d '{"team_id": "GROUP_ID_FROM_THE_TOKEN", "team_alias": "Claude Desktop users", "models": ["claude-sonnet-5", "claude-opus-5", "claude-haiku-4-5"], "max_budget": 500, "object_permission": {"mcp_servers": ["deepwiki", "github"]}}'
대신 위험이 낮은 서버는 allow_all_keys: true로 모두에게 열 수 있고(Public MCP servers), MCP access control이 접근 그룹과 도구별 권한을 다뤄요. Claude Desktop 쪽에서는 정적 가상 키가 그 엔트리의 헤더에 들어가요.
[ { "name": "litellm", "transport": "http", "url": "https://your-litellm-proxy.com/mcp", "headers": {"Authorization": "Bearer sk-...", "x-mcp-servers": "deepwiki,github"} }]
정적 헤더는 로그인한 사용자 자신의 토큰을 담을 수 없으므로, SSO 플릿은 headersHelper를 대신 사용해요. Claude Desktop이 실행하는 실행 파일로, 평평한 JSON 객체로 헤더를 출력하고 headersHelperTtlSec 일정으로 다시 실행되며 서버가 401 또는 403으로 응답할 때 다시 실행돼요(Claude Desktop 1.46388.1 이상). 같은 앱 등록에 대해 ID 토큰을 얻는 헬퍼는 채팅과 MCP 지출을 같은 LiteLLM 사용자에 유지해요. toolPolicy는 도구 이름을 allow, ask, 또는 blocked에 매핑한 것으로 도구별 확인 정책을 설정해요. 사용자 자신의 업스트림 로그인이 필요한 서버(GitHub, Atlassian 등)는 LiteLLM의 MCP OAuth passthrough 또는 게이트웨이 호스팅 DCR 브리지와 함께 서버별 URL https://your-litellm-proxy.com/mcp/에 대해 "oauth": true를 사용해요. Claude Desktop은 서버가 인증되지 않은 요청에 HTTP 401로 응답할 때만 루프백 콜백 http://127.0.0.1:53280/callback에서 그 로그인을 시작해요. available_on_public_internet: false로 표시된 서버는 비공개 범위(또는 설정한 mcp_internal_ip_ranges) 밖의 호출자에게 숨겨져서, 공용 인터넷의 노트북은 나머지만 볼 수 있어요(MCP servers on the public internet 참고).
Claude Desktop의 내장 커넥터("server": "microsoft365", "github", "websearch")는 앱 안에서 해당 벤더의 자체 API에 대해 실행되며 LiteLLM을 절대 통과하지 않아요. url 엔트리만 통과해요. GitHub나 Microsoft 365 트래픽을 LiteLLM 아래에 두려면, 벤더의 MCP 서버를 프록시의 mcp_servers 엔트리로 구성하고 url 엔트리가 그것을 가리키게 해요.
플릿으로 배포
구성 창의 Export는 적용한 것을 MDM이 기대하는 템플릿으로 바꿔요. .mobileconfig(macOS), .reg 파일 또는 ADMX(Windows), 또는 Intune OMA-URI JSON이에요. 관리 구성은 macOS에서 /Library/Managed Preferences//com.anthropic.claudefordesktop.plist, Windows에서 HKLM\SOFTWARE\Policies\Claude(또는 HKCU), Linux에서 /etc/claude-desktop/managed-settings.json에서 읽히고, Apply locally는 ~/Library/Application Support/Claude-3p/configLibrary/, %LOCALAPPDATA%\Claude-3p\configLibrary\, ~/.config/Claude-3p/configLibrary/에 기록해요. bootstrapUrl은 디바이스가 MDM 프로필 대신 사용자가 호스팅하는 서버에서 같은 구성을 가져오게 해요. LiteLLM은 지금은 부트스트랩 엔드포인트를 서빙하지 않아요.
LiteLLM 뒤에서 더 중요한 키 두 개가 있어요. inferenceCustomHeaders는 모든 추론 및 발견 요청에 보내는 추가 헤더의 JSON 객체로(라우팅과 테넌트 헤더만, 절대 자격 증명 아님), 프로필이 매 요청에 x-litellm-tags를 찍는 방법이에요. 태그 예산, 태그 라우팅, Usage 페이지가 모두 그걸로 그룹지어요. {"x-litellm-tags": "claude-desktop,finance"}는 별도의 키나 팀 없이 부서에 자체 예산을 부여해요. inferenceStreamIdleTimeoutSec(300~1800)은 Cowork 또는 Code 세션이 스트리밍 응답에서 모델 출력을 기다리는 시간을 늘리는데, LiteLLM이 업스트림 모델이 조용한 동안 SSE keep-alive 핑을 쓸 때만 그렇고, 아무것도 없는 응답은 여전히 기본값에서 타임아웃돼요.
사용자의 claude.ai 채팅 가져오기
개인 Claude 구독에서 게이트웨이로 옮긴 사용자는 빈 사이드바로 시작해요. 표준 Claude Desktop은 그 계정 아래 claude.ai에 채팅을 유지하는 반면, third-party inference의 Claude Desktop은 Anthropic 계정이 없고 Chat과 Cowork 기록을 디바이스에 보관해요(macOS에서 ~/Library/Application Support/Claude-3p/). 전환은 아무것도 삭제하지 않아요. 옛 채팅은 claude.ai에서 계속 읽을 수 있고, 두 모드는 한 머신에서 공존하므로 로그인 화면의 Anthropic 옵션은 게이트웨이 데이터를 건드리지 않고 표준 앱을 다시 불러와요. 다만 게이트웨이 앱에는 자동으로 표시되지 않을 뿐이에요.
Anthropic은 정확히 이 전환을 위한 가져오기 기능을 기본 꺼짐 상태로 제공해요. 관리 구성(MDM 프로필 또는 부트스트랩 응답, Anthropic의 레퍼런스에 따르면 키가 거기서 읽힘)에 claudeAiImport를 추가하고, Chat이 이미 켜져 있지 않으면 chatTabEnabled도 추가해요. third-party inference에서 Chat 화면은 기본 꺼짐이기 때문이에요. claudeAiImport는 위의 inferenceGatewayOidc와 같은 형태의 값이 JSON 객체인 하나의 키예요(.mobileconfig 또는 .reg에서는 JSON 문자열, 부트스트랩 응답이나 managed-settings.json에서는 네이티브 객체), Claude Desktop 1.10628.0 이상에서:
{ "chatTabEnabled": true, "claudeAiImport": {"enabled": true, "exportEnabled": true, "bannerBehavior": "show"}}
bannerBehavior는 전환을 셀프 서비스로 만드는 부분이에요. show는 새 채팅이나 작업 상단에 가져오기 프롬프트를 넣어 아무도 설정 페이지가 존재하는지 알 필요가 없게 하고, detect는 이전 Claude 설치의 세션을 보유한 머신에서만 표시해요. 설정하지 않으면 앱이 프롬프트를 표시하지 않아요.
그런 다음 각 사용자가 Settings > Import & export를 열고 **Import…**를 클릭한 뒤 마법사에서 claude.ai에 로그인해요. Fetch export는 그들의 채팅과 프로젝트를 가져와 게이트웨이 앱에 복사해요. 앱에서 로그인하고 싶지 않은 사용자는 claude.ai의 Settings > Privacy > Export data에서 zip을 다운로드하고(이메일 링크는 24시간 유효) **Choose file…**로 선택해요. 같은 마법사가 이전 표준 설치가 머신에 남긴 Cowork와 Code 세션도 잡아요. 가져온 채팅은 사이드바에서 열리고 Trust and resume 후 프록시에 대해 계속돼요. 가져오기는 일회성 복사로 중복 없이 다시 실행할 수 있고, 첨부 파일과 프로젝트 지식 파일은 절대 넘어오지 않으며(두 경로 모두에 적용되는 claude.ai 정책), claude.ai Team 또는 Enterprise 워크스페이스의 멤버는 워크스페이스의 데이터 및 개인정보 설정에서 소유자가 Allow members to export their own data를 켜야만 내보내기할 수 있어요. exportEnabled는 같은 설정 페이지에 **Export…**를 추가하는데, 이 컴퓨터의 채팅과 세션을 zip으로 만들어 같은 마법사로 다른 디바이스로 옮길 수 있어요. Anthropic의 가져오기 가이드에 각 단계의 스크린샷이 있어요.
사용량 귀속
SSO 아래에서 모든 요청은 토큰에서 upsert된 LiteLLM 사용자에게, 그리고 groups 클레임이 팀에 매핑되면 그 팀에게 귀속돼요. 그래서 Usage는 지출을 사람별, 팀별로 나누고 예산은 두 수준 모두에 적용돼요. 정적 키 아래에서는 키가 단위이고, 팀 또는 용도별로 하나씩 자체 max_budget을 가진 키가 실용적인 단위예요. 어느 쪽이든 inferenceCustomHeaders의 x-litellm-tags는 누가 인증하는지 바꾸지 않고 부서나 비용 센터 같은 세 번째 축을 추가해요.
문제 해결
모델 선택기가 비어 있거나, 연결 테스트는 통과하는데 Claude 모델이 나타나지 않는 경우. 발견은 claude 또는 anthropic을 포함하는 id만 유지해요. model_name을 바꾸거나 inferenceModels를 설정해요. v1.98.0보다 오래된 LiteLLM은 /v1/models에 OpenAI 형태로만 응답해서 앱이 파싱할 수 없어요. 업그레이드하거나 inferenceModels를 설정해 앱이 발견을 건너뛰게 해요.
로그인은 성공하고 탭은 닫히는데 GET /v1/models가 401로 응답하는 경우. 프록시의 general_settings 아래에 enable_jwt_auth: true가 없어서 bearer 토큰이 가상 키 인증으로 떨어지며 키 조회에 실패한 거예요. 로그인은 여전히 정상으로 보이는데, 브라우저 단계가 아이덴티티 제공자만 관여하기 때문이에요. 최근 LiteLLM 버전은 401 본문에 이렇게 말해요. "This key has the structure of a JWT, but JWT auth is not enabled on this proxy". 2단계의 JWT 구성을 추가해요.
브라우저가 redirect_mismatch와 함께 아이덴티티 제공자의 자체 오류 페이지에 도달하는 경우. 클라이언트에 콜백이 등록되지 않았거나, 제공자가 포트를 정확히 일치시키는데 Claude Desktop이 일시적(ephemeral) 포트를 고른 경우예요. 고정 포트로 http://127.0.0.1:PORT/callback을 등록하고 그 포트를 Redirect port로 설정해요. Okta와 Cognito 모두 정확히 일치시켜요.
로그인이 error=invalid_request&error_description=invalid_scope와 함께 바로 127.0.0.1로 되돌아오는 경우(Cognito). 요청이 앱 클라이언트에 없는 스코프를 물어본 거예요. Scopes를 비워 둬서 기본 세트가 Cognito가 지원하지 않는 offline_access를 요청했거나, 앱 클라이언트의 Login pages 구성에서 OIDC 스코프가 활성화되지 않은 경우예요. Scopes를 openid profile email로 설정하고 앱 클라이언트에 그 세 개를 활성화해요.
매 요청에 Authentication Error, Missing JWT Public Key URL from environment.가 뜨는 경우. 토큰의 iss가 어떤 issuers 엔트리와도 일치하지 않고(토큰의 iss 클레임을 issuer 값과 비교해요. Entra 토큰은 끝에 /v2.0이 붙어요) JWT_PUBLIC_KEY_URL 폴백도 설정되지 않은 경우예요.
시작 시 public_key_url과 audience를 지명하는 ValueError: Invalid arguments provided. config가 Anthropic의 스니펫을 따른 거예요. 그 값들을 issuers 엔트리로 옮겨요.
Authentication Error, Validation fails: Audience doesn't match. 토큰이 다른 클라이언트 ID에 발급된 거예요. audience는 Claude Desktop 앱 등록의 클라이언트 ID여야 하고, bearerTokenType은 id_token으로 두어야 해요. 접근 토큰의 audience는 요청된 API이기 때문이에요.
Token Expired 또는 사용자가 매시간 로그인하라는 요청을 받는 경우. 리프레시는 기본 스코프에 포함된 offline_access 스코프가 필요해요. id_token 모드의 커스텀 scopes 값은 이를 명시적으로 나열해야 해요. Google Workspace는 id_token 모드에서 어쨌든 매시간 다시 물어보므로 거기서는 bearerTokenType: access_token을 사용해요.
gateway SSO: server does not advertise device_authorization_endpoint. 앱이 inferenceGatewayOidc를 읽을 수 없는데, 보통 점으로 구분된 키나 잘못된 JSON으로 푸시됐기 때문이에요. 구성 창에서 다시 내보내요.
OIDC discovery failed (HTTP 404) 또는 (HTTP 405). issuer 값이 발급자 기본 URL이 아니라 메타데이터 URI거든요. /.well-known/openid-configuration 접미사를 제거해요.
브라우저가 Connected를 보여주는데 앱이 Token exchange failed (HTTP 401)을 보고하는 경우. 아이덴티티 제공자 등록이 시크릿을 기대하는 기밀(Web) 클라이언트거든요. 대신 Native 애플리케이션(Okta), Mobile and desktop applications 플랫폼(Entra), 또는 Mobile app 클라이언트(Cognito)를 등록해요. 클라이언트 타입은 생성 후 바꿀 수 없고 Cognito 클라이언트 시크릿은 생성 후 제거할 수 없으므로 새 클라이언트를 만들어요.
JWT 인증을 켠 직후, 이전에는 즉시 응답하던 게이트웨이에서 GET /v1/models에 도달할 수 없거나 타임아웃되는 경우. 첫 검증 요청이 프록시로 하여금 jwks_url에서 서명 키를 가져오게 하는데, 프록시 네트워크에서 아이덴티티 제공자로의 이그레스가 막혀 있으면 그 가져오기가 멈춰요. 프록시 컨테이너 안에서 curl -m 5 가 키 세트를 반환해야 해요. 멈춘다면 그 이그레스를 열어요.
LiteLLM MCP 엔드포인트의 tools/list가 비어서 돌아오는 경우. 로그인한 사용자가 어떤 서버에도 부여가 없거든요. 토큰의 그룹이 어떤 팀과도 일치하지 않거나, 팀의 object_permission에 mcp_servers가 없는 경우예요. 서버를 팀에 추가하거나 서버에 allow_all_keys: true를 설정해요.
비-Claude 모델에 대한 요청이 400으로 실패하는 경우. Cowork와 Code 세션은 Anthropic 전용 필드를 보내므로 해당 model_list 엔트리에서 drop_params: true를 설정해요.
1M 컨텍스트 창 항목이 없는 경우. supports1m이 발견된 id와 정확히 일치하지 않는 name을 가진 inferenceModels 엔트리에 있거든요.
개인 Claude 구독에서 게이트웨이로 옮긴 사용자가 옛 채팅을 전혀 못 보는 경우. 삭제된 것이 없어요. 채팅은 그 계정 아래 claude.ai에 있고, 게이트웨이 앱은 자체 로컬 기록을 유지해요. claudeAiImport를 켜고 사용자가 **Settings > Import & export > Import…**를 실행하게 해요(Bringing users' claude.ai chats over 참고).
Settings > Import & export가 이 배포에서 가져오기가 활성화되지 않았다고 말하는 경우. 관리 구성에 claudeAiImport가 없거나 그 enabled가 true가 아니거든요. 로컬로 적용된 것보다 디바이스의 관리형 프로필이 우선하므로 키가 프로필에 있어야 해요.
더 알아보기 (Learn more)
- JWT auth - 역할 매핑과 JWT-to-virtual-key 매핑을 포함한 모든
litellm_jwtauth옵션 - Virtual keys
- MCP gateway - MCP 게이트웨이, MCP 접근 제어, MCP OAuth passthrough
- Claude Code with LiteLLM