프롬프트

프롬프트는 서버가 재사용 가능한 프롬프트 템플릿과 워크플로우를 정의하여 클라이언트가 사용자와 LLM에게 쉽게 노출할 수 있도록 합니다. 이는 일반적인 LLM 상호작용을 표준화하고 공유하는 강력한 방법을 제공합니다.

프롬프트는 **사용자 제어** 방식으로 설계되었습니다. 즉, 사용자가 명시적으로 선택하여 사용할 수 있도록 서버에서 클라이언트로 노출됩니다.

개요

MCP의 프롬프트는 다음과 같은 기능을 가진 미리 정의된 템플릿입니다:

  • 동적 인자 수락
  • 리소스에서 컨텍스트 포함
  • 여러 상호작용 체인
  • 특정 워크플로우 안내
  • UI 요소로 노출 (슬래시 명령어 등)

프롬프트 구조

각 프롬프트는 다음과 같이 정의됩니다:

{
  name: string;              // 프롬프트의 고유 식별자
  description?: string;      // 사람이 읽을 수 있는 설명
  arguments?: [              // 선택적 인자 목록
    {
      name: string;          // 인자 식별자
      description?: string;  // 인자 설명
      required?: boolean;    // 인자 필수 여부
    }
  ]
}

프롬프트 검색

클라이언트는 prompts/list 엔드포인트를 통해 사용 가능한 프롬프트를 검색할 수 있습니다:

// 요청
{
  method: "prompts/list"
}

// 응답
{
  prompts: [
    {
      name: "analyze-code",
      description: "코드 잠재적 개선사항 분석",
      arguments: [
        {
          name: "language",
          description: "프로그래밍 언어",
          required: true
        }
      ]
    }
  ]
}

프롬프트 사용

프롬프트를 사용하려면 클라이언트가 prompts/get 요청을 보냅니다:

// 요청
{
  method: "prompts/get",
  params: {
    name: "analyze-code",
    arguments: {
      language: "python"
    }
  }
}

// 응답
{
  description: "Python 코드의 잠재적 개선사항 분석",
  messages: [
    {
      role: "user",
      content: {
        type: "text",
        text: "다음 Python 코드의 잠재적 개선사항을 분석해주세요:\n\n```python\ndef calculate_sum(numbers):\n    total = 0\n    for num in numbers:\n        total = total + num\n    return total\n\nresult = calculate_sum([1, 2, 3, 4, 5])\nprint(result)\n```"
      }
    }
  ]
}

동적 프롬프트

프롬프트는 동적일 수 있으며 다음을 포함할 수 있습니다:

임베디드 리소스 컨텍스트

{
  "name": "analyze-project",
  "description": "프로젝트 로그와 코드 분석",
  "arguments": [
    {
      "name": "timeframe",
      "description": "로그 분석 시간 기간",
      "required": true
    },
    {
      "name": "fileUri",
      "description": "검토할 코드 파일 URI",
      "required": true
    }
  ]
}

prompts/get 요청을 처리할 때:

{
  "messages": [
    {
      "role": "user",
      "content": {
        "type": "text",
        "text": "이 시스템 로그와 코드 파일의 문제점을 분석해주세요:"
      }
    },
    {
      "role": "user",
      "content": {
        "type": "resource",
        "resource": {
          "uri": "logs://recent?timeframe=1h",
          "text": "[2024-03-14 15:32:11] ERROR: Connection timeout in network.py:127\n[2024-03-14 15:32:15] WARN: Retrying connection (attempt 2/3)\n[2024-03-14 15:32:20] ERROR: Max retries exceeded",
          "mimeType": "text/plain"
        }
      }
    },
    {
      "role": "user",
      "content": {
        "type": "resource",
        "resource": {
          "uri": "file:///path/to/code.py",
          "text": "def connect_to_service(timeout=30):\n    retries = 3\n    for attempt in range(retries):\n        try:\n            return establish_connection(timeout)\n        except TimeoutError:\n            if attempt == retries - 1:\n                raise\n            time.sleep(5)\n\ndef establish_connection(timeout):\n    # Connection implementation\n    pass",
          "mimeType": "text/x-python"
        }
      }
    }
  ]
}

다단계 워크플로우

const debugWorkflow = {
  name: "debug-error",
  async getMessages(error: string) {
    return [
      {
        role: "user",
        content: {
          type: "text",
          text: `다음 오류가 발생했습니다: ${error}`
        }
      },
      {
        role: "assistant",
        content: {
          type: "text",
          text: "이 오류를 분석해 도와드리겠습니다. 지금까지 어떤 시도를 해보셨나요?"
        }
      },
      {
        role: "user",
        content: {
          type: "text",
          text: "서비스를 재시작해 보았지만 오류가 계속 발생합니다."
        }
      }
    ];
  }
};

구현 예제

MCP 서버에서 프롬프트를 구현하는 완전한 예제입니다:

    import { Server } from "@modelcontextprotocol/sdk/server";
    import {
      ListPromptsRequestSchema,
      GetPromptRequestSchema
    } from "@modelcontextprotocol/sdk/types";

    const PROMPTS = {
      "git-commit": {
        name: "git-commit",
        description: "Git 커밋 메시지 생성",
        arguments: [
          {
            name: "changes",
            description: "Git diff 또는 변경사항 설명",
            required: true
          }
        ]
      },
      "explain-code": {
        name: "explain-code",
        description: "코드 작동 방식 설명",
        arguments: [
          {
            name: "code",
            description: "설명할 코드",
            required: true
          },
          {
            name: "language",
            description: "프로그래밍 언어",
            required: false
          }
        ]
      }
    };

    const server = new Server({
      name: "example-prompts-server",
      version: "1.0.0"
    }, {
      capabilities: {
        prompts: {}
      }
    });

    // 사용 가능한 프롬프트 목록
    server.setRequestHandler(ListPromptsRequestSchema, async () => {
      return {
        prompts: Object.values(PROMPTS)
      };
    });

    // 특정 프롬프트 가져오기
    server.setRequestHandler(GetPromptRequestSchema, async (request) => {
      const prompt = PROMPTS[request.params.name];
      if (!prompt) {
        throw new Error(`프롬프트를 찾을 수 없습니다: ${request.params.name}`);
      }

      if (request.params.name === "git-commit") {
        return {
          messages: [
            {
              role: "user",
              content: {
                type: "text",
                text: `다음 변경사항에 대한 간결하지만 설명적인 커밋 메시지를 생성해주세요:\n\n${request.params.arguments?.changes}`
              }
            }
          ]
        };
      }

      if (request.params.name === "explain-code") {
        const language = request.params.arguments?.language || "알 수 없음";
        return {
          messages: [
            {
              role: "user",
              content: {
                type: "text",
                text: `이 ${language} 코드의 작동 방식을 설명해주세요:\n\n${request.params.arguments?.code}`
              }
            }
          ]
        };
      }

      throw new Error("프롬프트 구현을 찾을 수 없습니다");
    });
    from mcp.server import Server
    import mcp.types as types

    # 사용 가능한 프롬프트 정의
    PROMPTS = {
        "git-commit": types.Prompt(
            name="git-commit",
            description="Git 커밋 메시지 생성",
            arguments=[
                types.PromptArgument(
                    name="changes",
                    description="Git diff 또는 변경사항 설명",
                    required=True
                )
            ],
        ),
        "explain-code": types.Prompt(
            name="explain-code",
            description="코드 작동 방식 설명",
            arguments=[
                types.PromptArgument(
                    name="code",
                    description="설명할 코드",
                    required=True
                ),
                types.PromptArgument(
                    name="language",
                    description="프로그래밍 언어",
                    required=False
                )
            ],
        )
    }

    # 서버 초기화
    app = Server("example-prompts-server")

    @app.list_prompts()
    async def list_prompts() -> list[types.Prompt]:
        return list(PROMPTS.values())

    @app.get_prompt()
    async def get_prompt(
        name: str, arguments: dict[str, str] | None = None
    ) -> types.GetPromptResult:
        if name not in PROMPTS:
            raise ValueError(f"프롬프트를 찾을 수 없습니다: {name}")

        if name == "git-commit":
            changes = arguments.get("changes") if arguments else ""
            return types.GetPromptResult(
                messages=[
                    types.PromptMessage(
                        role="user",
                        content=types.TextContent(
                            type="text",
                            text=f"다음 변경사항에 대한 간결하지만 설명적인 "
                            f"커밋 메시지를 생성해주세요:\n\n{changes}"
                        )
                    )
                ]
            )

        if name == "explain-code":
            code = arguments.get("code") if arguments else ""
            language = arguments.get("language", "알 수 없음") if arguments else "알 수 없음"
            return types.GetPromptResult(
                messages=[
                    types.PromptMessage(
                        role="user",
                        content=types.TextContent(
                            type="text",
                            text=f"이 {language} 코드의 작동 방식을 설명해주세요:\n\n{code}"
                        )
                    )
                ]
            )

        raise ValueError("프롬프트 구현을 찾을 수 없습니다")

모범 사례

프롬프트를 구현할 때:

  1. 명확하고 설명적인 프롬프트 이름 사용
  2. 프롬프트와 인자에 대한 상세한 설명 제공
  3. 모든 필수 인자 검증
  4. 누락된 인자를 우아하게 처리
  5. 프롬프트 템플릿 버전 관리 고려
  6. 적절한 경우 동적 콘텐츠 캐싱
  7. 오류 처리 구현
  8. 예상 인자 형식 문서화
  9. 프롬프트 조합 가능성 고려
  10. 다양한 입력으로 프롬프트 테스트

UI 통합

프롬프트는 클라이언트 UI에서 다음과 같이 노출될 수 있습니다:

  • 슬래시 명령어
  • 빠른 작업
  • 컨텍스트 메뉴 항목
  • 명령 팔레트 항목
  • 안내된 워크플로우
  • 대화형 양식

업데이트 및 변경사항

서버는 프롬프트 변경사항을 클라이언트에 알릴 수 있습니다:

  1. 서버 기능: prompts.listChanged
  2. 알림: notifications/prompts/list_changed
  3. 클라이언트가 프롬프트 목록을 다시 가져옴

보안 고려사항

프롬프트를 구현할 때:

  • 모든 인자 검증
  • 사용자 입력 정화
  • 속도 제한 고려
  • 접근 제어 구현
  • 프롬프트 사용 감사
  • 민감 데이터 적절하게 처리
  • 생성된 콘텐츠 검증
  • 타임아웃 구현
  • 프롬프트 인젝션 위험 고려
  • 보안 요구사항 문서화