MCP 서버
Claude Code, Cursor 등 AI 코딩 도구에서 QA Note 이슈를 OAuth 한 번으로 연결하고 조회·수정하세요
목차
- MCP란?
- 한 번에 설치
- 인증 방식 한눈에 보기
- 방법 A. Remote HTTP + OAuth 원클릭 (권장)
- Claude Code
- Cursor
- Codex (OpenAI)
- OAuth 플로우 상세
- 방법 B. 로컬 바이너리 (stdio · npx)
- Claude Code
- Cursor / Codex
- 인증 플로우
- 방법 C. API Key (CI · 헤드리스 · 대체 경로)
- Remote HTTP에 API Key 부착
- stdio 바이너리에 API Key 부착
- 환경변수 (stdio 바이너리용)
- 사용 가능한 도구
- 조회 도구
- 쓰기 도구
- 프로젝트 목록 조회
- 언제 쓰나
- 입력
- 응답 필드
- 권한과 멀티 조직
- LLM 사용 시나리오
- 추천 워크플로우
- 동작 확인
- 트러블슈팅
- OAuth 브라우저 창이 뜨지 않음 (Remote HTTP)
- "Unauthorized" 에러
- `invalid_grant — Refresh token reuse detected`
- MCP 서버가 연결되지 않음 (stdio 바이너리)
- 토큰/세션 초기화
- SSH · Docker 등 브라우저 불가 환경
MCP란?
MCP(Model Context Protocol)는 AI 코딩 도구가 외부 데이터에 접근할 수 있게 해주는 표준 프로토콜입니다. QA Note MCP 서버 연결은 핸드오프 경로입니다 — Claude Code, Cursor, Codex 의 에이전트가 이슈를 받아 수정하는 동안 진행 상태가 QA Note에 자동으로 기록되므로, 개발자는 쓰던 도구를 떠나지 않고도 기록이 남습니다.
QA Note가 수집하는 풍부한 기술 메타데이터(콘솔 로그, 네트워크 요청, JS 에러, 성능 메트릭, React 컴포넌트 트리, 유저 액션, 엘리먼트 스타일, DOM 스냅샷 등)를 에이전트에 구조화해서 전달하므로, 이슈가 곧 바로 착수 가능한 작업 지시서가 됩니다. 경계도 분명합니다: 에이전트는 수정 제출까지만 기록할 수 있고, 검수 완료·보고 완료 전환은 사람과 리포트 발행의 몫입니다.
한 번에 설치
사용 중인 MCP 클라이언트를 고르면 복사 또는 설치 링크가 바로 실행됩니다. 처음 연결 시 브라우저 OAuth 동의창이 한 번 열립니다.
사용 중인 MCP 클라이언트 하나만 고르세요
아래 카드에서 복사 또는 설치 액션 한 번이면 QA Note MCP 가 붙습니다. 처음 연결 시 브라우저 OAuth 동의창이 한 번 열립니다.
Codex
CLI/IDE 공용 config.toml 등록 · OAuth 로그인
터미널에서 실행하면 서버 등록 후 OAuth 로그인 브라우저가 열립니다. IDE 확장도 같은 설정을 사용합니다.
공식 설치 가이드Claude Desktop
mcpServers 설정 JSON 복사
설정 파일 위치: macOS ~/Library/Application Support/Claude/claude_desktop_config.json · Windows %APPDATA%\Claude\claude_desktop_config.json
공식 설치 가이드Cursor
Deep link 한 번 클릭으로 Cursor 에 등록
Cursor 미설치 환경이면 "설정 JSON 복사" 로 ~/.cursor/mcp.json 에 직접 붙여넣으세요.
공식 설치 가이드Windsurf
mcpServers 설정 JSON 복사 (serverUrl 키)
Windsurf → Cascade → MCP Servers → Add Server 에 붙여넣으세요.
공식 설치 가이드ChatGPT
Developer Mode connector URL 복사
ChatGPT → Settings → Connectors → Developer Mode 활성화 → Add MCP server 에 붙여넣으세요.
공식 설치 가이드인증 방식 한눈에 보기
| 방식 | 언제 쓰나 | 키 발급 | 추천도 |
|---|---|---|---|
| Remote HTTP + OAuth | 평소 데스크톱 환경 (Claude Code, Cursor, Codex 등) | 필요 없음 (브라우저 동의 한 번) | ★★★ 기본값 |
| stdio 로컬 바이너리 | 조직 프록시로 HTTP MCP가 막힌 경우 · 오프라인 캐시가 필요한 경우 | 필요 없음 (동일한 OAuth) | ★★ |
| API Key | CI/CD · 헤드리스 서버 · OAuth 미지원 클라이언트 | 대시보드에서 qn_... 수동 발급 | ★ (대체 경로) |
기본은 OAuth 원클릭입니다. Authorization 헤더를 손으로 붙일 필요가 없습니다. Claude Code는 표준 MCP 디스커버리(
/.well-known/oauth-protected-resource)를 통해 QA Note 인증 서버를 자동 발견하고, Dynamic Client Registration(RFC 7591) → Authorization Code + PKCE S256 → Access/Refresh Token 교환을 수행합니다.
방법 A. Remote HTTP + OAuth 원클릭 (권장)
QA Note가 호스팅하는 HTTP MCP 엔드포인트(https://qanote.app/api/mcp)에 바로 연결합니다. 별도 바이너리 설치도, API Key 복사 붙여넣기도 없습니다.
Claude Code
터미널에 한 줄:
claude mcp add --transport http qanote https://qanote.app/api/mcp
등록 직후 Claude Code에서 /mcp 를 실행하면 브라우저가 자동으로 열리고, QA Note 로그인 · 동의(consent) 화면 한 번으로 인증이 끝납니다. 발급된 access/refresh token은 Claude Code 쪽에 안전하게 저장되며, 이후부터는 투명하게 갱신됩니다.
Cursor
~/.cursor/mcp.json 에 추가:
{
"mcpServers": {
"qanote": {
"url": "https://qanote.app/api/mcp"
}
}
}
headers 를 쓰지 않습니다. 처음 도구 호출 시 Cursor가 브라우저 OAuth 창을 띄워 줍니다.
Codex (OpenAI)
Codex CLI 설정 파일의 mcpServers 에 동일하게 추가:
{
"mcpServers": {
"qanote": {
"url": "https://qanote.app/api/mcp"
}
}
}
OAuth 플로우 상세
- 클라이언트가
https://qanote.app/api/mcp호출 →401 Unauthorized+WWW-Authenticate: Bearer resource_metadata=...응답 - 클라이언트가
/.well-known/oauth-protected-resource(RFC 9728) →/.well-known/oauth-authorization-server(RFC 8414) 를 차례로 읽어 인증 서버 메타데이터 획득 - Dynamic Client Registration (RFC 7591) 로
client_id자동 발급 (사용자 조치 불필요) - Authorization Code + PKCE S256 플로우로 브라우저에서 로그인 + MCP 접근 동의
/api/oauth/token에서 access token (mcp·offline_access스코프,aud=https://qanote.app/api/mcp바인딩 — RFC 8707) + refresh token 교환- 이후 요청에서 동일 refresh token 으로 access token 만 조용히 갱신됨
이슈 상세 페이지의 "MCP 프롬프트" 버튼으로 복사한 프롬프트에는 이슈 permalink가 포함되어, LLM이 resolve_by_url 도구 하나로 전체 컨텍스트를 당겨옵니다.
방법 B. 로컬 바이너리 (stdio · npx)
조직 네트워크에서 HTTP MCP가 막혀 있거나, 오프라인/SSH 환경에서도 자격증명을 로컬 캐시해 쓰고 싶을 때 사용합니다. 인증 자체는 동일한 OAuth 브라우저 로그인입니다.
Claude Code
claude mcp add qanote -- npx -y @qanote/mcp-server
Cursor / Codex
{
"mcpServers": {
"qanote": {
"command": "npx",
"args": ["-y", "@qanote/mcp-server"]
}
}
}
인증 플로우
- MCP 서버 최초 실행 시 저장된 크레덴셜이 없으면 브라우저가 자동으로 열립니다
- QA Note 계정으로 로그인 (이미 로그인되어 있으면 동의만 진행)
- 발급된 토큰이
~/.qanote/credentials.json에chmod 600으로 저장됩니다 - 이후 실행부터는 저장된 토큰을 자동 재사용하고, access token 만 조용히 갱신합니다
자격증명을 초기화하려면:
rm ~/.qanote/credentials.json
방법 C. API Key (CI · 헤드리스 · 대체 경로)
브라우저를 띄울 수 없는 환경(CI/CD, 컨테이너, 원격 서버)이나 OAuth 를 지원하지 않는 MCP 클라이언트에서만 사용하세요.
- QA Note 대시보드 → 조직 설정 → API Keys 탭
- "새 API Key" → 이름(예:
QA Note 운영 서버) · 만료일 · 권한 선택 → "생성" - 표시되는 키(
qn_...)를 복사하여 안전한 곳에 저장 (한 번만 노출됨)
운영 서버에서 데이터를 가져오기만 한다면 기본값인 projects:read + issues:read 만 사용하세요. 상태 변경이나 댓글 작성까지 자동화할 때만 issues:write 를 추가합니다. 운영 서버와 개발 도구의 자격증명을 분리할 수 있도록, 서버에는 API Key를 쓰고 Claude Code 같은 대화형 도구에는 OAuth를 별도 연결하는 구성을 권장합니다.
Remote HTTP에 API Key 부착
claude mcp add --transport http qanote https://qanote.app/api/mcp \
--header "Authorization: Bearer qn_발급한_키"
Cursor / Codex JSON:
{
"mcpServers": {
"qanote": {
"url": "https://qanote.app/api/mcp",
"headers": { "Authorization": "Bearer qn_발급한_키" }
}
}
}
stdio 바이너리에 API Key 부착
{
"mcpServers": {
"qanote": {
"command": "npx",
"args": ["-y", "@qanote/mcp-server"],
"env": {
"QANOTE_API_KEY": "qn_발급한_키"
}
}
}
}
환경변수 (stdio 바이너리용)
| 변수 | 필수 | 설명 | 기본값 |
|---|---|---|---|
QANOTE_API_KEY | 선택 | API Key (qn_...). 미설정 시 OAuth 브라우저 로그인 사용 | — |
QANOTE_URL | 선택 | QA Note 서버 URL | https://qanote.app |
Remote HTTP 방식은 클라이언트가 직접 url 을 지정하므로 환경변수가 필요 없습니다.
사용 가능한 도구
조회 도구
| 도구 | 설명 |
|---|---|
resolve_by_url | 이슈 permalink 하나로 이슈 + 기술 컨텍스트를 한 번에 조회 (권장 진입점) |
list_projects | 접근 가능한 프로젝트 목록 |
search_issues | 이슈 검색 및 필터링 (상태·우선순위·라벨·검색어) |
list_issue_queue | 터미널 LLM용 순차 처리 큐 |
get_issue | 이슈 상세 + 메타데이터 요약 |
list_comments | 이슈 코멘트 목록 조회 |
get_comments | 이슈 코멘트 목록 조회 (list_comments alias) |
get_console_logs | 브라우저 콘솔 로그 (에러/경고 우선 정렬) |
get_network_logs | 네트워크 요청 로그 (기본 에러만, 전체 조회 가능) |
get_user_actions | 이슈 발생 전 사용자 행동을 자연어로 변환 |
get_tech_context | JS 에러·성능·React 트리·환경 정보 통합 조회 |
get_element_styles | computed style · 박스 모델 · parent/sibling gap |
get_performance_metrics | Web Vitals · Navigation Timing |
get_environment_info | 브라우저·OS·네트워크·GPU·폰트 등 |
get_js_errors | 런타임 에러 + stack trace |
get_react_component_tree | React 컴포넌트 트리 스냅샷 |
get_storage | localStorage · sessionStorage · cookies (마스킹) |
get_dom_snapshot | DOM outerHTML (스크립트·입력값 마스킹) |
get_screenshots | 스크린샷 URL + 이미지 콘텐츠 embed |
쓰기 도구
| 도구 | 설명 |
|---|---|
claim_issue | 작업 시작 표시: 상태를 in_progress로 변경하고 시작 코멘트 추가 |
complete_issue | 작업 완료 기록: PR/배포/검증 내용을 남기고 resolved 또는 closed 처리 |
update_issue | 이슈 상태·우선순위 변경 |
add_comment | 이슈에 코멘트 추가 |
프로젝트 목록 조회
list_projects는 이슈 URL이 없는 상태에서 LLM이 QA Note 작업을 시작할 때 쓰는 첫 번째 탐색 도구입니다.
언제 쓰나
- "QA Note에서 프로젝트 목록 보여줘"
- "studiobaton 조직의 프로젝트만 보여줘"
- "qa-note 프로젝트의 critical 이슈 찾아줘"
- "내가 접근 가능한 프로젝트 중 다음 처리할 이슈 큐 보여줘"
이슈 permalink가 이미 있다면 list_projects보다 resolve_by_url을 먼저 사용하세요.
입력
| 파라미터 | 필수 | 설명 |
|---|---|---|
organization_slug | 선택 | 특정 조직의 프로젝트만 반환. 예: studiobaton |
응답 필드
list_projects는 JSON 배열을 반환합니다.
| 필드 | 설명 |
|---|---|
id | 이후 search_issues, get_issue, list_issue_queue 등에 넘길 프로젝트 ID |
name | 프로젝트 표시 이름 |
slug | 대시보드 URL의 프로젝트 slug |
createdAt | 프로젝트 생성 시각 |
organizationId | 조직 ID |
organizationSlug | 조직 slug. 멀티 조직 구분에 사용 |
organizationName | 조직 표시 이름 |
예시:
[
{
"id": "018f2f4b-...",
"name": "QA Note",
"slug": "qa-note",
"description": null,
"createdAt": "2026-04-21T08:13:44.000Z",
"organizationId": "018f2d10-...",
"organizationSlug": "studiobaton",
"organizationName": "Studio Baton"
}
]
권한과 멀티 조직
- OAuth 연결은 사용자 단위입니다. 여러 조직에 속해 있으면 접근 가능한 모든 active 조직의 프로젝트가 반환됩니다.
- API Key 연결은 키가 발급된 조직의 프로젝트만 반환합니다.
- 서로 다른 조직에 같은 project
slug가 있을 수 있습니다. LLM은slug만으로 프로젝트를 확정하지 말고organizationSlug + slug또는id로 확정해야 합니다. - 조직이 애매하면 LLM은 첫 번째 결과를 임의로 고르지 말고 사용자에게 어느 조직인지 확인해야 합니다.
LLM 사용 시나리오
사용자: "QA Note에서 프로젝트 목록 보여줘"
LLM: list_projects({}) 호출 → 조직명/조직 slug/프로젝트명/프로젝트 slug 중심으로 요약
사용자: "studiobaton 조직의 QA Note 프로젝트 open 이슈 보여줘"
LLM:
1. list_projects({ "organization_slug": "studiobaton" })
2. slug 또는 name이 QA Note인 프로젝트의 id 선택
3. search_issues({ "project_id": "<id>", "status": "open" })
사용자: "qa-note 프로젝트 critical 이슈 찾아줘"
LLM:
1. list_projects({})
2. slug가 qa-note인 프로젝트가 여러 개면 "studiobaton/qa-note와 client-a/qa-note 중 어느 조직인가요?"라고 확인
3. 확정된 project_id로 search_issues 호출
사용자: "내가 접근 가능한 qa-note의 다음 작업 큐 보여줘"
LLM:
1. list_projects({})
2. organizationSlug + slug로 프로젝트 확정
3. list_issue_queue({ "project_id": "<id>", "status": "open", "sort": "board_order" })
추천 워크플로우
AI 코딩 도구에서 자연어로 요청하면 됩니다:
# 이슈 검색
"QA Note에서 critical 이슈 찾아줘"
# 디버깅 컨텍스트 확인
"이슈 #42의 콘솔 에러와 네트워크 로그 보여줘"
# 유저 행동 재현
"이슈 #15에서 사용자가 어떤 행동을 했는지 알려줘"
# 이슈 업데이트
"이슈 #42를 resolved로 변경하고, 수정 내용을 코멘트로 남겨줘"
최적의 디버깅 순서:
- 이슈 상세 페이지의 "MCP 프롬프트" 버튼으로 permalink 포함 프롬프트 복사 →
resolve_by_url한 방 - 보조로
get_tech_context·get_console_logs·get_network_logs로 깊이 파기 - 해결 후
update_issue로 상태 변경 +add_comment로 원인/수정 기록
동작 확인
설정 후 AI 도구에서:
QA Note에서 프로젝트 목록 보여줘
프로젝트 목록이 정상적으로 출력되면 설정 완료입니다.
트러블슈팅
OAuth 브라우저 창이 뜨지 않음 (Remote HTTP)
- 방화벽/팝업 차단 환경에서는 Claude Code 가 터미널에 출력하는
authorizeURL 을 직접 브라우저에 붙여넣어 로그인 · 동의를 마치면 됩니다. 완료되면 토큰이 자동 저장됩니다. - 회사 프록시가
.well-known/oauth-*디스커버리를 막는 경우 → 방법 B 의 stdio 바이너리로 전환하거나, 방법 C 의 API Key 로 우회하세요.
"Unauthorized" 에러
- OAuth 방식: 저장된 토큰이 만료/폐기된 경우입니다. MCP 서버를 재시작하면 브라우저 로그인이 다시 시작됩니다. stdio 바이너리는
~/.qanote/credentials.json을 삭제. - API Key 방식: 키가
qn_로 시작하는지, 대시보드에서 폐기되지 않았는지 확인. 다른 조직의 리소스에 접근하려면 해당 조직에서 별도 키를 발급하세요.
invalid_grant — Refresh token reuse detected
구버전 OAuth 서버가 refresh token rotation 을 수행하던 시기에 발급된 토큰에서 발생할 수 있는 과도기 오류입니다. 최신 서버는 MCP refresh token 을 교체하지 않으므로 같은 토큰을 반복 사용해도 정상 갱신됩니다. 문제가 계속되면 클라이언트의 저장된 OAuth 토큰을 한 번만 삭제한 뒤 다시 연결하세요.
MCP 서버가 연결되지 않음 (stdio 바이너리)
npx -y @qanote/mcp-server를 터미널에서 직접 실행하여 정상 기동 여부 확인- Node.js 20 이상 필요
QANOTE_URL·QANOTE_API_KEY가 MCP 설정의env에 올바르게 포함됐는지 확인
토큰/세션 초기화
# stdio 바이너리
rm ~/.qanote/credentials.json
# Remote HTTP (Claude Code)
claude mcp remove qanote
claude mcp add --transport http qanote https://qanote.app/api/mcp
SSH · Docker 등 브라우저 불가 환경
→ 방법 C (API Key) 를 사용하세요. CI/CD 에서는 환경변수 또는 --header 주입이 가장 안전합니다.