1. 인증을 위한 실험실로서의 MCP Jam
MCP Jam은 “또 하나의 이상한 툴”이 아니라 MCP 클라이언트 역할을 수행할 수 있는 여러분의 실험용 스탠드입니다. 본질적으로 ChatGPT가 MCP 서버와 상호작용할 때의 동작을 에뮬레이션합니다. .well-known/oauth-protected-resource를 읽고, OAuth 플로우를 실행하며, 토큰을 요청에 붙이고, 무엇이 잘못되었는지 투명하게 보여줍니다.
아주 실용적인 포인트: MCP Jam에서 Default OAuth 플로우를 성공시키면 실제 ChatGPT App 연동 준비가 약 80%는 된 셈입니다. ChatGPT가 계정 연동(linking) 시 수행하는 일은 Jam이 이미 할 수 있으며, 로그와 조작 버튼이 더 투명할 뿐입니다.
지난 강의에서 우리 학습용 MCP 서버 GiftGenius에 대한 기본 인증을 설정했습니다. 토큰 검증 방식(JWT 또는 introspection)을 선택하고, .well-known/oauth-protected-resource를 구현했으며, 도구를 보호하는 미들웨어도 만들었습니다. 이제 이러한 요소들이 MCP Jam의 다양한 인증 모드에서 어떻게 동작하는지 확인해 보겠습니다.
이 강의의 목표:
- Jam에서 인증 모드(None, Bearer, OAuth with credentials, Default OAuth)를 의도적으로 전환할 수 있다;
- 각 모드에서 Jam이 MCP 서버로 정확히 무엇을 전송하는지 이해한다;
- 어느 부분이 고장 났는지 진단한다: MCP Server, Auth Server, 아니면 메타데이터인지;
- 보호된 도구는 토큰이 있어야만 동작하고, 공개 도구는 토큰 없이도 동작함을 검증한다.
2. 우리 학습용 MCP 서버: 무엇을 테스트하나
추상적으로만 이야기하지 않기 위해 맥락을 짧게 상기합니다. 학습용 애플리케이션 GiftGenius를 계속 사용합니다. 이는 사용자의 선물 추천을 돕고 주문 및 위시리스트를 보여주는 ChatGPT App입니다.
MCP 서버 측에 이미 준비된 것:
- 공개 도구, 예: search_gifts — 익명 호출 가능;
- 보호된 도구, 예: list_user_orders — 인증된 사용자만 동작하며 mcp:tools scope가 필요.
서버는 다음을 수행할 수 있음:
- .well-known/oauth-protected-resource를 게시;
- 토큰 검증(JWT 또는 introspection — 이전 강의에서 한 가지 방식을 선택함);
- 토큰에서 sub(user id), scope, aud를 추출해 도구 핸들러로 전달.
Node.js/TypeScript에서 전형적인 토큰 검증 미들웨어는 다음과 같을 수 있습니다:
// middleware/auth.ts
export function requireScope(requiredScope: string) {
return async (req: any, res: any, next: () => void) => {
const header = req.headers["authorization"];
if (!header?.startsWith("Bearer ")) {
res
.status(401)
.set(
"WWW-Authenticate",
`Bearer realm="mcp", resource_metadata="${process.env.BASE_URL}/.well-known/oauth-protected-resource", scope="${requiredScope}"`
)
.json({ error: "unauthorized" });
return;
}
// 여기서 이미 토큰(서명, exp, aud, scope...)을 검증하고
// 결과를 req.user 에 넣습니다
next();
};
}
이 미들웨어는 MCP의 보호된 도구 앞에서 사용됩니다. 토큰이 없으면 401과 resource_metadata를 포함한 올바른 WWW-Authenticate를 반환합니다. 이는 MCP Authorization 스펙이 요구하는 바입니다. 토큰 검증과 보조 함수 구현에 대한 자세한 분석은 이전 강의에서 이미 진행했으므로 여기서는 전제로 사용합니다.
3. MCP Jam의 인증 모드: 개요
MCP Jam에는 MCP 서버에 연결할 때 사용할 수 있는 여러 인증 모드가 있습니다. 이는 토큰이 전혀 없는 경우부터 완전한 Authorization Code + PKCE까지의 전형적인 OAuth 패턴에 대응합니다.
간단히 정리:
- None (No Auth) — Jam은 Authorization 헤더를 전혀 추가하지 않습니다. 익명 접근입니다. 공개 MCP 서버 또는 보호된 리소스가 401과 WWW-Authenticate로 올바르게 거부하는지 확인할 때 적합합니다.
- Bearer Token — Jam이 Authorization: Bearer <token>을 추가합니다. 이 토큰은 인터페이스에 수동으로 붙여 넣습니다. 이미 다른 방법(curl, Keycloak UI 등)으로 토큰을 발급받았고 MCP 리소스의 동작을 빠르게 점검하고 싶을 때 유용합니다.
- OAuth with credentials (Client Credentials) — Jam이 지정한 Client ID와 Secret을 사용하여 Auth Server에서 client_credentials로 직접 토큰을 가져옵니다. 사용자 참여가 없는 서버 간 인증에 가까운 “confidential client” 모드입니다.
- Default OAuth (Authorization Code + PKCE) — ChatGPT 유사 클라이언트(public client, secret 없음)에 가장 중요한 모드입니다. Jam은 resource_metadata를 읽고 Auth Server를 찾은 뒤, /authorize로 브라우저를 열어 PKCE 플로우를 수행하고 사용자 토큰을 획득합니다.
시각화를 위해 표로 정리해 봅니다.
| Jam 모드 | Jam이 보내는 것 | 토큰 발급 주체 | 전형적 시나리오 |
|---|---|---|---|
| None | Authorization 없음 | 없음 | 익명 도구, 401 확인 |
| Bearer Token | Bearer <manual> | 여러분(curl, IdP UI) | Resource Server 로직 테스트 |
| OAuth with cred. | Bearer <client token> | Jam — client_credentials | 서비스/관리용 도구 |
| Default OAuth | Bearer <user token> | Jam — Authorization Code+PKCE | ChatGPT와 같은 사용자 로그인 |
이제 각 모드를 순회하며 GiftGenius MCP 서버를 어떻게 통과시키는지 살펴보겠습니다.
4. None 모드: 서버가 올바르게 거부하는지 확인
가장 단순한 모드부터 시작합니다. 인증 없음.
MCP Jam에서 서버(예: http://localhost:4000/mcp)를 선택하고 연결 설정의 인증 모드를 None으로 지정합니다.
이때 일어나는 일:
- Jam이 MCP 연결을 설정한다;
- 도구 호출 시 Authorization 헤더를 추가하지 않는다;
- 공개 도구(예: search_gifts)는 호출할 수 있다;
- 보호된 도구(예: list_user_orders)를 호출하면 서버는 401 Unauthorized를 반환해야 한다.
이 401 응답에 올바른 WWW-Authenticate가 포함되는 것이 중요합니다. OpenAI와 MCP Authorization 스펙의 권장 사항에 가까운 예시는 다음과 같습니다(추가 필드 realm 및 scope 포함):
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp",
resource_metadata="https://giftgenius.example.com/.well-known/oauth-protected-resource",
scope="mcp:tools"
Content-Type: application/json
{"error": "unauthorized"}
Jam은 이러한 응답을 보면 리소스가 보호됨을 이해하고, 어디서 메타데이터(resource_metadata)를 가져와야 하는지와 요구되는 scopes가 무엇인지 파악합니다. None 모드에서는 단순히 에러를 보여주지만, Default OAuth 모드에서는 자동으로 해당 resource_metadata를 따라가 OAuth 플로우를 시작합니다.
디버깅 관점에서 None 모드로 확인할 것:
- 공개 도구가 토큰 없이도 동작하는지;
- 보호된 도구가 익명으로 절대 실행되지 않는지;
- WWW-Authenticate 헤더가 스펙에 부합하는지(Bearer와 resource_metadata 포함).
사소해 보이지만, 401을 반환하면서도 WWW-Authenticate를 누락하거나 잘못된 파라미터를 넣는(예: 최신의 resource_metadata 대신 구식 resource_metadata_uri 사용) 경우에서 많은 문제가 시작됩니다.
5. Bearer Token 모드: Resource Server 로직을 빠르게 점검
다음은 이미 유효한 토큰(Jam 외부에서 발급)으로 Resource Server 로직 자체를 점검하는 모드입니다. 서버가 이 토큰을 올바로 수락/거부하는지, scope와 audience 처리가 적절한지, sub을 여러분 서비스의 사용자와 정확히 연결하는지 등을 확인합니다.
MCP Jam에서 모드를 Bearer Token으로 전환하고 토큰 입력란에 다음과 같이 붙여 넣습니다:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
이제 Jam은 모든 MCP 요청에 다음 헤더를 추가합니다:
Authorization: Bearer eyJhbGciOi...
MCP 서버는 요청을 받아 requireScope("mcp:tools") 미들웨어를 통과시키며 JWT를 디코드하고 클레임을 검증합니다. 전형적인 검증 코드는 다음처럼 단순화해 볼 수 있습니다:
// auth/verifyToken.ts
import jwt from "jsonwebtoken";
export function verifyToken(header: string) {
const token = header.replace("Bearer ", "");
const payload = jwt.verify(token, process.env.JWT_PUBLIC_KEY!);
// 여기에서 aud, scope 등을 검증할 수 있습니다.
return payload as { sub: string; scope?: string };
}
그리고 미들웨어에서 이를 사용합니다:
// 내부 requireScope
const payload = verifyToken(header);
if (!payload.scope?.includes(requiredScope)) {
res.status(403).json({ error: "insufficient_scope" });
return;
}
(req as any).user = { id: payload.sub };
next();
Bearer 모드에서 시도해 볼 실험:
- 필요한 scope가 없는 토큰을 넣어 서버가 403/401을 반환하는지 확인;
- 잘못된 aud를 가진 토큰을 넣어 서버가 거부하는지 확인;
- 만료된 토큰으로 invalid_token 오류를 확인.
이는 UI 로그인이나 PKCE를 거치지 않고 Resource Server 로직을 “강하게 두드려 보는” 로컬 테스트 모드입니다. 여기서 확인한 사항은 이후 Default OAuth 모드에서 ChatGPT나 Jam이 획득한 토큰에도 그대로 적용됩니다.
6. OAuth with credentials (Client Credentials) 모드: “애플리케이션 명의” 토큰
이번에는 다소 드물지만 개념 파악에 유용한 모드인 OAuth with credentials, 즉 client_credentials 그랜트입니다. Jam에서 다음을 지정합니다:
- Client ID
- Client Secret
- 필요한 scopes(예: mcp:tools)
Jam은 여러분의 Auth Server의 token_endpoint에 대략 다음과 같은 요청을 보냅니다:
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&
client_id=<ID>&
client_secret=<SECRET>&
scope=mcp:tools
Auth Server는 보통 sub가 특정 사용자가 아니라 클라이언트 자체(예: sub = "mcp-jam-test-client")를 의미하는 토큰을 발급합니다. Jam은 이 토큰을 일반 Bearer처럼 사용하기 시작합니다.
MCP 세계에서 이 모드가 유용한 경우:
- 특정 사용자에 묶이지 않은 서비스/관리 도구(예: 로그 덤프, health-check, 기술 지원);
- 비즈니스 로직에서 사용자 토큰과 “클라이언트 토큰”을 구분해야 할 때 MCP 서버가 이를 구분할 수 있는지 점검.
ChatGPT Apps 맥락에서는 보통 사용되지 않습니다. ChatGPT는 public client로 시크릿을 보관하지 않기 때문입니다(public client에는 client_secret이 없어야 합니다). 하지만 Jam에서는 다음의 차이를 파악하는 데 도움이 됩니다:
- “이미 발급받은 토큰을 그냥 넣었다” (Bearer 모드);
- “Jam이 클라이언트 자격 증명으로 직접 토큰을 받아왔다” (OAuth with credentials).
학습용 서버에서는 예를 들어 admin_list_all_orders 같은 전용 MCP 도구를 만들어 grant_type=client_credentials와 해당 역할이 있어야만 접근 가능하도록 해 볼 수 있습니다. 오늘 강의의 필수는 아니지만 좋은 실험입니다.
7. Default OAuth 모드: ChatGPT처럼 완전한 Authorization Code + PKCE
이제 핵심인 Default OAuth입니다. 이 모드는 ChatGPT가 여러분 App의 계정을 연동할 때 수행하는 동작과 가장 가깝습니다. 클라이언트가 resource_metadata를 읽고 Auth Server로 가서 로그인 페이지를 열고, authorization code를 받은 뒤 Authorization Code + PKCE S256 방식으로 access token으로 교환합니다.
절차를 단계별로 살펴보겠습니다. 아래는 시퀀스 다이어그램입니다.
sequenceDiagram
participant Jam as MCP Jam (Client)
participant RS as MCP Server (Resource)
participant PRM as /.well-known/oauth-protected-resource
participant AS as Auth Server (Keycloak/Auth0)
Jam->>RS: 보호된 tool 호출(토큰 없음)
RS-->>Jam: 401 + WWW-Authenticate (resource_metadata=PRM)
Jam->>PRM: GET /.well-known/oauth-protected-resource
PRM-->>Jam: resource, authorization_servers, scopes_supported 등을 담은 JSON
Jam->>AS: GET /authorize?client_id=...&code_challenge=...&scope=...
Note right of AS: 사용자가 로그인하고 동의함
AS-->>Jam: authorization_code로 리디렉션
Jam->>AS: POST /token (code + code_verifier)
AS-->>Jam: { access_token, scope, expires_in, ... }
Jam->>RS: Authorization: Bearer <access_token>로 tool 호출
RS-->>Jam: 도구 실행 성공 결과
이 모드에서 반드시 확인할 사항:
- MCP 서버의 올바른 401/WWW-Authenticate 응답. 서버가 resource_metadata를 보내지 않거나 URL이 잘못되면, Jam은 PRM을 읽을 수 없고 OAuth 플로우를 시작할 수 없습니다.
- 유효한 .well-known/oauth-protected-resource 문서. resource, authorization_servers, scopes_supported 등이 올바르게 설정되어 있어야 Jam이 어디로 토큰을 받으러 가고 어떤 scopes를 요청해야 하는지 이해합니다.
- Auth Server의 올바른 설정.
- Authorization Code Flow와 PKCE S256이 활성화되어야 합니다.
- Client ID가 PRM에서 기대하는 값과 일치하거나(DCR — Dynamic Client Registration 사용) 적절히 등록되어야 합니다.
- Auth Server에 설정된 Redirect URI가 Jam이 사용하는 URI와 정확히 일치해야 합니다.
- PKCE S256. Jam은 code_challenge를 생성하며, Auth Server가 S256 방식을 지원한다고 가정합니다. PKCE가 비활성화되어 있거나 plain만 지원한다면 플로우는 실패합니다.
- Scopes와 audience. Auth Server는 필요한 aud와 요청된 scopes(mcp:tools 등)를 포함한 토큰을 발급해야 하고, MCP 서버는 이를 검증해야 합니다.
Default OAuth가 성공하면 얻게 되는 결과:
- Jam — MCP 서버와의 연결 상태에서 보호된 도구 list_user_orders가 Auth Server에서 로그인한 바로 그 사용자에 대한 데이터를 정확히 반환;
- Auth Server 로그 — 성공적인 authorize + token 교환;
- MCP 서버 로그 — 토큰 검증 성공 및 sub 추출.
디버깅을 위해 도구 핸들러에 간단한 로거를 추가하면 토큰에서 가져온 userId를 실제로 보고 있는지 확인하는 데 도움이 됩니다:
// MCP 도구 list_user_orders 핸들러 내부
export async function listUserOrders(args: any, context: any) {
const user = context.user as { id: string };
console.log("[MCP] listUserOrders for user", user.id);
// 이후 해당 사용자의 주문을 반환합니다
}
8. 어디가 문제인지: 모드별 진단
이제 MCP Jam의 증상만으로 문제가 MCP 서버, Auth Server, 메타데이터 중 어디에 있는지 파악하는 방법을 논의합니다. 이 섹션은 일종의 모드별 진단 체크리스트입니다.
None 모드에서:
보호된 도구를 호출했을 때 서버가 다음과 같이 응답한다면:
- 200 OK로 토큰 없이도 동작 — 해당 도구 앞에 토큰 검증이 없습니다. 미들웨어 추가 또는 scope 검증이 필요합니다.
- 401이지만 WWW-Authenticate가 없거나 resource_metadata가 잘못됨 — Jam은 어디서 메타데이터를 가져올지 몰라 Default OAuth를 시작할 수 없습니다. 앞서 제시한 예시처럼 헤더를 수정하세요.
Bearer Token 모드에서:
- Jam이 유효하다고 확신하는 토큰을 넣었는데도 401/403이 계속 발생(직접 curl/Postman으로 호출하면 통과하는 토큰) — Resource Server 로직 문제일 가능성이 큽니다. aud/scope 검증이 잘못됐거나 JWT 서명 검증에 사용한 공개키가 다른 경우 등.
- Jam에서 Bearer 토큰은 잘 동작하지만 Default OAuth에서만 실패 — 문제는 MCP 서버가 아니라 Auth Server 또는 PRM일 가능성이 큽니다. Default OAuth로 받은 토큰의 scope/aud가 수동 테스트에 사용한 토큰과 다를 수 있습니다.
OAuth with credentials 모드에서:
- Jam이 토큰을 받지 못함(/token 단계 오류) — Auth Server의 클라이언트 설정 문제를 확인하세요. 시크릿 불일치, client_credentials 미허용, scope 금지 등.
- 토큰은 있지만 MCP 서버가 거부 — 서버가 사용자 sub(이메일/사용자 ID)를 기대하는데 토큰에는 클라이언트 식별자만 있을 수 있습니다. 또는 aud/scope가 기대와 불일치합니다.
Default OAuth 모드에서:
가장 함정이 많은 시나리오입니다. 흔한 문제들:
- 잘못된 Redirect URI. Auth Server가 invalid_redirect_uri를 반환하거나 코드를 발급하지 않습니다. Jam이 사용하는 URI가 IdP 클라이언트 설정에 정확히 등록되어 있는지(슬래시/오탈자 포함) 확인하세요.
- PKCE 누락 또는 미지원. Auth Server가 PKCE를 요구하는데 Jam(또는 구버전 Jam)이 code_challenge를 보내지 않거나, 반대로 Jam은 S256을 보내는데 IdP가 이 방식을 지원하지 않으면 invalid_request가 발생합니다.
- 불일치한 scopes. PRM에는 mcp:tools를 선언했지만 IdP 클라이언트에는 openid만 허용되어 있다든가, 반대로 Jam이 IdP가 발급해 줄 수 있는 범위보다 더 많은 scope를 요청하는 경우입니다.
- 잘못된 audience(aud). 토큰의 aud가 MCP 서버가 기대하는 값과 다르면(예: 다른 리소스의 URL) 서버는 정당하게 토큰을 거부합니다.
아래 세 곳의 로그를 반드시 확인하는 습관을 들이세요:
- MCP Jam — PRM 파싱 오류, Auth Server로의 HTTP 요청 오류;
- Auth Server — /authorize, /token 로그가 거부 사유를 알려줍니다;
- MCP 서버 — 토큰 거부 사유(invalid_token, insufficient_scope, wrong_audience 등).
9. 실제 ChatGPT App과의 연관성
왜 Jam으로 이렇게 많은 시간을 실험하는가? 바로 Jam이야말로 인증 플로우를 여러분 손으로 제어하고 내부 과정을 전부 보여주는 실험 스탠드이기 때문입니다.
Jam에서 Default OAuth를 성공적으로 수행하면 다음을 사실상 입증한 것입니다:
- MCP 서버의 .well-known/oauth-protected-resource가 올바르다;
- Auth Server(Keycloak/Auth0/…) 설정이 정확하다;
- 역할, scopes, audience, claims가 기대와 일치한다;
- MCP 서버가 토큰을 검증하고 사용자를 연결할 수 있다.
같은 MCP 서버에 연결된 ChatGPT도 똑같이 동작합니다. PRM을 읽고, Auth Server로 가서 토큰을 받은 다음, Authorization: Bearer를 붙여 도구를 호출합니다.
차이는 ChatGPT에서는 최종 결과(“계정 연동 성공” 또는 “문제가 발생함”)만 보이지만 Jam에서는 전체 프로토콜을 확인하며 단계별로 “어디가 문제인지”를 파악할 수 있다는 점입니다.
10. 미니 실습: GiftGenius MCP 서버 순차 테스트
지금까지 내용을 실제 프로젝트에서 그대로 따라 할 수 있는 간단한 시나리오로 모읍니다.
먼저 MCP 서버를 실행합니다(예: pnpm dev:mcp). 다음을 확인합니다:
- http://localhost:4000/mcp(또는 여러분의 URL)에서 대기 중인지;
- /.well-known/oauth-protected-resource 엔드포인트가 올바른 JSON을 반환하는지;
- Auth Server(Keycloak)가 동작 중이며 Jam/ChatGPT용 public client가 설정되어 있는지.
이어서:
- None 모드.
Jam을 인증 없이 MCP 서버에 연결합니다. 다음을 확인하세요:- search_gifts가 정상 동작;
- list_user_orders가 올바른 401과 WWW-Authenticate를 반환.
- Bearer Token 모드.
Keycloak(UI 또는 curl)로 access token을 발급받아 Jam에 넣고 list_user_orders를 호출합니다. 다음을 확인하세요:- 유효한 토큰이면 도구가 동작하며 해당 사용자 주문을 반환한다;
- mcp:tools 없이 발급된 토큰이거나 aud가 다르면 서버가 오류를 반환한다.
- OAuth with credentials 모드.
confidential client가 있다면 Jam에 client_id, client_secret을 지정하고 필요한 scope를 설정합니다. 기술용 도구(예: admin_list_all_orders)를 호출하여 서비스 토큰에서만 동작하는지 확인합니다. - Default OAuth 모드.
Default OAuth를 활성화한 뒤 list_user_orders를 호출합니다. Jam은 자동으로:- 401 + WWW-Authenticate를 받고,
- PRM을 읽고,
- 브라우저를 열어 여러분이 Keycloak에 로그인하도록 하고,
- Authorization Code + PKCE로 토큰을 얻은 뒤,
- 해당 토큰으로 MCP 도구를 호출하여 여러분의 주문을 응답으로 보여줍니다.
네 가지 모드가 모두 기대대로 동작했다면 축하합니다. 여러분은 단순히 “Keycloak을 대충 설정”한 것이 아니라, 전체 인증 플로우를 점검하고 디버깅하는 방법을 실제로 이해한 것입니다.
11. MCP Jam과 인증 테스트에서 자주 발생하는 실수
실무에서는 아래와 같은 오류 패턴이 반복적으로 나타납니다. 증상만 보아도 알아챌 수 있도록 대표적인 “이렇게 하면 안 된다” 시나리오를 정리합니다.
오류 1: 보호된 도구가 None 모드에서도 동작하길 기대함.
개발자가 Jam을 None 모드로 켜고 list_user_orders를 호출한 뒤 401에 놀라 “혹시나” 하는 마음으로 서버의 토큰 검증을 제거하는 경우가 있습니다. 그 결과 MCP 도구가 익명으로 동작하게 되는데, 개인 정보나 커머스 시나리오에서는 절대 허용될 수 없습니다. None 모드는 서버가 토큰 없이 올바르게 거부하고 resource_metadata가 포함된 WWW-Authenticate를 반환하는지 확인하기 위한 것입니다.
오류 2: WWW-Authenticate 헤더 누락 또는 부정확.
아주 흔한 사례입니다. 서버가 401을 반환하면서 WWW-Authenticate를 포함하지 않거나, 구식 파라미터인 resource_metadata_uri를 사용하는 경우입니다. 이때 Jam(및 ChatGPT)은 Protected Resource Metadata를 어디서 가져와야 하는지 알 수 없어 Default OAuth가 시작되지 않습니다. 최소한 WWW-Authenticate: Bearer resource_metadata="https://.../.well-known/oauth-protected-resource"는 포함해야 합니다. realm과 scope는 선택 사항이지만, resource_metadata는 반드시 있어야 합니다.
오류 3: Bearer 모드만 테스트하고 Default OAuth는 무시.
개발자가 수동으로 토큰을 받아 Jam에 넣고 잘 동작하는 것을 보고 작업이 끝났다고 생각합니다. 그러나 실제 ChatGPT 연결 단계에서 .well-known이 부정확하거나 PKCE 미지원, Redirect URI 불일치 등의 이유로 링크가 실패합니다. Bearer 모드 테스트는 필요조건이지만 충분조건이 아닙니다. Default OAuth를 반드시 구동해 보아야 Auth Server와 PRM의 핵심 설정을 검증할 수 있습니다.
오류 4: 사용자 토큰이 필요한 곳에 client_credentials를 사용.
절박한 마음에 Jam에서 OAuth with credentials 모드를 켜고 client_credentials로 토큰을 받아 list_user_orders 같은 사용자용 도구에 사용하기도 합니다. 이 경우 토큰의 sub는 실제 사용자가 아니라 client_id이므로, 비즈니스 로직이 이상하게 동작하거나(예: “공용” 데이터를 보여줌) 해당 ID의 사용자를 찾지 못해 오류가 납니다. 실제 사용자가 개입하는 ChatGPT 시나리오에는 Authorization Code + PKCE(Default OAuth)가 필요하며, client_credentials는 서비스 작업에만 적합합니다.
오류 5: PRM, Auth Server, MCP 서버 간 scopes/audience 불일치.
.well-known/oauth-protected-resource에서 리소스를 https://giftgenius.example.com으로 선언하고 지원 scopes를 ["mcp:tools"]로 설정했다고 합시다. 그런데 Auth Server가 aud 없이 토큰을 발급하거나, MCP 서버의 검증이 aud = "https://giftgenius.example.com"과 mcp:tools 존재를 엄격히 요구하면, Default OAuth로 받은 토큰을 MCP 서버가 거부하게 됩니다. PRM, IdP의 클라이언트 설정, MCP 서버의 미들웨어 검증이 audience와 scope에 대해 서로 합의되었는지 항상 확인하세요.
오류 6: 구버전 MCP Jam 사용.
MCP Authorization 스펙은 활발히 발전 중이며, resource_metadata 같은 새로운 필드, 향상된 PKCE 플로우, 디버깅 도구가 추가되고 있습니다. 구버전 Jam은 최신 필드를 이해하지 못하거나 오래된 파라미터 이름만 지원할 수 있습니다. 최신 RFC에 맞게 모두 설정했는데 Jam이 “무엇을 해야 할지 모르는” 상황이 벌어질 수 있습니다. 좌절하기 전에 Jam이 최신 버전인지 확인하세요.
GO TO FULL VERSION