活用事例一覧へ
2026年7月27日開発・エンジニアリング検証時期:2026年4月

MCPサーバーは1ファイルで作れる|docstringがツール選択の精度を決める【AI活用検証vol.118】

MCPサーバーは1ファイルで作れる|docstringがツール選択の精度を決める【AI活用検証vol.118】

認証を通したら、次は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_accountsget_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_DAYSDURING 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_currencyJPYUSD が返れば正常
  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導入を支援しています。ご相談はお問い合わせフォームから、継続的な伴走支援についてはAI参謀をご覧ください。

関連する検証事例

© 株式会社AI棒 All Rights Reserved.