prompts.chatprompts.chatprompts.chat
BotPromptKỹ năngQuy trìnhDanh mụcThẻCao thủ PromptPhong cách
SáchCho trẻ emNhà phát triển
Đăng nhập
CC0 2026 prompts.chat
DeepWikiHướng dẫn...Tài liệuAPIQuyền riêng tưĐiều khoảnHỗ trợGiới thiệuGitHub
F

Fatih Kadir Akın

@f

446prompt
0lượt bình chọn nhận được
0đóng góp
Tham gia about 5 hours ago
446 đóng góp trong năm qua
Tháng 11
Tháng 12
Tháng 1
Tháng 2
Tháng 3
Tháng 4
Tháng 5
Tháng 6
Tháng 7
Tháng 8
Tháng 9
Tháng 10
M
W
F
Ít
Nhiều
Video
Trình tạo Video Ẩm thực Siêu thực (Hyperrealistic Food Video Creator)

Đóng vai một nhà quay phim ẩm thực chuyên nghiệp. Nhiệm vụ của bạn là tạo ra các video ẩm thực siêu thực phù hợp cho thực đơn, quảng cáo và ấn phẩm truyền thông. Đảm bảo chất lượng hình ảnh cao với kết cấu chân thực, bố cục kích thích vị giác và ánh sáng hoàn hảo.

Hãy đóng vai một Nhà quay phim Ẩm thực Chuyên nghiệp (Professional Food Videographer). Bạn là một chuyên gia trong việc tạo ra các video ẩm thực cuốn hút và kích thích vị giác.

Nhiệm vụ của bạn là sản xuất một video ẩm thực siêu thực (hyperrealistic) tập trung vào:
- Kết cấu bề mặt chân thực
- Bố cục trình bày hấp dẫn, kích thích vị giác
- Điều kiện ánh sáng hoàn hảo

Bạn sẽ:
- Ghi hình từng món ăn với sự chăm chút tỉ mỉ đến từng chi tiết nhỏ
- Làm nổi bật sức hấp dẫn thị giác của món ăn
- Đảm bảo video phù hợp để sử dụng cho advertisements hoặc trên nền tảng menus

Các ràng buộc:
- Duy trì phong cách quay quảng cáo thương mại chuyên nghiệp
- Sử dụng các kỹ thuật ảnh chuyển động (moving photo techniques) để tạo hiệu ứng thị giác sống động
MarketingContent Creation
F@f
0
Hướng Dẫn Sáng Tác Nhạc Darksynth Synthwave
Âm thanh

Hướng dẫn chi tiết về cấu trúc và phong cách để sáng tác nhạc darksynth synthwave, đầy đủ các yếu tố âm hưởng sâu lắng và hoài niệm. Lý tưởng để tạo ra những bản nhạc có nhịp điệu cuốn hút, phối khí nhiều lớp và chất cổ điển thập niên 1980.

Phong cách: nhạc darksynth synthwave kết hợp âm hưởng điện tử (electronic) và nhạc nền không gian (ambient), hoài niệm, bí ẩn, tràn đầy hy vọng, năng lượng dồn nén tăng dần, tốc độ 108 BPM, nhịp độ vừa phải (moderato), cảm giác lướt đi mạnh mẽ, đàn tổng hợp âm thanh (synthesizer), đàn guitar điện, nổi bật tiếng synthesizer, giọng hát nam trầm ấm pha hơi thở, trau chuốt, giàu không khí, sản xuất âm nhạc phối khí nhiều lớp, âm thanh thập niên 1980, dày dặn và đậm chất điện ảnh với sự ấm áp của thiết bị analog, viết ở giọng La thứ (Am), retrowave, outrun, hoài niệm thập niên 80, ánh đèn neon, chuyến lái xe xuyên đêm

Cấu trúc bài nhạc:
[INTRO] Tiếng đệm synth pad giàu không khí hiện dần lên (fade-in)
[VERSE] Nhịp trống mạnh mẽ cuốn hút bắt đầu cùng giọng hát
[PRE-CHORUS] Dồn nén đẩy sự căng thẳng lên cao
[CHORUS] Phối khí đầy đủ toàn bộ nhạc cụ, giai điệu vút cao
[VERSE] Đoạn verse thứ hai, bổ sung thêm các lớp âm thanh mới
[CHORUS] Lặp lại điệp khúc với các biến tấu
[BRIDGE] Đoạn lắng xuống (Breakdown), lược bớt nhạc cụ chỉ giữ lại phần cốt lõi
[DROP] Điệp khúc cuối cùng bùng nổ với năng lượng mạnh mẽ hơn
[OUTRO] Nhỏ dần âm lượng (Fade out) để lại đuôi vang (reverb tail) ngân dài

Lời bài hát:
Chủ đề: ký ức về một thành phố rực ánh đèn neon chưa từng tồn tại
F@f
0
Người đàn ông trong Thành phố
Hình ảnh
Người đàn ông trong Thành phố

Một bức ảnh khắc họa hình ảnh một người đàn ông giữa khung cảnh thành phố.

Tạo một bức ảnh chụp một người đàn ông tại Istanbul, sử dụng các biến số có thể tùy chỉnh sau đây:

- **Địa điểm (Location)**: Bao gồm các địa danh biểu tượng của Istanbul như Galata Tower, Blue Mosque, hoặc Bosphorus.
- **Thời điểm trong ngày (Time of Day)**: Ghi lại khung cảnh vào lúc sunrise, noon, hoặc sunset để tạo ra những bầu không khí cảm xúc khác nhau.
- **Trang phục (Attire)**: Cho người đàn ông mặc trang phục theo phong cách casual, business, hoặc traditional để phản ánh sự đa dạng về gu thời trang.
- **Hoạt động (Activity)**: Người đàn ông có thể đang walking, sitting, hoặc looking out over the city để truyền tải những câu chuyện khác nhau.

Hãy sử dụng các biến số này để kiến tạo một khung cảnh nhiếp ảnh độc đáo, phản ánh nền văn hóa sôi động và bầu không khí đa sắc màu của Istanbul.
TravelArtCreative Writing
F@f
0
Chuyên gia Sửa lỗi từ Sentry (Sentry Bug Fixer)
Văn bản

Xác định và sửa các lỗi phần mềm từ báo cáo theo dõi lỗi của Sentry, đảm bảo hiệu năng ứng dụng vận hành trơn tru.

Hãy đóng vai một Chuyên gia Sửa lỗi từ Sentry (Sentry Bug Fixer). Bạn là chuyên gia trong việc gỡ lỗi và giải quyết các sự cố phần mềm bằng hệ thống theo dõi lỗi Sentry.
Nhiệm vụ của bạn là đảm bảo các ứng dụng vận hành trơn tru bằng cách xác định nguyên nhân và khắc phục các lỗi do Sentry báo cáo.
Bạn sẽ:
- Phân tích các báo cáo của Sentry để hiểu rõ bản chất các lỗi
- Sắp xếp thứ tự ưu tiên xử lý lỗi dựa trên mức độ ảnh hưởng của chúng
- Triển khai các giải pháp để sửa triệt để những lỗi đã xác định
- Kiểm thử ứng dụng để xác nhận các bản sửa lỗi hoạt động đúng
- Tài liệu hóa những thay đổi đã thực hiện và truyền đạt rõ ràng cho nhóm phát triển
Quy tắc:
- Luôn sao lưu trạng thái hiện tại trước khi thực hiện bất kỳ thay đổi nào
- Tuân thủ các tiêu chuẩn viết mã và phương pháp thực hành tốt nhất
- Xác minh kỹ lưỡng các giải pháp trước khi triển khai lên môi trường thực tế
- Duy trì giao tiếp rõ ràng với các thành viên trong nhóm
Các biến số:
- projectName - tên dự án mà bạn đang làm việc
- high - mức độ nghiêm trọng của lỗi
- production - môi trường nơi lỗi đang xảy ra
CommunicationDebugging
F@f
0
Trình Xây Dựng Máy Chủ MCP (MCP Builder)
Kỹ năng

Hướng dẫn tạo các máy chủ MCP (Model Context Protocol) chất lượng cao giúp các mô hình ngôn ngữ lớn (LLM) tương tác với các dịch vụ bên ngoài thông qua các công cụ được thiết kế bài bản. Sử dụng khi xây dựng máy chủ MCP để tích hợp các API hoặc dịch vụ bên ngoài, dù bằng Python (FastMCP) hay Node/TypeScript (MCP SDK).

---
name: mcp-builder
description: Hướng dẫn tạo các máy chủ MCP (Model Context Protocol) chất lượng cao giúp các mô hình ngôn ngữ lớn (LLM) tương tác với các dịch vụ bên ngoài thông qua các công cụ được thiết kế bài bản. Sử dụng khi xây dựng máy chủ MCP để tích hợp các API hoặc dịch vụ bên ngoài, dù bằng Python (FastMCP) hay Node/TypeScript (MCP SDK).
license: Complete terms in LICENSE.txt
---

# Hướng Dẫn Phát Triển Máy Chủ MCP (MCP Server Development Guide)

## Tổng Quan

Tạo các máy chủ MCP (Model Context Protocol - Giao thức Ngữ cảnh Mô hình) cho phép các LLM tương tác với những dịch vụ bên ngoài thông qua các công cụ (tools) được thiết kế bài bản. Chất lượng của một máy chủ MCP được đo lường bằng mức độ hiệu quả mà nó giúp các LLM hoàn thành những tác vụ thực tế.

---

# Quy Trình Thực Hiện

## 🚀 Luồng Công Việc Tổng Quan

Việc tạo ra một máy chủ MCP chất lượng cao bao gồm bốn giai đoạn chính:

### Giai Đoạn 1: Nghiên Cứu Chuyên Sâu và Lập Kế Hoạch

#### 1.1 Hiểu Thiết Kế MCP Hiện Đại

**Độ Bao Phủ API so với Công Cụ Theo Luồng Công Việc (Workflow Tools):**
Cân bằng giữa việc bao phủ toàn diện các điểm cuối (endpoints) của API với các công cụ chuyên biệt theo luồng công việc. Các công cụ theo luồng công việc có thể tiện lợi hơn cho những tác vụ cụ thể, trong khi độ bao phủ API toàn diện mang lại cho tác tử (agents) sự linh hoạt để tự kết hợp các thao tác với nhau. Hiệu năng sẽ khác nhau tùy theo từng trình khách (client)—một số trình khách hưởng lợi từ khả năng thực thi mã nguồn kết hợp các công cụ cơ bản, trong khi những trình khách khác lại hoạt động tốt hơn với các luồng công việc cấp cao. Khi chưa chắc chắn, hãy ưu tiên độ bao phủ API toàn diện.

**Đặt Tên Công Cụ và Khả Năng Dễ Tìm Kiếm:**
Tên công cụ rõ ràng, giàu tính mô tả sẽ giúp tác tử tìm thấy đúng công cụ cần dùng một cách nhanh chóng. Hãy sử dụng các tiền tố nhất quán (ví dụ: `github_create_issue`, `github_list_repos`) và đặt tên theo hướng hành động.

**Quản Lý Ngữ Cảnh (Context Management):**
Các tác tử làm việc hiệu quả hơn khi mô tả công cụ ngắn gọn súc tích và có khả năng lọc/phân trang (filter/paginate) kết quả trả về. Hãy thiết kế các công cụ trả về dữ liệu có trọng tâm và thực sự liên quan. Một số trình khách hỗ trợ thực thi mã nguồn có thể giúp tác tử tự lọc và xử lý dữ liệu một cách hiệu quả.

**Thông Báo Lỗi Có Tính Hành Động:**
Thông báo lỗi cần hướng dẫn tác tử tìm ra hướng giải quyết kèm theo các gợi ý cụ thể và bước tiếp theo cần làm.

#### 1.2 Nghiên Cứu Tài Liệu Giao Thức MCP

**Duyệt đặc tả kỹ thuật MCP:**

Bắt đầu từ sơ đồ trang web (sitemap) để tìm các trang có liên quan: `https://modelcontextprotocol.io/sitemap.xml`

Sau đó truy xuất các trang cụ thể với hậu tố `.md` để lấy định dạng markdown (ví dụ: `https://modelcontextprotocol.io/specification/draft.md`).

Các trang quan trọng cần xem xét:
- Tổng quan đặc tả kỹ thuật và kiến trúc hệ thống
- Các cơ chế truyền tải (streamable HTTP, stdio)
- Định nghĩa về công cụ (tool), tài nguyên (resource) và lời nhắc mẫu (prompt)

#### 1.3 Nghiên Cứu Tài Liệu Framework

**Ngăn xếp công nghệ khuyên dùng:**
- **Ngôn ngữ**: TypeScript (hỗ trợ SDK chất lượng cao và khả năng tương thích tốt trong nhiều môi trường thực thi, ví dụ như MCPB. Hơn nữa, các mô hình AI rất giỏi sinh mã TypeScript nhờ mức độ phổ biến rộng rãi, hệ thống kiểu tĩnh và các công cụ kiểm tra mã nguồn linting tốt)
- **Phương thức truyền tải (Transport)**: Streamable HTTP cho các máy chủ từ xa, sử dụng JSON phi trạng thái (stateless JSON — đơn giản hơn để mở rộng quy mô và bảo trì so với các phiên có trạng thái và phản hồi phân luồng phức tạp). Dùng stdio cho các máy chủ chạy cục bộ.

**Tải tài liệu hướng dẫn framework:**

- **Thực hành Tốt nhất về MCP**: [📋 Xem Thực Hành Tốt Nhất](./reference/mcp_best_practices.md) - Các nguyên tắc cốt lõi

**Đối với TypeScript (khuyên dùng):**
- **TypeScript SDK**: Sử dụng WebFetch để tải `https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/README.md`
- [⚡ Hướng Dẫn TypeScript](./reference/node_mcp_server.md) - Các mẫu thiết kế và ví dụ bằng TypeScript

**Đối với Python:**
- **Python SDK**: Sử dụng WebFetch để tải `https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/main/README.md`
- [🐍 Hướng Dẫn Python](./reference/python_mcp_server.md) - Các mẫu thiết kế và ví dụ bằng Python

#### 1.4 Lập Kế Hoạch Triển Khai

**Tìm hiểu API:**
Xem xét tài liệu API của dịch vụ cần tích hợp để xác định các điểm cuối chính, yêu cầu xác thực và mô hình dữ liệu. Sử dụng công cụ tìm kiếm web và WebFetch khi cần thiết.

**Lựa Chọn Công Cụ:**
Ưu tiên độ bao phủ API toàn diện. Liệt kê danh sách các điểm cuối cần triển khai, bắt đầu từ những thao tác phổ biến nhất.

---

### Giai Đoạn 2: Triển Khai Mã Nguồn (Implementation)

#### 2.1 Thiết Lập Cấu Trúc Dự Án

Xem các hướng dẫn dành riêng cho từng ngôn ngữ để thiết lập dự án:
- [⚡ Hướng Dẫn TypeScript](./reference/node_mcp_server.md) - Cấu trúc dự án, package.json, tsconfig.json
- [🐍 Hướng Dẫn Python](./reference/python_mcp_server.md) - Tổ chức module, các thư viện phụ thuộc

#### 2.2 Triển Khai Hạ Tầng Cốt Lõi

Tạo các tiện ích dùng chung:
- Trình khách gọi API (API client) kèm cơ chế xác thực
- Các hàm hỗ trợ xử lý lỗi
- Định dạng phản hồi đầu ra (JSON/Markdown)
- Hỗ trợ phân trang (pagination)

#### 2.3 Triển Khai Các Công Cụ (Tools)

Đối với mỗi công cụ:

**Lược đồ Đầu vào (Input Schema):**
- Sử dụng Zod (TypeScript) hoặc Pydantic (Python)
- Đưa vào các ràng buộc dữ liệu và phần mô tả rõ ràng
- Bổ sung ví dụ minh họa ngay trong phần mô tả trường dữ liệu

**Lược đồ Đầu ra (Output Schema):**
- Định nghĩa `outputSchema` ở những nơi có thể cho dữ liệu có cấu trúc
- Sử dụng `structuredContent` trong phản hồi của công cụ (tính năng của TypeScript SDK)
- Giúp các trình khách hiểu và xử lý đầu ra của công cụ dễ dàng hơn

**Mô Tả Công Cụ (Tool Description):**
- Tóm tắt ngắn gọn chức năng của công cụ
- Mô tả chi tiết các tham số
- Lược đồ kiểu dữ liệu trả về

**Cài Đặt Logic (Implementation):**
- Sử dụng Async/await cho các thao tác I/O
- Xử lý lỗi đúng chuẩn kèm thông báo có hướng dẫn hành động cụ thể
- Hỗ trợ phân trang ở những nơi phù hợp
- Trả về cả nội dung văn bản lẫn dữ liệu có cấu trúc khi sử dụng các bộ SDK hiện đại

**Chú Giải Công Cụ (Annotations):**
- `readOnlyHint`: true/false
- `destructiveHint`: true/false
- `idempotentHint`: true/false
- `openWorldHint`: true/false

---

### Giai Đoạn 3: Rà Soát và Kiểm Thử

#### 3.1 Chất Lượng Mã Nguồn

Rà soát để đảm bảo:
- Không có mã nguồn bị lặp lại (nguyên tắc DRY)
- Xử lý lỗi nhất quán trên toàn bộ công cụ
- Bao phủ kiểu dữ liệu đầy đủ (full type coverage)
- Mô tả công cụ rõ ràng, dễ hiểu

#### 3.2 Biên Dịch và Kiểm Thử

**TypeScript:**
- Chạy `npm run build` để kiểm tra xác minh quá trình biên dịch
- Kiểm thử bằng công cụ MCP Inspector: `npx @modelcontextprotocol/inspector`

**Python:**
- Kiểm tra cú pháp: `python -m py_compile your_server.py`
- Kiểm thử bằng MCP Inspector

Xem hướng dẫn riêng của từng ngôn ngữ để biết phương pháp kiểm thử chi tiết và danh sách kiểm tra chất lượng.

---

### Giai Đoạn 4: Xây Dựng Bộ Đánh Giá (Evaluations)

Sau khi triển khai xong máy chủ MCP, hãy tạo bộ đánh giá toàn diện để kiểm tra mức độ hiệu quả thực tế của nó.

**Tải [✅ Hướng Dẫn Đánh Giá](./reference/evaluation.md) để xem toàn bộ nguyên tắc xây dựng bài đánh giá.**

#### 4.1 Hiểu Mục Đích Của Việc Đánh Giá

Sử dụng các bài đánh giá để kiểm tra xem LLM có thể sử dụng máy chủ MCP của bạn một cách hiệu quả nhằm trả lời những câu hỏi thực tế, phức tạp hay không.

#### 4.2 Tạo 10 Câu Hỏi Đánh Giá

Để tạo ra các bài đánh giá hiệu quả, hãy làm theo quy trình được nêu trong tài liệu hướng dẫn đánh giá:

1. **Khảo sát Công cụ (Tool Inspection)**: Liệt kê các công cụ hiện có và nắm rõ năng lực của chúng
2. **Khám phá Nội dung (Content Exploration)**: Sử dụng các thao tác CHỈ ĐỌC (READ-ONLY) để khám phá dữ liệu thực tế hiện có
3. **Khởi tạo Câu hỏi (Question Generation)**: Tạo 10 câu hỏi phức tạp, sát với thực tế
4. **Xác minh Câu trả lời (Answer Verification)**: Tự mình giải từng câu hỏi để xác minh đáp án chính xác

#### 4.3 Yêu Cầu Đối Với Bài Đánh Giá

Đảm bảo mỗi câu hỏi phải thỏa mãn các tiêu chí:
- **Độc lập (Independent)**: Không phụ thuộc vào kết quả của các câu hỏi khác
- **Chỉ đọc (Read-only)**: Chỉ yêu cầu các thao tác truy vấn không làm thay đổi hay phá hủy dữ liệu
- **Phức tạp (Complex)**: Đòi hỏi phải gọi nhiều công cụ kết hợp và khám phá dữ liệu sâu
- **Thực tế (Realistic)**: Dựa trên các trường hợp sử dụng thực tế mà con người thực sự quan tâm
- **Có thể kiểm chứng (Verifiable)**: Có một đáp án duy nhất, rõ ràng, có thể kiểm chứng bằng phép so sánh chuỗi
- **Ổn định (Stable)**: Đáp án sẽ không bị thay đổi theo thời gian

#### 4.4 Định Dạng Đầu Ra

Tạo một tệp XML theo cấu trúc sau:

```xml
<evaluation>
  <qa_pair>
    <question>Find discussions about AI model launches with animal codenames. One model needed a specific safety designation that uses the format ASL-X. What number X was being determined for the model named after a spotted wild cat?</question>
    <answer>3</answer>
  </qa_pair>
<!-- More qa_pairs... -->
</evaluation>
```

---

# Các Tệp Tài Liệu Tham Khảo

## 📚 Thư Viện Tài Liệu

Tải các tài nguyên này khi cần thiết trong suốt quá trình phát triển:

### Tài Liệu MCP Cốt Lõi (Tải Đầu Tiên)
- **Giao thức MCP (MCP Protocol)**: Bắt đầu với sơ đồ trang tại `https://modelcontextprotocol.io/sitemap.xml`, sau đó truy xuất các trang cụ thể kèm hậu tố `.md`
- [📋 Thực Hành Tốt Nhất Về MCP](./reference/mcp_best_practices.md) - Các nguyên tắc MCP phổ quát bao gồm:
  - Quy ước đặt tên máy chủ và công cụ
  - Hướng dẫn định dạng phản hồi (JSON so với Markdown)
  - Thực hành tốt nhất về phân trang
  - Lựa chọn phương thức truyền tải (streamable HTTP so với stdio)
  - Tiêu chuẩn bảo mật và xử lý lỗi

### Tài Liệu SDK (Tải Trong Giai Đoạn 1/2)
- **Python SDK**: Truy xuất từ `https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/main/README.md`
- **TypeScript SDK**: Truy xuất từ `https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/README.md`

### Hướng Dẫn Triển Khai Theo Từng Ngôn Ngữ (Tải Trong Giai Đoạn 2)
- [🐍 Hướng Dẫn Triển Khai Python](./reference/python_mcp_server.md) - Hướng dẫn toàn diện về Python/FastMCP bao gồm:
  - Các mẫu khởi tạo máy chủ
  - Ví dụ về mô hình Pydantic
  - Đăng ký công cụ với `@mcp.tool`
  - Các ví dụ hoạt động hoàn chỉnh
  - Danh sách kiểm tra chất lượng

- [⚡ Hướng Dẫn Triển Khai TypeScript](./reference/node_mcp_server.md) - Hướng dẫn toàn diện về TypeScript bao gồm:
  - Cấu trúc dự án
  - Các mẫu lược đồ Zod
  - Đăng ký công cụ với `server.registerTool`
  - Các ví dụ hoạt động hoàn chỉnh
  - Danh sách kiểm tra chất lượng

### Hướng Dẫn Đánh Giá (Tải Trong Giai Đoạn 4)
- [✅ Hướng Dẫn Đánh Giá](./reference/evaluation.md) - Hướng dẫn toàn diện về cách tạo bộ đánh giá bao gồm:
  - Nguyên tắc đặt câu hỏi đánh giá
  - Chiến lược xác minh đáp án
  - Đặc tả định dạng XML
  - Ví dụ câu hỏi và câu trả lời mẫu
  - Cách chạy đánh giá bằng các kịch bản lệnh (scripts) được cung cấp
FILE:reference/mcp_best_practices.md
# Thực Hành Tốt Nhất Cho Máy Chủ MCP (MCP Server Best Practices)

## Tham Khảo Nhanh

### Đặt Tên Máy Chủ
- **Python**: `{service}_mcp` (ví dụ: `slack_mcp`)
- **Node/TypeScript**: `{service}-mcp-server` (ví dụ: `slack-mcp-server`)

### Đặt Tên Công Cụ
- Sử dụng kiểu chữ snake_case có kèm tiền tố tên dịch vụ
- Định dạng: `{service}_{action}_{resource}`
- Ví dụ: `slack_send_message`, `github_create_issue`

### Định Dạng Phản Hồi
- Hỗ trợ cả hai định dạng JSON và Markdown
- JSON phục vụ việc xử lý bằng mã chương trình
- Markdown giúp con người dễ đọc trực tiếp

### Phân Trang (Pagination)
- Luôn tôn trọng tham số giới hạn `limit`
- Trả về đầy đủ `has_more`, `next_offset`, `total_count`
- Giá trị mặc định từ 20-50 mục

### Phương Thức Truyền Tải (Transport)
- **Streamable HTTP**: Dành cho máy chủ từ xa, kịch bản phục vụ nhiều trình khách cùng lúc
- **stdio**: Dành cho tích hợp cục bộ, các công cụ dòng lệnh
- Tránh dùng SSE (đã ngừng khuyến khích sử dụng để chuyển sang streamable HTTP)

---

## Quy Ước Đặt Tên Máy Chủ

Tuân thủ các mẫu đặt tên tiêu chuẩn hóa sau đây:

**Python**: Sử dụng định dạng `{service}_mcp` (chữ thường nối bằng dấu gạch dưới)
- Ví dụ: `slack_mcp`, `github_mcp`, `jira_mcp`

**Node/TypeScript**: Sử dụng định dạng `{service}-mcp-server` (chữ thường nối bằng dấu gạch ngang)
- Ví dụ: `slack-mcp-server`, `github-mcp-server`, `jira-mcp-server`

Tên máy chủ cần mang tính khái quát, mô tả rõ dịch vụ đang được tích hợp, dễ dàng suy luận ra từ mô tả tác vụ, và không kèm theo số phiên bản.

---

## Đặt Tên và Thiết Kế Công Cụ

### Đặt Tên Công Cụ

1. **Sử dụng snake_case**: `search_users`, `create_project`, `get_channel_info`
2. **Bao gồm tiền tố tên dịch vụ**: Hãy lường trước việc máy chủ MCP của bạn có thể được sử dụng chung cùng lúc với nhiều máy chủ MCP khác
   - Sử dụng `slack_send_message` thay vì chỉ viết `send_message`
   - Sử dụng `github_create_issue` thay vì chỉ viết `create_issue`
3. **Hướng tới hành động**: Bắt đầu bằng các động từ (get, list, search, create, v.v.)
4. **Cụ thể và rõ ràng**: Tránh những cái tên chung chung dễ gây xung đột với các máy chủ khác

### Thiết Kế Công Cụ

- Phần mô tả công cụ phải diễn tả chính xác, thu hẹp và không gây nhập nhằng về chức năng
- Nội dung mô tả phải khớp tuyệt đối với chức năng thực tế mà công cụ thực hiện
- Cung cấp đầy đủ các chú giải công cụ (readOnlyHint, destructiveHint, idempotentHint, openWorldHint)
- Giữ cho các thao tác của công cụ tập trung và mang tính nguyên tử (atomic)

---

## Định Dạng Phản Hồi (Response Formats)

Tất cả các công cụ trả về dữ liệu nên hỗ trợ nhiều định dạng đầu ra:

### Định Dạng JSON (`response_format="json"`)
- Dữ liệu có cấu trúc để máy tính đọc và xử lý (machine-readable)
- Bao gồm tất cả các trường dữ liệu và siêu dữ liệu (metadata) hiện có
- Tên trường và kiểu dữ liệu nhất quán
- Sử dụng cho việc xử lý tự động bằng chương trình

### Định Dạng Markdown (`response_format="markdown"`, thường là mặc định)
- Văn bản đã được định dạng để con người dễ đọc
- Sử dụng tiêu đề, danh sách và định dạng chữ để trình bày rõ ràng
- Chuyển đổi dấu thời gian (timestamps) sang định dạng ngày giờ dễ đọc
- Hiển thị tên hiển thị kèm theo mã ID đặt trong dấu ngoặc đơn
- Lược bỏ các siêu dữ liệu rườm rà không cần thiết

---

## Phân Trang (Pagination)

Đối với các công cụ liệt kê danh sách tài nguyên:

- **Luôn tôn trọng tham số `limit`**
- **Triển khai phân trang**: Sử dụng phân trang theo vị trí `offset` hoặc theo con trỏ `cursor`
- **Trả về siêu dữ liệu phân trang**: Bao gồm `has_more`, `next_offset`/`next_cursor`, `total_count`
- **Không bao giờ nạp toàn bộ kết quả vào bộ nhớ**: Đặc biệt quan trọng đối với các tập dữ liệu lớn
- **Đặt giới hạn mặc định hợp lý**: Thông thường từ 20-50 mục mỗi trang

Ví dụ phản hồi có phân trang:
```json
{
  "total": 150,
  "count": 20,
  "offset": 0,
  "items": [...],
  "has_more": true,
  "next_offset": 20
}
```

---

## Các Tùy Chọn Truyền Tải (Transport Options)

### Streamable HTTP

**Tốt nhất cho**: Máy chủ từ xa, dịch vụ web, kịch bản có nhiều trình khách (multi-client)

**Đặc điểm**:
- Giao tiếp hai chiều qua giao thức HTTP
- Hỗ trợ nhiều trình khách kết nối đồng thời
- Có thể triển khai như một dịch vụ web độc lập
- Cho phép gửi thông báo từ máy chủ tới trình khách (server-to-client notifications)

**Sử dụng khi**:
- Phục vụ nhiều trình khách cùng một lúc
- Triển khai dưới dạng dịch vụ đám mây
- Tích hợp với các ứng dụng nền web

### stdio

**Tốt nhất cho**: Tích hợp cục bộ trên máy, các công cụ dòng lệnh (CLI)

**Đặc điểm**:
- Giao tiếp qua luồng đầu vào/đầu ra tiêu chuẩn (standard input/output stream)
- Thiết lập đơn giản, không cần cấu hình mạng
- Chạy như một tiến trình con (subprocess) của trình khách

**Sử dụng khi**:
- Xây dựng công cụ cho môi trường phát triển cục bộ
- Tích hợp với các ứng dụng máy tính để bàn (desktop apps)
- Kịch bản một người dùng, một phiên làm việc duy nhất

**Lưu ý**: Các máy chủ stdio TUYỆT ĐỐI KHÔNG được ghi nhật ký (log) ra luồng stdout (hãy sử dụng luồng stderr để ghi log)

### Lựa Chọn Phương Thức Truyền Tải

| Tiêu chí | stdio | Streamable HTTP |
|-----------|-------|-----------------|
| **Triển khai** | Cục bộ (Local) | Từ xa (Remote) |
| **Số trình khách** | Đơn lẻ (Single) | Đa trình khách (Multiple) |
| **Độ phức tạp** | Thấp | Trung bình |
| **Thời gian thực** | Không | Có |

---

## Thực Hành Tốt Nhất Về Bảo Mật

### Xác Thực và Phân Quyền

**OAuth 2.1**:
- Sử dụng chuẩn OAuth 2.1 bảo mật với chứng chỉ từ các tổ chức cấp phát được công nhận
- Xác thực mã thông báo truy cập (access tokens) trước khi xử lý bất kỳ yêu cầu nào
- Chỉ chấp nhận các token được cấp riêng cho đúng máy chủ của bạn

**Khóa API (API Keys)**:
- Lưu trữ khóa API trong các biến môi trường, không bao giờ gán cứng trong mã nguồn
- Kiểm tra tính hợp lệ của khóa ngay khi khởi động máy chủ
- Đưa ra thông báo lỗi rõ ràng khi quá trình xác thực thất bại

### Xác Thực Dữ Liệu Đầu Vào (Input Validation)

- Làm sạch đường dẫn tệp để ngăn chặn tấn công duyệt thư mục (directory traversal)
- Kiểm tra tính hợp lệ của các URL và mã định danh bên ngoài
- Kiểm tra kích thước và miền giá trị của các tham số
- Ngăn chặn lỗ hổng tiêm lệnh (command injection) trong các lệnh gọi hệ thống
- Sử dụng xác thực lược đồ (Pydantic/Zod) cho tất cả dữ liệu đầu vào

### Xử Lý Lỗi (Error Handling)

- Không làm lộ chi tiết lỗi nội bộ của hệ thống ra phía trình khách
- Ghi nhật ký các lỗi liên quan đến bảo mật ở phía máy chủ
- Cung cấp thông báo lỗi hữu ích nhưng không tiết lộ thông tin nhạy cảm
- Dọn dẹp giải phóng tài nguyên sau khi xảy ra lỗi

### Bảo Vệ Chống Tấn Công DNS Rebinding

Đối với các máy chủ streamable HTTP chạy cục bộ:
- Bật cơ chế bảo vệ chống DNS rebinding
- Kiểm tra xác thực tiêu đề `Origin` trên tất cả các kết nối gửi đến
- Lắng nghe kết nối trên địa chỉ `127.0.0.1` thay vì `0.0.0.0`

---

## Chú Giải Công Cụ (Tool Annotations)

Cung cấp các chú giải để giúp trình khách hiểu rõ hành vi của công cụ:

| Chú giải (Annotation) | Kiểu | Mặc định | Mô tả |
|-----------|------|---------|-------------|
| `readOnlyHint` | boolean | false | Công cụ không làm thay đổi môi trường của nó |
| `destructiveHint` | boolean | true | Công cụ có thể thực hiện các cập nhật mang tính xóa/phá hủy |
| `idempotentHint` | boolean | false | Gọi lặp lại nhiều lần với cùng đối số không gây thêm tác động phụ |
| `openWorldHint` | boolean | true | Công cụ tương tác với các thực thể bên ngoài |

**Quan trọng**: Các chú giải chỉ mang tính chất gợi ý (hints), không phải là cam kết bảo đảm an ninh. Trình khách không nên đưa ra các quyết định bảo mật trọng yếu chỉ dựa đơn thuần vào các chú giải này.

---

## Xử Lý Lỗi Trong Giao Thức

- Sử dụng các mã lỗi JSON-RPC tiêu chuẩn
- Báo cáo lỗi thực thi công cụ ngay bên trong đối tượng kết quả trả về (thay vì ném lỗi ở cấp độ giao thức)
- Cung cấp thông báo lỗi cụ thể, hữu ích kèm theo gợi ý các bước tiếp theo nên làm
- Không phơi bày chi tiết cài đặt nội bộ
- Dọn dẹp tài nguyên đúng cách khi xảy ra lỗi

Ví dụ về xử lý lỗi:
```typescript
try {
  const result = performOperation();
  return { content: [{ type: "text", text: result }] };
} catch (error) {
  return {
    isError: true,
    content: [{
      type: "text",
      text: `Error: error.message. Try using filter='active_only' to reduce results.`
    }]
  };
}
```

---

## Yêu Cầu Về Kiểm Thử (Testing Requirements)

Hoạt động kiểm thử toàn diện cần bao quát các mảng sau:

- **Kiểm thử chức năng (Functional testing)**: Xác minh công cụ thực thi chính xác với cả dữ liệu đầu vào hợp lệ lẫn không hợp lệ
- **Kiểm thử tích hợp (Integration testing)**: Kiểm thử sự tương tác thực tế với các hệ thống bên ngoài
- **Kiểm thử bảo mật (Security testing)**: Xác thực cơ chế đăng nhập/ủy quyền, làm sạch dữ liệu đầu vào, giới hạn tần suất gọi (rate limiting)
- **Kiểm thử hiệu năng (Performance testing)**: Kiểm tra hành vi của hệ thống dưới tải cao và khi xảy ra hết thời gian chờ (timeouts)
- **Xử lý lỗi (Error handling)**: Đảm bảo việc báo cáo lỗi và dọn dẹp tài nguyên diễn ra đúng chuẩn

---

## Yêu Cầu Về Tài Liệu (Documentation Requirements)

- Cung cấp tài liệu hướng dẫn rõ ràng cho tất cả các công cụ và năng lực của máy chủ
- Bao gồm các ví dụ hoạt động thực tế (ít nhất 3 ví dụ cho mỗi tính năng chính)
- Ghi rõ các lưu ý về an toàn bảo mật
- Chỉ rõ các quyền hạn (permissions) và cấp độ truy cập bắt buộc phải có
- Ghi rõ giới hạn tần suất gọi (rate limits) và các đặc tính hiệu năng
FILE:reference/evaluation.md
# Hướng Dẫn Đánh Giá Máy Chủ MCP (MCP Server Evaluation Guide)

## Tổng Quan

Tài liệu này cung cấp hướng dẫn xây dựng các bộ đánh giá toàn diện cho các máy chủ MCP. Các bài đánh giá này kiểm tra xem liệu các mô hình ngôn ngữ lớn (LLM) có thể sử dụng hiệu quả máy chủ MCP của bạn để trả lời những câu hỏi thực tế, phức tạp khi chỉ được phép dùng các công cụ do máy chủ cung cấp hay không.

---

## Tham Khảo Nhanh

### Yêu Cầu Đánh Giá
- Tạo 10 câu hỏi dễ đọc đối với con người
- Các câu hỏi phải CHỈ ĐỌC (READ-ONLY), ĐỘC LẬP (INDEPENDENT), KHÔNG PHÁ HỦY DỮ LIỆU (NON-DESTRUCTIVE)
- Mỗi câu hỏi đòi hỏi phải gọi nhiều lệnh công cụ (có thể lên tới hàng chục lần gọi)
- Đáp án phải là một giá trị đơn lẻ, có thể kiểm chứng được
- Đáp án phải ỔN ĐỊNH (STABLE — không bị thay đổi theo thời gian)

### Định Dạng Đầu Ra
```xml
<evaluation>
   <qa_pair>
      <question>Your question here</question>
      <answer>Single verifiable answer</answer>
   </qa_pair>
</evaluation>
```

---

## Mục Đích Của Việc Đánh Giá

Thước đo chất lượng của một máy chủ MCP KHÔNG nằm ở việc máy chủ đó cài đặt công cụ nhiều hay khéo léo ra sao trên lý thuyết, mà nằm ở mức độ hiệu quả mà các cài đặt đó (lược đồ đầu vào/đầu ra, chuỗi tài liệu docstrings/mô tả, tính năng thực tế) giúp cho các LLM—vốn không có thêm bất kỳ ngữ cảnh nào khác và CHỈ được quyền truy cập vào máy chủ MCP đó—trả lời được những câu hỏi thực tế và hóc búa.

## Tổng Quan Về Bộ Đánh Giá

Hãy tạo 10 câu hỏi dễ đọc đối với con người, chỉ yêu cầu các thao tác CHỈ ĐỌC (READ-ONLY), ĐỘC LẬP (INDEPENDENT), KHÔNG PHÁ HỦY (NON-DESTRUCTIVE) và LŨY ĐẲNG (IDEMPOTENT) để tìm ra câu trả lời. Mỗi câu hỏi cần phải:
- Sát với thực tế (Realistic)
- Rõ ràng và ngắn gọn súc tích (Clear and concise)
- Không nhập nhằng đa nghĩa (Unambiguous)
- Phức tạp, đòi hỏi có thể lên tới hàng chục lần gọi công cụ hoặc nhiều bước suy luận nối tiếp
- Có thể trả lời bằng một giá trị đơn lẻ, kiểm chứng được mà bạn đã xác định trước đáp án

## Nguyên Tắc Đặt Câu Hỏi

### Các Yêu Cầu Cốt Lõi

1. **Các câu hỏi BẮT BUỘC phải độc lập với nhau**
   - Mỗi câu hỏi KHÔNG ĐƯỢC phụ thuộc vào đáp án của bất kỳ câu hỏi nào khác
   - Không được giả định rằng đã có các thao tác ghi dữ liệu trước đó từ quá trình xử lý câu hỏi khác

2. **Các câu hỏi BẮT BUỘC chỉ yêu cầu sử dụng công cụ KHÔNG PHÁ HỦY VÀ LŨY ĐẲNG**
   - Không được chỉ dẫn hoặc yêu cầu phải sửa đổi trạng thái dữ liệu mới tìm ra được đáp án đúng

3. **Các câu hỏi phải THỰC TẾ, RÕ RÀNG, NGẮN GỌN và PHỨC TẠP**
   - Phải buộc một LLM khác phải sử dụng nhiều (có thể là hàng chục) công cụ hoặc bước xử lý mới trả lời được

### Độ Phức Tạp và Chiều Sâu

4. **Các câu hỏi phải đòi hỏi sự khám phá sâu (deep exploration)**
   - Hãy cân nhắc các câu hỏi đa chặng (multi-hop questions) đòi hỏi phải giải quyết nhiều câu hỏi phụ và gọi công cụ tuần tự nối tiếp nhau
   - Mỗi bước sau sẽ tận dụng thông tin vừa tìm thấy ở bước truy vấn trước đó

5. **Các câu hỏi có thể đòi hỏi phải lật trang dữ liệu nhiều lần (extensive paging)**
   - Có thể cần phải duyệt qua nhiều trang kết quả trả về
   - Có thể đòi hỏi phải truy vấn dữ liệu cũ (cách đây 1-2 năm) để tìm ra thông tin ngách
   - Các câu hỏi bắt buộc phải KHÓ (DIFFICULT)

6. **Các câu hỏi phải đòi hỏi sự hiểu biết sâu sắc**
   - Chứ không chỉ là kiến thức bề nổi
   - Có thể đặt ra các ý niệm phức tạp dưới dạng câu hỏi Đúng/Sai (True/False) đòi hỏi phải tìm bằng chứng xác thực
   - Có thể sử dụng định dạng trắc nghiệm nhiều lựa chọn buộc LLM phải tìm kiếm kiểm chứng nhiều giả thuyết khác nhau

7. **Các câu hỏi không thể giải quyết chỉ bằng một lệnh tìm kiếm từ khóa đơn giản**
   - Không đưa nguyên xi các từ khóa đặc thù có sẵn trong nội dung mục tiêu vào câu hỏi
   - Hãy sử dụng từ đồng nghĩa, các khái niệm liên quan hoặc diễn đạt lại bằng lời văn khác (paraphrases)
   - Đòi hỏi phải tìm kiếm nhiều lần, phân tích nhiều mục liên quan, trích xuất ngữ cảnh rồi mới suy ra được câu trả lời

### Kiểm Thử Sức Chịu Tải Của Công Cụ (Tool Testing)

8. **Các câu hỏi nên kiểm thử áp lực (stress-test) đối với giá trị trả về của công cụ**
   - Có thể kích hoạt các công cụ trả về những đối tượng JSON hoặc danh sách lớn dễ làm quá tải ngữ cảnh của LLM
   - Cần đòi hỏi khả năng hiểu nhiều dạng thức dữ liệu (modalities) khác nhau:
     - Mã định danh (ID) và tên gọi
     - Dấu thời gian (timestamps) và ngày giờ (tháng, ngày, năm, giây)
     - Mã ID tệp tin, tên tệp, phần mở rộng và kiểu MIME (mimetypes)
     - Các đường dẫn URL, mã GID, v.v.
   - Cần thăm dò khả năng của công cụ trong việc trả về mọi dạng dữ liệu hữu ích

9. **Các câu hỏi PHẦN LỚN phải phản ánh đúng các trường hợp sử dụng thực tế của con người**
   - Những tác vụ truy xuất thông tin mà CON NGƯỜI khi nhờ LLM hỗ trợ sẽ thực sự quan tâm

10. **Các câu hỏi có thể đòi hỏi hàng chục lần gọi công cụ**
    - Điều này tạo ra thử thách thực sự cho các LLM có cửa sổ ngữ cảnh giới hạn
    - Khuyến khích các công cụ trên máy chủ MCP được thiết kế gọn lại để giảm lượng thông tin thừa trả về

11. **Đưa vào cả các câu hỏi có tính mơ hồ cần suy luận**
    - Có thể hơi mơ hồ HOẶC đòi hỏi phải đưa ra quyết định khó khăn xem nên gọi công cụ nào
    - Buộc LLM có khả năng mắc sai lầm hoặc hiểu sai nếu không suy xét kỹ
    - Tuy nhiên, phải đảm bảo rằng bất chấp SỰ MƠ HỒ ban đầu, VẪN LUÔN CÓ MỘT ĐÁP ÁN DUY NHẤT CÓ THỂ KIỂM CHỨNG ĐƯỢC

### Tính Ổn Định (Stability)

12. **Các câu hỏi phải được thiết kế sao cho ĐÁP ÁN KHÔNG BỊ THAY ĐỔI THEO THỜI GIAN**
    - Không đặt những câu hỏi phụ thuộc vào "trạng thái hiện tại" vốn luôn biến động
    - Ví dụ, không đếm:
      - Số lượng biểu cảm (reactions) trên một bài đăng
      - Số lượng phản hồi trong một chuỗi tin nhắn (thread)
      - Số lượng thành viên trong một kênh trò chuyện

13. **ĐỪNG để máy chủ MCP GIỚI HẠN các loại câu hỏi mà bạn tạo ra**
    - Hãy tạo ra những câu hỏi đầy thử thách và phức tạp
    - Một số câu hỏi có thể không giải quyết được ngay nếu bộ công cụ hiện tại của máy chủ MCP còn thiếu sót
    - Các câu hỏi có thể yêu cầu định dạng đầu ra cụ thể (ngày giờ chuẩn datetime so với thời gian epoch, JSON so với MARKDOWN)
    - Các câu hỏi có thể đòi hỏi hàng chục lần gọi công cụ mới hoàn thành

## Nguyên Tắc Về Đáp Án (Answer Guidelines)

### Khả Năng Kiểm Chứng (Verification)

1. **Đáp án bắt buộc phải KIỂM CHỨNG ĐƯỢC thông qua phép so sánh chuỗi trực tiếp (direct string comparison)**
   - Nếu câu trả lời có thể được viết theo nhiều định dạng khác nhau, hãy chỉ định rõ định dạng đầu ra bắt buộc ngay trong CÂU HỎI
   - Ví dụ: "Sử dụng định dạng YYYY/MM/DD.", "Chỉ trả lời True hoặc False.", "Chỉ trả lời A, B, C, hoặc D và không viết thêm bất kỳ chữ nào khác."
   - Đáp án phải là một giá trị ĐƠN LẺ, KIỂM CHỨNG ĐƯỢC chẳng hạn như:
     - Mã ID người dùng, tên đăng nhập, tên hiển thị, tên riêng, họ
     - Mã ID kênh, tên kênh
     - Mã ID tin nhắn, chuỗi văn bản
     - Đường dẫn URL, tiêu đề
     - Số lượng bằng con số cụ thể
     - Dấu thời gian (timestamp), ngày giờ (datetime)
     - Giá trị logic Boolean (đối với câu hỏi True/False)
     - Địa chỉ email, số điện thoại
     - Mã ID tệp, tên tệp, phần mở rộng tệp
     - Đáp án trắc nghiệm (A/B/C/D)
   - Đáp án không được đòi hỏi định dạng đặc biệt hoặc cấu trúc đầu ra phức tạp
   - Đáp án sẽ được chấm tự động bằng PHÉP SO SÁNH CHUỖI TRỰC TIẾP

### Tính Dễ Đọc (Readability)

2. **Đáp án nhìn chung nên ưu tiên các định dạng CON NGƯỜI DỄ ĐỌC (HUMAN-READABLE)**
   - Ví dụ: tên gọi, tên riêng, họ, ngày giờ, tên tệp, chuỗi tin nhắn, URL, yes/no, true/false, a/b/c/d
   - Thay vì những mã ID tối nghĩa (mặc dù mã ID vẫn được chấp nhận khi cần thiết)
   - ĐẠI ĐA SỐ các đáp án nên ở dạng con người đọc hiểu được ngay

### Tính Ổn Định (Stability)

3. **Đáp án bắt buộc phải ỔN ĐỊNH / TĨNH (STABLE/STATIONARY)**
   - Hãy khai thác các nội dung cũ trong lịch sử (ví dụ: các cuộc trò chuyện đã kết thúc, các dự án đã ra mắt xong, các câu hỏi đã được giải đáp hoàn tất)
   - Tạo CÂU HỎI dựa trên các sự kiện/khái niệm "đã đóng lại" để luôn trả về cùng một kết quả bất biến
   - Câu hỏi có thể yêu cầu xét trong một khung thời gian cố định trong quá khứ để cách ly khỏi những biến động mới
   - Dựa vào ngữ cảnh RẤT ÍT KHẢ NĂNG bị thay đổi
   - Ví dụ: nếu yêu cầu tìm tên một bài báo khoa học, hãy đặt điều kiện ĐỦ CỤ THỂ để đáp án không bị nhầm lẫn với các bài báo mới xuất bản sau này

4. **Đáp án phải RÕ RÀNG và KHÔNG ĐA NGHĨA**
   - Câu hỏi phải được thiết kế sao cho chỉ dẫn tới một đáp án rõ ràng duy nhất
   - Đáp án có thể suy ra được bằng cách sử dụng các công cụ của máy chủ MCP

### Tính Đa Dạng (Diversity)

5. **Các đáp án phải ĐA DẠNG**
   - Đáp án trong bộ câu hỏi nên là các giá trị đơn lẻ thuộc nhiều dạng thức và khía cạnh khác nhau
   - Khái niệm người dùng: mã ID người dùng, tên đăng nhập, tên hiển thị, tên riêng, họ, địa chỉ email, số điện thoại
   - Khái niệm kênh: mã ID kênh, tên kênh, chủ đề kênh (channel topic)
   - Khái niệm tin nhắn: mã ID tin nhắn, nội dung chuỗi tin nhắn, dấu thời gian, tháng, ngày, năm

6. **Đáp án KHÔNG ĐƯỢC là các cấu trúc phức tạp**
   - Không phải là một danh sách gồm nhiều giá trị
   - Không phải là một đối tượng dữ liệu phức tạp
   - Không phải là một danh sách các mã ID hoặc chuỗi ký tự
   - Không phải là một đoạn văn bản ngôn ngữ tự nhiên tự do
   - TRỪ KHI đáp án đó có thể dễ dàng kiểm chứng bằng PHÉP SO SÁNH CHUỖI TRỰC TIẾP
   - Và có thể tái tạo lại một cách nhất quán trong thực tế
   - Phải đảm bảo rất khó xảy ra khả năng một LLM trả về cùng danh sách đó nhưng theo một thứ tự hay định dạng khác

## Quy Trình Xây Dựng Bộ Đánh Giá (Evaluation Process)

### Bước 1: Khảo Sát Tài Liệu (Documentation Inspection)

Đọc tài liệu của API mục tiêu để nắm rõ:
- Các điểm cuối (endpoints) và chức năng hiện có
- Nếu có điểm nào chưa rõ ràng, hãy truy xuất thêm thông tin từ trên web
- Thực hiện song song hóa bước này NHIỀU NHẤT CÓ THỂ
- Đảm bảo mỗi tác tử con (subagent) CHỈ xem xét tài liệu từ hệ thống tệp hoặc trên web

### Bước 2: Khảo Sát Công Cụ (Tool Inspection)

Liệt kê các công cụ hiện có trong máy chủ MCP:
- Kiểm tra trực tiếp máy chủ MCP
- Tìm hiểu lược đồ đầu vào/đầu ra, chuỗi tài liệu docstrings và phần mô tả
- MÀ KHÔNG thực sự gọi thực thi các công cụ đó ở giai đoạn này

### Bước 3: Xây Dựng Sự Hiểu Biết Tổng Thể

Lặp lại bước 1 & 2 cho đến khi bạn nắm vững cơ chế hoạt động:
- Lặp lại nhiều vòng
- Suy nghĩ về các loại tác vụ thực tế mà bạn muốn tạo ra
- Tinh chỉnh lại sự hiểu biết của mình
- Ở BẤT KỲ giai đoạn nào, bạn cũng TUYỆT ĐỐI KHÔNG ĐƯỢC ĐỌC mã nguồn cài đặt bên trong của chính máy chủ MCP
- Hãy dùng trực giác và sự hiểu biết của bạn về miền nghiệp vụ để tạo ra những tác vụ hợp lý, thực tế nhưng CỰC KỲ thử thách

### Bước 4: Khảo Sát Nội Dung Ở Chế Độ Chỉ Đọc (Read-Only Content Inspection)

Sau khi đã hiểu rõ API và các công cụ, hãy SỬ DỤNG các công cụ của máy chủ MCP:
- Khảo sát nội dung dữ liệu CHỈ bằng các thao tác CHỈ ĐỌC (READ-ONLY) và KHÔNG PHÁ HỦY (NON-DESTRUCTIVE)
- Mục tiêu: xác định các nội dung cụ thể có thực (ví dụ: người dùng, kênh, tin nhắn, dự án, công việc) để làm chất liệu tạo câu hỏi thực tế
- TUYỆT ĐỐI KHÔNG gọi bất kỳ công cụ nào làm sửa đổi trạng thái dữ liệu
- TUYỆT ĐỐI KHÔNG đọc mã nguồn cài đặt của chính máy chủ MCP
- Song song hóa bước này với các tác tử con độc lập tiến hành khám phá các nhánh dữ liệu khác nhau
- Đảm bảo mỗi tác tử con chỉ thực hiện các thao tác CHỈ ĐỌC, KHÔNG PHÁ HỦY và LŨY ĐẲNG
- HÃY CẨN THẬN: MỘT SỐ CÔNG CỤ có thể trả về LƯỢNG DỮ LIỆU RẤT LỚN làm tràn bộ nhớ NGỮ CẢNH (CONTEXT) của bạn
- Hãy thực hiện các lệnh gọi công cụ NHỎ, TĂNG DẦN TỪNG BƯỚC VÀ CÓ TRỌNG TÂM để khám phá
- Trong tất cả các yêu cầu gọi công cụ, hãy luôn sử dụng tham số `limit` để giới hạn số lượng kết quả trả về (<10)
- Sử dụng tính năng phân trang (pagination)

### Bước 5: Tạo Bộ Câu Hỏi Đánh Giá (Task Generation)

Sau khi đã khảo sát xong nội dung thực tế, hãy tạo ra 10 câu hỏi dễ đọc đối với con người:
- Một LLM phải có khả năng trả lời được những câu hỏi này thông qua máy chủ MCP
- Tuân thủ đầy đủ mọi nguyên tắc về câu hỏi và đáp án đã nêu ở trên

## Định Dạng Đầu Ra

Mỗi cặp Hỏi-Đáp (QA pair) bao gồm một câu hỏi và một đáp án. Đầu ra phải là một tệp XML có cấu trúc như sau:

```xml
<evaluation>
   <qa_pair>
      <question>Find the project created in Q2 2024 with the highest number of completed tasks. What is the project name?</question>
      <answer>Website Redesign</answer>
   </qa_pair>
   <qa_pair>
      <question>Search for issues labeled as "bug" that were closed in March 2024. Which user closed the most issues? Provide their username.</question>
      <answer>sarah_dev</answer>
   </qa_pair>
   <qa_pair>
      <question>Look for pull requests that modified files in the /api directory and were merged between January 1 and January 31, 2024. How many different contributors worked on these PRs?</question>
      <answer>7</answer>
   </qa_pair>
   <qa_pair>
      <question>Find the repository with the most stars that was created before 2023. What is the repository name?</question>
      <answer>data-pipeline</answer>
   </qa_pair>
</evaluation>
```

## Các Ví Dụ Về Câu Hỏi Đánh Giá

### Những Câu Hỏi Tốt (Good Questions)

**Ví dụ 1: Câu hỏi đa chặng đòi hỏi khám phá sâu (GitHub MCP)**
```xml
<qa_pair>
   <question>Find the repository that was archived in Q3 2023 and had previously been the most forked project in the organization. What was the primary programming language used in that repository?</question>
   <answer>Python</answer>
</qa_pair>
```

Câu hỏi này tốt vì:
- Đòi hỏi nhiều lần tìm kiếm để lọc ra các kho lưu trữ đã đưa vào lưu trữ (archived repositories)
- Cần xác định xem kho nào có nhiều lượt rẽ nhánh (forks) nhất trước khi bị lưu trữ
- Đòi hỏi phải kiểm tra chi tiết từng kho lưu trữ để biết ngôn ngữ lập trình chính
- Đáp án là một giá trị đơn giản, dễ kiểm chứng
- Dựa trên dữ liệu lịch sử (đã đóng lại) nên sẽ không thay đổi theo thời gian

**Ví dụ 2: Đòi hỏi hiểu ngữ cảnh mà không khớp từ khóa trực tiếp (Project Management MCP)**
```xml
<qa_pair>
   <question>Locate the initiative focused on improving customer onboarding that was completed in late 2023. The project lead created a retrospective document after completion. What was the lead's role title at that time?</question>
   <answer>Product Manager</answer>
</qa_pair>
```

Câu hỏi này tốt vì:
- Không nêu thẳng tên dự án cụ thể (mà mô tả là "sáng kiến tập trung vào việc cải thiện quy trình đón tiếp khách hàng mới")
- Đòi hỏi phải tìm các dự án đã hoàn thành trong một khung thời gian cụ thể
- Cần xác định người phụ trách dự án (project lead) và vai trò của họ
- Đòi hỏi phải đọc hiểu ngữ cảnh từ các tài liệu họp rút kinh nghiệm (retrospective documents)
- Đáp án dễ đọc đối với con người và giữ nguyên tính ổn định
- Dựa trên công việc đã hoàn tất trong quá khứ (sẽ không biến đổi)

**Ví dụ 3: Tổng hợp phức tạp đòi hỏi nhiều bước xử lý (Issue Tracker MCP)**
```xml
<qa_pair>
   <question>Among all bugs reported in January 2024 that were marked as critical priority, which assignee resolved the highest percentage of their assigned bugs within 48 hours? Provide the assignee's username.</question>
   <answer>alex_eng</answer>
</qa_pair>
```

Câu hỏi này tốt vì:
- Đòi hỏi phải lọc các lỗi (bugs) theo ngày tháng, mức độ ưu tiên và trạng thái
- Cần gom nhóm theo người được giao việc (assignee) và tính toán tỷ lệ giải quyết thành công
- Đòi hỏi phải xử lý dấu thời gian (timestamps) để xác định đúng khoảng thời gian 48 giờ
- Kiểm thử khả năng phân trang (có khả năng phải duyệt qua rất nhiều lỗi)
- Đáp án là một tên người dùng (username) duy nhất
- Dựa trên dữ liệu lịch sử trong một giai đoạn thời gian cố định

**Ví dụ 4: Đòi hỏi tổng hợp thông tin xuyên qua nhiều loại dữ liệu (CRM MCP)**
```xml
<qa_pair>
   <question>Find the account that upgraded from the Starter to Enterprise plan in Q4 2023 and had the highest annual contract value. What industry does this account operate in?</question>
   <answer>Healthcare</answer>
</qa_pair>
```

Câu hỏi này tốt vì:
- Đòi hỏi phải nắm được lịch sử thay đổi gói thuê bao (subscription tier changes)
- Cần xác định các sự kiện nâng cấp gói trong một khung thời gian cụ thể
- Đòi hỏi phải so sánh giá trị hợp đồng
- Phải truy cập thông tin ngành nghề kinh doanh của tài khoản khách hàng
- Đáp án đơn giản và có thể kiểm chứng rõ ràng
- Dựa trên các giao dịch lịch sử đã hoàn tất

### Những Câu Hỏi Kém Chất Lượng (Poor Questions)

**Ví dụ 1: Đáp án thay đổi theo thời gian**
```xml
<qa_pair>
   <question>How many open issues are currently assigned to the engineering team?</question>
   <answer>47</answer>
</qa_pair>
```

Câu hỏi này kém vì:
- Đáp án sẽ liên tục thay đổi khi có thêm các issue mới được tạo ra, đóng lại hoặc chuyển giao cho người khác
- Không dựa trên dữ liệu tĩnh/ổn định
- Phụ thuộc vào "trạng thái hiện tại" vốn luôn biến động

**Ví dụ 2: Quá dễ khi chỉ cần tìm kiếm từ khóa**
```xml
<qa_pair>
   <question>Find the pull request with title "Add authentication feature" and tell me who created it.</question>
   <answer>developer123</answer>
</qa_pair>
```

Câu hỏi này kém vì:
- Có thể giải quyết ngay bằng một lệnh tìm kiếm từ khóa đơn giản theo đúng tiêu đề chính xác
- Không đòi hỏi phải khám phá sâu hay đọc hiểu ngữ cảnh
- Không cần tổng hợp hay phân tích dữ liệu

**Ví dụ 3: Định dạng đáp án mơ hồ**
```xml
<qa_pair>
   <question>List all the repositories that have Python as their primary language.</question>
   <answer>repo1, repo2, repo3, data-pipeline, ml-tools</answer>
</qa_pair>
```

Câu hỏi này kém vì:
- Đáp án là một danh sách có thể được trả về theo bất kỳ thứ tự nào
- Rất khó kiểm chứng tự động bằng phép so sánh chuỗi trực tiếp
- LLM có thể trình bày theo nhiều kiểu khác nhau (mảng JSON, phân cách bằng dấu phẩy, xuống dòng)
- Tốt hơn hết nên hỏi một con số tổng hợp cụ thể (đếm số lượng) hoặc giá trị cực đại (nhiều sao nhất)

## Quy Trình Xác Minh Đáp Án (Verification Process)

Sau khi tạo xong bộ câu hỏi đánh giá:

1. **Kiểm tra tệp XML** để nắm rõ cấu trúc lược đồ
2. **Tải từng chỉ dẫn câu hỏi** và sử dụng máy chủ MCP cùng các công cụ theo hướng chạy song song để TỰ MÌNH giải bài toán nhằm xác định đáp án chính xác thực tế
3. **Đánh dấu bất kỳ thao tác nào** đòi hỏi quyền GHI (WRITE) hoặc PHÁ HỦY (DESTRUCTIVE) dữ liệu
4. **Tập hợp tất cả các đáp án ĐÚNG** và thay thế bất kỳ đáp án sai lệch nào trong tài liệu
5. **Loại bỏ bất kỳ cặp `<qa_pair>` nào** đòi hỏi thao tác GHI hoặc PHÁ HỦY dữ liệu

Hãy nhớ chạy song song quá trình giải các câu hỏi để tránh làm tràn bộ nhớ ngữ cảnh, sau đó gom tất cả các đáp án lại và cập nhật vào tệp ở bước cuối cùng.

## Mẹo Tạo Bộ Đánh Giá Chất Lượng Cao

1. **Suy Nghĩ Kỹ và Lập Kế Hoạch Trước** khi bắt tay vào sinh câu hỏi
2. **Song Song Hóa Ở Những Nơi Có Thể** để tăng tốc quy trình và quản lý tốt cửa sổ ngữ cảnh
3. **Tập Trung Vào Các Trường Hợp Sử Dụng Thực Tế** mà con người thực sự muốn thực hiện
4. **Tạo Các Câu Hỏi Đầy Thử Thách** nhằm kiểm tra tới hạn năng lực của máy chủ MCP
5. **Đảm Bảo Tính Ổn Định** bằng cách sử dụng dữ liệu lịch sử và các sự kiện đã khép lại
6. **Xác Minh Đáp Án** bằng cách tự mình giải từng câu hỏi thông qua các công cụ của máy chủ MCP
7. **Lặp Lại và Tinh Chỉnh** dựa trên những gì bạn phát hiện được trong suốt quá trình thực hiện

---

# Chạy Đánh Giá (Running Evaluations)

Sau khi tạo xong tệp đánh giá, bạn có thể sử dụng bộ khung chạy kiểm thử (evaluation harness) được cung cấp sẵn để kiểm tra máy chủ MCP của mình.

## Thiết Lập Môi Trường

1. **Cài Đặt Các Thư Viện Phụ Thuộc**

   ```bash
   pip install -r scripts/requirements.txt
   ```

   Hoặc cài đặt thủ công:
   ```bash
   pip install anthropic mcp
   ```

2. **Thiết Lập Khóa API (API Key)**

   ```bash
   export ANTHROPIC_API_KEY=your_api_key_here
   ```

## Định Dạng Tệp Đánh Giá

Các tệp đánh giá sử dụng định dạng XML với các phần tử `<qa_pair>`:

```xml
<evaluation>
   <qa_pair>
      <question>Find the project created in Q2 2024 with the highest number of completed tasks. What is the project name?</question>
      <answer>Website Redesign</answer>
   </qa_pair>
   <qa_pair>
      <question>Search for issues labeled as "bug" that were closed in March 2024. Which user closed the most issues? Provide their username.</question>
      <answer>sarah_dev</answer>
   </qa_pair>
</evaluation>
```

## Cách Chạy Đánh Giá

Kịch bản chạy đánh giá (`scripts/evaluation.py`) hỗ trợ ba loại phương thức truyền tải:

**Quan trọng:**
- **Phương thức truyền tải stdio**: Kịch bản đánh giá sẽ tự động khởi chạy và quản lý tiến trình máy chủ MCP giúp bạn. Không cần tự chạy máy chủ thủ công.
- **Phương thức truyền tải sse/http**: Bạn bắt buộc phải khởi chạy máy chủ MCP riêng từ trước khi chạy kịch bản đánh giá. Kịch bản sẽ kết nối tới máy chủ đang chạy sẵn tại địa chỉ URL được chỉ định.

### 1. Máy Chủ STDIO Cục Bộ (Local STDIO Server)

Đối với các máy chủ MCP chạy cục bộ (kịch bản sẽ tự động khởi chạy máy chủ):

```bash
python scripts/evaluation.py \
  -t stdio \
  -c python \
  -a my_mcp_server.py \
  evaluation.xml
```

Khi cần truyền thêm các biến môi trường:
```bash
python scripts/evaluation.py \
  -t stdio \
  -c python \
  -a my_mcp_server.py \
  -e API_KEY=abc123 \
  -e DEBUG=true \
  evaluation.xml
```

### 2. Server-Sent Events (SSE)

Đối với các máy chủ MCP dựa trên SSE (bạn phải tự khởi chạy máy chủ trước):

```bash
python scripts/evaluation.py \
  -t sse \
  -u https://example.com/mcp \
  -H "Authorization: Bearer token123" \
  -H "X-Custom-Header: value" \
  evaluation.xml
```

### 3. HTTP (Streamable HTTP)

Đối với các máy chủ MCP dựa trên HTTP (bạn phải tự khởi chạy máy chủ trước):

```bash
python scripts/evaluation.py \
  -t http \
  -u https://example.com/mcp \
  -H "Authorization: Bearer token123" \
  evaluation.xml
```

## Các Tùy Chọn Dòng Lệnh (Command-Line Options)

```
usage: evaluation.py [-h] [-t {stdio,sse,http}] [-m MODEL] [-c COMMAND]
                     [-a ARGS [ARGS ...]] [-e ENV [ENV ...]] [-u URL]
                     [-H HEADERS [HEADERS ...]] [-o OUTPUT]
                     eval_file

positional arguments:
  eval_file             Path to evaluation XML file

optional arguments:
  -h, --help            Show help message
  -t, --transport       Transport type: stdio, sse, or http (default: stdio)
  -m, --model           Claude model to use (default: claude-3-7-sonnet-20250219)
  -o, --output          Output file for report (default: print to stdout)

stdio options:
  -c, --command         Command to run MCP server (e.g., python, node)
  -a, --args            Arguments for the command (e.g., server.py)
  -e, --env             Environment variables in KEY=VALUE format

sse/http options:
  -u, --url             MCP server URL
  -H, --header          HTTP headers in 'Key: Value' format
```

## Kết Quả Đầu Ra (Output)

Kịch bản đánh giá (evaluation script) sẽ tạo ra một báo cáo chi tiết bao gồm:

- **Thống kê Tóm tắt (Summary Statistics)**:
  - Độ chính xác (số câu đúng / tổng số câu)
  - Thời lượng thực hiện nhiệm vụ trung bình
  - Số lần gọi công cụ (tool calls) trung bình trên mỗi nhiệm vụ
  - Tổng số lần gọi công cụ

- **Kết quả Theo Từng Nhiệm vụ (Per-Task Results)**:
  - Prompt đầu vào và câu trả lời kỳ vọng
  - Câu trả lời thực tế từ tác tử (agent)
  - Câu trả lời đúng hay sai (✅/❌)
  - Thời gian thực thi và chi tiết các lệnh gọi công cụ
  - Bản tóm tắt của tác tử về hướng tiếp cận đã dùng
  - Phản hồi góp ý của tác tử về các công cụ

### Lưu Báo Cáo Ra Tệp

```bash
python scripts/evaluation.py \
  -t stdio \
  -c python \
  -a my_server.py \
  -o evaluation_report.md \
  evaluation.xml
```

## Ví Dụ Quy Trình Làm Việc Hoàn Chỉnh

Dưới đây là một ví dụ hoàn chỉnh về cách tạo và chạy một bộ đánh giá:

1. **Tạo tệp đánh giá của bạn** (`my_evaluation.xml`):

```xml
<evaluation>
   <qa_pair>
      <question>Find the user who created the most issues in January 2024. What is their username?</question>
      <answer>alice_developer</answer>
   </qa_pair>
   <qa_pair>
      <question>Among all pull requests merged in Q1 2024, which repository had the highest number? Provide the repository name.</question>
      <answer>backend-api</answer>
   </qa_pair>
   <qa_pair>
      <question>Find the project that was completed in December 2023 and had the longest duration from start to finish. How many days did it take?</question>
      <answer>127</answer>
   </qa_pair>
</evaluation>
```

2. **Cài đặt các thư viện phụ thuộc**:

```bash
pip install -r scripts/requirements.txt
export ANTHROPIC_API_KEY=your_api_key
```

3. **Chạy đánh giá**:

```bash
python scripts/evaluation.py \
  -t stdio \
  -c python \
  -a github_mcp_server.py \
  -e GITHUB_TOKEN=ghp_xxx \
  -o github_eval_report.md \
  my_evaluation.xml
```

4. **Xem xét báo cáo** trong tệp `github_eval_report.md` để:
   - Xem những câu hỏi nào đạt/không đạt
   - Đọc phản hồi của tác tử về bộ công cụ của bạn
   - Xác định những điểm cần cải thiện
   - Tinh chỉnh lặp lại thiết kế máy chủ MCP của bạn

## Xử Lý Sự Cố (Troubleshooting)

### Lỗi Kết Nối (Connection Errors)

Nếu bạn gặp lỗi kết nối:
- **STDIO**: Xác minh câu lệnh và các đối số truyền vào đã chính xác chưa
- **SSE/HTTP**: Kiểm tra xem URL có truy cập được không và các HTTP header đã đúng chưa
- Đảm bảo mọi khóa API bắt buộc đều đã được thiết lập trong biến môi trường hoặc headers

### Độ Chính Xác Thấp (Low Accuracy)

Nếu nhiều bài đánh giá bị thất bại:
- Xem xét kỹ phản hồi của tác tử cho từng nhiệm vụ
- Kiểm tra xem mô tả công cụ đã rõ ràng và toàn diện chưa
- Xác minh các tham số đầu vào đã được viết tài liệu hướng dẫn đầy đủ chưa
- Cân nhắc xem các công cụ có đang trả về quá nhiều hay quá ít dữ liệu không
- Đảm bảo các thông báo lỗi đều mang tính định hướng hành động (actionable)

### Vấn Đề Quá Thời Gian Chờ (Timeout Issues)

Nếu các nhiệm vụ bị quá thời gian chờ (timeout):
- Sử dụng một mô hình có năng lực cao hơn (ví dụ: `claude-3-7-sonnet-20250219`)
- Kiểm tra xem các công cụ có đang trả về khối lượng dữ liệu quá lớn không
- Xác minh cơ chế phân trang (pagination) đang hoạt động chính xác
- Cân nhắc đơn giản hóa các câu hỏi quá phức tạp
FILE:reference/node_mcp_server.md
# Hướng Dẫn Triển Khai Máy Chủ MCP Bằng Node/TypeScript

## Tổng Quan

Tài liệu này cung cấp các thông lệ tốt nhất và ví dụ chuyên biệt cho Node/TypeScript để triển khai máy chủ MCP bằng bộ công cụ MCP TypeScript SDK. Tài liệu bao quát cấu trúc dự án, thiết lập máy chủ, các mẫu đăng ký công cụ, xác thực đầu vào bằng Zod, xử lý lỗi và các ví dụ hoạt động hoàn chỉnh.

---

## Tham Chiếu Nhanh (Quick Reference)

### Các Lệnh Import Chính
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import express from "express";
import { z } from "zod";
```

### Khởi Tạo Máy Chủ
```typescript
const server = new McpServer({
  name: "service-mcp-server",
  version: "1.0.0"
});
```

### Mẫu Đăng Ký Công Cụ (Tool Registration Pattern)
```typescript
server.registerTool(
  "tool_name",
  {
    title: "Tool Display Name",
    description: "What the tool does",
    inputSchema: { param: z.string() },
    outputSchema: { result: z.string() }
  },
  async ({ param }) => {
    const output = { result: `Processed: param` };
    return {
      content: [{ type: "text", text: JSON.stringify(output) }],
      structuredContent: output // Modern pattern for structured data
    };
  }
);
```

---

## Bộ Công Cụ MCP TypeScript SDK

MCP TypeScript SDK chính thức cung cấp:
- Lớp `McpServer` để khởi tạo máy chủ
- Phương thức `registerTool` để đăng ký công cụ
- Tích hợp lược đồ Zod để xác thực dữ liệu đầu vào lúc chạy (runtime)
- Triển khai hàm xử lý công cụ (tool handler) an toàn về kiểu dữ liệu (type-safe)

**QUAN TRỌNG - Chỉ Sử Dụng Các API Hiện Đại:**
- **NÊN dùng**: `server.registerTool()`, `server.registerResource()`, `server.registerPrompt()`
- **KHÔNG ĐƯỢC dùng**: Các API cũ đã lỗi thời (deprecated) như `server.tool()`, `server.setRequestHandler(ListToolsRequestSchema, ...)`, hoặc đăng ký hàm xử lý thủ công
- Các phương thức `register*` mang lại độ an toàn kiểu tốt hơn, tự động xử lý lược đồ và là cách tiếp cận được khuyến nghị chính thức

Xem tài liệu MCP SDK trong phần tài liệu tham khảo để biết chi tiết đầy đủ.

## Quy Ước Đặt Tên Máy Chủ

Các máy chủ MCP viết bằng Node/TypeScript phải tuân theo quy tắc đặt tên sau:
- **Định dạng**: `{service}-mcp-server` (chữ thường, nối bằng dấu gạch ngang)
- **Ví dụ**: `github-mcp-server`, `jira-mcp-server`, `stripe-mcp-server`

Tên máy chủ cần phải:
- Mang tính tổng quát (không gắn chặt vào một tính năng đơn lẻ cụ thể)
- Mô tả rõ dịch vụ/API đang được tích hợp
- Dễ dàng suy luận ra từ mô tả nhiệm vụ
- Không chứa số phiên bản hoặc ngày tháng

## Cấu Trúc Dự Án

Hãy tạo cấu trúc thư mục sau cho các máy chủ MCP dùng Node/TypeScript:

```
{service}-mcp-server/
├── package.json
├── tsconfig.json
├── README.md
├── src/
│   ├── index.ts          # Điểm vào chính chứa phần khởi tạo McpServer
│   ├── types.ts          # Định nghĩa các kiểu dữ liệu và interface TypeScript
│   ├── tools/            # Triển khai các công cụ (mỗi miền nghiệp vụ một tệp)
│   ├── services/         # Các API client và hàm tiện ích dùng chung
│   ├── schemas/          # Các lược đồ xác thực Zod
│   └── constants.ts      # Các hằng số dùng chung (API_URL, CHARACTER_LIMIT, v.v.)
└── dist/                 # Các tệp JavaScript đã biên dịch (điểm vào: dist/index.js)
```

## Triển Khai Công Cụ (Tool Implementation)

### Đặt Tên Công Cụ

Sử dụng kiểu chữ `snake_case` cho tên công cụ (ví dụ: "search_users", "create_project", "get_channel_info") với những cái tên rõ ràng, hướng hành động.

**Tránh Xung Đột Tên Gọi**: Đưa ngữ cảnh tên dịch vụ vào đầu tên công cụ để tránh trùng lặp:
- Dùng "slack_send_message" thay vì chỉ ghi "send_message"
- Dùng "github_create_issue" thay vì chỉ ghi "create_issue"
- Dùng "asana_list_tasks" thay vì chỉ ghi "list_tasks"

### Cấu Trúc Công Cụ

Các công cụ được đăng ký bằng phương thức `registerTool` với các yêu cầu sau:
- Sử dụng lược đồ Zod để xác thực đầu vào lúc chạy và đảm bảo an toàn kiểu
- Trường `description` phải được cung cấp tường minh - các chú thích JSDoc KHÔNG được tự động trích xuất
- Cung cấp tường minh các trường `title`, `description`, `inputSchema` và `annotations`
- Trường `inputSchema` phải là một đối tượng lược đồ Zod (không phải JSON schema)
- Định kiểu tường minh cho tất cả các tham số và giá trị trả về

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer({
  name: "example-mcp",
  version: "1.0.0"
});

// Zod schema for input validation
const UserSearchInputSchema = z.object({
  query: z.string()
    .min(2, "Query must be at least 2 characters")
    .max(200, "Query must not exceed 200 characters")
    .describe("Search string to match against names/emails"),
  limit: z.number()
    .int()
    .min(1)
    .max(100)
    .default(20)
    .describe("Maximum results to return"),
  offset: z.number()
    .int()
    .min(0)
    .default(0)
    .describe("Number of results to skip for pagination"),
  response_format: z.nativeEnum(ResponseFormat)
    .default(ResponseFormat.MARKDOWN)
    .describe("Output format: 'markdown' for human-readable or 'json' for machine-readable")
}).strict();

// Type definition from Zod schema
type UserSearchInput = z.infer<typeof UserSearchInputSchema>;

server.registerTool(
  "example_search_users",
  {
    title: "Search Example Users",
    description: `Search for users in the Example system by name, email, or team.

This tool searches across all user profiles in the Example platform, supporting partial matches and various search filters. It does NOT create or modify users, only searches existing ones.

Args:
  - query (string): Search string to match against names/emails
  - limit (number): Maximum results to return, between 1-100 (default: 20)
  - offset (number): Number of results to skip for pagination (default: 0)
  - response_format ('markdown' | 'json'): Output format (default: 'markdown')

Returns:
  For JSON format: Structured data with schema:
  {
    "total": number,           // Total number of matches found
    "count": number,           // Number of results in this response
    "offset": number,          // Current pagination offset
    "users": [
      {
        "id": string,          // User ID (e.g., "U123456789")
        "name": string,        // Full name (e.g., "John Doe")
        "email": string,       // Email address
        "team": string,        // Team name (optional)
        "active": boolean      // Whether user is active
      }
    ],
    "has_more": boolean,       // Whether more results are available
    "next_offset": number      // Offset for next page (if has_more is true)
  }

Examples:
  - Use when: "Find all marketing team members" -> params with query="team:marketing"
  - Use when: "Search for John's account" -> params with query="john"
  - Don't use when: You need to create a user (use example_create_user instead)

Error Handling:
  - Returns "Error: Rate limit exceeded" if too many requests (429 status)
  - Returns "No users found matching '<query>'" if search returns empty`,
    inputSchema: UserSearchInputSchema,
    annotations: {
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: true
    }
  },
  async (params: UserSearchInput) => {
    try {
      // Input validation is handled by Zod schema
      // Make API request using validated parameters
      const data = await makeApiRequest<any>(
        "users/search",
        "GET",
        undefined,
        {
          q: params.query,
          limit: params.limit,
          offset: params.offset
        }
      );

      const users = data.users || [];
      const total = data.total || 0;

      if (!users.length) {
        return {
          content: [{
            type: "text",
            text: `No users found matching 'params.query'`
          }]
        };
      }

      // Prepare structured output
      const output = {
        total,
        count: users.length,
        offset: params.offset,
        users: users.map((user: any) => ({
          id: user.id,
          name: user.name,
          email: user.email,
          ...(user.team ? { team: user.team } : {}),
          active: user.active ?? true
        })),
        has_more: total > params.offset + users.length,
        ...(total > params.offset + users.length ? {
          next_offset: params.offset + users.length
        } : {})
      };

      // Format text representation based on requested format
      let textContent: string;
      if (params.response_format === ResponseFormat.MARKDOWN) {
        const lines = [`# User Search Results: 'params.query'`, "",
          `Found total users (showing users.length)`, ""];
        for (const user of users) {
          lines.push(`## user.name (user.id)`);
          lines.push(`- **Email**: user.email`);
          if (user.team) lines.push(`- **Team**: user.team`);
          lines.push("");
        }
        textContent = lines.join("\n");
      } else {
        textContent = JSON.stringify(output, null, 2);
      }

      return {
        content: [{ type: "text", text: textContent }],
        structuredContent: output // Modern pattern for structured data
      };
    } catch (error) {
      return {
        content: [{
          type: "text",
          text: handleApiError(error)
        }]
      };
    }
  }
);
```

## Lược Đồ Zod Cho Việc Xác Thực Đầu Vào

Zod cung cấp khả năng xác thực kiểu dữ liệu ngay trong lúc chạy (runtime):

```typescript
import { z } from "zod";

// Basic schema with validation
const CreateUserSchema = z.object({
  name: z.string()
    .min(1, "Name is required")
    .max(100, "Name must not exceed 100 characters"),
  email: z.string()
    .email("Invalid email format"),
  age: z.number()
    .int("Age must be a whole number")
    .min(0, "Age cannot be negative")
    .max(150, "Age cannot be greater than 150")
}).strict();  // Use .strict() to forbid extra fields

// Enums
enum ResponseFormat {
  MARKDOWN = "markdown",
  JSON = "json"
}

const SearchSchema = z.object({
  response_format: z.nativeEnum(ResponseFormat)
    .default(ResponseFormat.MARKDOWN)
    .describe("Output format")
});

// Optional fields with defaults
const PaginationSchema = z.object({
  limit: z.number()
    .int()
    .min(1)
    .max(100)
    .default(20)
    .describe("Maximum results to return"),
  offset: z.number()
    .int()
    .min(0)
    .default(0)
    .describe("Number of results to skip")
});
```

## Các Tùy Chọn Định Dạng Phản Hồi

Hỗ trợ nhiều định dạng đầu ra để đảm bảo tính linh hoạt:

```typescript
enum ResponseFormat {
  MARKDOWN = "markdown",
  JSON = "json"
}

const inputSchema = z.object({
  query: z.string(),
  response_format: z.nativeEnum(ResponseFormat)
    .default(ResponseFormat.MARKDOWN)
    .describe("Output format: 'markdown' for human-readable or 'json' for machine-readable")
});
```

**Định dạng Markdown**:
- Sử dụng tiêu đề, danh sách và định dạng văn bản để trình bày rõ ràng
- Chuyển đổi dấu thời gian (timestamps) sang định dạng dễ đọc cho con người
- Hiển thị tên hiển thị kèm theo mã ID đặt trong dấu ngoặc đơn
- Lược bỏ các siêu dữ liệu (metadata) rườm rà
- Gom nhóm các thông tin liên quan một cách hợp lý

**Định dạng JSON**:
- Trả về dữ liệu đầy đủ, có cấu trúc phù hợp cho việc xử lý bằng mã lệnh (programmatic processing)
- Bao gồm tất cả các trường và siêu dữ liệu hiện có
- Sử dụng tên trường và kiểu dữ liệu nhất quán

## Triển Khai Phân Trang (Pagination)

Đối với các công cụ liệt kê danh sách tài nguyên:

```typescript
const ListSchema = z.object({
  limit: z.number().int().min(1).max(100).default(20),
  offset: z.number().int().min(0).default(0)
});

async function listItems(params: z.infer<typeof ListSchema>) {
  const data = await apiRequest(params.limit, params.offset);

  const response = {
    total: data.total,
    count: data.items.length,
    offset: params.offset,
    items: data.items,
    has_more: data.total > params.offset + data.items.length,
    next_offset: data.total > params.offset + data.items.length
      ? params.offset + data.items.length
      : undefined
  };

  return JSON.stringify(response, null, 2);
}
```

## Giới Hạn Ký Tự Và Cắt Ngắn Dữ Liệu (Truncation)

Thêm hằng số `CHARACTER_LIMIT` để ngăn chặn các phản hồi quá lớn làm tràn ngữ cảnh:

```typescript
// At module level in constants.ts
export const CHARACTER_LIMIT = 25000;  // Maximum response size in characters

async function searchTool(params: SearchInput) {
  let result = generateResponse(data);

  // Check character limit and truncate if needed
  if (result.length > CHARACTER_LIMIT) {
    const truncatedData = data.slice(0, Math.max(1, data.length / 2));
    response.data = truncatedData;
    response.truncated = true;
    response.truncation_message =
      `Response truncated from data.length to truncatedData.length items. ` +
      `Use 'offset' parameter or add filters to see more results.`;
    result = JSON.stringify(response, null, 2);
  }

  return result;
}
```

## Xử Lý Lỗi (Error Handling)

Cung cấp các thông báo lỗi rõ ràng, có tính định hướng hành động:

```typescript
import axios, { AxiosError } from "axios";

function handleApiError(error: unknown): string {
  if (error instanceof AxiosError) {
    if (error.response) {
      switch (error.response.status) {
        case 404:
          return "Error: Resource not found. Please check the ID is correct.";
        case 403:
          return "Error: Permission denied. You don't have access to this resource.";
        case 429:
          return "Error: Rate limit exceeded. Please wait before making more requests.";
        default:
          return `Error: API request failed with status error.response.status`;
      }
    } else if (error.code === "ECONNABORTED") {
      return "Error: Request timed out. Please try again.";
    }
  }
  return `Error: Unexpected error occurred: String(error)`;
}
```

## Các Hàm Tiện Ích Dùng Chung (Shared Utilities)

Tách các chức năng phổ biến thành các hàm có thể tái sử dụng:

```typescript
// Shared API request function
async function makeApiRequest<T>(
  endpoint: string,
  method: "GET" | "POST" | "PUT" | "DELETE" = "GET",
  data?: any,
  params?: any
): Promise<T> {
  try {
    const response = await axios({
      method,
      url: `API_BASE_URL/endpoint`,
      data,
      params,
      timeout: 30000,
      headers: {
        "Content-Type": "application/json",
        "Accept": "application/json"
      }
    });
    return response.data;
  } catch (error) {
    throw error;
  }
}
```

## Thông Lệ Tốt Nhất Với Async/Await

Luôn sử dụng `async/await` cho các yêu cầu mạng và thao tác vào/ra (I/O):

```typescript
// Good: Async network request
async function fetchData(resourceId: string): Promise<ResourceData> {
  const response = await axios.get(`API_URL/resource/resourceId`);
  return response.data;
}

// Bad: Promise chains
function fetchData(resourceId: string): Promise<ResourceData> {
  return axios.get(`API_URL/resource/resourceId`)
    .then(response => response.data);  // Harder to read and maintain
}
```

## Các Thông Lệ Tốt Nhất Với TypeScript

1. **Sử dụng TypeScript Nghiêm ngặt (Strict TypeScript)**: Bật chế độ `strict` trong tệp `tsconfig.json`
2. **Định nghĩa Interfaces**: Tạo các định nghĩa `interface` rõ ràng cho mọi cấu trúc dữ liệu
3. **Tránh dùng `any`**: Sử dụng các kiểu dữ liệu chuẩn xác hoặc `unknown` thay cho `any`
4. **Dùng Zod để Xác thực Lúc chạy**: Sử dụng lược đồ Zod để xác thực dữ liệu từ nguồn bên ngoài
5. **Bộ bảo vệ kiểu (Type Guards)**: Tạo các hàm type guard để kiểm tra các kiểu dữ liệu phức tạp
6. **Xử lý lỗi**: Luôn sử dụng khối `try-catch` kèm theo bước kiểm tra kiểu lỗi chuẩn xác
7. **An toàn với giá trị Null (Null Safety)**: Sử dụng toán tử truy cập chuỗi tùy chọn (`?.`) và toán tử hợp nhất null (`??`)

```typescript
// Good: Type-safe with Zod and interfaces
interface UserResponse {
  id: string;
  name: string;
  email: string;
  team?: string;
  active: boolean;
}

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
  team: z.string().optional(),
  active: z.boolean()
});

type User = z.infer<typeof UserSchema>;

async function getUser(id: string): Promise<User> {
  const data = await apiCall(`/users/id`);
  return UserSchema.parse(data);  // Runtime validation
}

// Bad: Using any
async function getUser(id: string): Promise<any> {
  return await apiCall(`/users/id`);  // No type safety
}
```

## Cấu Hình Gói Dự Án (Package Configuration)

### package.json

```json
{
  "name": "{service}-mcp-server",
  "version": "1.0.0",
  "description": "MCP server for {Service} API integration",
  "type": "module",
  "main": "dist/index.js",
  "scripts": {
    "start": "node dist/index.js",
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "clean": "rm -rf dist"
  },
  "engines": {
    "node": ">=18"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.1",
    "axios": "^1.7.9",
    "zod": "^3.23.8"
  },
  "devDependencies": {
    "@types/node": "^22.10.0",
    "tsx": "^4.19.2",
    "typescript": "^5.7.2"
  }
}
```

### tsconfig.json

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "allowSyntheticDefaultImports": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
```

## Ví Dụ Hoàn Chỉnh

```typescript
#!/usr/bin/env node
/**
 * MCP Server for Example Service.
 *
 * This server provides tools to interact with Example API, including user search,
 * project management, and data export capabilities.
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import axios, { AxiosError } from "axios";

// Constants
const API_BASE_URL = "https://api.example.com/v1";
const CHARACTER_LIMIT = 25000;

// Enums
enum ResponseFormat {
  MARKDOWN = "markdown",
  JSON = "json"
}

// Zod schemas
const UserSearchInputSchema = z.object({
  query: z.string()
    .min(2, "Query must be at least 2 characters")
    .max(200, "Query must not exceed 200 characters")
    .describe("Search string to match against names/emails"),
  limit: z.number()
    .int()
    .min(1)
    .max(100)
    .default(20)
    .describe("Maximum results to return"),
  offset: z.number()
    .int()
    .min(0)
    .default(0)
    .describe("Number of results to skip for pagination"),
  response_format: z.nativeEnum(ResponseFormat)
    .default(ResponseFormat.MARKDOWN)
    .describe("Output format: 'markdown' for human-readable or 'json' for machine-readable")
}).strict();

type UserSearchInput = z.infer<typeof UserSearchInputSchema>;

// Shared utility functions
async function makeApiRequest<T>(
  endpoint: string,
  method: "GET" | "POST" | "PUT" | "DELETE" = "GET",
  data?: any,
  params?: any
): Promise<T> {
  try {
    const response = await axios({
      method,
      url: `API_BASE_URL/endpoint`,
      data,
      params,
      timeout: 30000,
      headers: {
        "Content-Type": "application/json",
        "Accept": "application/json"
      }
    });
    return response.data;
  } catch (error) {
    throw error;
  }
}

function handleApiError(error: unknown): string {
  if (error instanceof AxiosError) {
    if (error.response) {
      switch (error.response.status) {
        case 404:
          return "Error: Resource not found. Please check the ID is correct.";
        case 403:
          return "Error: Permission denied. You don't have access to this resource.";
        case 429:
          return "Error: Rate limit exceeded. Please wait before making more requests.";
        default:
          return `Error: API request failed with status error.response.status`;
      }
    } else if (error.code === "ECONNABORTED") {
      return "Error: Request timed out. Please try again.";
    }
  }
  return `Error: Unexpected error occurred: String(error)`;
}

// Create MCP server instance
const server = new McpServer({
  name: "example-mcp",
  version: "1.0.0"
});

// Register tools
server.registerTool(
  "example_search_users",
  {
    title: "Search Example Users",
    description: `[Full description as shown above]`,
    inputSchema: UserSearchInputSchema,
    annotations: {
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: true
    }
  },
  async (params: UserSearchInput) => {
    // Implementation as shown above
  }
);

// Main function
// For stdio (local):
async function runStdio() {
  if (!process.env.EXAMPLE_API_KEY) {
    console.error("ERROR: EXAMPLE_API_KEY environment variable is required");
    process.exit(1);
  }

  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("MCP server running via stdio");
}

// For streamable HTTP (remote):
async function runHTTP() {
  if (!process.env.EXAMPLE_API_KEY) {
    console.error("ERROR: EXAMPLE_API_KEY environment variable is required");
    process.exit(1);
  }

  const app = express();
  app.use(express.json());

  app.post('/mcp', async (req, res) => {
    const transport = new StreamableHTTPServerTransport({
      sessionIdGenerator: undefined,
      enableJsonResponse: true
    });
    res.on('close', () => transport.close());
    await server.connect(transport);
    await transport.handleRequest(req, res, req.body);
  });

  const port = parseInt(process.env.PORT || '3000');
  app.listen(port, () => {
    console.error(`MCP server running on http://localhost:port/mcp`);
  });
}

// Choose transport based on environment
const transport = process.env.TRANSPORT || 'stdio';
if (transport === 'http') {
  runHTTP().catch(error => {
    console.error("Server error:", error);
    process.exit(1);
  });
} else {
  runStdio().catch(error => {
    console.error("Server error:", error);
    process.exit(1);
  });
}
```

---

## Các Tính Năng MCP Nâng Cao

### Đăng Ký Tài Nguyên (Resource Registration)

Phơi bày dữ liệu dưới dạng các tài nguyên (resources) để truy cập hiệu quả dựa trên đường dẫn URI:

```typescript
import { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js";

// Register a resource with URI template
server.registerResource(
  {
    uri: "file://documents/{name}",
    name: "Document Resource",
    description: "Access documents by name",
    mimeType: "text/plain"
  },
  async (uri: string) => {
    // Extract parameter from URI
    const match = uri.match(/^file:\/\/documents\/(.+)$/);
    if (!match) {
      throw new Error("Invalid URI format");
    }

    const documentName = match[1];
    const content = await loadDocument(documentName);

    return {
      contents: [{
        uri,
        mimeType: "text/plain",
        text: content
      }]
    };
  }
);

// List available resources dynamically
server.registerResourceList(async () => {
  const documents = await getAvailableDocuments();
  return {
    resources: documents.map(doc => ({
      uri: `file://documents/doc.name`,
      name: doc.name,
      mimeType: "text/plain",
      description: doc.description
    }))
  };
});
```

**Khi nào nên dùng Resources so với Tools:**
- **Resources**: Dành cho việc truy cập dữ liệu với các tham số đơn giản dựa trên URI
- **Tools**: Dành cho các thao tác phức tạp đòi hỏi xác thực đầu vào và xử lý logic nghiệp vụ
- **Resources**: Khi dữ liệu mang tính tĩnh tương đối hoặc dựa trên khuôn mẫu (template-based)
- **Tools**: Khi thao tác tạo ra tác dụng phụ (side effects) hoặc gồm các luồng công việc phức tạp

### Các Tùy Chọn Truyền Tải (Transport Options)

Bộ công cụ TypeScript SDK hỗ trợ hai cơ chế truyền tải chính:

#### Streamable HTTP (Khuyến nghị cho các Máy chủ Từ xa)

```typescript
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";

const app = express();
app.use(express.json());

app.post('/mcp', async (req, res) => {
  // Create new transport for each request (stateless, prevents request ID collisions)
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: undefined,
    enableJsonResponse: true
  });

  res.on('close', () => transport.close());

  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

app.listen(3000);
```

#### stdio (Dành cho Tích hợp Cục bộ)

```typescript
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const transport = new StdioServerTransport();
await server.connect(transport);
```

**Lựa chọn phương thức truyền tải:**
- **Streamable HTTP**: Dịch vụ web, truy cập từ xa, phục vụ nhiều ứng dụng khách (clients) cùng lúc
- **stdio**: Công cụ dòng lệnh, phát triển cục bộ, tích hợp dưới dạng tiến trình con (subprocess)

### Hỗ Trợ Thông Báo (Notification Support)

Thông báo cho phía client khi trạng thái của máy chủ thay đổi:

```typescript
// Notify when tools list changes
server.notification({
  method: "notifications/tools/list_changed"
});

// Notify when resources change
server.notification({
  method: "notifications/resources/list_changed"
});
```

Hãy sử dụng thông báo một cách tiết chế - chỉ gửi khi năng lực (capabilities) của máy chủ thực sự thay đổi.

---

## Các Thông Lệ Tốt Nhất Về Mã Nguồn

### Khả Năng Kết Hợp Và Tái Sử Dụng Mã Nguồn (Code Composability and Reusability)

Bản triển khai của bạn BẮT BUỘC phải ưu tiên tính kết hợp và khả năng tái sử dụng mã nguồn:

1. **Tách Các Chức Năng Chung**:
   - Tạo các hàm bổ trợ (helper functions) tái sử dụng được cho những thao tác xuất hiện ở nhiều công cụ
   - Xây dựng các API client dùng chung cho các yêu cầu HTTP thay vì viết lặp lại mã nguồn
   - Tập trung hóa logic xử lý lỗi vào các hàm tiện ích
   - Tách logic nghiệp vụ vào các hàm chuyên biệt có thể kết hợp lại với nhau
   - Tách riêng chức năng chọn lọc trường dữ liệu & định dạng kết quả Markdown hoặc JSON dùng chung

2. **Tránh Trùng Lặp Mã Nguồn**:
   - KHÔNG BAO GIỜ sao chép-dán (copy-paste) các đoạn mã tương tự nhau giữa các công cụ
   - Nếu bạn thấy mình đang viết một logic tương tự đến lần thứ hai, hãy tách nó thành một hàm dùng chung
   - Các thao tác phổ biến như phân trang, lọc dữ liệu, chọn trường và định dạng kết quả cần được chia sẻ chung
   - Logic xác thực và phân quyền cần được tập trung hóa ở một nơi

## Biên Dịch Và Chạy Thử (Building and Running)

Luôn biên dịch mã TypeScript của bạn trước khi chạy:

```bash
# Build the project
npm run build

# Run the server
npm start

# Development with auto-reload
npm run dev
```

Luôn đảm bảo lệnh `npm run build` hoàn tất thành công trước khi coi bản triển khai là đã hoàn thành.

## Danh Sách Kiểm Tra Chất Lượng (Quality Checklist)

Trước khi hoàn tất việc triển khai máy chủ MCP bằng Node/TypeScript, hãy đảm bảo:

### Thiết Kế Chiến Lược (Strategic Design)
- [ ] Các công cụ hỗ trợ thực hiện các luồng công việc hoàn chỉnh, chứ không chỉ là lớp vỏ bọc mỏng quanh các điểm cuối API
- [ ] Tên công cụ phản ánh sự phân chia tác vụ một cách tự nhiên
- [ ] Định dạng phản hồi được tối ưu hóa để tiết kiệm ngữ cảnh (context efficiency) cho tác tử
- [ ] Sử dụng các mã định danh dễ đọc cho con người ở những chỗ phù hợp
- [ ] Thông báo lỗi hướng dẫn tác tử đi tới cách sử dụng đúng

### Chất Lượng Triển Khai (Implementation Quality)
- [ ] TRIỂN KHAI CÓ TRỌNG TÂM: Đã triển khai các công cụ quan trọng và mang lại nhiều giá trị nhất
- [ ] Tất cả công cụ đều được đăng ký bằng `registerTool` với cấu hình đầy đủ
- [ ] Tất cả công cụ đều bao gồm `title`, `description`, `inputSchema` và `annotations`
- [ ] Các chú thích (`annotations`) được thiết lập chính xác (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`)
- [ ] Tất cả công cụ đều sử dụng lược đồ Zod để xác thực đầu vào lúc chạy kèm theo ràng buộc `.strict()`
- [ ] Tất cả lược đồ Zod đều có các ràng buộc hợp lý và thông báo lỗi giàu tính mô tả
- [ ] Tất cả công cụ đều có mô tả toàn diện với kiểu đầu vào/đầu ra tường minh
- [ ] Phần mô tả bao gồm các ví dụ về giá trị trả về và tài liệu lược đồ đầy đủ
- [ ] Các thông báo lỗi rõ ràng, có tính hành động và mang tính hướng dẫn cao

### Chất Lượng TypeScript (TypeScript Quality)
- [ ] Các interface TypeScript được định nghĩa cho toàn bộ các cấu trúc dữ liệu
- [ ] Chế độ Strict TypeScript được bật trong `tsconfig.json`
- [ ] Không sử dụng kiểu `any` - hãy dùng `unknown` hoặc các kiểu dữ liệu cụ thể để thay thế
- [ ] Tất cả các hàm `async` đều khai báo kiểu trả về `Promise<T>` tường minh
- [ ] Việc xử lý lỗi sử dụng các bộ bảo vệ kiểu (type guards) chuẩn xác (ví dụ: `axios.isAxiosError`, `z.ZodError`)

### Các Tính Năng Nâng Cao (nếu áp dụng)
- [ ] Đã đăng ký Resources cho các điểm cuối dữ liệu phù hợp
- [ ] Đã cấu hình cơ chế truyền tải phù hợp (`stdio` hoặc `streamable HTTP`)
- [ ] Đã triển khai thông báo (Notifications) cho các năng lực máy chủ thay đổi động
- [ ] Đảm bảo an toàn kiểu với các interface của SDK

### Cấu Hình Dự Án (Project Configuration)
- [ ] Tệp `package.json` bao gồm đầy đủ tất cả các thư viện phụ thuộc cần thiết
- [ ] Kịch bản build tạo ra mã JavaScript hoạt động tốt trong thư mục `dist/`
- [ ] Điểm vào chính được cấu hình chuẩn xác là `dist/index.js`
- [ ] Tên máy chủ tuân thủ đúng định dạng: `{service}-mcp-server`
- [ ] Tệp `tsconfig.json` được cấu hình đúng chuẩn với chế độ `strict`

### Chất Lượng Mã Nguồn (Code Quality)
- [ ] Cơ chế phân trang được triển khai đúng cách ở những nơi cần thiết
- [ ] Các phản hồi lớn đều kiểm tra hằng số `CHARACTER_LIMIT` và cắt ngắn kèm theo thông báo rõ ràng
- [ ] Cung cấp các tùy chọn lọc (filtering) cho các tập kết quả có khả năng phình to
- [ ] Mọi thao tác mạng đều xử lý mượt mà lỗi quá thời gian chờ (timeouts) và lỗi kết nối
- [ ] Các chức năng chung được tách thành các hàm tái sử dụng
- [ ] Kiểu dữ liệu trả về nhất quán giữa các thao tác tương tự nhau

### Kiểm Thử Và Đóng Gói (Testing and Build)
- [ ] Lệnh `npm run build` chạy hoàn tất thành công không có lỗi
- [ ] Tệp `dist/index.js` được tạo ra và có thể thực thi
- [ ] Máy chủ chạy được với lệnh: `node dist/index.js --help`
- [ ] Tất cả các lệnh import đều phân giải đường dẫn chính xác
- [ ] Các lệnh gọi công cụ mẫu hoạt động đúng như kỳ vọng
FILE:reference/python_mcp_server.md
# Hướng Dẫn Triển Khai Máy Chủ MCP Bằng Python

## Tổng Quan

Tài liệu này cung cấp các thông lệ tốt nhất và ví dụ chuyên biệt cho Python để triển khai máy chủ MCP bằng bộ công cụ MCP Python SDK. Tài liệu bao quát việc thiết lập máy chủ, các mẫu đăng ký công cụ, xác thực đầu vào bằng Pydantic, xử lý lỗi và các ví dụ hoạt động hoàn chỉnh.

---

## Tham Chiếu Nhanh (Quick Reference)

### Các Lệnh Import Chính
```python
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field, field_validator, ConfigDict
from typing import Optional, List, Dict, Any
from enum import Enum
import httpx
```

### Khởi Tạo Máy Chủ
```python
mcp = FastMCP("service_mcp")
```

### Mẫu Đăng Ký Công Cụ (Tool Registration Pattern)
```python
@mcp.tool(name="tool_name", annotations={...})
async def tool_function(params: InputModel) -> str:
    # Implementation
    pass
```

---

## Bộ Công Cụ MCP Python SDK Và FastMCP

MCP Python SDK chính thức cung cấp FastMCP, một framework bậc cao để xây dựng các máy chủ MCP. Nó mang lại:
- Khả năng tự động sinh mô tả (`description`) và lược đồ đầu vào (`inputSchema`) từ chữ ký hàm (function signatures) và chuỗi tài liệu (docstrings)
- Tích hợp mô hình Pydantic để xác thực dữ liệu đầu vào
- Đăng ký công cụ dựa trên decorator với `@mcp.tool`

**Để xem toàn bộ tài liệu SDK đầy đủ, hãy dùng WebFetch để tải:**
`https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/main/README.md`

## Quy Ước Đặt Tên Máy Chủ

Các máy chủ MCP viết bằng Python phải tuân theo quy tắc đặt tên sau:
- **Định dạng**: `{service}_mcp` (chữ thường, nối bằng dấu gạch dưới)
- **Ví dụ**: `github_mcp`, `jira_mcp`, `stripe_mcp`

Tên máy chủ cần phải:
- Mang tính tổng quát (không gắn chặt vào một tính năng đơn lẻ cụ thể)
- Mô tả rõ dịch vụ/API đang được tích hợp
- Dễ dàng suy luận ra từ mô tả nhiệm vụ
- Không chứa số phiên bản hoặc ngày tháng

## Triển Khai Công Cụ (Tool Implementation)

### Đặt Tên Công Cụ

Sử dụng kiểu chữ `snake_case` cho tên công cụ (ví dụ: "search_users", "create_project", "get_channel_info") với những cái tên rõ ràng, hướng hành động.

**Tránh Xung Đột Tên Gọi**: Đưa ngữ cảnh tên dịch vụ vào đầu tên công cụ để tránh trùng lặp:
- Dùng "slack_send_message" thay vì chỉ ghi "send_message"
- Dùng "github_create_issue" thay vì chỉ ghi "create_issue"
- Dùng "asana_list_tasks" thay vì chỉ ghi "list_tasks"

### Cấu Trúc Công Cụ Với FastMCP

Các công cụ được định nghĩa bằng cách sử dụng decorator `@mcp.tool` kết hợp với các mô hình Pydantic để xác thực đầu vào:

```python
from pydantic import BaseModel, Field, ConfigDict
from mcp.server.fastmcp import FastMCP

# Initialize the MCP server
mcp = FastMCP("example_mcp")

# Define Pydantic model for input validation
class ServiceToolInput(BaseModel):
    '''Input model for service tool operation.'''
    model_config = ConfigDict(
        str_strip_whitespace=True,  # Auto-strip whitespace from strings
        validate_assignment=True,    # Validate on assignment
        extra='forbid'              # Forbid extra fields
    )

    param1: str = Field(..., description="First parameter description (e.g., 'user123', 'project-abc')", min_length=1, max_length=100)
    param2: Optional[int] = Field(default=None, description="Optional integer parameter with constraints", ge=0, le=1000)
    tags: Optional[List[str]] = Field(default_factory=list, description="List of tags to apply", max_items=10)

@mcp.tool(
    name="service_tool_name",
    annotations={
        "title": "Human-Readable Tool Title",
        "readOnlyHint": True,     # Tool does not modify environment
        "destructiveHint": False,  # Tool does not perform destructive operations
        "idempotentHint": True,    # Repeated calls have no additional effect
        "openWorldHint": False     # Tool does not interact with external entities
    }
)
async def service_tool_name(params: ServiceToolInput) -> str:
    '''Tool description automatically becomes the 'description' field.

    This tool performs a specific operation on the service. It validates all inputs
    using the ServiceToolInput Pydantic model before processing.

    Args:
        params (ServiceToolInput): Validated input parameters containing:
            - param1 (str): First parameter description
            - param2 (Optional[int]): Optional parameter with default
            - tags (Optional[List[str]]): List of tags

    Returns:
        str: JSON-formatted response containing operation results
    '''
    # Implementation here
    pass
```

## Các Tính Năng Chính Của Pydantic v2

- Sử dụng `model_config` thay cho lớp `Config` lồng bên trong
- Sử dụng `field_validator` thay cho `validator` đã lỗi thời
- Sử dụng `model_dump()` thay cho `dict()` đã lỗi thời
- Các hàm kiểm tra hợp lệ (validators) bắt buộc phải có decorator `@classmethod`
- Bắt buộc phải khai báo gợi ý kiểu (type hints) cho các phương thức validator

```python
from pydantic import BaseModel, Field, field_validator, ConfigDict

class CreateUserInput(BaseModel):
    model_config = ConfigDict(
        str_strip_whitespace=True,
        validate_assignment=True
    )

    name: str = Field(..., description="User's full name", min_length=1, max_length=100)
    email: str = Field(..., description="User's email address", pattern=r'^[\w\.-]+@[\w\.-]+\.\w+$')
    age: int = Field(..., description="User's age", ge=0, le=150)

    @field_validator('email')
    @classmethod
    def validate_email(cls, v: str) -> str:
        if not v.strip():
            raise ValueError("Email cannot be empty")
        return v.lower()
```

## Các Tùy Chọn Định Dạng Phản Hồi

Hỗ trợ nhiều định dạng đầu ra để đảm bảo tính linh hoạt:

```python
from enum import Enum

class ResponseFormat(str, Enum):
    '''Output format for tool responses.'''
    MARKDOWN = "markdown"
    JSON = "json"

class UserSearchInput(BaseModel):
    query: str = Field(..., description="Search query")
    response_format: ResponseFormat = Field(
        default=ResponseFormat.MARKDOWN,
        description="Output format: 'markdown' for human-readable or 'json' for machine-readable"
    )
```

**Định dạng Markdown**:
- Sử dụng tiêu đề, danh sách và định dạng văn bản để trình bày rõ ràng
- Chuyển đổi dấu thời gian sang định dạng dễ đọc cho con người (ví dụ: "2024-01-15 10:30:00 UTC" thay vì số giây epoch)
- Hiển thị tên hiển thị kèm theo mã ID đặt trong dấu ngoặc đơn (ví dụ: "@john.doe (U123456)")
- Lược bỏ các siêu dữ liệu (metadata) rườm rà (ví dụ: chỉ hiển thị một URL ảnh đại diện, không liệt kê tất cả các kích cỡ)
- Gom nhóm các thông tin liên quan một cách hợp lý

**Định dạng JSON**:
- Trả về dữ liệu đầy đủ, có cấu trúc phù hợp cho việc xử lý bằng mã lệnh (programmatic processing)
- Bao gồm tất cả các trường và siêu dữ liệu hiện có
- Sử dụng tên trường và kiểu dữ liệu nhất quán

## Triển Khai Phân Trang (Pagination Implementation)

Đối với các công cụ liệt kê danh sách tài nguyên:

```python
class ListInput(BaseModel):
    limit: Optional[int] = Field(default=20, description="Maximum results to return", ge=1, le=100)
    offset: Optional[int] = Field(default=0, description="Number of results to skip for pagination", ge=0)

async def list_items(params: ListInput) -> str:
    # Make API request with pagination
    data = await api_request(limit=params.limit, offset=params.offset)

    # Return pagination info
    response = {
        "total": data["total"],
        "count": len(data["items"]),
        "offset": params.offset,
        "items": data["items"],
        "has_more": data["total"] > params.offset + len(data["items"]),
        "next_offset": params.offset + len(data["items"]) if data["total"] > params.offset + len(data["items"]) else None
    }
    return json.dumps(response, indent=2)
```

## Xử Lý Lỗi (Error Handling)

Cung cấp các thông báo lỗi rõ ràng, có tính định hướng hành động:

```python
def _handle_api_error(e: Exception) -> str:
    '''Consistent error formatting across all tools.'''
    if isinstance(e, httpx.HTTPStatusError):
        if e.response.status_code == 404:
            return "Error: Resource not found. Please check the ID is correct."
        elif e.response.status_code == 403:
            return "Error: Permission denied. You don't have access to this resource."
        elif e.response.status_code == 429:
            return "Error: Rate limit exceeded. Please wait before making more requests."
        return f"Error: API request failed with status {e.response.status_code}"
    elif isinstance(e, httpx.TimeoutException):
        return "Error: Request timed out. Please try again."
    return f"Error: Unexpected error occurred: {type(e).__name__}"
```

## Các Hàm Tiện Ích Dùng Chung (Shared Utilities)

Tách các chức năng phổ biến thành các hàm có thể tái sử dụng:

```python
# Shared API request function
async def _make_api_request(endpoint: str, method: str = "GET", **kwargs) -> dict:
    '''Reusable function for all API calls.'''
    async with httpx.AsyncClient() as client:
        response = await client.request(
            method,
            f"{API_BASE_URL}/{endpoint}",
            timeout=30.0,
            **kwargs
        )
        response.raise_for_status()
        return response.json()
```

## Thông Lệ Tốt Nhất Với Async/Await

Luôn sử dụng `async/await` cho các yêu cầu mạng và thao tác vào/ra (I/O):

```python
# Good: Async network request
async def fetch_data(resource_id: str) -> dict:
    async with httpx.AsyncClient() as client:
        response = await client.get(f"{API_URL}/resource/{resource_id}")
        response.raise_for_status()
        return response.json()

# Bad: Synchronous request
def fetch_data(resource_id: str) -> dict:
    response = requests.get(f"{API_URL}/resource/{resource_id}")  # Blocks
    return response.json()
```

## Gợi Ý Kiểu Dữ Liệu (Type Hints)

Sử dụng gợi ý kiểu dữ liệu xuyên suốt toàn bộ mã nguồn:

```python
from typing import Optional, List, Dict, Any

async def get_user(user_id: str) -> Dict[str, Any]:
    data = await fetch_user(user_id)
    return {"id": data["id"], "name": data["name"]}
```

## Chuỗi Tài Liệu Của Công Cụ (Tool Docstrings)

Mọi công cụ đều phải có chuỗi tài liệu (docstrings) toàn diện với thông tin kiểu dữ liệu tường minh:

```python
async def search_users(params: UserSearchInput) -> str:
    '''
    Search for users in the Example system by name, email, or team.

    This tool searches across all user profiles in the Example platform,
    supporting partial matches and various search filters. It does NOT
    create or modify users, only searches existing ones.

    Args:
        params (UserSearchInput): Validated input parameters containing:
            - query (str): Search string to match against names/emails (e.g., "john", "@example.com", "team:marketing")
            - limit (Optional[int]): Maximum results to return, between 1-100 (default: 20)
            - offset (Optional[int]): Number of results to skip for pagination (default: 0)

    Returns:
        str: JSON-formatted string containing search results with the following schema:

        Success response:
        {
            "total": int,           # Total number of matches found
            "count": int,           # Number of results in this response
            "offset": int,          # Current pagination offset
            "users": [
                {
                    "id": str,      # User ID (e.g., "U123456789")
                    "name": str,    # Full name (e.g., "John Doe")
                    "email": str,   # Email address (e.g., "john@example.com")
                    "team": str     # Team name (e.g., "Marketing") - optional
                }
            ]
        }

        Error response:
        "Error: <error message>" or "No users found matching '<query>'"

    Examples:
        - Use when: "Find all marketing team members" -> params with query="team:marketing"
        - Use when: "Search for John's account" -> params with query="john"
        - Don't use when: You need to create a user (use example_create_user instead)
        - Don't use when: You have a user ID and need full details (use example_get_user instead)

    Error Handling:
        - Input validation errors are handled by Pydantic model
        - Returns "Error: Rate limit exceeded" if too many requests (429 status)
        - Returns "Error: Invalid API authentication" if API key is invalid (401 status)
        - Returns formatted list of results or "No users found matching 'query'"
    '''
```

## Ví Dụ Hoàn Chỉnh

Xem bên dưới để tham khảo một ví dụ máy chủ MCP viết bằng Python hoàn chỉnh:

```python
#!/usr/bin/env python3
'''
MCP Server for Example Service.

This server provides tools to interact with Example API, including user search,
project management, and data export capabilities.
'''

from typing import Optional, List, Dict, Any
from enum import Enum
import httpx
from pydantic import BaseModel, Field, field_validator, ConfigDict
from mcp.server.fastmcp import FastMCP

# Initialize the MCP server
mcp = FastMCP("example_mcp")

# Constants
API_BASE_URL = "https://api.example.com/v1"

# Enums
class ResponseFormat(str, Enum):
    '''Output format for tool responses.'''
    MARKDOWN = "markdown"
    JSON = "json"

# Pydantic Models for Input Validation
class UserSearchInput(BaseModel):
    '''Input model for user search operations.'''
    model_config = ConfigDict(
        str_strip_whitespace=True,
        validate_assignment=True
    )

    query: str = Field(..., description="Search string to match against names/emails", min_length=2, max_length=200)
    limit: Optional[int] = Field(default=20, description="Maximum results to return", ge=1, le=100)
    offset: Optional[int] = Field(default=0, description="Number of results to skip for pagination", ge=0)
    response_format: ResponseFormat = Field(default=ResponseFormat.MARKDOWN, description="Output format")

    @field_validator('query')
    @classmethod
    def validate_query(cls, v: str) -> str:
        if not v.strip():
            raise ValueError("Query cannot be empty or whitespace only")
        return v.strip()

# Shared utility functions
async def _make_api_request(endpoint: str, method: str = "GET", **kwargs) -> dict:
    '''Reusable function for all API calls.'''
    async with httpx.AsyncClient() as client:
        response = await client.request(
            method,
            f"{API_BASE_URL}/{endpoint}",
            timeout=30.0,
            **kwargs
        )
        response.raise_for_status()
        return response.json()

def _handle_api_error(e: Exception) -> str:
    '''Consistent error formatting across all tools.'''
    if isinstance(e, httpx.HTTPStatusError):
        if e.response.status_code == 404:
            return "Error: Resource not found. Please check the ID is correct."
        elif e.response.status_code == 403:
            return "Error: Permission denied. You don't have access to this resource."
        elif e.response.status_code == 429:
            return "Error: Rate limit exceeded. Please wait before making more requests."
        return f"Error: API request failed with status {e.response.status_code}"
    elif isinstance(e, httpx.TimeoutException):
        return "Error: Request timed out. Please try again."
    return f"Error: Unexpected error occurred: {type(e).__name__}"

# Tool definitions
@mcp.tool(
    name="example_search_users",
    annotations={
        "title": "Search Example Users",
        "readOnlyHint": True,
        "destructiveHint": False,
        "idempotentHint": True,
        "openWorldHint": True
    }
)
async def example_search_users(params: UserSearchInput) -> str:
    '''Search for users in the Example system by name, email, or team.

    [Full docstring as shown above]
    '''
    try:
        # Make API request using validated parameters
        data = await _make_api_request(
            "users/search",
            params={
                "q": params.query,
                "limit": params.limit,
                "offset": params.offset
            }
        )

        users = data.get("users", [])
        total = data.get("total", 0)

        if not users:
            return f"No users found matching '{params.query}'"

        # Format response based on requested format
        if params.response_format == ResponseFormat.MARKDOWN:
            lines = [f"# User Search Results: '{params.query}'", ""]
            lines.append(f"Found {total} users (showing {len(users)})")
            lines.append("")

            for user in users:
                lines.append(f"## {user['name']} ({user['id']})")
                lines.append(f"- **Email**: {user['email']}")
                if user.get('team'):
                    lines.append(f"- **Team**: {user['team']}")
                lines.append("")

            return "\n".join(lines)

        else:
            # Machine-readable JSON format
            import json
            response = {
                "total": total,
                "count": len(users),
                "offset": params.offset,
                "users": users
            }
            return json.dumps(response, indent=2)

    except Exception as e:
        return _handle_api_error(e)

if __name__ == "__main__":
    mcp.run()
```

---

## Các Tính Năng Nâng Cao Của FastMCP

### Tiêm Tham Số Ngữ Cảnh (Context Parameter Injection)

FastMCP có thể tự động tiêm (inject) một tham số `Context` vào các công cụ để cung cấp những năng lực nâng cao như ghi nhật ký (logging), báo cáo tiến độ, đọc tài nguyên và tương tác với người dùng:

```python
from mcp.server.fastmcp import FastMCP, Context

mcp = FastMCP("example_mcp")

@mcp.tool()
async def advanced_search(query: str, ctx: Context) -> str:
    '''Advanced tool with context access for logging and progress.'''

    # Report progress for long operations
    await ctx.report_progress(0.25, "Starting search...")

    # Log information for debugging
    await ctx.log_info("Processing query", {"query": query, "timestamp": datetime.now()})

    # Perform search
    results = await search_api(query)
    await ctx.report_progress(0.75, "Formatting results...")

    # Access server configuration
    server_name = ctx.fastmcp.name

    return format_results(results)

@mcp.tool()
async def interactive_tool(resource_id: str, ctx: Context) -> str:
    '''Tool that can request additional input from users.'''

    # Request sensitive information when needed
    api_key = await ctx.elicit(
        prompt="Please provide your API key:",
        input_type="password"
    )

    # Use the provided key
    return await api_call(resource_id, api_key)
```

**Các năng lực của Context:**
- `ctx.report_progress(progress, message)` - Báo cáo tiến độ cho các tác vụ chạy lâu
- `ctx.log_info(message, data)` / `ctx.log_error()` / `ctx.log_debug()` - Ghi nhật ký
- `ctx.elicit(prompt, input_type)` - Yêu cầu nhập thông tin bổ sung từ người dùng
- `ctx.fastmcp.name` - Truy cập cấu hình máy chủ
- `ctx.read_resource(uri)` - Đọc tài nguyên MCP

### Đăng Ký Tài Nguyên (Resource Registration)

Phơi bày dữ liệu dưới dạng tài nguyên để truy cập hiệu quả dựa trên khuôn mẫu (template-based):

```python
@mcp.resource("file://documents/{name}")
async def get_document(name: str) -> str:
    '''Expose documents as MCP resources.

    Resources are useful for static or semi-static data that doesn't
    require complex parameters. They use URI templates for flexible access.
    '''
    document_path = f"./docs/{name}"
    with open(document_path, "r") as f:
        return f.read()

@mcp.resource("config://settings/{key}")
async def get_setting(key: str, ctx: Context) -> str:
    '''Expose configuration as resources with context.'''
    settings = await load_settings()
    return json.dumps(settings.get(key, {}))
```

**Khi nào nên dùng Resources so với Tools:**
- **Resources**: Dành cho việc truy cập dữ liệu với các tham số đơn giản (khuôn mẫu URI)
- **Tools**: Dành cho các thao tác phức tạp đòi hỏi xác thực dữ liệu và xử lý logic nghiệp vụ

### Kiểu Đầu Ra Có Cấu Trúc (Structured Output Types)

FastMCP hỗ trợ nhiều kiểu dữ liệu trả về ngoài chuỗi ký tự (strings) thông thường:

```python
from typing import TypedDict
from dataclasses import dataclass
from pydantic import BaseModel

# TypedDict for structured returns
class UserData(TypedDict):
    id: str
    name: str
    email: str

@mcp.tool()
async def get_user_typed(user_id: str) -> UserData:
    '''Returns structured data - FastMCP handles serialization.'''
    return {"id": user_id, "name": "John Doe", "email": "john@example.com"}

# Pydantic models for complex validation
class DetailedUser(BaseModel):
    id: str
    name: str
    email: str
    created_at: datetime
    metadata: Dict[str, Any]

@mcp.tool()
async def get_user_detailed(user_id: str) -> DetailedUser:
    '''Returns Pydantic model - automatically generates schema.'''
    user = await fetch_user(user_id)
    return DetailedUser(**user)
```

### Quản Lý Vòng Đời (Lifespan Management)

Khởi tạo các tài nguyên duy trì xuyên suốt qua nhiều yêu cầu (requests):

```python
from contextlib import asynccontextmanager

@asynccontextmanager
async def app_lifespan():
    '''Manage resources that live for the server's lifetime.'''
    # Initialize connections, load config, etc.
    db = await connect_to_database()
    config = load_configuration()

    # Make available to all tools
    yield {"db": db, "config": config}

    # Cleanup on shutdown
    await db.close()

mcp = FastMCP("example_mcp", lifespan=app_lifespan)

@mcp.tool()
async def query_data(query: str, ctx: Context) -> str:
    '''Access lifespan resources through context.'''
    db = ctx.request_context.lifespan_state["db"]
    results = await db.query(query)
    return format_results(results)
```

### Các Tùy Chọn Truyền Tải (Transport Options)

FastMCP hỗ trợ hai cơ chế truyền tải chính:

```python
# stdio transport (for local tools) - default
if __name__ == "__main__":
    mcp.run()

# Streamable HTTP transport (for remote servers)
if __name__ == "__main__":
    mcp.run(transport="streamable_http", port=8000)
```

**Lựa chọn phương thức truyền tải:**
- **stdio**: Công cụ dòng lệnh, tích hợp cục bộ, thực thi dưới dạng tiến trình con (subprocess)
- **Streamable HTTP**: Dịch vụ web, truy cập từ xa, phục vụ nhiều ứng dụng khách (clients) cùng lúc

---

## Các Thông Lệ Tốt Nhất Về Mã Nguồn

### Khả Năng Kết Hợp Và Tái Sử Dụng Mã Nguồn (Code Composability and Reusability)

Bản triển khai của bạn BẮT BUỘC phải ưu tiên tính kết hợp và khả năng tái sử dụng mã nguồn:

1. **Tách Các Chức Năng Chung**:
   - Tạo các hàm bổ trợ (helper functions) tái sử dụng được cho những thao tác xuất hiện ở nhiều công cụ
   - Xây dựng các API client dùng chung cho các yêu cầu HTTP thay vì viết lặp lại mã nguồn
   - Tập trung hóa logic xử lý lỗi vào các hàm tiện ích
   - Tách logic nghiệp vụ vào các hàm chuyên biệt có thể kết hợp lại với nhau
   - Tách riêng chức năng chọn lọc trường dữ liệu & định dạng kết quả Markdown hoặc JSON dùng chung

2. **Tránh Trùng Lặp Mã Nguồn**:
   - KHÔNG BAO GIỜ sao chép-dán (copy-paste) các đoạn mã tương tự nhau giữa các công cụ
   - Nếu bạn thấy mình đang viết một logic tương tự đến lần thứ hai, hãy tách nó thành một hàm dùng chung
   - Các thao tác phổ biến như phân trang, lọc dữ liệu, chọn trường và định dạng kết quả cần được chia sẻ chung
   - Logic xác thực và phân quyền cần được tập trung hóa ở một nơi

### Các Thông Lệ Tốt Nhất Dành Riêng Cho Python

1. **Sử dụng Type Hints**: Luôn khai báo chú thích kiểu dữ liệu cho tham số hàm và giá trị trả về
2. **Mô hình Pydantic**: Định nghĩa các mô hình Pydantic rõ ràng cho mọi bước xác thực đầu vào
3. **Tránh Xác thực Thủ công**: Hãy để Pydantic xử lý việc xác thực đầu vào kèm theo các ràng buộc (constraints)
4. **Sắp xếp Lệnh Import Chuẩn xác**: Gom nhóm các lệnh import theo thứ tự (thư viện chuẩn, thư viện bên thứ ba, thư viện nội bộ)
5. **Xử lý Lỗi Cụ thể**: Sử dụng các kiểu ngoại lệ cụ thể (`httpx.HTTPStatusError`, không bắt `Exception` chung chung)
6. **Trình Quản lý Ngữ cảnh Bất đồng bộ (Async Context Managers)**: Sử dụng `async with` cho các tài nguyên cần giải phóng sau khi dùng
7. **Hằng số (Constants)**: Định nghĩa các hằng số cấp mô-đun bằng chữ IN HOA (`UPPER_CASE`)

## Danh Sách Kiểm Tra Chất Lượng (Quality Checklist)

Trước khi hoàn tất việc triển khai máy chủ MCP bằng Python, hãy đảm bảo:

### Thiết Kế Chiến Lược (Strategic Design)
- [ ] Các công cụ hỗ trợ thực hiện các luồng công việc hoàn chỉnh, chứ không chỉ là lớp vỏ bọc mỏng quanh các điểm cuối API
- [ ] Tên công cụ phản ánh sự phân chia tác vụ một cách tự nhiên
- [ ] Định dạng phản hồi được tối ưu hóa để tiết kiệm ngữ cảnh (context efficiency) cho tác tử
- [ ] Sử dụng các mã định danh dễ đọc cho con người ở những chỗ phù hợp
- [ ] Thông báo lỗi hướng dẫn tác tử đi tới cách sử dụng đúng

### Chất Lượng Triển Khai (Implementation Quality)
- [ ] TRIỂN KHAI CÓ TRỌNG TÂM: Đã triển khai các công cụ quan trọng và mang lại nhiều giá trị nhất
- [ ] Tất cả công cụ đều có tên gọi giàu tính mô tả và tài liệu hướng dẫn đầy đủ
- [ ] Kiểu dữ liệu trả về nhất quán giữa các thao tác tương tự nhau
- [ ] Đã triển khai xử lý lỗi cho toàn bộ các lệnh gọi ra dịch vụ bên ngoài
- [ ] Tên máy chủ tuân thủ đúng định dạng: `{service}_mcp`
- [ ] Mọi thao tác mạng đều sử dụng `async/await`
- [ ] Các chức năng chung được tách thành các hàm tái sử dụng
- [ ] Các thông báo lỗi rõ ràng, có tính hành động và mang tính hướng dẫn cao
- [ ] Đầu ra được xác thực và định dạng chuẩn xác

### Cấu Hình Công Cụ (Tool Configuration)
- [ ] Tất cả công cụ đều khai báo `'name'` và `'annotations'` trong decorator
- [ ] Các chú thích (`annotations`) được thiết lập chính xác (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`)
- [ ] Tất cả công cụ đều sử dụng `BaseModel` của Pydantic để xác thực đầu vào kèm theo định nghĩa `Field()`
- [ ] Tất cả các trường `Field` của Pydantic đều có kiểu dữ liệu tường minh và mô tả kèm ràng buộc
- [ ] Tất cả công cụ đều có docstring toàn diện với kiểu đầu vào/đầu ra tường minh
- [ ] Docstring bao gồm cấu trúc lược đồ hoàn chỉnh cho các giá trị trả về dạng dict/JSON
- [ ] Các mô hình Pydantic đảm nhiệm việc xác thực đầu vào (không cần viết mã kiểm tra thủ công)

### Các Tính Năng Nâng Cao (nếu áp dụng)
- [ ] Sử dụng tiêm ngữ cảnh (Context injection) cho việc ghi log, báo cáo tiến độ hoặc hỏi thêm thông tin người dùng
- [ ] Đã đăng ký Resources cho các điểm cuối dữ liệu phù hợp
- [ ] Đã triển khai quản lý vòng đời (Lifespan management) cho các kết nối duy trì liên tục
- [ ] Sử dụng các kiểu đầu ra có cấu trúc (`TypedDict`, mô hình Pydantic)
- [ ] Đã cấu hình cơ chế truyền tải phù hợp (`stdio` hoặc `streamable HTTP`)

### Chất Lượng Mã Nguồn (Code Quality)
- [ ] Tệp mã nguồn bao gồm đầy đủ các lệnh import hợp lệ, trong đó có các import từ Pydantic
- [ ] Cơ chế phân trang được triển khai đúng cách ở những nơi cần thiết
- [ ] Cung cấp các tùy chọn lọc (filtering) cho các tập kết quả có khả năng phình to
- [ ] Tất cả các hàm bất đồng bộ đều được định nghĩa đúng bằng `async def`
- [ ] Việc sử dụng HTTP client tuân theo mẫu bất đồng bộ với trình quản lý ngữ cảnh (`async with`) chuẩn xác
- [ ] Gợi ý kiểu dữ liệu (Type hints) được sử dụng xuyên suốt toàn bộ mã nguồn
- [ ] Các hằng số được định nghĩa ở cấp mô-đun bằng chữ IN HOA (`UPPER_CASE`)

### Kiểm Thử (Testing)
- [ ] Máy chủ chạy thành công với lệnh: `python your_server.py --help`
- [ ] Tất cả các lệnh import đều phân giải đường dẫn chính xác
- [ ] Các lệnh gọi công cụ mẫu hoạt động đúng như kỳ vọng
- [ ] Các kịch bản lỗi được xử lý êm thấm, không làm sập máy chủ
FILE:scripts/connections.py
"""Lightweight connection handling for MCP servers."""

from abc import ABC, abstractmethod
from contextlib import AsyncExitStack
from typing import Any

from mcp import ClientSession, StdioServerParameters
from mcp.client.sse import sse_client
from mcp.client.stdio import stdio_client
from mcp.client.streamable_http import streamablehttp_client


class MCPConnection(ABC):
    """Base class for MCP server connections."""

    def __init__(self):
        self.session = None
        self._stack = None

    @abstractmethod
    def _create_context(self):
        """Create the connection context based on connection type."""

    async def __aenter__(self):
        """Initialize MCP server connection."""
        self._stack = AsyncExitStack()
        await self._stack.__aenter__()

        try:
            ctx = self._create_context()
            result = await self._stack.enter_async_context(ctx)

            if len(result) == 2:
                read, write = result
            elif len(result) == 3:
                read, write, _ = result
            else:
                raise ValueError(f"Unexpected context result: {result}")

            session_ctx = ClientSession(read, write)
            self.session = await self._stack.enter_async_context(session_ctx)
            await self.session.initialize()
            return self
        except BaseException:
            await self._stack.__aexit__(None, None, None)
            raise

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """Clean up MCP server connection resources."""
        if self._stack:
            await self._stack.__aexit__(exc_type, exc_val, exc_tb)
        self.session = None
        self._stack = None

    async def list_tools(self) -> list[dict[str, Any]]:
        """Retrieve available tools from the MCP server."""
        response = await self.session.list_tools()
        return [
            {
                "name": tool.name,
                "description": tool.description,
                "input_schema": tool.inputSchema,
            }
            for tool in response.tools
        ]

    async def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> Any:
        """Call a tool on the MCP server with provided arguments."""
        result = await self.session.call_tool(tool_name, arguments=arguments)
        return result.content


class MCPConnectionStdio(MCPConnection):
    """MCP connection using standard input/output."""

    def __init__(self, command: str, args: list[str] = None, env: dict[str, str] = None):
        super().__init__()
        self.command = command
        self.args = args or []
        self.env = env

    def _create_context(self):
        return stdio_client(
            StdioServerParameters(command=self.command, args=self.args, env=self.env)
        )


class MCPConnectionSSE(MCPConnection):
    """MCP connection using Server-Sent Events."""

    def __init__(self, url: str, headers: dict[str, str] = None):
        super().__init__()
        self.url = url
        self.headers = headers or {}

    def _create_context(self):
        return sse_client(url=self.url, headers=self.headers)


class MCPConnectionHTTP(MCPConnection):
    """MCP connection using Streamable HTTP."""

    def __init__(self, url: str, headers: dict[str, str] = None):
        super().__init__()
        self.url = url
        self.headers = headers or {}

    def _create_context(self):
        return streamablehttp_client(url=self.url, headers=self.headers)


def create_connection(
    transport: str,
    command: str = None,
    args: list[str] = None,
    env: dict[str, str] = None,
    url: str = None,
    headers: dict[str, str] = None,
) -> MCPConnection:
    """Factory function to create the appropriate MCP connection.

    Args:
        transport: Connection type ("stdio", "sse", or "http")
        command: Command to run (stdio only)
        args: Command arguments (stdio only)
        env: Environment variables (stdio only)
        url: Server URL (sse and http only)
        headers: HTTP headers (sse and http only)

    Returns:
        MCPConnection instance
    """
    transport = transport.lower()

    if transport == "stdio":
        if not command:
            raise ValueError("Command is required for stdio transport")
        return MCPConnectionStdio(command=command, args=args, env=env)

    elif transport == "sse":
        if not url:
            raise ValueError("URL is required for sse transport")
        return MCPConnectionSSE(url=url, headers=headers)

    elif transport in ["http", "streamable_http", "streamable-http"]:
        if not url:
            raise ValueError("URL is required for http transport")
        return MCPConnectionHTTP(url=url, headers=headers)

    else:
        raise ValueError(f"Unsupported transport type: {transport}. Use 'stdio', 'sse', or 'http'")
FILE:scripts/evaluation.py
"""MCP Server Evaluation Harness

This script evaluates MCP servers by running test questions against them using Claude.
"""

import argparse
import asyncio
import json
import re
import sys
import time
import traceback
import xml.etree.ElementTree as ET
from pathlib import Path
from typing import Any

from anthropic import Anthropic

from connections import create_connection

EVALUATION_PROMPT = """You are an AI assistant with access to tools.

When given a task, you MUST:
1. Use the available tools to complete the task
2. Provide summary of each step in your approach, wrapped in <summary> tags
3. Provide feedback on the tools provided, wrapped in <feedback> tags
4. Provide your final response, wrapped in <response> tags

Summary Requirements:
- In your <summary> tags, you must explain:
  - The steps you took to complete the task
  - Which tools you used, in what order, and why
  - The inputs you provided to each tool
  - The outputs you received from each tool
  - A summary for how you arrived at the response

Feedback Requirements:
- In your <feedback> tags, provide constructive feedback on the tools:
  - Comment on tool names: Are they clear and descriptive?
  - Comment on input parameters: Are they well-documented? Are required vs optional parameters clear?
  - Comment on descriptions: Do they accurately describe what the tool does?
  - Comment on any errors encountered during tool usage: Did the tool fail to execute? Did the tool return too many tokens?
  - Identify specific areas for improvement and explain WHY they would help
  - Be specific and actionable in your suggestions

Response Requirements:
- Your response should be concise and directly address what was asked
- Always wrap your final response in <response> tags
- If you cannot solve the task return <response>NOT_FOUND</response>
- For numeric responses, provide just the number
- For IDs, provide just the ID
- For names or text, provide the exact text requested
- Your response should go last"""


def parse_evaluation_file(file_path: Path) -> list[dict[str, Any]]:
    """Parse XML evaluation file with qa_pair elements."""
    try:
        tree = ET.parse(file_path)
        root = tree.getroot()
        evaluations = []

        for qa_pair in root.findall(".//qa_pair"):
            question_elem = qa_pair.find("question")
            answer_elem = qa_pair.find("answer")

            if question_elem is not None and answer_elem is not None:
                evaluations.append({
                    "question": (question_elem.text or "").strip(),
                    "answer": (answer_elem.text or "").strip(),
                })

        return evaluations
    except Exception as e:
        print(f"Error parsing evaluation file {file_path}: {e}")
        return []


def extract_xml_content(text: str, tag: str) -> str | None:
    """Extract content from XML tags."""
    pattern = rf"<{tag}>(.*?)</{tag}>"
    matches = re.findall(pattern, text, re.DOTALL)
    return matches[-1].strip() if matches else None


async def agent_loop(
    client: Anthropic,
    model: str,
    question: str,
    tools: list[dict[str, Any]],
    connection: Any,
) -> tuple[str, dict[str, Any]]:
    """Run the agent loop with MCP tools."""
    messages = [{"role": "user", "content": question}]

    response = await asyncio.to_thread(
        client.messages.create,
        model=model,
        max_tokens=4096,
        system=EVALUATION_PROMPT,
        messages=messages,
        tools=tools,
    )

    messages.append({"role": "assistant", "content": response.content})

    tool_metrics = {}

    while response.stop_reason == "tool_use":
        tool_use = next(block for block in response.content if block.type == "tool_use")
        tool_name = tool_use.name
        tool_input = tool_use.input

        tool_start_ts = time.time()
        try:
            tool_result = await connection.call_tool(tool_name, tool_input)
            tool_response = json.dumps(tool_result) if isinstance(tool_result, (dict, list)) else str(tool_result)
        except Exception as e:
            tool_response = f"Error executing tool {tool_name}: {str(e)}\n"
            tool_response += traceback.format_exc()
        tool_duration = time.time() - tool_start_ts

        if tool_name not in tool_metrics:
            tool_metrics[tool_name] = {"count": 0, "durations": []}
        tool_metrics[tool_name]["count"] += 1
        tool_metrics[tool_name]["durations"].append(tool_duration)

        messages.append({
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use.id,
                "content": tool_response,
            }]
        })

        response = await asyncio.to_thread(
            client.messages.create,
            model=model,
            max_tokens=4096,
            system=EVALUATION_PROMPT,
            messages=messages,
            tools=tools,
        )
        messages.append({"role": "assistant", "content": response.content})

    response_text = next(
        (block.text for block in response.content if hasattr(block, "text")),
        None,
    )
    return response_text, tool_metrics


async def evaluate_single_task(
    client: Anthropic,
    model: str,
    qa_pair: dict[str, Any],
    tools: list[dict[str, Any]],
    connection: Any,
    task_index: int,
) -> dict[str, Any]:
    """Evaluate a single QA pair with the given tools."""
    start_time = time.time()

    print(f"Task {task_index + 1}: Running task with question: {qa_pair['question']}")
    response, tool_metrics = await agent_loop(client, model, qa_pair["question"], tools, connection)

    response_value = extract_xml_content(response, "response")
    summary = extract_xml_content(response, "summary")
    feedback = extract_xml_content(response, "feedback")

    duration_seconds = time.time() - start_time

    return {
        "question": qa_pair["question"],
        "expected": qa_pair["answer"],
        "actual": response_value,
        "score": int(response_value == qa_pair["answer"]) if response_value else 0,
        "total_duration": duration_seconds,
        "tool_calls": tool_metrics,
        "num_tool_calls": sum(len(metrics["durations"]) for metrics in tool_metrics.values()),
        "summary": summary,
        "feedback": feedback,
    }


REPORT_HEADER = """
# Evaluation Report

## Summary

- **Accuracy**: {correct}/{total} ({accuracy:.1f}%)
- **Average Task Duration**: {average_duration_s:.2f}s
- **Average Tool Calls per Task**: {average_tool_calls:.2f}
- **Total Tool Calls**: {total_tool_calls}

---
"""

TASK_TEMPLATE = """
### Task {task_num}

**Question**: {question}
**Ground Truth Answer**: `{expected_answer}`
**Actual Answer**: `{actual_answer}`
**Correct**: {correct_indicator}
**Duration**: {total_duration:.2f}s
**Tool Calls**: {tool_calls}

**Summary**
{summary}

**Feedback**
{feedback}

---
"""


async def run_evaluation(
    eval_path: Path,
    connection: Any,
    model: str = "claude-3-7-sonnet-20250219",
) -> str:
    """Run evaluation with MCP server tools."""
    print("🚀 Starting Evaluation")

    client = Anthropic()

    tools = await connection.list_tools()
    print(f"📋 Loaded {len(tools)} tools from MCP server")

    qa_pairs = parse_evaluation_file(eval_path)
    print(f"📋 Loaded {len(qa_pairs)} evaluation tasks")

    results = []
    for i, qa_pair in enumerate(qa_pairs):
        print(f"Processing task {i + 1}/{len(qa_pairs)}")
        result = await evaluate_single_task(client, model, qa_pair, tools, connection, i)
        results.append(result)

    correct = sum(r["score"] for r in results)
    accuracy = (correct / len(results)) * 100 if results else 0
    average_duration_s = sum(r["total_duration"] for r in results) / len(results) if results else 0
    average_tool_calls = sum(r["num_tool_calls"] for r in results) / len(results) if results else 0
    total_tool_calls = sum(r["num_tool_calls"] for r in results)

    report = REPORT_HEADER.format(
        correct=correct,
        total=len(results),
        accuracy=accuracy,
        average_duration_s=average_duration_s,
        average_tool_calls=average_tool_calls,
        total_tool_calls=total_tool_calls,
    )

    report += "".join([
        TASK_TEMPLATE.format(
            task_num=i + 1,
            question=qa_pair["question"],
            expected_answer=qa_pair["answer"],
            actual_answer=result["actual"] or "N/A",
            correct_indicator="✅" if result["score"] else "❌",
            total_duration=result["total_duration"],
            tool_calls=json.dumps(result["tool_calls"], indent=2),
            summary=result["summary"] or "N/A",
            feedback=result["feedback"] or "N/A",
        )
        for i, (qa_pair, result) in enumerate(zip(qa_pairs, results))
    ])

    return report


def parse_headers(header_list: list[str]) -> dict[str, str]:
    """Parse header strings in format 'Key: Value' into a dictionary."""
    headers = {}
    if not header_list:
        return headers

    for header in header_list:
        if ":" in header:
            key, value = header.split(":", 1)
            headers[key.strip()] = value.strip()
        else:
            print(f"Warning: Ignoring malformed header: {header}")
    return headers


def parse_env_vars(env_list: list[str]) -> dict[str, str]:
    """Parse environment variable strings in format 'KEY=VALUE' into a dictionary."""
    env = {}
    if not env_list:
        return env

    for env_var in env_list:
        if "=" in env_var:
            key, value = env_var.split("=", 1)
            env[key.strip()] = value.strip()
        else:
            print(f"Warning: Ignoring malformed environment variable: {env_var}")
    return env


async def main():
    parser = argparse.ArgumentParser(
        description="Evaluate MCP servers using test questions",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Evaluate a local stdio MCP server
  python evaluation.py -t stdio -c python -a my_server.py eval.xml

  # Evaluate an SSE MCP server
  python evaluation.py -t sse -u https://example.com/mcp -H "Authorization: Bearer token" eval.xml

  # Evaluate an HTTP MCP server with custom model
  python evaluation.py -t http -u https://example.com/mcp -m claude-3-5-sonnet-20241022 eval.xml
        """,
    )

    parser.add_argument("eval_file", type=Path, help="Path to evaluation XML file")
    parser.add_argument("-t", "--transport", choices=["stdio", "sse", "http"], default="stdio", help="Transport type (default: stdio)")
    parser.add_argument("-m", "--model", default="claude-3-7-sonnet-20250219", help="Claude model to use (default: claude-3-7-sonnet-20250219)")

    stdio_group = parser.add_argument_group("stdio options")
    stdio_group.add_argument("-c", "--command", help="Command to run MCP server (stdio only)")
    stdio_group.add_argument("-a", "--args", nargs="+", help="Arguments for the command (stdio only)")
    stdio_group.add_argument("-e", "--env", nargs="+", help="Environment variables in KEY=VALUE format (stdio only)")

    remote_group = parser.add_argument_group("sse/http options")
    remote_group.add_argument("-u", "--url", help="MCP server URL (sse/http only)")
    remote_group.add_argument("-H", "--header", nargs="+", dest="headers", help="HTTP headers in 'Key: Value' format (sse/http only)")

    parser.add_argument("-o", "--output", type=Path, help="Output file for evaluation report (default: stdout)")

    args = parser.parse_args()

    if not args.eval_file.exists():
        print(f"Error: Evaluation file not found: {args.eval_file}")
        sys.exit(1)

    headers = parse_headers(args.headers) if args.headers else None
    env_vars = parse_env_vars(args.env) if args.env else None

    try:
        connection = create_connection(
            transport=args.transport,
            command=args.command,
            args=args.args,
            env=env_vars,
            url=args.url,
            headers=headers,
        )
    except ValueError as e:
        print(f"Error: {e}")
        sys.exit(1)

    print(f"🔗 Connecting to MCP server via {args.transport}...")

    async with connection:
        print("✅ Connected successfully")
        report = await run_evaluation(args.eval_file, connection, args.model)

        if args.output:
            args.output.write_text(report)
            print(f"\n✅ Report saved to {args.output}")
        else:
            print("\n" + report)


if __name__ == "__main__":
    asyncio.run(main())
FILE:scripts/example_evaluation.xml
<evaluation>
   <qa_pair>
      <question>Calculate the compound interest on $10,000 invested at 5% annual interest rate, compounded monthly for 3 years. What is the final amount in dollars (rounded to 2 decimal places)?</question>
      <answer>11614.72</answer>
   </qa_pair>
   <qa_pair>
      <question>A projectile is launched at a 45-degree angle with an initial velocity of 50 m/s. Calculate the total distance (in meters) it has traveled from the launch point after 2 seconds, assuming g=9.8 m/s². Round to 2 decimal places.</question>
      <answer>87.25</answer>
   </qa_pair>
   <qa_pair>
      <question>A sphere has a volume of 500 cubic meters. Calculate its surface area in square meters. Round to 2 decimal places.</question>
      <answer>304.65</answer>
   </qa_pair>
   <qa_pair>
      <question>Calculate the population standard deviation of this dataset: [12, 15, 18, 22, 25, 30, 35]. Round to 2 decimal places.</question>
      <answer>7.61</answer>
   </qa_pair>
   <qa_pair>
      <question>Calculate the pH of a solution with a hydrogen ion concentration of 3.5 × 10^-5 M. Round to 2 decimal places.</question>
      <answer>4.46</answer>
   </qa_pair>
</evaluation>
FILE:scripts/requirements.txt
anthropic>=0.39.0
mcp>=1.1.0
APIPythonTypeScript+2
F@f
0
Trình Tạo Kỹ Năng (Skill Creator)
Kỹ năng

Hướng dẫn tạo các kỹ năng (skills) hiệu quả. Kỹ năng này nên được sử dụng khi người dùng muốn tạo một kỹ năng mới (hoặc cập nhật một kỹ năng hiện có) giúp mở rộng khả năng của Claude bằng kiến thức chuyên ngành, quy trình làm việc hoặc tích hợp công cụ.

---
name: skill-creator
description: Hướng dẫn tạo các kỹ năng (skills) hiệu quả. Kỹ năng này nên được sử dụng khi người dùng muốn tạo một kỹ năng mới (hoặc cập nhật một kỹ năng hiện có) giúp mở rộng khả năng của Claude bằng kiến thức chuyên ngành, quy trình làm việc hoặc tích hợp công cụ.
license: Xem đầy đủ các điều khoản trong LICENSE.txt
---

# Trình Tạo Kỹ Năng (Skill Creator)

Kỹ năng này cung cấp hướng dẫn để tạo ra các kỹ năng (skills) hiệu quả.

## Giới Thiệu Về Kỹ Năng (Skills)

Kỹ năng (Skills) là các gói mô-đun độc lập, khép kín giúp mở rộng khả năng của Claude bằng cách cung cấp
kiến thức chuyên ngành, quy trình làm việc và các công cụ chuyên biệt. Hãy hình dung chúng như những "cẩm nang hội nhập" (onboarding guides) cho các lĩnh vực
hoặc tác vụ cụ thể—chúng biến đổi Claude từ một tác tử đa năng thành một tác tử chuyên gia
được trang bị tri thức quy trình mà không một mô hình đơn lẻ nào có thể tự nắm giữ trọn vẹn.

### Những Gì Kỹ Năng Cung Cấp

1. Quy trình làm việc chuyên biệt - Các thủ tục nhiều bước dành cho những lĩnh vực cụ thể
2. Tích hợp công cụ - Hướng dẫn làm việc với các định dạng tệp hoặc API cụ thể
3. Chuyên môn nghiệp vụ - Kiến thức đặc thù của công ty, lược đồ dữ liệu (schemas), logic nghiệp vụ
4. Tài nguyên đóng gói kèm - Các kịch bản (scripts), tài liệu tham khảo (references) và tài sản (assets) phục vụ các tác vụ phức tạp và lặp đi lặp lại

## Các Nguyên Tắc Cốt Lõi

### Ngắn Gọn Là Chìa Khóa

Cửa sổ ngữ cảnh (context window) là tài sản chung. Các kỹ năng phải chia sẻ cửa sổ ngữ cảnh với mọi thứ khác mà Claude cần: câu lệnh hệ thống (system prompt), lịch sử hội thoại, siêu dữ liệu của các Kỹ năng khác và yêu cầu thực tế của người dùng.

**Giả định mặc định: Bản thân Claude vốn đã rất thông minh.** Chỉ bổ sung những ngữ cảnh mà Claude chưa có sẵn. Hãy chất vấn từng mẩu thông tin: "Claude có thực sự cần lời giải thích này không?" và "Đoạn văn này có xứng đáng với chi phí token mà nó tiêu tốn không?"

Ưu tiên các ví dụ ngắn gọn hơn là những lời giải thích dài dòng.

### Thiết Lập Bậc Tự Do Phù Hợp

Điều chỉnh mức độ chi tiết tương ứng với độ nhạy cảm dễ lỗi và tính biến thiên của tác vụ:

**Bậc tự do cao (hướng dẫn dạng văn bản)**: Sử dụng khi có nhiều cách tiếp cận cùng hợp lệ, các quyết định phụ thuộc vào ngữ cảnh, hoặc các nguyên tắc kinh nghiệm (heuristics) đóng vai trò dẫn dắt.

**Bậc tự do trung bình (mã giả hoặc kịch bản có tham số)**: Sử dụng khi đã có một mẫu chuẩn được ưu tiên, chấp nhận một số biến thể nhất định, hoặc cấu hình có ảnh hưởng đến hành vi.

**Bậc tự do thấp (các kịch bản cụ thể, ít tham số)**: Sử dụng khi các thao tác rất nhạy cảm và dễ phát sinh lỗi, tính nhất quán là yếu tố sống còn, hoặc bắt buộc phải tuân thủ nghiêm ngặt một trình tự cụ thể.

Hãy hình dung Claude đang khám phá một con đường: một cây cầu hẹp bắc qua vực thẳm cần những thanh lan can bảo vệ cụ thể (bậc tự do thấp), trong khi một cánh đồng rộng mở cho phép đi theo nhiều lối khác nhau (bậc tự do cao).

### Cấu Trúc Giải Phẫu Của Một Kỹ Năng

Mỗi kỹ năng bao gồm một tệp SKILL.md bắt buộc và các tài nguyên đóng gói kèm tùy chọn:

```
skill-name/
├── SKILL.md (bắt buộc)
│   ├── Siêu dữ liệu YAML frontmatter (bắt buộc)
│   │   ├── name: (bắt buộc)
│   │   └── description: (bắt buộc)
│   └── Hướng dẫn Markdown (bắt buộc)
└── Tài Nguyên Đóng Gói Kèm (tùy chọn)
    ├── scripts/          - Mã thực thi (Python/Bash/v.v.)
    ├── references/       - Tài liệu được nạp vào ngữ cảnh khi cần thiết
    └── assets/           - Các tệp được sử dụng trong kết quả đầu ra (mẫu, biểu tượng, phông chữ, v.v.)
```

#### SKILL.md (bắt buộc)

Mọi tệp SKILL.md đều bao gồm:

- **Frontmatter** (YAML): Chứa hai trường `name` và `description`. Đây là những trường duy nhất mà Claude đọc để xác định khi nào kỹ năng được kích hoạt sử dụng, do đó việc mô tả rõ ràng và toàn diện kỹ năng này là gì cũng như khi nào nên dùng nó là cực kỳ quan trọng.
- **Phần thân - Body** (Markdown): Các hướng dẫn và chỉ dẫn để sử dụng kỹ năng. Chỉ được nạp vào SAU KHI kỹ năng đã được kích hoạt (nếu có).

#### Tài Nguyên Đóng Gói Kèm (tùy chọn)

##### Kịch bản (`scripts/`)

Mã thực thi (Python/Bash/v.v.) dành cho các tác vụ đòi hỏi độ tin cậy tất định hoặc thường xuyên phải viết đi viết lại.

- **Khi nào nên đưa vào**: Khi cùng một đoạn mã cứ phải viết lại nhiều lần hoặc khi cần độ tin cậy tất định tuyệt đối
- **Ví dụ**: `scripts/rotate_pdf.py` cho các tác vụ xoay trang PDF
- **Lợi ích**: Tiết kiệm token, kết quả tất định, có thể được thực thi mà không cần nạp mã nguồn vào cửa sổ ngữ cảnh
- **Lưu ý**: Đôi khi Claude vẫn cần đọc các kịch bản này để vá lỗi hoặc điều chỉnh cho phù hợp với môi trường cụ thể

##### Tài liệu tham khảo (`references/`)

Tài liệu hướng dẫn và tư liệu tham khảo được thiết kế để nạp vào ngữ cảnh khi cần nhằm cung cấp thông tin cho quá trình xử lý và suy luận của Claude.

- **Khi nào nên đưa vào**: Dành cho các tài liệu mà Claude cần tra cứu trong quá trình làm việc
- **Ví dụ**: `references/finance.md` cho các lược đồ tài chính, `references/mnda.md` cho mẫu thỏa thuận bảo mật (NDA) của công ty, `references/policies.md` cho các chính sách công ty, `references/api_docs.md` cho đặc tả API
- **Trường hợp sử dụng**: Lược đồ cơ sở dữ liệu, tài liệu API, kiến thức chuyên ngành, chính sách công ty, hướng dẫn quy trình chi tiết
- **Lợi ích**: Giữ cho tệp SKILL.md luôn gọn nhẹ, chỉ nạp vào khi Claude xác định là thực sự cần dùng đến
- **Thực hành tốt nhất**: Nếu tệp có dung lượng lớn (>10k từ), hãy đưa các mẫu tìm kiếm grep vào trong SKILL.md
- **Tránh trùng lặp**: Thông tin chỉ nên nằm ở SKILL.md hoặc nằm ở các tệp trong thư mục references, không lặp lại ở cả hai nơi.

##### Tài sản (`assets/`)

Các tệp không nhằm mục đích nạp vào cửa sổ ngữ cảnh, mà được sử dụng trực tiếp bên trong sản phẩm đầu ra do Claude tạo ra.

- **Khi nào nên đưa vào**: Khi kỹ năng cần các tệp sẽ được đưa vào sản phẩm đầu ra cuối cùng
- **Ví dụ**: `assets/logo.png` cho tài sản nhận diện thương hiệu, `assets/slides.pptx` cho các mẫu bài thuyết trình PowerPoint
- **Trường hợp sử dụng**: Biểu mẫu (templates), hình ảnh, biểu tượng (icons), mã khung dựng sẵn (boilerplate code), phông chữ, tài liệu mẫu

### Nguyên Tắc Thiết Kế Tiết Lộ Dần Dần (Progressive Disclosure)

Các kỹ năng sử dụng hệ thống nạp ba cấp độ để quản lý ngữ cảnh một cách hiệu quả:

1. **Siêu dữ liệu (name + description)** - Luôn thường trực trong ngữ cảnh (~100 từ)
2. **Phần thân SKILL.md** - Được nạp khi kỹ năng được kích hoạt (<5k từ)
3. **Tài nguyên đóng gói kèm** - Được nạp theo nhu cầu thực tế của Claude

Hãy giữ phần thân SKILL.md tập trung vào những nội dung cốt lõi nhất và dưới 500 dòng để giảm thiểu tình trạng phình đại ngữ cảnh.

## Quy Trình Tạo Kỹ Năng

Việc tạo kỹ năng bao gồm các bước sau:

1. Hiểu rõ kỹ năng thông qua các ví dụ cụ thể
2. Lên kế hoạch cho các nội dung kỹ năng có thể tái sử dụng (scripts, references, assets)
3. Khởi tạo kỹ năng (chạy kịch bản init_skill.py)
4. Chỉnh sửa kỹ năng (triển khai các tài nguyên và viết nội dung SKILL.md)
5. Đóng gói kỹ năng (chạy kịch bản package_skill.py)
6. Lặp lại và cải tiến dựa trên quá trình sử dụng thực tế

### Bước 3: Khởi Tạo Kỹ Năng

Khi tạo một kỹ năng mới từ đầu, hãy luôn chạy kịch bản `init_skill.py`:

```bash
scripts/init_skill.py <skill-name> --path <output-directory>
```

### Bước 4: Chỉnh Sửa Kỹ Năng

Tham khảo các tài liệu hướng dẫn hữu ích sau dựa trên nhu cầu cụ thể của kỹ năng bạn đang tạo:

- **Các quy trình nhiều bước**: Xem references/workflows.md để biết các quy trình tuần tự và logic rẽ nhánh có điều kiện
- **Các định dạng đầu ra cụ thể hoặc tiêu chuẩn chất lượng**: Xem references/output-patterns.md để biết các mẫu biểu mẫu và mẫu ví dụ

### Bước 5: Đóng Gói Kỹ Năng

```bash
scripts/package_skill.py <path/to/skill-folder>
```

Kịch bản đóng gói sẽ kiểm tra tính hợp lệ và tạo ra một tệp .skill để phân phối.
FILE:references/workflows.md
# Các Mẫu Quy Trình Làm Việc (Workflow Patterns)

## Quy Trình Tuần Tự (Sequential Workflows)

Đối với các tác vụ phức tạp, hãy chia nhỏ hoạt động thành các bước tuần tự rõ ràng. Việc cung cấp cho Claude cái nhìn tổng quan về quy trình ngay ở phần đầu của tệp SKILL.md thường mang lại hiệu quả rất tốt:

```markdown
Việc điền một biểu mẫu PDF bao gồm các bước sau:

1. Phân tích biểu mẫu (chạy analyze_form.py)
2. Tạo ánh xạ trường dữ liệu (chỉnh sửa fields.json)
3. Xác thực ánh xạ (chạy validate_fields.py)
4. Điền biểu mẫu (chạy fill_form.py)
5. Kiểm tra kết quả đầu ra (chạy verify_output.py)
```

## Quy Trình Có Điều Kiện (Conditional Workflows)

Đối với các tác vụ có logic rẽ nhánh, hãy dẫn dắt Claude đi qua các điểm ra quyết định:

```markdown
1. Xác định loại sửa đổi:
   **Tạo nội dung mới?** → Làm theo "Quy trình tạo mới" bên dưới
   **Chỉnh sửa nội dung hiện có?** → Làm theo "Quy trình chỉnh sửa" bên dưới

2. Quy trình tạo mới: [steps]
3. Quy trình chỉnh sửa: [steps]
```
FILE:references/output-patterns.md
# Các Mẫu Đầu Ra (Output Patterns)

Sử dụng các mẫu này khi kỹ năng cần tạo ra kết quả đầu ra nhất quán, chất lượng cao.

## Mẫu Khuôn Khổ Định Sẵn (Template Pattern)

Cung cấp các biểu mẫu (templates) cho định dạng đầu ra. Điều chỉnh mức độ nghiêm ngặt sao cho khớp với nhu cầu của bạn.

**Đối với các yêu cầu nghiêm ngặt (như phản hồi API hoặc các định dạng dữ liệu cố định):**

```markdown
## Cấu trúc báo cáo

LUÔN LUÔN sử dụng chính xác cấu trúc biểu mẫu này:

# [Analysis Title]

## Tóm tắt điều hành (Executive summary)
[One-paragraph overview of key findings]

## Các phát hiện chính (Key findings)
- Phát hiện 1 kèm dữ liệu chứng minh
- Phát hiện 2 kèm dữ liệu chứng minh
- Phát hiện 3 kèm dữ liệu chứng minh

## Khuyến nghị (Recommendations)
1. Khuyến nghị cụ thể có tính hành động
2. Khuyến nghị cụ thể có tính hành động
```

**Đối với hướng dẫn linh hoạt (khi sự thích ứng theo tình huống mang lại lợi ích):**

```markdown
## Cấu trúc báo cáo

Dưới đây là định dạng mặc định hợp lý, nhưng hãy sử dụng sự phán đoán tốt nhất của bạn:

# [Analysis Title]

## Tóm tắt điều hành
[Overview]

## Các phát hiện chính
[Adapt sections based on what you discover]

## Khuyến nghị
[Tailor to the specific context]

Điều chỉnh các mục khi cần thiết cho phù hợp với loại hình phân tích cụ thể.
```

## Mẫu Ví Dự Minh Họa (Examples Pattern)

Đối với các kỹ năng mà chất lượng đầu ra phụ thuộc vào việc nhìn thấy các ví dụ mẫu, hãy cung cấp các cặp đầu vào/đầu ra (input/output pairs):

```markdown
## Định dạng thông điệp commit (Commit message format)

Tạo các thông điệp commit tuân theo các ví dụ sau:

**Ví dụ 1:**
Đầu vào (Input): Đã thêm xác thực người dùng bằng JWT tokens
Đầu ra (Output):
```
feat(auth): implement JWT-based authentication

Add login endpoint and token validation middleware
```

**Ví dụ 2:**
Đầu vào (Input): Đã sửa lỗi ngày tháng hiển thị không chính xác trong các báo cáo
Đầu ra (Output):
```
fix(reports): correct date formatting in timezone conversion

Use UTC timestamps consistently across report generation
```

Hãy tuân theo phong cách này: type(scope): mô tả ngắn gọn, sau đó là phần giải thích chi tiết.
```

Các ví dụ giúp Claude hiểu rõ phong cách mong muốn và mức độ chi tiết cụ thể hơn nhiều so với việc chỉ dùng lời mô tả đơn thuần.
FILE:scripts/quick_validate.py
#!/usr/bin/env python3
"""
Quick validation script for skills - minimal version
"""

import sys
import os
import re
import yaml
from pathlib import Path

def validate_skill(skill_path):
    """Basic validation of a skill"""
    skill_path = Path(skill_path)

    # Check SKILL.md exists
    skill_md = skill_path / 'SKILL.md'
    if not skill_md.exists():
        return False, "SKILL.md not found"

    # Read and validate frontmatter
    content = skill_md.read_text()
    if not content.startswith('---'):
        return False, "No YAML frontmatter found"

    # Extract frontmatter
    match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
    if not match:
        return False, "Invalid frontmatter format"

    frontmatter_text = match.group(1)

    # Parse YAML frontmatter
    try:
        frontmatter = yaml.safe_load(frontmatter_text)
        if not isinstance(frontmatter, dict):
            return False, "Frontmatter must be a YAML dictionary"
    except yaml.YAMLError as e:
        return False, f"Invalid YAML in frontmatter: {e}"

    # Define allowed properties
    ALLOWED_PROPERTIES = {'name', 'description', 'license', 'allowed-tools', 'metadata'}

    # Check for unexpected properties (excluding nested keys under metadata)
    unexpected_keys = set(frontmatter.keys()) - ALLOWED_PROPERTIES
    if unexpected_keys:
        return False, (
            f"Unexpected key(s) in SKILL.md frontmatter: {', '.join(sorted(unexpected_keys))}. "
            f"Allowed properties are: {', '.join(sorted(ALLOWED_PROPERTIES))}"
        )

    # Check required fields
    if 'name' not in frontmatter:
        return False, "Missing 'name' in frontmatter"
    if 'description' not in frontmatter:
        return False, "Missing 'description' in frontmatter"

    # Extract name for validation
    name = frontmatter.get('name', '')
    if not isinstance(name, str):
        return False, f"Name must be a string, got {type(name).__name__}"
    name = name.strip()
    if name:
        # Check naming convention (hyphen-case: lowercase with hyphens)
        if not re.match(r'^[a-z0-9-]+$', name):
            return False, f"Name '{name}' should be hyphen-case (lowercase letters, digits, and hyphens only)"
        if name.startswith('-') or name.endswith('-') or '--' in name:
            return False, f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens"
        # Check name length (max 64 characters per spec)
        if len(name) > 64:
            return False, f"Name is too long ({len(name)} characters). Maximum is 64 characters."

    # Extract and validate description
    description = frontmatter.get('description', '')
    if not isinstance(description, str):
        return False, f"Description must be a string, got {type(description).__name__}"
    description = description.strip()
    if description:
        # Check for angle brackets
        if '<' in description or '>' in description:
            return False, "Description cannot contain angle brackets (< or >)"
        # Check description length (max 1024 characters per spec)
        if len(description) > 1024:
            return False, f"Description is too long ({len(description)} characters). Maximum is 1024 characters."

    return True, "Skill is valid!"

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python quick_validate.py <skill_directory>")
        sys.exit(1)
    
    valid, message = validate_skill(sys.argv[1])
    print(message)
    sys.exit(0 if valid else 1)
FILE:scripts/init_skill.py
#!/usr/bin/env python3
"""
Skill Initializer - Creates a new skill from template

Usage:
    init_skill.py <skill-name> --path <path>

Examples:
    init_skill.py my-new-skill --path skills/public
    init_skill.py my-api-helper --path skills/private
    init_skill.py custom-skill --path /custom/location
"""

import sys
from pathlib import Path


SKILL_TEMPLATE = """---
name: {skill_name}
description: [TODO: Complete and informative explanation of what the skill does and when to use it. Include WHEN to use this skill - specific scenarios, file types, or tasks that trigger it.]
---

# {skill_title}

## Overview

[TODO: 1-2 sentences explaining what this skill enables]

## Resources

This skill includes example resource directories that demonstrate how to organize different types of bundled resources:

### scripts/
Executable code (Python/Bash/etc.) that can be run directly to perform specific operations.

### references/
Documentation and reference material intended to be loaded into context to inform Claude's process and thinking.

### assets/
Files not intended to be loaded into context, but rather used within the output Claude produces.

---

**Any unneeded directories can be deleted.** Not every skill requires all three types of resources.
"""

EXAMPLE_SCRIPT = '''#!/usr/bin/env python3
"""
Example helper script for {skill_name}

This is a placeholder script that can be executed directly.
Replace with actual implementation or delete if not needed.
"""

def main():
    print("This is an example script for {skill_name}")
    # TODO: Add actual script logic here

if __name__ == "__main__":
    main()
'''

EXAMPLE_REFERENCE = """# Reference Documentation for {skill_title}

This is a placeholder for detailed reference documentation.
Replace with actual reference content or delete if not needed.
"""

EXAMPLE_ASSET = """# Example Asset File

This placeholder represents where asset files would be stored.
Replace with actual asset files (templates, images, fonts, etc.) or delete if not needed.
"""


def title_case_skill_name(skill_name):
    """Convert hyphenated skill name to Title Case for display."""
    return ' '.join(word.capitalize() for word in skill_name.split('-'))


def init_skill(skill_name, path):
    """Initialize a new skill directory with template SKILL.md."""
    skill_dir = Path(path).resolve() / skill_name

    if skill_dir.exists():
        print(f"❌ Error: Skill directory already exists: {skill_dir}")
        return None

    try:
        skill_dir.mkdir(parents=True, exist_ok=False)
        print(f"✅ Created skill directory: {skill_dir}")
    except Exception as e:
        print(f"❌ Error creating directory: {e}")
        return None

    skill_title = title_case_skill_name(skill_name)
    skill_content = SKILL_TEMPLATE.format(skill_name=skill_name, skill_title=skill_title)

    skill_md_path = skill_dir / 'SKILL.md'
    try:
        skill_md_path.write_text(skill_content)
        print("✅ Created SKILL.md")
    except Exception as e:
        print(f"❌ Error creating SKILL.md: {e}")
        return None

    try:
        scripts_dir = skill_dir / 'scripts'
        scripts_dir.mkdir(exist_ok=True)
        example_script = scripts_dir / 'example.py'
        example_script.write_text(EXAMPLE_SCRIPT.format(skill_name=skill_name))
        example_script.chmod(0o755)
        print("✅ Created scripts/example.py")

        references_dir = skill_dir / 'references'
        references_dir.mkdir(exist_ok=True)
        example_reference = references_dir / 'api_reference.md'
        example_reference.write_text(EXAMPLE_REFERENCE.format(skill_title=skill_title))
        print("✅ Created references/api_reference.md")

        assets_dir = skill_dir / 'assets'
        assets_dir.mkdir(exist_ok=True)
        example_asset = assets_dir / 'example_asset.txt'
        example_asset.write_text(EXAMPLE_ASSET)
        print("✅ Created assets/example_asset.txt")
    except Exception as e:
        print(f"❌ Error creating resource directories: {e}")
        return None

    print(f"\n✅ Skill '{skill_name}' initialized successfully at {skill_dir}")
    return skill_dir


def main():
    if len(sys.argv) < 4 or sys.argv[2] != '--path':
        print("Usage: init_skill.py <skill-name> --path <path>")
        sys.exit(1)

    skill_name = sys.argv[1]
    path = sys.argv[3]

    print(f"🚀 Initializing skill: {skill_name}")
    print(f"   Location: {path}")
    print()

    result = init_skill(skill_name, path)
    sys.exit(0 if result else 1)


if __name__ == "__main__":
    main()
FILE:scripts/package_skill.py
#!/usr/bin/env python3
"""
Skill Packager - Creates a distributable .skill file of a skill folder

Usage:
    python utils/package_skill.py <path/to/skill-folder> [output-directory]

Example:
    python utils/package_skill.py skills/public/my-skill
    python utils/package_skill.py skills/public/my-skill ./dist
"""

import sys
import zipfile
from pathlib import Path
from quick_validate import validate_skill


def package_skill(skill_path, output_dir=None):
    """Package a skill folder into a .skill file."""
    skill_path = Path(skill_path).resolve()

    if not skill_path.exists():
        print(f"❌ Error: Skill folder not found: {skill_path}")
        return None

    if not skill_path.is_dir():
        print(f"❌ Error: Path is not a directory: {skill_path}")
        return None

    skill_md = skill_path / "SKILL.md"
    if not skill_md.exists():
        print(f"❌ Error: SKILL.md not found in {skill_path}")
        return None

    print("🔍 Validating skill...")
    valid, message = validate_skill(skill_path)
    if not valid:
        print(f"❌ Validation failed: {message}")
        print("   Please fix the validation errors before packaging.")
        return None
    print(f"✅ {message}\n")

    skill_name = skill_path.name
    if output_dir:
        output_path = Path(output_dir).resolve()
        output_path.mkdir(parents=True, exist_ok=True)
    else:
        output_path = Path.cwd()

    skill_filename = output_path / f"{skill_name}.skill"

    try:
        with zipfile.ZipFile(skill_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for file_path in skill_path.rglob('*'):
                if file_path.is_file():
                    arcname = file_path.relative_to(skill_path.parent)
                    zipf.write(file_path, arcname)
                    print(f"  Added: {arcname}")

        print(f"\n✅ Successfully packaged skill to: {skill_filename}")
        return skill_filename

    except Exception as e:
        print(f"❌ Error creating .skill file: {e}")
        return None


def main():
    if len(sys.argv) < 2:
        print("Usage: python utils/package_skill.py <path/to/skill-folder> [output-directory]")
        sys.exit(1)

    skill_path = sys.argv[1]
    output_dir = sys.argv[2] if len(sys.argv) > 2 else None

    print(f"📦 Packaging skill: {skill_path}")
    if output_dir:
        print(f"   Output directory: {output_dir}")
    print()

    result = package_skill(skill_path, output_dir)
    sys.exit(0 if result else 1)


if __name__ == "__main__":
    main()
AgentSkillMeta+1
F@f
0
Khởi tạo Ý tưởng (Idea Generation)
Văn bản

Bắt đầu quy trình làm việc của bạn bằng việc động não và phát thảo các ý tưởng ban đầu

Bạn là một trợ lý động não (brainstorming) sáng tạo. Hãy giúp người dùng tạo ra những ý tưởng đột phá cho dự án của họ.

1. Đặt các câu hỏi làm rõ về chủ đề topic
2. Tạo ra từ 5–10 ý tưởng đa dạng
3. Đánh giá từng ý tưởng dựa trên tính khả thi và mức độ tác động
4. Đề xuất 3 ý tưởng hàng đầu đáng để theo đuổi nhất

Hãy sáng tạo, tư duy đột phá vượt khỏi khuôn mẫu thông thường và khuyến khích các hướng tiếp cận mới lạ.
F@f
0
Bước 2: Lập Đề cương (Outline Creation)
Văn bản

Tạo một đề cương có cấu trúc chặt chẽ từ các ý tưởng đã phát thảo

Dựa trên các ý tưởng đã được tạo ra ở bước trước, hãy xây dựng một bản đề cương chi tiết.

Cấu trúc bản đề cương của bạn bao gồm:
- Các phần chính và các tiểu mục con
- Những điểm cốt lõi cần trình bày trong mỗi mục
- Thời gian / công sức ước tính cho từng phần
- Mối quan hệ phụ thuộc giữa các phần với nhau

Định dạng bản đề cương theo cấu trúc phân cấp rõ ràng, mạch lạc.
F@f
0
Bước 3a: Phân tích Chuyên sâu Kỹ thuật (Technical Deep Dive)
Văn bản

Nhánh kỹ thuật: Phân tích các yêu cầu kỹ thuật và tính khả thi

Thực hiện phân tích chuyên sâu về mặt kỹ thuật đối với dự án đã được phác thảo.

Phân tích các khía cạnh:
- Các yêu cầu kỹ thuật và các thành phần phụ thuộc (dependencies)
- Các cân nhắc về kiến trúc hệ thống
- Những thách thức kỹ thuật tiềm ẩn có thể phát sinh
- Các công cụ và công nghệ cần thiết
- Các tác động và yêu cầu về mặt hiệu năng

Đưa ra bản đánh giá kỹ thuật chi tiết kèm theo các khuyến nghị cụ thể.
F@f
0
Bước 3b: Khám phá Sáng tạo (Creative Exploration)
Văn bản

Nhánh sáng tạo: Khám phá các định hướng sáng tạo và nghệ thuật kể chuyện

Khám phá các chiều kích sáng tạo của dự án đã được phác thảo.

Tập trung vào:
- Các yếu tố tự sự và nghệ thuật kể chuyện (storytelling)
- Các cân nhắc về mặt thị giác và thẩm mỹ
- Tác động cảm xúc và sự gắn kết của người dùng
- Những góc tiếp cận sáng tạo độc đáo, khác biệt
- Nguồn cảm hứng từ các tác phẩm tiêu biểu khác

Tạo ra các ý tưởng chủ đạo (creative concepts) giúp thổi hồn cho dự án trở nên sống động.
F@f
0
Bước 4a: Kế hoạch Triển khai (Implementation Plan)
Văn bản

Xây dựng kế hoạch triển khai chi tiết dựa trên kết quả phân tích kỹ thuật

Xây dựng một kế hoạch triển khai toàn diện và chi tiết.

Bao gồm các nội dung:
- Phân chia thành các giai đoạn kèm theo các cột mốc quan trọng (milestones)
- Danh sách công việc cần làm kèm mức độ ưu tiên
- Phân bổ nguồn lực thực hiện
- Các chiến lược giảm thiểu rủi ro
- Ước tính khung thời gian hoàn thành (timeline)
- Các chỉ số đo lường mức độ thành công

Định dạng thành một bản kế hoạch dự án rõ ràng, có thể bắt tay vào thực thi ngay.
F@f
0
Bước 4b: Phát triển Câu chuyện (Story Development)
Văn bản

Phát triển mạch tự sự và nội dung chi tiết dựa trên bước khám phá sáng tạo

Phát triển toàn bộ câu chuyện và nội dung hoàn chỉnh dựa trên kết quả của bước khám phá sáng tạo.

Các hạng mục cần phát triển:
- Cung bậc phát triển cốt truyện (narrative arc) hoàn chỉnh
- Mô tả chi tiết nhân vật hoặc các yếu tố thành phần
- Các phân cảnh hoặc khoảnh khắc then chốt
- Lời thoại hoặc nội dung câu chữ (copy)
- Mô tả hình ảnh trực quan
- Các nhịp cảm xúc chủ đạo (emotional beats)

Tạo ra nội dung hấp dẫn, giàu sức thuyết phục và lôi cuốn người xem.
F@f
0
Bước 5: Đánh giá Cuối cùng (Final Review)
Văn bản

Rà soát và hợp nhất tất cả các luồng công việc để kiểm tra chất lượng lần cuối

Thực hiện rà soát tổng thể lần cuối cùng, hợp nhất tất cả các luồng công việc lại với nhau.

Danh mục kiểm tra đánh giá:
- Đã xác nhận tính khả thi về mặt kỹ thuật
- Tầm nhìn sáng tạo đã thống nhất đồng bộ
- Đã đáp ứng đầy đủ mọi yêu cầu đề ra
- Đã đạt các tiêu chuẩn về chất lượng
- Đảm bảo tính nhất quán trên tất cả các yếu tố thành phần
- Sẵn sàng để đưa vào xuất bản

Cung cấp bản đánh giá tổng kết cuối cùng kèm theo bất kỳ khuyến nghị tinh chỉnh cuối nào nếu có.
F@f
0
Bước 6: Xuất bản (Publication)
Văn bản

Bước cuối cùng: Chuẩn bị và thực hiện xuất bản

Chuẩn bị sản phẩm hoàn chỉnh cuối cùng để đưa vào xuất bản.

Các bước cuối cùng:
- Định dạng chuẩn xác cho nền tảng mục tiêu
- Tạo các tài liệu đi kèm hỗ trợ
- Thiết lập kênh phân phối
- Chuẩn bị thông báo ra mắt
- Lên lịch xuất bản chính thức
- Theo dõi phản hồi đón nhận ban đầu

Chúc mừng bạn đã hoàn thành trọn vẹn quy trình làm việc!
F@f
0
Video
Video Quảng bá prompts.chat sử dụng Remotion

Prompt này hướng dẫn tạo một video quảng bá dài 30 giây cho prompts.chat bằng cách sử dụng Remotion. Nội dung phác thảo các tài nguyên bắt buộc, bảng màu chủ đề, kiểu phông chữ và cấu trúc phân cảnh để giới thiệu hiệu quả các tính năng cũng như cộng đồng của nền tảng. Prompt bao gồm các kỹ thuật hoạt ảnh (animation) và hiệu ứng chuyển cảnh nhằm đảm bảo trải nghiệm xem mượt mà, cuốn hút, nhấn mạnh phạm vi tiếp cận toàn cầu của nền tảng và sự đa dạng của các loại prompt hiện có.

Tạo một video quảng bá dài 30 giây cho prompts.chat                     
                                                                              
Tài nguyên Bắt buộc                                                               
                                                                              
- https://prompts.chat/logo.svg - Tệp SVG Logo   
- https://raw.githubusercontent.com/flekschas/simple-world-map/refs/heads/master/world-map.svg - Tệp SVG bản đồ thế giới dùng cho cảnh cộng đồng toàn cầu       
                                                                              
Chủ đề Màu sắc (Giao diện Sáng - Light)                                                           
                                                                              
- Nền chính (Background): #ffffff                                                         
- Nền phụ (Background Alt): #f8fafc                                                     
- Màu chủ đạo (Primary): #6366f1 (Chàm - Indigo)                                                   
- Màu chủ đạo nhạt (Primary Light): #818cf8                                                      
- Màu nhấn (Accent): #22c55e (Xanh lá - Green)                                                     
- Chữ chính (Text): #0f172a                                                               
- Chữ mờ (Text Muted): #64748b                                                         
                                                                              
Phông chữ                                                                          
                                                                              
- Inter (độ đậm: 400, 600, 700, 800)                                         
                                                                              
---                                                                           
Cấu trúc Phân cảnh (8 Cảnh)                                                    
                                                                              
Cảnh 1: Mở đầu (5s)                                                         
                                                                              
- Logo xuất hiện                           
- Logo đặt chính giữa, phóng to dần vào khung hình với hiệu ứng hoạt ảnh lò xo (spring animation)                              
- Sau hoạt ảnh logo: dòng chữ "prompts.chat" lộ diện từ trái sang phải ngay bên dưới logo bằng thuộc tính clip-path                                                                     
- Dòng khẩu hiệu (tagline) xuất hiện: "The Free Social Platform for AI Prompts"                  
                                                                              
Cảnh 2: Cộng đồng Toàn cầu (4s)                                                
                                                                              
- Bản đồ thế giới toàn màn hình (độ mờ 25% opacity) làm phông nền                           
- 16 chấm tròn hoạt động nhấp nháy lan tỏa (pulsing dots) tại các thành phố lớn (LA, NYC, Toronto, Sao Paulo, London, Paris, Berlin, Lagos, Moscow, Dubai, Mumbai, Beijing, Tokyo, Singapore, Sydney, Warsaw)                                                    
- Mỗi chấm có vòng xung nhịp bên ngoài, xung nhịp bên trong và chấm tâm phát sáng        
- Tiêu đề: "A global community of prompt creators"                              
- Hàng thống kê số liệu: 8k+ users, 3k+ daily visitors, 1k+ prompts, 300+ contributors, 10+ languages                                                                 
- Lớp phủ chuyển màu (gradient overlay) ở phía dưới cùng để đảm bảo chữ dễ đọc                             
                                                                              
Cảnh 3: Giải pháp (2.5s)                                                      
                                                                              
- Ba từ xuất hiện nối tiếp nhau với hoạt ảnh lò xo (spring animation): "Discover." "Share." "Collect."                                                                    
- Mỗi từ mang một màu sắc khác nhau (primary, accent, primary light)               
                                                                              
Cảnh 4: Được Xây dựng cho Tất cả Mọi người (4s)                                              
                                                                              
- 8 biểu tượng chân dung người dùng (persona icons) trôi nổi quanh rìa màn hình với hoạt ảnh lơ lửng theo sóng sin/cos                                                                     
- Các nhóm người dùng: Students, Teachers, Researchers, Developers, Artists, Writers, Marketers, Entrepreneurs                                                      
- Mỗi mục có khung chứa biểu tượng kích thước 130x130 với nền và viền màu tương ứng              
- Tiêu đề chính giữa: "Built for everyone"                                          
- Phụ đề: "One prompt away from your next breakthrough."                    
                                                                              
Cảnh 5: Các Loại Prompt (5s)                                                    
                                                                              
- Tiêu đề: "Prompts for every need"                                             
- Khung giao diện kiểu trình duyệt (1400x800) có cụm nút đèn giao thông macOS và thanh URL hiển thị "prompts.chat"                                                                
- Ảnh chụp màn hình dạng lưới xếp gạch (masonry skeleton) cuộn dọc mượt mà với hoạt ảnh gia tốc (cubic ease-in-out)      
- 7 nhãn hình viên thuốc (pill-shaped) trôi nổi quanh rìa kèm biểu tượng:                      
  - Text (tím purple), Image (hồng pink), Video (vàng hổ phách amber), Audio (xanh lá green), Workflows (tím violet), Skills (xanh mòng két teal), JSON (đỏ red)                                           
                                                                              
Cảnh 6: Tính năng Nổi bật (4s)                                                        
                                                                              
- 4 thẻ tính năng (feature cards) xuất hiện lần lượt với hoạt ảnh lò xo (spring animation):               
  - Prompt Library (biểu tượng cuốn sách) - "Thousands of prompts across all categories" 
  - Skills & Workflows (biểu tượng tia sét) - "Automate multi-step AI tasks"           
  - Community (biểu tượng nhóm người dùng) - "Share and discover from creators"               
  - Open Source (biểu tượng dấu cộng trong vòng tròn) - "Self-host with complete privacy"        
                                                                              
Cảnh 7: Bằng chứng Xã hội / Uy tín (4s)                                                    
                                                                              
- Bộ đếm số sao GitHub chạy hoạt ảnh tăng dần (0 → 143,000+)                                 
- Biểu tượng ngôi sao nằm cạnh con số                                                     
- Huy hiệu (Badge): "The First Prompt Library — Since December 2022" kèm biểu tượng chiếc cúp    
- Văn bản: "Endorsed by OpenAI co-founders • Used by Harvard, Columbia & more"   
                                                                              
Cảnh 8: Lời kêu gọi Hành động - CTA (3.5s)                                                           
                                                                              
- Hoạt ảnh nền phát sáng (dải chuyển màu tỏa tròn radial gradient co giãn nhẹ)                         
- Tiêu đề: "Start exploring today"                                              
- Nút bấm lớn có logo + chữ "prompts.chat" (nền chuyển màu gradient, hiệu ứng nhịp đập nhẹ)                                                                        
- Phụ đề: "Free & Open Source"                                              
                                                                              
---                                                                           
Hiệu ứng Chuyển cảnh (0.4s mỗi lần chuyển)                                                       
                                                                              
- Cảnh 1→2: Mờ dần (Fade)                                                             
- Cảnh 2→3: Trượt vào từ bên phải (Slide from right)                                                 
- Cảnh 3→4: Mờ dần (Fade)                                                             
- Cảnh 4→5: Mờ dần (Fade)                                                             
- Cảnh 5→6: Trượt vào từ bên phải (Slide from right)                                                 
- Cảnh 6→7: Trượt lên từ phía dưới (Slide from bottom)                                                
- Cảnh 7→8: Mờ dần (Fade)                                                             
                                                                              
Các Kỹ thuật Hoạt ảnh Được Sử dụng                                                     
                                                                              
- `spring()` cho các hoạt ảnh co giãn tỷ lệ có độ nảy                                        
- `interpolate()` cho độ mờ (opacity), vị trí (position) và vùng cắt (clip-path)                          
- `Easing.inOut(Easing.cubic)` cho chuyển động cuộn trang mượt mà                                
- `Math.sin()/Math.cos()` cho các hoạt ảnh trôi nổi lơ lửng                               
- Độ trễ lệch nhịp (Staggered delays) cho các phần tử xuất hiện nối tiếp nhau                         
                                                                              
Các Thành phần (Components) Chính                                                                
                                                                              
- Các component biểu tượng SVG tùy chỉnh cho toàn bộ icon (không dùng emoji)                        
- Component Logo với đường vẽ вектор chữ "P" của prompts.chat                                   
- Component `FeatureCard` có thể tái sử dụng                                              
- `TransitionSeries` để quản lý chuỗi chuyển tiếp giữa các cảnh                                       
F@f
0
Trình Tạo Hình Ảnh Nhãn Dán (Sticker)
Hình ảnh
Trình Tạo Hình Ảnh Nhãn Dán (Sticker)

Tạo hình ảnh nhãn dán (sticker) đầy màu sắc với nền trong suốt, văn bản và biểu tượng có thể tùy chỉnh, tương tự phong cách của Stickermule.

1{
2 "role": "Nhà thiết kế Hình ảnh",
3 "task": "Tạo một hình ảnh nhãn dán (sticker) chi tiết với nền trong suốt.",
...+27 dòng nữa
DesignCreative
F@f
0
Phân cảnh Điện ảnh Lãng mạn Siêu Chân thực Dưới Mưa
Hình ảnh
Phân cảnh Điện ảnh Lãng mạn Siêu Chân thực Dưới Mưa

Tạo một phân cảnh điện ảnh siêu chân thực khắc họa một cặp đôi lãng mạn đứng dưới chiếc ô màu vàng trong cơn mưa. Ghi lại sự gắn kết cảm xúc và bầu không khí bằng các yếu tố điện ảnh cao cấp, đồng thời đảm bảo giữ nguyên các đường nét khuôn mặt không thay đổi.

Khuôn mặt phải giữ nguyên giống hệt 100% so với ảnh tham chiếu với cơ chế khóa danh tính tuyệt đối (absolute identity lock): không thay đổi khuôn mặt, không làm đẹp nhân tạo, không chỉnh sửa đối xứng, không thay đổi độ tuổi, không làm mịn da, không làm biến đổi biểu cảm, giữ nguyên tỷ lệ khuôn mặt, đôi mắt, sống mũi, đôi môi, đường viền hàm và kết cấu da tự nhiên. Phân cảnh ban đêm đậm chất điện ảnh siêu chân thực (ultra-photorealistic) dưới cơn mưa, nơi một cặp đôi lãng mạn đứng sát bên nhau dưới chiếc ô màu vàng trong một khu vườn được chiếu sáng dịu nhẹ. Cơn mưa nặng hạt đang trút xuống, được chiếu sáng bởi những dây đèn đom đóm màu vàng ấm áp và ánh đèn đường tạo nên hiệu ứng bokeh lung linh huyền ảo ở hậu cảnh, cùng mặt đất ướt đẫm phản chiếu ánh sáng. Người đàn ông cầm ô và nhìn người phụ nữ bằng ánh mắt dịu dàng, tràn ngập yêu thương, trong khi người phụ nữ ngước nhìn anh với nụ cười ấm áp, mềm mại và lãng mạn. Họ không rời mắt khỏi nhau, hoàn toàn đắm chìm trong thế giới của đối phương, truyền tải sự gắn kết cảm xúc sâu sắc. Những chiếc áo khoác thanh lịch hơi ướt mưa, kết cấu vải chân thực, ánh sáng viền (rim light) tinh tế phác họa đường nét khuôn mặt họ, những hạt mưa và làn sương mờ hiện rõ, độ sâu trường ảnh nông, góc nhìn ống kính 50mm, hạt phim tự nhiên, chỉnh màu điện ảnh cao cấp. Chỉ có ánh sáng, bầu không khí và môi trường xung quanh được phép thay đổi — khuôn mặt và danh tính của nhân vật phải được giữ nguyên hoàn toàn và bảo toàn một cách hoàn hảo.
F@f
0
Video
Video Cảnh Mưa Lãng mạn

Tạo một phân cảnh video lãng mạn nơi hai người đứng dưới mưa, nhìn sâu vào mắt nhau. Ghi lại bầu không khí với âm thanh của những hạt mưa rơi và không gian êm dịu, lãng mạn.

Họ đang đứng dưới màn mưa, trao nhau ánh nhìn đầy lãng mạn. Những hạt mưa rơi xung quanh họ và tiếng mưa rơi dịu nhẹ lấp đầy cả bầu không khí.
F@f
0
Biến Đổi Chủ Thể Thành Hình Dáng Thú Nhồi Bông Đáng Yêu
Hình ảnh
Biến Đổi Chủ Thể Thành Hình Dáng Thú Nhồi Bông Đáng Yêu

Prompt này biến đổi bất kỳ chủ thể hoặc hình ảnh nào thành hình dáng thú nhồi bông dễ thương, nhấn mạnh vào kết cấu mềm mại và các hình khối bo tròn. Nó giữ lại các đặc điểm nhận dạng đặc trưng, đặc biệt là ở con người, đồng thời sử dụng chất liệu vải nỉ hoặc lông cừu (fleece) để tạo vẻ ngoài thú nhồi bông đáng yêu. Quá trình biến đổi bao gồm các hình khối đơn giản, chi tiết khuôn mặt được khâu thủ công tinh tế và bảng màu pastel hoặc trung tính ấm áp, tạo ra một món đồ chơi nhồi bông duyên dáng, đáng sưu tầm và muốn ôm vào lòng.

Hãy biến đổi chủ thể hoặc hình ảnh thành hình dáng thú nhồi bông (plush toy) dễ thương với kết cấu mềm mại và các đường nét bo tròn. Nếu hình ảnh có chứa con người, hãy giữ lại các đặc điểm nhận diện đặc trưng để chủ thể vẫn có thể được nhận ra dễ dàng. Nếu không phải con người, hãy biến đồ vật hoặc con vật đó thành một món đồ chơi nhồi bông đáng yêu sử dụng kết cấu vải nỉ (felt) hoặc vải lông cừu mềm (fleece). Tạo hình phải mang vẻ ngoài ấm áp của chất liệu nỉ hoặc bông mềm, hình khối đơn giản, cùng đôi mắt, cái miệng và các chi tiết khuôn mặt được chế tác nhẹ nhàng, khéo léo. Sử dụng bảng màu pastel hoặc trung tính ấm áp dễ thương, đổ bóng mượt mà và những đường khâu chỉ tinh tế để gợi lên cảm giác về một món thú nhồi bông làm thủ công (handmade). Hãy mang lại cho nó biểu cảm khuôn mặt thân thiện, dễ thương, phần đầu hơi to một chút, tay chân ngắn mũm mĩm và phom dáng mềm mại chỉ muốn ôm vào lòng. Hình ảnh cuối cùng phải mang lại cảm giác duyên dáng, đáng để sưu tầm và trông giống như một món thú nhồi bông thực thụ. Nó phải thật dễ thương, ấm áp và mời gọi những cái ôm, trong khi vẫn giữ được rõ ràng khả năng nhận diện của chủ thể gốc.
F@f
0
prompts.chat taste
Văn bản

Phong cách thiết kế và công nghệ (taste) của prompts.chat

# Taste

# github-actions
- Sử dụng `actions/checkout@v6` và `actions/setup-node@v6` (không dùng bản v4) trong các quy trình GitHub Actions. Độ tự tin: 0.65
- Sử dụng Node.js phiên bản 24 trong các quy trình GitHub Actions (không dùng bản 20). Độ tự tin: 0.65

# project
- Dự án này là **prompts.chat** — một nền tảng mạng xã hội full-stack dành cho các prompt AI (được phát triển lên từ kho lưu trữ GitHub "Awesome ChatGPT Prompts"). Độ tự tin: 0.95
- Trình quản lý gói là npm (không phải pnpm hay yarn). Độ tự tin: 0.95

# architecture
- Sử dụng Next.js App Router với React Server Components theo mặc định; chỉ thêm `"use client"` cho các thành phần (components) có tính tương tác. Độ tự tin: 0.95
- Sử dụng Prisma ORM với PostgreSQL cho toàn bộ các thao tác truy cập cơ sở dữ liệu thông qua đối tượng đơn bản (singleton) tại `src/lib/db.ts`. Độ tự tin: 0.95
- Sử dụng mẫu đăng ký plugin (plugin registry pattern) cho các tích hợp xác thực (auth), lưu trữ (storage) và trình tạo phương tiện truyền thông (media generator). Độ tự tin: 0.90
- Sử dụng `revalidateTag()` để vô hiệu hóa bộ nhớ đệm (cache invalidation) sau các thao tác thay đổi dữ liệu (mutations). Độ tự tin: 0.90

# typescript
- Sử dụng TypeScript 5 ở chế độ nghiêm ngặt (strict mode) xuyên suốt toàn bộ dự án. Độ tự tin: 0.95

# styling
- Sử dụng Tailwind CSS 4 + Radix UI + shadcn/ui cho tất cả các thành phần giao diện người dùng (UI components). Độ tự tin: 0.95
- Sử dụng hàm tiện ích `cn()` cho các tên lớp Tailwind có điều kiện hoặc được gộp chung. Độ tự tin: 0.90

# api
- Xác thực toàn bộ dữ liệu đầu vào của các tuyến API (API routes) bằng các lược đồ Zod (Zod schemas). Độ tự tin: 0.95
- Có 61 tuyến API nằm dưới thư mục `src/app/api/` cộng với máy chủ MCP tại `src/pages/api/mcp.ts`. Độ tự tin: 0.90

# i18n
- Sử dụng `useTranslations()` (phía client) và `getTranslations()` (phía server) từ thư viện next-intl cho toàn bộ các chuỗi văn bản hiển thị cho người dùng. Độ tự tin: 0.95
- Hỗ trợ 17 ngôn ngữ bản địa (locales) kèm khả năng hiển thị từ phải sang trái (RTL) cho tiếng Ả Rập, tiếng Do Thái và tiếng Ba Tư (Farsi). Độ tự tin: 0.90

# database
- Sử dụng cơ chế xóa mềm (soft deletes - trường `deletedAt`) trên các mô hình Prompt và Comment — tuyệt đối không xóa cứng (hard-delete) các bản ghi này. Độ tự tin: 0.95

F@f
0
Ảnh Chụp Điện Thoại Nghiệp Dư Chân Thực Hiển Thị Cuộc Trò Chuyện WhatsApp
Hình ảnh
Ảnh Chụp Điện Thoại Nghiệp Dư Chân Thực Hiển Thị Cuộc Trò Chuyện WhatsApp
Tạo một bức ảnh nghiệp dư chân thực, chụp vội không chỉnh chu về một chiếc điện thoại thông minh vật lý đang hiển thị cuộc trò chuyện WhatsApp trên màn hình.

Chiếc điện thoại được cầm dọc bằng một tay, lộ rõ viền màn hình/ốp lưng tối màu, ánh sáng trong nhà mờ ấm, hơi nghiêng nhẹ, có độ nhòe, nhiễu hạt, lóa sáng, bóng phản chiếu, lấy nét không đều và căn khung hình chưa chuẩn. Bức ảnh phải trông giống như ảnh chụp màn hình điện thoại ngoài đời thực bằng một máy ảnh khác với chất lượng kém, chứ không phải ảnh chụp màn hình (screenshot) kỹ thuật số sắc nét.

Trên màn hình điện thoại, hiển thị cuộc trò chuyện WhatsApp giao diện kiểu iPhone bằng tiếng Thổ Nhĩ Kỳ với tên liên hệ là receiver_name cùng một ảnh đại diện nhỏ đính kèm (nếu không được cung cấp thì dùng biểu tượng hồ sơ mặc định của WhatsApp).

Chủ đề cuộc trò chuyện:
talk_subject

Tạo đoạn hội thoại WhatsApp một cách tự nhiên dựa trên chủ đề ở trên. Các tin nhắn của người liên hệ phải bằng ngôn ngữ Turkish và mang phong cách talk_style (ví dụ: tiếng Turkish bập bẹ kèm lỗi chính tả và cách dùng từ ngượng nghịu. Tin nhắn của tôi phải là tiếng Turkish chuẩn xác không có lỗi chính tả). Sử dụng các bong bóng tin nhắn đến màu trắng chân thực, bong bóng tin nhắn đi màu xanh lá, dấu thời gian, dấu tích kép màu xanh dương, và thanh nhập liệu WhatsApp ở dưới cùng.

Giữ cho màn hình vẫn đọc được chữ nhưng hơi nhòe nhẹ, giống như một màn hình điện thoại được chụp lại một cách vụng về.
F@f
0
Email Mời Tham dự Hội nghị
Văn bản

Một câu lệnh (prompt) giúp tạo email mời tham dự hội nghị một cách chuyên nghiệp.

Hãy đóng vai một Điều phối viên Sự kiện. Bạn đang tổ chức một hội nghị uy tín về chủ đề Innovative Technologies. Nhiệm vụ của bạn là soạn thảo một bức thư điện tử (email) chuyên nghiệp để gửi lời mời tới các khách tham dự tiềm năng.

Bạn sẽ:
- Làm nổi bật những điểm nhấn quan trọng của hội nghị
- Cung cấp các thông tin thiết yếu như ngày tháng, thời gian và địa điểm tổ chức
- Đưa vào lời kêu gọi hành động (call-to-action) khuyến khích người nhận xác nhận tham dự (RSVP)
- Sử dụng giọng văn trang trọng và cuốn hút

Quy tắc:
- Đảm bảo tính rõ ràng và ngắn gọn, súc tích
- Tuân thủ chuẩn mực giao tiếp qua email chuyên nghiệp

Ví dụ:

Tiêu đề: Thư mời Tham dự Hội nghị Công nghệ Đổi mới Sáng tạo của Chúng tôi

Kính gửi [Recipient's Name],

Chúng tôi vô cùng hân hạnh được trân trọng kính mời Quý vị tới tham dự hội nghị [Conference Name] diễn ra vào ngày [Date] tại [Venue]. Hãy cùng chúng tôi tham gia một ngày thảo luận chuyên sâu đầy giá trị và kết nối giao lưu trực tiếp với các nhà lãnh đạo đầu ngành.

Vui lòng xác nhận tham dự (RSVP) trước ngày [RSVP Deadline]. Chúng tôi rất mong được đón tiếp Quý vị.

Trân trọng,

[Your Name]
[Your Position]
[Contact Information]
F@f
0
Đóng Vai Nhà Thiết Kế Trò Chơi Escape Room Tại Nhà
Văn bản

Thiết kế một trò chơi giải đố thoát hiểm (escape room) tại nhà hoàn chỉnh, có thể in ra được — bao gồm cốt truyện, chuỗi câu đố liên kết, cách bố trí phòng, thang gợi ý và kịch bản cho người dẫn chương trình — được tinh chỉnh theo người chơi, không gian và ngân sách của bạn.

Tôi muốn bạn đóng vai một nhà thiết kế phòng chơi thoát hiểm (escape room) giàu kinh nghiệm, chuyên xây dựng các trò chơi giải đố nhập vai với chi phí thấp có thể tổ chức ngay tại nhà, trong lớp học hoặc tại văn phòng.

Hãy thiết kế một trò chơi escape room hoàn chỉnh cho tôi dựa trên các thông tin chi tiết sau:
- Dịp tổ chức & người chơi: Birthday party for 6 adults
- Chủ đề: A 1920s detective's locked study
- Không gian hiện có: One living room and a hallway
- Thời lượng: 45 minutes
- Độ khó: Medium — first-timers welcome
- Ngân sách & vật liệu: Under $20, mostly household items, paper and a printer

Trình bày bản thiết kế theo cấu trúc sau:

1. **Mở Đầu Cốt Truyện (Story Hook)** — Đoạn giới thiệu dài 3–4 câu để người dẫn chương trình (host) đọc to, nêu bật tình huống gay cấn, mục tiêu cần đạt được và lý do tại sao thời gian đang đếm ngược.
2. **Sơ Đồ Luồng Câu Đố (Puzzle Flow Map)** — Chuỗi từng bước (từ 5–8 câu đố) chỉ rõ cách lời giải của mỗi câu đố mở khóa manh mối tiếp theo. Kết hợp đa dạng các thể loại câu đố (suy luận logic, quan sát, mật mã, tìm kiếm vật lý, chơi chữ, phối hợp đồng đội). Bao gồm ít nhất một nhánh song song để cả nhóm luôn có việc để làm.
3. **Chi Tiết Từng Câu Đố (Puzzle Details)** — Đối với mỗi câu đố: tên gọi, thứ người chơi tìm thấy, cách giải đố, đáp án chính xác, đạo cụ cần thiết, và cách tự làm các đạo cụ đó với chi phí rẻ. Cung cấp đầy đủ toàn bộ văn bản mật mã, câu đố chữ hoặc nội dung cần in ra.
4. **Danh Sách Kiểm Tra Bố Trí Phòng (Room Setup Checklist)** — Vị trí giấu từng món đồ theo đúng thứ tự sắp đặt để người dẫn chương trình có thể chuẩn bị xong trong vòng chưa đầy 30 phút.
5. **Thang Gợi Ý (Hint Ladder)** — Ba mức gợi ý tăng dần cho mỗi câu đố (khơi gợi nhẹ → định hướng → gần sát đáp án).
6. **Kịch Bản Dẫn Dắt & Phân Bổ Thời Gian (Host Script & Timing)** — Thời điểm nên đưa ra gợi ý, cách xử lý khi người chơi bị bí hoặc giải quá nhanh, và khoảnh khắc kết thúc đầy kịch tính.
7. **Ghi Chú Về Khả Năng Tiếp Cận & An Toàn (Accessibility & Safety Notes)** — Các điều chỉnh phù hợp cho trẻ em, người chơi mù màu hoặc người hạn chế khả năng vận động, cùng lời nhắc nhở rằng không có thử thách nào đòi hỏi dùng sức mạnh, leo trèo hoặc sử dụng ổ khóa thật không thể mở ra.

Quy tắc:
- Mọi câu đố phải công bằng và có thể giải được hoàn toàn bằng thông tin có sẵn trong trò chơi — không đòi hỏi kiến thức bên ngoài.
- Tránh các manh mối đánh lạc hướng (red herrings) làm tốn quá vài phút của người chơi.
- Giữ cho cốt truyện nhất quán: mọi manh mối đều phải mang lại cảm giác thuộc về thế giới của trò chơi.
- Ở cuối bài, hãy hỏi tôi xem tôi có muốn nhận thêm bản trang đạo cụ gọn trong một trang để in ra hoặc biến thể "chế độ chuyên gia" (expert mode) khó hơn hay không.
F@f
0
Thế Giới Thu Nhỏ Bên Trong Chiếc Đồng Hồ Bỏ Túi Cổ Điển
Hình ảnh
Thế Giới Thu Nhỏ Bên Trong Chiếc Đồng Hồ Bỏ Túi Cổ Điển

Bức ảnh chụp cận cảnh (macro) một khung cảnh thu nhỏ đầy sức sống được kiến tạo bên trong chiếc đồng hồ bỏ túi cổ đang mở nắp, với các bánh răng biến thành địa hình đồi núi và kim đồng hồ hóa thành những cây cầu. Thay đổi biến số bối cảnh (scene) để đặt bất kỳ thế giới nào vào bên trong.

Ảnh chụp macro siêu chi tiết một chiếc đồng hồ bỏ túi cổ bằng đồng thau đang mở nắp nằm trên mặt bàn gỗ nhuốm màu thời gian. Bên trong vỏ đồng hồ, thay vì mặt số thông thường là cả một thế giới thu nhỏ sống động: a misty alpine valley with a winding river, pine forests and a little stone village. Những bánh răng của đồng hồ đan cài vào cảnh quan thành những ngọn đồi bậc thang và những guồng nước, còn kim đồng hồ tạo thành một cây cầu thanh mảnh bắc qua dòng sông. Những ánh đèn ấm áp nhỏ xíu tỏa sáng qua các ô cửa sổ trong ngôi làng. Ánh nắng giờ vàng (golden-hour) dịu nhẹ chiếu tới từ phía bên trái, kết hợp với độ sâu trường ảnh nông, hậu cảnh bokeh mịn màng và những hạt bụi lơ lửng trong luồng sáng. Nắp đồng hồ chạm khắc đang mở ra và đổ một bóng mờ dịu dàng. Chân thực như ảnh chụp, mang cảm giác mô hình thu nhỏ kiểu tilt-shift, kết cấu phong phú của đồng thau trầy xước và mặt kính, chỉnh màu đậm chất điện ảnh, bố cục 16:9.
PhotographyMacro
F@f
0
Previous4 / 19Next