RFC 9728으로 구현하는 MCP(Model Context Protocol) Zero-Config 인가: 서버 인가 설정을 클라이언트가 자동 탐색하는 방법
AI 에이전트가 수십 개의 외부 서비스와 동시에 통신하는 시대가 왔습니다. GitHub, Jira, Notion, Slack — 각 서비스마다 인가 방식이 다르고, 지금껏 개발자들은 이 인가 서버 정보를 일일이 코드에 하드코딩해왔습니다. 서비스가 늘어날수록 설정 파일은 비대해지고, 인가 서버 URL이 바뀌는 날엔 코드 전체를 뒤져야 했습니다.
MCP(Model Context Protocol) 는 Claude, GPT 같은 AI 호스트가 외부 도구·데이터 소스와 통신하기 위한 표준 프로토콜입니다. 개별 서비스들을 표준화된 인터페이스로 연결할 수 있어, AI 에이전트가 GitHub에서 코드를 조회하거나 Jira에서 이슈를 생성하는 작업을 일관된 방식으로 처리할 수 있습니다. 그런데 MCP 서버가 늘어날수록 각각의 인가(Authorization) 설정을 관리하는 문제가 함께 커집니다.
2025년 4월 IETF가 공식 발행한 RFC 9728 (OAuth 2.0 Protected Resource Metadata) 은 이 문제를 표준으로 해결합니다. 리소스 서버가 /.well-known/oauth-protected-resource 엔드포인트에 자신의 인가 요구사항을 담은 JSON 문서를 공개하면, 클라이언트는 런타임에 이를 조회해 "어디서 토큰을 받아야 하는지"를 자동으로 파악합니다. MCP 명세는 이를 한발 더 나아가 MCP 서버에게 RFC 9728 구현을 필수(MUST) 로 의무화했습니다.
이 글을 읽고 나면 MCP 서버에 Zero-Config 인가를 구현하고, AI 에이전트가 사전 설정 없이 어떤 서버에든 자동으로 인가 흐름을 완료하도록 만들 수 있습니다.
핵심 개념
인증(Authentication)과 인가(Authorization), 무엇이 다른가
본문을 읽기 전에 한 가지 개념을 구분해두면 좋습니다. 인증(Authentication) 은 "당신이 누구인가"를 확인하는 과정이고, 인가(Authorization) 는 "당신이 무엇을 할 수 있는가"를 결정하는 과정입니다. OAuth(Open Authorization)는 이름에서도 알 수 있듯 인가 프로토콜입니다. RFC 9728과 MCP 명세가 다루는 핵심도 인가이며, 이 글 전반에서 "인가"라는 표현을 사용합니다.
RFC 9728이 해결하는 문제
기존 OAuth 2.0 환경에서는 클라이언트가 리소스 서버에 접근하려면 인가 서버의 URL을 미리 알고 있어야 했습니다. 이는 두 가지 문제를 낳습니다.
- 인가 서버 정보가 클라이언트 코드나 설정 파일에 하드코딩됩니다.
- 새로운 리소스 서버를 추가할 때마다 개발자가 수동으로 설정을 변경해야 합니다.
RFC 9728은 리소스 서버가 자신의 인가 요구사항을 직접 공개하게 함으로써 이 의존 관계를 역전시킵니다.
RFC 9728이란?
OAuth 2.0 보호 리소스(Resource Server)가/.well-known/oauth-protected-resource경로에 JSON 메타데이터 문서를 게시하는 IETF 표준입니다. 클라이언트는 이 문서를 읽어 "어느 인가 서버에서 토큰을 받아야 하는가"를 런타임에 자동으로 파악합니다. 약 8.5년의 초안 기간을 거쳐 2025년 4월에 공식 표준으로 발행되었습니다.
OAuth 2.0과 OAuth 2.1의 차이
MCP는 OAuth 2.1을 기반으로 합니다. OAuth 2.0과의 핵심 차이는 다음 두 가지입니다.
| 변경 사항 | OAuth 2.0 | OAuth 2.1 |
|---|---|---|
| PKCE | 선택 사항 | 모든 클라이언트에 필수 |
| Implicit Flow | 사용 가능 | 완전 제거 |
PKCE(Proof Key for Code Exchange, RFC 7636) 는 OAuth 2.0 인가 코드 흐름에서 코드 탈취 공격을 방지하는 보안 확장입니다. 클라이언트가 임의의 코드 검증기를 생성하고 그 해시를 인가 요청에 포함시켜, 탈취된 코드로는 토큰을 발급받을 수 없도록 합니다. 모바일 앱·SPA 환경에서 코드 탈취 공격 사례가 누적되면서 OAuth 2.1에서는 PKCE 사용이 필수화되었습니다.
Zero-Config 인가 탐색 흐름
RFC 9728 기반 인가의 전체 흐름은 다음과 같습니다.
1. MCP 클라이언트 → 인가 토큰 없이 MCP 서버에 첫 요청
2. MCP 서버 → 401 Unauthorized 응답
WWW-Authenticate: Bearer
resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"
3. 클라이언트 → /.well-known/oauth-protected-resource 메타데이터 GET
4. 메타데이터에서 authorization_servers 필드로 인가 서버 URL 획득
5. 인가 서버 → OAuth 2.1 + PKCE 플로우로 액세스 토큰 발급
6. 클라이언트 → Bearer 토큰 포함하여 MCP 서버 재요청 → 성공클라이언트 개발자가 MCP 서버 URL 하나만 알면, 나머지는 모두 자동으로 처리됩니다.
2025년 11월 MCP 명세(2025-11-25)에서는 컨벤션 기반 탐색 방식도 추가되었습니다. WWW-Authenticate 헤더 없이도 클라이언트가 서버 URL에서 직접 /.well-known/oauth-protected-resource 경로를 구성해 조회하는 방식으로, 헤더를 반환하지 않는 서버와도 호환됩니다.
메타데이터 문서의 핵심 필드
/.well-known/oauth-protected-resource 엔드포인트가 반환하는 JSON 문서의 주요 필드는 다음과 같습니다.
| 필드 | 설명 |
|---|---|
resource |
보호 리소스의 식별자 URL |
authorization_servers |
신뢰하는 인가 서버 URL 목록 |
scopes_supported |
지원하는 OAuth 스코프 목록 |
bearer_methods_supported |
지원하는 Bearer 토큰 전달 방식 (header, body 등) |
jwks_uri |
공개키 조회 URI (토큰 서명 검증용) |
{
"resource": "https://api.myservice.com/mcp",
"authorization_servers": ["https://auth.myservice.com"],
"scopes_supported": ["mcp:tools:read", "mcp:tools:write"],
"bearer_methods_supported": ["header"],
"jwks_uri": "https://auth.myservice.com/.well-known/jwks.json"
}MCP 명세와 RFC 9728의 관계
MCP 명세는 OAuth 2.1 위에서 동작하며, RFC 9728을 다음과 같이 의무화합니다.
| 역할 | MCP에서의 대응 | RFC 9728 의무 수준 |
|---|---|---|
| OAuth 리소스 서버 | MCP 서버 | 구현 필수(MUST) |
| OAuth 클라이언트 | MCP 클라이언트 | 인가 서버 탐색 시 반드시 사용(MUST) |
2025년 6월 명세(MCP 2025-06-18)부터 RFC 9728이 의무화되었고, 2026년 3월에는 RFC 8707 Resource Indicators가 필수화되어 토큰 오남용 공격을 방지합니다.
관련 RFC 스택: RFC 9728 혼자로는 완전하지 않습니다. RFC 8414(인가 서버 메타데이터 탐색), RFC 7591(동적 클라이언트 등록), RFC 7636(PKCE), RFC 8707(Resource Indicators)과 결합해 완전한 Zero-Config 인가 스택이 완성됩니다.
도입 전에 알아두면 좋은 트레이드오프
구현에 앞서 핵심 트레이드오프를 파악해두면 좋습니다.
적합한 경우: 여러 MCP 서버를 동적으로 연결하는 AI 에이전트, 클라이언트 설정을 최소화하고 싶은 엔터프라이즈 게이트웨이, 표준 기반 IdP(Auth0, Keycloak, Entra ID 등)를 이미 운영 중인 조직.
주의가 필요한 경우: OAuth 스택 전체를 직접 구현해야 하는 소규모 팀(라이브러리 활용 권장), 로컬 개발 환경(HTTPS 구성 필요), 멀티 홉 에이전트 체인에서의 스코프 관리 복잡성.
실전 적용
예시 1: TypeScript MCP 서버 — mcp-auth 라이브러리 활용
TypeScript 환경에서 가장 빠르게 RFC 9728을 적용하는 방법은 mcp-auth 라이브러리를 활용하는 것입니다. 다음은 Express 기반 MCP 서버에 메타데이터 엔드포인트를 추가하고 Bearer 토큰을 검증하는 예시입니다.
import { MCPAuth } from 'mcp-auth';
import express from 'express';
const app = express();
// RFC 9728 메타데이터 설정
const mcpAuth = new MCPAuth({
mode: 'resource-server',
protectedResources: [{
resource: 'https://api.myservice.com/mcp',
authorizationServers: ['https://auth.myservice.com'],
scopesSupported: ['mcp:tools:read', 'mcp:tools:write'],
bearerMethodsSupported: ['header'],
jwksUri: 'https://auth.myservice.com/.well-known/jwks.json',
}]
});
// /.well-known/oauth-protected-resource 자동 마운트
app.use(mcpAuth.router());
// MCP 툴 엔드포인트 — 토큰 검증 미들웨어 적용
app.use('/mcp', mcpAuth.bearerAuth({
requiredScopes: ['mcp:tools:read']
}), async (req, res) => {
try {
// MCP 툴 목록 응답 (MCP 프로토콜 스키마 형식)
// { tools: [{ name: string, description: string, inputSchema: object }] }
res.json({ tools: [] });
} catch (err) {
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(3000);| 코드 부분 | 역할 |
|---|---|
MCPAuth({ mode: 'resource-server' }) |
RFC 9728 리소스 서버 모드 활성화 |
mcpAuth.router() |
/.well-known/oauth-protected-resource 엔드포인트 자동 등록 |
mcpAuth.bearerAuth({ requiredScopes }) |
JWT 검증 + 스코프 확인 미들웨어 |
클라이언트 측에서는 다음 개념적 흐름으로 동작합니다. 실제 구현 시에는 공식 TypeScript SDK의 README와 OAuth 예시를 참고하는 것을 권장합니다.
// 개념적 흐름 — 실제 SDK API는 공식 README를 참고하세요
//
// 내부적으로 일어나는 일:
// 1. 401 응답 수신
// 2. WWW-Authenticate 헤더(또는 컨벤션 기반)로 메타데이터 URL 탐색
// 3. /.well-known/oauth-protected-resource GET → authorization_servers 획득
// 4. OAuth 2.1 + PKCE 플로우 → 액세스 토큰 발급 및 캐싱
// 5. Bearer 토큰 포함하여 원래 요청 재시도
//
// @modelcontextprotocol/sdk 실제 연결 예시:
// import { Client } from "@modelcontextprotocol/sdk/client/index.js";
// const client = new Client({ name: "my-client", version: "1.0.0" });
// await client.connect(transport); // transport에 OAuth 핸들러를 포함합니다예시 2: Python FastMCP 서버 구현
다음은 Python 환경에서 FastMCP를 활용해 RFC 9728 메타데이터 엔드포인트를 자동으로 제공하는 예시입니다. OAuth21ResourceServer의 실제 API는 FastMCP 버전에 따라 다를 수 있으므로, 구현 전에 FastMCP 공식 저장소에서 최신 API를 확인하는 것을 권장합니다.
from fastmcp import FastMCP
from fastmcp.server.auth import OAuth21ResourceServer
mcp = FastMCP(
name="my-mcp-server",
auth=OAuth21ResourceServer(
resource="https://api.myservice.com/mcp",
authorization_servers=["https://auth.myservice.com"],
jwks_uri="https://auth.myservice.com/.well-known/jwks.json",
required_scopes=["mcp:tools"]
)
)
@mcp.tool()
async def get_data(query: str) -> dict:
"""데이터 조회 툴 — 자동으로 인가 보호됨"""
return {"result": query}
# /.well-known/oauth-protected-resource 자동 생성 및 서빙
if __name__ == "__main__":
mcp.run()| 파라미터 | 설명 |
|---|---|
resource |
이 MCP 서버의 고유 식별자 URL |
authorization_servers |
신뢰하는 인가 서버 목록 |
jwks_uri |
JWT 서명 검증에 사용할 공개키 엔드포인트 |
required_scopes |
모든 툴에 공통으로 요구하는 스코프 |
예시 3: 엔터프라이즈 MCP 게이트웨이 패턴 (Keycloak 연동)
대규모 엔터프라이즈 환경에서는 개별 MCP 서버가 각각 인가를 처리하는 대신, 중앙 게이트웨이 패턴이 더 적합합니다. 이 패턴에서는 게이트웨이 하나가 RFC 9728 메타데이터를 게시하고 Keycloak과 연동하며, 뒤쪽의 내부 API는 클라이언트에 노출되지 않습니다.
사용자 → AI 호스트(Claude/GPT)
↓
MCP 클라이언트
↓ (1. 첫 요청 → 401)
MCP 게이트웨이 ← /.well-known/oauth-protected-resource 게시
↓ (2. 메타데이터 탐색 → Keycloak URL 획득)
Keycloak (인가 서버)
↓ (3. OAuth 2.1 + PKCE → 토큰 발급)
MCP 게이트웨이 ← (4. Bearer 토큰으로 재요청)
↓ (토큰 검증 후 라우팅)
┌─────────┼─────────┐
GitHub API Jira API Notion API게이트웨이의 RFC 9728 메타데이터 예시:
{
"resource": "https://gateway.corp.com/mcp",
"authorization_servers": ["https://keycloak.corp.com/realms/ai-agents"],
"scopes_supported": [
"mcp:github:read",
"mcp:jira:write",
"mcp:notion:read"
],
"bearer_methods_supported": ["header"],
"jwks_uri": "https://keycloak.corp.com/realms/ai-agents/protocol/openid-connect/certs"
}이 패턴에서 클라이언트는 게이트웨이 URL 하나만 알면 됩니다. 내부 API가 추가되거나 변경되어도 클라이언트 설정을 수정할 필요가 없습니다.
예시 4: Azure AD (Entra ID) + MCP 서버 통합
마지막으로, Microsoft가 공개한 레퍼런스 아키텍처를 살펴봅니다. Azure Container Apps 기반 MCP 서버에 Entra ID를 인가 서버로 연결하는 패턴입니다. 아래 코드에서 {tenant-id}는 Azure Portal → App registrations 화면에서 확인할 수 있는 테넌트 ID 값으로 대체하면 됩니다.
// Azure Container Apps에서 실행되는 MCP 서버
const mcpAuth = new MCPAuth({
mode: 'resource-server',
protectedResources: [{
resource: 'https://my-mcp-app.azurecontainerapps.io/mcp',
// {tenant-id}: Azure Portal > App registrations에서 확인
authorizationServers: [
'https://login.microsoftonline.com/{tenant-id}/v2.0'
],
scopesSupported: ['api://my-mcp-app/mcp.tools'],
jwksUri: 'https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys',
}]
});Resource Indicators (RFC 8707): 2026년 3월부터 MCP 명세에서 필수화된 기능입니다. 인가 요청 시
resource파라미터로 토큰을 사용할 서버를 명시하면, 발급된 토큰이 해당 서버 전용으로 제한됩니다. 토큰을 탈취해 다른 서버에 재사용하는 "토큰 오남용(mis-redemption) 공격"을 방지합니다. 자세한 적용 방법은 다음 글에서 다룹니다.
장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 진정한 플러그 앤 플레이 | 클라이언트는 서버 URL만 알면 나머지 인가 설정을 모두 자동 탐색합니다. 수십 개의 MCP 서버를 연결해도 각 서버의 인가 서버를 코드에 하드코딩할 필요가 없습니다. |
| 표준 기반 상호운용성 | RFC 9728 + RFC 8414 + OAuth 2.1 조합으로 Auth0, Okta, Cognito, Keycloak, Entra ID 등 모든 표준 준수 IdP와 연동할 수 있습니다. 벤더 종속이 없습니다. |
| 동적 클라이언트 등록(DCR) 결합 시 완전 자동화 | RFC 7591과 함께 사용하면 클라이언트가 인가 서버에 사전 등록 없이 자동으로 Client ID를 획득할 수 있어 온보딩 마찰이 사라집니다. |
| 보안 강화 | JWKS URI가 메타데이터에 포함되어 토큰 서명 검증이 표준화되고, RFC 8707 Resource Indicators와 결합하면 토큰 오남용 공격을 방지할 수 있습니다. |
| AI 에이전트 멀티 서버 연결 최적화 | 에이전트가 GitHub, Jira, Notion을 동적으로 연결할 때 각 서버의 인가 요구사항을 런타임에 자동 파악하므로 에이전트 코드가 단순해집니다. |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 구현 복잡도 | OAuth 2.1 + PKCE + RFC 9728 + RFC 8414를 모두 올바르게 구현하는 것은 진입 장벽이 높습니다. | mcp-auth, FastMCP 등 검증된 라이브러리를 사용하는 것을 권장합니다. |
| Discovery Poisoning 위험 | 메타데이터 문서가 잘못 캐시되면 이후 모든 연결 시도가 차단되는 버그가 실제 클라이언트(Claude Code)에서 보고된 바 있습니다. | 캐시 TTL을 보수적으로 설정하고, 연결 실패 시 캐시를 무효화하는 로직을 추가하는 것을 권장합니다. |
| 설정 이식성 부재 | MCP 클라이언트를 교체하면 인가 설정을 처음부터 다시 진행해야 합니다. | 인가 설정을 외부 설정 파일로 분리해 관리하는 것이 도움이 됩니다. |
| HTTPS 필수 | 메타데이터 엔드포인트는 반드시 HTTPS로 서빙해야 해서 로컬 개발 환경 구성이 번거롭습니다. | mkcert로 로컬 HTTPS 인증서를 발급하거나, ngrok 터널을 활용하는 방법을 고려해볼 수 있습니다. |
| 토큰 유효 범위 관리 | 멀티 홉 에이전트 체인에서 각 구간의 스코프와 인가 컨텍스트를 일관되게 유지하는 것이 복잡합니다. | RFC 8707 Resource Indicators를 적용해 각 토큰의 사용 범위를 명시적으로 제한하는 것을 권장합니다. |
| 실제 구현 격차 | 공개 인터넷의 상당수 MCP 서버가 인가 없이 운영 중이라는 보고가 있습니다.[^1] | 서드파티 MCP 서버 연동 시 인가 구현 여부를 먼저 확인하는 것이 좋습니다. |
[^1]: GitGuardian의 OAuth for MCP 보고서를 참고한 내용입니다. 구체적인 수치와 원본 데이터는 해당 보고서를 직접 확인하는 것을 권장합니다.
실무에서 가장 흔한 실수
- 메타데이터 캐싱 없이 매 요청마다
/.well-known/oauth-protected-resource를 조회하는 것 — 메타데이터 문서는 자주 변경되지 않으므로 적절한 TTL로 캐싱해야 합니다. 그렇지 않으면 불필요한 레이턴시와 인가 서버 부하가 발생합니다. resource파라미터 없이 토큰을 요청하는 것 — 2026년 3월부터 MCP 명세에서 RFC 8707 Resource Indicators가 필수화되었습니다.resource파라미터를 인가 요청과 토큰 요청 모두에 포함하지 않으면 명세 비준수 상태가 됩니다.- 스코프를 과도하게 광범위하게 설정하는 것 —
mcp:*같은 와일드카드 스코프 대신mcp:tools:read,mcp:resources:write처럼 세분화된 스코프를 정의하면 최소 권한 원칙을 지킬 수 있고 감사 추적도 용이해집니다.
마치며
RFC 9728은 클라이언트-서버 간 인가 설정의 의존 방향을 뒤집습니다. 기존에는 클라이언트가 서버의 인가 서버를 "미리 알아야" 했다면, 이제는 서버가 자신의 인가 요구사항을 공개하고 클라이언트가 런타임에 이를 탐색합니다. 이 패러다임 전환이 수십 개의 MCP 서버를 연결할 때도 인가 설정 하드코딩을 없애주는 핵심 원리입니다.
지금 바로 시작해볼 수 있는 3단계:
- 서버 준비:
pnpm add mcp-auth(TypeScript) 또는pip install fastmcp(Python) 설치 후, 위의 예시 코드를 참고해/.well-known/oauth-protected-resource엔드포인트를 서버에 추가해볼 수 있습니다. Keycloak이나 Auth0 무료 티어를 인가 서버로 활용하면 별도 서버 없이도 시작이 가능합니다. - 탐색 흐름 검증:
curl https://your-mcp-server/.well-known/oauth-protected-resource로 메타데이터 문서가 올바르게 반환되는지 확인한 뒤,@modelcontextprotocol/sdk의 MCP 클라이언트로 실제 Zero-Config 연결을 시도해볼 수 있습니다. - RFC 8707 적용: 인가 요청 시
resource파라미터를 추가해 Resource Indicators를 활성화하면 토큰 오남용 공격 방어까지 갖춘 프로덕션 수준의 구현이 완성됩니다. 예를 들어, 인가 요청에resource=https://api.myservice.com/mcp를 포함시키면 발급된 토큰이 해당 서버 전용으로 제한됩니다. 자세한 적용 방법은 다음 글에서 다룹니다.
다음 글: RFC 8707 Resource Indicators와 OAuth 2.1 PKCE를 결합해 멀티 홉 AI 에이전트 체인에서 토큰 오남용 공격을 방어하는 방법
참고 자료
- RFC 9728 - OAuth 2.0 Protected Resource Metadata | IETF Datatracker
- Authorization - Model Context Protocol 공식 명세 (2025-06-18)
- Authorization - Model Context Protocol Draft
- What's New In The 2025-11-25 MCP Authorization Spec · Den Delimarsky
- The New MCP Authorization Specification (2026-04, Resource Indicators)
- Introducing RFC 9728: Say hello to standardized OAuth 2.0 resource metadata | WorkOS
- What Is Protected Resource Metadata (PRM) and How It Works | Descope
- Diving Into the MCP Authorization Specification | Descope
- Technical Deconstruction of MCP Authorization: OAuth 2.1 and IETF RFC Deep Dive
- Let's fix OAuth in MCP · Aaron Parecki
- How MCP Leverages OAuth 2.1 and RFC 9728 for Authorization | Gentoro
- Part Two: MCP Authorization The Hard Way | Solo.io
- Open Protocols for Agent Interoperability Part 2: Authentication on MCP | AWS
- Building a Secure MCP Server with OAuth 2.1 and Azure AD | Microsoft ISE Blog
- MCP Auth — Plug-and-play auth for MCP servers
- OAuth for MCP - Emerging Enterprise Patterns | GitGuardian
- MCP's 2026 roadmap makes enterprise readiness a top priority | WorkOS