Łączenie się z serwerem MCP Developer Knowledge za pomocą pakietów SDK MCP

Oficjalne pakiety SDK Model Context Protocol (MCP) umożliwiają aplikacjom i agentom AI programowe łączenie się z serwerem Developer Knowledge MCP. Użyj pakietu SDK MCP, gdy tworzysz niestandardowego agenta, usługę backendu lub własny serwer MCP, który musi wyszukiwać i pobierać oficjalną dokumentację dla deweloperów Google Developers, zamiast konfigurować istniejące IDE lub wspomaganie kodowania.

Z tego przewodnika dowiesz się, jak:

  • zainstalować pakiet SDK klienta MCP dla Pythona lub TypeScripta;
  • połączyć się z serwerem Developer Knowledge MCP za pomocą klucza interfejsu API lub domyślnego uwierzytelniania aplikacji (ADC);
  • wywoływać narzędzia search_documents, get_documents i answer_query;
  • obsługiwać błędy uwierzytelniania, limity i błędy narzędzi.

Zanim zaczniesz

Zanim zaczniesz, wykonaj czynności opisane w tych sekcjach.

Włączanie interfejsu API i tworzenie danych logowania

Włącz Developer Knowledge API w projekcie w chmurze Google Cloud i utwórz dane logowania dla metody uwierzytelniania. Szczegółowe instrukcje znajdziesz w artykule refer to Wybieranie metody uwierzytelniania.

Przykłady w tym przewodniku używają klucza interfejsu API. Aby zamiast tego użyć ADC, zapoznaj się z artykułem Uwierzytelnianie za pomocą domyślnego uwierzytelniania aplikacji.

Instalowanie pakietu SDK MCP

Aby zainstalować pakiet SDK klienta MCP, wybierz język programowania:

Python

W tym przewodniku używamy pakietu SDK MCP dla Pythona w wersji 2, która wymaga Pythona w wersji 3.10 lub nowszej. Wersja 1.x pakietu SDK używa innych nazw klas i funkcji, więc te przykłady nie będą z nią działać:

pip install --upgrade "mcp>=2"

TypeScript

W tym przewodniku używamy pakietu SDK MCP dla TypeScripta z Node.js w wersji 18 lub nowszej:

npm install @modelcontextprotocol/sdk

Ustawianie klucza interfejsu API

Aby uniknąć zakodowania klucza interfejsu API w kodzie źródłowym, zapisz go w zmiennej środowiskowej. Zastąp YOUR_API_KEY swoim kluczem interfejsu API:

export DEVELOPERKNOWLEDGE_API_KEY="YOUR_API_KEY"

Nawiązywanie połączenia z serwerem

Serwer Developer Knowledge MCP używa transportu HTTP z możliwością przesyłania strumieniowego pod tym adresem:

https://developerknowledge.googleapis.com/mcp

Aby się uwierzytelnić, przekaż klucz interfejsu API w nagłówku HTTP X-Goog-Api-Key w każdym żądaniu. Nie przekazuj klucza interfejsu API jako parametru zapytania URL.

Ten przykład łączy się z serwerem i wyświetla listę dostępnych narzędzi:

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();

Jeśli połączenie się powiedzie, w danych wyjściowych zostaną wyświetlone 3 narzędzia:

Available tools: search_documents, answer_query, get_documents

Pozostałe przykłady w tym przewodniku są uruchamiane w połączonym kliencie z tej sekcji. Przykłady w TypeScriptcie używają await najwyższego poziomu, więc uruchamiaj je jako moduły ES (np. za pomocą npx tsx index.ts).

Wywoływanie narzędzi

Każde narzędzie zwraca wynik jako uporządkowaną treść zgodną ze schematem wyjściowym narzędzia, dzięki czemu możesz odczytywać pola bezpośrednio, zamiast analizować tekst. Pełne schematy wejściowe i wyjściowe znajdziesz w dokumentacji MCP.

Wyszukiwanie dokumentacji

Wywołaj search_documents z ciągiem znaków query, aby pobrać najbardziej odpowiednie fragmenty dokumentacji. Każdy wynik zawiera fragment content i nazwę zasobu parent dokumentu, z którego pochodzi:

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`);
}

Pobieranie pełnych dokumentów

Jeśli fragment nie zawiera wystarczającej ilości kontekstu, przekaż jedną lub więcej wartości parent do get_documents, aby pobrać pełną treść Markdown każdej strony. W jednym wywołaniu możesz poprosić o maksymalnie 20 dokumentów. Ponieważ kilka fragmentów może pochodzić z tego samego dokumentu, najpierw usuń duplikaty wartości 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);
}

Pełne dokumenty mogą być duże, dlatego wywołuj get_documents tylko wtedy, gdy fragmenty wyszukiwania nie są wystarczające. Więcej informacji znajdziesz w artykule Optymalizowanie okna kontekstu i wykorzystania tokenów.

Generowanie odpowiedzi

Wywołaj answer_query, aby wygenerować odpowiedź na podstawie korpusu Developer Knowledge. Wynik zawiera answerText i references użyte do jego wygenerowania:

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'));

Pole references zawiera nazwy zasobów dokumentów, które możesz przekazać do get_documents. Ten sam dokument może pojawić się więcej niż raz, więc przed pobraniem dokumentów usuń duplikaty. Narzędzie answer_query ma niższy limit niż inne narzędzia. Więcej informacji znajdziesz w artykule Limity.

Uwierzytelnianie za pomocą domyślnego uwierzytelniania aplikacji

Zamiast klucza interfejsu API możesz uwierzytelniać się za pomocą domyślnego uwierzytelniania aplikacji (ADC). ADC działa z danymi logowania użytkownika podczas lokalnego programowania oraz z kontami usług, gdy kod jest uruchamiany w Google Cloud.

Gdy używasz ADC, wysyłaj te nagłówki w każdym żądaniu:

  • Authorization: token dostępu OAuth 2.0 Bearer z zakresem https://www.googleapis.com/auth/cloud-platform.
  • X-Goog-User-Project: identyfikator projektu, w którym jest włączony Developer Knowledge API i który jest obciążany za limit. Twoje konto musi mieć w tym projekcie uprawnienie serviceusage.services.use.

Tokeny dostępu wygasają, więc klienci działający przez dłuższy czas muszą je odświeżać. Poniższe przykłady używają bibliotek uwierzytelniania Google, aby dodać bieżący token do każdego żądania.

  1. Skonfiguruj ADC na potrzeby lokalnego programowania, wykonując czynności opisane na karcie ADC w artykule Wybieranie typu danych logowania OAuth.

  2. Ustaw zmienną środowiskową GOOGLE_CLOUD_PROJECT na projekt, w którym jest włączony Developer Knowledge API. Przykłady wysyłają ten identyfikator projektu w nagłówku X-Goog-User-Project:

    export GOOGLE_CLOUD_PROJECT="PROJECT_ID"
    
  3. Zainstaluj bibliotekę uwierzytelniania Google w swoim języku:

    Python

    pip install --upgrade google-auth requests
    

    TypeScript

    npm install google-auth-library
    
  4. Utwórz klienta za pomocą 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);
    

Obsługiwanie błędów

Błędy z serwera Developer Knowledge MCP docierają do Twojego kodu na 2 sposoby:

  • Błędy żądań: błędy uwierzytelniania, uprawnień i limitów są zwracane ze stanem błędu HTTP, takim jak 400, 403, lub 429. Pakiety SDK zgłaszają je jako wyjątki: MCPError w Pythonie i StreamableHTTPError w TypeScriptcie. Przechwytuj te wyjątki wokół wywołań narzędzi.
  • Błędy narzędzi: gdy narzędzie działa, ale nie może zrealizować żądania, wynik ma ustawioną wartość true w polu is_error (Python) lub isError (TypeScript), a pole content zawiera tekstowy opis problemu. Przed odczytaniem uporządkowanej treści sprawdź to pole.

Ten przykład obsługuje oba typy błędów:

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;
  }
}

Jeśli answer_query zwróci błąd limitu (429 RESOURCE_EXHAUSTED), wróć do search_documents, które ma wyższy limit. Informacje o strategiach ponawiania, w tym o wzrastającym czasie do ponowienia, znajdziesz w artykule Obsługa błędów i zarządzanie limitami. Typowe przyczyny błędów uwierzytelniania znajdziesz w artykułach Błędy klucza interfejsu API oraz Błędy OAuth i ADC.

Co dalej?