Skip to content

MCP サーバー ツール一覧(v2)

概要

項目内容
ステータス🟡 設計中
親ドキュメント案件提案書 / アーキテクチャ全体像
関連設計認証・テナンシ / インフラ・実装計画

本ドキュメントは Phase 1(MVP)で実装する 4 つの tool の詳細仕様を定義する。各 tool について以下を確定する:

  • 名前・説明
  • 必要 scope
  • 入力スキーマ(型・必須/任意・制約)
  • 出力スキーマ
  • エラー時の挙動

1. ツール命名規則

<domain>.<action>
部分
<domain>location / review / ranking / insight
<action>list / get / summary

プロダクト名は 含めない。1 つの tool(例: location.list)が認証情報の product クレームから自動で適切な Aurora に振り分けられる。

2. 共通仕様

2.1 共通出力ラッパー

すべての tool の出力は以下の形式で返す:

json
{
  "data": <tool 固有のペイロード>,
  "meta": {
    "request_id": "01HV...",
    "elapsed_ms": 123,
    "next_cursor": "..."
  }
}

meta.next_cursornull でない場合は次ページあり(後述のページング)。

2.2 ページング

カーソル方式を採用(offset/limit ではない)。大規模テーブル(口コミ・順位 数百万行)でも安定動作するため。

パラメータ説明
cursorstring?前ページレスポンスの meta.next_cursor
limitint?1〜100、デフォルト 50

cursor は不透明(opaque)な文字列として扱い、クライアントは解析しない。サーバー側で base64 エンコードされた検索条件を含める実装。

2.3 エラー形式

MCP の isError: true を返し、content[0].text に JSON でエラー詳細を載せる:

json
{
  "error_code": "FORBIDDEN_LOCATION",
  "message": "対象店舗へのアクセス権限がありません",
  "details": {
    "location_id": 8802,
    "user_id": 1435
  }
}

2.4 共通エラーコード

codeHTTP / JSON-RPC意味
UNAUTHORIZED401 / -32001access_token 不正・期限切れ
FORBIDDEN_SCOPE403 / -32004scope 不足
FORBIDDEN_USER403 / -32004対象ユーザーが操作範囲外
FORBIDDEN_LOCATION403 / -32004対象店舗が利用可能範囲外
FORBIDDEN_FEATURE403 / -32004対象ユーザーが該当機能フラグ OFF
INVALID_INPUT400 / -32602入力スキーマ違反
NOT_FOUND404 / -32601リソースが存在しない
RATE_LIMITED429 / -32003レート制限超過
INTERNAL_ERROR500 / -32603サーバー内部エラー

3. Phase 1 ツール一覧(4 個)

#tool 名副作用必要 scopeDB 主参照テーブル(実テーブル名)
1location.list🔵 読取mcp:location:readmappy_gbp_locations JOIN mappy_user_available_gbp_locations
2review.list🔵 読取mcp:review:readmappy_gbp_reviews (約 32 万行)
3ranking.list🔵 読取mcp:ranking:readmappy_search_rankings (約 1090 万行) + mappy_keywords JOIN
4insight.summary🔵 読取mcp:insight:readmappy_gbp_insights (約 430 万行)

4. ツール詳細

4.1 location.list

ユーザーがアクセス可能な店舗(ロケーション)の一覧を返す。

scope: mcp:location:read副作用: 🔵 読取

入力スキーマ

json
{
  "type": "object",
  "properties": {
    "q": {
      "type": "string",
      "description": "店舗名の部分一致検索",
      "maxLength": 100
    },
    "store_code": {
      "type": "string",
      "description": "店舗コードで完全一致絞り込み"
    },
    "cursor": { "type": "string" },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 50
    }
  }
}

出力スキーマ

json
{
  "data": {
    "locations": [
      {
        "id": 8802,
        "name": "locations/8802235673275682965",
        "title": "渋谷店",
        "address": "東京都渋谷区道玄坂...",
        "store_code": "SHIBUYA001",
        "primary_category": "美容室",
        "primary_category_display_name": "美容室",
        "new_review_uri": "https://search.google.com/local/writereview?placeid=...",
        "data_source": 1
      }
    ]
  },
  "meta": {
    "request_id": "01HV...",
    "elapsed_ms": 45,
    "next_cursor": "eyJsYXN0X2lkIjo4ODAyfQ=="
  }
}

電話番号は mappy_gbp_locations 本体に無い

電話番号や営業時間など詳細属性は raw_json カラム(GBP API のレスポンス JSON 全文)に含まれている。Phase 1 では返さない方針(必要なら Phase 2 の location.getraw_json から抽出して返す)。

tool description(AI 向け説明文)

location.list: 自分がアクセス可能な店舗(ロケーション)の一覧を返します。
店舗の検索・絞り込み・存在確認に使用します。副作用なし。
省略時は認証ユーザーがアクセス可能な全店舗を最大 50 件返します。
店舗名で検索したい場合は `q`、店舗コード完全一致は `store_code` を使用。

4.2 review.list

口コミ一覧を返す。

scope: mcp:review:read副作用: 🔵 読取

入力スキーマ

json
{
  "type": "object",
  "properties": {
    "location_id": {
      "type": "integer",
      "description": "未指定なら全アクセス可能店舗を対象"
    },
    "since": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601、デフォルト過去 30 日"
    },
    "until": {
      "type": "string",
      "format": "date-time"
    },
    "min_rating": {
      "type": "integer",
      "minimum": 1,
      "maximum": 5,
      "description": "最低評価フィルタ(例:3 → ☆3 以上のみ)"
    },
    "max_rating": {
      "type": "integer",
      "minimum": 1,
      "maximum": 5
    },
    "has_reply": {
      "type": "boolean",
      "description": "true: 返信済みのみ / false: 未返信のみ / 省略: 両方"
    },
    "cursor": { "type": "string" },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 50
    }
  }
}

出力スキーマ

json
{
  "data": {
    "reviews": [
      {
        "id": 12345,
        "gbp_location_id": 8802,
        "location_title": "渋谷店",
        "reviewer_display_name": "山田 太郎",
        "star_rating": "FIVE",
        "rating_numeric": 5,
        "comment": "とても親切な対応でした",
        "reply_comment": "ご来店ありがとうございました",
        "reply_comment_author": "店長",
        "create_time": "2026-05-01T10:00:00Z",
        "update_time": "2026-05-02T09:00:00Z"
      }
    ]
  },
  "meta": {
    "request_id": "01HV...",
    "elapsed_ms": 67,
    "next_cursor": null
  }
}

制約

制約
期間最大365 日
since/until 省略時過去 30 日(create_time で絞り込み)
location_id 省略時の最大店舗数100(超える場合は明示的な location_id 指定が必要)

star_rating の型変換について

mappy_gbp_reviews.star_rating カラムは Google 仕様の文字列(ONE / TWO / THREE / FOUR / FIVE)を格納している。MCP の出力では原文(star_rating)と数値化版(rating_numeric、1〜5 の整数)を両方返すことで AI クライアントの取り扱いを楽にする。入力の min_rating / max_rating は数値で受け取り、サーバー側で文字列に変換してクエリする。

tool description

review.list: 口コミ一覧を取得します。期間・評価・返信有無で絞り込み可能。
副作用なし。デフォルトは過去 30 日。
レポート生成、返信文の検討、評価傾向の確認等に使用します。

4.3 ranking.list

検索順位データを返す。

scope: mcp:ranking:read副作用: 🔵 読取

入力スキーマ

json
{
  "type": "object",
  "required": ["since", "until"],
  "properties": {
    "location_id": {
      "type": "integer",
      "description": "未指定なら全アクセス可能店舗"
    },
    "keyword": {
      "type": "string",
      "description": "キーワード完全一致または部分一致"
    },
    "since": {
      "type": "string",
      "format": "date",
      "description": "ISO 8601 日付(必須)"
    },
    "until": {
      "type": "string",
      "format": "date"
    },
    "aggregate": {
      "type": "string",
      "enum": ["daily", "weekly", "raw"],
      "default": "daily",
      "description": "集計粒度"
    },
    "cursor": { "type": "string" },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 50
    }
  }
}

出力スキーマ

json
{
  "data": {
    "rankings": [
      {
        "gbp_location_id": 8802,
        "location_title": "渋谷店",
        "keyword_id": 4567,
        "keyword": "美容室 渋谷",
        "ranking_at": "2026-05-01",
        "ranking": 3,
        "search_at": "2026-05-01T03:15:00Z"
      }
    ],
    "summary": {
      "avg_ranking": 3.2,
      "best_ranking": 1,
      "worst_ranking": 8,
      "total_records": 1234
    }
  },
  "meta": {
    "request_id": "01HV...",
    "elapsed_ms": 123,
    "next_cursor": "..."
  }
}

制約

制約
since / until必須ranking_at 列で絞り込み、インデックスあり)
期間最大365 日
location_id 未指定時の最大店舗数100

キーワードは別テーブル参照

mappy_search_rankingskeyword_id (FK) を持つだけで、キーワード文字列は mappy_keywords テーブルに格納されている。MCP では JOIN して keyword 文字列を返す。入力でキーワード絞り込みする場合も mappy_keywords.keyword LIKE '%xxx%' の経由が必要。

tool description

ranking.list: 検索順位データを取得します。期間指定必須(since/until)。
副作用なし。
順位推移の分析、キーワード別の順位確認、レポート生成に使用します。
集計粒度は daily / weekly / raw から選択(デフォルト daily)。

4.4 insight.summary

GBP(Google Business Profile)インサイトの集計データを返す。レポート生成のための主要データソース。

scope: mcp:insight:read副作用: 🔵 読取

入力スキーマ

json
{
  "type": "object",
  "required": ["since", "until"],
  "properties": {
    "location_id": {
      "type": "integer",
      "description": "未指定なら全アクセス可能店舗を合算"
    },
    "since": {
      "type": "string",
      "format": "date"
    },
    "until": {
      "type": "string",
      "format": "date"
    },
    "metrics": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "all_views_count",
          "views_count_by_search",
          "views_count_by_map",
          "views_count_by_desktop_search",
          "views_count_by_mobile_search",
          "views_count_by_desktop_map",
          "views_count_by_mobile_map",
          "action_count",
          "access_count_website",
          "access_count_route",
          "call_count"
        ]
      },
      "description": "未指定なら全 metric(mappy_gbp_insights の実カラム名)"
    },
    "aggregate": {
      "type": "string",
      "enum": ["daily", "weekly", "monthly", "total"],
      "default": "monthly"
    },
    "compare_previous_period": {
      "type": "boolean",
      "default": true,
      "description": "前期比較を含めるか"
    }
  }
}

出力スキーマ

json
{
  "data": {
    "summary": {
      "period": {
        "since": "2026-04-01",
        "until": "2026-04-30"
      },
      "metrics": {
        "all_views_count": 15234,
        "views_count_by_search": 9234,
        "views_count_by_map": 6000,
        "action_count": 2479,
        "access_count_website": 1234,
        "access_count_route": 789,
        "call_count": 456
      },
      "previous_period_diff": {
        "all_views_count": { "value": 1820, "percent": 13.6 },
        "access_count_website": { "value": -45, "percent": -3.5 }
      }
    },
    "series": [
      {
        "insight_at": "2026-04-01",
        "all_views_count": 500,
        "access_count_website": 40,
        "call_count": 15,
        "access_count_route": 25,
        "views_count_by_search": 320
      }
    ]
  },
  "meta": {
    "request_id": "01HV...",
    "elapsed_ms": 89
  }
}

tool description

insight.summary: GBP インサイトの集計データを返します。期間指定必須。
副作用なし。
表示回数(views) / ウェブサイトクリック / 電話 / 経路検索 / 検索数の集計と、
前期比較、日次/週次/月次の系列データを含む。
月次レポートの生成、KPI 確認、トレンド分析等に使用します。

5. レポート生成サポート(クライアント側)

MCP は 集計データの提供のみ を行い、PPTX レンダリングはクライアント AI のローカル環境で実施する。

5.1 想定シーケンス

5.2 クライアント側プロンプト推奨例(参考)

顧客が AI に「レポート作って」と頼む場合、AI が次のように tool を組み合わせる:

  1. insight.summary で全体 KPI と前期比を取得
  2. ranking.list で主要キーワードの順位推移を取得(weekly 集計)
  3. review.list で先月の口コミハイライト(min_rating でフィルタ)
  4. ローカル PPTX テンプレートにデータ流し込み

サーバー側で PPTX 生成しないため、テンプレートのバージョン管理・カスタマイズはクライアント責任。

6. レート制限・リソース保護

tool特殊制限
review.list期間 365 日超は INVALID_INPUT で拒否
ranking.list期間 365 日超は INVALID_INPUTsince/until 必須
insight.summary期間 365 日超は INVALID_INPUTsince/until 必須
全 tool 共通レスポンスサイズ 1 MB 超過時はページング推奨

7. ペイロード制限

種別上限
入力 JSON サイズ64 KB
出力 JSON サイズ1 MB(超える場合はページング)
meta.elapsed_ms 報告精度ミリ秒

8. 各 tool の SQL クエリ概略(実装ヒント)

実テーブル名・実カラム名は本番 Aurora 接続調査で確定済み。以下は基本形、実装時に詳細化。

8.1 location.list

sql
SELECT
    gl.id,
    gl.name,
    gl.title,
    gl.address,
    gl.store_code,
    gl.primary_category,
    gl.primary_category_display_name,
    gl.new_review_uri,
    gl.data_source
FROM mappy_gbp_locations gl
INNER JOIN mappy_user_available_gbp_locations uagl
    ON uagl.gbp_location_id = gl.id
WHERE uagl.user_id = :user_id
  AND (:q IS NULL OR gl.title LIKE CONCAT('%', :q, '%'))
  AND (:store_code IS NULL OR gl.store_code = :store_code)
ORDER BY gl.id
LIMIT :limit

8.2 review.list

sql
SELECT
    r.id,
    r.gbp_location_id,
    gl.title AS location_title,
    r.reviewer_display_name,
    r.star_rating,
    -- star_rating (FIVE/FOUR/THREE/TWO/ONE) を数値に変換
    CASE r.star_rating
        WHEN 'FIVE'  THEN 5
        WHEN 'FOUR'  THEN 4
        WHEN 'THREE' THEN 3
        WHEN 'TWO'   THEN 2
        WHEN 'ONE'   THEN 1
        ELSE NULL
    END AS rating_numeric,
    r.comment,
    r.reply_comment,
    r.reply_comment_author,
    r.create_time,
    r.update_time
FROM mappy_gbp_reviews r
INNER JOIN mappy_gbp_locations gl ON gl.id = r.gbp_location_id
INNER JOIN mappy_user_available_gbp_locations uagl
    ON uagl.gbp_location_id = r.gbp_location_id
WHERE uagl.user_id = :user_id
  AND r.create_time BETWEEN :since AND :until
  AND (:location_id IS NULL OR r.gbp_location_id = :location_id)
  AND (:min_rating IS NULL OR CASE r.star_rating WHEN 'FIVE' THEN 5 WHEN 'FOUR' THEN 4 WHEN 'THREE' THEN 3 WHEN 'TWO' THEN 2 WHEN 'ONE' THEN 1 END >= :min_rating)
  AND (:has_reply IS NULL
       OR (:has_reply = TRUE  AND r.reply_comment IS NOT NULL)
       OR (:has_reply = FALSE AND r.reply_comment IS NULL))
ORDER BY r.create_time DESC
LIMIT :limit

8.3 ranking.list

sql
SELECT
    sr.gbp_location_id,
    gl.title AS location_title,
    sr.keyword_id,
    kw.keyword,
    sr.ranking_at,
    sr.ranking,
    sr.search_at
FROM mappy_search_rankings sr
INNER JOIN mappy_gbp_locations gl ON gl.id = sr.gbp_location_id
INNER JOIN mappy_keywords kw ON kw.id = sr.keyword_id
INNER JOIN mappy_user_available_gbp_locations uagl
    ON uagl.gbp_location_id = sr.gbp_location_id AND uagl.user_id = sr.user_id
WHERE sr.user_id = :user_id
  AND sr.ranking_at BETWEEN :since AND :until   -- ranking_at に IDX あり、必須絞り込み
  AND (:location_id IS NULL OR sr.gbp_location_id = :location_id)
  AND (:keyword IS NULL OR kw.keyword LIKE CONCAT('%', :keyword, '%'))
ORDER BY sr.ranking_at, sr.gbp_location_id, sr.keyword_id
LIMIT :limit

パフォーマンス注意

mappy_search_rankings は約 1,090 万行。ranking_at の期間絞り込みは必須(インデックスあり、これがないとフルスキャンになる)。

8.4 insight.summary

sql
SELECT
    SUM(i.all_views_count)               AS all_views_count,
    SUM(i.views_count_by_search)         AS views_count_by_search,
    SUM(i.views_count_by_map)            AS views_count_by_map,
    SUM(i.views_count_by_desktop_search) AS views_count_by_desktop_search,
    SUM(i.views_count_by_mobile_search)  AS views_count_by_mobile_search,
    SUM(i.views_count_by_desktop_map)    AS views_count_by_desktop_map,
    SUM(i.views_count_by_mobile_map)     AS views_count_by_mobile_map,
    SUM(i.action_count)                  AS action_count,
    SUM(i.access_count_website)          AS access_count_website,
    SUM(i.access_count_route)            AS access_count_route,
    SUM(i.call_count)                    AS call_count
FROM mappy_gbp_insights i
INNER JOIN mappy_user_available_gbp_locations uagl
    ON uagl.gbp_location_id = i.gbp_location_id
WHERE uagl.user_id = :user_id
  AND i.insight_at BETWEEN :since AND :until
  AND (:location_id IS NULL OR i.gbp_location_id = :location_id)

集計粒度(aggregate=daily/weekly/monthly/total)に応じて GROUP BY を変える:

aggregateGROUP BY 句
dailyi.insight_at
weeklyYEARWEEK(i.insight_at)
monthlyDATE_FORMAT(i.insight_at, '%Y-%m')
total(GROUP BY なし、全期間合算)

パフォーマンス注意

mappy_gbp_insights は約 430 万行。insight_at での期間絞り込み + gbp_location_id での絞り込みが必須。

8.5 テナンシ判定のための補助クエリ

機能フラグチェック(認証設計 §6.3 より):

sql
SELECT
    id,
    user_type,
    parent_user_id,
    is_enabled,
    search_ranking_enabled,
    search_ranking_access_level,
    gbp_connection_settings_access_level,
    anti_tamper_screen_access_level,
    is_smart_meo
FROM mappy_users
WHERE id = :user_id

is_enabled = 0 なら全 tool 拒否、mcp:ranking:read 要求時は search_ranking_enabled = 1 必須、等を Lambda Authorizer で判定。

9. 未確定事項(設計レビュー or 実装開始前に確定)

#項目状態
1各テーブルの実カラム名(gbp_locations 等)Phase 1 開始時に Aurora 接続して確認、本ドキュメント実装時のクエリは仮
2cursor の暗号化要否base64 (JSON) で平文 / 暗号化 / 完全 opaque ID 形式
3location.list の出力に含める属性数詳細属性(営業時間・URL 等)は location.get (Phase 2) で取得する設計のままで OK か
4tool description の言語日本語 / 英語 / 両方併記
5各 tool のキャッシュ可否(クライアント側)クライアントが結果をキャッシュしてもよいか、サーバー側で Cache-Control を返すか
6エラーメッセージのローカライズ日本語のみ / クライアント言語に追随

10. 関連ドキュメント