Định nghĩa Prompts trên server

Xây dựng client & mở rộngTrung cấp30 phút

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:

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

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ư:

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

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.

  • Slash commands (/format, /summarize, /review)
  • Buttons (workflow trigger trong Claude Desktop)
  • Menu items (phải chuột → Action)
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
┌──────────────────────────────────────────────────────────┐
│                                                          │
│   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             │
│                                                          │
└──────────────────────────────────────────────────────────┘

Decorator @mcp.prompt()

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

Bóc tách chi tiết

@mcp.prompt(name, description)

Function parameters

Return type: list[base.Message]

Body function — prompt craft

  • name — slash command Claude Desktop sẽ show (user gõ /format)
  • description — hiển thị trong autocomplete / tooltip
  • Giống tool, dùng type hint + Field(description=...) để document
  • Client UI tự gen form yêu cầu user điền parameter
  • Message list gửi tới Claude
  • Có thể mix UserMessage và AssistantMessage để tạo few-shot
  • Đâ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
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)
    ]

Cấu trúc Message list

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

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

Multi-turn với Assistant response

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

Multi-turn với Assistant response

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

Chain with context

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}"),
]

Chain with context

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

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."),
]

Case study: 4 prompt template thực tế

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

Prompt 2: summarize (với options)

@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)

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

@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)

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

@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:

Case study: 4 prompt template thực tế (tiếp)

Prompt 4: translate (với locale options)

- 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

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
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

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)

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

# In mcp_server.py
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)

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

Inspector show messages sẽ gửi Claude:

uv run mcp dev mcp_server.py

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

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:

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.

  • Hiểu task không?
  • Gọi tool đúng không?
  • Output quality có cao?
{
  "messages": [
    {
      "role": "user",
      "content": "Your goal is to reformat a document..."
    }
  ]
}

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

📝 Writing tools

Prompts:

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

💼 Sales enablement

Prompts:

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

⚖️ Legal

Prompts:

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:

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

🏥 Medical

Prompts:

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

🔒 Security

Prompts:

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

  • /outline {topic} — generate blog outline
  • /editorial {text} — proofread with style guide
  • /headline {article} — generate 5 headline variants
  • /prep-call {account} — compile briefing từ CRM data
  • /objection-handler {concern} — generate response talking points
  • /follow-up {call_notes} — draft follow-up email
  • /contract-review {doc_id} — flag unusual clauses
  • /redline {original} {proposed} — compare versions
  • /precedent-search {case_topic} — find similar past cases
  • /sql-to-chart {query} — write SQL + viz recommendation
  • /metric-review {kpi} — analyze trend + flag anomaly
  • /cohort-analysis {segment} — structured cohort breakdown
  • /patient-summary {patient_id} — aggregate chart review
  • /drug-check {medications} — interaction analysis
  • /diff-diagnosis {symptoms} — differential diagnosis outline
  • /threat-model {architecture} — STRIDE analysis
  • /pen-test-plan {target} — generate test plan
  • /ioc-lookup {indicator} — threat intelligence enrich

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

❌ Prompt rỗng / generic

Sai lầm:

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="help")
def help_prompt():
    return [base.UserMessage("Help me")]

❌ Hard-code secret trong prompt body

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.

@mcp.prompt(name="deploy")
def deploy_prompt():
    return [base.UserMessage(f"""
Use API key sk-xxx to deploy... # leak!
""")]

Mẹo nâng cao

Mẹo 1: Version prompts

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(name="format_v2", description="...")
def format_v2(doc_id: str): ...

# Keep old for backward compat
@mcp.prompt(name="format", description="... (legacy, prefer format_v2)")
def format_v1(doc_id: str): ...

Mẹo 2: Argument completion

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

Mẹo 3: Prompt composition

@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)]

Mẹo 3: Prompt composition

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

Mẹo 4: Metrics về prompt usage

Server-side track:

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

  • Prompt nào được dùng nhất
  • Prompt nào fail (arg validation error)
  • Completion rate (tool followup thành công?)
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.
""")]

Áp dụng ngay

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

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

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:

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:

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:

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:

  • Output messages có đúng format không? ___________
  • {doc_id} đã interpolate? ___________
  • Prompt có đọc tự nhiên không? ___________
  • /summarize doc_id length
  • /translate doc_id target_lang
  • /diff doc_id_a doc_id_b
  • Tên prompt: ___________
  • Params: ___________
  • Prompt body bao nhiêu từ: ___________
  • Claude output có align với guideline không? ___________
from mcp.server.fastmcp.prompts import base

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

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

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}"),
]

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

🎯 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
  • MCP spec — Prompts
  • FastMCP Prompts module
  • "Defining prompts in MCP" — Anthropic Academy video
Nội dung này có hữu ích không?