1. Giới thiệu
Trong lớp học lập trình này, bạn sẽ tìm hiểu cách sử dụng Google Cloud Agent Registry và Agent Development Kit (ADK) để tự động khám phá và gọi các máy chủ Giao thức ngữ cảnh mô hình (MCP) đã đăng ký trong thời gian chạy, loại bỏ các điểm cuối và cấu hình công cụ được mã hoá cứng.
Phòng thí nghiệm này sử dụng ADK và ngôn ngữ lập trình Python để xây dựng một ứng dụng. Bạn có thể áp dụng các nguyên tắc tương tự trong các ngôn ngữ lập trình khác và sử dụng các khung phát triển tác nhân khác.
Bạn sẽ thực hiện
- Bật và định cấu hình Google Cloud Agent Registry trong dự án của bạn
- Triển khai máy chủ MCP lên Cloud Run và đăng ký máy chủ đó trong Agent Registry
- Tạo một tác nhân ADK gọi máy chủ MCP bằng URL được cố định giá trị trong mã
- Thay đổi tác nhân để khám phá máy chủ MCP trong Sổ đăng ký tác nhân theo danh tính của máy chủ đó
- Thay đổi tác nhân để khám phá máy chủ MCP trong thời gian chạy bằng tính năng tìm kiếm ngữ nghĩa
Bạn cần có
- Một trình duyệt web hiện đại (chẳng hạn như Chrome)
- Một dự án trên Google Cloud đã bật tính năng thanh toán
- Một tài khoản có quyền tạo và quản lý tài nguyên, cũng như định cấu hình chính sách IAM trên dự án trên đám mây của Google
2. Thiết lập môi trường
Hướng dẫn trong lớp học lập trình này giả định rằng bạn chạy các lệnh trong Cloud Shell trong Google Cloud Console. Cloud Shell là một môi trường dòng lệnh chạy trong Google Cloud, được tải sẵn các công cụ cần thiết. Nếu đã cài đặt gcloud CLI, git và Python 3 trong môi trường cục bộ, bạn cũng có thể chạy các lệnh tại đó.
Tạo một dự án trên Google Cloud
- Trong Google Cloud Console, trên trang bộ chọn dự án, hãy tạo một dự án trên đám mây của Google Cloud.
- Đảm bảo rằng bạn đã bật tính năng thanh toán cho dự án trên đám mây của bạn. Tìm hiểu cách kiểm tra xem tính năng thanh toán có được bật trong dự án hay không.
Bắt đầu một cửa sổ dòng lệnh Cloud Shell
- Sử dụng bộ chọn tài nguyên để chọn dự án Google Cloud mà bạn đã tạo.
- Nhấp vào Kích hoạt Cloud Shell
ở đầu Google Cloud Console.
- Sau khi kết nối với Cloud Shell, hãy xác minh thông tin xác thực của bạn:
gcloud auth list - Xác nhận rằng dự án của bạn đã được định cấu hình:
gcloud config get project - Nếu dự án của bạn không được thiết lập như mong đợi hoặc nếu bạn sử dụng một môi trường trên máy cục bộ thay vì Cloud Shell, hãy thiết lập mã dự án:
export GOOGLE_CLOUD_PROJECT=<YOUR_PROJECT_ID> gcloud config set project "${GOOGLE_CLOUD_PROJECT}"
Chọn một dự án trên đám mây của Google và đặt các biến môi trường
- Bật các API Google Cloud cần thiết:
gcloud services enable \ agentregistry.googleapis.com \ aiplatform.googleapis.com \ cloudbuild.googleapis.com \ run.googleapis.com \ --project="${GOOGLE_CLOUD_PROJECT}"
Thiết lập môi trường Python
- Tạo và kích hoạt môi trường ảo, đồng thời cài đặt các phần phụ thuộc bắt buộc:
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. Tạo và đăng ký máy chủ MCP Zoo
Ở bước này, bạn sẽ tạo và đăng ký một máy chủ MCP Zoo. Máy chủ này cung cấp 3 công cụ: list_species – liệt kê tất cả các loài sống trong sở thú, get_animals_by_species – liệt kê các loài động vật thuộc một loài cụ thể và get_animal_details – lấy thêm thông tin về một loài động vật cụ thể.
Bạn có thể tìm thấy tất cả tệp cho bước này trên GitHub.
Tạo máy chủ MCP
- Thiết lập thư mục và dự án Python cho máy chủ 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 - Tải mã máy chủ MCP xuống:
curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step3/server.py -o server.py
Triển khai máy chủ MCP lên Cloud Run và đăng ký tại Agent Registry
Tiếp theo, bạn sẽ tạo Dockerfile cho máy chủ và triển khai máy chủ lên Cloud Run.
- Tạo
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 - Tạo tài khoản dịch vụ được chỉ định cho máy chủ:
gcloud iam service-accounts create zoo-mcp-server-sa \ --display-name="Zoo MCP Server Service Account" - Triển khai máy chủ đến khu vực
us-west1. Nếu bạn thấy lời nhắc sau, hãy xác nhận việc tạo kho lưu trữ Artifact Registry mới. Lời nhắc này sẽ xuất hiện nếu bạn không có kho lưu trữ Artifact Registry trong dự án cho khu vựcgcloud 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-west1:Deploying 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)? - Đăng ký máy chủ MCP trong Sổ đăng ký tác nhân tại vị trí "global":
Các lệnh này tạofastmcp 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.jsonbằng tệp kê khai công cụ và gọi API Sổ đăng ký tác nhân để đăng ký máy chủ. - Xác minh rằng tác nhân xuất hiện trong sổ đăng ký:
Bạn sẽ thấy kết quả YAML có thông tin về máy chủ MCP như sau: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. Tạo một tác nhân ADK cơ bản để sử dụng máy chủ Zoo MCP
Trong bước này, bạn sẽ tạo một tác nhân ADK để trả lời các yêu cầu của người dùng về sở thú bằng URL máy chủ MCP của Sở thú được cố định giá trị trong mã. Giá trị cố định trong mã buộc bạn phải biết URL của máy chủ MCP TRƯỚC KHI triển khai tác nhân.
Bạn có thể tìm thấy các tệp nguồn cho bước này trên GitHub.
Tạo ứng dụng tác nhân
Tạo một bản dựng khung của dự án ADK và tác nhân Zoo sử dụng máy chủ Zoo MCP.
- Thiết lập thư mục và dự án cho tác nhân 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 - Tạo thư mục gói tác nhân và tải tệp
__init__.pyxuống: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 - Tạo tệp
.envvà điền các biến môi trường được dùng trong bước này và các bước sau: Lệnh này ghi tất cả biến môi trường cần thiết trong bước này và các bước khám phá tiếp theo vàoMCP_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, vì vậy, bạn không cần tạo lại tệp sau này. Trong bước này, các biến sau đây sẽ được sử dụng:GOOGLE_GENAI_USE_ENTERPRISE,GOOGLE_CLOUD_PROJECTvàGOOGLE_CLOUD_LOCATION: Được ADK và SDK Google GenAI sử dụng ngầm để định tuyến các lệnh gọi mô hình Gemini thông qua Vertex AI trong dự án trên đám mây và vị trí Google Cloud của bạn (global). Biến này cũng lưu trữ vị trí cho Sổ đăng ký tác nhân nơi máy chủ MCP được đăng ký.GOOGLE_CLOUD_PROJECT_NUMBER: Không được dùng trong bước này; được điền sẵn ở đây để dùng trong bước tiếp theo.MCP_SERVER_URL: Đọc trực tiếp trongzoo_agent/agent.pyđể định cấu hìnhMcpToolsetbằng URL Cloud Run được cố định giá trị trong mã của máy chủ Zoo MCP.
- Tải
agent.pyxuống cùng với mã cho tác nhân Zoo kết nối với máy chủ MCP Zoo: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
Cách hoạt động của quy trình uỷ quyền máy chủ MCP
Vì dịch vụ Cloud Run zoo-mcp-server được triển khai bằng cờ --invoker-iam-check và --no-allow-unauthenticated, nên mọi yêu cầu HTTP đến máy chủ MCP đều phải bao gồm một mã thông báo giá trị nhận dạng do Google ký trong tiêu đề Authorization. Mã thông báo phải xác định một phương thức gọi có quyền run.routes.invoke đối với dịch vụ.
Trong zoo_agent/agent.py, lớp _GoogleAuthHeaders lấy thông tin xác thực mã thông báo giá trị nhận dạng theo một trong hai cách. Trước tiên, ứng dụng gọi fetch_id_token_credentials() để lấy mã thông báo nhận dạng cho URL dịch vụ từ máy chủ siêu dữ liệu hoặc khoá tài khoản dịch vụ. Nếu không thành công, hệ thống sẽ sử dụng mã thông báo giá trị nhận dạng của Thông tin xác thực mặc định của ứng dụng (ADC) của người dùng. Mỗi khi được gọi, thực thể này sẽ làm mới mã thông báo nếu cần và trả về tiêu đề 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}"}
Tác nhân truyền một thực thể của lớp đến McpToolset dưới dạng đối số header_provider. ADK gọi nhà cung cấp tiêu đề và đính kèm các tiêu đề mà nhà cung cấp đó trả về cho mọi yêu cầu đến máy chủ MCP. Vì máy khách HTTP do ADK tạo, nên cùng một mã sẽ hoạt động bất kể phiên bản MCP Python SDK và thư viện HTTP của SDK đó được cài đặt:
zoo_mcp_toolset = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url=f"{MCP_SERVER_URL}/mcp",
timeout=30.0,
),
header_provider=_GoogleAuthHeaders(MCP_SERVER_URL),
)
Đối tượng được yêu cầu là URL dịch vụ (MCP_SERVER_URL), còn URL kết nối bao gồm đường dẫn /mcp nơi máy chủ FastMCP phân phát giao thức MCP. Thời gian chờ 30 giây giúp Cloud Run có thời gian khởi động nguội.
Kiểm thử tác nhân bằng sân chơi ADK trong môi trường cục bộ
Trước khi kiểm thử tác nhân, hãy đảm bảo rằng tài khoản người dùng của bạn có thể gọi máy chủ MCP.
- Chỉ dành cho thiết bị đầu cuối cục bộ (bỏ qua bước này trong Cloud Shell): khởi chạy ADC bằng thông tin xác thực người dùng của bạn:
Làm theo hướng dẫn để hoàn tất quá trình khởi chạy ADC.gcloud auth application-default login - (Không bắt buộc) Cấp quyền cho tài khoản người dùng của bạn để gọi máy chủ MCP từ xa:
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' - Chạy tác nhân bằng lệnh
adk run: hoặc chạy giao diện web ADK bằng lệnhadk run zoo_agentadk web: rồi mở URL mà lệnh in trong trình duyệt.Nếu bạn sử dụng Cloud Shell, hãy thêm đối sốadk web --port 8080--allow_originsđể cho phép mở giao diện Web từ Cloud Shell: Sau khi chạy lệnh, hãy tìm biểu tượng Web Preview (Xem trước trên web)adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
trong thanh công cụ Cloud Shell (bên phải). Nhấp vào biểu tượng đó rồi chọn Preview on port 8080 (Xem trước trên cổng 8080). Cloud Shell sẽ mở một thẻ trình duyệt mới hiển thị giao diện web của ADK. - Bắt đầu trò chuyện với tác nhân. Ví dụ: hỏi về các loài động vật trong sở thú:
Tác nhân gọiHow many animals are in the zoo?list_speciesrồi gọiget_animals_by_speciescho từng loài để tính toán câu trả lời.
Giao diện web của ADK cho phép bạn khám phá cách hoạt động của tác nhân, bao gồm cả các lệnh gọi đến máy chủ MCP của Zoo. Hãy xem tài liệu về giao diện người dùng web để tìm hiểu cách thực hiện.
5. Tự động khám phá điểm cuối của máy chủ MCP
Trong bước này, bạn sẽ thay đổi tác nhân để khám phá điểm cuối của máy chủ MCP Zoo đã đăng ký trong Sổ đăng ký tác nhân thay vì cố định giá trị trong mã URL điểm cuối của máy chủ đó.
Bạn có thể tìm thấy tất cả tệp cho bước này trên GitHub.
Bạn sử dụng tính năng tích hợp ADK với Sổ đăng ký tác nhân để tìm và truy xuất URL của máy chủ. Để triển khai, bạn cần có những thông tin sau:
- Số dự án và vị trí của Sổ đăng ký tác nhân nơi máy chủ MCP của bạn được đăng ký (
global). - Mã dịch vụ mà bạn đã dùng để đăng ký máy chủ MCP (
zoo-mcp-server).
Cập nhật tác nhân để sử dụng Sổ đăng ký tác nhân
- Tải tệp
agent.pyđã cập nhật xuống từ GitHub để ghi đè phiên bản đầu tiên của tác nhân: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.pyBiến môi trường trong bước này
Với bản cập nhật này, tác nhân sẽ dựa vào các biến môi trường sau đây củazoo_agent/.env:GOOGLE_CLOUD_PROJECT_NUMBER: Hiện bắt buộc phải tạo URN nhận dạng của máy chủ MCP (MCP_SERVER_URN).GOOGLE_CLOUD_PROJECT,GOOGLE_GENAI_USE_ENTERPRISEvàGOOGLE_CLOUD_LOCATION: Được ADK dùng để định cấu hình quyền truy cập vào Vertex AI. Vị trí này cũng được dùng để tìm máy chủ MCP.- Không còn cần thiết:
MCP_SERVER_URLkhông còn được sử dụng vì tác nhân truy xuất URL điểm cuối của máy chủ MCP từ Sổ đăng ký tác nhân thay vì đọc URL đó từ môi trường.
Cách hoạt động của tính năng nhận dạng và khám phá máy chủ MCP trong
Trong Sổ đăng ký tác nhân, mỗi máy chủ MCP đã đăng ký đều có một giá trị nhận dạng riêng biệt được lưu trữ trong trườngAgentRegistrymcpServerId, được định dạng dưới dạng Tên tài nguyên thống nhất (URN). Bạn có thể xem mã này trong kết quả của lệnhgcloud agent-registry mcp-servers listở Bước 3. Tác nhân sẽ tạo lại giá trị nhận dạng này từ các biến môi trường: URN mã hoá số dự án của sổ đăng ký (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}), số dự án và vị trí nơi máy chủ được đăng ký (projects:{PROJECT_NUMBER}:locations:{LOCATION}) và mã dịch vụ mà bạn đã sử dụng trong lệnhgcloud agent-registry services create(agentregistry:services:zoo-mcp-server).Tác nhân liệt kê các máy chủ MCP đã đăng ký bằngregistry.list_mcp_servers()và so khớpmcpServerIdcủa mỗi máy chủ vớiMCP_SERVER_URN. Sau đó, lớp này sẽ truyền tài nguyên của máy chủ phù hợpname(projects/.../locations/.../mcpServers/...) đếnregistry.get_mcp_toolset(). Lớp này sẽ đọc URL điểm cuối từ giao diện đã đăng ký của máy chủ (https://...run.app/mcp) và trả về mộtMcpToolsetđược kết nối với 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()thêm tên hiển thị của máy chủ vào trước tên công cụ, vì vậy, tác nhân sẽ thấy các công cụ làzoo_mcp_server_list_species,zoo_mcp_server_get_animals_by_speciesvàzoo_mcp_server_get_animal_details. Tiền tố này giúp ngăn chặn xung đột tên khi một tác nhân sử dụng các công cụ từ nhiều máy chủ MCP. Dòng cuối cùng tăng thời gian chờ kết nối từ giá trị mặc định là 5 giây để có thời gian cho quá trình khởi động nguội của Cloud Run.Cách thay đổi quy trình uỷ quyền bằng
AgentRegistryAgentRegistryxác thực các lệnh gọi API của riêng mình, chẳng hạn nhưlist_mcp_servers(), bằng ADC, vì vậy, nhà cung cấp tiêu đề chỉ cần thiết cho các lệnh gọi đến máy chủ MCP được phát hiện. VìAgentRegistrytạoMcpToolsettrong nội bộ, nên bạn truyền trình cung cấp tiêu đề đến hàm khởi tạoAgentRegistry.Ở bước trước, đối tượng là giá trịMCP_SERVER_URLmà bạn đã biết trước. Giờ đây, URL chỉ được biết sau khi khám phá. ADK gọi nhà cung cấp tiêu đề khi tác nhân gửi yêu cầu đến máy chủ MCP, chứ không phải khiAgentRegistryđược tạo. Điều này cho phép tác nhân tạo nhà cung cấp trước rồi liên kết nhà cung cấp đó với điểm cuối được phát hiện sau khiget_mcp_toolset()trả về. Phương thứcbind()lấy thông tin xác thực theo cách tương tự như hàm khởi tạo trong bước trước, với đối tượng được lấy từ URL điểm cuối đã khám phá mà không có đường dẫn/mcp: Phương thứcdef 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__giống như trong bước trước, ngoại trừ việc phương thức này sẽ báo lỗi nếu được gọi trướcbind(). - Chạy tác nhân để kiểm tra thay đổi. Sử dụng lệnh
adk run: hoặc chạy giao diện web ADK bằng lệnhadk run zoo_agentadk web: rồi mở URL mà lệnh in trong trình duyệt.Nếu bạn sử dụng Cloud Shell, hãy thêm đối sốadk web --port 8080--allow_originsđể cho phép mở giao diện Web từ Cloud Shell: Hỏi tác nhân cùng một câu hỏi như ở bước trước. Câu trả lời vẫn như vậy, nhưng trong giao diện web của ADK, các lệnh gọi công cụ hiện xuất hiện dưới dạngadk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"zoo_mcp_server_list_speciesvàzoo_mcp_server_get_animals_by_species.
Thay đổi này giúp bạn không cần phải có URL máy chủ MCP rõ ràng. Tuy nhiên, bạn vẫn cần biết số dự án, vị trí của sổ đăng ký và mã dịch vụ dùng để đăng ký máy chủ MCP. Ngoài ra, quá trình khám phá được thực hiện tĩnh trong quá trình khởi chạy tác nhân.
6. Sử dụng tính năng tìm kiếm ngữ nghĩa cho máy chủ MCP trong chế độ khám phá động
Trong bước này, bạn sẽ cải thiện khả năng khám phá động của máy chủ MCP bằng cách thay thế tính năng so khớp URN chính xác khi khởi động bằng tính năng tìm kiếm ngữ nghĩa trong thời gian chạy. Phương pháp này giúp bạn không cần biết số dự án nơi máy chủ được triển khai hoặc danh tính chính xác của máy chủ (MCP_SERVER_URN), đồng thời tích hợp tính năng khám phá công cụ trực tiếp vào quy trình thời gian chạy chuẩn của tác nhân thay vì xử lý trong quá trình khởi chạy.
Bạn có thể tìm thấy tất cả tệp cho bước này trên GitHub.
Cập nhật tác nhân để sử dụng tính năng khám phá ngữ nghĩa trong thời gian chạy
- Tải tệp
agent.pyđã cập nhật xuống từ GitHub để ghi đè cách triển khai hiện có: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.pyBiến môi trường trong bước này
Vì tác nhân không còn tạo URN của máy chủ MCP, nên cấu hình bắt buộc sẽ được đơn giản hoá hơn nữa:GOOGLE_CLOUD_PROJECT,GOOGLE_GENAI_USE_ENTERPRISEvàGOOGLE_CLOUD_LOCATION: Được ADK dùng để định cấu hình quyền truy cập vào Vertex AI. Vị trí cũng được dùng để khám phá máy chủ MCP.- Không còn cần thiết:
GOOGLE_CLOUD_PROJECT_NUMBERkhông còn cần thiết nữa (ngoàiMCP_SERVER_URLđã bị xoá ở bước trước).
Chuyển từ so khớp chính xác sang tìm kiếm ngữ nghĩa
Thay vì gọiregistry.list_mcp_servers()trong quá trình khởi chạy mô-đun và so sánh từng mục nhập vớiMCP_SERVER_URNđã tạo,agent.pyhiện xác định một hàm công cụ khám phádiscover_zoo_mcp_serversgọiregistry.search_mcp_servers()bằngsearch_type="SEMANTIC": Việc này cho phép tác nhân tìm kiếm trong Sổ đăng ký tác nhân trong thời gian chạy bằng cách sử dụng nội dung mô tả bằng ngôn ngữ tự nhiên về các chức năng mà tác nhân cần (ví dụ: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") và lưu tên tài nguyên máy chủ được phát hiện vào trạng thái phiên ADK (tool_context.state).Cách tích hợp tính năng khám phá linh hoạt vào quy trình làm việc của nhân viên hỗ trợ
Để gắn các công cụ từ máy chủ được phát hiện trong thời gian chạy và hướng dẫn tác nhân thực hiện quy trình phát hiện rồi gọi,agent.pykết hợp một phương thức triển khaiBaseToolsettuỳ chỉnh (RegistryDiscoveredToolset) với một trình cung cấp lệnh động (zoo_guide_instruction):- Bộ công cụ động (
RegistryDiscoveredToolset): Tạo lớp conBaseToolsetvà đặtself._use_invocation_cache = Falseđể ADK đánh giá lại các công cụ có sẵn ngay sau khidiscover_zoo_mcp_serverscập nhật trạng thái phiên thay vì lưu danh sách công cụ trống ban đầu vào bộ nhớ đệm. Khi được gọi,get_tools()sẽ đọc tên máy chủ được phát hiện từ trạng thái, tạo mộtMcpToolsetđã xác thực cho mỗi máy chủ trong một luồng worker và trả về tất cả công cụ đang hoạt động. Việc không kết nối được với một máy chủ sẽ không ảnh hưởng đến các máy chủ khác._build_toolset()thực hiện chặn I/O, vì vậy,_tools_of()chạy nó trong một luồng worker.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] - Chỉ dẫn linh động cho nhân viên hỗ trợ (
zoo_guide_instruction): Đánh giá trạng thái phiên trước mỗi lượt của mô hình. Khi chưa phát hiện được máy chủ nào, hệ thống sẽ thêm_DISCOVERY_PROMPTđể hướng dẫn tác nhân gọidiscover_zoo_mcp_serverstrước. Sau khi khám phá thành công, câu lệnh sẽ được cập nhật để liệt kê các máy chủ MCP đang hoạt động và hướng dẫn tác nhân sử dụng trực tiếp các công cụ sở thú đã gắn mà không cần chạy lại quy trình khám phá: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." )
Uỷ quyền cho các máy chủ được phát hiện linh động
Tính năng tìm kiếm ngữ nghĩa có thể trả về một số máy chủ và ADK gọi một nhà cung cấp tiêu đề mà không cho biết máy chủ nào nhận được yêu cầu. Do đó,_build_toolset()sẽ tạo một_GoogleAuthHeadersvàAgentRegistryriêng cho từng máy chủ được phát hiện và liên kết nhà cung cấp với điểm cuối của máy chủ đó. Nhà cung cấp này giống như trong bước trước, có cờ_boundkhiến nhà cung cấp này không hoạt động nếu được sử dụng trướcbind().Logic giữ lại và làm mới
Để tránh truy vấn Sổ đăng ký tác nhân ở mỗi lượt trong khi vẫn làm mới các điểm cuối được phát hiện theo định kỳ,agent.pytriển khai việc giữ lại trạng thái bằng thời gian tồn tại (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- Giữ lại trong phạm vi ứng dụng: Tên máy chủ được phát hiện và dấu thời gian
discovered_atđược lưu trữ ở trạng thái phiên trong khoáapp:zoo_mcp_servers. Việc sử dụng tiền tốapp:của ADK sẽ đặt phạm vi cho bản ghi khám phá được lưu vào bộ nhớ đệm trong suốt quá trình chạy ứng dụng. - Xác thực TTL và tự động làm mới:
_fresh_discovery()xác minh rằng bản ghi đã lưu tồn tại và tuổi của bản ghi không vượt quáDISCOVERY_TTL_SECONDS(24 giờ). Khi bản ghi còn mới,RegistryDiscoveredToolsetsẽ sử dụng lại các kết nốiMcpToolsetđã lưu vào bộ nhớ đệm (self._toolsets),zoo_guide_instructionbỏ qua lời nhắc khám phá và mọi lệnh gọi lặp lại đếndiscover_zoo_mcp_serverssẽ ngay lập tức trả về"already_discovered". Sau khi TTL 24 giờ hết hạn,_fresh_discovery()sẽ trả vềNone, khiếnRegistryDiscoveredToolset.get_tools()đóng các phiên máy khách MCP hiện có (await self.close()) vàzoo_guide_instructionnhắc tác nhân thực hiện một quy trình khám phá mới.
- Chạy tác nhân để kiểm thử quy trình khám phá ngữ nghĩa. Sử dụng lệnh
adk run: hoặc chạy giao diện web ADK bằng lệnhadk run zoo_agentadk web: rồi mở URL mà lệnh in trong trình duyệt.Nếu bạn sử dụng Cloud Shell, hãy thêm đối sốadk web --port 8080--allow_originsđể cho phép mở giao diện Web từ Cloud Shell: Khi bạn đặt câu hỏi cho tác nhân về sở thú (ví dụ:adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"How many animals are in the zoo?), hãy quan sát thấy tác nhân này trước tiên gọidiscover_zoo_mcp_servers, gắn động các công cụ từ máy chủ MCP được phát hiện, sau đó gọi các công cụ của sở thú, chẳng hạn nhưzoo_mcp_server_list_speciesvàzoo_mcp_server_get_animals_by_species, để trả lời câu hỏi của bạn.Đặt một câu hỏi tiếp theo, ví dụ:Which is the oldest lion?. Tác nhân trả lời bằng các công cụ về sở thú đã được gắn và không gọi lạidiscover_zoo_mcp_servers, vì bản ghi khám phá trong trạng thái phiên vẫn còn mới.
7. Dọn dẹp
Cách 1: Xoá dự án
Nếu bạn đã tạo một dự án riêng cho lớp học lập trình này, thì cách dễ nhất để loại bỏ việc thanh toán là xoá dự án:
gcloud projects delete "${GOOGLE_CLOUD_PROJECT}" --quiet
Cách 2: Xoá từng tài nguyên
Nếu bạn đã sử dụng một dự án hiện có và muốn giữ lại dự án đó, hãy xoá từng tài nguyên được tạo trong lớp học lập trình này:
- Xoá thông tin đăng ký máy chủ MCP khỏi Sổ đăng ký tác nhân. Vì bạn đã đăng ký máy chủ theo cách thủ công, nên việc xoá dịch vụ Cloud Run sẽ không xoá thông tin đăng ký:
gcloud agent-registry services delete zoo-mcp-server \ --location=global \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Xoá dịch vụ máy chủ MCP Cloud Run:
gcloud run services delete zoo-mcp-server \ --region=us-west1 \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Xoá tài khoản dịch vụ được tạo cho máy chủ MCP:
gcloud iam service-accounts delete \ "zoo-mcp-server-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Nếu kho lưu trữ Artifact Registry
cloud-run-source-deployđược tạo riêng trong lớp học lập trình này, hãy xoá kho lưu trữ đó để tránh bị tính phí lưu trữ hình ảnh vùng chứa:gcloud artifacts repositories delete cloud-run-source-deploy \ --location=us-west1 \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Nếu muốn, bạn có thể xoá thư mục dự án cục bộ đã tạo trong Cloud Shell:
cd ~ && rm -rf adk-dynamic-discovery
8. Xin chúc mừng
Trong lớp học lập trình này, bạn đã tìm hiểu cách khám phá và sử dụng máy chủ MCP một cách linh động bằng Sổ đăng ký tác nhân của Google Cloud và Bộ công cụ phát triển tác nhân (ADK):
- Đã đăng ký một máy chủ MCP: Triển khai một máy chủ MCP bảo mật cho Cloud Run và đăng ký máy chủ đó trong Agent Registry.
- Phát hiện máy chủ MCP theo danh tính: Phân giải điểm cuối của máy chủ MCP từ Sổ đăng ký tác nhân thay vì cố định giá trị trong mã URL của máy chủ đó.
- Khám phá máy chủ MCP theo ngữ nghĩa: Cho phép tác nhân tìm và gắn các công cụ MCP trong thời gian chạy bằng tính năng tìm kiếm bằng ngôn ngữ tự nhiên, có khả năng giữ lại và làm mới.