Agent skills, MCP server hay rules file: chọn gì?
So sánh skill, MCP server và rules file theo chi phí token, thời điểm nạp context và công sức bảo trì. Biết quy tắc chọn cơ chế ít tốn nhất.
Agent skills, MCP server và rules file: câu trả lời ngắn gọn
Agent skills, MCP server và rules file đều đưa kiến thức vào context của coding agent. Hãy chọn theo mục đích của kiến thức đó. MCP (model context protocol) dành cho dữ liệu có thể thay đổi vào lần truy vấn tiếp theo. Skill dành cho một quy trình mà hôm nay bạn có thể viết ra và sau sáu tuần vẫn đúng. Rules file dành cho một vài thông tin bắt buộc phải được áp dụng trong mọi session.
Lựa chọn này có chi phí, và chi phí đó 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 turn, vì toàn bộ context window được gửi lại trong mỗi request. Vì vậy, câu hỏi hữu ích không phải là cơ chế nào có thể thực hiện công 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 cơ chế trước khi sử dụng
Ba cơ chế này được nạp ở các thời điểm khác nhau. Thời điểm nạp là điểm khác biệt chính.
Rules file được nạp toàn bộ khi khởi động, trong mọi session, bất kể có liên quan hay không. Claude Code đọc CLAUDE.md khi bắt đầu mọi conversation và luôn nạp toàn bộ, không phụ thuộc vào độ dài. Tài liệu chính thức khuyến nghị mỗi file dưới 200 dòng, vì file dài hơn vừa tốn thêm context, vừa khó được tuân thủ đầy đủ. Hai tác động này cùng làm hiệu quả giảm, nên rules file dài 900 dòng còn tệ hơn là không có.
Skill được nạp theo hai giai đoạn. Khi khởi động, chỉ dòng description trong 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 dùng. Phần nội dung chỉ được nạp khi skill được invoke. Vì vậy, một 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.
Trước đây, MCP server là cơ chế 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 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 có chi phí, và trong các cấu hình tắt tool search, toàn bộ chi phí vẫn phát sinh ngay từ đầu.
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"
}
]Đây là các con số ước tính, không phải số đo từ máy của bạn. Chúng dựa trên kích thước phần văn bản mỗi cơ chế nạp vào, với quy đổi khoảng bốn ký tự cho mỗi token: rules file 200 dòng chiếm khoảng 10 KB markdown, mô tả skill dài 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 cắt ngắn từng mô tả tool và từng 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 các số liệu thực tế của chính mình.
Hãy đọc hai hàng đầu tiên cùng nhau. Rules file 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ó invoke skill. Hai hàng cuối là cùng một server, được tính hai lần với tool search lần lượt bật và tắt: 500 token so với 4,500. Khoảng chênh này là lý do các khuyến nghị 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 này. Đặt ENABLE_TOOL_SEARCH để điều khiển chế độ này: false nạp mọi 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 của chúng nằm trong 10% context window.
# Load schemas up front only if they fit in 5% of the window
ENABLE_TOOL_SEARCH=auto:5 claudeCâu hỏi quyết định là: dữ liệu có thay đổi giữa các lần gọi hay 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 thứ 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, monitoring dashboard hoặc API nội bộ của bạn (application programming interface). Ghi thông tin đó vào file không giúp ích, vì nội dung bạn ghi sẽ stale ngay khi người khác chỉnh sửa bản ghi. Codebase của bạn cũng thuộc nhóm này, vì cấu trúc của nó thay đổi sau mỗi commit. Đây là lý do nên cung cấp cho agent map đã parse của repository qua MCP thay vì mô tả cấu trúc trong một file sẽ dần lỗi thời.
Nếu câu trả lời vẫn đúng sau sáu tuần mà không cần ai bảo trì, bạn cần một skill. Ví dụ: release checklist, quy trình migration, cấu trúc của các error response hoặc cách repository này yêu cầu viết test. Skill là một file trong git. Nó không có port, process hay 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 fact phải áp dụng cho những công việc bạn chưa nghĩ đến, hãy đặt nó vào rules file. Run make lint before committing. Never push to main. Handlers live in src/api/handlers/. Mỗi fact viết trên một dòng. Khi một entry phát triển thành các bước thực hiện, nó không còn là fact mà đã trở thành procedure, và nên được chuyển vào một skill.
Khi chỉ cần file rules
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. Các file gần working directory hơn sẽ được đọc sau cùng.
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ì hai bản sao vì chúng sẽ dần lệch nội dung.
ln -s AGENTS.md CLAUDE.mdSymlink không in ra gì nếu 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 bên dưới Memory files. Nếu không thấy mục này, agent chưa từng đọc file đó và việc viết lại câu chữ sẽ không giúp ích gì. Nếu bạn cũng muốn thêm các dòng chỉ dành cho Claude, hãy dùng dạng import và đặt các dòng đó bên dưới import.
@AGENTS.md
## Claude Code
Use plan mode for changes under `src/billing/`.Có một điểm dễ mắc lỗi ở đây. @path import không làm giảm context. File được import sẽ được mở rộng và nạp khi khởi chạy cùng với file tham chiếu đến nó, tối đa bốn cấp. Chia một file rules 600 dòng thành sáu file import chỉ giúp con người dễ tổ chức hơn, còn token cost thay đổi chính xác bằng không. Bạn nên đọc các quy ước phía sau AGENTS.md và bản tương ứng hướng đến người dùng trước khi chọn layout.
Thứ thực sự giúp giảm cost là .claude/rules/ có trường paths. File rules có paths trong 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 chạy 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 với một danh sách paths trên mọi thứ chỉ có ý nghĩa bên trong một directory.
Khi bạn muốn một skill
Một skill là một thư mục chứa 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ó 2 nhiệm vụ. Nó cho biết skill làm gì và cho biết khi nào cần dùng skill đó. Một description như "Hỗ trợ deploy" không cung cấp đủ thông tin để model đối chiếu với request, nên skill âm thầm không bao giờ đượ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 display label trong các danh sách.
Sau khi một skill được invoke, nội dung đã render của nó sẽ được đưa vào conversation dưới dạng một message duy nhất và vẫn giữ nguyên trong 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 nội dung ngắn gọn vì từ thời điểm đó mỗi dòng đều tạo thêm chi phí lặp lại cho từng request. Sau khi auto-compaction, Claude Code sẽ gắn lại lần invoke 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 invoke nhiều skill lớn trong cùng một session, các skill cũ nhất sẽ bị loại hoàn toàn. Vì vậy, một skill có thể dường như không còn tác dụng sau một conversation dài. Invoke lại skill đó để đưa nó trở lại. Một skill có nhiều procedure cho thấy rõ sự đánh đổi này: skill unlazy và phương pháp Depth Tree sử dụng đáng kể context cho các gate và plan file, đổi lại là agent không còn sớm tuyên bố công việc đã hoàn tất. Khi cùng một procedure áp dụng cho nhiều codebase, hãy 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 MCP server chỉ cần một command, còn transport quyết định cách server đó hoạt động.
# 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 stdio server, nó phân tách các option riêng của Claude Code khỏi command line dùng để khởi động server. Nếu bỏ qua, một --port 8080 dành cho server sẽ bị phân tích như option của claude mcp add, rồi bị command này từ chối.
claude mcp list
claude mcp get notionclaude mcp add xác nhận bằng một dòng Added .... Dòng này chỉ cho biết cấu hình đã được ghi vào disk. claude mcp list mới là command cho bạn biết trạng thái thực tế, vì nó in health status 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 list command bị lỗi. Trong một session, /mcp cho cùng chế độ xem theo từng server và hiển thị 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. Đây 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ả: mọi state cần giữ lại phải nằm phía sau server, trong database hoặc file, và đó là một thành phần bạn phải vận hành.
Một MCP server là một process 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 ở đâu đó. Nếu nó chạy trên VPS (virtual private server) của bạn, bạn phải tự chịu trách nhiệm về uptime của nó.
stdio server là trường hợp ít tốn công nhất. Claude Code khởi chạy nó dưới dạng child process khi session bắt đầu, rồi process kết thúc khi session kết thúc. Bạn không cần monitor hay patch nó theo lịch riêng. HTTP server từ xa là một service chạy lâu dài. 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.targetsudo systemctl daemon-reload
sudo systemctl enable --now notes-mcp
systemctl is-active notes-mcp
journalctl -u notes-mcp -n 50 --no-pagersystemctl is-active phải in ra active. Nếu nó in ra failed, journal sẽ chứa nguyên nhân. Trong lần chạy đầu tiên, nguyên nhân gần như luôn là thiếu environment variable hoặc cổng đã bị process khác chiếm. 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ó. Một MCP server có thể truy cập database và trả lời trên public port mà không có authentication thực chất là một database bạn đã công khai. Chạy MCP server trên VPS trình bày đầy đủ phần 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 đang giao tiếp với nó. OAuth token của nó sẽ hết hạn, và claude mcp list bắt đầu in ra ! Needs authentication vào một thời điểm bất tiện. Credential của nó nằm trong config file hoặc header Authorization, nên cần được bảo vệ như mọi secret khác. Đây là một chủ đề 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 quý, một skill chỉ 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 đo chi phí context của chính bạn
Đừng tiếp tục ước tính. Hãy chạy /context trong một session. Lệnh này in ra phần phân bổ lúc 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.
Hãy kiểm tra 2 đ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, nên đây là điều đầu tiên cần loại trừ khi instructions bị bỏ qua. Nếu file đã được liệt kê nhưng rule vẫn bị bỏ qua, nguyên nhân nằm ở nơi khác hoàn toàn. Các lý do agent bỏ qua một instruction mà nó vẫn nhìn thấy đáng để kiểm tra trước khi bạn lại sửa dòng đó. Sau đó xem các server tiêu tốn bao nhiêu. Nếu một server bạn dùng 2 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 đó có nghĩa là Claude Code đã đọc danh sách tool từ session trước thay vì kết nối lúc khởi động. Nó 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 từ message đầu tiên, nên không có gì cần sửa. Đặt MCP_DISCOVERY_CACHE=0 nếu bạn muốn mọi server đều kết nối lúc khởi động. Để có cái nhìn tổng thể hơn, quản lý context window của Claude Code giải thích nội dung nào còn lại sau khi compaction, còn chi phí thực tế của các token đó quy đổi các con số thành tiền.
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à nội dung duy nhất trong context trước khi skill chạy. Vì vậy, nếu nó không nêu đúng tình huống thì skill sẽ không 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." Mô tả mơ hồ sẽ âm thầm thất bại, nên khó nhận ra nguyên nhân.
Nguyên nhân thứ hai là lỗi chính tả trong frontmatter, và lỗi này sẽ hiển thị rõ. Key không được hỗ trợ sẽ bị từ chối ngay:
Unexpected key(s) in SKILL.md frontmatter: argument-hint. Allowed properties are: allowed-tools, compatibility, description, license, metadata, nameNguyên nhân thứ ba là vị trí. Project skill được tải từ .claude/skills/ trong working directory và mọi thư mục cha cho đến repository root. Skill nằm trong các thư mục con bên dưới vị trí bạn bắt đầu sẽ không được tải khi khởi chạy. Chúng chỉ xuất hiện lần đầu agent đọc hoặc chỉnh sửa file bên trong thư mục con đó. Trước thời điểm này, chúng không xuất hiện trong autocomplete và không thể được gọi bằng tên.
Tương đương MCP của lỗi âm thầm này là một entry .mcp.json có url nhưng không có type. Claude Code đọc mọi entry không có type dưới dạng stdio server, nên bỏ qua entry đó và hiển thị:
MCP server "notes" has a "url" but no "type"; add "type": "http" (or "sse" / "ws") to this entryDùng cả ba cùng nhau
Ba cơ chế này không cạnh tranh cùng một vị trí. Một cấu hình hiệu quả sẽ dùng từng cơ chế ở nơi phù hợp và ít tốn kém 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 nạp khi cần. Một MCP server, đôi khi là hai, kết nối đến những 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 mô hình tổng quan về cơ chế đầu tiên, bài viết giải thích agent skill thực sự là gì trình bày chi tiết về format.
Có một phép thử giúp giải quyết hầu hết tranh luận về vị trí phù hợp của một nội dung. 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 có kế hoạch duy trì server đó hoạt động.
FAQ
Tôi nên viết skill hay triển khai một 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 hiện tại 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 một MCP server, vì mọi nội dung bạn ghi lại sẽ cũ 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 process để chạy, không cần mở port và không có lịch cập nhật, 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 trước rất nhiều. 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 toàn bộ 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 bên thứ nhất, 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 rằng toàn bộ được nạp 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 dành cho các agent khác, hãy trỏ một file này đến file kia thay vì giữ hai 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 một CLAUDE.md rồi thêm các chỉ dẫn 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 mục Memory files.
Tại sao skill của tôi ngừng có tác dụng giữa chừng trong session?
Auto-compaction thường là nguyên nhân. Khi cuộc hội thoại đượ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 phân bổ ngân sách từ skill được gọi gần đây nhất. Vì vậy, 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. Gọi lại skill để khôi phục toàn bộ nội dung.
Làm thế nào để ngăn một file rules dài được nạp trong mọi session?
Chuyển những phần chỉ đôi khi cần dùng vào 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 cố định nên được chuyển thành skill, vì phần nội dung của skill không tốn chi phí cho đến khi được gọi.