---
title: "MCPサーバーは1ファイルで作れる｜docstringがツール選択の精度を決める"
excerpt: "Python + FastMCPでGoogle広告のMCPサーバーを構築しました。1ファイル・14ツールで、AIに「過去30日のパフォーマンスを見せて」と聞くだけでデータが返ります。実装より効くのはdocstringの品質。AIが渡す値の揺れを受け側で吸収する設計や、GAQLの注意点もまとめています。"
author: "AI活編集部"
author_username: ai-katsu-editorial
published_at: 2026-04-05T00:00:00+00:00
verified_at: 2026-04-05T00:00:00+00:00
models: ["Claude"]
tools: ["Google Ads", "MCP"]
tasks: ["開発・エンジニアリング", "ツール検証"]
domain: "マーケティング"
canonical_url: https://ai-katsu.jp/articles/google-ads-mcp-local-server
---
# MCPサーバーは1ファイルで作れる｜docstringがツール選択の精度を決める

認証を通したら、次はMCPサーバーの実装です。ここは拍子抜けするほど簡単で、**`google_ads_server.py` という1ファイルで完結**します。

この検証では、Python + FastMCPでGoogle広告のMCPサーバーを構築し、**AIに「過去30日のキャンペーンパフォーマンスを見せて」と聞くだけでデータが返る状態**を作りました。

> 本記事は3回シリーズの第2回です。第1回では認証設定、第3回ではリモート化を扱います。

## この検証の概要

| 項目 | 内容 |
| --- | --- |
| 検証時期 | 2026年4月5日 |
| やりたかったこと | 認証基盤の上にMCPサーバーを構築し、Claude DesktopやCursorから自然言語でGoogle広告データを操作する |
| 技術スタック | Python 3.11+ / mcp（FastMCP）>= 1.3.0 / google-auth / requests / pydantic |
| 実装規模 | **1ファイル・14ツール** |
| 接続先 | Claude Desktop / Cursor（stdioトランスポート） |

## 結論：docstringの品質がツール選択の精度を決める

FastMCPでは `@mcp.tool()` デコレータを付けるだけで、Python関数がMCPツールとして公開されます。

```
@mcp.tool()
async def list_accounts() -> str:
    """アクセス可能なすべてのGoogle Adsアカウントを一覧表示します。"""
    creds = get_credentials()
    headers = get_headers(creds)
    url = f"https://googleads.googleapis.com/{API_VERSION}/customers:listAccessibleCustomers"
    response = requests.get(url, headers=headers)
    # ... 結果をフォーマットして返す
```

ここで**最も重要なのはdocstringです。**AIはこの説明文を読んで「このツールは何ができるのか」を判断します。

**つまりdocstringの品質が、そのままツール選択の精度に直結します。**実装が正しくても説明が曖昧だと、意図したツールが呼ばれません。

パラメータについても同様で、Pydanticの `Field` に説明を付けます。

```
@mcp.tool()
async def execute_gaql_query(
    customer_id: str = Field(description="Google Ads顧客ID（10桁、ハイフンなし）"),
    query: str = Field(description="GAQLクエリ文字列")
) -> str:
    """カスタムGAQL（Google Ads Query Language）クエリを実行します。"""
```

AIはこのdescriptionを見て、ユーザーの入力からパラメータをマッピングします。

## 完成イメージ

| やりたいこと | AIへの指示例 | 呼ばれるツール |
| --- | --- | --- |
| アカウント確認 | 「アカウント一覧を表示して」 | `list_accounts` |
| パフォーマンス分析 | 「過去30日のキャンペーンパフォーマンスを見せて」 | `get_campaign_performance` |
| カスタムクエリ | GAQLを直接指定 | `execute_gaql_query` |
| 広告クリエイティブ確認 | 「広告の見出しと説明文を一覧で見たい」 | `get_ad_creatives` |
| トークン確認 | 「認証トークンの有効性を確認して」 | `check_token_validity` |

**GAQLクエリもAIが組み立てるため、クエリ構文を覚える必要はありません。**

## 提供する14のツール

| カテゴリ | ツール |
| --- | --- |
| **アカウント** | `list_accounts` / `get_account_currency` |
| **クエリ・レポート** | `execute_gaql_query` / `run_gaql` / `get_campaign_performance` / `get_ad_performance` / `get_ad_creatives` / `list_resources` |
| **アセット** | `get_image_assets` / `download_image_asset` / `get_asset_usage` / `analyze_image_assets` |
| **認証** | `check_token_validity` / `refresh_access_token` |

実際に使うと、**ほとんどのケースは `list_accounts` → `get_campaign_performance` のワークフローか、`run_gaql` でカスタムクエリを投げるパターンに集約されます。**

ツールを14個用意しても、日常的に使われるのは数個です。それでも網羅しておくと、AIが状況に応じて選べます。

## 認証は2方式を切り替え可能に

環境変数 `GOOGLE_ADS_AUTH_TYPE` で切り替えます。

| 方式 | 用途 | 特徴 |
| --- | --- | --- |
| **OAuth 2.0**（デフォルト） | 個人ユーザー向け | トークンファイルから読み込み、期限切れなら自動リフレッシュ、無ければブラウザ認証 |
| **サービスアカウント** | 自動化システム向け | JSONキーから認証。**ドメイン委任にも対応** |

```
def get_oauth_credentials():
    """OAuthユーザー認証情報を取得・更新します。"""
    creds = None
    token_path = GOOGLE_ADS_CREDENTIALS_PATH

    if os.path.exists(token_path):
        creds = Credentials.from_authorized_user_info(creds_data, SCOPES)

    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())  # 自動リフレッシュ
        else:
            flow = InstalledAppFlow.from_client_config(client_config, SCOPES)
            creds = flow.run_local_server(port=8080)

    return creds
```

## 地味にハマる：顧客IDのフォーマット

実運用で効いてくる細部です。

**ユーザーはハイフン付き・引用符付きなど、さまざまな形式で顧客IDを入力してきます。**そこで正規化関数を用意しています。

```
def format_customer_id(customer_id: str) -> str:
    customer_id = str(customer_id)
    customer_id = customer_id.replace('\\"', '').replace('"', '')
    customer_id = ''.join(char for char in customer_id if char.isdigit())
    return customer_id.zfill(10)
```

`123-456-7890` でも `"1234567890"` でも、すべて `1234567890` に正規化されます。

**AIが渡してくる値の揺れを吸収する関数です。**人間相手なら「ハイフンなしで入力してください」と書けますが、AI経由では入力形式を制御できません。受け側で吸収する必要があります。

## GAQLで知っておくべき3点

```
SELECT campaign.name, metrics.clicks, metrics.impressions, metrics.cost_micros
FROM campaign
WHERE segments.date DURING LAST_30_DAYS
ORDER BY metrics.cost_micros DESC
LIMIT 50
```

-   **`cost_micros` は100万分の1単位**——`1,000,000` = 1通貨単位
-   **日付フィルタは定数が使える**——`DURING LAST_7_DAYS`、`DURING LAST_30_DAYS` など
-   **出力形式を切り替えられる**——`run_gaql` なら table / json / csv

SQLとの違いとして、**JOINが不要**（リソース間の関係はAPI側で解決される）、**`*` によるワイルドカード選択ができない**点があります。

ただし細かい構文を覚える必要はほとんどありません。「過去7日間でクリック数が多い順にキャンペーンを表示して」と伝えれば、AIが適切なクエリを生成します。

## 接続設定

stdioトランスポートで起動するため、クライアント側がプロセスを直接起動して通信します。

```
if __name__ == "__main__":
    mcp.run(transport="stdio")
```

Claude Desktopの場合は設定ファイルに追記します。

```
{
  "mcpServers": {
    "google-ads": {
      "command": "python",
      "args": ["/path/to/mcp-google-ads/google_ads_server.py"],
      "env": {
        "GOOGLE_ADS_AUTH_TYPE": "oauth",
        "GOOGLE_ADS_CREDENTIALS_PATH": "/path/to/google_ads_token.json",
        "GOOGLE_ADS_DEVELOPER_TOKEN": "your-developer-token",
        "GOOGLE_ADS_LOGIN_CUSTOMER_ID": "your-mcc-id"
      }
    }
  }
}
```

Cursorの場合は Settings → MCP から「Add new MCP server」で、Typeを command、Commandに実行パスを指定します。

## テストMCCでの動作確認手順

Developer TokenがPending状態でも、以下の順で確認できます。

1.  **`list_accounts`** — テストMCC配下のアカウントIDが表示されればOK
2.  **`get_account_currency`** — `JPY` や `USD` が返れば正常
3.  **`get_campaign_performance`** — **テストMCCにはキャンペーンがないので空データが返るが、エラーが出なければAPI通信は成功**

本番承認後は、**`GOOGLE_ADS_LOGIN_CUSTOMER_ID` を本番MCCのIDに切り替えるだけで移行完了**です。

## ローカルMCPサーバーの限界

1ファイルで実用的なものが作れる一方、構造的な課題があります。

-   **PCが起動していないと使えない**——ローカルプロセスのため、外出先からは使えない
-   **チーム共有ができない**——各メンバーが個別にセットアップする必要がある
-   **認証情報の管理**——各端末に `.env` やトークンファイルを置く必要がある

**個人利用なら十分ですが、組織で使うとなると別の構成が必要になります。**次回はこれをリモート化します。

## よくある質問

### MCPサーバーの実装は難しいですか？

**1ファイルで完結します。**FastMCPの `@mcp.tool()` デコレータを付けるだけで、Python関数がMCPツールとして公開されます。

### 意図したツールが呼ばれません

**docstringを見直してください。**AIはdocstringを読んでツールを選択するため、説明の品質がそのまま選択精度になります。パラメータもPydanticの `Field` にdescriptionを付けます。

### GAQLの構文を覚える必要がありますか？

**ほとんど不要です。**「過去7日間でクリック数が多い順に」と伝えれば、AIが適切なクエリを生成します。ただし `cost_micros` が100万分の1単位である点は知っておくと解釈を誤りません。

### 顧客IDでエラーになります

**入力形式の揺れを吸収する正規化関数を用意してください。**AI経由では入力形式を制御できないため、ハイフンや引用符を除去して10桁ゼロ埋めする処理を受け側に置きます。

## まとめ

-   MCPサーバーは**1ファイル・14ツールで実用的なものが作れる**
-   **docstringの品質がツール選択の精度に直結する**。実装より説明が効く
-   認証は**OAuthとサービスアカウントの2方式**を環境変数で切り替え
-   **AIが渡す値の揺れは受け側で吸収する**（顧客IDの正規化）
-   GAQLは**JOIN不要・ワイルドカード不可**。`cost_micros` は100万分の1単位
-   実際に使われるのは**14ツールのうち数個**に集約される
-   ローカル構成の限界は**PC依存・チーム共有不可・認証情報の分散**

AIに外部APIを触らせる仕組みでは、機能の多さより「AIが正しく選べるか」が実用性を決めます。説明文の設計が実装と同じくらい重要になります。

株式会社AI棒では、こうしたAI活用の検証を日々行い、企業の広告運用・マーケティング業務へのAI導入を支援しています。ご相談は[お問い合わせフォーム](/contact)から、継続的な伴走支援については[AI参謀](/ai-sanbo)をご覧ください。
