1. Introducción
Descripción general
En este lab, compilarás e implementarás un servidor de Model Context Protocol (MCP). Los servidores de MCP son útiles para proporcionar a los LLM acceso a herramientas y servicios externos. Lo configurarás como un servicio seguro y listo para producción en Cloud Run al que se puede acceder desde varios clientes. Luego, te conectarás al servidor de MCP remoto desde la CLI de Gemini.
Actividades
Usaremos FastMCP para crear un servidor de MCP de zoológico que tenga dos herramientas: get_animals_by_species
y get_animal_details
. FastMCP proporciona una forma rápida y pythonica de compilar servidores y clientes de MCP.
Qué aprenderás
- Implementa el servidor de MCP en Cloud Run.
- Protege el extremo de tu servidor exigiendo la autenticación para todas las solicitudes, lo que garantiza que solo los clientes y agentes autorizados puedan comunicarse con él.
- Conéctate al extremo de tu servidor de MCP seguro desde Gemini CLI
2. Configuración del proyecto
- Si aún no tienes una Cuenta de Google, debes crear una.
- Usa una cuenta personal en lugar de una cuenta de trabajo o institución educativa. Es posible que las cuentas de trabajo y de instituciones educativas tengan restricciones que te impidan habilitar las APIs necesarias para este lab.
- Accede a la consola de Google Cloud.
- Habilita la facturación en la consola de Cloud.
- Completar este lab debería costar menos de USD 1 en recursos de Cloud.
- Puedes seguir los pasos al final de este lab para borrar recursos y evitar cargos adicionales.
- Los usuarios nuevos pueden acceder a la prueba gratuita de USD 300.
- Crea un proyecto nuevo o elige reutilizar uno existente.
3. Abre el editor de Cloud Shell
- Haz clic en este vínculo para navegar directamente al editor de Cloud Shell.
- Si se te solicita autorización en algún momento, haz clic en Autorizar para continuar.
- Si la terminal no aparece en la parte inferior de la pantalla, ábrela:
- Haz clic en Ver.
- Haz clic en Terminal.
- En la terminal, configura tu proyecto con este comando:
- Formato:
gcloud config set project [PROJECT_ID]
- Ejemplo:
gcloud config set project lab-project-id-example
- Si no recuerdas el ID de tu proyecto, haz lo siguiente:
- Puedes enumerar todos los IDs de tus proyectos con el siguiente comando:
gcloud projects list | awk '/PROJECT_ID/{print $2}'
- Puedes enumerar todos los IDs de tus proyectos con el siguiente comando:
- Formato:
- Deberías ver el siguiente mensaje:
Si ves unUpdated property [core/project].
WARNING
y se te preguntaDo you want to continue (Y/n)?
, es probable que hayas ingresado el ID del proyecto de forma incorrecta. Presionan
, presionaEnter
y vuelve a intentar ejecutar el comandogcloud config set project
.
4. Habilita las APIs
En la terminal, habilita las APIs:
gcloud services enable \
run.googleapis.com \
artifactregistry.googleapis.com \
cloudbuild.googleapis.com
Si se te solicita autorización, haz clic en Autorizar para continuar.
Este comando puede tardar unos minutos en completarse, pero, finalmente, debería producir un mensaje de éxito similar a este:
Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.
5. Prepara tu proyecto de Python
- Crea una carpeta llamada
mcp-on-cloudrun
para almacenar el código fuente de la implementación:mkdir mcp-on-cloudrun && cd mcp-on-cloudrun
- Crea un proyecto de Python con la herramienta
uv
para generar un archivopyproject.toml
: El comandouv init --description "Example of deploying an MCP server on Cloud Run" --bare --python 3.13
uv init
crea un archivopyproject.toml
para tu proyecto.Para ver el contenido del archivo, ejecuta el siguiente comando: El resultado debería ser similar al siguiente:cat pyproject.toml
[project] name = "mcp-on-cloudrun" version = "0.1.0" description = "Example of deploying an MCP server on Cloud Run" requires-python = ">=3.13" dependencies = []
6. Crea el servidor de MCP del zoo
Para proporcionar un contexto valioso que permita mejorar el uso de LLM con MCP, configura un servidor de MCP de zoo con FastMCP, un framework estándar para trabajar con el Protocolo de contexto del modelo. FastMCP proporciona una forma rápida de compilar servidores y clientes de MCP con Python. Este servidor de MCP proporciona datos sobre los animales de un zoológico ficticio. Para simplificar, almacenamos los datos en la memoria. Para un servidor de MCP de producción, probablemente quieras proporcionar datos de fuentes como bases de datos o APIs.
- Ejecuta el siguiente comando para agregar FastMCP como dependencia en el archivo
pyproject.toml
: Esto agregará un archivouv add fastmcp==2.11.1 --no-sync
uv.lock
a tu proyecto. - Crea y abre un nuevo archivo
server.py
para el código fuente del servidor de MCP: El comandocloudshell edit server.py
cloudshell edit
abrirá el archivoserver.py
en el editor que se encuentra sobre la terminal. - Agrega el siguiente código fuente del servidor de MCP del zoo al archivo
server.py
:import asyncio import logging import os from typing import List, Dict, Any from fastmcp import FastMCP logger = logging.getLogger(__name__) logging.basicConfig(format="[%(levelname)s]: %(message)s", level=logging.INFO) mcp = FastMCP("Zoo Animal MCP Server 🦁🐧🐻") # Dictionary of animals at the zoo ZOO_ANIMALS = [ { "species": "lion", "name": "Leo", "age": 7, "enclosure": "The Big Cat Plains", "trail": "Savannah Heights" }, { "species": "lion", "name": "Nala", "age": 6, "enclosure": "The Big Cat Plains", "trail": "Savannah Heights" }, { "species": "lion", "name": "Simba", "age": 3, "enclosure": "The Big Cat Plains", "trail": "Savannah Heights" }, { "species": "lion", "name": "King", "age": 8, "enclosure": "The Big Cat Plains", "trail": "Savannah Heights" }, { "species": "penguin", "name": "Waddles", "age": 2, "enclosure": "The Arctic Exhibit", "trail": "Polar Path" }, { "species": "penguin", "name": "Pip", "age": 4, "enclosure": "The Arctic Exhibit", "trail": "Polar Path" }, { "species": "penguin", "name": "Skipper", "age": 5, "enclosure": "The Arctic Exhibit", "trail": "Polar Path" }, { "species": "penguin", "name": "Chilly", "age": 3, "enclosure": "The Arctic Exhibit", "trail": "Polar Path" }, { "species": "penguin", "name": "Pingu", "age": 6, "enclosure": "The Arctic Exhibit", "trail": "Polar Path" }, { "species": "penguin", "name": "Noot", "age": 1, "enclosure": "The Arctic Exhibit", "trail": "Polar Path" }, { "species": "elephant", "name": "Ellie", "age": 15, "enclosure": "The Pachyderm Sanctuary", "trail": "Savannah Heights" }, { "species": "elephant", "name": "Peanut", "age": 12, "enclosure": "The Pachyderm Sanctuary", "trail": "Savannah Heights" }, { "species": "elephant", "name": "Dumbo", "age": 5, "enclosure": "The Pachyderm Sanctuary", "trail": "Savannah Heights" }, { "species": "elephant", "name": "Trunkers", "age": 10, "enclosure": "The Pachyderm Sanctuary", "trail": "Savannah Heights" }, { "species": "bear", "name": "Smokey", "age": 10, "enclosure": "The Grizzly Gulch", "trail": "Polar Path" }, { "species": "bear", "name": "Grizzly", "age": 8, "enclosure": "The Grizzly Gulch", "trail": "Polar Path" }, { "species": "bear", "name": "Barnaby", "age": 6, "enclosure": "The Grizzly Gulch", "trail": "Polar Path" }, { "species": "bear", "name": "Bruin", "age": 12, "enclosure": "The Grizzly Gulch", "trail": "Polar Path" }, { "species": "giraffe", "name": "Gerald", "age": 4, "enclosure": "The Tall Grass Plains", "trail": "Savannah Heights" }, { "species": "giraffe", "name": "Longneck", "age": 5, "enclosure": "The Tall Grass Plains", "trail": "Savannah Heights" }, { "species": "giraffe", "name": "Patches", "age": 3, "enclosure": "The Tall Grass Plains", "trail": "Savannah Heights" }, { "species": "giraffe", "name": "Stretch", "age": 6, "enclosure": "The Tall Grass Plains", "trail": "Savannah Heights" }, { "species": "antelope", "name": "Speedy", "age": 2, "enclosure": "The Tall Grass Plains", "trail": "Savannah Heights" }, { "species": "antelope", "name": "Dash", "age": 3, "enclosure": "The Tall Grass Plains", "trail": "Savannah Heights" }, { "species": "antelope", "name": "Gazelle", "age": 4, "enclosure": "The Tall Grass Plains", "trail": "Savannah Heights" }, { "species": "antelope", "name": "Swift", "age": 5, "enclosure": "The Tall Grass Plains", "trail": "Savannah Heights" }, { "species": "polar bear", "name": "Snowflake", "age": 7, "enclosure": "The Arctic Exhibit", "trail": "Polar Path" }, { "species": "polar bear", "name": "Blizzard", "age": 5, "enclosure": "The Arctic Exhibit", "trail": "Polar Path" }, { "species": "polar bear", "name": "Iceberg", "age": 9, "enclosure": "The Arctic Exhibit", "trail": "Polar Path" }, { "species": "walrus", "name": "Wally", "age": 10, "enclosure": "The Walrus Cove", "trail": "Polar Path" }, { "species": "walrus", "name": "Tusker", "age": 12, "enclosure": "The Walrus Cove", "trail": "Polar Path" }, { "species": "walrus", "name": "Moby", "age": 8, "enclosure": "The Walrus Cove", "trail": "Polar Path" }, { "species": "walrus", "name": "Flippers", "age": 9, "enclosure": "The Walrus Cove", "trail": "Polar Path" } ] @mcp.tool() def get_animals_by_species(species: str) -> List[Dict[str, Any]]: """ 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. Args: species: The species of the animal (e.g., 'lion', 'penguin'). Returns: A list of dictionaries, where each dictionary represents an animal and contains details like name, age, enclosure, and trail. """ logger.info(f">>> 🛠️ Tool: 'get_animals_by_species' called for '{species}'") return [animal for animal in ZOO_ANIMALS if animal["species"].lower() == species.lower()] @mcp.tool() def get_animal_details(name: str) -> Dict[str, Any]: """ Retrieves the details of a specific animal by its name. Args: name: The name of the animal. Returns: A dictionary with the animal's details (species, name, age, enclosure, trail) or an empty dictionary if the animal is not found. """ logger.info(f">>> 🛠️ Tool: 'get_animal_details' called for '{name}'") for animal in ZOO_ANIMALS: if animal["name"].lower() == name.lower(): return animal return {} if __name__ == "__main__": logger.info(f"🚀 MCP server started on port {os.getenv('PORT', 8080)}") asyncio.run( mcp.run_async( transport="http", host="0.0.0.0", port=os.getenv("PORT", 8080), ) )
¡Tu código está completo! Es hora de implementar el servidor de MCP en Cloud Run.
7. Implementa en Cloud Run
Ahora implementa un servidor de MCP en Cloud Run directamente desde el código fuente.
- Crea y abre un nuevo
Dockerfile
para la implementación en Cloud Run:cloudshell edit Dockerfile
- Incluye el siguiente código en el Dockerfile para usar la herramienta
uv
y ejecutar el archivoserver.py
:# 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 EXPOSE $PORT # Run the FastMCP server CMD ["uv", "run", "server.py"]
- Ejecuta el comando
gcloud
para implementar la aplicación en Cloud Run Usa la marcagcloud run deploy zoo-mcp-server \ --no-allow-unauthenticated \ --region=europe-west1 \ --source=. \ --labels=dev-tutorial=codelab-mcp
--no-allow-unauthenticated
para exigir autenticación. Esto es importante por motivos de seguridad. Si no requieres autenticación, cualquier persona puede llamar a tu servidor de MCP y, potencialmente, dañar tu sistema. - Confirma la creación de un nuevo repositorio de Artifact Registry. Como es la primera vez que implementas en Cloud Run desde el código fuente, verás lo siguiente:
EscribeDeploying from source requires an Artifact Registry Docker repository to store built containers. A repository named [cloud-run-source-deploy] in region [europe-west1] will be created. Do you want to continue (Y/n)?
Y
y presionaEnter
. Esto creará un repositorio de Artifact Registry para tu implementación. Esto es necesario para almacenar el contenedor de Docker del servidor de MCP para el servicio de Cloud Run. - Después de unos minutos, verás un mensaje como el siguiente:
Service [zoo-mcp-server] revision [zoo-mcp-server-12345-abc] has been deployed and is serving 100 percent of traffic.
Implementaste tu servidor de MCP. Ahora puedes usarlo.
8. Agrega el servidor de MCP remoto a Gemini CLI
Ahora que implementaste correctamente un servidor MCP remoto, puedes conectarte a él con varias aplicaciones, como Google Code Assist o Gemini CLI. En esta sección, estableceremos una conexión con tu nuevo servidor de MCP remoto usando la CLI de Gemini.
- Otorga permiso a tu cuenta de usuario para llamar al servidor de MCP remoto
gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \ --member=user:$(gcloud config get-value account) \ --role='roles/run.invoker'
- Guarda tus credenciales de Google Cloud y el número de proyecto en variables de entorno para usarlos en el archivo de configuración de Gemini:
export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)") export ID_TOKEN=$(gcloud auth print-identity-token)
- Abre el archivo de configuración de Gemini CLI
cloudshell edit ~/.gemini/settings.json
- Reemplaza tu archivo de configuración de la CLI de Gemini para agregar el servidor de MCP de Cloud Run
{ "mcpServers": { "zoo-remote": { "httpUrl": "https://zoo-mcp-server-$PROJECT_NUMBER.europe-west1.run.app/mcp/", "headers": { "Authorization": "Bearer $ID_TOKEN" } } }, "selectedAuthType": "cloud-shell", "hasSeenIdeIntegrationNudge": true }
- Inicia Gemini CLI en Cloud Shell
Es posible que debas presionargemini
Enter
para aceptar algunos parámetros de configuración predeterminados. - Haz que Gemini enumere las herramientas de MCP disponibles en su contexto.
/mcp
- Pídele a Gemini que encuentre algo en el zoológico
La CLI de Gemini debe saber que debe usar el servidor de MCP deWhere can I find penguins?
zoo-remote
y te preguntará si quieres permitir la ejecución de MCP. - Usa la flecha hacia abajo y, luego, presiona
Enter
para seleccionar.Yes, always allow all tools from server "zoo-remote"
El resultado debe mostrar la respuesta correcta y un cuadro de visualización que indique que se usó el servidor de MCP.
¡Lo lograste! Implementaste correctamente un servidor de MCP remoto en Cloud Run y lo probaste con la CLI de Gemini.
Cuando quieras finalizar la sesión, escribe /quit
y, luego, presiona Enter
para salir de la CLI de Gemini.
Depuración
Si ves un error como este:
🔍 Attempting OAuth discovery for 'zoo-remote'... ❌ 'zoo-remote' requires authentication but no OAuth configuration found Error connecting to MCP server 'zoo-remote': MCP server 'zoo-remote' requires authentication. Please configure OAuth or check server settings.
Es probable que se haya agotado el tiempo de espera del token de ID y que se deba configurar ID_TOKEN
nuevamente.
- Escribe
/quit
y, luego, presionaEnter
para salir de Gemini CLI. - Configura tu proyecto en la terminal
gcloud config set project [PROJECT_ID]
- Reinicia el paso 2 anterior
9. Verifica las llamadas a herramientas en los registros del servidor (opcional)
Para verificar que se llamó a tu servidor de MCP de Cloud Run, consulta los registros del servicio.
gcloud run services logs read zoo-mcp-server --region europe-west1 --limit=5
Deberías ver un registro de salida que confirme que se realizó una llamada a la herramienta. 🛠️
2025-08-05 19:50:31 INFO: 169.254.169.126:39444 - "POST /mcp/ HTTP/1.1" 200 OK 2025-08-05 19:50:31 [INFO]: Processing request of type CallToolRequest 2025-08-05 19:50:31 [INFO]: >>> 🛠️ Tool: 'get_animals_by_species' called for 'penguin'
10. (Opcional) Agrega la instrucción de MCP al servidor
Una instrucción de MCP puede acelerar tu flujo de trabajo para las instrucciones que ejecutas con frecuencia, ya que crea una abreviatura para una instrucción más larga.
La CLI de Gemini convierte automáticamente las instrucciones de MCP en comandos de barra personalizados para que puedas invocar una instrucción de MCP escribiendo /prompt_name
, donde prompt_name
es el nombre de tu instrucción de MCP.
Crea una instrucción de MCP para que puedas encontrar rápidamente un animal en el zoológico escribiendo /find animal
en la CLI de Gemini.
- Agrega este código a tu archivo
server.py
sobre el guardián principal (if __name__ == "__main__":
).@mcp.prompt() def find(animal: str) -> str: """ Find which exhibit and trail a specific animal might be located. """ return ( f"Please find the exhibit and trail information for {animal} in the zoo. " f"Respond with '[animal] can be found in the [exhibit] on the [trail].'" f"Example: Penguins can be found in The Arctic Exhibit on the Polar Path." )
- Vuelve a implementar tu aplicación en Cloud Run
gcloud run deploy zoo-mcp-server \ --no-allow-unauthenticated \ --region=europe-west1 \ --source=. \ --labels=dev-tutorial=codelab-mcp
- Actualiza tu ID_TOKEN para tu servidor de MCP remoto
export ID_TOKEN=$(gcloud auth print-identity-token)
- Después de implementar la nueva versión de tu aplicación, inicia Gemini CLI.
gemini
- En la instrucción, usa el nuevo comando personalizado que creaste:
/find --animal="lions"
Deberías ver que Gemini CLI llama a la herramienta get_animals_by_species
y da formato a la respuesta según las instrucciones de la instrucción de MCP.
╭───────────────────────────╮ │ > /find --animal="lion" │ ╰───────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │ ✔ get_animals_by_species (zoo-remote MCP Server) get_animals_by_species (zoo-remote MCP Server) │ │ │ │ [{"species":"lion","name":"Leo","age":7,"enclosure":"The Big Cat │ │ Plains","trail":"Savannah │ │ Heights"},{"species":"lion","name":"Nala","age":6,"enclosure":"The Big Cat │ │ Plains","trail":"Savannah │ │ Heights"},{"species":"lion","name":"Simba","age":3,"enclosure":"The Big Cat │ │ Plains","trail":"Savannah │ │ Heights"},{"species":"lion","name":"King","age":8,"enclosure":"The Big Cat │ │ Plains","trail":"Savannah Heights"}] │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ✦ Lions can be found in The Big Cat Plains on the Savannah Heights.
Objetivos flexibles para ponerte a prueba
Para un desafío adicional, intenta seguir los mismos pasos para crear una instrucción que devuelva datos curiosos sobre especies animales específicas en el zoológico.
O, para poner a prueba lo que aprendiste, piensa en una herramienta que usarías con frecuencia y, luego, implementa un segundo servidor de MCP remoto. Luego, agrégalo a la configuración de la CLI de Gemini para ver si funciona.
11. Conclusión
¡Felicitaciones! Implementaste correctamente un servidor de MCP remoto seguro y te conectaste a él.
Continúa con el siguiente lab
Este lab es el primero de una serie de tres partes. En el segundo lab, usarás el servidor de MCP que creaste con un agente de ADK.
Usa un servidor de MCP en Cloud Run con un agente de ADK
(Opcional) Limpieza
Si no continuarás con el siguiente lab y deseas limpiar lo que creaste, puedes borrar tu proyecto de Cloud para evitar que se generen cargos adicionales.
Si bien Cloud Run no cobra cuando el servicio no se usa, es posible que se te cobre por el almacenamiento de la imagen del contenedor en Artifact Registry. Si borras tu proyecto de Cloud, se dejan de facturar todos los recursos que usaste en ese proyecto.
Si quieres, borra el proyecto:
gcloud projects delete $GOOGLE_CLOUD_PROJECT
También puedes borrar los recursos innecesarios de tu disco de Cloud Shell. Puedes hacer lo siguiente:
- Borra el directorio del proyecto del codelab:
rm -rf ~/mcp-on-cloudrun
- Advertencia. Esta próxima acción no se puede deshacer. Si quieres borrar todo el contenido de Cloud Shell para liberar espacio, puedes borrar todo el directorio principal. Ten cuidado de guardar en otro lugar todo lo que quieras conservar.
sudo rm -rf $HOME