Mappy MCP サーバー新設(v3 — Laravel 内蔵版)
概要
| 項目 | 内容 |
|---|---|
| ステータス | 🔵 提案中 |
| GitLab Issue | Mappy #58 |
| 対象プロダクト | GMAC / GCOR / PIPIT (KingMeo) / 口コミONE の 4 製品(Phase 1 は GMAC のみ有効化) |
| 想定工数(Phase 1) | 8.0 人日(AI 前提 — AIリテイク × レビュー時間で積算) |
| 追加インフラコスト | ほぼゼロ(既存 Laravel EC2 に相乗り、新規 AWS リソースなし) |
| 実装スタック | Laravel 内蔵(既存コードベースに追加)+ Bearer トークン認証 |
社内メンバーが各自の Claude / ChatGPT 等の AI エージェントから、Mappy 4 製品のデータを MCP (Model Context Protocol) 経由で参照できるようにする。実装は既存 Laravel アプリケーションへの機能追加のみで完結させる。
v2 からの方針転換について
旧提案 /projects/mcp-server-v2(Lambda + API Gateway + OAuth 2.1 + DynamoDB 構成)は、設計レビューの結果、Laravel 内蔵の軽量構成へ方針転換したためアーカイブ扱いとする。v2 で実施した DB 実スキーマ調査・SQL 記述(/design/mcp-tools-v2)は本設計に流用している。
背景・目的
- 利用者(主に社内メンバー)は各自 Claude / ChatGPT 等の AI エージェントを持っているが、現状その AI から製品データにアクセスする手段がない
- 代表ユースケースは「AI による月間レポート作成」— AI がローカルの PPTX テンプレートにデータを流し込むための、プログラマブルなデータ取得手段が必要
- v2 提案では独立したサーバーレス構成(Lambda / OAuth 2.1 / DynamoDB)を検討したが、技術レビューの結果「管理画面で取れる情報を MCP でエージェント向けにインターフェース公開するだけ」という要件に対して構成が大きすぎると判断。既存 Laravel に内蔵する最小構成へ転換した
方針(技術レビューで確定した 4 機能)
大掛かりな独立システムは作らない。必要な機能は以下の 4 つのみ。
| # | 機能 | 実装先 |
|---|---|---|
| 1 | ログインユーザー毎のトークン払い出し | 管理画面(既存ユーザー詳細画面にタブ追加) |
| 2 | MCP 用 API 作成 | Laravel(既存 Service 層を流用) |
| 3 | MCP 用エンドポイント用意 | Laravel(POST /api/mappy/mcp の 1 ルート) |
| 4 | トークン認証 | Laravel(専用ミドルウェア + 独自テーブル mappy_mcp_tokens) |
OAuth 認可サーバー・Lambda・API Gateway・DynamoDB 等の新規 AWS リソースは一切追加しない。ホスティングは既存 Laravel EC2 に相乗りする。
スコープ
対象プロダクト(4 製品)
4 製品は同一 Laravel コードベースの env 切替で運用されており、DB は製品ごとに別 Aurora クラスターだが同一スキーマ。コードは 4 製品共通で実装し、env フラグ MCP_ENABLED で製品別に ON/OFF する。
| 製品 | ドメイン | Aurora クラスター | Phase 1 |
|---|---|---|---|
| GMAC | gmac-g.com | gmac-db-production-cluster-1 | 有効(MCP_ENABLED=true) |
| GCOR | g-cor-m.com | gcor-db-production-cluster | 無効(env で false) |
| PIPIT (KingMeo) | king-meo.com | kingmeo-db-production-cluster | 無効(env で false) |
| 口コミONE | kuchikomione.com | kuchikomi-one-db-production-cluster | 無効(env で false) |
Phase 2 以降の他製品展開は「env を true にする + migration 適用確認」のみで完了する。
Phase 1 の範囲
- GMAC のみ有効化、読取専用 7 tool(下表)
- トランスポートは MCP Streamable HTTP の単発 request/response モード(SSE 不使用)、JSON-RPC 2.0
- トークン発行は管理者のみ(管理画面から。セルフ発行は Phase 2 検討)
対象外
| 項目 | 理由 |
|---|---|
| 書込み系 tool(投稿・口コミ返信等) | Phase 1 は読取専用に限定 |
| PPTX 生成 | MCP サーバーはデータ提供のみ。レポート生成は AI クライアント側(#9 案件と並行・独立) |
| ChatGPT 対応 | ChatGPT コネクタは「認証なし」or「OAuth 2.1 認可サーバー」の二択で静的 Bearer トークンに未対応。OAuth 2.1 実装が必要なため Phase 2 以降 |
| OAuth / 新規 AWS リソース / 監査 DB テーブル / キャッシュ層 / 承認フロー | 最小構成方針によりスコープ外 |
| マッピーログイン回数の提供 | レポート項目の対象外と決定済 |
Phase 1 で提供する 7 tool(確定)
| # | tool 名 | データ源 | 主要出力 | 備考 |
|---|---|---|---|---|
| 1 | location_list | mappy_gbp_locations (境界: mappy_integrated_gbp_locations、GROUP は mappy_group_location 経由) | 店舗一覧: id / title(店舗名) / address / primary_category_display_name / store_code / status | モデル GbpLocation。status は 0=normal〜6=disabled。境界は integratedLocations 準拠 (available は不採用、D-3 参照) |
| 2 | review_list | mappy_gbp_reviews | 口コミ一覧 (star_rating / comment / create_time / reviewer_display_name / reply_comment) + 集計 (件数 / 平均評価 / 評価分布 / 返信率) | type=1 (REVIEW_TYPE) で必ず絞る。期間フィルタは create_time。ReviewService::getReviewCountOfLocations / getAverageRatingOfLocations / getReviewCountGroupByRating + calculateReplyRateForLocations 流用。star_rating は varchar だが数値保存済みで AVG 可 |
| 3 | ranking_list | mappy_search_rankings JOIN mappy_keywords | キーワード別の平均検索順位・日次順位推移 (keyword 文字列 / ranking / ranking_at) | ranking NULL/0/>=61 は圏外 (OUT_OF_RANGE_VALUE=61)。JOIN 時 mappy_keywords.deleted_at IS NULL (レポート互換は withTrashed)。期間列は ranking_at (IDX あり)。RankingService::getAverageRankingInPeriod / getRankingChartData 流用。search_ranking_enabled=0 のユーザーは業務エラー |
| 4 | insight_summary | mappy_gbp_insights | 期間 SUM: 表示回数 (all_views / search / map 別) / 行動数 action_count (website+route+call 内訳含む) / action_rate | 期間列は insight_at (unique[gbp_location_id, insight_at]、日次 1 行/店舗)。InsightService::getInsightCountOfLocations (月間レポートの正解クエリ) ほぼそのまま流用 |
| 5 | post_list | mappy_gbp_posts | 投稿一覧 (title / summary / topic_type / gbp_state / create_time / gbp_search_url) + 期間投稿記事数 | 期間列は create_time。件数集計は StatisticService::getPostOperationRanking と同形 (state フィルタなし)。gbp_state='LIVE' フィルタはオプションパラメータとして提供。現行 DownloadableReport に投稿数セクションは無く、分析(統計)機能が集計の正 |
| 6 | media_list | mappy_gbp_media_updated_locations | 写真一覧 (gbp_media_url / gbp_media_thumbnail_url / category / gbp_state / created_at) + 期間投稿写真数 | 期間列は created_at。StatisticService::getPictureOperationRanking と同形。gbp_state='LIVE' フィルタはオプション。mappy_gbp_media_schedules は予約メタであり主データ源にしない。originates_from_gbp 等の削除済み列は使用不可 |
| 7 | keyword_list | GBP Performance API 直呼び (GET {locationName}/searchkeywords/impressions/monthly)。恒久テーブルなし | 月次流入キーワードと表示回数 / 上位 10 / unique 流入キーワード数 | KeywordService::retrieveMonthlyKeywordsImpressions + BusinessProfileApiService::getMonthlyKeywordsImpressions 流用。Google トークンは getApiUser() (root MAIN) で解決。insightsValue.value 空は threshold 値代替。mappy_report_keywords は鮮度保証のない使い捨てキャッシュのため使わない。mappy_keywords (順位計測用登録キーワード) は流入キーワードではない (ranking_list 側専用) |
tool 名はアンダースコア区切りで統一する。AI クライアント側のツール名制約(Anthropic Messages API: ^[a-zA-Z0-9_-]{1,64}$)にドット入り名が適合しないため(詳細は技術設計書 §4.1)。
月間レポート必要項目とのカバレッジ
代表ユースケース(AI が月間レポートをローカル PPTX テンプレートで生成)に必要な項目は、7 tool ですべて取得できる。
| 月間レポート必要項目 | 取得 tool |
|---|---|
| 表示回数(検索 / マップ別含む) | #4 insight_summary |
| 行動数(Web サイト / 経路 / 電話の内訳含む) | #4 insight_summary |
| 平均検索順位 | #3 ranking_list |
| 口コミ評価・評価分布・返信率 | #2 review_list |
| キーワード上位 10 | #7 keyword_list |
| 流入キーワード数 | #7 keyword_list |
| 投稿記事数 | #5 post_list |
| 投稿写真数 | #6 media_list |
| 店舗基本情報 | #1 location_list |
| マッピーログイン回数 | 対象外(決定済) |
アーキテクチャ概要
すべて既存リソースの中で完結する。新規 AWS リソースは追加しない。
- 認証: 独自テーブル
mappy_mcp_tokens(SHA-256 ハッシュのみ保存、平文は発行時に一度だけ表示)+ 専用ミドルウェアによる Bearer トークン検証。Laravel 5.7 のため Sanctum は使えず、Sanctum 方式を模倣した最小実装とする - エンドポイント:
POST /api/mappy/mcpの 1 ルートのみ。外部 MCP SDK は PHP 8.1+ 必須でインストール不能のため、自前の軽量 JSON-RPC ハンドラで実装(外部 Composer パッケージ追加なし) - テナンシ: トークン → ユーザー → 参照可能店舗(
mappy_integrated_gbp_locations準拠)の境界を全 tool で強制。月間自動レポートと同じスコープ解決 - レート制限・ログ: ユーザー単位 60 req/分 + 専用ログチャネル(
storage/logs/mcp-*.log)で利用状況を記録
詳細な設計判断(テーブル DDL、JSON-RPC メソッド仕様、エラー表、クライアント互換性)は 技術設計書 を参照。
利用イメージ
- 管理者: 管理画面 → アカウント管理 → 対象ユーザー編集 → MCPトークンタブ → 「発行」→ モーダルに一度だけ表示される平文トークンをコピーし、利用者へ安全な手段で受け渡し
- 利用者: 自分の AI クライアントにトークンを設定(Claude Code なら 1 コマンド)
claude mcp add --transport http mappy https://gmac-g.com/api/mappy/mcp \
--header "Authorization: Bearer <token>"- 日常利用: AI に「先月の月間レポートを作って」と依頼すると、AI が
location_list→insight_summary→review_list→ranking_list→keyword_list→post_list→media_listを組み合わせてデータを取得し、ローカルの PPTX テンプレートにレポートを生成する
クライアント互換性
Claude Code は標準対応(推奨・最も簡単)。Claude Desktop / claude.ai は mcp-remote プロキシ経由で接続可(クライアント PC に Node.js が必要)。ChatGPT は静的 Bearer トークン未対応のため Phase 1 では接続不可(OAuth 2.1 実装が必要、Phase 2 以降)。
画面モック
Phase 1 で追加する画面は既存ユーザー詳細画面への「MCPトークン」タブ 1 つのみ(新規独立画面なし。詳細は技術設計書 §7)。タブのインタラクティブモック(一覧 / 発行モーダル(平文一度だけ表示)/ 失効確認)は製造前レビュー時に追加予定とし、技術設計書の未確定事項(§12)で追跡する。
概算工数(AI前提)
体制
| 役割 | 人数 | 担当内容 |
|---|---|---|
| 設計者 | 1名 | 要件確認 → AIに設計書作成指示 → レビュー → 製造へ指示 |
| 製造者 | 1名 | ISSUEを元にAIに作成指示 → コードレビュー → テスト実施 → デプロイ |
工数内訳
| # | 作業項目 | AIリテイク | レビュー | 工数(人日) | 担当 |
|---|---|---|---|---|---|
| 1 | 技術設計書レビュー・製造指示(設計書の最終確認 → 製造者への着手指示) | 1回 | 0.5日/回 | 0.5 | 設計者 |
| 2 | DB・モデル(migration mappy_mcp_tokens + モデル McpToken) | 1回 | 0.5日/回 | 0.5 | 製造者 |
| 3 | トークン管理 API(一覧 / 発行 / 失効の 3 本、auth:admin_api グループ) | 1回 | 0.5日/回 | 0.5 | 製造者 |
| 4 | 管理画面 UI(トークンタブ追加 + 平文一度だけ表示モーダル + 失効確認 + 出し分け) | 2回 | 0.5日/回 | 1.0 | 製造者 |
| 5 | 認証ミドルウェア(Bearer 検証 / 有効性確認 / 最終使用日時更新 / 401 応答) | 1回 | 0.5日/回 | 0.5 | 製造者 |
| 6 | MCP エンドポイント(JSON-RPC 2.0 ハンドラ + ツール定義層分離 + エラー表準拠) | 2回 | 0.5日/回 | 1.0 | 製造者 |
| 7 | tool 実装(DB 系 6 本 — 既存 Service 流用 + テナンシ境界 + ページング) | 3回 | 0.5日/回 | 1.5 | 製造者 |
| 8 | tool 実装(API 系 1 本 — keyword_list、GBP Performance API 直呼び) | 1回 | 0.5日/回 | 0.5 | 製造者 |
| 9 | env・設定(MCP_ENABLED を 4 製品 env + config、無効製品 404、ログチャネル) | 1回 | 0.5日/回 | 0.5 | 製造者 |
| 10 | 結合テスト(Claude Code / Claude Desktop 実接続、7 tool の tools/call 実行確認、値を管理画面表示と突合) | 2回 | 0.5日/回 | 1.0 | 製造者 |
| 11 | ドキュメント(利用者向け接続手順書 + 運用手順(発行 / 失効)) | 1回 | 0.5日/回 | 0.5 | 設計者 |
| 合計 | 8.0 | 設計者 1.0日 / 製造者 7.0日 |
- 目標レンジ 5〜8 人日内。バッファは各項目のリテイク分に内包済み(別途バッファ行は設けない)
- 前提: PHP 7.1 互換構文 / 外部パッケージ追加なし / 既存 Service 流用のため集計ロジックの新規設計工数は含まない
- スコープ外(工数 0): OAuth・新規 AWS リソース・監査テーブル・書込み tool・ChatGPT 対応・PPTX 生成
スケジュール
開始日は仮置き(着手日確定後に更新)。
| タスク | 担当 | 日数 | 7/6 | 7/7 | 7/8 | 7/9 | 7/10 | 7/11 | 7/12 | 7/13 | 7/14 | 7/15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 月 | 火 | 水 | 木 | 金 | 土 | 日 | 月 | 火 | 水 | |||
| 技術設計書レビュー・製造指示 | 設計者 | 1d | ||||||||||
| DB・モデル / トークン管理API | 製造者 | 1d | ||||||||||
| 認証ミドルウェア / env・設定 | 製造者 | 1d | ||||||||||
| MCPエンドポイント(JSON-RPC ハンドラ) | 製造者 | 1d | ||||||||||
| tool 実装(7 tool) | 製造者 | 2d | ||||||||||
| 管理画面トークンUI | 製造者 | 1d | ||||||||||
| 接続手順書・運用手順作成 | 設計者 | 1d | ||||||||||
| 結合テスト・実クライアント接続確認 | 製造者 | 1d |
- ガントチャートの最小粒度は 1 日のため、設計者タスク des1(工数内訳 #1 = 0.5 人日)と des2(工数内訳 #11 = 0.5 人日)は各 1 日枠で表示している(設計者計 1.0 人日)
- 製造者タスク dev1〜dev6 は計 7 日 = 7.0 人日で、工数内訳の担当別合計(設計者 1.0日 / 製造者 7.0日 = 8.0 人日)と対応する
#9 レポート AI 案件との関係
本件と #9 レポート AI 案件 は並行・独立で進める。
- 本件(MCP サーバー): データ提供のみ。PPTX 生成はしない
- #9 レポート AI: レポート生成側の案件。MCP 経由のデータ取得を前提にできるが、依存関係は持たせない(どちらが先にリリースされても他方に影響しない)
リスク・未確定事項
| # | 項目 | 内容・対策 |
|---|---|---|
| 1 | Laravel 5.7 / PHP 7.1 制約 | 外部 MCP SDK・Sanctum が使えず自前実装となる。単発 request/response モードの最小実装に絞り、ツール定義層を分離して将来の SDK 移行に備える |
| 2 | AI クライアント互換性 | Claude Code の Authorization ヘッダ欠落の既知バグ事例があるため、結合テストでサーバーアクセスログによるヘッダ到達確認を必須とする。Claude Desktop は mcp-remote 経由(Node.js 必要) |
| 3 | ChatGPT 非対応 | 静的 Bearer トークンでは接続不可(OAuth 2.1 必須)。利用者への事前周知と手順書への明記で誤解を防ぐ |
| 4 | 既存 EC2 への負荷 | mappy_search_rankings は 1,000 万行級。ユーザー単位 60 req/分のレート制限 + 期間上限 13 ヶ月 + 既存レポートと同等クエリの流用で抑制する |
| 5 | トークン運用 | Phase 1 は無期限トークン(発行は管理者のみ)。最終使用日時を記録し、定期棚卸し・漏洩時の即時失効を運用手順書に含める |
| 6 | keyword_list の外部 API 依存 | GBP Performance API 直呼びのため、Google アクセストークン失効時は当該 tool のみ業務エラーとなる(他 6 tool には影響しない) |
関連リンク
- 技術設計書: MCP サーバー v3 — テーブル DDL / JSON-RPC 仕様 / エラー表 / クライアント接続互換の詳細
- 旧提案: MCP サーバー v2 — アーカイブ(Lambda + OAuth 2.1 構成。DB スキーマ調査は本設計に流用)
- v2 tool 設計: DB 実スキーマ・SQL 記述 — 流用元
- GitLab Issue Mappy #58