Gemini 統合を Skills 機能でシンプル化 - MCP からの移行
背景
以前、Gemini MCP サーバーを Claude Code に統合して、Claude のコンテキストを節約しながら Gemini の大規模コンテキストと Google Search を活用していました。
MCP サーバーは Code Assist API を直接呼び出す構成で、OAuth2 認証により安定して動作していました。
その後、Claude Code に Skills 機能が登場しました。Skills は専門知識を発見可能な機能としてパッケージ化する仕組みで、Claude が自律的に判断して使用(model-invoked)する点が特徴です。
Skills の登場により、MCP サーバーを使わずとも、Bash + Node.js スクリプトで直接 API を呼び出す方式で同じ機能を実現できることに気づきました。
Skills とは
Skills は Claude Code の機能拡張の仕組みです。MCP が外部ツールとの統合プロトコルであるのに対し、Skills は専門知識をパッケージ化して Claude に提供します。
Skills には、従来の MCP にはない 3 つの特徴があります。
まず、Model-invoked(モデルが自律判断)という仕組みです。Claude がリクエストのコンテキストと Skill の説明を基に、自律的に使用を判断します。ユーザーが明示的にトリガーする必要はありません。
次に、Progressive disclosure(段階的開示)です。SKILL.md の frontmatter(名前、説明)だけが最初にロードされ、詳細な指示やサポートファイルは必要な時だけロードされます。これによりトークン消費を削減できます。
最後に、Git ベースの配布です。プロジェクトの .claude/skills/ ディレクトリに配置することで、チーム全体で共有できます。
移行の理由
MCP サーバー経由の構成も問題なく動作していましたが、Skills を使った構成には以下のメリットがあり、移行を決めました。
Progressive disclosure によるトークン消費の削減
MCP の場合、すべてのツール定義(名前、説明、パラメータスキーマ)を Claude のコンテキストに含める必要があります。
Skills の場合、SKILL.md の frontmatter(数十トークン程度)だけが最初にロードされ、詳細なガイダンスは Claude が必要と判断した時だけロードされます。
シンプルな実装
MCP サーバーのコードを確認すると、実際には Code Assist API を fetch() で直接呼び出しているだけでした。
// MCP サーバー内部
const response = await fetch(`${CODE_ASSIST_BASE_URL}:generateContent`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify(requestBody),
});
このような単純な API 呼び出しであれば、MCP を経由する必要はありません。Bash ツールで Node.js スクリプトを直接実行する方がシンプルです。
プロセス階層の削減
MCP 構成では 4 層構造でした。
Claude → MCP Client → MCP Server (Node.js) → Code Assist API
Skills を使った構成では 3 層に削減されます。
Claude → Bash → Node.js スクリプト → Code Assist API
MCP Client と Server の間の stdio 通信とプロトコルオーバーヘッドが不要になります。
新しい構成
Skills を使った構成では、SKILL.md で使用方法を定義し、Node.js スクリプトで Code Assist API を直接呼び出します。Claude は Bash ツールでスクリプトを実行し、結果を取得します。
アーキテクチャの変化
上図は MCP サーバー経由の構成(4 層)と Skills を使った構成(3 層)の違いを示しています。MCP Client と Server の間の stdio 通信が不要になり、Claude が Bash ツールで直接 Node.js スクリプトを実行する構成に変更されました。
Code Assist API とは
Google Gemini Code Assist API (https://cloudcode-pa.googleapis.com/v1internal) は、Gemini モデルにアクセスするための API です。通常の Gemini API (generativelanguage.googleapis.com) とは異なるインフラで動作し、OAuth2 認証を使用します。
主な特性として、OAuth2 認証によるアクセストークン取得が必要で、Gemini Code Assist for individuals として動作します。
実装
Skill の作成
~/.claude/skills/gemini-api/SKILL.md を作成し、Code Assist API の使い方を Claude に教えます。
SKILL.md には以下の内容を記載しました。
- YAML フロントマター(Skill のメタデータ、名前、説明、使用可能なツール)
- 基本的な使い方(コマンド構文とモデルの選択)
- 使用判断フロー(いつ Code Assist API を使うべきかの判断基準)
- コマンドパターン(具体的な実行例)
主要なコマンドパターンは次の通りです。
# SwiftUI のベストプラクティス調査
~/.claude/skills/gemini-api/dist/index.js -p "SwiftUI ベストプラクティス 2025 パフォーマンス最適化" -m gemini-2.5-flash
# HTML の内容を要約(curl で取得後)
# curl で HTML 取得 → gemini-api で要約(パイプ経由)
curl -s https://example.com | ~/.claude/skills/gemini-api/dist/index.js -p "以下の HTML を日本語で要約してください" -m gemini-2.5-flash
# 技術選定の比較検討
~/.claude/skills/gemini-api/dist/index.js -p "MVVM vs VIPER vs Clean Architecture iOS アプリ開発 2025" -m gemini-2.5-pro
OAuth 認証
Code Assist API は OAuth2 認証を使用します。初回実行時の認証フローは以下の通りです。
まず、スクリプトが ~/.gemini/oauth_creds.json から認証情報を読み込みます。アクセストークンが期限切れの場合、自動的にリフレッシュします。新しいトークンをファイルに保存します。
認証情報は Gemini CLI と共有されるため、Gemini CLI を事前にセットアップしておく必要があります。
# 初回のみ Gemini CLI で OAuth 認証
npm install -g @google/gemini-cli
gemini # ブラウザで Google アカウント認証
# 以降は gemini-api が自動的にトークンを取得・更新
~/.claude/skills/gemini-api/dist/index.js -p "テスト"
認証情報は ~/.gemini/oauth_creds.json に保存され、access_token、refresh_token、expiry_date を含みます。プロジェクト ID は Code Assist API から自動取得されます(例:empirical-crawler-7k0xs)。
CLAUDE.md での優先順位設定
Claude が自動的に gemini-api Skill を使用するように、~/.claude/CLAUDE.md に設定を追加します。
情報検索・調査は gemini-api を最優先し、URL の内容取得は curl → gemini-api の組み合わせ(パイプ経由)を使用します。
情報検索の場合、Claude は情報検索が必要と判断すると、Skill ツールで gemini-api を invoke し、Bash ツールで ~/.claude/skills/gemini-api/dist/index.js コマンドを実行します。
URL 要約の場合、curl で HTML を取得し、パイプ経由で gemini-api に渡して要約します。
正しい実行例は以下の通りです。
# ユーザー: "https://example.com の内容を要約して"
# 正しい行動:
# curl で HTML 取得 → パイプで gemini-api に渡す
Skill(command: "gemini-api")
→ curl -s "https://example.com" | ~/.claude/skills/gemini-api/dist/index.js -p "以下の HTML を日本語で要約してください" -m gemini-2.5-flash
Code Assist API の制限と curl の活用
運用を進める中で、Code Assist API には重要な制限があることが判明しました。
URL コンテキストツールは非サポート
一般の Gemini API (generativelanguage.googleapis.com) では、URL コンテキストツールを使って Web ページの内容を直接取得できます。
// 一般の Gemini API(consumer 向け)
const result = await model.generateContent({
contents: [{ role: "user", parts: [{ text: "https://example.com を要約" }] }],
tools: [{ urlContext: {} }], // URL コンテキストツールを有効化
});
しかし、Code Assist API はこの機能をサポートしていません。
検証プロセス
URL コンテキストツールの動作を検証するため、3 つのテストを実施しました。
まず、URL を直接プロンプトに含めるテストです。
~/.claude/skills/gemini-api/dist/index.js \
-p "https://example.com/article の内容を要約" \
-m gemini-2.5-flash
結果は、URL の内容ではなく、全く異なる内容が返されました。
次に、tools パラメータで URL コンテキストを有効化するテストです。Code Assist API に tools: [{ url_context: {} }] を追加しました。
const response = await client.generateContent({
model,
contents: [{ role: "user", parts: [{ text: prompt }] }],
tools: [{ url_context: {} }], // URL コンテキスト有効化を試みる
});
結果は、urlContextMetadata が存在せず、依然として誤った内容が返されました。
最後に、curl で HTML 取得後、gemini-api で要約するテストです。
# 1. curl で HTML を取得
curl -s "https://example.com/article" > /tmp/article.html
# 2. gemini-api で要約
~/.claude/skills/gemini-api/dist/index.js \
-p "以下の HTML を要約: $(cat /tmp/article.html)" \
-m gemini-2.5-flash
結果は、正しく記事内容が要約されました。
curl との組み合わせ
Code Assist API は URL の直接フェッチをサポートしていないため、curl で HTML を取得してパイプで gemini-api に渡す方式を採用しました。
ユーザーが「https://example.com を要約して」と依頼した場合、Claude は以下のように処理します。
curl -s "https://example.com" | ~/.claude/skills/gemini-api/dist/index.js \
-p "以下の HTML を日本語で要約してください" \
-m gemini-2.5-flash
この方式には 3 つのメリットがあります。curl は標準的なツールで安定して動作する確実性があり、パイプで標準入力に渡すだけのシンプルさがあり、中間ファイルが不要な効率性を兼ね備えています。
gemini-api スクリプトに標準入力からの読み取り機能を追加しました。
async function readStdin(): Promise<string> {
// 標準入力がパイプから来ているかチェック
if (process.stdin.isTTY) {
return "";
}
const chunks: Buffer[] = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
return Buffer.concat(chunks).toString("utf-8");
}
async function main() {
const { prompt, model } = parseArgs();
const stdinContent = await readStdin();
// 標準入力がある場合は、それをプロンプトの前に追加
const fullPrompt = stdinContent
? `${stdinContent}\n\n${prompt}`
: prompt;
// API 呼び出し
const response = await client.generateContent({
model,
contents: [{ role: "user", parts: [{ text: fullPrompt }] }],
}, randomUUID());
}
SKILL.md に URL 要約のセクションを追加しました。Code Assist API は URL の直接フェッチをサポートしていないため、curl で HTML を取得してから gemini-api に渡す方式を推奨しています。
Claude が自動的に以下のパターンで処理します。curl で URL の HTML を取得し、gemini-api で HTML 内容を要約します。
実行例は以下の通りです。
curl -s https://example.com | ~/.claude/skills/gemini-api/dist/index.js \
-p "以下の HTML を日本語で3つのポイントにまとめてください" \
-m gemini-2.5-flash
API の違いまとめ
| 機能 | Code Assist API | Gemini API (consumer) |
|---|---|---|
| URL コンテキストツール | ❌ 非サポート | ✅ サポート |
| Grounding with Google Search | ❌ 不明 | ✅ サポート |
| 認証方式 | OAuth 2.0 | API Key |
| 用途 | コード生成・開発支援 | 汎用 AI タスク |
Code Assist API はコード生成に最適化されており、Web スクレイピング機能は範囲外と考えられます。
まとめ
Claude Code の Skills 機能により、専門知識をパッケージ化して Claude が自律的に使用できる仕組みが実現しました。MCP サーバー経由の構成から、Bash + Node.js による直接 API 呼び出しへ移行し、いくつかの改善を達成しました。
Progressive disclosure により SKILL.md の frontmatter のみ常時ロードし、詳細は必要時のみロードすることでトークンを削減しました。Model-invoked の仕組みにより、Claude がコンテキストに応じて自律的に Skill を使用します。プロセス階層も 4 層から 3 層へシンプル化されました。
最終的なアーキテクチャは、情報検索・技術調査は Code Assist API を直接呼び出し、URL の内容取得には curl と gemini-api の組み合わせ(パイプ経由)を使用し、コード生成・レビューは Claude が直接処理するという構成になりました。
MCP は外部ツールとの統合プロトコルとして強力ですが、単純な API 呼び出しであれば、Skills + Bash の方がシンプルです。Skills の Progressive disclosure により、トークン消費を抑えながら専門知識を提供できます。
Code Assist API の特性(URL コンテキスト非サポート)を理解し、curl との組み合わせで補完することで、柔軟な情報取得を実現しました。
技術的な詳細
実装構成
~/.claude/skills/gemini-api/
├── SKILL.md # Skill の定義と使用方法
├── package.json # 依存関係
├── tsconfig.json # TypeScript 設定
├── src/
│ ├── index.ts # CLI エントリーポイント
│ ├── oauth.ts # OAuth2 認証処理
│ ├── codeAssistClient.ts # Code Assist API クライアント
│ └── types.ts # 型定義
└── dist/
├── index.js # ビルド済み CLI
├── oauth.js
├── codeAssistClient.js
└── types.js
コアロジック
// OAuth2 認証
const oauth2Client = new OAuth2Client(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI);
const savedCreds = await loadCredentials(); // ~/.gemini/oauth_creds.json
oauth2Client.setCredentials(savedCreds);
const accessToken = await getAccessToken(oauth2Client);
// Code Assist API 呼び出し
const requestBody = {
model: "gemini-2.5-flash",
project: projectId, // Code Assist API から取得
user_prompt_id: randomUUID(),
request: {
contents: [
{
role: "user",
parts: [{ text: prompt }],
},
],
},
};
const response = await fetch(`https://cloudcode-pa.googleapis.com/v1internal:generateContent`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify(requestBody),
});
const data = await response.json();
const text = data.response.candidates[0].content.parts[0].text;
console.log(text);
Claude は Bash ツールでこのスクリプトを実行し、stdout から結果を取得します。OAuth2 認証、API 呼び出し、レスポンス解析をすべて Node.js スクリプト内で完結させます。
参考リンク
関連する技術スタック
Code Assist API は https://cloudcode-pa.googleapis.com/v1internal を使用し、OAuth2 認証には google-auth-library パッケージを使用しています。Gemini CLI で OAuth 認証情報の初期セットアップを実施します。Claude Code Skills は専門知識をパッケージ化し、Claude が自律的に使用できる機能拡張です。