khi làm việc với mcp server, tôi không bắt đầu bằng câu hỏi server này có bao nhiêu tool, kết nối được bao nhiêu dịch vụ, hay có thể trao cho agent bao nhiêu quyền. tôi bắt đầu bằng một câu hỏi thấp hơn, cơ bản hơn, nhưng cũng thực tế hơn rất nhiều. server này có thật sự khởi chạy được trong môi trường tự động hay không.
đó là điểm mà nhiều người bỏ qua. trong các buổi demo, mọi thứ thường trông khá ổn. server chạy được trên máy của dev, client kết nối được, vài lệnh được gọi thành công, và toàn bộ trải nghiệm tạo cảm giác rằng hệ thống đã sẵn sàng. nhưng khi đưa cùng server đó vào một môi trường headless, không có thao tác tay, không có cấu hình ẩn, không có secret được châm thêm ở phút cuối, rất nhiều thứ bắt đầu lộ ra. server có thể treo ngay lúc initialize. server có thể đòi biến môi trường mà tài liệu không mô tả rõ. server có thể ghi log lung tung ra stdout và tự phá giao thức stdio của chính nó. lúc đó, mọi tranh luận về security sâu hơn đều trở nên quá sớm.
vì vậy, với tôi, lớp kiểm tra đầu tiên không phải là security review theo nghĩa quen thuộc. lớp đầu tiên là launchability, tức khả năng khởi chạy sạch, lặp lại được, và vận hành đúng giao thức trong một kịch bản tự động hóa. nếu chưa qua được lớp này, mọi đánh giá phía sau đều thiếu nền tảng. một server không khởi chạy ổn định thì không thể trở thành một integration đáng tin cậy, dù ý tưởng của nó có hấp dẫn đến đâu.
đó cũng là lý do tôi rất quan tâm đến cách đánh giá mcp server theo hướng tất định và CI FIRST. thay vì nhìn bằng cảm giác, tôi muốn có một baseline rõ ràng. server được khởi chạy qua stdio. client thực hiện initialize. tiếp theo là tools/list. sau đó mới đến bước phân tích bề mặt tool mà server phơi ra. tôi muốn biết nó có tuân thủ giao diện không. tôi muốn biết schema đầu vào có đủ chặt không. tôi muốn biết mô tả tool có đủ rõ để con người lẫn agent hiểu đúng cách dùng không. tôi cũng muốn biết blast radius của các tool đó lớn đến đâu nếu giao cho agent gọi tự động.
cách nhìn này làm lộ ra một nghịch lý rất thú vị trong hệ sinh thái mcp. có khá nhiều server mà nếu đã chạy lên được thì điểm đánh giá không hề tệ. nhưng số lượng server chết ngay từ vòng đầu lại lớn hơn nhiều người tưởng. điều đó cho thấy vấn đề không hẳn là các server này quá nguy hiểm hay thiết kế hoàn toàn sai. vấn đề là rất nhiều server chưa đạt đến mức trưởng thành vận hành cơ bản để có thể được đánh giá một cách tử tế.
tôi xem đây là sự khác nhau giữa hai lớp rất rõ. lớp thứ nhất là preflight. nó chỉ trả lời một câu hỏi rất thực dụng. server có khởi chạy sạch và nói chuyện đúng giao thức hay không. lớp thứ hai mới là reviewability. ở lớp này tôi mới bàn đến chuyện tool surface có rõ ràng không, metadata có đủ không, quyền lực mà tool mở ra có cần sandbox mạnh hơn hay human approval hay không. nếu nhập hai lớp này làm một, hệ thống đánh giá sẽ méo ngay từ đầu, vì có quá nhiều thứ chết trước khi tới được bàn cân thật sự.
trong số các lỗi tôi gặp thường xuyên nhất, vấn đề schema đầu vào yếu là thứ đáng lưu ý nhất. nhiều server có tool nhìn qua thì có vẻ đầy đủ. tên tool nghe hợp lý. mô tả cũng không hẳn sai. nhưng khi mở input schema ra, mọi thứ lại quá lỏng. không rõ trường nào bắt buộc. kiểu dữ liệu không chặt. cho phép thêm thuộc tính tùy ý. mô tả trường quá mơ hồ. với con người, đôi khi ta vẫn đoán được cần gọi như thế nào. nhưng với agent, nhất là llm, vùng mơ hồ này là đất sống của suy đoán xác suất. hệ quả là lời gọi sai tham số, retry vô ích, hao token, và khó debug.
một schema yếu có thể trông như thế này:
</> JSON
{
"name": "send_message",
"description": "do something with a message",
"input_schema": {
"type": "object",
"properties": {},
"additionalProperties": true
}
}về mặt hình thức, tool vẫn tồn tại. nhưng trên thực tế, tool này buộc agent phải tự đoán gần như toàn bộ cấu trúc đầu vào. đó là một thiết kế kém cho môi trường tự động hóa. một phiên bản tốt hơn nên rõ ràng ngay từ biên của giao diện:
</> JSON
{
"name": "send_email",
"description": "send a plain text email to one recipient",
"input_schema": {
"type": "object",
"properties": {
"to": {
"type": "string",
"description": "recipient email address"
},
"subject": {
"type": "string",
"description": "email subject"
},
"body": {
"type": "string",
"description": "plain text content"
}
},
"required": ["to", "subject", "body"],
"additionalProperties": false
}
}sự khác biệt ở đây không nằm ở hình thức viết json đẹp hơn. sự khác biệt nằm ở chỗ interface đã được định nghĩa như một hợp đồng kỹ thuật thực sự. khi contract rõ, agent có ít không gian để tưởng tượng sai. khi contract mơ hồ, mỗi lời gọi tool đều trở thành một ván cược.
một lỗi khác nghe rất nhỏ nhưng lại phá hỏng hệ thống rất nhanh, đó là ghi text linh tinh ra stdout. trong giao tiếp stdio, stdout phải được giữ sạch cho json rpc. chỉ cần vài dòng log kiểu server started hay loading config xuất ra sai kênh là client có thể parse hỏng toàn bộ phản hồi.
ví dụ đơn giản như sau:
</> python
import sys
import json
print("server started")
sys.stdout.write(json.dumps({
"jsonrpc": "2.0",
"id": 1,
"result": {"tools": []}
}))
sys.stdout.flush()đoạn code này trông vô hại nếu chỉ nhìn bằng mắt thường. nhưng thực tế nó đã làm bẩn stdout trước khi json rpc được gửi đi. trong môi trường local, người viết có thể không để ý. trong môi trường client tự động, đây là lỗi đủ để server bị xem như không nói đúng giao thức.
nếu cần log, tôi luôn tách sang stderr:
</> python
import sys
import json
print("server started", file=sys.stderr)
sys.stdout.write(json.dumps({
"jsonrpc": "2.0",
"id": 1,
"result": {"tools": []}
}))
sys.stdout.flush()đây là ví dụ rất điển hình cho một lớp lỗi không thuộc phạm vi security theo nghĩa khai thác lỗ hổng, nhưng lại tác động trực tiếp đến độ tin cậy kỹ thuật của hệ thống. và khi một hệ thống không đáng tin cậy ở lớp giao tiếp cơ bản, mọi đánh giá về trust phía trên đều bị lung lay.
tôi cũng không nhìn điểm số như một phán quyết. một server điểm thấp không tự động có nghĩa là độc hại. một server điểm cao cũng không có nghĩa là an toàn tuyệt đối. điểm số chỉ là tín hiệu để tôi biết server đó có dễ review hay không, có vận hành nhất quán hay không, có mô tả giao diện đủ rõ hay không, và bề mặt quyền lực của nó lớn đến mức nào. chẳng hạn, những tool thiên về bộ nhớ hay truy xuất dữ liệu đọc thường dễ kiểm soát hơn các tool có khả năng tạo thư mục, sửa file hay ghi đè tài nguyên hệ thống. không phải vì tool ghi file là xấu về bản chất, mà vì blast radius của nó lớn hơn nhiều nếu bị agent dùng sai hoặc bị lạm dụng.
với góc nhìn đó, mcp server không còn là một tiện ích nhỏ gắn thêm cho vui. nó là một boundary thực sự của hệ thống. nó giống api. nó giống plugin. nó giống một package có quyền thực thi trong pipeline. hễ đã là boundary thì phải được review như boundary. phải có preflight. phải có contract rõ. phải có metadata đủ dùng. phải có nguyên tắc giảm quyền. và phải có khả năng kiểm tra lặp lại trong ci.
nếu đưa cách nghĩ này vào devsecops, ta sẽ thấy nó rất hợp tự nhiên. trước đây, nhiều nhóm chỉ hỏi integration này có nhiều tính năng không. bây giờ tôi tin rằng câu hỏi đúng hơn là integration này có reviewable không?, có deterministic enough để đặt vào pipeline không, và nếu giao cho agent thì agent sẽ được quyền gì, trong phạm vi nào, dưới cơ chế kiểm soát nào. đây là chỗ mà kỹ thuật thật sự quan trọng hơn demo.
một kịch bản quét cơ bản có thể bắt đầu rất đơn giản:
</> bash
mcp-scorecard scan --cmd python server.py
nếu muốn biến nó thành quality gate trong ci, ta có thể buộc ngưỡng tối thiểu và xuất machine readable artifact:
</> bash
mcp-scorecard scan \
--min-score 80 \
--json-out mcp-report.json \
--sarif mcp-report.sarif \
--cmd python server.pykhi làm vậy, tôi không kỳ vọng công cụ thay tôi đưa ra toàn bộ phán đoán chuyên môn. tôi chỉ cần nó cung cấp một lớp tín hiệu ổn định, có thể tái lập, và có thể audit được. đó là loại công cụ hữu ích nhất trong kỹ thuật, vì nó không cố thay thế người kỹ sư, mà làm cho quyết định của kỹ sư nhất quán hơn.
điểm tôi muốn nhấn mạnh ở cuối bài rất đơn giản. trong một hệ sinh thái đang tăng trưởng nhanh như mcp, người ta rất dễ bị hút về phía bề nổi. nhiều tool hơn. nhiều kết nối hơn. nhiều khả năng hơn. nhưng với tôi, thước đo trưởng thành đầu tiên vẫn là những thứ buồn tẻ hơn rất nhiều. server có khởi chạy sạch không. có nói đúng giao thức không. schema có đủ chặt không. metadata có đủ rõ không. quyền lực mà tool mở ra có được nhìn thấy và review được không.
nếu chưa trả lời được những câu hỏi đó, thì nói về security ở lớp cao hơn vẫn còn quá sớm. trước khi giao thêm quyền cho agent, tôi luôn muốn chắc một điều rất cơ bản. mcp server đứng phía sau phải thật sự chạy được, chạy sạch, và chạy theo cách mà một nhóm kỹ sư có thể kiểm tra được bằng quy trình nghiêm túc chứ không phải bằng niềm tin.



