MCP サーバー ツールカタログ
| 項目 | 内容 |
|---|---|
| ステータス | 🟡 設計中 |
| 関連案件 | #13 Mappy MCP サーバー新設 |
| 親ドキュメント | MCP サーバー 全体像 |
| 関連設計 | 認証・テナンシ / 書込安全装置 |
1. 本ドキュメントの目的
MCP サーバーが提供する全 tool の仕様を一覧化する。 各 tool について以下を確定する:
- 名前・説明
- 必要 scope
- 入力スキーマ(型・必須/任意・制約)
- 出力スキーマ
- 副作用(読取/書込/非同期)
- エラー時の挙動
実装スタック(Python / TypeScript)に依存しない言語非依存の定義とする。 スキーマは JSON Schema 風で記述(Python なら Pydantic、TS なら Zod に変換)。
2. ツール命名規則
<domain>.<action>| 部分 | 例 |
|---|---|
<domain> | location / posts / reviews / ranking / insight / report / keyword / group / media / sms / questionnaire / sns / notification / batch |
<action> | list / get / create / update / delete / summary / status |
例: location.list, review.reply, report.create, batch.status
3. 共通仕様
3.1 共通入力パラメータ
すべての tool で以下のオプション入力を許容する:
| パラメータ | 型 | 説明 |
|---|---|---|
user_id | int? | 対象ユーザー ID(省略時は認証ユーザー自身)。supervisor admin のみ任意指定可 |
dry_run | bool? | true で実行せず、想定される副作用のサマリーを返す(書込系のみ) |
idempotency_key | string? | UUID 推奨。同一キーは 24h 重複拒否(書込系のみ) |
3.2 共通出力ラッパー
{
"data": <tool 固有のペイロード>,
"meta": {
"request_id": "01HVABC...",
"elapsed_ms": 123,
"next_cursor": "..." // ページング時
}
}3.3 ページング
カーソル方式を採用(offset/limit ではなく)。理由:大規模テーブル(mappy_search_rankings 1,000 万行)でも安定動作するため。
| パラメータ | 型 | 説明 |
|---|---|---|
cursor | string? | 前ページレスポンスの next_cursor |
limit | int? | 1〜100、デフォルト 50 |
レスポンスの meta.next_cursor が null でないなら次ページあり。
3.4 エラー形式
MCP の isError: true を返し、content[0].text に JSON でエラー詳細を載せる:
{
"error_code": "FORBIDDEN_LOCATION",
"message": "対象ロケーションへのアクセス権限がありません",
"details": {
"location_id": 8802,
"user_id": 1435
}
}4. ツール一覧マトリクス
凡例:
- 副作用: 🔵 読取 / 🟠 書込同期 / 🔴 書込非同期
- Phase: 1/2/3
- DB レート: 高負荷想定テーブル
Phase 1(MVP)— 認証 + 読取系
| # | tool 名 | 副作用 | scope | DB 主参照 | 説明 |
|---|---|---|---|---|---|
| 1 | location.list | 🔵 | mappy:location:read | mappy_gbp_locations + mappy_user_available_gbp_locations | アクセス可能なロケーション一覧 |
| 2 | location.get | 🔵 | mappy:location:read | mappy_gbp_locations | ロケーション詳細(raw_json 含む) |
| 3 | review.list | 🔵 | mappy:reviews:read | mappy_gbp_reviews (28 万行) | 口コミ一覧 |
| 4 | ranking.list | 🔵 | mappy:ranking:read | mappy_search_rankings (1,000 万行) | 検索順位 |
| 5 | insight.summary | 🔵 | mappy:insight:read | mappy_gbp_insights (420 万行) | インサイト集計 |
Phase 2 — 書込系拡張
| # | tool 名 | 副作用 | scope | 説明 |
|---|---|---|---|---|
| 6 | post.list | 🔵 | mappy:posts:read | 投稿一覧 |
| 7 | post.create | 🔴 | mappy:posts:write | 投稿作成(複数店舗一括対応) |
| 8 | post.delete | 🟠 | mappy:posts:write | 投稿削除 |
| 9 | review.reply | 🟠 | mappy:reviews:write | 口コミ返信 |
| 10 | review.template.list | 🔵 | mappy:reviews:read | 返信テンプレート一覧 |
| 11 | review.template.create | 🟠 | mappy:reviews:write | 返信テンプレート登録 |
| 12 | media.list | 🔵 | mappy:media:read | メディア一覧 |
| 13 | media.upload | 🔴 | mappy:media:write | メディアアップロード(一括対応) |
| 14 | media.delete | 🟠 | mappy:media:delete | メディア削除 |
| 15 | location.update | 🟠 | mappy:location:write | ロケーション属性更新 |
| 16 | batch.status | 🔵 | (Phase 1〜認証済み) | 非同期処理の状態確認 |
Phase 3 — レポート・分析
| # | tool 名 | 副作用 | scope | 説明 |
|---|---|---|---|---|
| 17 | report.list | 🔵 | mappy:report:read | レポート一覧 |
| 18 | report.get | 🔵 | mappy:report:read | レポート取得(HTML / PDF パス) |
| 19 | report.create | 🔴 | mappy:report:write | レポート生成リクエスト |
| 20 | keyword.list | 🔵 | mappy:keyword:read | キーワード一覧・流入 |
| 21 | group.list | 🔵 | mappy:group:read | グループ一覧 |
| 22 | group.compare | 🔵 | mappy:group:read + mappy:ranking:read | グループ間の順位・インサイト比較 |
| 23 | questionnaire.list | 🔵 | mappy:questionnaire:read | アンケート一覧 |
| 24 | questionnaire.answers | 🔵 | mappy:questionnaire:read | 回答取得 |
| 25 | sms.list | 🔵 | mappy:sms:read | SMS ログ |
5. Phase 1 ツール詳細
5.1 location.list
ユーザーがアクセス可能なロケーション一覧を返す。
scope: mappy:location:read副作用: 🔵 読取 DB: mappy_gbp_locations JOIN mappy_user_available_gbp_locations
入力スキーマ:
{
"type": "object",
"properties": {
"user_id": { "type": "integer", "description": "対象ユーザー ID(省略時は認証ユーザー)" },
"group_id": { "type": "integer", "description": "グループで絞り込み" },
"store_code": { "type": "string", "description": "店舗コードで完全一致絞り込み" },
"q": { "type": "string", "description": "店舗名の部分一致検索" },
"cursor": { "type": "string" },
"limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
}
}出力スキーマ:
{
"data": {
"locations": [
{
"id": 8802,
"name": "locations/8802235673275682965",
"title": "渋谷店",
"address": "東京都渋谷区...",
"store_code": "SHIBUYA001",
"primary_category": "美容室",
"data_source": "places_api_app",
"new_review_uri": "https://..."
}
]
},
"meta": { "next_cursor": "...", "request_id": "...", "elapsed_ms": 123 }
}エラー:
| code | 条件 |
|---|---|
FORBIDDEN_USER | target_user_id が階層外 |
INVALID_GROUP | group_id が対象ユーザーのグループでない |
5.2 location.get
ロケーションの詳細情報を返す。GBP raw_json も含む(要求時)。
scope: mappy:location:read副作用: 🔵 読取
入力:
{
"type": "object",
"required": ["location_id"],
"properties": {
"location_id": { "type": "integer" },
"include_raw": { "type": "boolean", "default": false, "description": "GBP raw_json を含める(大きいので明示要求のみ)" }
}
}出力:
{
"data": {
"id": 8802,
"name": "locations/8802235673275682965",
"title": "渋谷店",
"address": "...",
"primary_category": "美容室",
"categories": ["美容室", "ヘアサロン"],
"phone_numbers": ["03-1234-5678"],
"website_uri": "https://...",
"regular_hours": { ... },
"latlng": { "latitude": 35.65, "longitude": 139.70 },
"data_source": "places_api_app",
"raw": { ... } // include_raw=true のときのみ
}
}エラー:
| code | 条件 |
|---|---|
FORBIDDEN_LOCATION | 対象ユーザーが location_id にアクセス不可 |
NOT_FOUND | location_id が存在しない |
5.3 review.list
口コミ一覧を返す。
scope: mappy:reviews:read副作用: 🔵 読取 DB: mappy_gbp_reviews(28 万行、必ず期間絞り込み推奨)
入力:
{
"type": "object",
"properties": {
"user_id": { "type": "integer" },
"location_id": { "type": "integer", "description": "未指定なら全アクセス可能店舗から" },
"since": { "type": "string", "format": "date-time", "description": "ISO8601、デフォルト過去 30 日" },
"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 }
}
}出力:
{
"data": {
"reviews": [
{
"id": 12345,
"location_id": 8802,
"reviewer_name": "山田 太郎",
"rating": 5,
"comment": "...",
"reply_comment": "...",
"create_time": "2026-05-01T10:00:00Z",
"update_time": "2026-05-02T09:00:00Z"
}
]
},
"meta": { "next_cursor": "...", ... }
}期間制約
過去 90 日を超える期間は明示的な許可が必要(負荷対策)。未指定時は過去 30 日。
5.4 ranking.list
検索順位データを返す。
scope: mappy:ranking:read + mappy_users.search_ranking_enabled = 1副作用: 🔵 読取 DB: mappy_search_rankings(1,000 万行、期間絞り込み必須)
入力:
{
"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 }
}
}必須制約
since/until必須- 期間は最大 365 日
location_id未指定の場合、ユーザーの全店舗を返すが、その場合は最大 100 店舗まで
出力:
{
"data": {
"rankings": [
{
"location_id": 8802,
"keyword": "美容室 渋谷",
"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
GBP インサイトの集計を返す。
scope: mappy:insight:read副作用: 🔵 読取 DB: mappy_gbp_insights(420 万行)
入力:
{
"type": "object",
"required": ["since", "until"],
"properties": {
"user_id": { "type": "integer" },
"location_id": { "type": "integer" },
"group_id": { "type": "integer", "description": "グループ集計" },
"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": "未指定なら全 metric"
},
"aggregate": { "type": "string", "enum": ["daily", "weekly", "monthly", "total"], "default": "monthly" }
}
}出力:
{
"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. Phase 2 ツール詳細(抜粋)
6.1 post.create
投稿を作成する(複数店舗一括対応、非同期)。
scope: mappy:posts:write副作用: 🔴 書込非同期 DB: 既存 Jobs キュー経由 戻り値: batch_id → 状態確認は batch.status
入力:
{
"type": "object",
"required": ["location_ids", "post"],
"properties": {
"location_ids": {
"type": "array",
"items": { "type": "integer" },
"minItems": 1,
"maxItems": 100,
"description": "一括対象店舗"
},
"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": "5 店舗超の一括反映時に必須(書込安全装置参照)" }
}
}出力(dry_run=false):
{
"data": {
"batch_id": "01HVABC...",
"status": "processing",
"queued_at": "2026-05-25T10:00:00Z",
"target_count": 12,
"estimated_completion_seconds": 60
}
}出力(dry_run=true):
{
"data": {
"would_execute": true,
"target_locations": [...],
"validation_warnings": [
{ "location_id": 8802, "warning": "前回投稿から 1 時間未満" }
]
}
}確認トークン必須ケース
location_idsの長さが 5 以上 →confirm_token必須- 詳細は 書込安全装置 を参照
6.2 review.reply
口コミに返信する。
scope: mappy:reviews:write副作用: 🟠 書込同期(GBP API 経由) DB: mappy_gbp_reviews
入力:
{
"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" }
}
}出力:
{
"data": {
"review_id": 12345,
"reply_comment": "...",
"reply_at": "2026-05-25T10:00:01Z"
}
}6.3 batch.status
非同期処理(post.create / media.upload 等)の状態確認。
scope: ベース認証のみ(特定 scope 不要、ただし当該 batch の発行ユーザーと一致が必要) 副作用: 🔵 読取
入力:
{
"type": "object",
"required": ["batch_id"],
"properties": {
"batch_id": { "type": "string" }
}
}出力:
{
"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. Phase 3 ツール詳細(抜粋)
7.1 report.create
レポート生成リクエスト。
scope: mappy:report:write副作用: 🔴 書込非同期 DB: mappy_reports の status/progress 内包パターンを利用
入力:
{
"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" }
}
}出力:
{
"data": {
"report_id": 59,
"status": "queued",
"estimated_completion_seconds": 30
}
}完了状態は report.get で確認。
7.2 report.get
レポート取得。
scope: mappy:report:read副作用: 🔵 読取
入力:
{
"type": "object",
"required": ["report_id"],
"properties": {
"report_id": { "type": "integer" }
}
}出力:
{
"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
グループ間の比較データを返す。
scope: mappy:group:read + mappy:ranking:read + mappy:insight:read副作用: 🔵 読取
入力:
{
"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. レート制限・リソース保護
| tool | 特殊制限 |
|---|---|
ranking.list | 期間 365 日超は拒否 |
review.list | 期間 90 日超は明示確認必要 |
post.create | location_ids 5 以上で confirm_token 必須 |
media.upload | 1 リクエスト 10 メディアまで |
report.create | 同一ユーザー 1 時間 5 回まで |
batch.status | ポーリング推奨間隔 5 秒、最大 1 req/sec |
9. ペイロード制限
| 種別 | 上限 |
|---|---|
| 入力 JSON サイズ | 256 KB |
| 出力 JSON サイズ | 1 MB(超える場合はページング) |
raw_json 含む output | 5 MB |
| 一括対象数 (location_ids) | 100 |
10. tool description の書き方
LLM が tool を選択しやすくするため、description は以下を含める:
- 何をするか (1 行)
- いつ使うか (1〜2 行、利用シーン)
- 入力の必須・任意の要点
- 副作用の有無
例:
location.list: ユーザーがアクセス可能な店舗の一覧を返します。
店舗の検索・絞り込み・グループ別の確認に使用します。
副作用なし。user_id 省略時は認証ユーザー自身の店舗を返します。11. 未確定事項(設計レビューで決定)
| # | 項目 | 候補 |
|---|---|---|
| 1 | tool 命名のドット vs アンダースコア | location.list / location_list(MCP 仕様では両方可) |
| 2 | cursor の暗号化要否 | base64(JSON) / 暗号化 / 完全 opaque |
| 3 | raw_json 出力時のフィルタリング | 機微情報除外フィルタの要否 |
| 4 | tool description の言語 | 日本語 / 英語 / 両方 |
| 5 | 一括処理の最大サイズ(100 vs 500) | DB / GBP API 負荷次第 |
| 6 | report.get の file_url の署名期限 | 1h / 24h / 7d |
| 7 | group.compare の group_ids 上限 | 5 / 10 / 20 |
| 8 | metrics の追加(CTR 等) | Phase 3 以降で追加 |
12. 関連ドキュメント
- MCP サーバー 全体像
- 認証・テナンシ — scope の階層構造
- 書込安全装置 — dry_run / idempotency / confirm_token の詳細
- DB 設計(監査ログ) — tool 呼び出しの記録