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 の出力は以下の形式で返す:
{
"data": <tool 固有のペイロード>,
"meta": {
"request_id": "01HV...",
"elapsed_ms": 123,
"next_cursor": "..."
}
}meta.next_cursor が null でない場合は次ページあり(後述のページング)。
2.2 ページング
カーソル方式を採用(offset/limit ではない)。大規模テーブル(口コミ・順位 数百万行)でも安定動作するため。
| パラメータ | 型 | 説明 |
|---|---|---|
cursor | string? | 前ページレスポンスの meta.next_cursor |
limit | int? | 1〜100、デフォルト 50 |
cursor は不透明(opaque)な文字列として扱い、クライアントは解析しない。サーバー側で base64 エンコードされた検索条件を含める実装。
2.3 エラー形式
MCP の isError: true を返し、content[0].text に JSON でエラー詳細を載せる:
{
"error_code": "FORBIDDEN_LOCATION",
"message": "対象店舗へのアクセス権限がありません",
"details": {
"location_id": 8802,
"user_id": 1435
}
}2.4 共通エラーコード
| code | HTTP / JSON-RPC | 意味 |
|---|---|---|
UNAUTHORIZED | 401 / -32001 | access_token 不正・期限切れ |
FORBIDDEN_SCOPE | 403 / -32004 | scope 不足 |
FORBIDDEN_USER | 403 / -32004 | 対象ユーザーが操作範囲外 |
FORBIDDEN_LOCATION | 403 / -32004 | 対象店舗が利用可能範囲外 |
FORBIDDEN_FEATURE | 403 / -32004 | 対象ユーザーが該当機能フラグ OFF |
INVALID_INPUT | 400 / -32602 | 入力スキーマ違反 |
NOT_FOUND | 404 / -32601 | リソースが存在しない |
RATE_LIMITED | 429 / -32003 | レート制限超過 |
INTERNAL_ERROR | 500 / -32603 | サーバー内部エラー |
3. Phase 1 ツール一覧(4 個)
| # | tool 名 | 副作用 | 必要 scope | DB 主参照テーブル(実テーブル名) |
|---|---|---|---|---|
| 1 | location.list | 🔵 読取 | mcp:location:read | mappy_gbp_locations JOIN mappy_user_available_gbp_locations |
| 2 | review.list | 🔵 読取 | mcp:review:read | mappy_gbp_reviews (約 32 万行) |
| 3 | ranking.list | 🔵 読取 | mcp:ranking:read | mappy_search_rankings (約 1090 万行) + mappy_keywords JOIN |
| 4 | insight.summary | 🔵 読取 | mcp:insight:read | mappy_gbp_insights (約 430 万行) |
4. ツール詳細
4.1 location.list
ユーザーがアクセス可能な店舗(ロケーション)の一覧を返す。
scope: mcp:location:read副作用: 🔵 読取
入力スキーマ
{
"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
}
}
}出力スキーマ
{
"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.get で raw_json から抽出して返す)。
tool description(AI 向け説明文)
location.list: 自分がアクセス可能な店舗(ロケーション)の一覧を返します。
店舗の検索・絞り込み・存在確認に使用します。副作用なし。
省略時は認証ユーザーがアクセス可能な全店舗を最大 50 件返します。
店舗名で検索したい場合は `q`、店舗コード完全一致は `store_code` を使用。4.2 review.list
口コミ一覧を返す。
scope: mcp:review:read副作用: 🔵 読取
入力スキーマ
{
"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
}
}
}出力スキーマ
{
"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副作用: 🔵 読取
入力スキーマ
{
"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
}
}
}出力スキーマ
{
"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_rankings は keyword_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副作用: 🔵 読取
入力スキーマ
{
"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": "前期比較を含めるか"
}
}
}出力スキーマ
{
"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 を組み合わせる:
insight.summaryで全体 KPI と前期比を取得ranking.listで主要キーワードの順位推移を取得(weekly 集計)review.listで先月の口コミハイライト(min_rating でフィルタ)- ローカル PPTX テンプレートにデータ流し込み
サーバー側で PPTX 生成しないため、テンプレートのバージョン管理・カスタマイズはクライアント責任。
6. レート制限・リソース保護
| tool | 特殊制限 |
|---|---|
review.list | 期間 365 日超は INVALID_INPUT で拒否 |
ranking.list | 期間 365 日超は INVALID_INPUT、since/until 必須 |
insight.summary | 期間 365 日超は INVALID_INPUT、since/until 必須 |
| 全 tool 共通 | レスポンスサイズ 1 MB 超過時はページング推奨 |
7. ペイロード制限
| 種別 | 上限 |
|---|---|
| 入力 JSON サイズ | 64 KB |
| 出力 JSON サイズ | 1 MB(超える場合はページング) |
meta.elapsed_ms 報告精度 | ミリ秒 |
8. 各 tool の SQL クエリ概略(実装ヒント)
実テーブル名・実カラム名は本番 Aurora 接続調査で確定済み。以下は基本形、実装時に詳細化。
8.1 location.list
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 :limit8.2 review.list
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 :limit8.3 ranking.list
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
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 を変える:
| aggregate | GROUP BY 句 |
|---|---|
daily | i.insight_at |
weekly | YEARWEEK(i.insight_at) |
monthly | DATE_FORMAT(i.insight_at, '%Y-%m') |
total | (GROUP BY なし、全期間合算) |
パフォーマンス注意
mappy_gbp_insights は約 430 万行。insight_at での期間絞り込み + gbp_location_id での絞り込みが必須。
8.5 テナンシ判定のための補助クエリ
機能フラグチェック(認証設計 §6.3 より):
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 接続して確認、本ドキュメント実装時のクエリは仮 |
| 2 | cursor の暗号化要否 | base64 (JSON) で平文 / 暗号化 / 完全 opaque ID 形式 |
| 3 | location.list の出力に含める属性数 | 詳細属性(営業時間・URL 等)は location.get (Phase 2) で取得する設計のままで OK か |
| 4 | tool description の言語 | 日本語 / 英語 / 両方併記 |
| 5 | 各 tool のキャッシュ可否(クライアント側) | クライアントが結果をキャッシュしてもよいか、サーバー側で Cache-Control を返すか |
| 6 | エラーメッセージのローカライズ | 日本語のみ / クライアント言語に追随 |
10. 関連ドキュメント
- 案件提案書
- アーキテクチャ全体像
- 認証・テナンシ — scope と tool の対応
- インフラ・実装計画