mappy_mcp_audit_logs (Audit log MCP)
| 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 | Auth & Tenancy / Cơ chế an toàn write |
ER diagram
Tổng quan
Bảng audit log ghi lại toàn bộ thao tác phát sinh qua MCP Server. Read・Write・từ chối・dry_run・lỗi・rate limit・OAuth event đều được ghi không sót, làm dữ liệu nền tảng cho phát hiện lạm dụng・phân tích sự cố・đáp ứng compliance.
Phương châm thiết kế
- Ghi toàn bộ: tool call dù thành công/thất bại đều ghi
- Theo dõi thao tác thay mặt: tách biệt
user_id(người thực thi) vàtarget_user_id(đối tượng) - Tính tìm kiếm: index tổ hợp trên client_id / user_id / tool_name / created_at
- Thời gian giữ: chuẩn 1 năm (write 3 năm, tùy yêu cầu)
- Cân nhắc PII: không ghi token・password, comment body lưu vào store riêng
- Tần suất ghi: ghi async dự kiến vì tần suất cao (có thể tận dụng Jobs queue hiện có)
Định nghĩa bảng
mappy_mcp_audit_logs
| No | Tên cột (logical) | Tên cột (physical) | Kiểu | NULL | Key | Mô tả |
|---|---|---|---|---|---|---|
| 1 | ID | id | bigint unsigned | NO | PK | Auto increment |
| 2 | Request ID | request_id | varchar(64) | NO | UK | ID duy nhất kiểu ULID. Có cả trong response, user đối chiếu được |
| 3 | Loại event | event_type | varchar(64) | NO | IDX | tool_call / oauth_authorize / oauth_token / oauth_revoke / oauth_register / rate_limit / dry_run / confirm_token_issued / confirm_token_consumed / forbidden / error |
| 4 | Client ID | client_id | varchar(64) | YES | IDX, FK | mappy_mcp_oauth_clients.client_id |
| 5 | User auth ID | user_id | bigint unsigned | YES | IDX, FK | mappy_users.id (hoặc NULL khi là admin) |
| 6 | Loại user auth | user_kind | varchar(16) | YES | - | mappy / admin |
| 7 | Admin ID | admin_id | bigint unsigned | YES | FK | admins.id (khi user_kind=admin) |
| 8 | User đối tượng ID | target_user_id | bigint unsigned | YES | IDX, FK | mappy_users.id (khi thao tác thay mặt, thường giống user_id) |
| 9 | Tên tool | tool_name | varchar(128) | YES | IDX | location.list v.v. OAuth event = NULL |
| 10 | Scope yêu cầu | scope_required | varchar(255) | YES | - | Scope cần để chạy tool (cách bằng dấu phẩy) |
| 11 | Scope được cấp | scope_granted | text | YES | - | Scope gắn vào access token (cách bằng dấu phẩy) |
| 12 | Hash input | input_hash | char(64) | YES | - | SHA-256(input JSON đã chuẩn hóa) |
| 13 | Tóm tắt input | input_summary | json | YES | - | Chỉ trích các tham số chính (loại trừ PII / dữ liệu lớn) |
| 14 | Status | status | varchar(32) | NO | IDX | success / failed / forbidden / rate_limited / invalid / queued / partial |
| 15 | Error code | error_code | varchar(64) | YES | - | FORBIDDEN_LOCATION v.v. |
| 16 | Error message | error_message | text | YES | - | Message chi tiết |
| 17 | Số lượng response | result_count | int | YES | - | Số dòng trả về của list tool |
| 18 | Batch ID | batch_id | varchar(64) | YES | IDX, FK | Trường hợp xử lý bất đồng bộ: mappy_mcp_batches.batch_id |
| 19 | Idempotency key | idempotency_key | varchar(64) | YES | IDX | Client cung cấp |
| 20 | Confirm token | confirm_token | varchar(64) | YES | - | Token đã phát・tiêu thụ khi áp dụng hàng loạt |
| 21 | dry_run | is_dry_run | tinyint(1) | NO | - | 0: thực thi / 1: dry_run |
| 22 | Thời gian thực thi (ms) | elapsed_ms | int unsigned | YES | - | Mili-giây thực thi tool |
| 23 | Request IP | request_ip | varchar(45) | YES | - | IPv4/IPv6 |
| 24 | User-Agent | user_agent | varchar(255) | YES | - | UA của client |
| 25 | Session ID | mcp_session_id | varchar(64) | YES | IDX | Session Streamable HTTP |
| 26 | Created at | created_at | timestamp(3) | NO | IDX | Độ chính xác mili-giây (để sắp xếp trong cùng giây) |
Index
| Tên | Cột | Mục đích |
|---|---|---|
idx_audit_user_created | (user_id, created_at) | Tìm theo thời gian theo user |
idx_audit_target_created | (target_user_id, created_at) | Audit thao tác thay mặt |
idx_audit_client_created | (client_id, created_at) | Phân tích sử dụng theo client |
idx_audit_tool_created | (tool_name, created_at) | Phân tích sử dụng theo tool |
idx_audit_status_created | (status, created_at) | Tổng hợp thất bại・từ chối |
idx_audit_batch | (batch_id) | Tổng hợp theo batch |
idx_audit_idempotency | (idempotency_key) | Phát hiện duplicate |
idx_audit_session | (mcp_session_id, created_at) | Trace theo session |
Partitioning
- Partition tháng theo created_at (phân tán tải ghi・dễ archive log cũ)
- Vận hành dự kiến: archive partition đã quá 1 năm sang DB khác / S3
mappy_mcp_batches
Bảng quản lý trạng thái xử lý bất đồng bộ (write tool). Là nguồn tham chiếu của tool batch.status.
| No | Tên cột (logical) | Tên cột (physical) | Kiểu | NULL | Key | Mô tả |
|---|---|---|---|---|---|---|
| 1 | ID | id | bigint unsigned | NO | PK | Auto increment |
| 2 | Batch ID | batch_id | varchar(64) | NO | UK | ULID. Trả về client |
| 3 | Client ID | client_id | varchar(64) | NO | IDX, FK | Client phát hành |
| 4 | User ID | user_id | bigint unsigned | NO | IDX, FK | User phát hành |
| 5 | User đối tượng ID | target_user_id | bigint unsigned | NO | - | Cho thao tác thay mặt |
| 6 | Tên tool | tool_name | varchar(128) | NO | - | post.create v.v. |
| 7 | Status | status | varchar(32) | NO | IDX | queued / processing / done / partial / failed / cancelled |
| 8 | JSON input | input_json | json | NO | - | Input gốc (đã cân nhắc PII) |
| 9 | Số lượng đối tượng | target_count | int unsigned | NO | - | Số location_ids v.v. |
| 10 | Số thành công | success_count | int unsigned | NO | - | Mặc định 0 |
| 11 | Số thất bại | failure_count | int unsigned | NO | - | Mặc định 0 |
| 12 | Chi tiết thất bại | failures | json | YES | - | error_code/message theo location |
| 13 | Thời điểm queue | queued_at | timestamp | NO | - | Lúc dispatch |
| 14 | Thời điểm bắt đầu | started_at | timestamp | YES | - | Lúc Worker pull |
| 15 | Thời điểm hoàn thành | completed_at | timestamp | YES | - | Lúc chốt status cuối |
| 16 | Job ID hiện có | job_id | bigint unsigned | YES | - | jobs.id (Laravel Jobs queue) |
| 17 | Idempotency key | idempotency_key | varchar(64) | YES | IDX | Client cung cấp |
| 18 | Progress (%) | progress | tinyint unsigned | NO | - | 0〜100 |
| 19 | Created at | created_at | timestamp | NO | - | Thời điểm tạo record |
| 20 | Updated at | updated_at | timestamp | NO | - | Thời điểm update cuối |
Index
| Tên | Cột |
|---|---|
idx_batches_user_status | (user_id, status) |
idx_batches_client_created | (client_id, created_at) |
idx_batches_idempotency | (user_id, idempotency_key) |
mappy_mcp_rate_limit_logs (tùy chọn・bổ trợ Redis)
Lịch sử hit rate limit. Counter và trạng thái ngắn hạn ở Redis, ghi persistent ở bảng này.
| No | Cột | Kiểu | Mô tả |
|---|---|---|---|
| 1 | id | bigint unsigned PK | Auto increment |
| 2 | scope | varchar(64) | user_per_min / client_per_hour v.v. |
| 3 | client_id | varchar(64) | Client tương ứng |
| 4 | user_id | bigint unsigned | User tương ứng |
| 5 | tool_name | varchar(128) | Tool tương ứng |
| 6 | limit_value | int | Giá trị giới hạn |
| 7 | observed_count | int | Count quan sát |
| 8 | retry_after_seconds | int | retry-after trả cho client |
| 9 | created_at | timestamp | Thời điểm phát sinh |
Redis vs DB
- Counter・trạng thái gần đây ở Redis (nhanh, INCR + EXPIRE)
- DB dùng cho persist và phân tích lịch sử hit (ghi async)
Phương châm ghi tóm tắt input
input_summary không bao gồm PII, chỉ ghi các điểm chính.
Spec tóm tắt theo từng tool
| tool | Bao gồm trong input_summary |
|---|---|
location.list | user_id, group_id, limit, has_filter |
location.get | location_id, include_raw |
review.list | user_id, location_id, since, until, has_filter |
ranking.list | user_id, location_id, since, until, aggregate, keyword (truncated 50 chars) |
insight.summary | user_id, location_id/group_id, since, until, metrics |
post.create | location_ids, target_count, has_event, has_cta, has_media, dry_run |
review.reply | review_id, reply_length |
media.upload | location_ids, target_count, media_count, total_bytes |
report.create | template, period, group_id, location_id, include_ai_advice |
Quy tắc loại trừ PII
| Mục | Cách xử lý |
|---|---|
Body post (summary) | Không ghi (chỉ ghi 50 ký tự đầu vào input_summary) |
Body trả lời review (reply) | Không ghi (chỉ ghi số ký tự) |
Bản thân media (media_urls) | Chỉ ghi domain của URL |
| Keyword có thể chứa thông tin cá nhân | Không ghi |
| Access token / refresh_token / code | Tuyệt đối không ghi |
Không bao gồm token trong audit log
Chỉ ghi jti của JWT, cấm ghi token body. Đối phó rủi ro rò rỉ.
Ví dụ query tổng hợp cho report
① Tình hình sử dụng theo client (30 ngày gần nhất)
SELECT
client_id,
COUNT(*) AS total_calls,
SUM(status = 'success') AS success_calls,
SUM(status = 'forbidden') AS forbidden_calls,
SUM(status = 'rate_limited') AS rate_limited_calls,
AVG(elapsed_ms) AS avg_elapsed_ms
FROM mappy_mcp_audit_logs
WHERE event_type = 'tool_call'
AND created_at >= NOW() - INTERVAL 30 DAY
GROUP BY client_id
ORDER BY total_calls DESC;② Ranking sử dụng theo tool
SELECT
tool_name,
COUNT(*) AS calls,
COUNT(DISTINCT user_id) AS unique_users,
AVG(elapsed_ms) AS avg_elapsed_ms,
MAX(elapsed_ms) AS max_elapsed_ms
FROM mappy_mcp_audit_logs
WHERE event_type = 'tool_call'
AND created_at >= NOW() - INTERVAL 30 DAY
AND status = 'success'
GROUP BY tool_name
ORDER BY calls DESC;③ Phát hiện thao tác thay mặt
SELECT
request_id,
user_kind,
admin_id,
target_user_id,
tool_name,
created_at
FROM mappy_mcp_audit_logs
WHERE user_kind = 'admin'
AND target_user_id IS NOT NULL
AND created_at >= NOW() - INTERVAL 7 DAY
ORDER BY created_at DESC;④ Tổng hợp thất bại write
SELECT
tool_name,
error_code,
COUNT(*) AS occurrences,
MIN(created_at) AS first_seen,
MAX(created_at) AS last_seen
FROM mappy_mcp_audit_logs
WHERE status IN ('failed', 'partial')
AND event_type = 'tool_call'
AND created_at >= NOW() - INTERVAL 7 DAY
GROUP BY tool_name, error_code
ORDER BY occurrences DESC;⑤ Tỷ lệ hit rate limit
SELECT
client_id,
user_id,
COUNT(*) AS rate_hits
FROM mappy_mcp_audit_logs
WHERE status = 'rate_limited'
AND created_at >= NOW() - INTERVAL 24 HOUR
GROUP BY client_id, user_id
HAVING rate_hits >= 10
ORDER BY rate_hits DESC;Đối tượng alert (CloudWatch Alarms v.v.)
| Điều kiện | Mức thông báo |
|---|---|
| 5 phút có ≥ 10 forbidden của cùng user_id | WARNING |
| 5 phút có ≥ 50 rate_limited của cùng client_id | WARNING |
| 1 giờ có ≥ 100 thao tác thay mặt của admin | CRITICAL |
| ≥ 10 xung đột cùng idempotency_key | WARNING |
| Tỷ lệ phát OAuth token thất bại > 10% | CRITICAL |
| Trung bình elapsed_ms > 3000ms (trung bình 30 phút) | WARNING |
Giữ・archive data
| Loại data | Thời gian giữ | Nơi archive |
|---|---|---|
| audit_logs (read) | 1 năm | S3 Glacier |
| audit_logs (write) | 3 năm | S3 Glacier |
| batches | 3 tháng (done/failed) | Xóa |
| batches (cancelled/partial) | 1 năm | S3 |
| rate_limit_logs | 90 ngày | Xóa |
Vận hành dạng archive partition tháng nguyên si, xóa bằng DROP PARTITION.
Ước lượng dung lượng
Giá trị giả định
| Mục | Giá trị |
|---|---|
| Số client active Phase 1 | 100 |
| Số tool call / client / ngày | 100 |
| Size 1 record | Khoảng 1 KB (bao gồm input_summary) |
→ 10,000 record/ngày = khoảng 10 MB → 300,000 record/tháng = khoảng 300 MB → 3,650,000 record/năm = khoảng 3.6 GB
Phase 2/3 write tăng thì dự kiến gấp đôi. Vận hành partition tháng là đủ.
DDL (tham khảo)
CREATE TABLE mappy_mcp_audit_logs (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
request_id VARCHAR(64) NOT NULL,
event_type VARCHAR(64) NOT NULL,
client_id VARCHAR(64) DEFAULT NULL,
user_id BIGINT UNSIGNED DEFAULT NULL,
user_kind VARCHAR(16) DEFAULT NULL,
admin_id BIGINT UNSIGNED DEFAULT NULL,
target_user_id BIGINT UNSIGNED DEFAULT NULL,
tool_name VARCHAR(128) DEFAULT NULL,
scope_required VARCHAR(255) DEFAULT NULL,
scope_granted TEXT DEFAULT NULL,
input_hash CHAR(64) DEFAULT NULL,
input_summary JSON DEFAULT NULL,
status VARCHAR(32) NOT NULL,
error_code VARCHAR(64) DEFAULT NULL,
error_message TEXT DEFAULT NULL,
result_count INT DEFAULT NULL,
batch_id VARCHAR(64) DEFAULT NULL,
idempotency_key VARCHAR(64) DEFAULT NULL,
confirm_token VARCHAR(64) DEFAULT NULL,
is_dry_run TINYINT(1) NOT NULL DEFAULT 0,
elapsed_ms INT UNSIGNED DEFAULT NULL,
request_ip VARCHAR(45) DEFAULT NULL,
user_agent VARCHAR(255) DEFAULT NULL,
mcp_session_id VARCHAR(64) DEFAULT NULL,
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id, created_at),
UNIQUE KEY uk_request_id (request_id),
KEY idx_audit_user_created (user_id, created_at),
KEY idx_audit_target_created (target_user_id, created_at),
KEY idx_audit_client_created (client_id, created_at),
KEY idx_audit_tool_created (tool_name, created_at),
KEY idx_audit_status_created (status, created_at),
KEY idx_audit_batch (batch_id),
KEY idx_audit_idempotency (idempotency_key),
KEY idx_audit_session (mcp_session_id, created_at)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COMMENT='MCP Server audit log'
PARTITION BY RANGE (UNIX_TIMESTAMP(created_at)) (
PARTITION p202606 VALUES LESS THAN (UNIX_TIMESTAMP('2026-07-01')),
PARTITION p202607 VALUES LESS THAN (UNIX_TIMESTAMP('2026-08-01')),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
CREATE TABLE mappy_mcp_batches (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
batch_id VARCHAR(64) NOT NULL,
client_id VARCHAR(64) NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
target_user_id BIGINT UNSIGNED NOT NULL,
tool_name VARCHAR(128) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'queued',
input_json JSON NOT NULL,
target_count INT UNSIGNED NOT NULL,
success_count INT UNSIGNED NOT NULL DEFAULT 0,
failure_count INT UNSIGNED NOT NULL DEFAULT 0,
failures JSON DEFAULT NULL,
queued_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
started_at TIMESTAMP NULL DEFAULT NULL,
completed_at TIMESTAMP NULL DEFAULT NULL,
job_id BIGINT UNSIGNED DEFAULT NULL,
idempotency_key VARCHAR(64) DEFAULT NULL,
progress TINYINT UNSIGNED NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uk_batch_id (batch_id),
KEY idx_batches_user_status (user_id, status),
KEY idx_batches_client_created (client_id, created_at),
KEY idx_batches_idempotency (user_id, idempotency_key)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COMMENT='MCP quản lý batch bất đồng bộ';Điểm chưa chốt (quyết định tại review design)
| # | Mục | Phương án |
|---|---|---|
| 1 | Đơn vị partition | Tháng / Tuần (tùy tải ghi) |
| 2 | Thời gian giữ | Chuẩn 1 năm / kéo dài tùy yêu cầu pháp lý |
| 3 | Nơi archive | S3 Glacier / Cho phép search bằng Athena |
| 4 | Size tối đa input_summary | 1 KB / 4 KB |
| 5 | Có tổng hợp realtime không | Có cần dashboard Redash v.v. không |
| 6 | Trách nhiệm filter PII | MCP Server / Laravel API |
Tài liệu liên quan
- MCP Server (Tổng thể)
- Auth & Tenancy — Mục ghi authz thành công・thất bại
- Cơ chế an toàn write — Liên kết với bảng batches
- DB (OAuth client) — Nguồn tham chiếu client_id