MCP Server — Thiết kế chức năng (tổng thể)
| Mục | Nội dung |
|---|---|
| Trạng thái | 🟡 Đang thiết kế |
| Case liên quan | #13 MCP Server (Thiết lập mới) |
| GitLab Issue | Mappy #58 |
| Tài liệu cha | (Tài liệu này là cha của thiết kế chức năng MCP) |
| Tài liệu con | Tool Catalog / Auth & Tenancy / Cơ chế an toàn write |
1. Mục đích tài liệu
Định nghĩa bức tranh tổng thể của thiết kế chức năng MCP Server. So sánh stack triển khai (Python / TypeScript), phân chia component, flow request, spec transport, phương châm xử lý lỗi sẽ được chốt tại đây làm tiền đề cho các tài liệu design sau (tool catalog・auth・write safety・audit log v.v.).
2. Bức tranh tổng thể hệ thống
2.1 Sơ đồ kiến trúc
2.2 Trách nhiệm từng component
| Component | Trách nhiệm | Vị trí |
|---|---|---|
| Streamable HTTP endpoint | Tiếp nhận protocol MCP, duy trì kết nối long-lived, gửi/nhận message và event | Process MCP Server |
| OAuth 2.1 Authz Server | Đăng ký client, phát authorization code, phát token, kiểm tra scope | Process MCP Server (hoặc tách riêng) |
| Tool Dispatcher | Routing từ tên tool tới hàm implement, validate input, kiểm tra scope, format kết quả | Process MCP Server |
| Audit Logger | Ghi tất cả tool call, auth failure, exception vào audit log DB | Process MCP Server |
| Laravel API (cho MCP) | Thêm endpoint MCP dưới /api/mcp/*. Gọi service・model hiện có | Trong Laravel app |
| Mappy DB | Dữ liệu thực. Từ MCP chỉ access qua Laravel API | Hiện có |
| GBP API | Trung gian tới Google Business Profile. Laravel API quản lý token | External |
| Jobs Queue | Xử lý bất đồng bộ cho write tool. Dùng bảng jobs hiện có | Hiện có |
Cấm truy cập DB trực tiếp
MCP Server không SELECT/UPDATE Mappy DB trực tiếp. Mọi data access đều qua Laravel API. Lý do:
- Không duplicate logic kiểm tra tenancy・quyền
- Tận dụng validation・service logic・cơ chế audit hiện có
- Tối thiểu hóa phạm vi ảnh hưởng khi schema thay đổi
3. Flow request
3.1 Flow kết nối lần đầu・xác thực (OAuth 2.1 + PKCE)
Chi tiết xem Auth & Tenancy.
3.2 Flow gọi read tool (đồng bộ)
3.3 Flow gọi write tool (bất đồng bộ)
Chi tiết xem Cơ chế an toàn write.
4. Transport — Streamable HTTP
4.1 Lý do chọn
- Transport chính thức của MCP 2025-03-26 spec (SSE cũ đang dần bỏ)
- Duy trì kết nối long-lived nhưng hợp với HTTP/2 / HTTP/3
- Xử lý được bằng load balancer・WAF・CDN tiêu chuẩn
- ChatGPT Connectors / Claude Desktop đều hỗ trợ chính thức
4.2 Endpoint
| Path | Method | Mục đích |
|---|---|---|
/sse | GET | Thiết lập session MCP (event stream) |
/messages | POST | Client gửi message MCP |
/oauth/authorize | GET | Endpoint authorize OAuth |
/oauth/token | POST | Phát access token |
/oauth/register | POST | Dynamic client registration (RFC 7591) |
/.well-known/oauth-authorization-server | GET | OAuth metadata |
/health | GET | Health check |
4.3 Quản lý session
- 1 connection = 1 session
- Session ID phát từ server, trả về qua header
Mcp-Session-Id - Khi disconnect thì hủy session, kết nối lại = session mới
- Idle timeout: 30 phút
- Bắt buộc re-auth: khi refresh_token hết hạn
5. So sánh chi tiết stack triển khai
Đề xuất song song 2 phương án, nhưng tài liệu này làm rõ tiêu chí chọn.
5.1 Phương án A: Python (FastMCP)
| Mục | Nội dung |
|---|---|
| MCP library | FastMCP 2.x |
| Python version đề xuất | 3.11+ |
| HTTP server | Uvicorn (ASGI) |
| Implement OAuth 2.1 | Authlib hoặc tự implement |
| Validate input/output | Pydantic v2 |
| Laravel API client | httpx (async) |
| Logging | structlog + JSON output |
| Test | pytest + pytest-asyncio |
| Quản lý dependency | Poetry hoặc uv |
Sample code (định nghĩa tool):
from fastmcp import FastMCP
from pydantic import BaseModel, Field
mcp = FastMCP("Mappy MCP")
class LocationListInput(BaseModel):
user_id: int = Field(..., description="ID user đối tượng")
group_id: int | None = Field(None, description="Lọc theo group")
limit: int = Field(50, ge=1, le=100)
@mcp.tool(scopes=["mappy:location:read"])
async def location_list(input: LocationListInput) -> list[dict]:
"""Trả về danh sách location mà user có quyền access"""
resp = await laravel_api.get(
"/api/mcp/locations",
params=input.model_dump(exclude_none=True),
)
return resp.json()5.2 Phương án B: TypeScript (SDK chính thức)
| Mục | Nội dung |
|---|---|
| MCP library | @modelcontextprotocol/sdk |
| Node version đề xuất | 20 LTS |
| HTTP server | Express / Hono / Fastify |
| Implement OAuth 2.1 | oauth4webapi / panva/jose |
| Validate input/output | Zod |
| Laravel API client | undici / fetch |
| Logging | pino |
| Test | vitest |
| Quản lý dependency | pnpm hoặc npm |
Sample code (định nghĩa tool):
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
const server = new McpServer({ name: "Mappy MCP", version: "1.0.0" });
const LocationListInput = z.object({
user_id: z.number().int().describe("ID user đối tượng"),
group_id: z.number().int().optional(),
limit: z.number().int().min(1).max(100).default(50),
});
server.tool(
"location.list",
"Trả về danh sách location mà user có quyền access",
LocationListInput.shape,
async (input) => {
const resp = await fetch(
`${LARAVEL_API}/api/mcp/locations?${new URLSearchParams(input)}`,
);
return { content: [{ type: "text", text: await resp.text() }] };
},
);5.3 Bảng so sánh
| Tiêu chí | Python (FastMCP) | TypeScript (SDK chính thức) | Ghi chú |
|---|---|---|---|
| Hỗ trợ chính thức | Cộng đồng (phổ biến) | Chính thức | TS lợi thế hơn một bước |
| Tài liệu | Tốt | Đầy đủ | TS lợi thế hơn một bước |
| Type safety | Pydantic (runtime) | Zod + TS (static) | TS mạnh hơn |
| Skill nội bộ hiện có | Tích lũy qua scraping・AI | Tích lũy phía frontend | Tương đương |
| Đồng cư với AI report | ○ cùng Python | × khác ngôn ngữ | Python lợi thế |
| Mở rộng PPTX/Excel v.v. | ○ python-pptx v.v. | △ ít library cho Node.js | Python lợi thế |
| Đồng cư trong Laradock | Thêm container mới | Tận dụng được container Node hiện có | TS lợi thế |
| Tốc độ khởi động・memory | Nặng hơn Node.js | Nhẹ | TS lợi thế |
| Độ trưởng thành Streamable HTTP | Ổn định | Ổn định | Tương đương |
| Library OAuth 2.1 | Authlib (phong phú) | oauth4webapi (chuẩn) | Tương đương |
5.4 Đề xuất và tiêu chí phán đoán
Đề xuất: Python (FastMCP)
Thứ tự ưu tiên của tiêu chí phán đoán:
- Tính dễ đồng cư・vận hành tích hợp (AI report #9 đã chọn Python)
- Khả năng mở rộng tương lai (option PPTX/Excel, tích hợp ML)
- Skill nội bộ (scraping cũng là Python)
Với 3 điểm này Python ưu thế. Lợi thế của TypeScript (hỗ trợ chính thức・type safety) cũng có, nhưng ưu tiên tích hợp vận hành xuyên tổ chức.
Quyết định cuối cùng tại review design → ngay trước khi raise Issue manufacture.
6. Khác biệt hành vi theo client
| Client | Transport | OAuth flow | Lưu ý |
|---|---|---|---|
| Claude Desktop | Streamable HTTP / stdio cả 2 | Browser nội bộ chạy PKCE flow | Hỗ trợ macOS / Windows / Linux |
| Claude Code | Streamable HTTP / stdio cả 2 | Tương tự | CLI based |
| ChatGPT Connectors | Chỉ Streamable HTTP | Màn hình cấu hình connector trên web | Phải đăng ký redirect URI bên phía ChatGPT |
6.1 Bắt buộc để đảm bảo tương thích
- Trả về
/.well-known/oauth-authorization-server - Dynamic client registration RFC 7591 (ChatGPT có thể không cần đăng ký trước)
- Bắt buộc support PKCE (
code_challenge_method=S256) - Tool description nên kèm tiếng Anh hoặc có cấu trúc theo ngôn ngữ của AI khách
- Whitelist redirect URI: đăng ký URI chính thức của Claude / ChatGPT
7. Xử lý lỗi
7.1 Phân loại lỗi
| Loại | HTTP / JSON-RPC | Ví dụ | Hành động |
|---|---|---|---|
| Lỗi auth | 401 | Token vô hiệu hoặc hết hạn | Yêu cầu client re-auth |
| Lỗi authz | 403 / -32604 | Thiếu scope | Trả về tool result với isError: true |
| Lỗi input | 400 / -32602 | Validation fail | Trả về kèm message chi tiết |
| 4xx phía Mappy | Pass-through | Record không tồn tại v.v. | Giữ error code và trả về |
| 5xx phía Mappy | 502 / -32603 | Laravel API gặp sự cố | Ghi Audit Log, xét khả năng retry |
| GBP API hỏng | 502 | Google bị lỗi | Truyền thông điệp lỗi cho AI khách |
| Timeout | 504 | Quá 30 giây không response | Thông báo client, write thì truy theo batch_id |
7.2 Phương châm timeout
| Khu vực | Timeout |
|---|---|
| MCP Client → MCP Server | Theo cấu hình client (thường 30〜60 giây) |
| MCP Server → Laravel API | 10 giây (read) / 30 giây (write đồng bộ) |
| Laravel API → GBP API | Theo cấu hình hiện có (thường 30 giây) |
| Toàn bộ write (đến khi job xong) | Bất đồng bộ, truy theo batch_id |
7.3 Phương châm retry
- Read: chỉ 5xx, client tự retry 1 lần
- Write: chỉ cho phép resend với idempotency_key. Server không auto retry
- Rate limit GBP API: tận dụng middleware rate-limiter hiện có của Laravel
8. Rate limit
| Phạm vi áp dụng | Giới hạn | Lý do |
|---|---|---|
| Theo access token | 60 request/phút (read) | Chống DoS |
| Theo access token | 10 request/phút (write) | Cân nhắc giới hạn GBP API |
| Theo client ID | 1000 request/giờ | Chống loạn từ một client |
| Cùng idempotency_key | 24 giờ | Chống duplicate write |
9. Cấu hình deploy (tổng quan)
Chi tiết xem Thiết kế hạ tầng.
| Mục | Nội dung |
|---|---|
| Vị trí | AWS (cùng VPC với Mappy hiện có) |
| Container | ECS Fargate (hoặc EC2) |
| Load balancer | ALB (HTTPS termination) |
| Domain | mcp.mappy.example.com (tạm) |
| TLS | Cert ACM |
| Common store | Redis (session, counter rate limit, cache idempotency) |
| Quản lý secret | AWS Secrets Manager |
| Tập trung log | CloudWatch Logs |
| Giám sát | CloudWatch Metrics / Alarms |
10. Audit log
MCP Server ghi toàn bộ thao tác vào audit log. Chi tiết xem DB (audit log).
Đối tượng ghi:
- Tool call (input・output・thời gian thực thi・mã kết quả)
- Authz thành công・thất bại
- Phát token・revoke
- Rate limit hit
- Exception・timeout
11. Tenancy・Scope (tổng quan)
Chi tiết xem Auth & Tenancy.
| Tầng | Phạm vi thao tác |
|---|---|
admin (is_supervisor=1) | Toàn bộ mappy_users |
admin (is_supervisor=0) | User trong phạm vi preview_user_id |
| MAIN_USER | Bản thân + group dưới parent_user_id |
| MASTER_USER | Toàn bộ group dưới parent_user_id |
| GROUP_USER | Chỉ bản thân |
Ranh giới cửa hàng:
- Lọc qua
mappy_user_available_gbp_locations - Theo group:
mappy_groups+mappy_group_location(dạng số ít)
12. Tài liệu thiết kế liên quan
| Tài liệu | Nội dung |
|---|---|
| Tool Catalog | Schema input/output của từng tool, scope, phân loại side effect |
| Auth & Tenancy | Chi tiết OAuth 2.1 flow, thiết kế scope, logic xác định tenancy |
| Cơ chế an toàn write | dry_run / idempotency / confirm token / batch polling |
| DB (audit log) | Định nghĩa bảng audit log |
| DB (OAuth client) | Bảng quản lý OAuth client・token |
| Thiết kế hạ tầng | Cấu hình AWS・container・network |
| Chi tiết Issue | Task implement・điều kiện nghiệm thu |
13. Điểm chưa chốt (quyết định tại review design)
| # | Mục | Phương án |
|---|---|---|
| 1 | Stack triển khai | Python (FastMCP) / TypeScript (SDK chính thức) |
| 2 | Library OAuth | Python: Authlib / tự implement TS: oauth4webapi / tự implement |
| 3 | Hostname MCP Server | mcp.mappy.example.com (tạm) |
| 4 | Phạm vi cho phép thao tác thay mặt admin | Phase 1 chỉ chính admin / Phase 2 hỗ trợ preview_user_id |
| 5 | Giá trị rate limit cuối cùng | Trên đây là tạm, điều chỉnh sau khi xem tải production |
| 6 | Redis vs DynamoDB | Chọn store cho session・idempotency |
| 7 | Cache phía client | AI khách có cache tool result hay không |