Lộ trình học›MCP›Introduction to MCP Mở bản đồ

7.9 · Định nghĩa Prompts trên server

Bài viếtTrung cấp30 phút đọc

Hãy xem xét cùng một task — format một tài liệu thành markdown — với 2 cách approach:

Sau bài này, bạn sẽ
  1. Giải thích Prompts là gì và khác gì với Tools/Resources (user-controlled)
  2. Định nghĩa prompt qua decorator @mcp.prompt() với parameters có Field description
  3. Build một prompt template cho use case thực tế (format, summarize, review)
  4. Hiểu tại sao prompt trong MCP = "chuyên gia đóng gói kiến thức prompt engineering"
  5. Test prompt qua MCP Inspector để verify interpolation đúng
  6. Phân biệt khi nào tạo prompt vs để user tự gõ

Mở đầu: Vì sao prompt "thô" luôn thua prompt "được craft"

Cách 1: User tự gõ

User gõ:

Hãy format lại cho đẹp

Claude không biết:

  • Tài liệu nào? (chưa mention)
  • Format kiểu gì? (markdown? HTML? plain?)
  • "Đẹp" nghĩa là gì? (nhiều spacing? ít spacing? có emoji?)
  • Giữ nguyên content hay có thể chỉnh sửa?

Kết quả: response thô, không nhất quán, user phải follow-up nhiều lần.

Cách 2: Dùng prompt template đã craft

User gõ slash command: /format plan.md

Đằng sau, MCP server đã định nghĩa prompt template:

Your goal is to reformat a document to be written with markdown syntax.

The id of the document you need to reformat is:
<document_id>
plan.md
</document_id>

Add in headers, bullet points, tables, etc as necessary.
Feel free to add in structure.
Use the 'edit_document' tool to edit the document.
After the document has been reformatted, provide a summary of
changes you made.

Guidelines:
- Preserve original meaning
- Use H2/H3 headers for sections
- Use bullet points for lists of 3+ items
- Use tables for comparative data
- Add code blocks with language hints for code snippets

Claude nhận prompt đã được tinh chỉnh bởi MCP server author. Kết quả:

  • Claude đọc đúng doc qua tool
  • Format theo guideline cụ thể
  • Report lại changes

User không phải là prompt engineer. Server author đã làm giúp — một lần, test kỹ, mọi user hưởng lợi.

Đó là giá trị cốt lõi của Prompts trong MCP.

Prompts là gì — Định nghĩa chính xác

Prompts là primitive MCP cho phép server expose message templates mà client (user) có thể kích hoạt. Thường xuất hiện trong UI như:

  • Slash commands (/format, /summarize, /review)
  • Buttons (workflow trigger trong Claude Desktop)
  • Menu items (phải chuột → Action)
┌──────────────────────────────────────────────────────────┐
│                                                          │
│   USER                                                   │
│     │ "/format plan.md"                                  │
│     ▼                                                    │
│   CLIENT (Claude Desktop, Cursor, CLI of you)            │
│     │ GetPromptRequest {                                 │
│     │   name: "format",                                  │
│     │   arguments: { doc_id: "plan.md" }                 │
│     │ }                                                  │
│     ▼                                                    │
│   MCP SERVER                                             │
│     │ Gọi format_document("plan.md")                     │
│     │ Return list[Message] với prompt đã interpolate     │
│     ▼                                                    │
│   CLIENT nhận messages                                   │
│     │ Send lên Claude API với tool definitions           │
│     ▼                                                    │
│   CLAUDE nhận prompt craft + tools → execute             │
│                                                          │
└──────────────────────────────────────────────────────────┘

Khác biệt với Tool và Resource

ToolResourcePrompt
Controlled byModelAppUser
Khi nào activate?Claude decideApp fetchUser click/type
UI presentationTool call (hidden)Content in promptSlash / button
Interactive?1-shotData injectTrigger workflow
Ví dụcreate_issue()github://repo/X/new-pr

Key insight: Prompts là cách server author đóng gói kiến thức prompt engineering thành UX đơn giản cho end-user. User không cần biết cách prompt tốt — họ chỉ cần click.

Decorator @mcp.prompt()

Định nghĩa prompt giống tool/resource nhưng return list of Messages thay vì data.

from mcp.server.fastmcp.prompts import base
from pydantic import Field


@mcp.prompt(
    name="format",
    description="Rewrites the contents of the document in Markdown format."
)
def format_document(
    doc_id: str = Field(description="Id of the document to format")
) -> list[base.Message]:
    prompt = f"""
Your goal is to reformat a document to be written with markdown syntax.

The id of the document you need to reformat is:
<document_id>
{doc_id}
</document_id>

Add in headers, bullet points, tables, etc as necessary.
Feel free to add in structure.
Use the 'edit_document' tool to edit the document.
After the document has been reformatted, provide a summary of
changes you made.
"""
    return [
        base.UserMessage(prompt)
    ]

Bóc tách chi tiết

@mcp.prompt(name, description)

  • name — slash command Claude Desktop sẽ show (user gõ /format)
  • description — hiển thị trong autocomplete / tooltip

Function parameters

  • Giống tool, dùng type hint + Field(description=...) để document
  • Client UI tự gen form yêu cầu user điền parameter

Return type: list[base.Message]

  • Message list gửi tới Claude
  • Có thể mix UserMessage và AssistantMessage để tạo few-shot

Body function — prompt craft

  • Đây là nơi bạn đầu tư thời gian
  • Dùng f-string interpolate parameter
  • Tag XML (<document_id>) giúp Claude parse rõ ràng

Cấu trúc Message list

Single UserMessage (đơn giản nhất)

return [base.UserMessage("Do this task...")]

Phổ biến cho workflow đơn lẻ.

Multi-turn với Assistant response

return [
    base.UserMessage("Show me an example of good code review."),
    base.AssistantMessage("""Great review comment:
    - Points out specific line
    - Suggests alternative
    - Explains why
    Example: 'Line 42: Consider using defaultdict instead...'"""),
    base.UserMessage(f"Now review this PR: {pr_url}"),
]

Few-shot pattern — Claude thấy ví dụ trước khi làm task thật.

Chain with context

return [
    base.UserMessage("Here's the document you'll work with."),
    base.AssistantMessage("I'll wait for your instructions."),
    base.UserMessage(f"Format doc {doc_id} as markdown."),
]

Ít dùng hơn, nhưng hữu ích để set up "state".

Case study: 4 prompt template thực tế

Prompt 1: format (đơn giản)

@mcp.prompt(name="format", description="Reformat document in Markdown")
def format_document(doc_id: str = Field(...)) -> list[base.Message]:
    return [base.UserMessage(f"""
Reformat document '{doc_id}' to Markdown.

Rules:
1. Add appropriate headers (H2/H3)
2. Convert lists to bullets
3. Preserve original meaning
4. Use edit_document tool to save changes

Return summary of changes when done.
""")]

Prompt 2: summarize (với options)

@mcp.prompt(name="summarize", description="Summarize document with configurable length")
def summarize(
    doc_id: str = Field(description="Document ID"),
    length: str = Field(description="Length: brief, medium, detailed", default="medium"),
) -> list[base.Message]:
    length_map = {
        "brief": "1-2 sentences",
        "medium": "1 paragraph (3-5 sentences)",
        "detailed": "3-4 paragraphs covering main points and supporting details",
    }
    return [base.UserMessage(f"""
Summarize document '{doc_id}' in {length_map[length]}.

Focus on:
- Main thesis or purpose
- Key findings or arguments
- Actionable conclusions (if any)

Use read_doc_contents tool to access the document.
""")]

Prompt 3: review (với few-shot)

@mcp.prompt(name="review", description="Code review with examples")
def review_code(
    doc_id: str = Field(description="Code file ID"),
) -> list[base.Message]:
    return [
        base.UserMessage("Review this code for a Python file."),
        base.AssistantMessage("""I'll provide review using this format:
- **Bugs:** issues that will cause failures
- **Style:** readability and convention
- **Performance:** optimization opportunities
- **Security:** potential vulnerabilities

Example for a quick function:

def add(a, b): return a+b

- Bugs: none
- Style: missing type hints, single-line
- Performance: fine
- Security: no input validation but low-risk for arithmetic
"""),
        base.UserMessage(f"Now review {doc_id} following the same format."),
    ]

Prompt 4: translate (với locale options)

@mcp.prompt(name="translate", description="Translate document to target language")
def translate(
    doc_id: str = Field(description="Document ID"),
    target_lang: str = Field(description="Target language code (e.g. 'vi', 'ja', 'es')"),
    preserve_formatting: bool = Field(description="Keep markdown/HTML formatting", default=True),
) -> list[base.Message]:
    formatting_note = "Preserve all markdown, HTML, and code blocks unchanged." if preserve_formatting else "Plain text output only."
    return [base.UserMessage(f"""
Translate document '{doc_id}' to {target_lang}.

Guidelines:
- {formatting_note}
- Keep technical terms in original when no good equivalent exists
- Maintain original tone (formal/informal) and style
- Use culturally appropriate phrasing

Steps:
1. read_doc_contents to access document
2. Translate
3. edit_document to save back (same doc_id with _translated suffix)
""")]

Bảng so sánh: Prompt vs các cách khác

CáchProsConsPhù hợp cho
MCP PromptReusable, versioned, server-maintainedPhải build serverChuyên workflow trong app
System prompt hard-codeĐơn giảnKhông customize đượcApp single-purpose
User gõ thẳngFlexibleInconsistent qualityAd-hoc exploration
Custom slash handler in appFull controlTied to app codeApp-specific shortcut

Khi nào chọn MCP Prompt:

  • Cần chia sẻ prompt qua nhiều client
  • Prompt complex, cần update độc lập với app
  • Muốn "đóng gói" expertise domain
  • App có workflow lặp đi lặp lại

Ví dụ thực chiến: Build format prompt đầy đủ

Tình huống

Bạn muốn user của document MCP server có command /format để reformat doc sang markdown.

Bước 1: Add prompt định nghĩa (~2 phút)

from mcp.server.fastmcp.prompts import base

@mcp.prompt(
    name="format",
    description="Rewrites the contents of the document in Markdown format."
)
def format_document(
    doc_id: str = Field(description="Id of the document to format")
) -> list[base.Message]:
    prompt = f"""
Your goal is to reformat a document to be written with markdown syntax.

The id of the document you need to reformat is:
<document_id>
{doc_id}
</document_id>

Add in headers, bullet points, tables, etc as necessary.
Feel free to add in structure.

Use the 'edit_document' tool to edit the document.
After the document has been reformatted, provide a summary of
changes you made.
"""
    return [base.UserMessage(prompt)]

Bước 2: Restart server, check Inspector (~1 phút)

uv run mcp dev mcp_server.py

Inspector → tab Prompts → thấy format. Click, nhập doc_id = "plan.md", Get Prompt.

Inspector show messages sẽ gửi Claude:

{
  "messages": [
    {
      "role": "user",
      "content": "Your goal is to reformat a document..."
    }
  ]
}

Notice {doc_id} đã được interpolate thành plan.md.

Bước 3: Verify prompt chất lượng (~5 phút)

Copy messages output vào Claude Desktop / Workbench. Chạy thử. Claude:

  • Hiểu task không?
  • Gọi tool đúng không?
  • Output quality có cao?

Iterate description / guidelines đến khi hài lòng.

Bước 4: Deploy (0 phút — đã xong)

MCP server restart → prompt available cho mọi client. Một lần craft, nhiều user hưởng lợi.

Kết quả

Không có prompt, user có thể tự gõ task format doc. Với prompt craft, output chất lượng cao hơn và nhất quán hơn qua mọi lần gọi.

Ví dụ theo ngành — Prompts library patterns

Writing tools

Prompts:

  • /outline {topic} — generate blog outline
  • /editorial {text} — proofread with style guide
  • /headline {article} — generate 5 headline variants

Pattern: Writer click command thay vì gõ prompt dài. Output nhất quán với brand voice.

Sales enablement

Prompts:

  • /prep-call {account} — compile briefing từ CRM data
  • /objection-handler {concern} — generate response talking points
  • /follow-up {call_notes} — draft follow-up email

Pattern (Michael Cohen workflow): SDR có "playbook" prompts. New rep onboard nhanh vì best-practice prompts sẵn có.

Legal

Prompts:

  • /contract-review {doc_id} — flag unusual clauses
  • /redline {original} {proposed} — compare versions
  • /precedent-search {case_topic} — find similar past cases

Pattern: Legal ops team craft prompts với guidance tuân thủ bar association. Junior associate dùng mà không lo sai legal ethics.

Data analysis

Prompts:

  • /sql-to-chart {query} — write SQL + viz recommendation
  • /metric-review {kpi} — analyze trend + flag anomaly
  • /cohort-analysis {segment} — structured cohort breakdown

Pattern: Analyst chuyên data viết prompt, product manager dùng. Democratize data work.

Medical

Prompts:

  • /patient-summary {patient_id} — aggregate chart review
  • /drug-check {medications} — interaction analysis
  • /diff-diagnosis {symptoms} — differential diagnosis outline

Pattern: Chief resident craft prompts tuân thủ medical guidelines. Trainee dùng với safety net.

Security

Prompts:

  • /threat-model {architecture} — STRIDE analysis
  • /pen-test-plan {target} — generate test plan
  • /ioc-lookup {indicator} — threat intelligence enrich

Pattern: Security team đóng gói expertise vào prompt. Dev/SRE self-serve basic security queries.

Anti-patterns — Những sai lầm cần tránh

Prompt rỗng / generic

Sai lầm:

@mcp.prompt(name="help")
def help_prompt():
    return [base.UserMessage("Help me")]
Tại sao là sai
Không cung cấp value trên câu user tự gõ. Pollute namespace.
Cách đúng
Chỉ tạo prompt khi có value add thật — guidance, structure, few-shot, domain knowledge.

Hard-code secret trong prompt body

Sai lầm:

@mcp.prompt(name="deploy")
def deploy_prompt():
    return [base.UserMessage(f"""
Use API key sk-xxx to deploy... # leak!
""")]
Tại sao là sai
Secret vào prompt → vào logs → vào Claude context → training data risk.
Cách đúng
Secret qua tool gọi env var, không qua prompt body.

Prompt quá dài/phức tạp

Sai lầm
Prompt 5000 token với 20 guideline.
Tại sao là sai
Claude overwhelm, miss instructions. Token cost cao.
Cách đúng
Concise. 500-1500 token là sweet spot. Nếu dài hơn, break thành nhiều prompt nhỏ.

Không test interpolation

Sai lầm
Deploy prompt với {doc_id} chưa bao giờ verify qua Inspector.
Tại sao là sai
Typo trong template → {doc_id} chưa replace → Claude thấy {doc_id} literal, confused.
Cách đúng
Luôn test qua Inspector "Get Prompt" trước khi commit.

Prompt phụ thuộc tool không có

Sai lầm
Prompt nói "Use the get_data tool..." nhưng server không define get_data.
Tại sao là sai
Claude gọi tool không tồn tại → confused, fabricate.
Cách đúng
Trong server reference chính xác tool đã define. Hoặc prompt generic không mention tool name cụ thể.

Mix ngôn ngữ inconsistent

Sai lầm
Prompt bắt đầu tiếng Anh, guideline tiếng Việt, tool call tiếng Anh.
Tại sao là sai
Claude output mixed language, lộn xộn.
Cách đúng
Chọn 1 ngôn ngữ, stay consistent. Nếu multi-language, set rõ expectation đầu prompt.

Mẹo nâng cao

Mẹo 1: Version prompts

@mcp.prompt(name="format_v2", description="...")
def format_v2(doc_id: str): ...

@mcp.prompt(name="format", description="... (legacy, prefer format_v2)")
def format_v1(doc_id: str): ...

Evolve prompt mà không break user đã quen command cũ.

Mẹo 2: Argument completion

MCP spec hỗ trợ argument completion — server back autocomplete cho prompt param.

@mcp.prompt_argument_completion("format", "doc_id")
async def complete_doc_id(prefix: str) -> list[str]:
    return [d for d in docs.keys() if d.startswith(prefix)]

User gõ /format re → popup report.pdf, request.md. UX gần như native.

Mẹo 3: Prompt composition

def _base_guidelines() -> str:
    return """
- Preserve original meaning
- Use appropriate formatting
- Cite sources when applicable
"""

@mcp.prompt(name="format")
def format(doc_id: str):
    return [base.UserMessage(f"""
Task: Format {doc_id}.

Guidelines:
{_base_guidelines()}

Specific format: markdown.
""")]

@mcp.prompt(name="summarize")
def summarize(doc_id: str):
    return [base.UserMessage(f"""
Task: Summarize {doc_id}.

Guidelines:
{_base_guidelines()}

Specific length: 1 paragraph.
""")]

Share guideline giữa nhiều prompt → update 1 chỗ → mọi prompt benefit.

Mẹo 4: Metrics về prompt usage

Server-side track:

  • Prompt nào được dùng nhất
  • Prompt nào fail (arg validation error)
  • Completion rate (tool followup thành công?)

Dùng để iterate — prompt ít dùng nên cải thiện discoverability hoặc deprecate.

Áp dụng ngay

Bài tập 1: Add format prompt (~10 phút)

Bước 1: Mở mcp_server.py. Import:

from mcp.server.fastmcp.prompts import base
Bước 2
Add format_document prompt như bài.
Bước 3
Save, restart mcp dev mcp_server.py.
Bước 4
Inspector tab Prompts → click format → nhập doc_id = "plan.md" → Get Prompt.
Bước 5
Ghi lại:
  • Output messages có đúng format không? ___________
  • {doc_id} đã interpolate? ___________
  • Prompt có đọc tự nhiên không? ___________

Bài tập 2: Viết prompt thứ hai (~15 phút)

Tự design một prompt mới cho document server. Ý tưởng:

  • /summarize doc_id length
  • /translate doc_id target_lang
  • /diff doc_id_a doc_id_b
Bước 1
Chọn 1, design params + guidelines.
Bước 2
Viết decorator và body prompt.
Bước 3
Test qua Inspector với các input khác nhau.
Bước 4
Ghi lại:
  • Tên prompt: ___________
  • Params: ___________
  • Prompt body bao nhiêu từ: ___________
  • Claude output có align với guideline không? ___________

Bài tập 3 (thử thách): Few-shot prompt (~15 phút)

Design prompt có 2-3 example (UserMessage + AssistantMessage) trước request cuối.

Ví dụ: /tag-extract {doc_id} — extract tags từ doc.

Pattern:

return [
    base.UserMessage("Extract tags from: 'Python tutorial for beginners'"),
    base.AssistantMessage("Tags: python, tutorial, beginner, programming"),
    base.UserMessage("Extract tags from: 'Deep learning with transformers'"),
    base.AssistantMessage("Tags: deep-learning, transformers, ml, neural-networks"),
    base.UserMessage(f"Extract tags from content of doc {doc_id}"),
]

Test: output có follow format example không?

Tóm tắt bài học

Những điều cốt lõi
  • Prompts là slash command craft sẵn — User trigger, server craft, Claude execute. User không cần là prompt engineer.
  • Decorator @mcp.prompt() giống tool/resource — Name, description, params với Field. Return list[base.Message].
  • Message list hỗ trợ multi-turn / few-shot — UserMessage + AssistantMessage mix để set up context, show example, sau đó task thật.
  • Server author đóng gói expertise — Prompt tốt = domain knowledge + best-practice + consistency. Deploy 1 lần, mọi user hưởng lợi.
  • Test qua Inspector bắt buộc — Verify interpolation, verify guidelines, verify Claude produces expected output trước khi ship.

Tài liệu tham khảo

Kiểm tra nhanh2 câu hỏi · chọn đáp án để xem giải thích
    Bạn đã hoàn thành bài này chưa?Tiến độ lưu trên trình duyệt của bạn.

    Khóa học cùng lộ trình