MCP SDK を使用して Developer Knowledge MCP サーバーに接続する

公式の Model Context Protocol(MCP)SDK を使用すると、独自のアプリケーションや AI エージェントを Developer Knowledge MCP サーバーに プログラムで接続できます。既存の IDE やコーディング支援を構成するのではなく、Google Developers の公式デベロッパー向けドキュメントを検索して取得する必要があるカスタム エージェント、バックエンド サービス、または独自の MCP サーバーを構築する場合は、MCP SDK を使用します。

このガイドでは、次の方法について説明します。

  • Python または TypeScript 用の MCP クライアント SDK をインストールする。
  • API キーまたはアプリケーションのデフォルト認証情報(ADC)を使用して、Developer Knowledge MCP サーバーに接続する。
  • search_documents、get_documents、answer_query ツールを呼び出す。
  • 認証、割り当て、ツールのエラーを処理する。

始める前に

始める前に、次のセクションを完了してください。

API を有効にして認証情報を作成する

Google Cloud プロジェクトで Developer Knowledge API を有効にして、認証方法の認証情報を作成します。手順については、 認証方法を選択する をご覧ください。

このガイドの例では、API キーを使用します。代わりに ADC を使用するには、 アプリケーションのデフォルト認証情報で認証するをご覧ください。

MCP SDK をインストールする

プログラミング言語を選択して、MCP クライアント SDK をインストールします。

Python

このガイドでは、 MCP Python SDK のバージョン 2 を使用します。これには Python 3.10 以降が必要です。SDK のバージョン 1.x ではクラス名と関数名が異なるため、これらの例は機能しません。

pip install --upgrade "mcp>=2"

TypeScript

このガイドでは、Node.js 18 以降で MCP TypeScript SDK を使用します。

npm install @modelcontextprotocol/sdk

API キーを設定する

ソースコードに API キーをハードコードしないように、環境変数に保存します。YOUR_API_KEY を実際の API キーに置き換えます。

export DEVELOPERKNOWLEDGE_API_KEY="YOUR_API_KEY"

サーバーに接続する

Developer Knowledge MCP サーバーは、次のエンドポイントで Streamable HTTP トランスポート を使用します。

https://developerknowledge.googleapis.com/mcp

認証を行うには、リクエストごとに X-Goog-Api-Key HTTP ヘッダーに API キーを渡します。API キーを URL クエリ パラメータとして渡さないでください。

次の例では、サーバーに接続して使用可能なツールを一覧表示します。

Python

import asyncio
import os

from mcp import Client
from mcp.client.streamable_http import (
    create_mcp_http_client,
    streamable_http_client,
)

MCP_URL = "https://developerknowledge.googleapis.com/mcp"

async def main() -> None:
    headers = {"X-Goog-Api-Key": os.environ["DEVELOPERKNOWLEDGE_API_KEY"]}

    async with create_mcp_http_client(headers=headers) as http_client:
        transport = streamable_http_client(MCP_URL, http_client=http_client)
        async with Client(transport) as client:
            tools = await client.list_tools()
            names = ", ".join(tool.name for tool in tools.tools)
            print("Available tools:", names)

if __name__ == "__main__":
    asyncio.run(main())

TypeScript

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import {
  StreamableHTTPClientTransport,
} from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const apiKey = process.env.DEVELOPERKNOWLEDGE_API_KEY;
if (!apiKey) {
  throw new Error('Set the DEVELOPERKNOWLEDGE_API_KEY variable.');
}

const transport = new StreamableHTTPClientTransport(
  new URL('https://developerknowledge.googleapis.com/mcp'),
  { requestInit: { headers: { 'X-Goog-Api-Key': apiKey } } },
);

const client = new Client({ name: 'my-docs-agent', version: '1.0.0' });
await client.connect(transport);

const { tools } = await client.listTools();
console.log('Available tools:', tools.map((tool) => tool.name).join(', '));

await client.close();

接続に成功すると、出力には次の 3 つのツールが表示されます。

Available tools: search_documents, answer_query, get_documents

このガイドの残りの例は、このセクションで接続したクライアント内で実行されます。TypeScript の例ではトップレベルの await を使用するため、ES モジュールとして実行します(例: npx tsx index.ts)。

ツールを呼び出す

各ツールは、ツールの出力スキーマに一致する構造化コンテンツとして結果を返します。そのため、テキストを解析するのではなく、フィールドを直接読み取ることができます。完全な入力スキーマと出力スキーマについては、 MCP リファレンスをご覧ください。

ドキュメントを検索する

query 文字列を指定して search_documents を呼び出し、最も関連性の高いドキュメント チャンクを取得します。各結果には、チャンク content と、そのチャンクの元のドキュメントの parent リソース名が含まれます。

Python

search = await client.call_tool(
    "search_documents",
    {"query": "How do I create a Cloud Storage bucket?"},
)
results = search.structured_content["results"]
for chunk in results[:3]:
    print(f"{chunk['parent']}\n{chunk['content'][:200]}...\n")

TypeScript

const search = await client.callTool({
  name: 'search_documents',
  arguments: { query: 'How do I create a Cloud Storage bucket?' },
});
const { results } = search.structuredContent as {
  results: { parent: string; id: string; content: string }[];
};
for (const chunk of results.slice(0, 3)) {
  console.log(`${chunk.parent}\n${chunk.content.slice(0, 200)}...\n`);
}

完全なドキュメントを取得する

チャンクに十分なコンテキストが含まれていない場合は、1 つ以上の parent 値を get_documents に渡して、各ページの完全な Markdown コンテンツを取得します。1 回の呼び出しで最大 20 個のドキュメントをリクエストできます。複数のチャンクが同じドキュメントから取得される可能性があるため、最初に重複する parent 値を削除します。

Python

names = list(dict.fromkeys(chunk["parent"] for chunk in results[:3]))
get = await client.call_tool("get_documents", {"names": names})
for document in get.structured_content["documents"]:
    print(f"{document['title']} ({document['uri']})")
    print(document["content"])

TypeScript

const names = [...new Set(results.slice(0, 3).map((c) => c.parent))];
const get = await client.callTool({
  name: 'get_documents',
  arguments: { names },
});
const { documents } = get.structuredContent as {
  documents: {
    name: string;
    uri: string;
    title: string;
    content: string;
  }[];
};
for (const document of documents) {
  console.log(`${document.title} (${document.uri})`);
  console.log(document.content);
}

完全なドキュメントは大きくなる可能性があるため、検索チャンクが不十分な場合にのみ get_documents を呼び出します。詳細については、 コンテキスト ウィンドウとトークンの使用量を最適化するをご覧ください。

回答を生成する

answer_query を呼び出して、 Developer Knowledge コーパスに基づいて回答を生成します。結果には、answerText と、生成に使用された references が含まれます。

Python

answer = await client.call_tool(
    "answer_query",
    {"query": "How do I create a Cloud Storage bucket?"},
)
print(answer.structured_content["answerText"])
for reference in answer.structured_content["references"]:
    print(reference)

TypeScript

const answer = await client.callTool({
  name: 'answer_query',
  arguments: { query: 'How do I create a Cloud Storage bucket?' },
});
const { answerText, references } = answer.structuredContent as {
  answerText: string;
  references: string[];
};
console.log(answerText);
console.log(references.join('\n'));

references フィールドには、get_documents に渡すことができるドキュメント リソース名が含まれています。同じドキュメントが複数回表示される可能性があるため、ドキュメントを取得する前に重複を削除してください。answer_query ツールの割り当ては、他のツールよりも少なくなっています。詳細については、 割り当てと上限をご覧ください。

アプリケーションのデフォルト認証情報で認証する

API キーの代わりに、 アプリケーションのデフォルト認証情報(ADC)で認証できます。 ADC は、ローカル開発ではユーザー認証情報を使用し、コードが Google Cloud で実行される場合はサービス アカウントを使用します。

ADC を使用する場合は、リクエストごとに次のヘッダーを送信します。

  • Authorization: https://www.googleapis.com/auth/cloud-platform スコープを持つ Bearer OAuth 2.0 アクセス トークン。
  • X-Goog-User-Project: Developer Knowledge API が有効になっていて、割り当ての課金対象となるプロジェクトの ID。このプロジェクトに対する serviceusage.services.use 権限が必要です。

アクセス トークンは期限切れになるため、長時間実行されるクライアントは更新する必要があります。次の例では、Google 認証ライブラリを使用して、各リクエストに現在のトークンを追加します。

  1. OAuth 認証情報のタイプを選択するの [Choose your OAuth credential type] [ADC] タブの手順に沿って、ローカル開発用に ADC を設定します。

  2. GOOGLE_CLOUD_PROJECT 環境変数を、Developer Knowledge API が有効になっているプロジェクトに設定します。次の例では、このプロジェクト ID を X-Goog-User-Project ヘッダーで送信します。

    export GOOGLE_CLOUD_PROJECT="PROJECT_ID"
    
  3. 使用する言語の Google 認証ライブラリをインストールします。

    Python

    pip install --upgrade google-auth requests
    

    TypeScript

    npm install google-auth-library
    
  4. ADC を使用してクライアントを作成します。

    Python

    import os
    
    import google.auth
    import google.auth.transport.requests
    import httpx2  # Installed automatically with mcp>=2
    from mcp import Client
    from mcp.client.streamable_http import (
        create_mcp_http_client,
        streamable_http_client,
    )
    
    MCP_URL = "https://developerknowledge.googleapis.com/mcp"
    
    class GoogleCredentialsAuth(httpx2.Auth):
        """Adds a current ADC access token and quota project to requests."""
    
        def __init__(self, project_id: str) -> None:
            self.credentials, _ = google.auth.default(
                scopes=["https://www.googleapis.com/auth/cloud-platform"]
            )
            self.project_id = project_id
    
        def auth_flow(self, request):
            if not self.credentials.valid:
                self.credentials.refresh(
                    google.auth.transport.requests.Request()
                )
            request.headers["Authorization"] = (
                f"Bearer {self.credentials.token}"
            )
            request.headers["X-Goog-User-Project"] = self.project_id
            yield request
    
    async def main() -> None:
        auth = GoogleCredentialsAuth(os.environ["GOOGLE_CLOUD_PROJECT"])
        async with create_mcp_http_client(auth=auth) as http_client:
            transport = streamable_http_client(
                MCP_URL, http_client=http_client
            )
            async with Client(transport) as client:
                ...
    

    TypeScript

    import { GoogleAuth } from 'google-auth-library';
    import { Client } from '@modelcontextprotocol/sdk/client/index.js';
    import {
      StreamableHTTPClientTransport,
    } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
    
    const auth = new GoogleAuth({
      scopes: 'https://www.googleapis.com/auth/cloud-platform',
    });
    const projectId = process.env.GOOGLE_CLOUD_PROJECT;
    if (!projectId) {
      throw new Error('Set the GOOGLE_CLOUD_PROJECT variable.');
    }
    
    const transport = new StreamableHTTPClientTransport(
      new URL('https://developerknowledge.googleapis.com/mcp'),
      {
        // Add a current access token and the quota project to each request.
        fetch: async (url, init) => {
          const headers = new Headers(init?.headers);
          for (const [name, value] of await auth.getRequestHeaders()) {
            headers.set(name, value);
          }
          headers.set('X-Goog-User-Project', projectId);
          return fetch(url, { ...init, headers });
        },
      },
    );
    
    const client = new Client({ name: 'my-docs-agent', version: '1.0.0' });
    await client.connect(transport);
    

エラーを処理する

Developer Knowledge MCP サーバーからのエラーは、次の 2 つの方法でコードに到達します。

  • リクエスト エラー: 認証エラー、権限エラー、割り当てエラーは、 HTTP エラー ステータス (400、403、429 など) で返されます。SDK は、Python では MCPError、TypeScript では StreamableHTTPError として例外を発生させます。ツール呼び出しの周囲でこれらの例外をキャッチします。
  • ツールエラー: ツールが実行されてもリクエストを完了できない場合、 結果の is_error(Python)または isError(TypeScript)が true に設定され、 content フィールドに問題のテキスト説明が含まれます。構造化コンテンツを読み取る前に、このフィールドを確認してください。

次の例では、両方のエラータイプを処理します。

Python

from mcp import MCPError

try:
    result = await client.call_tool(
        "search_documents", {"query": "Cloud Storage buckets"}
    )
except MCPError as error:
    print(f"Request failed ({error.code}): {error.message}")
else:
    if result.is_error:
        print("Tool error:", result.content)
    else:
        results = result.structured_content["results"]

TypeScript

import {
  StreamableHTTPError,
} from '@modelcontextprotocol/sdk/client/streamableHttp.js';

try {
  const result = await client.callTool({
    name: 'search_documents',
    arguments: { query: 'Cloud Storage buckets' },
  });
  if (result.isError) {
    console.error('Tool error:', result.content);
  } else {
    const { results } = result.structuredContent as {
      results: { parent: string; id: string; content: string }[];
    };
  }
} catch (error) {
  if (error instanceof StreamableHTTPError) {
    console.error(`Request failed (${error.code}): ${error.message}`);
  } else {
    throw error;
  }
}

answer_query が割り当てエラー(429 RESOURCE_EXHAUSTED)を返した場合は、割り当ての多い search_documents にフォールバックします。再試行戦略(指数バックオフを含む)については、 エラー処理と割り当て管理をご覧ください。 認証エラーの一般的な原因については、 API キーのエラーと OAuth と ADC のエラーをご覧ください。

次のステップ