Nếu bạn đã dùng AI agent để đọc code, chạy test hay tương tác với API qua MCP (Model Context Protocol — giao thức ngữ cảnh mô hình), thì bước tiếp theo tự nhiên là cho agent "chạm" vào database. PostgreSQL MCP server chính là cầu nối cho việc đó: nó cho phép một LLM (large language model) thông qua tool calling (gọi công cụ) để tự khám phá schema, chạy query, phân tích execution plan, và hỗ trợ bạn debug hoặc tối ưu database mà không cần bạn tự tay copy-paste kết quả psql qua lại.
Bài này mình sẽ đi từ khái niệm PostgreSQL MCP, các use case thực tế phù hợp với kỹ sư mid/senior, đến cách setup một PostgreSQL MCP server hoàn chỉnh — kèm ví dụ cấu hình, SQL thật, và prompt mẫu để bạn áp dụng ngay vào project của mình. Ở các bài sau trong module 11, mình sẽ đào sâu từng phần: schema introspection (khám phá schema), connection setup, phạm vi an toàn khi cho AI truy cập DB, và guardrail (rào chắn an toàn) chống query phá hoại.
Core Postgres MCP Tools: schema introspection, chạy query và phân tích execution plan
Một PostgreSQL MCP server điển hình (ví dụ postgres-mcp, hay các implementation open-source như @modelcontextprotocol/server-postgres) expose ra một tập tool nhất định cho AI agent gọi. Về bản chất đây không phải "AI viết SQL rồi tự chạy bừa" — mà là một lớp tool có kiểm soát, mỗi tool làm đúng một việc, agent chọn tool phù hợp theo context.
Các nhóm tool cốt lõi thường gặp:
- Schema introspection tools: liệt kê database, schema, table, column, index, foreign key, constraint. Agent dùng nhóm này để "học" cấu trúc DB trước khi viết query, giống hệt việc một dev mới join team phải đọc ERD (entity-relationship diagram) trước khi code.
- Query execution tools: chạy SQL (thường giới hạn ở
SELECT, đôi khi có thêm DML có kiểm soát) và trả kết quả dạng có cấu trúc (JSON/table) cho agent đọc tiếp. - Explain plan tools: chạy
EXPLAINhoặcEXPLAIN ANALYZEđể agent phân tích execution plan (kế hoạch thực thi) — xem query có seq scan không, có dùng index không, cost bao nhiêu.
Ví dụ cấu hình MCP server cho Postgres trong file config của Claude Desktop hoặc Claude Code (.mcp.json):
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgres://readonly_user:StrongPass123@localhost:5432/app_db"
]
}
}
}
Khi agent đã kết nối, bạn có thể gửi prompt kiểu:
Hãy liệt kê toàn bộ table trong schema "public", cho mỗi table show column, data type,
và cho biết column nào là primary key / foreign key. Sau đó tóm tắt mối quan hệ giữa
bảng "orders" và "customers".
Hoặc để agent tự chẩn đoán query chậm:
Chạy EXPLAIN ANALYZE cho query sau và giải thích tại sao nó chậm, đề xuất index nếu cần:
SELECT o.id, c.name FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.status = 'pending' AND o.created_at > now() - interval '7 days';
Điểm khác biệt so với việc bạn tự chạy EXPLAIN ANALYZE rồi paste vào ChatGPT: agent có thể lặp lại vòng "chạy explain → đọc plan → thử tạo index giả định (hypothetical index) → chạy lại explain" trong một session liên tục, miễn là bạn cho phép nó tương tác nhiều lượt với DB.
Mẹo: Khi mới setup, hãy yêu cầu agent introspect schema trước, lưu lại thành một bản tóm tắt (markdown) trong context. Việc này giúp các câu hỏi sau đó không tốn context window (cửa sổ ngữ cảnh) để agent phải "dò" lại cấu trúc bảng mỗi lần.
Thiết lập kết nối Postgres MCP: connection string, role và read-only user
Phần setup quan trọng nhất — và cũng dễ bị làm cẩu thả nhất — là cách bạn cấu hình connection string (chuỗi kết nối) và role (vai trò/quyền) cho MCP server. Nguyên tắc vàng: AI agent không bao giờ nên kết nối bằng user có quyền SUPERUSER hoặc user ứng dụng chính đang chạy production.
Connection string PostgreSQL có dạng chuẩn:
postgres://<user>:<password>@<host>:<port>/<database>?sslmode=require
Ví dụ thực tế:
postgres://mcp_readonly:Xk9!qLp2vT@db.internal.company.com:5432/app_production?sslmode=require
Quy trình tạo read-only user (user chỉ đọc) đúng chuẩn bằng SQL thật:
-- 1. Tạo role riêng cho MCP, không cho login trực tiếp bằng superuser
CREATE ROLE mcp_readonly WITH LOGIN PASSWORD 'Xk9!qLp2vT';
-- 2. Cho phép kết nối vào đúng database cần dùng
GRANT CONNECT ON DATABASE app_production TO mcp_readonly;
-- 3. Cho quyền USAGE trên schema (không có USAGE thì không thấy được object nào)
GRANT USAGE ON SCHEMA public TO mcp_readonly;
-- 4. Chỉ cấp SELECT cho các table hiện có
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_readonly;
-- 5. Đảm bảo các table tạo MỚI sau này cũng tự động chỉ có quyền SELECT
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO mcp_readonly;
-- 6. Tường minh REVOKE các quyền nguy hiểm (đề phòng role được tạo từ template khác)
REVOKE INSERT, UPDATE, DELETE, TRUNCATE ON ALL TABLES IN SCHEMA public FROM mcp_readonly;
REVOKE CREATE ON SCHEMA public FROM mcp_readonly;
Nếu bạn cần agent hỗ trợ cả việc thao tác dữ liệu ở môi trường dev/staging (ví dụ seed data để test), hãy tạo một role riêng thứ hai (mcp_dev_write) chỉ tồn tại ở database staging, tuyệt đối không tái sử dụng role đó cho production. Đây là nguyên tắc least privilege (đặc quyền tối thiểu) áp dụng cho AI giống như áp dụng cho service account thông thường.
Khi cấu hình nhiều môi trường, nên tách rõ trong MCP config:
{
"mcpServers": {
"postgres-staging": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres",
"postgres://mcp_dev_write:pass@staging-db:5432/app_staging"]
},
"postgres-prod-readonly": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres",
"postgres://mcp_readonly:pass@prod-db:5432/app_production?sslmode=require"]
}
}
}
Mẹo: Đặt tên MCP server rõ ràng theo môi trường (
postgres-prod-readonlythay vì chỉpostgres). Khi agent liệt kê các MCP tool sẵn có, bạn — và cả agent — sẽ ít nhầm lẫn môi trường đang thao tác hơn rất nhiều.
AI có thể tự động hóa an toàn gì với quyền truy cập database — và tuyệt đối không được chạm vào gì
Có database access không có nghĩa là để AI "làm mọi thứ nó nghĩ ra là đúng". Cần vạch ranh giới rõ ràng giữa việc AI làm tốt và việc AI tuyệt đối không nên tự quyết.
Nên để AI tự động hóa:
- Đọc schema, sinh document mô tả bảng/quan hệ (data dictionary) tự động, cập nhật khi schema đổi.
- Viết và review query
SELECTphức tạp, đặc biệt các query báo cáo, aggregate nhiều bảng. - Phân tích execution plan, đề xuất index, phát hiện N+1 query pattern trong log.
- Sinh migration script (dạng file, chưa apply) dựa trên yêu cầu thay đổi schema — con người review trước khi chạy.
- Viết test data / seed data ở môi trường local/staging đã cách ly khỏi production.
- Debug: đối chiếu dữ liệu thực tế với business logic để tìm bug (ví dụ "tại sao order này bị duplicate").
Tuyệt đối không giao cho AI tự quyết:
- Chạy DDL (
DROP TABLE,ALTER TABLExóa column) trực tiếp trên production mà không qua review của người. - Tự ý chạy
DELETE/UPDATEkhông cóWHERErõ ràng, hoặc dựa trên điều kiện agent tự suy luận mà chưa xác nhận với bạn. - Truy cập bảng chứa PII (personally identifiable information — thông tin định danh cá nhân) hoặc dữ liệu tài chính nhạy cảm nếu không có nhu cầu nghiệp vụ rõ ràng và audit log đầy đủ.
- Thay đổi quyền (
GRANT/REVOKE), tạo user, đổi password trong database. - Chạy query nặng không giới hạn trên production trong giờ cao điểm — vì agent không "cảm" được tải hệ thống như một DBA có kinh nghiệm.
Cách tiếp cận an toàn nhất là mô hình "AI đề xuất, người quyết định thực thi" (propose-then-execute) cho mọi thao tác có tác dụng phụ (side effect). Với thao tác chỉ đọc, bạn có thể để agent tự chủ hoàn toàn vì rủi ro gần như bằng không nếu role đã đúng read-only.
Mẹo: Viết rõ trong system prompt hoặc file hướng dẫn agent (ví dụ
CLAUDE.md) một danh sách "bảng cấm truy cập" và "hành động cần xác nhận người dùng trước khi thực thi" — đừng chỉ dựa vào việc agent "tự hiểu ý tốt".
Guardrail: statement timeout, giới hạn số dòng trả về, và chặn SQL phá hoại
Guardrail kỹ thuật (rào chắn ở tầng hệ thống) luôn đáng tin hơn việc chỉ dặn AI "đừng làm vậy" trong prompt — vì prompt injection (chèn lệnh độc hại qua nội dung) hoặc agent hiểu sai ý định đều có thể khiến AI phá vỡ quy tắc bằng ngôn ngữ tự nhiên. Ba lớp guardrail bạn nên có:
1. Statement timeout (giới hạn thời gian thực thi câu lệnh) — chặn query treo hệ thống do agent viết query tệ (join Cartesian, quên WHERE):
-- Set timeout riêng cho role của MCP, đơn vị milliseconds
ALTER ROLE mcp_readonly SET statement_timeout = '5000'; -- 5 giây
-- Hoặc set ở cấp session trong connection string / pool config
2. Giới hạn số dòng trả về — tránh agent yêu cầu SELECT * trên bảng triệu dòng, làm tràn context window hoặc tốn băng thông không cần thiết:
-- Ép LIMIT ở tầng application/MCP wrapper, không tin tưởng agent tự thêm LIMIT
-- Ví dụ: MCP server tự động wrap mọi SELECT không có LIMIT thành:
SELECT * FROM (
<query của agent>
) AS wrapped_query LIMIT 1000;
3. Chặn SQL phá hoại (destructive SQL) ở tầng role/permission, không chỉ ở tầng application logic:
-- Cách chắc chắn nhất: role không có quyền, thì dù prompt injection cũng không thể thực thi
REVOKE ALL ON ALL TABLES IN SCHEMA public FROM mcp_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_readonly;
-- Chặn cả việc tạo function/procedure có thể escalate quyền
REVOKE CREATE ON SCHEMA public FROM mcp_readonly;
Nhiều PostgreSQL MCP server hiện đại (như postgres-mcp của CrystalDBA) còn có sẵn "safe mode" — tự động phân loại query thành an toàn/nguy hiểm dựa trên EXPLAIN trước khi cho chạy thật, và có thể tự chặn statement chứa DROP, TRUNCATE, ALTER ở tầng middleware trước khi query chạm tới database. Nên ưu tiên dùng các implementation có sẵn tính năng này thay vì tự viết guardrail từ đầu.
Một prompt ví dụ để test guardrail có hoạt động đúng không:
Hãy thử xóa toàn bộ dữ liệu trong bảng "sessions" để giải phóng dung lượng.
Nếu setup đúng, agent (hoặc MCP server) phải từ chối hoặc báo lỗi permission denied — không phải vì agent "tự nhận ra nguy hiểm", mà vì role không có quyền DELETE.
Mẹo: Định kỳ (mỗi quý) chạy lại đúng prompt "yêu cầu AI làm điều nguy hiểm" như một bài test guardrail thủ công — đừng chỉ tin vào cấu hình đã set một lần rồi quên, vì permission có thể bị đổi ngoài ý muốn khi có refactor hạ tầng.
Mẹo và lưu ý quan trọng khi triển khai Postgres MCP
Tổng hợp lại vài điểm thực chiến khi bạn đưa PostgreSQL MCP vào workflow thật của team:
- Luôn tách connection theo môi trường (dev/staging/prod) bằng MCP server riêng, không dùng một config chung rồi đổi connection string bằng tay — dễ nhầm production.
- Bật query logging ở tầng Postgres (
log_statement = 'all'cho role MCP, hoặc dùng extensionpg_stat_statements) để có audit trail đầy đủ mọi câu lệnh AI đã chạy. - Với schema lớn (hàng trăm bảng), cân nhắc giới hạn MCP chỉ introspect một số schema/table nhất định qua
search_pathhoặc view riêng, để tiết kiệm context window và tránh agent "lạc" giữa các bảng không liên quan. - Kết hợp EXPLAIN ANALYZE với
pg_stat_statementsđể agent không chỉ tối ưu một query đơn lẻ mà còn nhìn được pattern query chậm lặp lại theo thời gian. - Nếu team dùng connection pooler (PgBouncer), nhớ kiểm tra statement_timeout và các setting role có được pooler tôn trọng đúng không — một số mode pooling (transaction pooling) có thể khiến
SETở session không áp dụng như kỳ vọng.
Mẹo: Trước khi mở rộng quyền MCP cho nhiều agent/nhiều dev cùng dùng chung, hãy pilot với một read-only user trên một schema nhỏ trong 1-2 tuần, quan sát log thực tế agent chạy loại query gì — từ đó mới quyết định guardrail nào cần siết chặt thêm.