vibeclaude.netvibeclaude.netvibeclaude.net
Tin tứcSkillsMCPThủ thuậtKhoá họcBảng giá
Đăng nhập
vibeclaude.net
  • Tin tức
  • Skills
  • MCP
  • Thủ thuật
  • Khoá học
  • Bảng giá
Đăng nhập
vibeclaude.netvibeclaude.net

Tin tức, skills, video và khoá học mới nhất về Claude AI bằng tiếng Việt.

Mục lục

  • Bắt đầu
  • Tin tức
  • Skills
  • MCP
  • Thủ thuật
  • Sản phẩm
  • Khoá học

Liên kết

  • Anthropic
  • Claude.ai
  • Anthropic Blog

© 2026 vibeclaude.net

Không phải sản phẩm chính thức của Anthropic. Mọi nhãn hiệu thuộc về chủ sở hữu của chúng.

📚Bài 3/12 · Series Xây MCP Server từ zero đến production: chuẩn kết nối AI với mọi hệ thốngMCP Tools, Resources và Prompts giải thích qua demo: hiểu 3 primitives của Model Context Protocol và khi nào dùng cái nào

MCP Tools, Resources và Prompts giải thích qua demo: hiểu 3 primitives của Model Context Protocol và khi nào dùng cái nào

Phân biệt 3 primitives cốt lõi của MCP qua demo thực tế: Tools (action có side effect), Resources (dữ liệu đọc qua URI) và Prompts (template workflow)

17 tháng 6, 2026· 2876 từ

Khi đọc spec MCP lần đầu, mình bị nhầm Tools với Resources suốt 2 ngày — cả hai đều "expose data cho LLM", vậy khác nhau ở đâu? Câu trả lời nằm ở 3 primitive: Tools (action có side effect), Resources (read-only data qua URI), và Prompts (template workflow user-invoked). Bài này mình dựng 1 MCP server FastMCP giả lập GitHub trong Python, implement cả 3 primitive trong cùng file, kèm bảng quyết định khi nào nên chọn cái nào và 1 lỗ hổng bảo mật vừa được phát hiện trong mcp-server-git.

3 primitives của MCP: Tools, Resources, Prompts là gì

Model Context Protocol (MCP) là một tiêu chuẩn mở được Anthropic giới thiệu vào tháng 11 năm 2024 [F1]. Nó cho phép các trợ lý AI tương tác với công cụ và nguồn dữ liệu bên ngoài, như hệ thống tệp hay API [F1]. Thay vì chỉ cung cấp các điểm cuối API, MCP tạo ra một lớp trừu tượng cấp cao bao bọc các chức năng này [F1].

MCP được xây dựng dựa trên ba primitive chính: Tools, Resources và Prompts [F2]. Việc hiểu rõ ba thành phần này giúp bạn quyết định nên dùng primitive nào cho từng trường hợp cụ thể. Chúng ta sẽ cùng tìm hiểu chi tiết từng loại.

Tools: Hành động và Side Effect

Tools cho phép các mô hình ngôn ngữ lớn (LLM) thực hiện các hành động cụ thể, ví dụ như tìm kiếm chuyến bay hoặc đặt lịch [F2]. Điểm đặc biệt của Tools là chúng thường có side effect, tức là chúng thay đổi trạng thái của hệ thống bên ngoài. Model sẽ tự quyết định khi nào cần gọi một Tool dựa trên ngữ cảnh hội thoại và mục tiêu của người dùng.

Resources: Dữ liệu Read-Only

Resources được dùng để đọc dữ liệu thông qua một URI (Uniform Resource Identifier) [F2]. Khác với Tools, Resources thường chỉ cung cấp dữ liệu read-only và không tạo ra side effect. Người dùng hoặc ứng dụng sẽ chủ động chọn và tải Resources vào ngữ cảnh của model khi cần thiết, giúp model truy cập thông tin liên quan mà không cần tự động thực hiện hành động.

Prompts: Template Workflow

Prompts là các template quy trình làm việc được người dùng kích hoạt [F2]. Chúng thường được gắn với các lệnh slash command (ví dụ: /summarize) và cho phép người dùng thực hiện một chuỗi hành động hoặc truy xuất thông tin theo một quy trình định trước. Prompts giúp chuẩn hóa các tác vụ lặp lại và cung cấp một giao diện dễ sử dụng cho người dùng cuối.

Để dễ hình dung, bạn có thể nghĩ về Tools như những động từ (verb) mà model có thể thực hiện, Resources như những danh từ (noun) mà model có thể đọc, và Prompts như những công thức (recipe) để hoàn thành một nhiệm vụ cụ thể. Việc phân biệt luồng điều khiển – ai kích hoạt (model, người dùng hay ứng dụng) – là yếu tố then chốt để chọn primitive phù hợp.

Hình minh họa cho phần 3 primitives của mcp: tools, resources, prompts là gì

Demo setup: server FastMCP để minh hoạ cả 3

Để minh hoạ rõ 3 primitive, mình dựng một MCP server giả lập GitHub trong cùng một file Python. Stack: Python 3.11 cộng SDK `mcp` chính thức của Anthropic [F1]. Trong SDK có module `FastMCP` — wrapper giúp khai báo Tool, Resource, Prompt qua decorator, đỡ phải tự viết JSON-RPC handler tay. Code dưới đây ngắn nhưng đủ để mount vào Claude Desktop và test ngay. Mục tiêu: thấy được 3 cú pháp đứng song song, để khi đọc phần sau bạn dễ phân biệt khi nào nên dùng cái nào.

# server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("github-demo")

@mcp.tool()
def search_issues(repo: str, query: str) -> list[dict]:
    """Search issues trong repo theo từ khoá."""
    # call GitHub API here
    return [{"number": 42, "title": f"match: {query}"}]

@mcp.resource("repo://readme/{owner}/{name}")
def get_readme(owner: str, name: str) -> str:
    """Đọc README của repo qua URI."""
    return f"# {owner}/{name}\n..."

@mcp.prompt()
def review_pr(pr_number: int) -> str:
    """Template để review pull request."""
    return (
        f"Review PR #{pr_number}: kiểm tra logic, "
        "test coverage, và security implications."
    )

if __name__ == "__main__":
    mcp.run(transport="stdio")

Ba decorator map trực tiếp sang 3 loại capability mà MCP định nghĩa [F2]. `@mcp.tool` đăng ký function thành Tool — Claude đọc signature và docstring để quyết định khi nào gọi. `@mcp.resource` nhận pattern URI với placeholder, client gọi qua URI cụ thể như `repo://readme/anthropic/claude-cookbook` để pull dữ liệu read-only. `@mcp.prompt` không thực thi code, nó chỉ trả về string — user chọn từ menu trong client rồi inject vào conversation. Type hint của return value (`list[dict]`, `str`) sẽ được SDK serialize tự động sang JSON khi gửi qua wire, bạn không cần marshal tay.

# Cài SDK (nên pin version cụ thể, xem note cuối section)
pip install "mcp[cli]"

# Chạy server local qua stdio
python server.py

# Hoặc test interactive bằng MCP Inspector
npx @modelcontextprotocol/inspector python server.py

Test interactive nhanh nhất là dùng MCP Inspector — tool chính thức để xem qua từng Tool, Resource và Prompt mà server expose, gọi thử với input tự chế. Sau khi yên tâm server chạy đúng, mount vào Claude Desktop bằng cách thêm entry vào `claude_desktop_config.json`. File này nằm trong `~/Library/Application Support/Claude/` trên macOS hoặc `%APPDATA%\Claude\` trên Windows. Restart Claude Desktop để load server, biểu tượng tool sẽ hiện trong khung chat nếu mọi thứ ok.

{
  "mcpServers": {
    "github-demo": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}

Một lưu ý quan trọng trước khi đem ra production: pin version SDK trong `requirements.txt` hoặc `pyproject.toml`. MCP còn đang evolve nhanh, signature decorator hoặc transport API có thể đổi giữa các minor version, code chạy hôm nay có thể vỡ tuần sau. Ngoài ra một số server MCP chính thức từng dính lỗ hổng bảo mật — ví dụ `mcp-server-git` của Anthropic có 3 lỗ hổng prompt injection ảnh hưởng tất cả version phát hành trước 8/12/2025, áp dụng cho cả cài đặt mặc định [F3]. Update sớm và đọc changelog là thói quen nên giữ khi làm với ecosystem còn mới như MCP.

Tools: khi nào dùng và cách thiết kế schema

Tools trong MCP cho phép các mô hình ngôn ngữ lớn (LLM) thực hiện các hành động cụ thể, ví dụ như tìm kiếm chuyến bay hoặc đặt lịch [F2]. Điểm khác biệt chính của Tools là chúng tạo ra các "side effect" – tức là thay đổi trạng thái của hệ thống bên ngoài. Ví dụ, tạo một issue mới trên GitHub, gửi email, hoặc cập nhật cơ sở dữ liệu.

Khi thiết kế Tool, mình cần định nghĩa input schema cho nó. Schema này thường dùng định dạng Pydantic hoặc JSON Schema. Model sẽ đọc mô tả và schema này để hiểu khi nào cần gọi Tool và cần những thông tin gì. Tên Tool nên theo dạng `verb_noun` rõ ràng, ví dụ như `create_issue` hoặc `search_flights`. Mô tả (description) của Tool cũng cần nêu rõ side effect mà nó tạo ra.

Dưới đây là một ví dụ về cách định nghĩa một Tool để đặt chuyến bay với input schema và kiểu trả về có cấu trúc:

{
  "name": "book_flight",
  "description": "Đặt một chuyến bay với các thông tin chi tiết được cung cấp. Hành động này sẽ tạo một đặt chỗ chuyến bay mới.",
  "input_schema": {
    "type": "object",
    "properties": {
      "departure_airport": {"type": "string"},
      "destination_airport": {"type": "string"},
      "departure_date": {"type": "string", "format": "date"},
      "return_date": {"type": "string", "format": "date"}
    },
    "required": ["departure_airport", "destination_airport", "departure_date"]
  },
  "output_schema": {
    "type": "object",
    "properties": {
      "booking_id": {"type": "string"},
      "status": {"type": "string"}
    }
  }
}

Một lưu ý quan trọng là model sẽ load toàn bộ schema của tất cả các Tool vào ngữ cảnh của nó. Nếu bạn có quá nhiều Tool hoặc schema quá phức tạp, điều này có thể làm tăng đáng kể lượng token sử dụng, dẫn đến chi phí cao hơn và latency chậm hơn. Do đó, hãy giữ schema ngắn gọn và chỉ định nghĩa những Tool thực sự cần thiết.

Khi bạn chỉ cần đọc dữ liệu cố định mà không tạo ra side effect, việc sử dụng Resource thường sẽ hiệu quả và tiết kiệm chi phí hơn so với Tool. Resource được thiết kế đặc biệt để đọc dữ liệu thông qua URI [F2].

Resources: dữ liệu qua URI và pattern application-controlled

Resources là primitive thứ hai trong MCP. Theo spec, resource cho phép đọc dữ liệu qua URI [F2]. Khác với tool — vốn dùng để thực thi hành động — resource đơn thuần là một nguồn dữ liệu read-only mà client gắn vào context khi cần. Model không tự gọi resource, nó chỉ đọc nội dung mà client cung cấp sẵn trong prompt.

Mỗi resource có một URI scheme do bạn tự định nghĩa. Một vài pattern phổ biến: `file:///home/user/project/main.py` cho source code, `db://postgres/users/schema` cho schema bảng, `github://owner/repo/issues/42` cho issue cụ thể. Server khai báo các resource khả dụng, sau đó client (Claude Desktop, Cursor, Cline) hiển thị danh sách để user chọn attach vào hội thoại.

from mcp.server import Server
from mcp.types import Resource

app = Server("db-server")

@app.list_resources()
async def list_resources() -> list[Resource]:
    return [
        Resource(
            uri="db://postgres/users/schema",
            name="Users table schema",
            mimeType="text/plain",
        )
    ]

@app.read_resource()
async def read_resource(uri: str) -> str:
    if uri == "db://postgres/users/schema":
        return """
CREATE TABLE users (
  id UUID PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  created_at TIMESTAMPTZ DEFAULT now()
);
"""
    raise ValueError(f"Unknown resource: {uri}")

Điểm phân biệt quan trọng nhất giữa Tool và Resource nằm ở câu hỏi: **ai quyết định gọi nó?** Tool là model-controlled — Claude tự xét context và quyết khi nào trigger `book_flight()`. Resource thì ngược lại, gọi là application-controlled hoặc user-controlled. App hoặc user chủ động attach `db://postgres/users/schema` vào prompt, model chỉ thấy nội dung sau khi client đã đính kèm.

Vì vậy resource phù hợp với dữ liệu mà user biết trước là cần đưa vào: file source code đang mở trong editor, DDL của table, config project (package.json, tsconfig.json), tài liệu nội bộ trong wiki, hay snapshot của object như git commit. Còn nếu cần model tự quyết định fetch — ví dụ "tìm issue mới nhất khớp keyword X" — đó là việc của tool, không phải resource.

Một mẹo hữu ích: dùng resource template với placeholder trong URI. Thay vì khai báo từng resource cụ thể, bạn đăng ký pattern dạng `github://{owner}/{repo}/readme` và server resolve động khi user request một cặp owner/repo cụ thể. Cách này phù hợp khi số resource lớn hoặc không biết trước danh sách — phổ biến với GitHub, Notion, Linear, nơi tài nguyên là tập hợp object động.

Hình minh họa cho phần resources: dữ liệu qua uri và pattern application-controlled

Prompts: template workflow user-invoked

Prompts trong MCP là các template có thể tái sử dụng, thường hiển thị dưới dạng slash command (lệnh gạch chéo) trong các client như Claude Desktop hay Cursor. Điểm khác biệt lớn nhất giữa Prompt với Tool và Resource là Prompt do chính người dùng chủ động chọn và kích hoạt, chứ không phải model tự động gọi dựa trên ngữ cảnh hội thoại [F2].

Mình thường dùng Prompts cho các workflow lặp đi lặp lại. Ví dụ như `/review-pr` để yêu cầu Claude xem lại một pull request, `/generate-test` để tạo test case cho một hàm, hay `/summarize-meeting` để tóm tắt nội dung cuộc họp. Các Prompts này giúp chuẩn hóa đầu vào và đầu ra cho các tác vụ thường xuyên.

Khi bạn chọn một Prompt, nó sẽ nhận các đối số (arguments) từ người dùng, sau đó render thành một tập hợp các messages chuẩn hóa để gửi đến model. Model sẽ xử lý các messages này và thực hiện tác vụ mong muốn. Bạn cũng có thể nhúng kèm Resource trong response của Prompt để cung cấp ngữ cảnh ngay lập tức cho model.

import { definePrompt } from '@anthropic-ai/model-context-protocol';

const reviewPrPrompt = definePrompt({
  name: 'review-pr',
  description: 'Review a GitHub Pull Request',
  input: {
    type: 'object',
    properties: {
      pr_number: { type: 'number', description: 'Pull Request number' }
    },
    required: ['pr_number']
  },
  async handler(input) {
    const { pr_number } = input;
    return [
      { type: 'text', text: `Please review Pull Request #${pr_number}.` },
      { type: 'tool_code', text: `github.pull_request.get(${pr_number})` } // Example: embed tool call
    ];
  }
});

Anthropic đã giới thiệu primitive elicitation vào tháng 9 năm 2025 để mở rộng khả năng tương tác của Prompts, giúp chúng linh hoạt và mạnh mẽ hơn nữa.

So sánh nhanh và bảng quyết định khi nào dùng cái nào

Sau khi đi qua từng primitive, mình đặt 3 cái cạnh nhau để bạn dễ chọn. MCP có 3 building block cốt lõi: Tools cho action, Resources cho đọc data qua URI, và Prompts cho workflow template [F2]. Khác biệt chính nằm ở 3 trục: ai quyết định gọi, có side effect không, và client phát hiện ra sao.

  • Tool — model tự quyết định gọi trong lúc reason. Có side effect (ghi DB, gửi mail, tạo PR). Client auto-discover qua `tools/list`.

  • Resource — app hoặc user chọn attach vào context. Read-only, không thay đổi state ngoài. Discover qua URI hoặc resource list.

  • Prompt — user chủ động invoke, thường qua slash command. Không tự chạy gì, chỉ inject template vào conversation. Discover qua menu UI.

Decision tree để chọn primitive thì khá thẳng. Bạn muốn AI thực hiện hành động thay đổi state — book vé, commit code, update record — thì Tool. Bạn muốn cung cấp data tham khảo cho model đọc, không cần model gọi function — schema DB, file config, API spec — thì Resource. Bạn có workflow lặp lại với cùng kiểu prompt — code review, debug log, viết test — thì Prompt.

3 anti-pattern hay gặp

Thứ nhất: nhồi mọi thứ vào Tool. Mình thấy nhiều server expose `read_file` như Tool trong khi đáng lẽ phải là Resource. Hậu quả là mỗi lần model muốn xem file, nó tốn 1 round-trip và 1 slot trong tool list. Resource cho phép user attach file thẳng vào context, rẻ và rõ ràng hơn.

Thứ hai: ship 20 prompt template kiểu `review-code`, `review-code-strict`, `review-code-typescript`. Model đủ mạnh để tự reason từ 1 prompt tổng quát. Quá nhiều template làm UI rối và user quên cái nào để làm gì. Thứ ba và phổ biến nhất: server chỉ ship Tool, bỏ qua Resource và Prompt. Lý do là client thường support Tool tốt nhất, nhưng đó là miss opportunity. Bạn đang ép model gọi function cho việc lẽ ra chỉ cần đưa data vào context, hoặc lặp lại workflow lẽ ra một slash command là xong.

Lưu ý bảo mật và bước tiếp theo

Trước khi mount MCP server vào Claude Desktop, có một vấn đề bảo mật bạn cần biết. Các nhà nghiên cứu an ninh mạng đã phát hiện 3 lỗ hổng prompt injection trong mcp-server-git chính thức của Anthropic, cho phép attacker thao túng trợ lý AI thực hiện hành động không mong muốn mà không cần truy cập trực tiếp vào hệ thống [F3]. Các lỗ hổng này ảnh hưởng tới mọi phiên bản phát hành trước ngày 8/12/2025 và áp dụng cho cấu hình mặc định [F3]. Nếu bạn đang dùng server git này, update là việc đầu tiên cần làm trước khi đọc tiếp phần dưới.

Trong 3 primitives, Tools là loại rủi ro cao nhất vì có side effect — ghi file, gọi API tốn tiền, commit code, gửi mail. Resources và Prompts chủ yếu phục vụ đọc dữ liệu và template nên blast radius thấp hơn [F2]. Quy tắc của mình: mọi destructive action (delete, force push, payment, drop table) phải có human approval, không bao giờ auto-approve. Ngay cả Resource description cũng có thể chứa prompt injection ẩn nếu bạn load nội dung từ source không tin cậy — đừng để LLM đọc raw HTML từ web mà chưa sanitize.

  1. Audit version mcp-server và các package phụ thuộc, ưu tiên bản phát hành sau 8/12/2025 [F3]

  2. Thêm allowlist domain/path cho Tools đụng tới filesystem hoặc network

  3. Bật human-in-the-loop cho mọi action ghi (write/delete/commit)

  4. Log đầy đủ tool call để audit khi có incident

Bước tiếp theo nếu muốn đào sâu: đọc spec chính thức tại [modelcontextprotocol.io](https://modelcontextprotocol.io) để nắm protocol layer (JSON-RPC over stdio/SSE). Sau đó cài MCP Inspector — công cụ debug local — và test server bằng tay trước khi cắm vào Claude. Cách này cho bạn thấy chính xác schema input/output mà không phải đọc log Claude Desktop.

MCP mới được Anthropic giới thiệu tháng 11/2024 và đang tiến hóa nhanh [F1]. Tools/Resources/Prompts là 3 primitive đủ cover phần lớn use case hiện tại, nhưng spec đang bổ sung thêm các capability mới — đáng theo dõi nếu bạn đang xây tool nội bộ cho team. Phần kế tiếp mình sẽ deep dive vào cách viết MCP server custom trong 50 dòng TypeScript, expose API nội bộ cho Claude một cách an toàn.

Tóm lại: Tools cho action có side effect, Resources cho data đọc qua URI, Prompts cho workflow template — chọn đúng primitive sẽ giảm 50% confusion khi design MCP server. Nếu bạn muốn xem demo chạy trực tiếp và phần giải thích chi tiết hơn về schema, có thể tham khảo video gốc được link trong phần đầu bài.

📚 Trong series này

← Bài 2

Build MCP server đầu tiên bằng Python: tutorial từng bước với FastMCP và Claude Desktop

Bài 4 →

Kết nối Claude với database qua MCP server: demo Postgres tutorial cho Claude Desktop truy vấn SQL bằng ngôn ngữ tự nhiên

Xem toàn bộ series →

⚠️ Tự động tổng hợp bằng AI

Bài viết được hỗ trợ tạo bởi AI — vui lòng xem video gốc để tham khảo trực tiếp.