-
MCP Prompts 완전 분해: 최적 프롬프트를 서버에 봉인하고 재사용하는 방법IT 2026. 6. 13. 21:00
MCP의 세 기본 단위 중 Prompts는 가장 오해받는다. Tools는 "AI가 실행하는 함수"라고 설명하면 직관적으로 와닿는다. Resources는 "AI가 읽는 데이터"라고 하면 이해된다. 그런데 Prompts는? "서버에 저장된 재사용 프롬프트 템플릿"이라는 설명을 들으면 "그냥 클라이언트에서 관리하면 되지 않나?"라는 의문이 든다.
이 의문이 Prompts를 이해하는 출발점이다.
왜 프롬프트를 서버에 두는가
AI 개발 도구를 만든다고 가정하자. "코드 리뷰 해줘"라고 요청할 때 어떤 프롬프트를 쓸 것인가? 팀원 10명이 각자 다른 방식으로 요청하면 결과 품질이 들쭉날쭉해진다. 누군가 몇 달에 걸쳐 다듬은 최적 프롬프트가 있다면, 그걸 모두가 쓸 수 있어야 한다.
클라이언트(IDE 플러그인)에 프롬프트를 하드코딩할 수도 있다. 하지만 서버 팀이 프롬프트를 개선하면 모든 클라이언트가 업데이트해야 한다. MCP Prompts는 이 문제를 뒤집는다 — 최적 프롬프트 지식을 서버에 캡슐화하고, 클라이언트는 그냥 가져다 쓴다.
다이어그램 설명: 아래는 각자 다른 방식으로 요청하는 상황 — 프롬프트가 사람마다 달라서 결과 품질이 보장되지 않는다. 위는 Prompts를 쓰는 상황 — 서버의
code-review템플릿을 인자만 바꿔서 쓴다. 서버가 프롬프트를 업데이트하면 모든 클라이언트가 자동으로 최신 버전을 쓰게 된다.또 하나의 차이가 있다. Tools는 모델이 자율적으로 실행을 결정한다. Prompts는 사람이 명시적으로 선택한다. IDE의 슬래시 명령어(
/code-review)나 드롭다운 메뉴로 사람이 골라야 활성화된다. 이 차이가 Prompts를 Tools·Resources와 별도로 분리한 이유다.그림: Tools·Resources·Prompts의 트리거 주체 비교 — Prompts만 사람이 명시적으로 선택한다 다이어그램 설명: 세 단위의 가장 큰 차이는 누가 시작하느냐다. Tools는 모델이 필요하다고 판단하면 자동 실행한다. Resources는 클라이언트가 모델이 추론하기 전에 데이터를 채운다. Prompts는 사람이 골라야 활성화된다. 이 세 번째 트리거 주체가 Prompts를 "사용 패턴 캡슐화" 도구로 만드는 이유다.
prompts/list — "이 서버로 어떻게 요청하면 잘 되나"
클라이언트가 서버에서 사용 가능한 프롬프트 템플릿 목록을 조회한다. IDE가 슬래시 명령어 드롭다운을 채울 때 이 메서드를 호출한다.
// 요청 { "jsonrpc": "2.0", "id": 1, "method": "prompts/list" } // 응답 { "jsonrpc": "2.0", "id": 1, "result": { "prompts": [ { "name": "code-review", "description": "코드를 검토하고 개선점을 제안합니다", "arguments": [ { "name": "language", "description": "프로그래밍 언어 (예: rust, python, go)", "required": true // 이 인자 없으면 prompts/get 호출 불가 }, { "name": "focus", "description": "리뷰 초점: 보안 | 성능 | 가독성 | 전반", "required": false // 없으면 서버가 기본값 사용 }, { "name": "severity", "description": "지적할 최소 심각도: 낮음 | 보통 | 높음", "required": false } ] }, { "name": "explain-error", "description": "컴파일/런타임 오류 메시지를 분석하고 원인을 설명합니다", "arguments": [ { "name": "error_message", "description": "오류 메시지 전체", "required": true }, { "name": "language", "description": "프로그래밍 언어", "required": true } ] }, { "name": "write-tests", "description": "함수나 클래스에 대한 단위 테스트를 작성합니다", "arguments": [ { "name": "framework", "description": "테스트 프레임워크 (pytest, jest, cargo test)", "required": false } ] } ] } }코드 설명:
arguments배열의required: true인자는prompts/get을 호출할 때 반드시 넘겨야 한다. 없으면 서버가 오류를 반환한다.required: false인자는 선택 사항이며 서버가 기본값으로 채운다. 클라이언트는 이 메타데이터를 바탕으로 UI를 구성한다 — 필수 인자는 입력 필드를 붉게 표시하거나 빈 채로 제출하지 못하게 막는 식이다.nextCursor가 있으면 프롬프트 목록도 페이지네이션된다.prompts/get — 인자를 채워서 완성된 messages를 받는다
사람이 프롬프트를 선택하고 인자를 입력하면, 클라이언트는
prompts/get으로 완성된 메시지 배열을 받는다. 이 배열은 LLM 대화의 history 형식과 동일하다.다이어그램 설명: 사람이 슬래시 명령어를 선택하고 인자를 입력하면, 클라이언트가
prompts/get으로 완성된messages배열을 받는다. 클라이언트는 이 배열을 현재 대화 context 앞에 prepend하고, 모델에게 전달한다. 모델은 서버가 설계한 최적 프롬프트 구조를 그대로 받게 된다. 사람은 어떤 프롬프트를 쓸지만 결정하고, 실제 프롬프트 엔지니어링은 서버가 담당한다.// prompts/get 요청 { "jsonrpc": "2.0", "id": 2, "method": "prompts/get", "params": { "name": "code-review", "arguments": { "language": "rust", "focus": "보안" } } } // 응답 — 완성된 messages 배열 { "jsonrpc": "2.0", "id": 2, "result": { "description": "Rust 보안 코드 리뷰", // 사람이 읽기 좋은 설명 (선택) "messages": [ { "role": "user", "content": { "type": "text", "text": "다음 Rust 코드를 보안 관점에서 검토해 주세요.\n\n검토 기준:\n- unsafe 블록 사용 여부와 안전성\n- 정수 오버플로 및 패닉 가능성\n- 소유권·수명 관련 메모리 안전성\n- 입력 검증 누락 여부\n\n각 지적 사항은 '심각도: [높음/보통/낮음]' 형식으로 분류해 주세요." } } ] } }코드 설명:
messages배열은 LLM API의 대화 history 형식과 동일하다.role은user·assistant·system중 하나고,content에 타입과 내용이 담긴다. 서버는 인자를 받아 이 배열을 동적으로 생성한다 — Rust라면 Rust 특화 보안 기준을, Python이라면 Python 특화 기준을 담는 식이다. 클라이언트는 이 배열을 현재 대화 앞에 prepend하면 된다. 프롬프트 내용을 모르고, 알 필요도 없다. 서버가 최신 버전으로 업데이트하면 클라이언트는 자동으로 개선된 버전을 쓴다.messages 내 Resource 참조 — 데이터도 함께 넣는다
messages배열의content는 텍스트 외에 resource 타입도 지원한다. 프롬프트 템플릿 안에 특정 Resource 참조를 포함할 수 있다.// messages 내 resource 타입 content { "messages": [ { "role": "user", "content": { "type": "resource", "resource": { "uri": "file:///workspace/src/main.c", "mimeType": "text/x-csrc", "text": "#include ..." // 서버가 미리 읽어서 삽입 } } }, { "role": "user", "content": { "type": "text", "text": "위 파일을 보안 관점에서 검토해 줘." } } ] }코드 설명: 서버가
prompts/get을 처리할 때 Resource의 내용을 직접 읽어서messages에 삽입할 수 있다. 클라이언트가 따로resources/read를 호출하지 않아도 된다. 이 패턴은 "프롬프트 + 데이터"를 한 번의 왕복으로 받아오는 효율적인 방식이다. 예를 들어 코드 리뷰 프롬프트가 현재 편집 중인 파일의 내용을 자동으로 포함해서 돌려주는 식이다.notifications/prompts/list_changed — 프롬프트 목록이 바뀌었다
서버가 새 프롬프트를 추가하거나 기존 프롬프트를 제거하면 클라이언트에게 알린다. 클라이언트는 이 알림을 받고
prompts/list를 다시 호출해서 최신 목록을 가져간다.다이어그램 설명:
notifications/prompts/list_changed는 params가 없다 — "목록이 바뀌었으니 다시 조회해"라는 invalidation 신호만이다. 클라이언트가prompts/list를 재호출해서 최신 상태를 가져간다. 이 알림 지원 여부는 초기화 핸드셰이크의capabilities.prompts.listChanged: true로 확인한다. 지원하지 않는 서버라면 클라이언트는 세션 시작 시 한 번 조회한 목록을 계속 쓴다.completion/complete — 인자 입력 중 자동완성 제공
사람이 프롬프트 인자를 입력하는 도중에 서버에게 후보 값을 물어볼 수 있다. IDE 자동완성과 같은 경험이다.
prompts/list에서 받은arguments메타데이터만으로는 어떤 값이 유효한지 알 수 없을 때 이 메서드가 유용하다.// "language" 인자에 "py" 입력 중 → 후보 요청 { "jsonrpc": "2.0", "id": 3, "method": "completion/complete", "params": { "ref": { "type": "ref/prompt", // 어떤 종류인지 — ref/prompt 또는 ref/resource "name": "code-review" // 어떤 프롬프트의 인자인지 }, "argument": { "name": "language", // 어떤 인자를 완성하는지 "value": "py" // 지금까지 입력한 값 } } } // 응답 — 후보 목록 { "jsonrpc": "2.0", "id": 3, "result": { "completion": { "values": ["python", "pypy"], // "py"로 시작하는 후보들 "total": 2, // 전체 후보 수 (선택) "hasMore": false // 더 있으면 true (100개 초과 시 페이징) } } }코드 설명:
ref.type: "ref/prompt"는 이게 프롬프트 인자 자동완성임을 나타낸다.ref/resource라면 리소스 URI 템플릿의 파라미터 자동완성이다. 서버는argument.value(지금까지 입력한 값)를 prefix로 써서 매칭되는 후보를 돌려준다. 후보가 100개를 넘으면hasMore: true로 잘려서 오고, 사용자가 더 타이핑하면 다시 물어봐야 한다. 이 메서드가 없어도 Prompts는 동작한다 — 인자 값을 자유 입력으로 받으면 된다. 하지만 있으면 사용자 경험이 훨씬 좋아진다.실전 시나리오 — 팀 공용 코드 품질 MCP 서버
지금까지 다룬 메서드들이 실제 팀 환경에서 어떻게 조합되는지 보자. 팀의 코드 품질 기준과 리뷰 패턴을 MCP Prompts로 서비스하는 시나리오다.
다이어그램 설명: 개발자가 직접 경험하는 흐름이다. IDE가 시작할 때 한 번
prompts/list를 조회해서 슬래시 명령어를 준비한다. 개발자가/code-review를 입력하면 인자 입력 폼이 뜨고,completion/complete로 자동완성을 제공한다. 인자를 채우면prompts/get으로 완성된 프롬프트를 받아 모델에게 전달한다. 팀이 서버에 새 프롬프트를 추가하면list_changed알림으로 모든 IDE의 슬래시 명령어가 자동 업데이트된다 — 배포 없이.Prompts가 주는 것 — 프롬프트 엔지니어링의 분업
MCP Prompts의 핵심 가치는 프롬프트 엔지니어링의 분업이다. 서버를 만드는 팀(또는 개인)이 수많은 시행착오를 거쳐 최적화한 프롬프트를 서버에 봉인한다. 클라이언트를 만드는 팀은 그 결과물을 그냥 가져다 쓴다. 최적 프롬프트가 무엇인지 몰라도 된다.
이 분업이 실제로 중요한 이유가 있다. "언어별로 다른 코드 리뷰 기준", "팀 코딩 스타일에 맞춘 피드백 방식", "도메인별 보안 체크리스트" 같은 지식은 서버 팀이 가장 잘 안다. 이 지식을 프롬프트로 구체화하는 작업은 반복 실험이 필요하다. Prompts는 이 실험 결과를 서버에 가두고, 클라이언트가 매번 재발명하지 않아도 되게 한다. 서버가 프롬프트를 개선할 때 클라이언트는 업데이트 없이 자동으로 더 나은 결과를 받는다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
MCP sampling/createMessage: AI 도구가 AI를 부르는 역방향 설계 (0) 2026.06.14 MCP Roots 완전 분해: 서버가 클라이언트에게 먼저 묻는 역방향 설계 (0) 2026.06.13 MCP Prompts의 멀티턴 messages — 서버가 모델의 첫 생각을 설계하는 방법 (0) 2026.06.13 MCP Resources 완전 분해: URI로 AI에게 데이터를 공급하는 7가지 메서드 (0) 2026.06.12 MCP가 JSON-RPC 봉투 안에 채운 것들: 세 기본 단위와 20가지 메서드 전체 지도 (0) 2026.06.12 JSON-RPC 2.0이 정의하는 건 봉투 6단어뿐: MCP 사례로 그 안과 밖을 가른다 (1) 2026.06.12 서브에이전트 패키지를 직접 뜯어보다 — debug-pack 플러그인 해부 (0) 2026.06.11 Claude에 브라우저 눈과 손을 달다 — Playwright MCP 플러그인 (0) 2026.06.10 Claude에 GitHub 전체를 연결하다 — GitHub MCP 플러그인 실전 가이드 (0) 2026.06.10 Claude Code에서 나만의 AI 전문가 만들기 — 서브에이전트 제작 가이드 (0) 2026.06.10