Workspace MCP Tool AI 생성(자연어 → query_spec 초안) + 검토 게이트 QA 가이드 (#2045)
이 문서는 Workspace MCP Tool AI 생성 모드(자연어 → query_spec 초안 → 검토 대기)를 QA가 화면에서 직접 따라 검증할 수 있도록 단계별 스크린샷과 함께 정리한 것입니다. 모든 화면은 실제 GenD prod 환경의 데모 워크스페이스에서 캡처했습니다.
개요
MCP Tool은 워크스페이스 관리자가 등록하는 읽기 전용(SELECT-only) MCP 도구입니다. 등록 경로는 세 가지입니다.
- 마법사(Wizard) — 5단계 구조화 경로(
SqlBuilderWizard). 컬럼 선택·필터·정렬을 GUI로 조립합니다(안전 경로). - 고급(Advanced — raw SQL) — JOIN·서브쿼리 등 마법사로 표현하기 어려운
SELECT문을 Monaco 에디터에 직접 작성합니다(P2.1, #2044). → 고급 SQL QA 가이드 - AI 생성(P2.2, #2045) — 자연어 프롬프트를 입력하면 LLM 이
query_spec초안을 만들고, 그 초안으로 5단계 마법사를 미리 채워(prefill) 관리자 검토를 받는 경로입니다. 이 문서가 다루는 대상입니다.
AI 생성 흐름
- 자연어 입력 — "데이터마트에서 …를 조회하는 도구" 처럼 한국어로 원하는 도구를 설명합니다.
- LLM 초안 생성 —
POST /api/v1/workspace-mcp-tools/draft-from-prompt가 호출됩니다. 백엔드는 워크스페이스의 허용 LLM 키 + quota 가드로 LLM 을 호출해query_spec_jsonb(마법사 스펙) 초안을 만들고, 그 도구를 항상검토 대기(pending_review)상태로 저장합니다 — 절대 auto-activate 하지 않습니다. - 마법사 prefill + 출처(provenance) 배너 — 생성된 초안은 5단계 마법사를 미리 채우며, 마법사 상단에 보라색 "이 도구는 AI 가 생성했습니다 (model=…)" 출처 배너가 떠 관리자가 활성화 전에 spec 을 반드시 검토하도록 합니다.
- 검토 게이트 — 저장된 도구는 목록에 검토 대기 배지로 나타나며, ws-admin 이 검토 큐에서 승인해야
활성(active)이 됩니다(거부 시비활성(disabled)). AI 경로도 고급(raw-SQL) 경로와 동일한 검토 게이트를 거칩니다.
§9.4 보안 모델 — LLM 출력은 신뢰하지 않는다 (단일 신뢰 경계)
AI 생성의 핵심은 LLM 출력을 절대 신뢰하지 않는 것입니다. 보안 보증은 다음과 같습니다.
- 화이트리스트 강제(생성 전) — system prompt 가 워크스페이스의 허용 ORM 모델/컬럼 메타(
build_whitelist)를 첨부하고,target_model_ref·select_cols를 그 안으로만 제약합니다. - 생성 후 재검증(단일 신뢰 경계) —
validate_against_whitelist가 LLM 출력의target_model_ref/컬럼/필터 컬럼이 화이트리스트 밖이면 422 로 거부합니다. LLM 이 무엇을 내놓든 서버가 다시 검증합니다. - prompt-injection 방어 — system prompt 가 user prompt 보다 우선합니다. user 가 "이전 지시 무시", "users 테이블 추가" 등을 요청해도 화이트리스트 후검증이 차단합니다.
- raw SQL 금지(AI 경로) — AI 경로는 마법사 스펙만 생성합니다. LLM 이 raw SQL 을 끼워 넣으면
DraftWhitelistError로 거부됩니다. - 항상
검토 대기, 자가 활성화 불가 — AI 가 만든 도구는 언제나pending_review로 저장되며, 작성자(혹은 AI)가 스스로 활성화할 수 없습니다. 반드시 별도 ws-admin 검토를 거칩니다. - quota 가드 — LLM 호출은 워크스페이스 키 quota 로 보호되며, 한도 초과 시 429 로 거부됩니다.
권한: 등록 화면(
/workspace/mcp-tools/new) 진입·작성과 검토 큐의 승인/거부는 모두 워크스페이스 admin(글로벌admin역할 또는 Keycloak/tenants/<slug>/admins멤버) 만 가능합니다. 비-admin 의 초안 생성 차단(403)·화이트리스트 거부(422)·quota 초과(429)는 LLM 이 비결정적이라 화면에서 안정적으로 재현하기 어려워(아래 참고) API/단위 테스트로 검증합니다.
사전 준비 / 시드
별도 시드 스크립트는 필요 없습니다. 이 가이드의 모든 상태(검토 대기 초안)는 검증 절차 자체가 화면에서 생성합니다. E2E 스펙(ui/tests/mcp-tool-ai-draft-e2e.spec.ts)도 동일하게 각 시나리오에서 AI 초안을 직접 만들고, 끝나면 API로 소프트 삭제합니다.
- 계정: 워크스페이스 admin 계정(글로벌
admin역할이면 충족). - LLM: 워크스페이스에 LLM 키(레지스트리 바인딩 또는 env fallback)가 연결되어 있어야 초안이 생성됩니다. LLM 이 없는 환경에서는 초안 생성 단계에서 인라인 오류가 뜨며, 이때 화이트리스트/검토 게이트 보증은 API 테스트로 확인합니다.
- 도구 이름은 AI 가 정합니다 —
zz-e2e-*같은 고정 이름이 아니라 LLM 이tool_name(예:daily_sales_by_customer)을 정합니다. 같은 이름이 이미 있으면 백엔드가 자동으로 접미사를 붙여 충돌을 피합니다. - 검증 후 만든 도구는 목록에서 ⋯ 삭제(소프트 삭제) 로 정리할 수 있습니다.
1. 등록 화면 — AI 생성 탭 (S1)
좌측 사이드바 MCP Tools → [도구 등록] 으로 들어가면 등록 화면에 마법사(Wizard), 고급(Advanced — raw SQL), AI 생성 세 탭이 보입니다. 기본은 마법사 탭입니다.

AI 생성 탭을 클릭하면 자연어 프롬프트 입력 영역이 열립니다. 도구 설명(자연어) 텍스트 영역, AI 로 초안 생성 버튼, 그리고 "AI 가 생성한 도구는 검토 대기(pending_review) 상태로 저장되며, 활성화 전 관리자 검토가 필요합니다" 라는 노란 안내가 함께 표시됩니다. 프롬프트가 비어 있으면 생성 버튼은 비활성입니다.

확인 포인트(QA): 세 탭이 모두 보이는지, AI 탭에 프롬프트 입력창·생성 버튼·"검토 대기" 안내가 함께 나오는지, 프롬프트가 없을 때 생성 버튼이 비활성인지 확인합니다.
2. 자연어 프롬프트 → 생성 → 마법사 prefill + 출처 배너 (S2)
AI 탭에서 원하는 도구를 한국어로 설명합니다(예: "데이터마트에서 이름과 ID 를 조회하는 읽기 전용 도구"). 프롬프트를 입력하면 AI 로 초안 생성 버튼이 활성화됩니다.

AI 로 초안 생성 을 누르면 draft-from-prompt 가 호출되어 LLM 이 query_spec 초안을 만들고, 그 초안으로 5단계 마법사가 자동으로 채워집니다. 마법사 상단에는 보라색 출처(provenance) 배너 — "이 도구는 AI 가 생성했습니다 (model=…). 활성화 전 spec 을 검토하세요." + 입력한 프롬프트 — 가 떠, 이 spec 이 LLM 에서 비롯되었으니 활성화 전에 반드시 검토해야 함을 알립니다.

확인 포인트(QA): 생성 후 화면이 마법사 탭으로 전환되고, 보라색 AI 출처 배너(model + 프롬프트)가 스텝퍼 위에 보이는지, 마법사 1단계부터 검토 가능한지 확인합니다. (LLM 이 없는 환경에서는 대신 인라인 오류가 뜹니다.)
3. 검토 대기 등록 — 목록 배지 + 검토 큐 (S3)
AI 초안은 draft-from-prompt 단계에서 이미 검토 대기(pending_review) 로 저장됩니다(자가 활성화 없음). MCP Tools 목록(/workspace/mcp-tools)으로 가면 AI 가 정한 이름의 도구가 나타나며, 상태 열에 노란 검토 대기(Pending review) 배지가 붙습니다.

검토 대기 도구가 있으면 목록 상단에 검토 큐(ws-admin 전용)가 나타나고, AI 초안도 고급(raw-SQL) 제출과 똑같이 이 큐에 올라와 승인/거부를 기다립니다. ws-admin 이 승인 하면 활성, 거부 하면 비활성 으로 바뀝니다(검토 큐 승인/거부 화면은 고급 SQL QA 가이드 §4 와 동일).

확인 포인트(QA): 방금 생성한 AI 초안이 목록에 검토 대기로 보이는지(자가 활성화가 일어나지 않음), 검토 큐에 같은 도구가 올라와 있는지 확인합니다.
4. 음성(negative) — 화이트리스트 밖 / prompt-injection 프롬프트 거부 (S4)
화이트리스트 밖 데이터(예: 내부 인증 테이블)나 prompt-injection ("이전 지시 무시 …")을 요청하면, 백엔드가 LLM 출력을 화이트리스트로 재검증해 422 로 거부합니다. UI 는 거부 사유를 인라인 오류 박스(mcp-ai-error)로 분명히 표시하며, 마법사 prefill(출처 배너)은 나타나지 않고, 도구는 생성되지 않습니다.

확인 포인트(QA): 인라인 오류가 뜨고 출처 배너는 나오지 않아야 하며, 도구가 생성되지 않아야 합니다.
캡처 주의: 위 스크린샷은 prod 실행 당시 LLM 이 택한 경로를 그대로 담습니다. LLM 이 비결정적이므로, injection 프롬프트가 우연히 화이트리스트 안에 머물러 초안이 생성되는 경우도 있고(아래 비결정성 주의 참조), 화이트리스트 밖으로 벗어나 422 인라인 오류가 뜨는 경우도 있습니다. 422 거부 보증 자체는 LLM 을 스텁한 API/단위 테스트가 결정적으로 검증합니다.
LLM 비결정성 주의: LLM 이 비결정적이라 prod 에서 같은 프롬프트가 항상 같은 결과를 내지는 않습니다(우연히 화이트리스트 안에 머물 수 있음). 화이트리스트 후검증 422·prompt-injection 거부·raw-SQL 거부·비-admin 403·quota 429 는 LLM 을 스텁한 API/단위 테스트
apps/api/tests/test_workspace_mcp_tool_ai_draft.py가 결정적으로 검증합니다. 화면 검증은 이 보증의 UI 노출(인라인 오류)을 확인하는 보조 수단입니다.
QA 체크리스트 요약
| # | 시나리오 | 기대 결과 |
|---|---|---|
| S1 | 등록 화면 AI 탭 | 마법사·고급·AI 세 탭 표시, AI 탭에 프롬프트 입력 + "검토 대기" 안내 + (빈 프롬프트 시) 생성 비활성 |
| S2 | 자연어 → 생성 | 마법사 5단계 prefill + 보라색 AI 출처 배너(model + 프롬프트) |
| S3 | 검토 대기 등록 | 목록에 검토 대기 배지로 등장 + 검토 큐에 노출(자가 활성화 없음) |
| S4 | 화이트리스트 밖 / injection | 인라인 422 오류, 출처 배너 미표시, 도구 미생성 |
| — | 화이트리스트/injection/403/429 결정적 검증 | (LLM 비결정) API/단위 테스트로 검증 |
비결정 경로는 화면으로 안정 검증 불가: LLM 출력이 비결정적이라 화이트리스트 후검증(422)·prompt-injection 거부·raw-SQL 거부·비-admin 403·quota 429 는 브라우저로 안정 재현이 어렵습니다. 이는 API/단위 테스트
apps/api/tests/test_workspace_mcp_tool_ai_draft.py가 LLM 을 스텁해 결정적으로 검증합니다.
알아두기
- LLM 출력은 신뢰하지 않음: 화이트리스트는 생성 전(system prompt) 과 생성 후(server 재검증) 양쪽에서 강제됩니다 — 단일 신뢰 경계는 서버의 후검증입니다(§9.4).
- 항상 검토 대기: AI 가 만든 도구는 언제나
pending_review로 저장되며 auto-activate 되지 않습니다. 활성화는 별도 ws-admin 검토(/activate)를 거칩니다. - 마법사로 인계: AI 는 초안만 만들고, 실제 검토·수정·저장은 기존 5단계 마법사에서 합니다. 관리자는 prefill 된 spec 을 그대로 쓰거나 수정한 뒤 저장할 수 있습니다.
- 읽기 전용 보증: AI 경로는 마법사 스펙만 생성하므로 raw SQL 이 끼어들 수 없고,
workspace_id격리는 백엔드가 주입합니다. - 이름 충돌 자동 해소: AI 가 제안한
tool_name이 이미 존재하면 백엔드가 짧은 접미사를 붙여 초안을 잃지 않게 합니다(관리자가 마법사에서 이름을 바꿀 수 있음). - 소프트 삭제: 삭제는
status=deleted소프트 삭제이며, 같은 이름을 나중에 다시 등록할 수 있습니다.