SSD Nodes Learn 🎉 VPS từ $4.99/tháng
Hướng dẫn Matt ConnorBởi Matt Connor

Agent skill, MCP server hay rules file: Chọn gì?

So sánh skill, MCP server và rules file theo token, context và công sức bảo trì. Biết khi nào nên dùng từng loại để coding agent không nạp thừa dữ liệu.

Agent skills so với MCP server và rules file: câu trả lời ngắn

Agent skill, MCP server và rules file đều đưa kiến thức vào ngữ cảnh của coding agent. Hãy chọn theo chức năng của phần kiến thức đó. MCP (model context protocol) dùng cho dữ liệu có thể thay đổi vào lần bạn tra cứu tiếp theo. Skill dùng cho một quy trình bạn có thể viết ra hôm nay và sau 6 tuần vẫn còn đúng. Rules file dùng cho một vài thông tin bắt buộc phải giữ nguyên trong mọi session.

Lựa chọn này có một cái giá, đó là context. Mỗi token dùng cho một instruction mà agent không cần là một token không thể dùng cho code mà agent đang đọc. Bạn cũng phải trả token đó ở mỗi lượt, vì toàn bộ context window được gửi lại cùng mỗi request. Vì vậy, câu hỏi hữu ích không phải là cơ chế nào có thể làm được việc. Trong hầu hết trường hợp, cả ba cơ chế đều làm được. Câu hỏi là cơ chế nào tốn ít nhất khi đang không hoạt động.

Chi phí của từng loại trước khi bạn sử dụng

Ba loại này được nạp ở những thời điểm khác nhau. Đây là điểm khác biệt chính.

File rules được nạp toàn bộ khi khởi động, trong mọi session, dù có liên quan hay không. Claude Code đọc CLAUDE.md khi bắt đầu mọi cuộc hội thoại và luôn nạp toàn bộ file, bất kể độ dài. Mục tiêu được tài liệu hóa là dưới 200 dòng cho mỗi file, vì file dài hơn vừa tốn thêm context, vừa khiến model tuân thủ kém hơn. Hai tác động này cùng chiều, nên file rules dài 900 dòng còn tệ hơn là không có.

Skill được nạp theo 2 giai đoạn. Khi khởi động, chỉ dòng description trong phần frontmatter SKILL.md của mỗi skill được đưa vào context. Nhờ đó, model biết skill tồn tại và biết sơ bộ khi nào cần áp dụng. Phần nội dung chính chỉ được nạp khi skill được gọi. Vì vậy, tài liệu tham chiếu dài 400 dòng gần như không tốn gì cho đến khi thực sự cần dùng.

Trước đây MCP server là loại tốn kém nhất. Đây cũng là điểm khiến phần lớn các so sánh bạn đọc hiện nay đã lỗi thời. Tool search được bật mặc định trong các phiên bản Claude Code hiện tại. Khi bắt đầu session, chỉ tên các tool và trường instructions của server được nạp; các schema JSON (JavaScript object notation) đầy đủ được trì hoãn cho đến khi Claude tìm kiếm các tool đó. Việc thêm một server không còn tốn hàng nghìn token ngay từ đầu. Tuy nhiên, nó vẫn tốn một phần context, và vẫn tốn toàn bộ ngay từ đầu trong các cấu hình tắt tool search.

ChartStartup and post-use context cost, estimated tokens
The data behind this chart
[
  {
    "label": "Rules file, 200 lines",
    "at_startup": "2,500",
    "after_use": "2,500"
  },
  {
    "label": "Skill, 12 KB body",
    "at_startup": 40,
    "after_use": "3,000"
  },
  {
    "label": "MCP server, tool search on",
    "at_startup": 500,
    "after_use": "3,200"
  },
  {
    "label": "MCP server, tool search off",
    "at_startup": "4,500",
    "after_use": "4,500"
  }
]

Đó là các con số ước tính, không phải số đo từ máy của bạn. Chúng được tính từ kích thước phần văn bản mà mỗi cơ chế nạp vào, với quy đổi khoảng 4 ký tự cho mỗi token: file rules 200 dòng có khoảng 10 KB markdown, phần mô tả skill có khoảng 160 ký tự, còn server cung cấp 12 tool có khoảng 18 KB schema cùng một block instructions 2 KB. Claude Code giới hạn mỗi phần mô tả tool và mỗi trường instructions của server ở mức 2 KB, nên phần này có giới hạn tối đa. Phần tiếp theo sẽ hướng dẫn bạn đọc số liệu thực tế của chính mình.

Hãy đọc 2 dòng đầu tiên cùng nhau. File rules tốn 2,500 token trong một session mà không ai cần đến nó. Skill tốn 40 token trong cùng session đó, và tốn 3,000 trong 1 trên 10 session có gọi skill. 2 dòng cuối là cùng một server, được hiển thị 2 lần với tool search lần lượt bật và tắt: 500 token so với 4,500. Chênh lệch này là lý do các khuyến cáo cũ về việc MCP làm phình context vẫn còn được nhắc lại.

Tool search cần model hỗ trợ các block tool_reference. Tính đến August 2026, các model này gồm Claude Sonnet 4.5, Haiku 4.5, Opus 4.5 và các phiên bản mới hơn. Claude Code tắt tool search khi ANTHROPIC_BASE_URL trỏ đến một host không phải first party, vì phần lớn proxy không chuyển tiếp các block đó. Đặt ENABLE_TOOL_SEARCH để kiểm soát hành vi này: false nạp toàn bộ schema ngay từ đầu, true trì hoãn tất cả schema, còn auto chỉ nạp schema ngay từ đầu khi tổng kích thước nằm trong 10% context window.

# Load schemas up front only if they fit in 5% of the window
ENABLE_TOOL_SEARCH=auto:5 claude

Câu hỏi quyết định là: dữ liệu có thay đổi giữa các lần gọi không?

Hãy hỏi điều này trước, vì nó loại ngay một lựa chọn. Nếu agent cần đọc hoặc ghi dữ liệu có thể khác đi vào lần kiểm tra tiếp theo, bạn cần một server. Ví dụ: issue tracker, database, dashboard giám sát hoặc API nội bộ của bạn (application programming interface). Việc ghi thông tin đó vào file không giải quyết được vấn đề, vì thông tin sẽ cũ ngay khi người khác chỉnh sửa bản ghi.

Nếu câu trả lời vẫn đúng sau sáu tuần mà không cần ai duy trì, bạn cần một skill. Ví dụ: checklist phát hành, quy trình migration, cấu trúc response lỗi hoặc quy ước viết test của repository này. Skill là một file trong git. Nó không có port, process hoặc failure mode nào ngoài việc chứa thông tin sai; code review có thể phát hiện lỗi đó.

Nếu đó là một sự thật phải áp dụng cho những công việc bạn chưa nghĩ đến, hãy đặt nó vào file rules. Run make lint before committing. Never push to main. Handlers live in src/api/handlers/. Mỗi mục một dòng. Khi một mục phát triển thành các bước, nó không còn là một sự thật mà đã trở thành một procedure, và nên được chuyển vào skill.

Khi chỉ cần một file rules

Các file rules được nạp từ nhiều vị trí, theo thứ tự từ phạm vi rộng nhất đến cụ thể nhất: file policy do hệ thống quản lý, ~/.claude/CLAUDE.md cá nhân của bạn, ./CLAUDE.md hoặc ./.claude/CLAUDE.md của project, và ./CLAUDE.local.md bị gitignore. Tất cả file được phát hiện sẽ được nối lại thay vì ghi đè lẫn nhau, và các file gần working directory hơn sẽ được đọc sau.

Claude Code đọc CLAUDE.md, không đọc AGENTS.md. Nếu repository của bạn đã có AGENTS.md cho các tool khác, đừng duy trì 2 bản vì chúng sẽ dần không còn đồng bộ.

ln -s AGENTS.md CLAUDE.md

Symlink không in gì nếu tạo thành công. Hãy bắt đầu một session, chạy /context, rồi xác nhận CLAUDE.md xuất hiện dưới Các file Memory. Nếu không thấy file này trong danh sách, agent chưa bao giờ đọc nó và viết lại câu chữ sẽ không giúp được gì. Nếu bạn cũng muốn thêm các dòng dành riêng cho Claude, hãy dùng dạng import thay thế và đặt chúng bên dưới dòng import.

@AGENTS.md

## Claude Code

Use plan mode for changes under `src/billing/`.

Có một điểm dễ nhầm ở đây. @path import không giúp tiết kiệm context. File được import sẽ được mở rộng và nạp khi khởi động cùng với file đã tham chiếu đến nó, tối đa 4 cấp. Chia một file rules dài 600 dòng thành 6 file import giúp con người dễ tổ chức hơn, nhưng không làm thay đổi token cost. Bạn nên đọc các quy ước đằng sau AGENTS.md và bản tương ứng dành cho con người trước khi chọn layout.

Điều thực sự giúp giảm cost là .claude/rules/ có trường paths. File rules có paths frontmatter chỉ được nạp khi agent truy cập một file khớp với một trong các pattern.

---
paths:
  - "src/api/**/*.ts"
---

# API rules

- Every endpoint validates its input.
- Use the standard error response shape.

Rule không có trường paths sẽ được nạp khi khởi động, với cùng priority như .claude/CLAUDE.md. Vì vậy, pattern nên dùng là các rule ngắn, luôn được áp dụng, cùng một danh sách paths cho những rule chỉ có ý nghĩa bên trong một directory.

Khi bạn cần một skill

Một skill là một thư mục chứa file SKILL.md. Skill cá nhân nằm tại ~/.claude/skills/<name>/SKILL.md và áp dụng cho mọi project trên máy của bạn. Skill của project nằm tại .claude/skills/<name>/SKILL.md, đi cùng repository và có thể được review trong pull request như mọi file khác.

mkdir -p ~/.claude/skills/summarize-changes
---
name: summarize-changes
description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
---

Run `git status` and `git diff` against the merge base.
Group the changes by intent, not by file.
Call out anything touching auth, migrations or deletions.

description là phần duy nhất của file đó được đưa vào context trước khi skill chạy, nên nó có hai nhiệm vụ. Phần này mô tả skill làm gì và cho biết khi nào nên dùng skill. Một description như “Giúp triển khai” không cung cấp cho model thông tin để đối chiếu với request, nên skill sẽ âm thầm không được kích hoạt và bạn kết luận rằng skill không hoạt động.

Tên thư mục trở thành command, nên ví dụ trên cung cấp cho bạn /summarize-changes. Trong skill cá nhân hoặc skill của project, frontmatter name chỉ đặt nhãn hiển thị trong các danh sách.

Khi một skill được gọi, nội dung đã render của nó được đưa vào cuộc hội thoại dưới dạng một message duy nhất và tiếp tục nằm trong context suốt phần còn lại của session. Claude Code không đọc lại file ở các turn sau. Hãy viết các instruction có hiệu lực lâu dài thay vì các bước chỉ dùng một lần, đồng thời giữ phần body ngắn gọn, vì từ thời điểm đó mỗi dòng đều trở thành chi phí lặp lại trong từng request. Sau khi auto-compaction, Claude Code gắn lại lần gọi gần nhất của từng skill, giữ 5,000 token đầu tiên của mỗi skill trong tổng budget 25,000 token. Nếu gọi nhiều skill lớn trong cùng một session, các skill cũ nhất sẽ bị loại bỏ hoàn toàn. Vì vậy, một skill có thể có vẻ không còn tác động sau một cuộc hội thoại dài. Hãy gọi lại skill để đưa nó trở lại. Khi cùng một quy trình áp dụng cho nhiều codebase, dùng chung một skill cho nhiều repository thay vì sao chép file.

Khi bạn cần một MCP server

Thêm một server chỉ cần một command, còn transport quyết định hình thức của nó.

# Remote HTTP server
claude mcp add --transport http notion https://mcp.notion.com/mcp

# Remote HTTP server behind a bearer token
claude mcp add --transport http secure-api https://api.example.com/mcp \
  --header "Authorization: Bearer your-token"

# Local stdio server: everything after -- is passed through untouched
claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
  -- npx -y airtable-mcp-server

-- rất quan trọng. Với một server stdio, nó phân tách các option của Claude Code khỏi command line dùng để khởi động server của bạn. Nếu bỏ qua, một --port 8080 dành cho server sẽ bị phân tích như một option của claude mcp add, rồi bị lệnh đó từ chối.

claude mcp list
claude mcp get notion

claude mcp add xác nhận bằng một dòng Added ..., nhưng dòng này chỉ cho biết cấu hình đã được ghi xuống disk. claude mcp list mới là command cho bạn biết trạng thái thực tế, vì nó in trạng thái health bên cạnh từng server: ✔ Connected, ! Needs authentication hoặc ✘ Failed to connect. Trạng thái failure nghĩa là Claude Code không thể kết nối đến server đó, không phải lệnh list bị lỗi. Trong một session, /mcp hiển thị cùng thông tin theo từng server và thêm số lượng tool.

Mỗi lần gọi đến một MCP server là độc lập và mang theo mọi thứ cần thiết. Đó là lý do MCP server không nhớ request trước đó của bạn. Đây là một lựa chọn thiết kế, và bạn phải chấp nhận hệ quả của nó: mọi state cần được lưu giữ phải nằm phía sau server, trong database hoặc file, và đó là thành phần bạn phải vận hành.

Một MCP server là một tiến trình bạn phải chạy

Đây là phần chi phí mà các bảng so sánh của vendor thường bỏ qua. Một skill là một file. MCP server là phần mềm chạy ở một nơi nào đó. Nếu nơi đó là VPS (virtual private server) của bạn, bạn phải chịu trách nhiệm về uptime của nó.

Trường hợp rẻ nhất là server stdio. Claude Code khởi chạy server này dưới dạng child process khi session bắt đầu, và process sẽ dừng khi session kết thúc. Bạn không cần monitor hay tự lên lịch patch cho nó. HTTP server từ xa là một service chạy lâu dài, nên cần mọi thứ mà một service chạy lâu dài cần.

[Unit]
Description=Notes MCP server
After=network-online.target
Wants=network-online.target

[Service]
User=mcp
WorkingDirectory=/srv/notes-mcp
ExecStart=/usr/bin/node /srv/notes-mcp/dist/server.js
Environment=PORT=8931
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now notes-mcp
systemctl is-active notes-mcp
journalctl -u notes-mcp -n 50 --no-pager

systemctl is-active phải in ra active. Nếu nó in ra failed, journal chứa nguyên nhân. Khi chạy lần đầu, nguyên nhân gần như luôn là thiếu biến môi trường hoặc một process khác đã bind vào cổng đó. Restart=on-failure không phải tùy chọn trong trường hợp này, vì MCP server bị crash sẽ không tự thông báo. Bạn chỉ phát hiện ra khi agent báo rằng nó không thể đọc issue tracker của bạn.

Bind process vào 127.0.0.1 và đặt một reverse proxy có TLS (transport layer security) phía trước nó. MCP server có thể truy cập database và trả lời trên một cổng public mà không có xác thực thì chẳng khác nào một database bạn đã public. Chạy MCP server trên VPS trình bày đầy đủ cách cấu hình proxy, certificate và firewall.

Sau đó, hãy tính trung thực phần công việc định kỳ. Service này nhận security update theo lịch riêng, không liên quan đến agent kết nối với nó. OAuth token của nó hết hạn, và claude mcp list bắt đầu in ! Needs authentication vào một thời điểm rất bất tiện. Credential của nó nằm trong một file cấu hình hoặc header Authorization, nên phải được bảo vệ như mọi secret khác. Đây là một chủ đề lớn riêng: giữ secret ngoài tầm với của AI agent. Skill không phát sinh bất kỳ công việc nào trong số đó.

Hãy cân nhắc phương án thay thế trước khi xây dựng. Nếu dữ liệu phía sau server đề xuất chỉ thay đổi khoảng một lần mỗi quarter, một skill cho agent biết cần tìm ở đâu và ý nghĩa của các field sẽ rẻ hơn một service mà bạn phải duy trì hoạt động.

Cách tự đo mức tiêu tốn context

Đừng ước tính nữa. Hãy chạy /context trong một session. Lệnh này in ra phần phân bổ khi khởi động: system prompt, các file memory, tools và MCP servers, cùng trọng số token của từng mục.

Kiểm tra hai điểm. Trong Memory files, xác nhận mọi file rules bạn mong đợi đều được liệt kê. Agent không thể thấy file bị thiếu, vì vậy đây là điều đầu tiên cần loại trừ khi agent bỏ qua hướng dẫn. Tiếp theo, xem chi phí của các server. Nếu một server bạn chỉ dùng hai lần mỗi tháng nằm trong số các mục lớn nhất của danh sách, hãy tắt nó trong /mcp và bật lại cho những session cần nó. Cấu hình vẫn được giữ nguyên.

Một remote server cũng có thể báo trạng thái như cached 2h ago · connects on first use · 5 tools. Điều đó nghĩa là Claude Code đã đọc danh sách tool từ session trước thay vì kết nối khi khởi động, và sẽ kết nối vào lần đầu tiên một tool được gọi. Các tool vẫn khả dụng ngay từ message đầu tiên, nên không cần sửa gì. Đặt MCP_DISCOVERY_CACHE=0 nếu bạn muốn mọi server đều kết nối khi khởi động. Để có cái nhìn tổng thể hơn, quản lý cửa sổ context của Claude Code giải thích những gì còn lại sau khi compaction, còn chi phí thực tế của các token đó chuyển các con số thành chi phí tiền bạc.

Vì sao skill của tôi không bao giờ được kích hoạt?

Nguyên nhân thường gặp là description. Đây là phần văn bản duy nhất trong context trước khi skill chạy, nên nếu nó không nêu rõ tình huống thì sẽ không có nội dung nào khớp. Hãy viết điều kiện kích hoạt ngay trong câu: "Dùng khi người dùng hỏi có gì thay đổi, muốn tạo commit message hoặc yêu cầu review diff của họ." Mô tả mơ hồ sẽ fail trong im lặng, nên rất khó phát hiện lỗi này.

Nguyên nhân thứ hai là lỗi chính tả trong frontmatter. Lỗi này được báo rõ ràng. Key không xác định sẽ bị từ chối ngay:

Unexpected key(s) in SKILL.md frontmatter: argument-hint. Allowed properties are: allowed-tools, compatibility, description, license, metadata, name

Nguyên nhân thứ ba là vị trí. Project skill được nạp từ .claude/skills/ trong thư mục làm việc và mọi thư mục cha cho đến repository root. Skill trong các thư mục con bên dưới vị trí bạn bắt đầu sẽ không được nạp khi khởi chạy. Chúng chỉ xuất hiện lần đầu agent đọc hoặc chỉnh sửa một file trong thư mục con đó. Vì vậy, trước thời điểm đó, chúng không xuất hiện trong autocomplete và không thể được gọi theo tên.

Tương đương MCP của lỗi im lặng này là một entry .mcp.jsonurl nhưng không có type. Claude Code coi mọi entry không có type là stdio server, nên bỏ qua entry đó và báo:

MCP server "notes" has a "url" but no "type"; add "type": "http" (or "sse" / "ws") to this entry

Dùng cả ba cùng nhau

Ba cơ chế này không cạnh tranh cùng một vị trí. Một setup hoạt động tốt sẽ dùng từng cơ chế ở nơi có chi phí thấp nhất. File rules chứa một số dòng áp dụng ở mọi nơi. Skills chứa các quy trình và chỉ được load khi phù hợp. Một MCP server, đôi khi là hai, kết nối với các hệ thống có nội dung không thể dự đoán trước. Nếu bạn vẫn đang xây dựng mental model về cơ chế đầu tiên, phần giải thích skill của agent thực sự là gì trình bày chi tiết về format.

Một phép thử có thể giải quyết phần lớn tranh luận về việc nên đặt một nội dung ở đâu. Xóa nội dung đó, bắt đầu session mới và giao task cho agent. Nếu agent chỉ chậm hơn, nội dung đó nên nằm trong skill. Nếu agent tự tin đưa ra câu trả lời sai, nội dung đó nên nằm trong file rules. Nếu agent hoàn toàn không lấy được thông tin, bạn cần server, đồng thời cần một kế hoạch duy trì server đó hoạt động.

FAQ

Tôi nên viết skill hay dựng MCP server?

Hãy quyết định dựa trên việc thông tin có thay đổi giữa các lần gọi hay không. Nếu agent phải đọc trạng thái trực tiếp mà người khác có thể chỉnh sửa, chẳng hạn issue tracker, database hoặc dashboard, bạn cần MCP server, vì mọi nội dung bạn ghi lại sẽ lỗi thời ngay khi bản ghi thay đổi. Nếu bạn có thể ghi câu trả lời một lần mà 6 tuần sau vẫn đúng, hãy viết một skill. Skill là một file trong git, không cần tiến trình để chạy, không cần mở port và không có lịch patch, nên đây là lựa chọn ít tốn kém hơn bất cứ khi nào có thể dùng được.

MCP server còn làm đầy context window của tôi không?

Ít hơn nhiều so với trước đây. Tool search được bật mặc định trong Claude Code hiện tại, nên lúc bắt đầu session chỉ có tên tool và trường instructions của server được nạp; schema đầy đủ chỉ được lấy khi Claude tìm kiếm chúng. Việc nạp ngay từ đầu vẫn xảy ra khi tool search bị tắt: với ENABLE_TOOL_SEARCH=false, với ANTHROPIC_BASE_URL trỏ đến một proxy không phải first party, hoặc trên model cũ hơn thế hệ Claude 4.5. Chạy /context để xem bạn đang ở trường hợp nào, vì các con số trong những bài so sánh cũ giả định việc nạp ngay từ đầu.

Claude Code có đọc AGENTS.md không?

Không. Claude Code đọc CLAUDE.md. Nếu repository của bạn đã có AGENTS.md cho các agent khác, hãy trỏ một file này đến file kia thay vì duy trì 2 bản sao. Chạy ln -s AGENTS.md CLAUDE.md để tạo symlink thông thường, hoặc đặt @AGENTS.md ở dòng đầu tiên của CLAUDE.md rồi thêm các instruction dành riêng cho Claude bên dưới. Sau đó bắt đầu một session và chạy /context để xác nhận CLAUDE.md xuất hiện trong Memory files.

Tại sao skill của tôi ngừng có tác dụng giữa session?

Auto-compaction thường là nguyên nhân. Khi conversation được tóm tắt, Claude Code gắn lại lần gọi gần đây nhất của từng skill, giữ lại 5,000 token đầu tiên của mỗi skill, trong tổng ngân sách 25,000 token cho tất cả skill. Claude Code cấp ngân sách từ skill được gọi gần đây nhất, nên nếu bạn đã gọi nhiều skill lớn, các skill cũ hơn sẽ bị loại bỏ hoàn toàn. Hãy gọi lại skill để khôi phục toàn bộ nội dung của nó.

Làm cách nào để ngăn một file rules dài được nạp trong mọi session?

Chuyển các phần chỉ đôi khi cần dùng sang các file .claude/rules/ có trường paths trong frontmatter, để mỗi file chỉ được nạp khi agent truy cập file phù hợp. Tách file thành các import @path không giúp ích, vì các file được import sẽ được mở rộng và nạp lúc khởi động cùng với file tham chiếu đến chúng. Mọi nội dung là quy trình nhiều bước thay vì thông tin thường trực nên trở thành skill, vì phần nội dung của skill không tốn chi phí cho đến khi được gọi.