Skip to content

MCP Server — Tool Catalog

MụcNộ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 chaMCP Server (Tổng thể)
Design liên quanAuth & Tenancy / Cơ chế an toàn write

1. Mục đích tài liệu

Tổng hợp danh sách spec của toàn bộ tool mà MCP Server cung cấp. Cho từng tool sẽ chốt các mục sau:

  • Tên・mô tả
  • Scope cần
  • Schema input (kiểu・bắt buộc/tùy chọn・ràng buộc)
  • Schema output
  • Side effect (read/write/async)
  • Hành vi khi lỗi

Định nghĩa language-independent, không phụ thuộc stack triển khai (Python / TypeScript). Schema viết theo phong cách JSON Schema (Python convert sang Pydantic, TS sang Zod).

2. Quy tắc đặt tên tool

<domain>.<action>
PhầnVí dụ
<domain>location / posts / reviews / ranking / insight / report / keyword / group / media / sms / questionnaire / sns / notification / batch
<action>list / get / create / update / delete / summary / status

Ví dụ: location.list, review.reply, report.create, batch.status

3. Spec chung

3.1 Tham số input chung

Tất cả tool đều chấp nhận các input optional sau:

ParamKiểuMô tả
user_idint?ID user đối tượng (lược đi = user đang auth). Chỉ supervisor admin được chỉ định tùy ý
dry_runbool?true = không thực thi, trả về tóm tắt side effect dự kiến (chỉ write tool)
idempotency_keystring?Khuyến nghị UUID. Cùng key bị từ chối duplicate trong 24h (chỉ write tool)

3.2 Wrapper output chung

json
{
  "data": <payload riêng của tool>,
  "meta": {
    "request_id": "01HVABC...",
    "elapsed_ms": 123,
    "next_cursor": "..." // khi paging
  }
}

3.3 Paging

Dùng kiểu cursor (không phải offset/limit). Lý do: ổn định ngay cả với bảng lớn (mappy_search_rankings 10 triệu dòng).

ParamKiểuMô tả
cursorstring?next_cursor từ response trang trước
limitint?1〜100, mặc định 50

meta.next_cursor của response không null = còn trang tiếp.

3.4 Format lỗi

Trả isError: true của MCP, đặt chi tiết lỗi dạng JSON vào content[0].text:

json
{
  "error_code": "FORBIDDEN_LOCATION",
  "message": "Không có quyền truy cập location đối tượng",
  "details": {
    "location_id": 8802,
    "user_id": 1435
  }
}

4. Ma trận danh sách tool

Chú thích:

  • Side effect: 🔵 Read / 🟠 Write đồng bộ / 🔴 Write bất đồng bộ
  • Phase: 1/2/3
  • DB rate: bảng dự kiến tải cao

Phase 1 (MVP) — Auth + Read

#Tên toolSide effectscopeDB tham chiếu chínhMô tả
1location.list🔵mappy:location:readmappy_gbp_locations + mappy_user_available_gbp_locationsDanh sách location có thể access
2location.get🔵mappy:location:readmappy_gbp_locationsChi tiết location (bao gồm raw_json)
3review.list🔵mappy:reviews:readmappy_gbp_reviews (280k dòng)Danh sách review
4ranking.list🔵mappy:ranking:readmappy_search_rankings (10M dòng)Xếp hạng tìm kiếm
5insight.summary🔵mappy:insight:readmappy_gbp_insights (4.2M dòng)Tổng hợp insight

Phase 2 — Mở rộng write

#Tên toolSide effectscopeMô tả
6post.list🔵mappy:posts:readDanh sách post
7post.create🔴mappy:posts:writeTạo post (support nhiều cửa hàng cùng lúc)
8post.delete🟠mappy:posts:writeXóa post
9review.reply🟠mappy:reviews:writeTrả lời review
10review.template.list🔵mappy:reviews:readDanh sách template reply
11review.template.create🟠mappy:reviews:writeĐăng ký template reply
12media.list🔵mappy:media:readDanh sách media
13media.upload🔴mappy:media:writeUpload media (support hàng loạt)
14media.delete🟠mappy:media:deleteXóa media
15location.update🟠mappy:location:writeUpdate thuộc tính location
16batch.status🔵(Phase 1〜chỉ cần auth)Kiểm tra trạng thái xử lý bất đồng bộ

Phase 3 — Báo cáo・Phân tích

#Tên toolSide effectscopeMô tả
17report.list🔵mappy:report:readDanh sách report
18report.get🔵mappy:report:readLấy report (HTML / PDF path)
19report.create🔴mappy:report:writeYêu cầu tạo report
20keyword.list🔵mappy:keyword:readDanh sách từ khóa・lưu lượng
21group.list🔵mappy:group:readDanh sách group
22group.compare🔵mappy:group:read + mappy:ranking:readSo sánh ranking・insight giữa các group
23questionnaire.list🔵mappy:questionnaire:readDanh sách khảo sát
24questionnaire.answers🔵mappy:questionnaire:readLấy câu trả lời
25sms.list🔵mappy:sms:readLog SMS

5. Chi tiết tool Phase 1

5.1 location.list

Trả về danh sách location mà user có quyền access.

scope: mappy:location:readSide effect: 🔵 Read DB: mappy_gbp_locations JOIN mappy_user_available_gbp_locations

Schema input:

json
{
  "type": "object",
  "properties": {
    "user_id": { "type": "integer", "description": "ID user đối tượng (lược đi = user auth)" },
    "group_id": { "type": "integer", "description": "Lọc theo group" },
    "store_code": { "type": "string", "description": "Lọc khớp tuyệt đối theo store code" },
    "q": { "type": "string", "description": "Tìm khớp một phần theo tên cửa hàng" },
    "cursor": { "type": "string" },
    "limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
  }
}

Schema output:

json
{
  "data": {
    "locations": [
      {
        "id": 8802,
        "name": "locations/8802235673275682965",
        "title": "Shibuya",
        "address": "Tokyo Shibuya-ku...",
        "store_code": "SHIBUYA001",
        "primary_category": "Tiệm tóc",
        "data_source": "places_api_app",
        "new_review_uri": "https://..."
      }
    ]
  },
  "meta": { "next_cursor": "...", "request_id": "...", "elapsed_ms": 123 }
}

Lỗi:

codeĐiều kiện
FORBIDDEN_USERtarget_user_id ngoài phân tầng
INVALID_GROUPgroup_id không thuộc group của user đối tượng

5.2 location.get

Trả về thông tin chi tiết location. Bao gồm GBP raw_json (khi yêu cầu).

scope: mappy:location:readSide effect: 🔵 Read

Input:

json
{
  "type": "object",
  "required": ["location_id"],
  "properties": {
    "location_id": { "type": "integer" },
    "include_raw": { "type": "boolean", "default": false, "description": "Bao gồm GBP raw_json (lớn nên chỉ khi yêu cầu rõ ràng)" }
  }
}

Output:

json
{
  "data": {
    "id": 8802,
    "name": "locations/8802235673275682965",
    "title": "Shibuya",
    "address": "...",
    "primary_category": "Tiệm tóc",
    "categories": ["Tiệm tóc", "Hair salon"],
    "phone_numbers": ["03-1234-5678"],
    "website_uri": "https://...",
    "regular_hours": { ... },
    "latlng": { "latitude": 35.65, "longitude": 139.70 },
    "data_source": "places_api_app",
    "raw": { ... }  // chỉ khi include_raw=true
  }
}

Lỗi:

codeĐiều kiện
FORBIDDEN_LOCATIONUser đối tượng không access được location_id
NOT_FOUNDlocation_id không tồn tại

5.3 review.list

Trả về danh sách review.

scope: mappy:reviews:readSide effect: 🔵 Read DB: mappy_gbp_reviews (280k dòng, khuyến nghị bắt buộc lọc theo kỳ)

Input:

json
{
  "type": "object",
  "properties": {
    "user_id": { "type": "integer" },
    "location_id": { "type": "integer", "description": "Không chỉ định = tất cả cửa hàng có quyền" },
    "since": { "type": "string", "format": "date-time", "description": "ISO8601, mặc định 30 ngày trước" },
    "until": { "type": "string", "format": "date-time" },
    "min_rating": { "type": "integer", "minimum": 1, "maximum": 5 },
    "max_rating": { "type": "integer", "minimum": 1, "maximum": 5 },
    "has_reply": { "type": "boolean" },
    "cursor": { "type": "string" },
    "limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
  }
}

Output:

json
{
  "data": {
    "reviews": [
      {
        "id": 12345,
        "location_id": 8802,
        "reviewer_name": "Yamada Taro",
        "rating": 5,
        "comment": "...",
        "reply_comment": "...",
        "create_time": "2026-05-01T10:00:00Z",
        "update_time": "2026-05-02T09:00:00Z"
      }
    ]
  },
  "meta": { "next_cursor": "...", ... }
}

Ràng buộc kỳ

Kỳ quá 90 ngày cần xác nhận rõ ràng (đối phó tải). Không chỉ định = 30 ngày trước.

5.4 ranking.list

Trả về dữ liệu xếp hạng tìm kiếm.

scope: mappy:ranking:read + mappy_users.search_ranking_enabled = 1Side effect: 🔵 Read DB: mappy_search_rankings (10M dòng, bắt buộc lọc theo kỳ)

Input:

json
{
  "type": "object",
  "required": ["since", "until"],
  "properties": {
    "user_id": { "type": "integer" },
    "location_id": { "type": "integer" },
    "keyword": { "type": "string" },
    "since": { "type": "string", "format": "date-time" },
    "until": { "type": "string", "format": "date-time" },
    "aggregate": { "type": "string", "enum": ["daily", "weekly", "raw"], "default": "daily" },
    "cursor": { "type": "string" },
    "limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
  }
}

Ràng buộc bắt buộc

  • since / until bắt buộc
  • Kỳ tối đa 365 ngày
  • Khi không chỉ định location_id, trả tất cả cửa hàng của user, nhưng tối đa 100 cửa hàng

Output:

json
{
  "data": {
    "rankings": [
      {
        "location_id": 8802,
        "keyword": "tiệm tóc Shibuya",
        "date": "2026-05-01",
        "rank": 3,
        "search_volume": 1234
      }
    ],
    "summary": {
      "avg_rank": 3.2,
      "best_rank": 1,
      "worst_rank": 8,
      "total_records": 1234
    }
  },
  "meta": { ... }
}

5.5 insight.summary

Trả về tổng hợp GBP insight.

scope: mappy:insight:readSide effect: 🔵 Read DB: mappy_gbp_insights (4.2M dòng)

Input:

json
{
  "type": "object",
  "required": ["since", "until"],
  "properties": {
    "user_id": { "type": "integer" },
    "location_id": { "type": "integer" },
    "group_id": { "type": "integer", "description": "Tổng hợp theo group" },
    "since": { "type": "string", "format": "date" },
    "until": { "type": "string", "format": "date" },
    "metrics": {
      "type": "array",
      "items": { "type": "string", "enum": ["views", "actions_website", "actions_phone", "actions_directions", "search_count"] },
      "description": "Không chỉ định = tất cả metric"
    },
    "aggregate": { "type": "string", "enum": ["daily", "weekly", "monthly", "total"], "default": "monthly" }
  }
}

Output:

json
{
  "data": {
    "summary": {
      "period": { "since": "2026-04-01", "until": "2026-04-30" },
      "metrics": {
        "views": 15234,
        "actions_website": 1234,
        "actions_phone": 456,
        "actions_directions": 789,
        "search_count": 2345
      },
      "previous_period_diff": {
        "views": { "value": 1820, "percent": 13.6 }
      }
    },
    "series": [
      { "date": "2026-04-01", "views": 500, "actions_website": 40 }
    ]
  }
}

6. Chi tiết tool Phase 2 (trích)

6.1 post.create

Tạo post (hỗ trợ nhiều cửa hàng cùng lúc, bất đồng bộ).

scope: mappy:posts:writeSide effect: 🔴 Write bất đồng bộ DB: qua Jobs queue hiện có Giá trị trả: batch_id → check status bằng batch.status

Input:

json
{
  "type": "object",
  "required": ["location_ids", "post"],
  "properties": {
    "location_ids": {
      "type": "array",
      "items": { "type": "integer" },
      "minItems": 1,
      "maxItems": 100,
      "description": "Cửa hàng đối tượng hàng loạt"
    },
    "post": {
      "type": "object",
      "required": ["summary"],
      "properties": {
        "summary": { "type": "string", "maxLength": 1500 },
        "topic_type": { "type": "string", "enum": ["STANDARD", "EVENT", "OFFER"] },
        "event": {
          "type": "object",
          "properties": {
            "title": { "type": "string", "maxLength": 58 },
            "start_time": { "type": "string", "format": "date-time" },
            "end_time": { "type": "string", "format": "date-time" }
          }
        },
        "call_to_action": {
          "type": "object",
          "properties": {
            "action_type": { "type": "string", "enum": ["BOOK", "ORDER", "SHOP", "LEARN_MORE", "SIGN_UP", "CALL"] },
            "url": { "type": "string", "format": "uri" }
          }
        },
        "media_urls": { "type": "array", "items": { "type": "string", "format": "uri" } }
      }
    },
    "dry_run": { "type": "boolean", "default": false },
    "idempotency_key": { "type": "string" },
    "confirm_token": { "type": "string", "description": "Bắt buộc khi đăng hàng loạt > 5 cửa hàng (xem cơ chế an toàn write)" }
  }
}

Output (dry_run=false):

json
{
  "data": {
    "batch_id": "01HVABC...",
    "status": "processing",
    "queued_at": "2026-05-25T10:00:00Z",
    "target_count": 12,
    "estimated_completion_seconds": 60
  }
}

Output (dry_run=true):

json
{
  "data": {
    "would_execute": true,
    "target_locations": [...],
    "validation_warnings": [
      { "location_id": 8802, "warning": "Chưa đến 1 giờ kể từ post trước" }
    ]
  }
}

Trường hợp bắt buộc confirm_token

6.2 review.reply

Trả lời review.

scope: mappy:reviews:writeSide effect: 🟠 Write đồng bộ (qua GBP API) DB: mappy_gbp_reviews

Input:

json
{
  "type": "object",
  "required": ["review_id", "reply"],
  "properties": {
    "review_id": { "type": "integer" },
    "reply": { "type": "string", "maxLength": 4000 },
    "dry_run": { "type": "boolean", "default": false },
    "idempotency_key": { "type": "string" }
  }
}

Output:

json
{
  "data": {
    "review_id": 12345,
    "reply_comment": "...",
    "reply_at": "2026-05-25T10:00:01Z"
  }
}

6.3 batch.status

Kiểm tra trạng thái xử lý bất đồng bộ (post.create / media.upload v.v.).

scope: Chỉ cần auth cơ bản (không cần scope riêng, nhưng phải khớp user phát hành batch) Side effect: 🔵 Read

Input:

json
{
  "type": "object",
  "required": ["batch_id"],
  "properties": {
    "batch_id": { "type": "string" }
  }
}

Output:

json
{
  "data": {
    "batch_id": "01HVABC...",
    "status": "done", // queued / processing / done / failed / partial
    "queued_at": "...",
    "started_at": "...",
    "completed_at": "...",
    "target_count": 12,
    "success_count": 11,
    "failure_count": 1,
    "failures": [
      { "location_id": 8802, "error_code": "GBP_PERMISSION_DENIED", "message": "..." }
    ]
  }
}

7. Chi tiết tool Phase 3 (trích)

7.1 report.create

Yêu cầu tạo report.

scope: mappy:report:writeSide effect: 🔴 Write bất đồng bộ DB: Sử dụng pattern status/progress nội tại của mappy_reports

Input:

json
{
  "type": "object",
  "required": ["template", "period"],
  "properties": {
    "user_id": { "type": "integer" },
    "location_id": { "type": "integer" },
    "group_id": { "type": "integer" },
    "template": { "type": "string", "enum": ["monthly", "yearly"] },
    "period": {
      "type": "object",
      "required": ["since", "until"],
      "properties": {
        "since": { "type": "string", "format": "date" },
        "until": { "type": "string", "format": "date" }
      }
    },
    "include_ai_advice": { "type": "boolean", "default": true },
    "output_format": { "type": "string", "enum": ["html", "pdf"], "default": "html" }
  }
}

Output:

json
{
  "data": {
    "report_id": 59,
    "status": "queued",
    "estimated_completion_seconds": 30
  }
}

Check trạng thái hoàn thành bằng report.get.

7.2 report.get

Lấy report.

scope: mappy:report:readSide effect: 🔵 Read

Input:

json
{
  "type": "object",
  "required": ["report_id"],
  "properties": {
    "report_id": { "type": "integer" }
  }
}

Output:

json
{
  "data": {
    "report_id": 59,
    "status": "done", // pending / processing / done / failed
    "progress": 100,
    "file_url": "https://...?signed=...",
    "format": "html",
    "generated_at": "2026-05-25T11:00:00Z",
    "ai_advice": "..."
  }
}

7.3 group.compare

Trả về dữ liệu so sánh giữa các group.

scope: mappy:group:read + mappy:ranking:read + mappy:insight:readSide effect: 🔵 Read

Input:

json
{
  "type": "object",
  "required": ["group_ids", "since", "until"],
  "properties": {
    "group_ids": { "type": "array", "items": { "type": "integer" }, "minItems": 2, "maxItems": 10 },
    "since": { "type": "string", "format": "date" },
    "until": { "type": "string", "format": "date" },
    "metrics": { "type": "array", "items": { "type": "string", "enum": ["avg_rank", "views", "actions"] } }
  }
}

8. Rate limit・Bảo vệ resource

toolGiới hạn đặc biệt
ranking.listTừ chối kỳ > 365 ngày
review.listKỳ > 90 ngày cần xác nhận rõ ràng
post.createlocation_ids >= 5 thì bắt buộc confirm_token
media.uploadTối đa 10 media / request
report.createTối đa 5 lần / giờ / user
batch.statusKhoảng polling khuyến nghị 5 giây, tối đa 1 req/sec

9. Giới hạn payload

LoạiGiới hạn
Kích thước JSON input256 KB
Kích thước JSON output1 MB (vượt thì paging)
Output bao gồm raw_json5 MB
Số lượng đối tượng hàng loạt (location_ids)100

10. Cách viết tool description

Để LLM dễ chọn tool, description nên bao gồm:

  1. Làm gì (1 dòng)
  2. Khi nào dùng (1〜2 dòng, kịch bản sử dụng)
  3. Điểm chính của input bắt buộc・tùy chọn
  4. Có side effect không

Ví dụ:

location.list: Trả về danh sách cửa hàng mà user có quyền access.
Dùng để tìm kiếm cửa hàng, lọc, xem theo group.
Không side effect. Lược user_id = trả cửa hàng của user auth.

11. Điểm chưa chốt (quyết định tại review design)

#MụcPhương án
1Đặt tên tool: dot vs underscorelocation.list / location_list (spec MCP cho phép cả 2)
2Có mã hóa cursor khôngbase64(JSON) / mã hóa / opaque hoàn toàn
3Filter khi output raw_jsonCó filter loại trừ thông tin nhạy cảm không
4Ngôn ngữ tool descriptionTiếng Nhật / Tiếng Anh / cả 2
5Kích thước tối đa xử lý hàng loạt (100 vs 500)Tùy tải DB / GBP API
6Hạn signed URL của report.get1h / 24h / 7d
7Giới hạn group_ids của group.compare5 / 10 / 20
8Thêm metrics (CTR v.v.)Thêm từ Phase 3 trở đi

12. Tài liệu liên quan