<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: joshyfruit</title>
    <description>The latest articles on Forem by joshyfruit (@joshyfruit).</description>
    <link>https://forem.com/joshyfruit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3239425%2Fd6f67ac4-5c23-42b7-bf4f-7a26435dd76f.jpeg</url>
      <title>Forem: joshyfruit</title>
      <link>https://forem.com/joshyfruit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joshyfruit"/>
    <language>en</language>
    <item>
      <title>From IT Manager to AI Engineer: Build a Cloud Infrastructure Agent with Cloud Run's Managed MCP Server</title>
      <dc:creator>joshyfruit</dc:creator>
      <pubDate>Thu, 30 Apr 2026 00:57:46 +0000</pubDate>
      <link>https://forem.com/joshyfruit/from-it-manager-to-ai-engineer-build-a-cloud-infrastructure-agent-with-cloud-runs-managed-mcp-pan</link>
      <guid>https://forem.com/joshyfruit/from-it-manager-to-ai-engineer-build-a-cloud-infrastructure-agent-with-cloud-runs-managed-mcp-pan</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Google Cloud NEXT '26 dropped over 260 announcements.&lt;/strong&gt; Most headlines went to Gemini 3.1, TPU v8, and the Agentic Data Cloud. But buried in the Cloud Run section was something that made me stop scrolling — a &lt;strong&gt;fully managed remote MCP server&lt;/strong&gt;, now generally available. If you manage infrastructure AND build AI systems, this one's for you.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why This Hit Different for Me
&lt;/h2&gt;

&lt;p&gt;I wear two hats. One day I'm SSH-ing into VMs, reviewing Cloud Run deployments, and making sure services don't fall over at 2am. The next I'm wiring up LLM agents, building tool pipelines, and figuring out why my context window blew up. These two worlds have always felt weirdly disconnected.&lt;/p&gt;

&lt;p&gt;MCP (Model Context Protocol) on Cloud Run is the first thing I've seen that genuinely bridges them. Instead of hand-crafting API clients for every infra operation your agent needs to do, you point it at a managed MCP server — and suddenly your AI agent can deploy services, read logs, and inspect health metrics like a junior SRE who never sleeps.&lt;/p&gt;

&lt;p&gt;Let's build it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;By the end of this walkthrough you'll have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The built-in Cloud Run MCP server&lt;/strong&gt; wired up to Gemini CLI so you can manage deployments via natural language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A custom MCP server&lt;/strong&gt; running on Cloud Run that exposes infrastructure health tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An ADK agent&lt;/strong&gt; that combines both to answer questions like &lt;em&gt;"Which of my services had errors in the last hour?"&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the full picture of what we're assembling:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jo4gpfuq5dh16ayrp0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jo4gpfuq5dh16ayrp0c.png" alt="Architecture diagram showing the Cloud Run Managed MCP Server setup — Gemini CLI and ADK Agent connect through Model Armor and Cloud IAM to both the Google Managed MCP Server (run.googleapis.com/mcp) and a custom infra-health-mcp service on Cloud Run, which reads from Cloud Logging and Artifact Registry" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Google Cloud project with billing enabled&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;&lt;code&gt;gcloud&lt;/code&gt; CLI&lt;/a&gt; installed and authenticated&lt;/li&gt;
&lt;li&gt;Python 3.10+&lt;/li&gt;
&lt;li&gt;Docker (for building the custom server)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/google-gemini/gemini-cli" rel="noopener noreferrer"&gt;Gemini CLI&lt;/a&gt; installed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Set your project up front so every command just works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-project-id"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-central1"&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project &lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IAM roles you'll need on your account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;roles/run.admin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;roles/iam.serviceAccountUser&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;roles/artifactregistry.writer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 1 — Use the Built-in Cloud Run MCP Server
&lt;/h2&gt;

&lt;p&gt;Google now hosts a fully managed MCP server at &lt;code&gt;https://run.googleapis.com/mcp&lt;/code&gt;. It exposes tools like &lt;code&gt;list_services&lt;/code&gt;, &lt;code&gt;get_service&lt;/code&gt;, &lt;code&gt;deploy_service_from_image&lt;/code&gt;, and &lt;code&gt;deploy_service_from_archive&lt;/code&gt; — no setup required on your end.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Authenticate
&lt;/h3&gt;

&lt;p&gt;The managed endpoint uses your Google Cloud identity. Make sure your ADC (Application Default Credentials) are set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud auth application-default login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Wire it to Gemini CLI
&lt;/h3&gt;

&lt;p&gt;Open (or create) &lt;code&gt;~/.gemini/settings.json&lt;/code&gt; and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cloud-run"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://run.googleapis.com/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"transport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Talk to Your Infrastructure
&lt;/h3&gt;

&lt;p&gt;Fire up Gemini CLI and try this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gemini

&amp;gt; List all my Cloud Run services in us-central1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see it call &lt;code&gt;list_services&lt;/code&gt; under the hood and return a clean summary of every service, its URL, and status. No &lt;code&gt;gcloud run services list --region us-central1 --format=json | jq ...&lt;/code&gt; gymnastics required.&lt;/p&gt;

&lt;p&gt;Try something bolder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Deploy the image us-docker.pkg.dev/cloudrun/container/hello to a new service
  called "hello-from-agent" in us-central1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It calls &lt;code&gt;deploy_service_from_image&lt;/code&gt;, fills in the parameters, and your service is live. That's infrastructure-as-conversation, and honestly it feels a little magical the first time.&lt;/p&gt;

&lt;p&gt;Here's what a full agent session looks like — listing services, spotting errors, and triggering a hotfix deploy all from one prompt chain:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxipz598e5f3u0zhj4uv4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxipz598e5f3u0zhj4uv4.png" alt="Terminal screenshot showing a Gemini CLI session: the agent calls infra-health-mcp to list services, checks error rates in parallel across three services, surfaces a 5xx spike on api-gateway, then deploys a hotfix image via the Cloud Run managed MCP server" width="800" height="792"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;IT Specialist note:&lt;/strong&gt; The managed endpoint enforces Cloud IAM on every call. If your credentials don't have &lt;code&gt;run.services.create&lt;/code&gt;, the deploy fails cleanly with a permission error — not a hallucinated success. That's the kind of guardrail you need when agents touch production infra.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part 2 — Build &amp;amp; Deploy Your Own Custom MCP Server
&lt;/h2&gt;

&lt;p&gt;The built-in server covers Cloud Run operations. But what about your custom health checks, log analysis, or cross-service diagnostics? That's where you roll your own.&lt;/p&gt;

&lt;p&gt;We'll build an &lt;strong&gt;Infra Health MCP Server&lt;/strong&gt; with three tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;list_services&lt;/code&gt; — wraps Cloud Run's Admin API&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_service_error_rate&lt;/code&gt; — queries Cloud Logging for 5xx errors&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;check_service_health&lt;/code&gt; — returns a simple green/yellow/red status&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Create the Project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;infra-health-mcp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;infra-health-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;pyproject.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[project]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"infra-health-mcp"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;requires-python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.10&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="py"&gt;"fastmcp&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"google-cloud-run&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"google-cloud-logging&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Write the MCP Server
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;server.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.cloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;run_v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;cloud_logging&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[%(levelname)s]: %(message)s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Infra Health MCP Server&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run_v2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ServicesClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;log_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cloud_logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_services&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;List all Cloud Run services with their status and URLs.

    Args:
        project_id: Google Cloud project ID
        region: GCP region (e.g. us-central1)

    Returns:
        JSON list of services with name, URL, and last deployment time
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Listing services in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;projects/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/locations/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;run_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_services&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uri&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;last_deployed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_time&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ready&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminal_condition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminal_condition&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_service_error_rate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get the 5xx error count for a Cloud Run service over a time window.

    Args:
        project_id: Google Cloud project ID
        region: GCP region
        service_name: Name of the Cloud Run service
        minutes: How many minutes back to look (default 60)

    Returns:
        JSON with total requests, error count, and error rate percentage
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Checking error rate for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; over last &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; minutes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;filter_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;resource.type=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cloud_run_revision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;resource.labels.service_name=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;resource.labels.location=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;httpRequest.status&amp;gt;=500 &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timestamp&amp;gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;error_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;filter_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;filter_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;window_minutes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error_count_5xx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;error_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checked_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;


&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_service_health&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return a simple health status for a Cloud Run service.

    Args:
        project_id: Google Cloud project ID
        region: GCP region
        service_name: Name of the Cloud Run service

    Returns:
        JSON with status (green/yellow/red) and a human-readable reason
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Health check for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;projects/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/locations/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/services/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminal_condition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminal_condition&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNKNOWN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CONDITION_SUCCEEDED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;green&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Service is running and healthy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CONDITION_FAILED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CONTAINER_FAILED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;red&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Service is in a failed state: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yellow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Service state is uncertain: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PORT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Infra Health MCP server starting on port &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;streamable-http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why Streamable HTTP?&lt;/strong&gt; Cloud Run is stateless and scales horizontally. The older SSE transport needed persistent connections — a terrible fit for serverless. Streamable HTTP uses plain POST/GET, so every request is independent. Your MCP server scales to zero between calls and you only pay when it's actually doing work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3: Containerize It
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.13-slim&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED=1&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;uv &lt;span class="nb"&gt;sync&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; $PORT&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["uv", "run", "server.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Create a Service Account
&lt;/h3&gt;

&lt;p&gt;Your MCP server needs permission to read Cloud Run and Cloud Logging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud iam service-accounts create infra-health-sa &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--display-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Infra Health MCP Server"&lt;/span&gt;

gcloud projects add-iam-policy-binding &lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"serviceAccount:infra-health-sa@&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.iam.gserviceaccount.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"roles/run.viewer"&lt;/span&gt;

gcloud projects add-iam-policy-binding &lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"serviceAccount:infra-health-sa@&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.iam.gserviceaccount.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"roles/logging.viewer"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Build &amp;amp; Deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create Artifact Registry repo&lt;/span&gt;
gcloud artifacts repositories create mcp-servers &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repository-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;

&lt;span class="c"&gt;# Build and push&lt;/span&gt;
gcloud builds submit &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-docker.pkg.dev/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/mcp-servers/infra-health:latest"&lt;/span&gt;

&lt;span class="c"&gt;# Deploy&lt;/span&gt;
gcloud run deploy infra-health-mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-docker.pkg.dev/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/mcp-servers/infra-health:latest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-allow-unauthenticated&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512Mi &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cpu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--concurrency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;120 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"infra-health-sa@&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.iam.gserviceaccount.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloud Run gives you a URL like &lt;code&gt;https://infra-health-mcp-&amp;lt;hash&amp;gt;-uc.a.run.app&lt;/code&gt;. Grab it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MCP_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gcloud run services describe infra-health-mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'value(status.url)'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Test It Locally via the Cloud Run Proxy
&lt;/h3&gt;

&lt;p&gt;Don't expose your MCP server to the internet directly. Use the proxy to test with your local credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud run services proxy infra-health-mcp &lt;span class="nt"&gt;--region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt; &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now hit it at &lt;code&gt;http://localhost:3000&lt;/code&gt; — your credentials are injected automatically, no token management needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3 — Wire Both Servers into an ADK Agent
&lt;/h2&gt;

&lt;p&gt;Now the fun part. We'll build an agent that uses &lt;strong&gt;both&lt;/strong&gt; MCP servers — the built-in Cloud Run one and your custom infra health server — to answer infrastructure questions like a seasoned SRE.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install ADK
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;google-adk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the Agent
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;agent.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LlmAgent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.tools.mcp_tool.mcp_toolset&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MCPToolset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StreamableHTTPConnectionParams&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;MCP_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MCP_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# your infra-health-mcp URL
&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Connect to both MCP servers
&lt;/span&gt;    &lt;span class="n"&gt;cloud_run_tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MCPToolset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;StreamableHTTPConnectionParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://run.googleapis.com/mcp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;infra_health_tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MCPToolset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;StreamableHTTPConnectionParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MCP_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;infra-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-2.0-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are an infrastructure operations assistant. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You have access to Cloud Run management tools and infrastructure health tools. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;When asked about service health or errors, always check both the service status &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;and recent error rates before answering. Be concise and actionable.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cloud_run_tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;infra_health_tools&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Example queries — swap these for interactive input
&lt;/span&gt;    &lt;span class="n"&gt;queries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;List all my Cloud Run services in us-central1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Which services had 5xx errors in the last hour?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Give me a health summary for all services&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;MCP_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$MCP_URL&lt;/span&gt; python agent.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see the agent autonomously call &lt;code&gt;list_services&lt;/code&gt;, then loop over each service calling &lt;code&gt;get_service_error_rate&lt;/code&gt; and &lt;code&gt;check_service_health&lt;/code&gt; — building a full infra health picture without you writing a single orchestration loop.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is the moment it clicks.&lt;/strong&gt; You didn't write "for service in services: check health". The agent reasoned its way to that pattern. Your job was defining the tools. That's a genuine shift in how we build infra tooling.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Security: Don't Skip This Section
&lt;/h2&gt;

&lt;p&gt;Agents with infrastructure permissions need real guardrails. Here's what I'd put in place before letting this near production:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Scope service account permissions tightly.&lt;/strong&gt; The &lt;code&gt;infra-health-sa&lt;/code&gt; has read-only roles. If you want an agent that can also deploy, create a &lt;em&gt;separate&lt;/em&gt; service account for write operations and require explicit approval flows before those tools fire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use IAM deny policies for the write MCP tools.&lt;/strong&gt; You can explicitly deny &lt;code&gt;run.services.create&lt;/code&gt; on specific service accounts at the project level — useful if you only want agents to have deploy access in staging, not prod.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Enable Model Armor.&lt;/strong&gt; Google's Model Armor sits in front of MCP calls and blocks prompt injection attempts, malicious URIs, and unsafe content before they reach your tools. Enable it in the Google Cloud console under AI Safety.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Cloud Audit Logs are your friend.&lt;/strong&gt; Every MCP tool call made through Google-managed servers is logged automatically. Set up a log-based alert for any &lt;code&gt;deploy_service_from_image&lt;/code&gt; calls from service accounts that shouldn't be deploying.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example: alert on unexpected deploys&lt;/span&gt;
gcloud logging metrics create unexpected-agent-deploy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"MCP deploy calls from unexpected accounts"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--log-filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'protoPayload.methodName="google.cloud.run.v2.Services.CreateService"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  My Honest Take
&lt;/h2&gt;

&lt;p&gt;What impressed me most at NEXT '26 isn't any single feature — it's that Google is treating MCP as a first-class citizen across the entire platform. BigQuery has a managed MCP server. Cloud Logging has one. Cloud SQL is getting one. This is becoming the standard interface layer between AI agents and cloud services.&lt;/p&gt;

&lt;p&gt;For IT specialists and infrastructure engineers, this is actually exciting rather than threatening. The tedious parts of infra ops — writing one-off scripts to list resources, cross-referencing logs with deployment times, checking health across 20 services — are exactly what agents are good at. You shift from &lt;em&gt;doing&lt;/em&gt; the repetitive tasks to &lt;em&gt;designing the tools&lt;/em&gt; that do them.&lt;/p&gt;

&lt;p&gt;The rough edges? Auth setup for remote MCP servers is still fiddly, especially in multi-project setups. The ADK toolset documentation is still catching up to the pace of announcements. And "fully managed" doesn't yet mean "zero config" — you still need to wire up IAM carefully.&lt;/p&gt;

&lt;p&gt;But the direction is clear, and the foundation is solid. The infra engineer who learns to build good MCP servers is going to be unreasonably productive over the next few years.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Explore the &lt;a href="https://cloud.google.com/run/docs/use-cloud-run-mcp" rel="noopener noreferrer"&gt;official Cloud Run MCP docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Check out the &lt;a href="https://codelabs.developers.google.com/codelabs/cloud-run/use-mcp-server-on-cloud-run-with-an-adk-agent" rel="noopener noreferrer"&gt;ADK MCP codelab&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Try adding a &lt;code&gt;deploy_service&lt;/code&gt; tool to your custom server — and see how differently it feels when an agent handles rollback logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop a comment if you build something cool with this. I'm especially curious what domain-specific MCP servers people come up with.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built and tested as part of the Google Cloud NEXT '26 Writing Challenge. All code examples use placeholder project IDs — swap in your own before running.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloudnextchallenge</category>
    </item>
    <item>
      <title>From Prompts to Action: My Journey Through the Google &amp; Kaggle AI Agents Bootcamp</title>
      <dc:creator>joshyfruit</dc:creator>
      <pubDate>Mon, 15 Dec 2025 02:03:16 +0000</pubDate>
      <link>https://forem.com/joshyfruit/from-prompts-to-action-my-journey-through-the-google-kaggle-ai-agents-bootcamp-2489</link>
      <guid>https://forem.com/joshyfruit/from-prompts-to-action-my-journey-through-the-google-kaggle-ai-agents-bootcamp-2489</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-kaggle-ai-agents-2025-11-10"&gt;Google AI Agents Writing Challenge&lt;/a&gt;: Learning Reflections&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  From Prompts to Action: My Journey Through the Google &amp;amp; Kaggle AI Agents Bootcamp
&lt;/h1&gt;

&lt;p&gt;As someone who has watched AI evolve from "magic black box" to "everyday tool," I often felt a barrier between &lt;em&gt;using&lt;/em&gt; AI and &lt;em&gt;building&lt;/em&gt; with it. Aside from a chatbot what else could AI do and how do I harness the power of AI? I thought building agents required a PhD in Machine Learning. This week, the &lt;strong&gt;5-Day AI Agents Intensive Course&lt;/strong&gt; by Google and Kaggle completely shattered that illusion.&lt;/p&gt;

&lt;p&gt;It turns out, if you can write a Python function, you can build an agent. Here is my deep dive into the code, the concepts, and the tools that made this journey accessible, featuring my capstone project: &lt;strong&gt;Jarbest&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Awakening: Hello, Agent
&lt;/h2&gt;

&lt;p&gt;As coming from non-developer background, I always imagined software as a "bricklayer"—rigidly following a blueprint. Day 1 introduced me to the &lt;strong&gt;Agent&lt;/strong&gt;: a system that acts more like a &lt;strong&gt;film director&lt;/strong&gt;. It doesn't just predict text; it has a "Brain" (the model), "Hands" (tools), and a "Nervous System" (orchestration) to autonomously perceive, reason, and act. We learned that an agent operates in a continuous loop—&lt;strong&gt;Mission, Scan, Think, Act, Observe&lt;/strong&gt;—constantly adapting its plan to solve problems. This framework demystified the magic: I wasn't just coding a chatbot; I was building a system with the agency to execute multi-step missions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Aha!" Moment: It's Just Python &amp;amp; The "USB Port" for AI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhclwvcfuxyc93hlc6ivx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhclwvcfuxyc93hlc6ivx.jpeg" alt="The " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Day 2 was a revelation. I learned that Models are just "Brains"—pattern predictors that cannot see or act. To be useful, they need &lt;strong&gt;Tools&lt;/strong&gt;: the "Eyes" and "Hands" that let them fetch data or execute actions.&lt;/p&gt;

&lt;p&gt;But connecting every tool to every model is a nightmare (the N x M problem). Enter the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of MCP as the &lt;strong&gt;USB port for AI&lt;/strong&gt;. Before USB, you needed a specific cable for every device. MCP lets you plug any tool into any agent using a standard connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code: Giving the Agent "Hands"
&lt;/h3&gt;

&lt;p&gt;In my project, &lt;strong&gt;Jarbest&lt;/strong&gt; (an accessible personal companion), I needed an agent that could check bank account balances. instead of writing a custom connector, I used MCP to "plug in" a secure banking server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Finance Agent: Manages the banks transactions
&lt;/span&gt;&lt;span class="n"&gt;finance_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;finance_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An agent that can help with banking operations like checking balances...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# Assuming; This toolset connects to a secure internal banking server
&lt;/span&gt;    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;MCPToolset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;connection_params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;StreamableHTTPConnectionParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BANK_MCP_URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/mcp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters (and the danger):&lt;/strong&gt;&lt;br&gt;
The agent reads these tool definitions and knows exactly when to use them. If a user asks "Can I afford this pizza?", the agent inherently knows it must first call &lt;code&gt;check_balance&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, the notes warned us: &lt;strong&gt;Using MCP is like plugging in a random USB drive found on the street.&lt;/strong&gt; It could be a legitimate tool, or it could be a "Tool Shadow" (a malicious copy). That's why in Jarbest, I implemented a strict &lt;strong&gt;Application-Layer Gateway&lt;/strong&gt; (via hardcoded allowlists)—ensuring the agent can &lt;em&gt;only&lt;/em&gt; connect to my specific, internal MCP banking server, preventing it from ever "plugging in" to an untrusted source&lt;/p&gt;
&lt;h2&gt;
  
  
  Deep Dive: The Brain (Memory)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzn75ysl68gfa4mx36o3b.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzn75ysl68gfa4mx36o3b.jpeg" alt="The Memory" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Day 3 was where things got sophisticated. A chatbot forgets you the moment you close the tab. An &lt;em&gt;agent&lt;/em&gt; remembers.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Jarbest&lt;/strong&gt;, which is designed for elderly users who value consistency, memory is critical. If "Grandma Jane" asks for her "usual order," the agent shouldn't ask "What is that?"; it should know.&lt;/p&gt;

&lt;p&gt;Here is how I implemented the "Brain" in my root agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;root_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;root_agent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    You are Jarbest...
    Memory: Use the load_memory tool to recall past conversations and preferences 
    (e.g., &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ordering the usual&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;).
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;load_memory&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;--- This single line gives the agent a "brain"
&lt;/span&gt;    &lt;span class="n"&gt;after_agent_callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;auto_save_to_memory&lt;/span&gt; &lt;span class="c1"&gt;# Auto-saves every interaction
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Non-Developer Perspective:&lt;/strong&gt; Think of &lt;code&gt;load_memory&lt;/code&gt; like giving the agent a filing cabinet. When Grandma Jane says "Order me some food," the agent thinks: &lt;em&gt;"I need to check if she has a preference,"&lt;/em&gt; opens the cabinet (&lt;code&gt;load_memory&lt;/code&gt;), finds "Likes Large Pepperoni Pizza," and acts on it. Watching this thought process in real-time was mind-blowing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Squeeze": Debugging the Black Box
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9y170zectzkrr1qb3whi.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9y170zectzkrr1qb3whi.jpeg" alt="The " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Day 4 taught us that "it works" isn't enough. You need to know &lt;em&gt;why&lt;/em&gt; it works. When building a safety-focused agent like Jarbest, I couldn't afford "hallucinations."&lt;/p&gt;

&lt;p&gt;Exploring the &lt;strong&gt;Agent Observability&lt;/strong&gt; labs, I learned to trace the agent's reasoning steps. When my agent refused to order a pizza, I could look at the trace and see:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;User:&lt;/strong&gt; "Order a pizza."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Tool Call:&lt;/strong&gt; &lt;code&gt;check_balance&lt;/code&gt; -&amp;gt; returned $5.00.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Reasoning:&lt;/strong&gt; "Pizza costs $20. User has $5. Result: Unsafe."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Response:&lt;/strong&gt; "I cannot complete this order because your balance is too low."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Seeing that raw reasoning log felt like looking into the matrix. It transformed the LLM from a mysterious oracle into a logical, debuggable software component. I realized I wasn't just "prompting" anymore; I was engineering logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ecosystem: Agents Talking to Agents (A2A)
&lt;/h2&gt;

&lt;p&gt;Day 5 introduced the &lt;strong&gt;Agent-to-Agent (A2A) Protocol&lt;/strong&gt;. This is where I moved from building a single assistant to building a &lt;em&gt;team&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;My "Purchaser Agent" doesn't know how to make pizza. Instead, it connects to a completely separate "Pizza Shop Agent" (simulating a 3rd party vendor).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Creating a client-side proxy for a remote agent
&lt;/span&gt;&lt;span class="n"&gt;pizza_agent_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RemoteA2aAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pizza_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# The "Agent Card" acts like a business card for discovery
&lt;/span&gt;    &lt;span class="n"&gt;agent_card&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:10000/.well-known/agent-card.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Remote pizza agent from external vendor...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;purchaser_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;purchaser_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Your goal is to help the user find and buy items.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;AgentTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza_agent_proxy&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;--- Treating another agent as a tool
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Cool Idea:&lt;/strong&gt; The "Agent Card" isn't just a technical manifest; it's a completely new way for businesses to interact.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;For SMBs (Small to Medium Businesses):&lt;/strong&gt; Instead of constantly maintaining and documenting complex APIs for developers to read, you simply publish an "Agent Card" (like a digital business card) that describes what your service does (e.g., "I sell pepperoni pizza").&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;For Developers:&lt;/strong&gt; It saves massive amounts of time. My support agent just reads this card and &lt;em&gt;instantly&lt;/em&gt; knows how to ask for a customized order.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Future:&lt;/strong&gt; This allows agents to communicate autonomously, representing the transaction of each individual without human friction. It’s like an API that reads itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Capstone Spotlight: Jarbest - Agents for Good
&lt;/h2&gt;

&lt;p&gt;Applying these concepts, I built &lt;strong&gt;Jarbest&lt;/strong&gt; for the "Agents for Good" track.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; The digital world is full of dark patterns and complex UIs that exploit vulnerable users, especially the elderly.&lt;br&gt;
&lt;strong&gt;The Solution:&lt;/strong&gt; A unified "Action Space" that replaces app sprawl.&lt;br&gt;
Jarbest eliminates the friction of switching between banking apps, delivery apps, and websites. Instead of forcing Grandma Jane to install and navigate ten different confusing interfaces, Jarbest uses &lt;strong&gt;Tools, MCP, and A2A&lt;/strong&gt; to communicate with these services directly on her behalf.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt; Simplicity and Safety. By centralizing these actions into one verified conversation, we inherently protect the user. They no longer need to open browsers or install random apps where they might fall victim to phishing sites or fake download buttons. Jarbest acts as the safe, validated operational layer for their digital life.&lt;/p&gt;

&lt;p&gt;Jarbest uses a &lt;strong&gt;Hierarchical Architecture&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsa2lg0yowi0n2rc6uw8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsa2lg0yowi0n2rc6uw8.png" alt="Design Architecture" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Guardian (Root Agent):&lt;/strong&gt; The "Thinking" layer. It never touches money directly. It validates safety.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Auditor (Finance Agent):&lt;/strong&gt; The only agent with access to the MCP banking server (via MCP).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Doer (Purchaser Agent):&lt;/strong&gt; The logistics layer that talks to vendors (via A2A).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This separation of concerns ensures that even if the "Doer" gets confused, the "Guardian" prevents any financial mistakes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/coozgan/kaggle-5day-ai-agents-capstone" rel="noopener noreferrer"&gt;Check out the Code Repository Here&lt;/a&gt;&lt;br&gt;
&lt;a href="https://youtu.be/-NyenNXYFfY" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34glvbwfhlgl8wo2ppzg.jpg" alt="Watch on YouTube" width="480" height="360"&gt;&lt;/a&gt;&lt;br&gt;
WATCH THIS VIDEO 👆👆👆&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Multi-Agent Architecture?
&lt;/h2&gt;

&lt;p&gt;Working with a single "god agent" that handles everything creates a bottleneck. It forces one model to juggle complex reasoning (safety checks, intent parsing) with mundane execution (API calls, order formatting), leading to context overflow and hallucinations.&lt;/p&gt;

&lt;p&gt;By breaking the system into specialized sub-agents, we achieve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Reduced Cognitive Load:&lt;/strong&gt; The Root Agent focuses purely on orchestration and safety, while the Purchaser Agent focuses solely on logistics.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Efficiency:&lt;/strong&gt; We can route simple tasks to faster, cheaper models (Gemini 2.5 Flash) and reserve the powerful reasoning models (Gemini 3 Pro) for the Guardian role.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Scalability:&lt;/strong&gt; New vendors (e.g., a Pharmacy Agent) can be added as new tools for the Purchaser Agent without retraining or complicating the Root Agent's logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Secret Weapon: NotebookLM
&lt;/h2&gt;

&lt;p&gt;The course came with dense whitepapers—goldmines of information on "Context Engineering" and "Agent Quality." But digesting 20-page PDFs can be daunting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Feed the Brain:&lt;/strong&gt; I downloaded the &lt;em&gt;Context Engineering&lt;/em&gt; whitepaper and uploaded it directly to &lt;strong&gt;NotebookLM&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Conversation:&lt;/strong&gt; Instead of reading linearly, I interrogated the text.

&lt;ul&gt;
&lt;li&gt;  &lt;em&gt;Me:&lt;/em&gt; "Explain the trade-offs between vector databases and keyword search for agent memory."&lt;/li&gt;
&lt;li&gt;  &lt;em&gt;NotebookLM:&lt;/em&gt; It synthesized the answer &lt;em&gt;specifically from the whitepaper&lt;/em&gt;, citing the exact page numbers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Podcast:&lt;/strong&gt; I used the "Audio Overview" feature to generate a podcast of the whitepaper. I listened to two AI hosts debate the merits of "Session" vs. "Memory" while I cooked dinner. It turned homework into entertainment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently (The Roadmap)
&lt;/h2&gt;

&lt;p&gt;Building &lt;strong&gt;Jarbest&lt;/strong&gt; in just 5 days was a sprint, and I left plenty of ideas on the cutting room floor. If I had another week, here is what I would tackle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Dynamic Tool Loading:&lt;/strong&gt; Instead of hardcoding tools, I'd want the agent to "discover" new MCP servers on the local network automatically.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Voice Interface:&lt;/strong&gt; Accessibility is key for my target audience (elderly users). Adding a voice layer on top of the text interface would be a game-changer.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Proactive Alerts:&lt;/strong&gt; Currently, the agent waits for input. I want to build a background loop where it can nudge the user: &lt;em&gt;"Hey, you usually order groceries on Tuesday. Should I do that?"&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This bootcamp didn’t just teach me syntax; it fundamentally shifted my mental model of software development. I went from viewing AI as a passive chatbot to seeing it as a dynamic, composable ecosystem of "Doers".&lt;/p&gt;

&lt;p&gt;The combination of accessible frameworks like the Google GenAI SDK, standardized protocols like MCP, and powerful reasoning models has truly democratized agency. You don't need a research lab or a PhD to build systems that perceive, reason, and act—you just need a clear mission and the curiosity to prompt it.&lt;/p&gt;

&lt;p&gt;If you've been on the fence about diving into AI Agents, now is the time to start. The tools are ready and the barrier to entry has never been lower. I can't wait to see what you build.&lt;/p&gt;

</description>
      <category>googleaichallenge</category>
      <category>ai</category>
      <category>agents</category>
      <category>devchallenge</category>
    </item>
    <item>
      <title>Let's Deploy n8n on ec2 instances 🚀🚀🚀</title>
      <dc:creator>joshyfruit</dc:creator>
      <pubDate>Wed, 10 Dec 2025 13:05:04 +0000</pubDate>
      <link>https://forem.com/joshyfruit/lets-deploy-n8n-on-ec2-instances-4jdm</link>
      <guid>https://forem.com/joshyfruit/lets-deploy-n8n-on-ec2-instances-4jdm</guid>
      <description>&lt;h2&gt;
  
  
  I really love automating
&lt;/h2&gt;

&lt;p&gt;I’ve always loved automating things in my workflows.  &lt;/p&gt;

&lt;p&gt;When I stumbled on n8n, I was honestly in awe. Suddenly all the ideas I had before—like updating Google Sheets, posting to social media, hosting my own APIs, syncing Google Drive to an S3 bucket, and a lot more—became realistic without needing to learn a new framework or library for every single task.  &lt;/p&gt;

&lt;p&gt;n8n makes it much easier to build small automations that actually ship and help you in day‑to‑day work, and that’s what made me fall in love with it.  &lt;/p&gt;

&lt;p&gt;So we’ll walk through how to deploy n8n on an AWS EC2 instance so you can start running your own automations on your own infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launching the Instance 🚀
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Launch an Instance
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Log in to your AWS Management Console.&lt;/li&gt;
&lt;li&gt; Navigate to the &lt;strong&gt;EC2 Dashboard&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;Launch instance&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Name and OS Selection
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Name:&lt;/strong&gt; Give your instance a recognizable name, such as &lt;code&gt;n8n&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Application and OS Images (AMI):&lt;/strong&gt; Select &lt;strong&gt;Amazon Linux&lt;/strong&gt;. The default "Amazon Linux 2023 AMI" is a great choice and is eligible for the Free Tier.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Architecture:&lt;/strong&gt; Select &lt;code&gt;64-bit (ARM)&lt;/code&gt; for better cost efficiency on AWS (Graviton processors offer better performance-per-dollar).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Choose an Instance Type
&lt;/h3&gt;

&lt;p&gt;Select an instance type that suits your needs. For this tutorial, we are using &lt;strong&gt;t4g.medium&lt;/strong&gt; (ARM-based Graviton). Here's why t4g.medium is the most cost-efficient choice for n8n:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Better Performance-per-Dollar:&lt;/strong&gt; AWS Graviton2 processors offer 40% better price-to-performance than comparable x86 instances&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sufficient Resources:&lt;/strong&gt; 2 vCPUs and 4GB RAM handle n8n with multiple workers smoothly without over-provisioning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Burstable Performance:&lt;/strong&gt; T4g instances include CPU credits, allowing you to handle traffic spikes without constant high usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower Costs:&lt;/strong&gt; t4g.medium costs ~40% less than equivalent t3.medium with superior performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production-Ready:&lt;/strong&gt; Unlike t4g.micro (limited burstable capacity), t4g.medium can sustain moderate workflows continuously&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For comparison:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;t4g.micro:&lt;/strong&gt; Limited for production (Free Tier only, ~1 vCPU, 1GB RAM)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;t4g.medium:&lt;/strong&gt; Recommended for small-to-medium deployments (2 vCPUs, 4GB RAM)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;t4g.large:&lt;/strong&gt; For high-volume workflows (2 vCPUs, 8GB RAM)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Key Pair
&lt;/h3&gt;

&lt;p&gt;Under &lt;strong&gt;Key pair (login)&lt;/strong&gt;, select an existing key pair from the dropdown menu to ensure you can SSH into your server later. If you don't have one, click "Create new key pair".&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Network Settings (Security Groups)
&lt;/h3&gt;

&lt;p&gt;This is a crucial step to ensure your instance is accessible.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Under &lt;strong&gt;Network settings&lt;/strong&gt;, choose &lt;strong&gt;Create security group&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Ensure the following rules are checked/added:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SSH&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;Your IP (0.0.0.0/0 for testing only)&lt;/td&gt;
&lt;td&gt;Remote terminal access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;Web traffic (redirect to HTTPS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTPS&lt;/td&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;Secure web traffic for n8n UI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqw8naiglesky7klblzt9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqw8naiglesky7klblzt9.png" alt="n8n Essential Security Groups" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Configure Storage
&lt;/h3&gt;

&lt;p&gt;The default &lt;strong&gt;8 GiB&lt;/strong&gt; of gp3 storage is usually sufficient for a basic installation. You can leave this as is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Advanced Details - User Data (The Installation Script)
&lt;/h3&gt;

&lt;p&gt;This is the most important part of the automation. Instead of manually installing software after the server boots, we will provide a script to do it automatically.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Scroll down to the &lt;strong&gt;Advanced details&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt; Scroll to the very bottom to find the &lt;strong&gt;User data&lt;/strong&gt; text field.&lt;/li&gt;
&lt;li&gt; Paste the following script. This script updates the system, installs Docker, sets up Docker Compose, and configures permissions.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git docker

&lt;span class="c"&gt;# Install Docker Compose plugin (system-wide)&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /usr/local/lib/docker/cli-plugins
curl &lt;span class="nt"&gt;-SL&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/docker/compose/releases/latest/download/docker-compose-linux-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/lib/docker/cli-plugins/docker-compose
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/lib/docker/cli-plugins/docker-compose

&lt;span class="c"&gt;# Enable and start Docker&lt;/span&gt;
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;docker
systemctl start docker

&lt;span class="c"&gt;# Allow ec2-user to run docker without sudo&lt;/span&gt;
usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker ec2-user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Highlight:&lt;/strong&gt; The script above automates the installation of Git, Docker, and Docker Compose, saving you several manual command-line steps later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 8: Launch
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Review your settings in the &lt;strong&gt;Summary&lt;/strong&gt; panel on the right.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Launch instance&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Wait for the success message, then click the Instance ID to view your running server.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Connecting and Configuring 🔌
&lt;/h2&gt;

&lt;p&gt;Now that your instance is running, we need to connect to it and download the necessary n8n configuration files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Connect to your Instance
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Select your running instance from the list.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;Connect&lt;/strong&gt; button at the top right of the console.&lt;/li&gt;
&lt;li&gt; Select the &lt;strong&gt;EC2 Instance Connect&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt; Leave the default username as &lt;code&gt;ec2-user&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Click the orange &lt;strong&gt;Connect&lt;/strong&gt; button. A new browser window will open with a terminal interface.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Verify Installation
&lt;/h3&gt;

&lt;p&gt;Once the terminal loads, verify that the installation script from Part 1 ran successfully by checking the Docker Compose version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;If this command returns a version number, your environment is ready.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Download the n8n Setup
&lt;/h3&gt;

&lt;p&gt;We will use a pre-configured setup from GitHub to get n8n running quickly.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Clone the repository:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/coozgan/hosting-n8n-aws.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate into the project directory:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate into the project directory:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hosting-n8n-aws
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Deploying n8n with Workers 🐳
&lt;/h2&gt;

&lt;p&gt;Now we'll set up a production-ready n8n deployment with distributed workers, Redis queuing, PostgreSQL persistence, and Caddy reverse proxy for SSL/TLS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Review Configuration
&lt;/h3&gt;

&lt;p&gt;Review the architecture. This setup includes:flow triggers&lt;br&gt;
    - &lt;strong&gt;n8n Workers&lt;/strong&gt;: Execute workflows asynchronously from a job queue&lt;br&gt;
    - &lt;strong&gt;Redis&lt;/strong&gt;: Manages job distribution and retries&lt;br&gt;
    - &lt;strong&gt;PostgreSQL&lt;/strong&gt;: Stores workflows, executions, and user data&lt;br&gt;
    - &lt;strong&gt;Caddy&lt;/strong&gt;: Reverse proxy with automatic SSL/TLS certificates&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Configure Environment Variables
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create and edit the &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env-example .env
nano .env
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;a. Create a DuckDNS subdomain&lt;br&gt;
Go to &lt;a href="https://www.duckdns.org" rel="noopener noreferrer"&gt;https://www.duckdns.org&lt;/a&gt; and sign in with GitHub, Google, etc.&lt;/p&gt;

&lt;p&gt;b. On the main page, choose a subdomain name (for example, myn8n) and click add domain.&lt;/p&gt;

&lt;p&gt;c. &lt;strong&gt;Find your ip address&lt;/strong&gt; head back to instance connect and type the command, then copy the &lt;em&gt;IP Address&lt;/em&gt;;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl ifconfig.me
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;d. paste the &lt;em&gt;IP Address&lt;/em&gt; duckdns.org website and add the &lt;em&gt;IP Address&lt;/em&gt;.&lt;/p&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6m8u6ot9zp9l0318dug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6m8u6ot9zp9l0318dug.png" alt="Paste IP Address on Duckdns.org" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add the following essential variables:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Domain Configuration&lt;/span&gt;
&lt;span class="nv"&gt;DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-domain.com &lt;span class="c"&gt;#use localhost if you don't have domain&lt;/span&gt;

&lt;span class="c"&gt;# n8n Encryption (generate a strong random key)&lt;/span&gt;
&lt;span class="nv"&gt;N8N_ENCRYPTION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; CHOOSE_YOUR_OWN_KEY

&lt;span class="c"&gt;# PostgreSQL Database&lt;/span&gt;
&lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CHOOSE_YOUR_OWN_PASSWORD

&lt;span class="c"&gt;# Timezone&lt;/span&gt;
&lt;span class="nv"&gt;GENERIC_TIMEZONE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;America/New_York
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Press &lt;code&gt;Ctrl+X&lt;/code&gt; to save, then &lt;code&gt;Y&lt;/code&gt; and &lt;code&gt;Enter&lt;/code&gt; to exit nano.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Keep your &lt;code&gt;N8N_ENCRYPTION_KEY&lt;/code&gt; and &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; safe. If you lose them, you won't be able to recover your workflows or data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3: Start the Services
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Press &lt;code&gt;Ctrl+X&lt;/code&gt; to exit, then press &lt;code&gt;Y&lt;/code&gt; to confirm save, and &lt;code&gt;Enter&lt;/code&gt; to finalize.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Keep your &lt;code&gt;N8N_ENCRYPTION_KEY&lt;/code&gt; and &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; safe. If you lose them, you won't be able to recover your workflows or data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3: Start the Services
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Verify all containers are running:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check the logs to ensure everything started correctly:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 4: Access Your n8n Instance
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Once services are healthy, navigate to your domain in a web browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you configured a domain: &lt;code&gt;https://your-domain.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If testing without domain: &lt;code&gt;http://&amp;lt;instance_public_ipaddress&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You should see the n8n login screen. Create your first user account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Congratulations! You have yourself a n8n single‑instance.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1g0quu8j8hwz4thlecmh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1g0quu8j8hwz4thlecmh.png" alt="Architecture Diagram" width="800" height="1131"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Scaling and Best Practices ⚡
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scaling Horizontally (within the instance)
&lt;/h3&gt;

&lt;p&gt;To add more worker instances, update your &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;n8n-worker-2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;n8n-worker&lt;/span&gt;
  &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;n8n-worker-2&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;#copy the rest of the code&lt;/span&gt;

&lt;span class="na"&gt;n8n-worker-3&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;n8n-worker&lt;/span&gt;
  &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;n8n-worker-3&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;#copy the rest of the code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;a href="https://github.com/coozgan/hosting-n8n-aws?tab=readme-ov-file#horizontal-worker-scaling" rel="noopener noreferrer"&gt;For more information follow this...&lt;/a&gt; 👈
&lt;/h4&gt;




&lt;h3&gt;
  
  
  Performance Optimization Tips
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Adjust Worker Count:&lt;/strong&gt; Start with 2-3 workers and monitor CPU/memory usage.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Database Tuning:&lt;/strong&gt; Prune old execution logs to keep PostgreSQL performant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The docker-compose includes &lt;code&gt;EXECUTIONS_DATA_PRUNE=true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It keeps 7 days of history by default (&lt;code&gt;EXECUTIONS_DATA_MAX_AGE=168&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Redis Configuration:&lt;/strong&gt; The Redis instance has a 512MB memory limit with LRU eviction policy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor with: &lt;code&gt;docker exec redis redis-cli info stats&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Security Best Practices
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enable User Management:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The setup has &lt;code&gt;N8N_USER_MANAGEMENT_DISABLED=false&lt;/code&gt; by default&lt;/li&gt;
&lt;li&gt;Create separate user accounts for team members&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Restrict SSH Access:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update your security group to only allow SSH from your IP&lt;/li&gt;
&lt;li&gt;In AWS Console: Security Groups &amp;gt; Inbound Rules &amp;gt; Edit SSH rule&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Backup Strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regularly backup your PostgreSQL database:
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;postgres pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; postgres postgres &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Backup your n8n data volume:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; n8n-with-workers_n8n-data:/data &lt;span class="se"&gt;\ &lt;/span&gt;
   &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/backup alpine &lt;span class="nb"&gt;tar &lt;/span&gt;czf /backup/n8n-backup.tar.gz &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-C&lt;/span&gt; /data &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SSL/TLS Certificates:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Caddy automatically obtains and renews Let's Encrypt certificates&lt;/li&gt;
&lt;li&gt;Certificates are stored in the &lt;code&gt;caddy-data&lt;/code&gt; volume&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue: Cannot Connect to n8n solution
&lt;/h3&gt;

&lt;p&gt;1️⃣ Check if all services are running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose ps

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2️⃣ Check Caddy logs for SSL certificate issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose logs caddy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3️⃣ Ensure your security group allows HTTP (port 80) and HTTPS (port 443)&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue: Workers Not Processing Jobs Solution
&lt;/h3&gt;

&lt;p&gt;1️⃣ Verify Redis is healthy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec redis redis-cli ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2️⃣ Check worker logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose logs n8n-worker

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3️⃣ Ensure &lt;code&gt;EXECUTIONS_MODE=queue&lt;/code&gt; is set in your environment&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue: Database Connection Errors Solution
&lt;/h3&gt;

&lt;p&gt;1️⃣ Verify PostgreSQL is running and healthy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose logs postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2️⃣ Check database credentials match in &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec postgres psql -U postgres -d postgres -c "\dt"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3️⃣ Ensure &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; in your &lt;code&gt;.env&lt;/code&gt; is correct&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue:Out of Disk Space Solution
&lt;/h3&gt;

&lt;p&gt;1️⃣ Check disk usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker system df
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2️⃣ Prune old data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker system prune -a
docker volume prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3️⃣ Clean up old execution logs in n8n UI: &lt;code&gt;Settings → Executions → Delete&lt;/code&gt; old executions&lt;/p&gt;




&lt;h2&gt;
  
  
  Monitoring and Maintenance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Monitor Container Health
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Real-time resource usage
docker stats

# View service logs
docker compose logs -f [service-name]

# Check specific service health
docker compose ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Regular Maintenance Tasks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Weekly:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor disk usage: &lt;code&gt;df -h&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check error logs: &lt;code&gt;docker compose logs --since 1w | grep -i error&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Monthly:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backup databases and volumes&lt;/li&gt;
&lt;li&gt;Review user access and remove inactive accounts&lt;/li&gt;
&lt;li&gt;Update Docker images: &lt;code&gt;docker compose pull &amp;amp;&amp;amp; docker compose up -d&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quarterly:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security audit of your workflows&lt;/li&gt;
&lt;li&gt;Review and optimize your AWS security groups&lt;/li&gt;
&lt;li&gt;Test disaster recovery (restore from backup)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Cost Optimization on AWS
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Instance Type:&lt;/strong&gt; t4g.medium provides the best balance of cost and performance for n8n deployments

&lt;ul&gt;
&lt;li&gt;ARM-based Graviton2 processors (40% better price-to-performance than x86)&lt;/li&gt;
&lt;li&gt;Sufficient resources for multiple workers without over-provisioning&lt;/li&gt;
&lt;li&gt;Monthly cost: ~$9-12 (vs ~$15-18 for equivalent x86 instances)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Transfer:&lt;/strong&gt; Minimize data egress by keeping compute and data in the same region&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RDS Alternative:&lt;/strong&gt; For high-volume deployments, consider AWS RDS for PostgreSQL instead of self-hosted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch:&lt;/strong&gt; Set up alarms for unusual activity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-Scaling:&lt;/strong&gt; Use EC2 Auto Scaling Groups for multiple instances in production&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Next Steps:make it production-ready
&lt;/h2&gt;

&lt;p&gt;This guide focuses on getting n8n running quickly on EC2 so you can start experimenting. For a more production-ready setup, you’ll likely want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use serverless deployment (ECS Fargate, Elasticache and RDS)&lt;/li&gt;
&lt;li&gt;Use a custom domain with Route 53 or another DNS provider&lt;/li&gt;
&lt;li&gt;Use Elastic Load Balancer and Automatic Scaling Group on ECS Clusters&lt;/li&gt;
&lt;li&gt;Configure environment variables for n8n (like WEBHOOK_URL, credentials, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those topics can be a follow-up post in this series, where the EC2 instance you just created becomes the base for a more secure, robust n8n deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion 🎉
&lt;/h2&gt;

&lt;p&gt;You now have a production-grade n8n automation platform running on AWS with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Cost effective to small to medium automations&lt;/li&gt;
&lt;li&gt;✅ Scalable worker architecture on ARM-based Graviton processors&lt;/li&gt;
&lt;li&gt;✅ Persistent data storage with PostgreSQL&lt;/li&gt;
&lt;li&gt;✅ Automatic SSL/TLS encryption via Caddy&lt;/li&gt;
&lt;li&gt;✅ Job queue system with Redis for reliable executions&lt;/li&gt;
&lt;li&gt;✅ Easy horizontal scaling within your instance&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>architecture</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>Host your first AI Agent on Agentcore</title>
      <dc:creator>joshyfruit</dc:creator>
      <pubDate>Tue, 11 Nov 2025 04:04:06 +0000</pubDate>
      <link>https://forem.com/joshyfruit/host-your-first-ai-agent-on-agentcore-2o06</link>
      <guid>https://forem.com/joshyfruit/host-your-first-ai-agent-on-agentcore-2o06</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exploring AgentCore: Effortless AI Agent Deployment with AWS Strands SDK&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I had a good time exploring AgentCore and I see what AWS is trying to do: eliminate the second guessing on how to deploy your AI Agent. With the release of the Strands SDK, AWS has introduced a truly streamlined way to get agents running quickly. In this post, I’ll walk you through how I hosted my own agent on AgentCore, with everything from setup to cloud deployment.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why AgentCore and Strands SDK?
&lt;/h3&gt;

&lt;p&gt;AWS AgentCore Runtime solves a major pain-point for developers: &lt;strong&gt;secure, reliable, and scalable deployment of AI agents without wrangling cloud infrastructure details&lt;/strong&gt;. The new Strands SDK tightly integrates with AgentCore, letting you focus on your agent logic while AWS handles scaling, session isolation, and production readiness.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step-by-Step: Deploying Your Agent
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS account and CLI configured (&lt;code&gt;aws configure&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Python 3.10+, pip, Docker&lt;/li&gt;
&lt;li&gt;Install the AgentCore toolkit and Strands SDK:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;bedrock-agentcore bedrock-agentcore-starter-toolkit strands-agents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sample Agent (&lt;code&gt;my_agent.py&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bedrock_agentcore&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BedrockAgentCoreApp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BedrockAgentCoreApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.entrypoint&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;user_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello! How can I help you today?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Local Test (optional):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python my_agent.py
&lt;span class="c"&gt;# In another terminal:&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8080/invocations &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"prompt": "Hello!"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configure for Cloud Deployment:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agentcore configure &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; my_agent.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow prompts for runtime options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy to AgentCore Runtime:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agentcore launch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll get an Agent ARN for your deployed agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invoke the Deployed Agent:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agentcore invoke &lt;span class="s1"&gt;'{"prompt": "Tell me a joke"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Architecture Diagram
&lt;/h3&gt;

&lt;p&gt;Here’s a quick summary of the workflow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwa3knt57g7g4tm73jmw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwa3knt57g7g4tm73jmw.png" alt=" " width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Local testing uses HTTP. Deployment and cloud invocation use AWS endpoints over HTTPS for secure communication.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  My Takeaways
&lt;/h3&gt;

&lt;p&gt;AgentCore Runtime and the Strands SDK make deploying AI agents on AWS simple and production-ready. From local prototyping to scalable cloud hosting, you skip the usual cloud headaches and focus on building features.&lt;/p&gt;

&lt;p&gt;Let me know if you want a deeper dive into custom agent logic, more advanced deployment options, or troubleshooting advice!&lt;/p&gt;




</description>
      <category>aws</category>
      <category>agents</category>
      <category>ai</category>
    </item>
    <item>
      <title>HealthBuddy SG: AI-Powered Wellness</title>
      <dc:creator>joshyfruit</dc:creator>
      <pubDate>Mon, 15 Sep 2025 06:22:24 +0000</pubDate>
      <link>https://forem.com/joshyfruit/healthbuddy-sg-ai-powered-wellness-ji8</link>
      <guid>https://forem.com/joshyfruit/healthbuddy-sg-ai-powered-wellness-ji8</guid>
      <description>

&lt;h2&gt;
  
  
  HealthBuddy SG: AI-Powered Wellness for Singapore’s Tropics
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-ai-studio-2025-09-03"&gt;Google AI Studio Multimodal Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;What I Built&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;HealthBuddy SG is a next-generation health and wellness applet designed specifically for Singaporeans and anyone living in a humid, tropical climate. It empowers users to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check their health with natural voice interaction&lt;/strong&gt; (symptom analyzer and recommendations)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scan and analyze local food&lt;/strong&gt; for nutrition and climate-adapted advice&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track personal progress&lt;/strong&gt; and receive daily, hyper-local, climate-aware guidance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My goal: &lt;strong&gt;bring personalized, practical AI health support to the palm of every Singapore resident&lt;/strong&gt;, helping users thrive amidst heat, humidity, and busy urban life.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Demo&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;🔗 Live Applet: &lt;a href="https://healthbuddy-sg-ver1-192629822894.us-west1.run.app/" rel="noopener noreferrer"&gt;https://healthbuddy-sg-ver1-192629822894.us-west1.run.app/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffxmij8k1pbzbf6xa9ne3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffxmij8k1pbzbf6xa9ne3.png" alt="Dashboard UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx7lhqs3pb7bx5vwschpj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx7lhqs3pb7bx5vwschpj.png" alt="Progress Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1o5atxw8hazrs6s4ve9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1o5atxw8hazrs6s4ve9.png" alt="Image Scanner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgikwwrdwhjxvmceai18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgikwwrdwhjxvmceai18.png" alt="Image Scanner Results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1ujoagwk8k7e773fgdo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1ujoagwk8k7e773fgdo.png" alt="QuickDoc an Health AI Voice Assistant"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Demo:&lt;br&gt;
&lt;a href="https://youtu.be/tvuQoJtL4YM" rel="noopener noreferrer"&gt;🎥 Click here to watch HealthBuddy SG’s main features in action&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;How I Used Google AI Studio&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Google AI Studio was the backbone&lt;/strong&gt; for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rapidly building and refining prompts for symptom descriptions (English/Singapore-style)&lt;/li&gt;
&lt;li&gt;Testing and iterating on voice/audio input, food image recognition, and text-based queries&lt;/li&gt;
&lt;li&gt;Using the “Get code” feature to scaffold web endpoints and integrate Gemini in my workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gemini’s &lt;strong&gt;multimodal APIs made it possible to interconnect speech, text, and image understanding&lt;/strong&gt;, enabling truly natural, cross-modal user experiences.&lt;/p&gt;

&lt;p&gt;Cloud Run allowed frictionless, scalable deployment, making the applet ready for public use and further enhancements.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Multimodal Features&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;HealthBuddy SG leverages the following Google AI multimodal capabilities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Voice Symptom Analysis&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users describe how they feel by speaking naturally; Gemini listens, analyzes, and provides health recommendations with Singapore context: “Is it haze related?”, “Possible dengue?”, “Stay hydrated advice”, etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Food Scanner for Local Cuisine&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload food photos; Gemini identifies local dishes (laksa, roti prata, etc.), assesses their nutrition, and gives “heat/hydration” tips specific to Singapore’s climate.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Personalized Progress Dashboard&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visualizes health data, voice check-ins, and food analysis over time with clear cards and charts inspired by leading health apps.&lt;/li&gt;
&lt;li&gt;Delivers actionable, climate-adapted suggestions to boost user wellbeing.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Singapore-Specific Health Guidance&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every result is enhanced with real tips for local concerns (haze, dengue, humidity, heatstroke).&lt;/li&gt;
&lt;li&gt;Smart recommendations for when to seek help from nearby clinics, polyclinics, or pharmacies.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Professional, Responsive UI&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistent card-based design, friendly mascot, easily navigable for mobile or desktop, ensuring a joyful user journey.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Why This Matters&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Singaporeans face unique health challenges—tropical illnesses, heat, and rapid urban pace. HealthBuddy SG uses &lt;strong&gt;state-of-the-art Google AI multimodal technology&lt;/strong&gt; to provide support that’s context-aware and actionable, helping every user make smarter daily wellness choices.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Solo submission by Joshua Cymon Gomez (&lt;a href="https://dev.toyour-devto-link"&gt;@joshyfruit &lt;/a&gt;), using open source and original code/UX—starter voice assistant, new modules, pro-level UI, and code structured for future growth.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  devchallenge #googleaichallenge #ai #gemini #singapore #healthtech
&lt;/h1&gt;




</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>ai</category>
      <category>gemini</category>
    </item>
  </channel>
</rss>
