Logto로 MCP 기반 앱에 인증 (Authentication) 활성화하기
이 가이드는 mcp-auth를 사용하여 Logto를 MCP 서버와 통합하는 방법을 안내합니다. 이를 통해 표준 OpenID Connect 플로우를 사용하여 사용자를 인증하고, 안전하게 아이덴티티 정보를 가져올 수 있습니다.
이 가이드에서 배우게 될 내용:
- MCP 서버의 인가 (Authorization) 서버로 Logto를 구성하는 방법
- MCP 서버에 “whoami” 도구를 설정하여 현재 사용자의 아이덴티티 클레임 (Claims)을 반환하는 방법
- MCP Inspector (MCP 클라이언트)로 플로우를 테스트하는 방법
이 튜토리얼을 마치면, MCP 서버는 다음과 같이 동작합니다:
- Logto 테넌트에서 사용자를 인증 (Authentication)합니다.
- "whoami" 도구 호출 시 아이덴티티 클레임 (
sub
,username
,name
,email
등)을 반환합니다.
통합이 완료되면, MCP Inspector를 웹 앱 등 여러분만의 MCP 클라이언트로 교체하여 MCP 서버가 제공하는 도구와 리소스에 접근할 수 있습니다.
사전 준비 사항
- Logto Cloud (또는 자체 호스팅) 테넌트
- Node.js 또는 Python 환경
아키텍처 이해하기
- MCP 서버: MCP 클라이언트에 도구와 리소스를 제공하는 서버입니다.
- MCP 클라이언트: 인증 (Authentication) 플로우를 시작하고 통합을 테스트하는 데 사용되는 클라이언트입니다. 이 가이드에서는 MCP Inspector를 클라이언트로 사용합니다.
- Logto: OpenID Connect 제공자 (인가 (Authorization) 서버) 역할을 하며 사용자 아이덴티티를 관리합니다.
비공식 시퀀스 다이어그램은 전체 프로세스의 흐름을 보여줍니다:
MCP가 빠르게 발전하고 있기 때문에, 위 다이어그램이 최신 정보를 완전히 반영하지 않을 수 있습니다. 최신 정보는 mcp-auth 문서를 참고하세요.
Logto에서 앱 설정하기
- Logto 콘솔에 로그인하세요.
- 애플리케이션 → 애플리케이션 생성 → 프레임워크 없이 앱 생성으로 이동하세요.
- 유형 선택: 싱글 페이지 앱.
- 앱 이름 및 기타 필수 항목을 입력한 후 애플리케이션 생성을 클릭하세요.
- App ID와 발급자 (Issuer) 엔드포인트를 저장하고 복사하세요.
MCP 서버 설정하기
프로젝트 생성 및 의존성 설치
- Python
- Node.js
mkdir mcp-server
cd mcp-server
uv init # 또는 원하는 프로젝트 구조를 사용하세요
uv add "mcp[cli]" starlette uvicorn mcpauth # 또는 원하는 패키지 매니저를 사용하세요
mkdir mcp-server
cd mcp-server
npm init -y
npm install @modelcontextprotocol/sdk express mcp-auth # 또는 원하는 패키지 매니저를 사용하세요
Logto로 MCP 인증 구성하기
앞서 복사한 발급자 (Issuer) 엔드포인트를 <your-logto-issuer-endpoint>
로 교체하세요.
- Python
- Node.js
whoami.py
에서:
from mcpauth import MCPAuth
from mcpauth.config import AuthServerType
from mcpauth.utils import fetch_server_config
auth_issuer = '<your-logto-issuer-endpoint>'
auth_server_config = fetch_server_config(auth_issuer, type=AuthServerType.OIDC)
mcp_auth = MCPAuth(server=auth_server_config)
whoami.js
에서:
import { MCPAuth, fetchServerConfig } from 'mcp-auth';
const authIssuer = '<your-logto-issuer-endpoint>';
const mcpAuth = new MCPAuth({
server: await fetchServerConfig(authIssuer, { type: 'oidc' }),
});
토큰 검증 구현하기
액세스 토큰 (Access token)을 검증하고 사용자 정보를 가져오기 위해, 다음과 같이 액세스 토큰 검증을 구현해야 합니다:
- Python
- Node.js
import requests
from mcpauth.types import AuthInfo
def verify_access_token(token: str) -> AuthInfo:
endpoint = auth_server_config.metadata.userinfo_endpoint
response = requests.get(
endpoint,
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
data = response.json()
return AuthInfo(
token=token,
subject=data.get("sub"),
issuer=auth_server_config.metadata.issuer,
claims=data,
)
const verifyToken = async (token) => {
const { userinfoEndpoint, issuer } = mcpAuth.config.server.metadata;
const response = await fetch(userinfoEndpoint, {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) throw new Error('Token verification failed');
const userInfo = await response.json();
return {
token,
issuer,
subject: userInfo.sub,
claims: userInfo,
};
};
"whoami" 도구 구현하기
이제, 클라이언트가 보낸 액세스 토큰 (Access token)으로 userinfo 엔드포인트를 요청하여 현재 사용자의 아이덴티티 클레임 (Claims)을 반환하는 "whoami" 도구를 구현해봅시다.
현재 SDK 버전에서 Streamable HTTP 트랜스포트에 대한 공식 지원이 없으므로 예제에서는 SSE 트랜스포트를 사용합니다. 이론적으로는 HTTP 호환 트랜스포트라면 어떤 것이든 사용할 수 있습니다.
- Python
- Node.js
from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount
from starlette.middleware import Middleware
mcp = FastMCP("WhoAmI")
@mcp.tool()
def whoami() -> dict:
"""
현재 사용자의 아이덴티티 정보를 반환합니다.
"""
return (
mcp_auth.auth_info.claims
if mcp_auth.auth_info
else {"error": "Not authenticated"}
)
bearer_auth = Middleware(mcp_auth.bearer_auth_middleware(verify_access_token))
app = Starlette(
routes=[
mcp_auth.metadata_route(), # OIDC 메타데이터를 디스커버리용으로 제공
Mount('/', app=mcp.sse_app(), middleware=[bearer_auth]),
],
)
서버 실행:
uvicorn whoami:app --host 0.0.0.0 --port 3001
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import express from 'express';
// MCP 서버 생성 및 whoami 도구 등록
const server = new McpServer({ name: 'WhoAmI', version: '0.0.0' });
server.tool('whoami', ({ authInfo }) => ({
content: [
{ type: 'text', text: JSON.stringify(authInfo?.claims ?? { error: 'Not authenticated' }) },
],
}));
// Express 앱 & MCP Auth 미들웨어
const app = express();
app.use(mcpAuth.delegatedRouter());
app.use(mcpAuth.bearerAuth(verifyToken));
// SSE 트랜스포트 (SDK 문서 참고)
const transports = {};
app.get('/sse', async (_req, res) => {
const transport = new SSEServerTransport('/messages', res);
transports[transport.sessionId] = transport;
res.on('close', () => delete transports[transport.sessionId]);
await server.connect(transport);
});
app.post('/messages', async (req, res) => {
const sessionId = String(req.query.sessionId);
const transport = transports[sessionId];
if (transport) await transport.handlePostMessage(req, res, req.body);
else res.status(400).send('No transport found for sessionId');
});
app.listen(3001);
서버 실행:
node whoami.js
통합 테스트하기
-
MCP 서버를 시작하세요.
-
MCP Inspector를 시작하세요.
현재 MCP Inspector 구현의 한계로 인해, mcp-auth에서 포크된 버전을 사용해야 합니다:
git clone https://github.com/mcp-auth/inspector.git
cd inspector
npm install
npm run dev그런 다음, 터미널에 표시된 URL을 엽니다.
-
MCP Inspector에서:
- Transport Type:
SSE
- URL:
http://localhost:3001/sse
- OAuth Client ID: Logto App ID를 붙여넣으세요.
- Auth Params:
{"scope": "openid profile email"}
- Redirect URI: 이 URL은 자동으로 채워집니다. 복사하세요.
- Transport Type:
-
Logto 콘솔에서 앞서 생성한 애플리케이션을 찾아 상세 페이지를 열고, 복사한 redirect URI를 설정 / Redirect URIs 섹션에 붙여넣으세요. 변경 사항을 저장하세요.
-
MCP Inspector로 돌아가 Connect를 클릭하세요. Logto 로그인 경험으로 리디렉션됩니다.
로그인을 완료하면 MCP Inspector로 다시 리디렉션됩니다. Tools -> List Tools -> whoami -> Run Tool로 이동하세요.
아래와 같이 사용자 클레임 (Claims)이 표시되어야 합니다:
{
"sub": "user_XXXX",
"username": "alice",
"name": "Alice Smith",
"email": "[email protected]"
}
- Python
- Node.js
전체 MCP 서버 코드는 mcp-auth/python 저장소에서 확인할 수 있습니다.
전체 MCP 서버 코드는 mcp-auth/js 저장소에서 확인할 수 있습니다.