12. Prompts — 재사용 프롬프트 템플릿
- Prompt가 "사용자 제어" 재사용 템플릿이라는 점을 이해한다
- 프롬프트 정의(arguments)와 가져오기(
prompts/get→ messages)를 읽고 쓸 수 있다 - 프롬프트에 리소스를 임베드해 워크플로를 패키징하는 법을 안다
메뉴판 비유
Tool이 "모델이 누르는 버튼", Resource가 "사서가 꺼내 주는 자료"라면, Prompt는 "손님이 직접 고르는 메뉴"입니다. 메뉴에는 정해진 레시피(메시지 구성)가 있고, 손님(사용자)이 "이걸로 주세요"라고 명시적으로 선택합니다. 옵션(매운맛 정도 = arguments)을 골라 주문을 맞춤화할 수 있죠.
MCP는 서버가 프롬프트 템플릿을 클라이언트에 노출하는 표준 방법을 제공합니다. 프롬프트는 언어 모델과 상호작용하기 위한 구조화된 메시지와 지시를 제공합니다. 클라이언트는 사용 가능한 프롬프트를 발견하고, 내용을 가져오고, 인자를 채워 맞춤화할 수 있습니다.
핵심은 사용자 제어(user-controlled) 입니다. 프롬프트는 사용자가 명시적으로 골라 쓰도록 서버에서 노출됩니다. 보통 UI의 사용자 주도 명령(예: 슬래시 명령 /git-commit)으로 트리거됩니다 — 모델이 도구처럼 알아서 부르지 않습니다.
flowchart LR
U["사용자"] -->|"슬래시 명령 등으로 선택"| P["Prompt 템플릿"]
P -->|"arguments 채움"| G["prompts/get"]
G -->|"messages 반환"| LLM["LLM에 주입"]
classDef u fill:#fde68a,stroke:#b45309,color:#000;
classDef p fill:#5eead4,stroke:#0f766e,color:#000;
classDef llm fill:#ddd6fe,stroke:#6d28d9,color:#000;
class U u;
class P,G p;
class LLM llm;
프롬프트 정의 — prompts/list
클라이언트는 prompts/list로 사용 가능한 프롬프트를 발견합니다. 각 프롬프트는 이름·설명·인자 목록을 가집니다.
{
"name": "analyze-project",
"description": "프로젝트 로그와 코드를 분석",
"arguments": [
{ "name": "timeframe", "description": "로그 분석 기간", "required": true },
{ "name": "fileUri", "description": "검토할 코드 파일 URI", "required": true }
]
}
arguments는 사용자가 채울 빈칸입니다. required로 필수 여부를 표시합니다. IDE에서 프롬프트를 고르면 인자 입력란이 뜨고, 값을 채워 삽입하는 "프롬프트 템플릿" UX가 이 정의 위에서 동작합니다.
프롬프트 가져오기 — prompts/get → messages
사용자가 인자를 채우면 클라이언트가 prompts/get을 호출하고, 서버는 LLM에 그대로 넣을 수 있는 messages 배열을 돌려줍니다.
{
"messages": [
{
"role": "user",
"content": { "type": "text", "text": "다음 시스템 로그와 코드 파일에서 문제를 분석해줘:" }
},
{
"role": "user",
"content": {
"type": "resource",
"resource": {
"uri": "logs://recent?timeframe=1h",
"mimeType": "text/plain",
"text": "[15:32:11] ERROR: Connection timeout in network.py:127\n..."
}
}
}
]
}
각 메시지는 role(user·assistant 등)과 content를 가집니다. content의 type은 text만이 아니라 resource 도 될 수 있습니다 — 즉 프롬프트가 서버 관리 콘텐츠(문서·코드·로그)를 대화 흐름에 직접 끼워 넣을 수 있습니다. 이를 임베디드 리소스(embedded resource) 라 합니다.
왜 프롬프트는 가장 덜 쓰이나
솔직히 짚자면, 프롬프트는 세 primitive 중 생태계에서 가장 적게 쓰입니다. 튜토리얼과 예제는 도구로 넘쳐나지만, 프롬프트는 아예 없거나 사용자가 직접 칠 수 있는 수준의 사소한 "시스템 메시지 래퍼"에 그치는 경우가 많습니다. 이유는 둘입니다 — 도구는 데모하기 쉽지만 프롬프트는 워크플로를 설계해야 하고, 대부분의 SDK가 프롬프트를 단순 메시지 템플릿으로만 다뤄 다단계 워크플로·단계 간 데이터 흐름에 대한 내장 추상화가 빈약합니다.
세 primitive 다시 비교
flowchart TD
subgraph WHO["누가 부르나?"]
M["모델 → Tool"]
A["애플리케이션 → Resource"]
U["사용자 → Prompt"]
end
classDef tool fill:#5eead4,stroke:#0f766e,color:#000;
classDef res fill:#ddd6fe,stroke:#6d28d9,color:#000;
classDef pr fill:#fde68a,stroke:#b45309,color:#000;
class M tool;
class A res;
class U pr;
| Tool | Resource | Prompt | |
|---|---|---|---|
| 제어 | 모델 | 애플리케이션 | 사용자 |
| 부작용 | 있음 (행동) | 없음 (읽기) | 없음 (템플릿) |
| 발견 | tools/list | resources/list | prompts/list |
| 사용 | tools/call | resources/read | prompts/get |
| 트리거 | 모델이 자동 | 호스트가 주입 | 사용자가 선택(슬래시 등) |
🔒 보안 — 프롬프트도 검증한다: 프롬프트는 인자를 받아 메시지를 만들고, 종종 리소스를 임베드합니다. 구현은 인젝션 공격이나 무단 리소스 접근을 막기 위해 모든 프롬프트 입력·출력을 신중히 검증해야 합니다(스펙 MUST). 예컨대 fileUri 인자로 임의 파일을 임베드하게 두면, 사용자가 권한 밖 파일을 끌어올 수 있습니다. 공통 실패는 표준 JSON-RPC 에러로 돌려줍니다.
이 장에서 배운 것
- Prompt는 사용자가 명시적으로 고르는 재사용 템플릿이며 사용자 제어(보통 슬래시 명령).
prompts/list로 발견(이름·설명·arguments),prompts/get으로 messages 배열을 받아 LLM에 주입.- 메시지 content는
text뿐 아니라resource(임베디드 리소스) 도 가능해, 워크플로를 패키징할 수 있다. - 생태계에서 가장 덜 쓰이지만, 도메인 특화 반복 워크플로에서 값어치가 크다. 입력·출력 검증은 필수.
✍️ 확인 문제
- "우리 팀 표준 PR 리뷰 절차"를 Tool·Resource·Prompt 중 무엇으로 만드는 게 가장 자연스러운가? 제어 모델로 설명해 보자.
prompts/get응답의 메시지 content에type: "resource"를 쓰면 무엇을 할 수 있나? 한 가지 활용을 들어 보자.- 프롬프트 인자
fileUri로 임의 파일을 임베드하게 두면 어떤 위험이 있나? 어떻게 막을까?
다음 장: 13. 알림·구독·로깅 등 부가 기능