MCP Server — Thiết kế hạ tầng
| 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) |
| Tài liệu cha | MCP Server (Tổng thể) |
| Design liên quan | DB (audit log) / DB (OAuth) |
1. Mục đích tài liệu
Chốt cấu hình hosting MCP Server trên AWS.
- Tích hợp vào hạ tầng GMAC hiện có (VPC
vpc-0b4c742f84f3b2d9d, regionap-northeast-1) - Vận hành container (ECS Fargate)
- Store quản lý trạng thái auth・OAuth
- Network・Security
- Giám sát・Alert
- Cấu hình chung cho cả 2 phương án stack (Python / TypeScript)
2. Phương châm tận dụng hạ tầng hiện có
2.1 Tái sử dụng
| Mục | Resource hiện có | Phương châm |
|---|---|---|
| VPC | vpc-0b4c742f84f3b2d9d (10.0.0.0/16) | Dùng nguyên si |
| Private subnet | subnet-095de9e00c1894a0c (1a), subnet-0f38ab1dce252e375 (1c) | Nơi đặt MCP ECS task |
| Public subnet | subnet-0ff23c0ce66750053 (1a), subnet-0ebe6abb3572271e8 (1c) | Nơi đặt ALB riêng MCP |
| RDS | h2t-db (MySQL 8.0) | Thêm bảng MCP vào mappy DB |
| ECR | Thêm repo mới h2t-mappy-mcp | Theo quy tắc đặt tên hiện có |
| S3 | Bucket hiện có | Nguyên tắc không tạo bucket mới |
| CloudWatch | Theo cấu hình log group hiện có | Thêm log group mới |
| Route 53 | Hosted zone hiện có | Thêm subdomain mcp.mappy.<existing-domain> |
| AWS Account | 635388747933 | Cùng account |
2.2 Thêm mới
| Mục | Lý do |
|---|---|
| ALB | MCP chạy trên domain độc lập (tách khỏi ALB hiện có) |
| ElastiCache (Redis) | OAuth code・session・counter rate limit |
| Secrets Manager | JWT signing key・OAuth client secret |
| ECS Service | h2t-mcp-server Fargate |
| Security Group | h2t-sg-mcp, h2t-sg-mcp-redis |
| ACM Certificate | mcp.mappy.example.com (tạm) |
| WAF | Tăng cường DDoS / rate limit (tùy chọn) |
3. Sơ đồ tổng thể
4. Cấu hình container (ECS Fargate)
4.1 Task definition
| Mục | Giá trị |
|---|---|
| Task definition family | h2t-mcp-server |
| Loại khởi động | Fargate |
| Platform version | LATEST |
| CPU | 0.5 vCPU (Phase 1) / 1 vCPU (từ Phase 2) |
| Memory | 1 GB (Phase 1) / 2 GB (từ Phase 2) |
| Network mode | awsvpc |
| Task role | h2t-mcp-task-role |
| Execution role | h2t-mcp-execution-role |
| Log driver | awslogs |
| Log group | /ecs/h2t-mcp-server |
4.2 Container definition
| Mục | Giá trị |
|---|---|
| Image | 635388747933.dkr.ecr.ap-northeast-1.amazonaws.com/h2t-mappy-mcp:<tag> |
| Port | 8000 (trong container) → forward HTTP từ ALB |
| Health check | GET /health (interval 30 giây, timeout 5 giây, retry 3 lần) |
| Stop timeout | 30 giây (graceful shutdown của Streamable HTTP) |
| Env variable | Xem "Env・Secret" dưới |
4.3 Service definition
| Mục | Giá trị |
|---|---|
| Tên service | h2t-mcp-server |
| Cluster | h2t-cluster (hiện có) hoặc mới h2t-mcp-cluster |
| Số task mong muốn | Phase 1: 2 / Phase 2: 4 / Phase 3: 6 |
| Số task tối thiểu | 2 |
| Số task tối đa | 10 |
| Loại deploy | Rolling Update (minimum healthy 50%, maximum 200%) |
| Auto Scaling | Target Tracking (CPU 70%, số request 100/phút) |
| Load balancer | Target group h2t-mcp-tg của h2t-mcp-alb |
| Service discovery | Tùy chọn |
Sticky session của Streamable HTTP
Session Streamable HTTP của MCP là long-lived. Cần thiết lập sticky session của ALB (cùng client tới cùng task). Hoặc thiết kế tập trung trạng thái session ở Redis để stateless. Design này áp dụng quản lý tập trung Redis (stateless).
5. Load balancer (ALB)
5.1 Cấu hình
| Mục | Giá trị |
|---|---|
| Tên ALB | h2t-mcp-alb |
| Scheme | internet-facing |
| Subnet | h2t-public-1a, h2t-public-1c |
| Security group | h2t-sg-mcp-alb (mới) |
| Idle timeout | 600 giây (đối phó kết nối long-lived của Streamable HTTP) |
| HTTP/2 | Bật |
| Attach WAF | optional (xem xét từ Phase 2) |
5.2 Listener
| Port | Protocol | Hành vi |
|---|---|---|
| 80 | HTTP | Redirect 301 sang HTTPS |
| 443 | HTTPS | ACM certificate, forward sang target group h2t-mcp-tg |
5.3 Target group
| Mục | Giá trị |
|---|---|
| Tên | h2t-mcp-tg |
| Protocol | HTTP |
| Port | 8000 |
| Health check path | /health |
| Health check interval | 30 giây |
| Health check threshold | Thành công 2 lần, thất bại 3 lần |
| Deregistration delay | 30 giây (chờ kết nối kết thúc) |
| Sticky session | Tắt (do quản lý tập trung Redis) |
6. Data store
6.1 ElastiCache Redis (mới)
Dùng cho OAuth code・session・rate limit・cache idempotency.
| Mục | Giá trị |
|---|---|
| Engine | Redis 7.x |
| Node type | cache.t4g.micro (Phase 1) / cache.t4g.small (từ Phase 2) |
| Số node | 1 (Phase 1, không Multi-AZ) / Cluster Mode (xem xét ở Phase 3) |
| Subnet | h2t-private-1a, h2t-private-1c |
| Security group | h2t-sg-mcp-redis |
| Mã hóa (at-rest) | Bật |
| Mã hóa (in-transit) | Bật (TLS) |
| Auth | Redis AUTH (quản lý qua Secrets Manager) |
| Backup | Snapshot hàng ngày, giữ 7 ngày |
| Maintenance window | Thứ Ba 04:00-05:00 JST |
Thiết kế key
| Prefix | Mục đích | TTL |
|---|---|---|
oauth:code:<hash> | Authorization code | 60 giây |
oauth:state:<hash> | Verify state | 5 phút |
mcp:session:<id> | Session Streamable HTTP | 30 phút (idle) |
idempotency:<client_id>:<user_id>:<key> | Cache idempotency | 24 giờ |
confirm:<token> | Confirm token áp dụng hàng loạt | 5 phút |
ratelimit:<scope>:<key> | Counter rate limit | 1 phút〜1 giờ |
jwt:revoke:<jti> | Danh sách revoke access token (cache Bloom Filter) | Đến exp của JWT |
6.2 RDS (hiện có)
Thêm nhóm bảng MCP vào mappy DB:
mappy_mcp_audit_logsmappy_mcp_batchesmappy_mcp_rate_limit_logsmappy_mcp_oauth_clientsmappy_mcp_oauth_consentsmappy_mcp_oauth_refresh_tokensmappy_mcp_oauth_access_token_revocations
Chi tiết xem DB audit log / DB OAuth.
Update SG nguồn kết nối RDS
Thêm access 3306 từ h2t-sg-mcp mới vào inbound của h2t-sg-rds hiện có.
7. ECR・Container image
7.1 Repository
| Tên | URI |
|---|---|
h2t-mappy-mcp | 635388747933.dkr.ecr.ap-northeast-1.amazonaws.com/h2t-mappy-mcp |
7.2 Quy tắc tag image
| Tag | Mục đích |
|---|---|
latest | Main branch mới nhất |
v1.0.0 | Bản release |
staging-<sha> | Staging |
dev-<sha> | Môi trường dev |
7.3 Cấu hình image
Phương án A: Python (FastMCP)
FROM python:3.11-slim
WORKDIR /app
COPY pyproject.toml uv.lock /app/
RUN pip install --no-cache-dir uv && \
uv sync --frozen --no-dev
COPY src/ /app/src/
USER 1000
EXPOSE 8000
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]Phương án B: TypeScript
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json pnpm-lock.yaml /app/
RUN corepack enable && pnpm install --frozen-lockfile
COPY src/ /app/src/
COPY tsconfig.json /app/
RUN pnpm build
FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=build /app/node_modules /app/node_modules
COPY --from=build /app/dist /app/dist
COPY package.json /app/
USER 1000
EXPOSE 8000
CMD ["node", "dist/main.js"]7.4 Kích thước image dự kiến
| Ngôn ngữ | Kích thước mong đợi |
|---|---|
| Python (slim base) | Khoảng 200 MB |
| Node.js (alpine base) | Khoảng 150 MB |
8. Network・Security Group
8.1 SG mới
| Tên SG | Inbound | Mục đích |
|---|---|---|
h2t-sg-mcp-alb | 80 / 443 from 0.0.0.0/0 | ALB |
h2t-sg-mcp | 8000 from h2t-sg-mcp-alb | ECS task |
h2t-sg-mcp-redis | 6379 from h2t-sg-mcp | Redis |
8.2 Thêm vào SG hiện có
| SG hiện có | Thêm inbound |
|---|---|
h2t-sg-rds | 3306 from h2t-sg-mcp |
8.3 Outbound
Tất cả SG:
- 443 to 0.0.0.0/0 (cho GBP API・external API)
- 53 to 0.0.0.0/0 (DNS)
- Nếu dùng VPC endpoint thì không cần
9. Env・Secret
9.1 Env (ghi trực tiếp vào task definition)
| Tên biến | Ví dụ |
|---|---|
APP_ENV | production / staging |
LOG_LEVEL | INFO |
MCP_BASE_URL | https://mcp.mappy.example.com |
OAUTH_ISSUER | Như trên |
OAUTH_AUDIENCE | mappy-mcp |
LARAVEL_API_BASE_URL | https://api.mappy.example.com |
REDIS_HOST | h2t-mcp-redis.cache.amazonaws.com |
REDIS_PORT | 6379 |
RDS_HOST | Hiện có |
RDS_DATABASE | mappy |
9.2 Secrets Manager (chỉ tham chiếu)
| Tên Secret | Nội dung |
|---|---|
h2t-mcp/jwt-signing-keys | JWT signing key (private key RS256, nhiều kid) |
h2t-mcp/redis-auth-token | Token Redis AUTH |
h2t-mcp/rds-credentials | Username・password DB |
h2t-mcp/laravel-api-token | Internal auth token cho Laravel API |
h2t-mcp/oauth-client-secrets | Secret của trusted client (registry) |
Gán IAM policy sau vào task role:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue"],
"Resource": [
"arn:aws:secretsmanager:ap-northeast-1:635388747933:secret:h2t-mcp/*"
]
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:ap-northeast-1:635388747933:log-group:/ecs/h2t-mcp-server:*"
]
}
]
}10. Route 53 / Domain
| Mục | Giá trị |
|---|---|
| Domain | mcp.mappy.example.com (tạm, chốt khi review) |
| ALB alias | h2t-mcp-alb-xxxxxxxx.ap-northeast-1.elb.amazonaws.com |
| Loại record | A (alias) |
| Health check | Tùy chọn (có thể cover bằng CloudWatch) |
10.1 Ứng cử viên domain
| Phương án | Ví dụ | Lưu ý |
|---|---|---|
| Tách subdomain | mcp.mappy.example.com | Đề xuất. Dễ tách CORS・Cookie |
| Tách path | mappy.example.com/mcp | KHÔNG. Long-lived của Streamable HTTP và cấu hình ALB hiện có xung đột |
| Domain riêng | mappy-mcp.com | Có thể nếu chấp nhận chi phí domain mới・không cần SEO |
11. CI/CD
11.1 Theo cách hiện có
| Step | Nội dung |
|---|---|
| Code repository | h2t/mappy-mcp (mới) hoặc subdirectory trong h2t/mappy |
| Build pipeline | GitLab CI (tận dụng CI hiện có) |
| Build target | Push từ Dockerfile lên ECR h2t-mappy-mcp |
| Deploy | Update ECS service (apply revision mới của task definition) |
| Rollback | Revert về revision trước |
11.2 Phân chia môi trường
| Môi trường | ALB | ECS service | Redis | RDS |
|---|---|---|---|---|
| Production | h2t-mcp-alb | h2t-mcp-server | h2t-mcp-redis | h2t-db |
| Staging | h2t-mcp-alb-stg | h2t-mcp-server-stg | h2t-mcp-redis-stg | h2t-db-stg |
| Development | (docker-compose local) | - | (Redis local) | (MySQL local) |
12. Giám sát・Alert
12.1 CloudWatch Metrics
| Metric | Threshold | Action |
|---|---|---|
| ECS CPU usage | > 80% (5 phút) | Auto Scaling + thông báo |
| ECS Memory usage | > 80% (5 phút) | Thông báo |
| ALB tỷ lệ 5xx | > 1% (5 phút) | Thông báo CRITICAL |
| ALB Target Health | < 50% healthy | Thông báo CRITICAL |
| Thời gian response ALB | p99 > 3 giây (5 phút) | Thông báo WARNING |
| Redis CPU | > 70% (5 phút) | Xem xét scale up |
| Redis memory | > 80% | Scale up |
| Số kết nối RDS | > 80% max_connections | WARNING |
12.2 CloudWatch Logs
| Log group | Thời gian giữ |
|---|---|
/ecs/h2t-mcp-server | 30 ngày |
/ecs/h2t-mcp-server-access | 90 ngày |
/ecs/h2t-mcp-server-audit | 1 năm |
12.3 Quy ước field log
Cấu trúc dạng JSON, bắt buộc có các field sau:
{
"timestamp": "2026-05-26T10:00:00Z",
"level": "INFO",
"service": "mcp-server",
"request_id": "...",
"user_id": 1435,
"client_id": "...",
"tool_name": "location.list",
"elapsed_ms": 123,
"message": "tool executed"
}12.4 Thông báo alert
- Channel Slack #h2t-mcp-alerts
- CRITICAL thì thông báo điện thoại + email
13. Backup・DR
| Đối tượng | Phương pháp | Tần suất |
|---|---|---|
| Data RDS | Auto backup (hiện có) | Hàng ngày 7 ngày |
| Data Redis | Snapshot | Hàng ngày 7 ngày |
| Task definition ECS | Quản lý version (auto) | Auto |
| Secrets Manager | Giữ version | Auto |
RTO (recovery time objective): trong 1 giờ RPO (recovery point objective): khôi phục data trong 24 giờ gần nhất
14. Ước lượng chi phí (Phase 1)
| Resource | Ước lượng tháng |
|---|---|
| ECS Fargate (2 task × 0.5 vCPU / 1 GB) | Khoảng 30 USD |
| ALB | Khoảng 25 USD |
| ElastiCache Redis (cache.t4g.micro) | Khoảng 15 USD |
| ACM Certificate | Miễn phí |
| Route 53 | Khoảng 1 USD |
| Secrets Manager (5 secret) | Khoảng 2 USD |
| CloudWatch Logs (30 ngày × 5 GB) | Khoảng 5 USD |
| Lưu trữ ECR | Khoảng 1 USD |
| Truyền data | Khoảng 5 USD |
| Tổng | Khoảng 84 USD/tháng |
Từ Phase 2:
- Tăng ECS task (4 task × 1 vCPU / 2 GB) → khoảng 120 USD/tháng
- Scale up Redis → khoảng 30 USD/tháng
- Tổng: khoảng 180 USD/tháng
Tối ưu chi phí
- Phase 1 dùng Fargate Spot giảm 30〜50%
- Đồng cư trên RDS hiện có nên không phát sinh chi phí DB
- Môi trường staging vận hành bằng Fargate Spot + node 1
15. Checklist bảo mật
| Mục | Trạng thái |
|---|---|
| ALB chỉ HTTPS (HTTP redirect 301) | ✅ |
| ECS task đặt trong private subnet | ✅ |
| Redis mã hóa in-transit + at-rest | ✅ |
| RDS theo cấu hình hiện có (đã mã hóa) | ✅ |
| Tham chiếu Secrets Manager giới hạn bằng IAM | ✅ |
| WAF (xem xét từ Phase 2) | 🔵 |
| Nội bộ hóa traffic AWS API qua VPC endpoint | 🔵 (đề xuất) |
| Bật GuardDuty (hiện có) | ✅ |
| Theo dõi thay đổi resource bằng AWS Config | Theo cấu hình hiện có |
| Tập trung access log vào S3 | 🔵 |
16. Quy trình deploy (Phase 1 lần đầu)
Chuẩn bị nền tảng
- Tạo SG mới (
h2t-sg-mcp-alb/h2t-sg-mcp/h2t-sg-mcp-redis) - Thêm inbound vào
h2t-sg-rdshiện có - Tạo instance ElastiCache Redis
- Lưu JWT key・các secret vào Secrets Manager
- Tạo ECR repository
h2t-mappy-mcp
- Tạo SG mới (
DB migration
Xây ALB
- Phát ACM certificate (
mcp.mappy.example.com) - Tạo ALB / target group
- Thêm record Route 53
- Phát ACM certificate (
Xây ECS
- Cluster (tận dụng hiện có hoặc mới)
- Task definition (IAM role, env, tham chiếu Secrets)
- Tạo service (liên kết ALB, Auto Scaling)
Deploy app lần đầu
- Build Dockerfile & push ECR
- Update ECS service
Kiểm tra hoạt động
- Xác nhận response
/health - Xác nhận
/.well-known/oauth-authorization-server - Test kết nối từ Claude Desktop / ChatGPT
- Xác nhận response
Cấu hình giám sát・alert
- Tạo CloudWatch Alarm
- Cấu hình thông báo Slack
17. Điểm chưa chốt (quyết định tại review design)
| # | Mục | Phương án |
|---|---|---|
| 1 | Domain name cho MCP | mcp.mappy.example.com (tạm) |
| 2 | ECS cluster | Tận dụng hiện có / mới |
| 3 | Thời điểm đưa WAF | Phase 1 / Phase 2 |
| 4 | Redis Multi-AZ | Hỗ trợ Phase 1 / từ Phase 2 |
| 5 | Phạm vi dùng Fargate Spot | Toàn production / chỉ staging |
| 6 | Cấu hình repository | Mới h2t/mappy-mcp / trong h2t/mappy |
| 7 | Flow approve deploy | Auto / approve thủ công |
| 8 | Đưa VPC endpoint | Ngay / Phase 2 |