1. บทนำ
ใน Codelab นี้ คุณจะได้เรียนรู้วิธีใช้ Google Cloud Agent Registry และ Agent Development Kit (ADK) เพื่อค้นหาและเรียกใช้เซิร์ฟเวอร์ Model Context Protocol (MCP) ที่ลงทะเบียนไว้แบบไดนามิกที่รันไทม์ ซึ่งจะช่วยลดการกำหนดค่าปลายทางและเครื่องมือที่ฮาร์ดโค้ดไว้
ห้องทดลองนี้ใช้ ADK และภาษาโปรแกรม Python เพื่อสร้างแอปพลิเคชัน หลักการเดียวกันนี้สามารถนำไปใช้ในภาษาโปรแกรมอื่นๆ และใช้เฟรมเวิร์กการพัฒนา Agent อื่นๆ ได้
สิ่งที่คุณต้องทำ
- เปิดใช้และกำหนดค่า Google Cloud Agent Registry ในโปรเจ็กต์
- ติดตั้งใช้งานเซิร์ฟเวอร์ MCP ใน Cloud Run และลงทะเบียนใน Agent Registry
- สร้าง ADK Agent ที่เรียกใช้เซิร์ฟเวอร์ MCP โดยใช้ URL ที่ฮาร์ดโค้ด
- เปลี่ยน Agent เพื่อค้นหาเซิร์ฟเวอร์ MCP ใน Agent Registry ตามข้อมูลประจำตัว
- เปลี่ยน Agent ให้ค้นหาเซิร์ฟเวอร์ MCP ขณะรันไทม์โดยใช้การค้นหาเชิงความหมาย
สิ่งที่คุณต้องมี
- เว็บเบราว์เซอร์ที่ทันสมัย (เช่น Chrome)
- โปรเจ็กต์ Google Cloud ที่เปิดใช้การเรียกเก็บเงิน
- บัญชีที่มีสิทธิ์สร้างและจัดการทรัพยากร รวมถึงกำหนดค่านโยบาย IAM ในโปรเจ็กต์ Google Cloud
2. ตั้งค่าสภาพแวดล้อม
วิธีการใน Codelab นี้จะถือว่าคุณเรียกใช้คำสั่งใน Cloud Shell ในคอนโซล Google Cloud Cloud Shell เป็นสภาพแวดล้อมบรรทัดคำสั่งที่ทำงานใน Google Cloud ซึ่งมาพร้อมกับเครื่องมือที่จำเป็นที่โหลดไว้ล่วงหน้า หากคุณติดตั้ง gcloud CLI, git และ Python 3 ในสภาพแวดล้อมภายในเครื่อง คุณจะเรียกใช้คำสั่งในสภาพแวดล้อมดังกล่าวได้ด้วย
สร้างโปรเจ็กต์ Google Cloud
- ในคอนโซล Google Cloud ในหน้าตัวเลือกโปรเจ็กต์ ให้สร้างโปรเจ็กต์ Google Cloud
- ตรวจสอบว่าได้เปิดใช้การเรียกเก็บเงินสำหรับโปรเจ็กต์ที่อยู่ในระบบคลาวด์แล้ว ดูวิธีตรวจสอบว่ามีการเปิดใช้การเรียกเก็บเงินในโปรเจ็กต์หรือไม่
เริ่มเทอร์มินัล Cloud Shell
- ใช้เครื่องมือเลือกทรัพยากรเพื่อเลือกโปรเจ็กต์ Google Cloud ที่คุณสร้างขึ้น
- คลิกเปิดใช้งาน Cloud Shell
ที่ด้านบนของคอนโซล Google Cloud
- เมื่อเชื่อมต่อกับ Cloud Shell แล้ว ให้ยืนยันการตรวจสอบสิทธิ์โดยทำดังนี้
gcloud auth list - ยืนยันว่าได้กำหนดค่าโปรเจ็กต์แล้ว โดยทำดังนี้
gcloud config get project - หากโปรเจ็กต์ไม่ได้ตั้งค่าตามที่คาดไว้ หรือหากคุณใช้สภาพแวดล้อมในเครื่องแทน Cloud Shell ให้ตั้งค่ารหัสโปรเจ็กต์โดยทำดังนี้
export GOOGLE_CLOUD_PROJECT=<YOUR_PROJECT_ID> gcloud config set project "${GOOGLE_CLOUD_PROJECT}"
เลือกโปรเจ็กต์ที่อยู่ในระบบคลาวด์ของ Google และตั้งค่าตัวแปรสภาพแวดล้อม
- เปิดใช้ Google Cloud API ที่จำเป็น ดังนี้
gcloud services enable \ agentregistry.googleapis.com \ aiplatform.googleapis.com \ cloudbuild.googleapis.com \ run.googleapis.com \ --project="${GOOGLE_CLOUD_PROJECT}"
ตั้งค่าสภาพแวดล้อม Python
- สร้างและเปิดใช้งานสภาพแวดล้อมเสมือน และติดตั้งทรัพยากร Dependency ที่จำเป็น ดังนี้
mkdir -p adk-dynamic-discovery && cd adk-dynamic-discovery python3 -m venv .venv source .venv/bin/activate pip install "fastmcp<5.0.0" "google-adk[mcp,a2a,agent-identity]>=2.5.0,<3.0.0"
3. สร้างและลงทะเบียนเซิร์ฟเวอร์ Zoo MCP
ในขั้นตอนนี้ คุณจะสร้างและลงทะเบียนเซิร์ฟเวอร์ Zoo MCP เซิร์ฟเวอร์จะแสดงเครื่องมือ 3 อย่าง ได้แก่ list_species แสดงรายการสายพันธุ์ทั้งหมดที่อาศัยอยู่ในสวนสัตว์ get_animals_by_species แสดงรายการสัตว์ของสายพันธุ์ที่เฉพาะเจาะจง และ get_animal_details รับข้อมูลเพิ่มเติมเกี่ยวกับสัตว์ที่เฉพาะเจาะจง
คุณดูไฟล์ทั้งหมดสำหรับขั้นตอนนี้ได้ใน GitHub
สร้างเซิร์ฟเวอร์ MCP
- ตั้งค่าโฟลเดอร์และโปรเจ็กต์ Python สำหรับเซิร์ฟเวอร์ MCP
mkdir mcp-on-cloudrun && cd mcp-on-cloudrun uv init --description "Example of deploying an MCP server on Cloud Run" --bare --python 3.13 uv add "fastmcp<5.0.0" --no-sync - ดาวน์โหลดโค้ดเซิร์ฟเวอร์ MCP โดยทำดังนี้
curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step3/server.py -o server.py
ติดตั้งใช้งานเซิร์ฟเวอร์ MCP ใน Cloud Run และลงทะเบียนที่ Agent Registry
ถัดไป คุณจะสร้าง Dockerfile สำหรับเซิร์ฟเวอร์และติดตั้งใช้งานเซิร์ฟเวอร์ใน Cloud Run
- สร้าง
Dockerfile:cat << EOF > Dockerfile # Use the official Python image FROM python:3.13-slim # Install uv COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ # Install the project into /app COPY . /app WORKDIR /app # Allow statements and log messages to immediately appear in the logs ENV PYTHONUNBUFFERED=1 # Install dependencies RUN uv sync # Run the FastMCP server CMD ["uv", "run", "server.py"] EOF - สร้างบัญชีบริการที่กำหนดสำหรับเซิร์ฟเวอร์ โดยทำดังนี้
gcloud iam service-accounts create zoo-mcp-server-sa \ --display-name="Zoo MCP Server Service Account" - ติดตั้งใช้งานเซิร์ฟเวอร์ในภูมิภาค
us-west1 หากเห็นข้อความแจ้งต่อไปนี้ ให้ยืนยันการสร้างที่เก็บ Artifact Registry ใหม่ ข้อความแจ้งนี้จะแสดงขึ้นหากคุณไม่มีที่เก็บ Artifact Registry ในโปรเจ็กต์สำหรับภูมิภาคgcloud run deploy zoo-mcp-server \ --service-account=zoo-mcp-server-sa@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \ --region=us-west1 \ --source=. \ --labels=dev-tutorial=codelab-dynamic-mcp-discovery \ --invoker-iam-check \ --no-allow-unauthenticatedus-west1Deploying from source requires an Artifact Registry Docker repository to store built containers. A repository named [cloud-run-source-deploy] in region [us-west1] will be created. Do you want to continue (Y/n)? - ลงทะเบียนเซิร์ฟเวอร์ MCP ใน Agent Registry ที่ตำแหน่ง "global" โดยทำดังนี้
คำสั่งเหล่านี้จะสร้างfastmcp inspect server.py --format mcp | jq 'del(..|nulls) | del(.tools[]?._meta?)' > toolspec.json MCP_URL=$(gcloud run services describe zoo-mcp-server --region=us-west1 --format='value(status.url)') gcloud agent-registry services create zoo-mcp-server \ --display-name="zoo-mcp-server" \ --project="${GOOGLE_CLOUD_PROJECT}" \ --location=global \ --mcp-server-spec-type="tool-spec" \ --mcp-server-spec-content="toolspec.json" \ --interfaces="url=${MCP_URL}/mcp,protocolBinding=jsonrpc"toolspec.jsonพร้อมไฟล์ Manifest ของเครื่องมือและเรียกใช้ Agent Registry API เพื่อลงทะเบียนเซิร์ฟเวอร์ - ตรวจสอบว่า Agent ปรากฏในรีจิสทรีโดยทำดังนี้
คุณจะเห็นเอาต์พุต YAML ที่มีข้อมูลเซิร์ฟเวอร์ MCP ดังนี้gcloud agent-registry mcp-servers list \ --location=global \ --filter="display_name=zoo-mcp-server"--- attributes: agentregistry.googleapis.com/system/RuntimeReference: uri: //agentregistry.googleapis.com/projects/[PROJECT_NUMBER]/locations/global/services/zoo-mcp-server createTime: '<create time datetime in ISO 8601>' displayName: zoo-mcp-server interfaces: - protocolBinding: JSONRPC url: https://zoo-mcp-server-[PROJECT_NUMBER].us-west1.run.app/mcp mcpServerId: urn:mcp:projects-[PROJECT_NUMBER]:projects:[PROJECT_NUMBER]:locations:global:agentregistry:services:zoo-mcp-server name: projects/[PROJECT_ID]/locations/global/mcpServers/agentregistry-00000000-0000-0000-1234-56789abcdef tools: - annotations: idempotentHint: true readOnlyHint: true description: |- Returns every species in the zoo. Use it first for zoo-wide questions such as counting all animals or listing everything the zoo has. name: list_species - annotations: idempotentHint: true readOnlyHint: true description: |- Retrieves all animals of a specific species from the zoo. Can also be used to collect the base data for aggregate queries of animals of a specific species - like counting the number of penguins or finding the oldest lion. name: get_animals_by_species - annotations: idempotentHint: true readOnlyHint: true description: Retrieves the details of a specific animal by its name. name: get_animal_details updateTime: '<last updated datetime in ISO 8601>'
4. สร้าง ADK Agent พื้นฐานเพื่อใช้เซิร์ฟเวอร์ MCP ของสวนสัตว์
ในขั้นตอนนี้ คุณจะสร้าง ADK Agent เพื่อตอบคำขอของผู้ใช้เกี่ยวกับสวนสัตว์ด้วยความช่วยเหลือจาก URL ของเซิร์ฟเวอร์ MCP ของสวนสัตว์ที่ฮาร์ดโค้ดไว้ การฮาร์ดโค้ดจะบังคับให้คุณต้องทราบ URL ของเซิร์ฟเวอร์ MCP ก่อนที่จะติดตั้งใช้งาน Agent
คุณดูไฟล์ต้นฉบับสำหรับขั้นตอนนี้ได้ใน GitHub
สร้างแอปพลิเคชัน Agent
สร้างโครงสร้างของโปรเจ็กต์ ADK และ Agent สวนสัตว์ที่ใช้เซิร์ฟเวอร์ MCP ของสวนสัตว์
- ตั้งค่าโฟลเดอร์และโปรเจ็กต์สำหรับ Agent ของ Zoo ดังนี้
mkdir ../sample-agent && cd ../sample-agent uv init --description "Example of ADK agent to showcase dynamic MCP discovery" --bare --python 3.13 uv add "google-adk[mcp,a2a,agent-identity]>=2.5.0,<3.0.0" --no-sync - สร้างโฟลเดอร์แพ็กเกจ Agent และดาวน์โหลดไฟล์
__init__.pyโดยทำดังนี้mkdir zoo_agent curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step4/zoo_agent/__init__.py -o zoo_agent/__init__.py - สร้างไฟล์
.envและป้อนตัวแปรสภาพแวดล้อมที่ใช้ในขั้นตอนนี้และขั้นตอนต่อไปนี้ คำสั่งนี้จะเขียนตัวแปรสภาพแวดล้อมทั้งหมดที่จำเป็นในขั้นตอนนี้และขั้นตอนการค้นหาที่ตามมาลงในMCP_URL=$(gcloud run services describe zoo-mcp-server --region=us-west1 --format='value(status.url)') cat << EOF > zoo_agent/.env GOOGLE_GENAI_USE_ENTERPRISE=TRUE GOOGLE_CLOUD_LOCATION=global GOOGLE_CLOUD_PROJECT="${GOOGLE_CLOUD_PROJECT}" GOOGLE_CLOUD_PROJECT_NUMBER=$(gcloud projects describe ${GOOGLE_CLOUD_PROJECT} \ --format='value(projectNumber)') MCP_SERVER_URL="${MCP_URL}" EOFzoo_agent/.envคุณจึงไม่จำเป็นต้องสร้างไฟล์อีกครั้งในภายหลัง ในขั้นตอนนี้ ระบบจะใช้ตัวแปรต่อไปนี้GOOGLE_GENAI_USE_ENTERPRISE,GOOGLE_CLOUD_PROJECTและGOOGLE_CLOUD_LOCATION: ADK และ Google GenAI SDK จะใช้โดยนัยเพื่อกำหนดเส้นทางการเรียกใช้โมเดล Gemini ผ่าน Vertex AI ในโปรเจ็กต์ที่อยู่ในระบบคลาวด์และตำแหน่ง Google Cloud ของคุณ (global) นอกจากนี้ยังจัดเก็บตำแหน่งสำหรับ Agent Registry ที่ลงทะเบียนเซิร์ฟเวอร์ MCP ด้วยGOOGLE_CLOUD_PROJECT_NUMBER: ไม่ได้ใช้ในขั้นตอนนี้ แต่จะป้อนข้อมูลล่วงหน้าที่นี่เพื่อใช้ในขั้นตอนถัดไปMCP_SERVER_URL: อ่านโดยตรงในzoo_agent/agent.pyเพื่อกำหนดค่าMcpToolsetด้วย URL ของ Cloud Run ที่ฮาร์ดโค้ดของเซิร์ฟเวอร์ Zoo MCP
- ดาวน์โหลด
agent.pyพร้อมโค้ดสำหรับ Zoo Agent ที่เชื่อมต่อกับเซิร์ฟเวอร์ Zoo MCP:curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step4/zoo_agent/agent.py -o zoo_agent/agent.py
วิธีการทำงานของการให้สิทธิ์เซิร์ฟเวอร์ MCP
เนื่องจากมีการติดตั้งใช้งานบริการ Cloud Run zoo-mcp-server ด้วยแฟล็ก --invoker-iam-check และ --no-allow-unauthenticated คำขอ HTTP ทุกรายการที่ส่งไปยังเซิร์ฟเวอร์ MCP จึงต้องมีโทเค็นระบุตัวตนที่ลงนามโดย Google ในส่วนหัว Authorization โทเค็นต้องระบุผู้โทรที่มีสิทธิ์ run.routes.invoke ในบริการ
ใน zoo_agent/agent.py คลาส _GoogleAuthHeaders จะได้รับข้อมูลเข้าสู่ระบบของโทเค็นระบุตัวตนด้วยวิธีใดวิธีหนึ่งจาก 2 วิธี ขั้นแรก ระบบจะเรียกใช้ fetch_id_token_credentials() เพื่อรับโทเค็นระบุตัวตนสำหรับ URL ของบริการจากเซิร์ฟเวอร์ข้อมูลเมตาหรือคีย์บัญชีบริการ หากไม่สำเร็จ ระบบจะใช้โทเค็นระบุตัวตนของข้อมูลรับรองเริ่มต้นของแอปพลิเคชัน (ADC) ของผู้ใช้ ทุกครั้งที่มีการเรียกใช้อินสแตนซ์ ระบบจะรีเฟรชโทเค็นหากจำเป็น และแสดงผลส่วนหัว Authorization ดังนี้
class _GoogleAuthHeaders:
def __init__(self, audience: str):
self._request = Request()
self._id_token = False
try:
self._credentials = id_token.fetch_id_token_credentials(audience, request=self._request)
except exceptions.DefaultCredentialsError:
self._credentials, _ = google.auth.default()
self._id_token = True
if not isinstance(self._credentials, credentials.Credentials):
raise
def __call__(self, unused_context: ReadonlyContext | None = None) -> dict[str, str]:
if not self._credentials.valid or (self._id_token and not self._credentials.id_token):
self._credentials.refresh(self._request)
token = self._credentials.id_token if self._id_token else self._credentials.token
return {"Authorization": f"Bearer {token}"}
Agent จะส่งอินสแตนซ์ของคลาสไปยัง McpToolset เป็นอาร์กิวเมนต์ header_provider ADK จะเรียกใช้ผู้ให้บริการส่วนหัวและแนบส่วนหัวที่แสดงผลไปยังคำขอทุกรายการที่ส่งไปยังเซิร์ฟเวอร์ MCP เนื่องจากไคลเอ็นต์ HTTP สร้างขึ้นโดย ADK โค้ดเดียวกันจึงทำงานได้ไม่ว่าคุณจะติดตั้ง MCP Python SDK และไลบรารี HTTP เวอร์ชันใดก็ตาม
zoo_mcp_toolset = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url=f"{MCP_SERVER_URL}/mcp",
timeout=30.0,
),
header_provider=_GoogleAuthHeaders(MCP_SERVER_URL),
)
กลุ่มเป้าหมายที่ขอคือ URL ของบริการ (MCP_SERVER_URL) ส่วน URL การเชื่อมต่อจะมีเส้นทาง /mcp ที่เซิร์ฟเวอร์ FastMCP แสดงโปรโตคอล MCP ระยะหมดเวลา 30 วินาทีช่วยให้มีเวลาสำหรับ Cold Start ของ Cloud Run
ทดสอบ Agent โดยใช้ Playground ของ ADK ในสภาพแวดล้อมในเครื่อง
ก่อนทดสอบ Agent โปรดตรวจสอบว่าบัญชีผู้ใช้ของคุณเรียกใช้เซิร์ฟเวอร์ MCP ได้
- เทอร์มินัลในเครื่องเท่านั้น (ข้ามขั้นตอนนี้ใน Cloud Shell): เริ่มต้น ADC ด้วยข้อมูลเข้าสู่ระบบของผู้ใช้ โดยทำดังนี้
ทำตามวิธีการเพื่อเริ่มต้นใช้งาน ADC ให้เสร็จสมบูรณ์gcloud auth application-default login - (ไม่บังคับ) ให้สิทธิ์บัญชีผู้ใช้ในการเรียกใช้เซิร์ฟเวอร์ MCP ระยะไกล โดยทำดังนี้
gcloud run services add-iam-policy-binding zoo-mcp-server \ --region=us-west1 \ --project=${GOOGLE_CLOUD_PROJECT} \ --member=user:$(gcloud config get-value account) \ --role='roles/run.invoker' - เรียกใช้ Agent ด้วยคำสั่ง
adk runดังนี้ หรือเรียกใช้อินเทอร์เฟซเว็บ ADK ด้วยคำสั่งadk run zoo_agentadk webดังนี้ แล้วเปิด URL ที่คำสั่งพิมพ์ในเบราว์เซอร์ หากใช้ Cloud Shell ให้เพิ่มอาร์กิวเมนต์adk web --port 8080--allow_originsเพื่ออนุญาตให้เปิดอินเทอร์เฟซเว็บจาก Cloud Shell หลังจากเรียกใช้คำสั่งแล้ว ให้ค้นหาไอคอนตัวอย่างเว็บadk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
ในแถบเครื่องมือ Cloud Shell (ด้านขวา) คลิกไอคอนดังกล่าว แล้วเลือกแสดงตัวอย่างในพอร์ต 8080 Cloud Shell จะเปิดแท็บเบราว์เซอร์ใหม่ที่แสดงอินเทอร์เฟซเว็บของ ADK - เริ่มแชทกับตัวแทน เช่น ถามเกี่ยวกับสัตว์ในสวนสัตว์
Agent จะเรียกใช้How many animals are in the zoo?list_speciesแล้วเรียกใช้get_animals_by_speciesสำหรับแต่ละสายพันธุ์เพื่อคำนวณคำตอบ
อินเทอร์เฟซเว็บของ ADK ช่วยให้คุณสำรวจวิธีการทำงานของ Agent ได้ รวมถึงการเรียกไปยังเซิร์ฟเวอร์ MCP ของสวนสัตว์ ดูวิธีได้ที่เอกสารประกอบเกี่ยวกับ Web UI
5. ค้นหาปลายทางของเซิร์ฟเวอร์ MCP แบบไดนามิก
ในขั้นตอนนี้ คุณจะเปลี่ยน Agent ให้ค้นหาปลายทางของเซิร์ฟเวอร์ MCP ของสวนสัตว์ที่ลงทะเบียนไว้ใน Agent Registry แทนการฮาร์ดโค้ด URL ปลายทาง
คุณดูไฟล์ทั้งหมดสำหรับขั้นตอนนี้ได้ใน GitHub
คุณใช้การผสานรวม ADK กับ Agent Registry เพื่อค้นหาและดึงข้อมูล URL ของเซิร์ฟเวอร์ คุณต้องมีข้อมูลต่อไปนี้เพื่อติดตั้งใช้งาน
- หมายเลขโปรเจ็กต์และตำแหน่งของ Agent Registry ที่เซิร์ฟเวอร์ MCP ของคุณลงทะเบียนไว้ (
global) - รหัสบริการที่คุณใช้ลงทะเบียนเซิร์ฟเวอร์ MCP (
zoo-mcp-server)
อัปเดตตัวแทนเพื่อใช้ Agent Registry
- ดาวน์โหลดไฟล์
agent.pyที่อัปเดตแล้วจาก GitHub เพื่อลบล้าง Agent เวอร์ชันแรกcurl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step5/zoo_agent/agent.py -o zoo_agent/agent.pyตัวแปรสภาพแวดล้อมในขั้นตอนนี้
ในการอัปเดตนี้ Agent จะใช้ตัวแปรสภาพแวดล้อมต่อไปนี้จากzoo_agent/.envGOOGLE_CLOUD_PROJECT_NUMBER: ตอนนี้จำเป็นต้องสร้าง URN ข้อมูลประจำตัวของเซิร์ฟเวอร์ MCP (MCP_SERVER_URN)GOOGLE_CLOUD_PROJECT,GOOGLE_GENAI_USE_ENTERPRISEและGOOGLE_CLOUD_LOCATION: ADK ใช้เพื่อกำหนดค่าการเข้าถึง Vertex AI นอกจากนี้ ระบบยังใช้ตำแหน่งเพื่อค้นหาเซิร์ฟเวอร์ MCP ด้วย- ไม่จำเป็นต้องใช้แล้ว: ระบบจะไม่ใช้
MCP_SERVER_URLอีกต่อไปเนื่องจาก Agent จะดึงข้อมูล URL ปลายทางของเซิร์ฟเวอร์ MCP จาก Agent Registry แทนที่จะอ่านจากสภาพแวดล้อม
วิธีการทำงานของข้อมูลระบุตัวตนและการค้นพบเซิร์ฟเวอร์ MCP ใน
ใน Agent Registry เซิร์ฟเวอร์ MCP ที่ลงทะเบียนแต่ละเครื่องจะมีตัวระบุที่ไม่ซ้ำกันซึ่งจัดเก็บไว้ในช่องAgentRegistrymcpServerIdโดยจัดรูปแบบเป็น Uniform Resource Name (URN) คุณจะเห็นรหัสดังกล่าวในเอาต์พุตของคำสั่งgcloud agent-registry mcp-servers listในขั้นตอนที่ 3 ตัวแทนจะสร้างตัวระบุนี้ขึ้นใหม่จากตัวแปรสภาพแวดล้อม ดังนี้ URN จะเข้ารหัสหมายเลขโปรเจ็กต์ของรีจิสทรี (PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] PROJECT_NUMBER = os.environ["GOOGLE_CLOUD_PROJECT_NUMBER"] LOCATION = os.environ.get("GOOGLE_CLOUD_LOCATION", "global") MCP_SERVER_URN = f"urn:mcp:projects-{PROJECT_NUMBER}:projects:{PROJECT_NUMBER}:locations:{LOCATION}:agentregistry:services:zoo-mcp-server"projects-{PROJECT_NUMBER}) หมายเลขโปรเจ็กต์และตำแหน่งที่ลงทะเบียนเซิร์ฟเวอร์ (projects:{PROJECT_NUMBER}:locations:{LOCATION}) และรหัสบริการที่คุณใช้ในคำสั่งgcloud agent-registry services create(agentregistry:services:zoo-mcp-server) ตัวแทนจะแสดงรายการเซิร์ฟเวอร์ MCP ที่ลงทะเบียนด้วยregistry.list_mcp_servers()และจับคู่mcpServerIdของเซิร์ฟเวอร์แต่ละเครื่องกับMCP_SERVER_URNจากนั้นจะส่งทรัพยากรname(projects/.../locations/.../mcpServers/...) ของเซิร์ฟเวอร์ที่ตรงกันไปยังregistry.get_mcp_toolset()ซึ่งจะอ่าน URL ปลายทางจากอินเทอร์เฟซที่ลงทะเบียนของเซิร์ฟเวอร์ (https://...run.app/mcp) และแสดงผลMcpToolsetที่เชื่อมต่อกับ URL ดังกล่าวheader_provider = _GoogleAuthHeaders() registry = AgentRegistry( project_id=PROJECT_ID, location=LOCATION, header_provider=header_provider, ) mcp_server_name = next( ( s["name"] for s in registry.list_mcp_servers().get("mcpServers", []) if s.get("mcpServerId") == MCP_SERVER_URN ), None, ) if not mcp_server_name: raise RuntimeError(f"MCP server '{MCP_SERVER_URN}' not found in Agent Registry.") zoo_mcp_toolset = registry.get_mcp_toolset(mcp_server_name=mcp_server_name) header_provider.bind(zoo_mcp_toolset.connection_params.url) zoo_mcp_toolset.connection_params.timeout = 30.0get_mcp_toolset()จะใส่ชื่อที่แสดงของเซิร์ฟเวอร์ไว้หน้าชื่อเครื่องมือ เพื่อให้ Agent เห็นเครื่องมือเป็นzoo_mcp_server_list_species,zoo_mcp_server_get_animals_by_speciesและzoo_mcp_server_get_animal_detailsคำนำหน้าจะช่วยป้องกันไม่ให้ชื่อซ้ำกันเมื่อ Agent ใช้เครื่องมือจากเซิร์ฟเวอร์ MCP หลายเครื่อง บรรทัดสุดท้ายจะเพิ่มระยะหมดเวลาการเชื่อมต่อจากค่าเริ่มต้น 5 วินาทีเพื่อให้มีเวลาสำหรับ Cold Start ของ Cloud Runการให้สิทธิ์เปลี่ยนแปลงไปอย่างไรเมื่อใช้
AgentRegistryAgentRegistryตรวจสอบสิทธิ์การเรียก API ของตนเอง เช่นlist_mcp_servers()ด้วย ADC ดังนั้นจึงจำเป็นต้องใช้ผู้ให้บริการส่วนหัวสำหรับการเรียกไปยังเซิร์ฟเวอร์ MCP ที่ค้นพบเท่านั้น เนื่องจากAgentRegistryสร้างMcpToolsetภายใน คุณจึงส่งผู้ให้บริการส่วนหัวไปยังคอนสตรัคเตอร์AgentRegistryในขั้นตอนก่อนหน้า กลุ่มเป้าหมายคือค่าMCP_SERVER_URLที่คุณทราบล่วงหน้า ตอนนี้ URL จะเป็นที่รู้จักหลังจากการค้นพบเท่านั้น ADK จะเรียกใช้ผู้ให้บริการส่วนหัวเมื่อ Agent ส่งคำขอไปยังเซิร์ฟเวอร์ MCP ไม่ใช่เมื่อสร้างAgentRegistryซึ่งจะช่วยให้ Agent สร้างผู้ให้บริการก่อนและเชื่อมโยงกับปลายทางที่ค้นพบหลังจากget_mcp_toolset()แสดงผล เมธอดbind()จะรับข้อมูลเข้าสู่ระบบในลักษณะเดียวกับคอนสตรัคเตอร์ในขั้นตอนก่อนหน้า โดยกลุ่มเป้าหมายจะนำมาจาก URL ปลายทางที่ค้นพบโดยไม่มีเส้นทาง/mcpดังนี้ เมธอดdef bind(self, endpoint_url: str) -> None: parsed = urlparse(endpoint_url) audience = f"{parsed.scheme}://{parsed.netloc}" try: self._credentials = id_token.fetch_id_token_credentials(audience, request=self._request) except exceptions.DefaultCredentialsError: self._credentials, _ = google.auth.default() self._id_token = True if not isinstance(self._credentials, credentials.Credentials): raise__call__จะเหมือนกับในขั้นตอนก่อนหน้า ยกเว้นว่าจะแสดงข้อผิดพลาดหากมีการเรียกใช้ก่อนbind() - เรียกใช้ Agent เพื่อทดสอบการเปลี่ยนแปลง ใช้คำสั่ง
adk runดังนี้ หรือเรียกใช้อินเทอร์เฟซเว็บ ADK ด้วยคำสั่งadk run zoo_agentadk webดังนี้ แล้วเปิด URL ที่คำสั่งพิมพ์ในเบราว์เซอร์ หากใช้ Cloud Shell ให้เพิ่มอาร์กิวเมนต์adk web --port 8080--allow_originsเพื่ออนุญาตให้เปิดอินเทอร์เฟซเว็บจาก Cloud Shell ถามคำถามเดียวกับในขั้นตอนก่อนหน้ากับตัวแทน คำตอบยังคงเหมือนเดิม แต่ในอินเทอร์เฟซเว็บของ ADK การเรียกใช้เครื่องมือจะปรากฏเป็นadk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"zoo_mcp_server_list_speciesและzoo_mcp_server_get_animals_by_species
การเปลี่ยนแปลงนี้ช่วยให้คุณไม่ต้องใช้ URL ของเซิร์ฟเวอร์ MCP ที่ชัดเจน อย่างไรก็ตาม คุณยังคงต้องทราบหมายเลขโปรเจ็กต์ ตำแหน่งรีจิสทรี และรหัสบริการที่ใช้ลงทะเบียนเซิร์ฟเวอร์ MCP และการค้นพบจะดำเนินการแบบคงที่ระหว่างการเริ่มต้น Agent
6. ใช้การค้นหาเชิงความหมายสำหรับเซิร์ฟเวอร์ MCP ในการค้นพบแบบไดนามิก
ในขั้นตอนนี้ คุณจะปรับปรุงการค้นพบแบบไดนามิกของเซิร์ฟเวอร์ MCP โดยแทนที่การจับคู่ URN แบบตรงทั้งหมดเมื่อเริ่มต้นระบบด้วยการค้นหาเชิงความหมายขณะรันไทม์ วิธีนี้ช่วยให้ไม่จำเป็นต้องทราบหมายเลขโปรเจ็กต์ที่มีการติดตั้งใช้งานเซิร์ฟเวอร์หรือข้อมูลประจำตัวที่แน่นอนของเซิร์ฟเวอร์ (MCP_SERVER_URN) และผสานรวมการค้นพบเครื่องมือเข้ากับเวิร์กโฟลว์รันไทม์มาตรฐานของ Agent โดยตรง แทนที่จะจัดการที่การเริ่มต้น
คุณดูไฟล์ทั้งหมดสำหรับขั้นตอนนี้ได้ใน GitHub
อัปเดต Agent ให้ใช้การค้นหาเชิงความหมายที่รันไทม์
- ดาวน์โหลดไฟล์
agent.pyที่อัปเดตแล้วจาก GitHub เพื่อเขียนทับการติดตั้งใช้งานที่มีอยู่ โดยทำดังนี้curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step6/zoo_agent/agent.py -o zoo_agent/agent.pyตัวแปรสภาพแวดล้อมในขั้นตอนนี้
เนื่องจาก Agent ไม่ได้สร้าง URN ของเซิร์ฟเวอร์ MCP อีกต่อไป การกำหนดค่าที่จำเป็นจึงง่ายขึ้นไปอีก ดังนี้GOOGLE_CLOUD_PROJECT,GOOGLE_GENAI_USE_ENTERPRISEและGOOGLE_CLOUD_LOCATION: ADK ใช้เพื่อกำหนดค่าการเข้าถึง Vertex AI นอกจากนี้ ระบบยังใช้ตำแหน่งเพื่อค้นหาเซิร์ฟเวอร์ MCP ด้วย- ไม่จำเป็นอีกต่อไป: ไม่จำเป็นต้องใช้
GOOGLE_CLOUD_PROJECT_NUMBERอีกต่อไป (นอกเหนือจากMCP_SERVER_URLซึ่งนำออกไปแล้วในขั้นตอนก่อนหน้า)
เปลี่ยนจากการจับคู่แบบตรงทั้งหมดเป็นการค้นหาเชิงความหมาย
แทนที่จะเรียกใช้registry.list_mcp_servers()ระหว่างการเริ่มต้นโมดูลและเปรียบเทียบแต่ละรายการกับMCP_SERVER_URNที่สร้างขึ้น ตอนนี้agent.pyจะกำหนดฟังก์ชันเครื่องมือการค้นพบdiscover_zoo_mcp_serversที่เรียกใช้registry.search_mcp_servers()ด้วยsearch_type="SEMANTIC": ซึ่งจะช่วยให้ Agent ค้นหา Agent Registry ได้ที่รันไทม์โดยใช้คำอธิบายภาษาธรรมชาติเกี่ยวกับความสามารถที่ต้องการ (เช่นdef discover_zoo_mcp_servers(query: str, tool_context: ToolContext) -> dict[str, Any]: """Discovers Zoo MCP server(s) matching the query using semantic search. Args: query: Semantic description of zoo capabilities needed (e.g. 'zoo animal info') """ cached = _fresh_discovery(tool_context.state) if cached: return { "status": "already_discovered", "message": "Zoo MCP servers are already discovered.", "servers": cached["servers"], } try: response = registry.search_mcp_servers( search_string=query, search_type="SEMANTIC", page_size=5, ) except Exception as e: return { "status": "error", "message": f"Agent Registry search failed: {e}", } found_servers = response.get("mcpServers", []) if not found_servers: return { "status": "not_found", "message": f"No MCP servers found in Agent Registry at {LOCATION} matching '{query}'.", } valid_servers = [s["name"] for s in found_servers if "name" in s] tool_context.state[DISCOVERY_STATE_KEY] = { "servers": valid_servers, "discovered_at": time.time(), } return { "status": "discovered", "message": f"Successfully discovered {len(valid_servers)} Zoo MCP server(s).", "servers": valid_servers, }"zoo animals, exhibits, habitat, care, feeding schedule") และบันทึกชื่อทรัพยากรเซิร์ฟเวอร์ที่ค้นพบไว้ในสถานะเซสชัน ADK (tool_context.state)วิธีผสานรวมการค้นพบแบบไดนามิกเข้ากับเวิร์กโฟลว์ของ Agent
หากต้องการต่อเชื่อมเครื่องมือจากเซิร์ฟเวอร์ที่ค้นพบขณะรันไทม์และแนะนำ Agent ผ่านขั้นตอนการค้นพบแล้วเรียกใช้agent.pyจะรวมการติดตั้งใช้งานBaseToolsetที่กำหนดเอง (RegistryDiscoveredToolset) เข้ากับผู้ให้บริการคำสั่งแบบไดนามิก (zoo_guide_instruction) ดังนี้- ชุดเครื่องมือแบบไดนามิก (
RegistryDiscoveredToolset): คลาสย่อยBaseToolsetและตั้งค่าself._use_invocation_cache = Falseเพื่อให้ ADK ประเมินเครื่องมือที่ใช้ได้อีกครั้งทันทีหลังจากdiscover_zoo_mcp_serversอัปเดตสถานะเซสชัน แทนที่จะแคชรายการเครื่องมือที่ว่างเปล่าในตอนแรก เมื่อเรียกใช้get_tools()ระบบจะอ่านชื่อเซิร์ฟเวอร์ที่ค้นพบจากสถานะ สร้างMcpToolsetที่ตรวจสอบสิทธิ์แล้วสำหรับเซิร์ฟเวอร์แต่ละรายการในเทรดผู้ปฏิบัติงาน และแสดงเครื่องมือที่ใช้งานอยู่ทั้งหมด การเข้าถึงเซิร์ฟเวอร์หนึ่งไม่สำเร็จจะไม่ส่งผลต่อเซิร์ฟเวอร์อื่นๆ_build_toolset()ดำเนินการ I/O แบบบล็อก ดังนั้น_tools_of()จึงเรียกใช้ในเทรดผู้ปฏิบัติงานclass RegistryDiscoveredToolset(BaseToolset): def __init__(self, agent_registry: AgentRegistry): super().__init__() self._registry = agent_registry self._toolsets: dict[str, McpToolset] = {} self._use_invocation_cache = False def _build_toolset(self, server_name: str) -> McpToolset: header_provider = _GoogleAuthHeaders() server_registry = AgentRegistry( project_id=self._registry.project_id, location=self._registry.location, header_provider=header_provider, ) toolset = server_registry.get_mcp_toolset(server_name) header_provider.bind(toolset.connection_params.url) toolset.connection_params.timeout = 30.0 return toolset async def _tools_of(self, server_name: str, ctx: ReadonlyContext) -> list[BaseTool]: try: if server_name not in self._toolsets: self._toolsets[server_name] = await asyncio.to_thread( self._build_toolset, server_name ) return await self._toolsets[server_name].get_tools_with_prefix(ctx) except Exception as e: print(f"Skipping MCP server {server_name}: {e}") return [] async def get_tools(self, readonly_context: ReadonlyContext | None = None) -> list[BaseTool]: if not readonly_context: return [] record = _fresh_discovery(readonly_context.state) if not record or not record.get("servers"): if self._toolsets: await self.close() return [] per_server = await asyncio.gather( *(self._tools_of(name, readonly_context) for name in record["servers"]) ) return [tool for tools in per_server for tool in tools] - คำสั่ง Agent แบบไดนามิก (
zoo_guide_instruction): ประเมินสถานะเซสชันก่อนแต่ละเทิร์นของโมเดล เมื่อยังไม่พบเซิร์ฟเวอร์ ระบบจะเพิ่ม_DISCOVERY_PROMPTเพื่อสั่งให้ Agent เรียกใช้discover_zoo_mcp_serversก่อน เมื่อการค้นพบสำเร็จ ระบบจะอัปเดตพรอมต์เพื่อแสดงรายการเซิร์ฟเวอร์ MCP ที่ใช้งานอยู่ และสั่งให้ Agent ใช้เครื่องมือสวนสัตว์ที่ต่อเชื่อมโดยตรงโดยไม่ต้องเรียกใช้การค้นพบอีกครั้งdef zoo_guide_instruction(ctx: ReadonlyContext) -> str: record = _fresh_discovery(ctx.state) if record is None: return _ROLE + _DISCOVERY_PROMPT return _ROLE + ( f"\nYour active Zoo MCP servers: {', '.join(record['servers'])}. " "Do not run discovery again; use the active zoo tools directly to fulfill the visitor's request." )
การให้สิทธิ์สำหรับเซิร์ฟเวอร์ที่ค้นพบแบบไดนามิก
การค้นหาเชิงความหมายสามารถแสดงผลเซิร์ฟเวอร์ได้หลายรายการ และ ADK จะเรียกใช้ผู้ให้บริการส่วนหัวโดยไม่บอกว่าคำขอจะส่งไปยังเซิร์ฟเวอร์ใด ดังนั้น_build_toolset()จึงสร้าง_GoogleAuthHeadersและAgentRegistryเฉพาะสำหรับเซิร์ฟเวอร์ที่ค้นพบแต่ละรายการ และเชื่อมโยงผู้ให้บริการกับปลายทางของเซิร์ฟเวอร์นั้น ผู้ให้บริการจะเหมือนกับในขั้นตอนก่อนหน้า โดยมีแฟล็ก_boundที่ทำให้ดำเนินการไม่สำเร็จหากใช้ก่อนbind()ตรรกะการเก็บรักษาและการรีเฟรช
เพื่อหลีกเลี่ยงการค้นหา Agent Registry ในทุกรอบขณะที่ยังคงรีเฟรชปลายทางที่ค้นพบเป็นระยะๆagent.pyจะใช้การเก็บรักษาสถานะด้วย Time-to-Live (TTL) ดังนี้DISCOVERY_STATE_KEY = "app:zoo_mcp_servers" DISCOVERY_TTL_SECONDS = 24 * 60 * 60 def _fresh_discovery(state: Any) -> dict[str, Any] | None: if not state: return None record = state.get(DISCOVERY_STATE_KEY) if not record or not record.get("servers"): return None if time.time() - record.get("discovered_at", 0) > DISCOVERY_TTL_SECONDS: return None return record- การเก็บรักษาที่กำหนดขอบเขตระดับแอปพลิเคชัน: ชื่อเซิร์ฟเวอร์ที่ค้นพบและการประทับเวลา
discovered_atจะจัดเก็บไว้ในสถานะเซสชันภายใต้คีย์app:zoo_mcp_serversการใช้คำนำหน้าapp:ของ ADK จะกำหนดขอบเขตของระเบียนการค้นพบที่แคชไว้ตลอดการเรียกใช้แอปพลิเคชัน - การตรวจสอบ TTL และการรีเฟรชอัตโนมัติ:
_fresh_discovery()จะยืนยันว่ามีระเบียนที่บันทึกไว้และอายุของระเบียนไม่เกินDISCOVERY_TTL_SECONDS(24 ชั่วโมง) ขณะที่ระเบียนยังใหม่อยู่RegistryDiscoveredToolsetจะใช้การเชื่อมต่อMcpToolsetที่แคชไว้ (self._toolsets) ซ้ำzoo_guide_instructionจะข้ามพรอมต์การค้นพบ และการเรียกซ้ำไปยังdiscover_zoo_mcp_serversจะแสดงผล"already_discovered"ทันที เมื่อ TTL 24 ชั่วโมงหมดอายุ_fresh_discovery()จะแสดงผลNoneซึ่งทำให้RegistryDiscoveredToolset.get_tools()ปิดเซสชันไคลเอ็นต์ MCP ที่มีอยู่ (await self.close()) และzoo_guide_instructionจะแจ้งให้ Agent ทำการค้นหาใหม่
- เรียกใช้ Agent เพื่อทดสอบเวิร์กโฟลว์การค้นพบเชิงความหมาย ใช้คำสั่ง
adk runดังนี้ หรือเรียกใช้อินเทอร์เฟซเว็บ ADK ด้วยคำสั่งadk run zoo_agentadk webดังนี้ แล้วเปิด URL ที่คำสั่งพิมพ์ในเบราว์เซอร์ หากใช้ Cloud Shell ให้เพิ่มอาร์กิวเมนต์adk web --port 8080--allow_originsเพื่ออนุญาตให้เปิดอินเทอร์เฟซเว็บจาก Cloud Shell เมื่อคุณถามคำถาม Agent เกี่ยวกับสวนสัตว์ (เช่นadk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"How many animals are in the zoo?) ให้สังเกตว่า Agent จะเรียกใช้discover_zoo_mcp_serversก่อน จากนั้นจะต่อเชื่อมเครื่องมือจากเซิร์ฟเวอร์ MCP ที่ค้นพบแบบไดนามิก แล้วเรียกใช้เครื่องมือสวนสัตว์ เช่นzoo_mcp_server_list_speciesและzoo_mcp_server_get_animals_by_speciesเพื่อตอบคำถามของคุณ ลองถามคำถามติดตามผล เช่นWhich is the oldest lion?Agent จะตอบโดยใช้เครื่องมือ Zoo ที่ต่อเชื่อมอยู่แล้ว และจะไม่เรียกใช้discover_zoo_mcp_serversอีกครั้ง เนื่องจากระเบียนการค้นพบในสถานะเซสชันยังคงเป็นปัจจุบันอยู่
7. ล้างข้อมูล
ตัวเลือกที่ 1: ลบโปรเจ็กต์
หากคุณสร้างโปรเจ็กต์เฉพาะสำหรับ Codelab นี้ วิธีที่ง่ายที่สุดในการกำจัดการเรียกเก็บเงินคือการลบโปรเจ็กต์ โดยทำดังนี้
gcloud projects delete "${GOOGLE_CLOUD_PROJECT}" --quiet
ตัวเลือกที่ 2: ลบทรัพยากรทีละรายการ
หากคุณใช้โปรเจ็กต์ที่มีอยู่และต้องการเก็บไว้ ให้ลบทรัพยากรแต่ละรายการที่สร้างขึ้นระหว่าง Codelab นี้ โดยทำดังนี้
- ลบการลงทะเบียนเซิร์ฟเวอร์ MCP ออกจาก Agent Registry เนื่องจากคุณลงทะเบียนเซิร์ฟเวอร์ด้วยตนเอง การลบบริการ Cloud Run จึงไม่ได้นำการลงทะเบียนออก
gcloud agent-registry services delete zoo-mcp-server \ --location=global \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - ลบบริการเซิร์ฟเวอร์ MCP ของ Cloud Run โดยทำดังนี้
gcloud run services delete zoo-mcp-server \ --region=us-west1 \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - ลบบัญชีบริการที่สร้างขึ้นสำหรับเซิร์ฟเวอร์ MCP โดยทำดังนี้
gcloud iam service-accounts delete \ "zoo-mcp-server-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - หากสร้างที่เก็บ Artifact Registry
cloud-run-source-deployขึ้นมาโดยเฉพาะในระหว่างที่ทำ Codelab นี้ ให้ลบที่เก็บดังกล่าวเพื่อหลีกเลี่ยงค่าใช้จ่ายในการจัดเก็บอิมเมจคอนเทนเนอร์ โดยทำดังนี้gcloud artifacts repositories delete cloud-run-source-deploy \ --location=us-west1 \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - (ไม่บังคับ) นำไดเรกทอรีโปรเจ็กต์ที่อยู่ในเครื่องที่สร้างใน Cloud Shell ออกโดยทำดังนี้
cd ~ && rm -rf adk-dynamic-discovery
8. ขอแสดงความยินดี
ใน Codelab นี้ คุณได้เรียนรู้วิธีค้นหาและใช้เซิร์ฟเวอร์ MCP แบบไดนามิกด้วย Google Cloud Agent Registry และ Agent Development Kit (ADK) ดังนี้
- ลงทะเบียนเซิร์ฟเวอร์ MCP: ติดตั้งใช้งานเซิร์ฟเวอร์ MCP ที่ปลอดภัยใน Cloud Run และลงทะเบียนใน Agent Registry
- ค้นพบเซิร์ฟเวอร์ MCP ตามข้อมูลระบุตัวตน: แก้ไขปลายทางของเซิร์ฟเวอร์ MCP จาก Agent Registry แทนการฮาร์ดโค้ด URL
- ค้นพบเซิร์ฟเวอร์ MCP ตามความหมาย: อนุญาตให้ Agent ค้นหาและต่อเชื่อมเครื่องมือ MCP ขณะรันไทม์โดยใช้การค้นหาด้วยภาษาธรรมชาติ พร้อมการเก็บรักษาและการรีเฟรช