MCP SDK를 사용하여 개발자 지식 MCP 서버에 연결

공식 모델 컨텍스트 프로토콜 (MCP) SDK 를 사용하면 자체 애플리케이션과 AI 에이전트를 개발자 지식 MCP 서버에 프로그래매틱 방식으로 연결할 수 있습니다. 기존 IDE 또는 코딩 지원을 구성하는 대신 공식 Google Developers 개발자 문서를 검색하고 가져와야 하는 커스텀 에이전트, 백엔드 서비스 또는 자체 MCP 서버를 빌드할 때는 MCP SDK를 사용하세요.

이 가이드에서는 다음을 수행하는 방법을 알아봅니다.

  • Python 또는 TypeScript용 MCP 클라이언트 SDK를 설치합니다.
  • API 키 또는 애플리케이션 기본 사용자 인증 정보 (ADC)를 사용하여 개발자 지식 MCP 서버에 연결합니다.
  • search_documents, get_documents, answer_query 도구를 호출합니다.
  • 인증, 할당량, 도구 오류를 처리합니다.

시작하기 전에

시작하기 전에 다음 섹션을 완료하세요.

API 사용 설정 및 사용자 인증 정보 만들기

Google Cloud 프로젝트에서 개발자 지식 API를 사용 설정하고 인증 방법의 사용자 인증 정보를 만듭니다. 단계별 안내는 다음을 참고하세요 인증 방법 선택.

이 가이드의 예시에서는 API 키를 사용합니다. 대신 ADC를 사용하려면 애플리케이션 기본 사용자 인증 정보로 인증을 참고하세요.

MCP SDK 설치

프로그래밍 언어를 선택하여 MCP 클라이언트 SDK를 설치합니다.

Python

이 가이드에서는 Python 3.10 이상이 필요한 MCP Python SDK 버전 2를 사용합니다. SDK 버전 1.x는 다른 클래스 및 함수 이름을 사용하므로 이러한 예시는 버전 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"

서버에 연결

개발자 지식 MCP 서버는 스트리밍 가능 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();

연결에 성공하면 출력에 세 가지 도구가 나열됩니다.

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

전체 문서 가져오기

청크에 컨텍스트가 충분하지 않으면 하나 이상의 parent 값을 get_documents에 전달하여 각 페이지의 전체 Markdown 콘텐츠를 가져옵니다. 단일 호출에서 최대 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를 호출하여 개발자 지식 말뭉치에 기반한 답변을 생성합니다. 결과에는 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: 개발자 지식 API가 사용 설정되어 있고 할당량에 대해 청구되는 프로젝트의 ID입니다. 이 프로젝트에 대한 serviceusage.services.use 권한이 계정에 필요합니다.

액세스 토큰은 만료되므로 장기 실행 클라이언트는 액세스 토큰을 갱신해야 합니다. 다음 예시에서는 Google 인증 라이브러리를 사용하여 각 요청에 현재 토큰을 추가합니다.

  1. OAuth 사용자 인증 정보 유형 선택의 ADC 탭에 있는 단계에 따라 로컬 개발을 위한 ADC를 설정합니다.

  2. 개발자 지식 API가 사용 설정된 프로젝트로 GOOGLE_CLOUD_PROJECT 환경 변수를 설정합니다. 예시에서는 이 프로젝트 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);
    

오류 처리

개발자 지식 MCP 서버의 오류는 두 가지 방법으로 코드에 도달합니다.

  • 요청 오류: 인증, 권한, 할당량 오류는 400, 403, 429와 같은 HTTP 오류 상태와 함께 반환됩니다. 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 오류를 참고하세요.

다음 단계

  • MCP 참조에서 전체 도구 스키마를 살펴봅니다.
  • 말뭉치 참조에서 색인이 생성된 제품을 알아봅니다.
  • 클라이언트 라이브러리를 사용하여 개발자 지식 API를 직접 호출합니다.