<?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>DEV Community: Sarvar Nadaf</title>
    <description>The latest articles on DEV Community by Sarvar Nadaf (@sarvar_04).</description>
    <link>https://dev.to/sarvar_04</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1163149%2F5afa2902-591e-4944-b6fa-9bbba80c6e95.png</url>
      <title>DEV Community: Sarvar Nadaf</title>
      <link>https://dev.to/sarvar_04</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sarvar_04"/>
    <language>en</language>
    <item>
      <title>I Followed the n8n AWS Docs and It Broke at the First Command</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Fri, 25 Sep 2026 14:09:50 +0000</pubDate>
      <link>https://dev.to/aws-builders/i-followed-the-n8n-aws-docs-and-it-broke-at-the-first-command-4e1k</link>
      <guid>https://dev.to/aws-builders/i-followed-the-n8n-aws-docs-and-it-broke-at-the-first-command-4e1k</guid>
      <description>&lt;p&gt;You SSH into a fresh EC2 box, run &lt;code&gt;sudo dnf install -y docker&lt;/code&gt;, then the very next command from the guide you're following:&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;p&gt;And it dies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker: &lt;span class="s1"&gt;'compose'&lt;/span&gt; is not a docker command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hit this on camera. Followed the steps, watched n8n never come up, and spent a few minutes convinced I'd broken something. I hadn't. Almost every "self-host n8n on AWS" tutorial has this exact hole in it, because the authors tested on Ubuntu or a VPS where Docker installs differently. On Amazon Linux 2023, the default AMI for EC2, the command they tell you to run gives you half of what you need.&lt;/p&gt;

&lt;p&gt;This post is the fix, and the full path from a bare EC2 instance to your first n8n login. Two containers, one paste-ready compose file, no reverse proxy yet (that's the hardening step, and it's its own article). By the end you have n8n on Postgres running on a box you own.&lt;/p&gt;




&lt;h2&gt;
  
  
  The gotcha, up front
&lt;/h2&gt;

&lt;p&gt;On Amazon Linux 2023:&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;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;installs the Docker &lt;strong&gt;engine&lt;/strong&gt;. It does not install the &lt;strong&gt;Compose v2 plugin&lt;/strong&gt;. They're separate now. Compose stopped being a standalone &lt;code&gt;docker-compose&lt;/code&gt; binary years ago and became a plugin that lives under Docker's CLI, and &lt;code&gt;dnf&lt;/code&gt;'s docker package doesn't bundle it. So the engine runs fine, &lt;code&gt;docker run&lt;/code&gt; works, and then &lt;code&gt;docker compose&lt;/code&gt; throws &lt;code&gt;'compose' is not a docker command&lt;/code&gt; because the plugin isn't there.&lt;/p&gt;

&lt;p&gt;On Ubuntu you'd install &lt;code&gt;docker.io&lt;/code&gt; plus &lt;code&gt;docker-compose-plugin&lt;/code&gt; from Docker's apt repo and never notice. On AL2023 the plugin is on you. Here's the whole install, plugin included:&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;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /usr/local/lib/docker/cli-plugins
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://github.com/docker/compose/releases/download/v2.29.7/docker-compose-linux-x86_64 &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;sudo chmod&lt;/span&gt; +x /usr/local/lib/docker/cli-plugins/docker-compose
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That third command is the one the other guides skip: it drops Docker's official Compose plugin binary into the directory the CLI actually looks in. Now both answer:&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;sudo &lt;/span&gt;docker &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# Docker version 25.0.14, build 0bab007&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker compose version
&lt;span class="c"&gt;# Docker Compose version v2.29.7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two version banners is how you know the box is actually ready. If you only check the first one, you find out the plugin is missing at &lt;code&gt;docker compose up&lt;/code&gt;, which is the worst time to find out.&lt;/p&gt;

&lt;p&gt;One catch on the download URL: it ends in &lt;code&gt;x86_64&lt;/code&gt;, which is right for a &lt;code&gt;t3&lt;/code&gt; (Intel) box. If you launched a Graviton/ARM instance (&lt;code&gt;t4g&lt;/code&gt; and friends), grab the &lt;code&gt;aarch64&lt;/code&gt; binary instead by swapping the filename to &lt;code&gt;docker-compose-linux-aarch64&lt;/code&gt;. The wrong architecture installs cleanly and then fails with an exec-format error the moment you run it, which sends you hunting in the wrong place. Pick the binary that matches your instance. And &lt;code&gt;v2.29.7&lt;/code&gt; is just the version I pinned here; check &lt;a href="https://github.com/docker/compose/releases" rel="noopener noreferrer"&gt;Docker's releases&lt;/a&gt; and use the current one if you'd rather not lag.&lt;/p&gt;




&lt;h2&gt;
  
  
  Launch the box first
&lt;/h2&gt;

&lt;p&gt;Before any of that, you need the instance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AMI:&lt;/strong&gt; Amazon Linux 2023.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size:&lt;/strong&gt; &lt;code&gt;t3.small&lt;/code&gt; or larger. n8n plus Postgres want about 2 GB of RAM. Skip &lt;code&gt;t2.micro&lt;/code&gt; and &lt;code&gt;t3.micro&lt;/code&gt; (1 GB) unless you like watching containers get OOM-killed mid-run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Group:&lt;/strong&gt; SSH (22) from your IP only. For this private first test you can open 5678 to your IP only too. Do not open 5678 to &lt;code&gt;0.0.0.0/0&lt;/code&gt;. An open n8n editor on the public internet is an editor anyone can find and claim.&lt;/li&gt;
&lt;li&gt;SSH in: &lt;code&gt;ssh -i your-key.pem ec2-user@&amp;lt;PUBLIC_IP&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything below runs on that box as &lt;code&gt;ec2-user&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why bother self-hosting at all
&lt;/h2&gt;

&lt;p&gt;n8n is a credential aggregator. One instance can hold your Stripe key, your database password, your Slack token, your Google OAuth, all in one place. On n8n Cloud that pile lives on someone else's server, priced per seat, capped on executions. Self-hosting moves it onto a box inside your own AWS account: no seat fees, no execution caps, your data stays home, and you pick the version.&lt;/p&gt;

&lt;p&gt;The trade is honest. You now own the patching, the backups, and the security. This article gets it running. The hardening article (next in the series) closes the door behind it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The stack: two containers
&lt;/h2&gt;

&lt;p&gt;That's the whole thing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;n8n&lt;/strong&gt; runs the editor and the workflow engine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postgres&lt;/strong&gt; stores workflows and executions so they survive a restart.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not SQLite. n8n defaults to SQLite, which is fine for a five-minute look, but it handles concurrent executions poorly and migrating off it later is an afternoon you won't enjoy. Start on Postgres.&lt;/p&gt;

&lt;p&gt;Clone the repo so you have the compose file on the box:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/simplynadaf/self-host-n8n-on-ec2.git
&lt;span class="nb"&gt;cd &lt;/span&gt;self-host-n8n-on-ec2/compose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the compose file:&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;n8n&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;n8nio/n8n:1.123.64&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5678:5678"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_SECURE_COOKIE=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_DIAGNOSTICS_ENABLED=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_PERSONALIZATION_ENABLED=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_ENCRYPTION_KEY=change-me-to-a-long-random-string-please&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_TYPE=postgresdb&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_POSTGRESDB_HOST=postgres&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_POSTGRESDB_DATABASE=n8n&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_POSTGRESDB_USER=n8n&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_POSTGRESDB_PASSWORD=change-me-strong-db-password&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB=n8n&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=n8n&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=change-me-strong-db-password&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pg_data:/var/lib/postgresql/data&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pg_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three lines decide whether this works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The image is pinned to &lt;code&gt;1.123.64&lt;/code&gt;, not &lt;code&gt;latest&lt;/code&gt;.&lt;/strong&gt; Pinning makes the build reproducible, and this version patches a real issue: CVE-2026-65589, an info-disclosure bug where credentials passed as custom headers in LLM sub-nodes could land in execution records. Run &lt;code&gt;latest&lt;/code&gt; and you're one silent restart away from a version you didn't choose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;N8N_ENCRYPTION_KEY&lt;/code&gt; encrypts every credential n8n stores.&lt;/strong&gt; Set a real 32-plus character random string and save it somewhere safe right now. Lose it and every saved credential is unrecoverable. A restored backup without this key is a database full of workflows whose logins can't be decrypted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DB password appears twice&lt;/strong&gt; (&lt;code&gt;DB_POSTGRESDB_PASSWORD&lt;/code&gt; in the n8n service, &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; in postgres) and the two values must match. If they don't, n8n can't reach its own database and the container just restarts in a loop while you wonder why the editor never loads.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bring it up
&lt;/h2&gt;

&lt;p&gt;Edit the two passwords and the encryption key, then:&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;sudo &lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker compose ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First run pulls both images and starts them. Give it twenty to forty seconds, then check n8n is answering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'n8n -&amp;gt; HTTP %{http_code}\n'&lt;/span&gt; http://localhost:5678
&lt;span class="c"&gt;# n8n -&amp;gt; HTTP 200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;200&lt;/code&gt; means n8n is up and serving. Once Compose was actually installed, this came back green on the first try.&lt;/p&gt;




&lt;h2&gt;
  
  
  First login
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;http://&amp;lt;PUBLIC_IP&amp;gt;:5678&lt;/code&gt; in a browser. A fresh instance shows the setup wizard. Create your owner account with an email and a strong password, submit, and you land on the canvas.&lt;/p&gt;

&lt;p&gt;Do this immediately after the box comes up, not tomorrow. The first account created on a fresh n8n becomes the &lt;strong&gt;owner&lt;/strong&gt;, and until someone submits that form, it's open to whoever reaches it first. On a private Security Group that's only you. It's still a habit worth keeping.&lt;/p&gt;

&lt;p&gt;One line in the compose file explains itself here: &lt;code&gt;N8N_SECURE_COOKIE=false&lt;/code&gt;. That's only so first login works over plain HTTP on a raw IP while testing. It's a development shortcut, not a keeper. In production n8n sits behind HTTPS and this setting goes away.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before this goes anywhere near the internet
&lt;/h2&gt;

&lt;p&gt;The compose here publishes port 5678 directly. That's fine for a private test where the Security Group only lets your IP in, but it falls apart on the open internet, where certificate transparency logs announce every new HTTPS host within minutes and scanners find fresh boxes fast.&lt;/p&gt;

&lt;p&gt;Before you point a domain at this or widen the Security Group:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep n8n patched (1.123.64 or newer).&lt;/li&gt;
&lt;li&gt;Stop publishing 5678. Use &lt;code&gt;expose&lt;/code&gt; so it's only reachable inside the Docker network.&lt;/li&gt;
&lt;li&gt;Put Caddy in front for automatic TLS, and restrict the editor to your admin IP. Leave only &lt;code&gt;/webhook/*&lt;/code&gt; public.&lt;/li&gt;
&lt;li&gt;Move the encryption key and DB password into AWS Secrets Manager, read through a least-privilege IAM role.&lt;/li&gt;
&lt;li&gt;Security Group: allow 22 (your IP), 80, 443. Never 5678 to &lt;code&gt;0.0.0.0/0&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That whole checklist is the hardening article in this series. If this box is going public, that's your next read.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stop and start without losing data
&lt;/h2&gt;

&lt;p&gt;Your data lives in the &lt;code&gt;pg_data&lt;/code&gt; volume, so you can stop the stack safely:&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;sudo &lt;/span&gt;docker compose down     &lt;span class="c"&gt;# stop, keep the data&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;    &lt;span class="c"&gt;# start again&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;down&lt;/code&gt; stops the containers and keeps the volume, so your workflows and account are still there on the next &lt;code&gt;up&lt;/code&gt;. To also wipe the data, that's &lt;code&gt;down -v&lt;/code&gt;, and only when you mean it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you have now
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;n8n running on an EC2 box you control, backed by Postgres.&lt;/li&gt;
&lt;li&gt;A pinned, patched image instead of a moving &lt;code&gt;latest&lt;/code&gt; target.&lt;/li&gt;
&lt;li&gt;An encryption key you actually set and saved.&lt;/li&gt;
&lt;li&gt;A clear line for what to do before this faces the internet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And you know the one thing most AWS n8n guides get wrong: on Amazon Linux 2023, installing Docker does not install Compose, and the fix is one &lt;code&gt;curl&lt;/code&gt; into the plugin directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the series goes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Get it running&lt;/strong&gt; (this one): bare EC2 to first login, past the Compose gotcha.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harden it:&lt;/strong&gt; TLS, closed editor port, Secrets Manager, least-privilege IAM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Give it a brain:&lt;/strong&gt; wire this same n8n to Amazon Bedrock and build a real AI agent on the canvas, model running in your account, no OpenAI key anywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The compose file, the install and verify scripts, and the full setup notes are in the repo: &lt;a href="https://github.com/simplynadaf/self-host-n8n-on-ec2" rel="noopener noreferrer"&gt;github.com/simplynadaf/self-host-n8n-on-ec2&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more on AWS architecture, DevOps, and AI Infrastructure:&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Portfolio&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://dev.to/sarvar_04"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://www.youtube.com/@sarvar-nadaf" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="mailto:simplynadaf@gmail.com"&gt;Email&lt;/a&gt; | &lt;a href="https://builder.aws.com/community/@sarvar" rel="noopener noreferrer"&gt;AWS Builder Center&lt;/a&gt; | &lt;a href="https://x.com/SarvarN_04" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>n8n</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Per-Agent Cost Tracking for Multi-Agent AI on AWS</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Wed, 23 Sep 2026 13:41:46 +0000</pubDate>
      <link>https://dev.to/sarvar_04/per-agent-cost-tracking-for-multi-agent-ai-on-aws-10eg</link>
      <guid>https://dev.to/sarvar_04/per-agent-cost-tracking-for-multi-agent-ai-on-aws-10eg</guid>
      <description>&lt;p&gt;Your multi-agent run just returned a perfect answer. Clean summary, right resources, no errors. Your APM dashboard (the application performance monitoring you already run: uptime, latency, error rate) says 200 OK, latency fine, everything green.&lt;/p&gt;

&lt;p&gt;And you were silently billed about 1.4x what you should have been.&lt;/p&gt;

&lt;p&gt;That is the part nobody shows you. Nested traces and per-agent cost are becoming common; the primitives are easy to find now. What stays rare is a data model that lets you &lt;em&gt;act&lt;/em&gt; on them: catch the run that looks completely successful while it burns money in the middle. The paper "Why Do Multi-Agent LLM Systems Fail?" (MAST, arXiv:2503.13657) hand-annotated 150 traces across 7 state-of-the-art multi-agent systems, hit an inter-annotator agreement of kappa=0.88, and measured failure rates from 41% to 86.7%. The uncomfortable finding: many of those failures do not crash. They complete. They look fine.&lt;/p&gt;

&lt;p&gt;In this article I build a small read-only "AWS Account Investigator" crew, wire real cost into every trace span, and then reproduce three silent-waste patterns with real Amazon Nova Pro dollars. You can run the whole thing for $0 locally. Nothing gets created, modified, or deleted in your AWS account.&lt;/p&gt;

&lt;p&gt;If you only have two minutes, jump straight to the unique part: catching silent waste. The build up to it matters, but that section is the payoff.&lt;/p&gt;

&lt;p&gt;I spent about a week on this against a real AWS account: a few days probing the SDK's behavior before I trusted it, then several more building the crew, watching the trace design break twice, and reading the SDK source when the docs ran out. What follows is written from that, not from a quickstart. The scars are in here on purpose, because they are the part that saves you the week.&lt;/p&gt;

&lt;p&gt;This is for people already building AI agents who have never put a real observability layer under them. You know agents, tools, and crews. Where the tracing vocabulary (spans, traces, OpenTelemetry) is new, I define it the first time it shows up.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why agent observability is a different problem&lt;/li&gt;
&lt;li&gt;Picking the instrumentation&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Adding Traccia to your code&lt;/li&gt;
&lt;li&gt;The stack: AWS native and read only&lt;/li&gt;
&lt;li&gt;The cost bridge and one gotcha&lt;/li&gt;
&lt;li&gt;Modeling a multi-agent crew in traces&lt;/li&gt;
&lt;li&gt;The unique part: catching silent waste&lt;/li&gt;
&lt;li&gt;Watching it happen: the live control panel&lt;/li&gt;
&lt;li&gt;Build your own, at zero cost and read only&lt;/li&gt;
&lt;li&gt;An honest take on Traccia&lt;/li&gt;
&lt;li&gt;Honest caveats&lt;/li&gt;
&lt;li&gt;FAQ&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why agent observability is a different problem
&lt;/h2&gt;

&lt;p&gt;Traditional application monitoring answers three questions: is it up, is it fast, is it erroring. For a CRUD service that is enough, because the work is deterministic and the failure modes are loud. An AI agent breaks all three assumptions. It decides its own control flow at runtime, it calls tools in an order you did not hardcode, and it pays per token for every reasoning step. A run can be up, fast, and error-free while doing the wrong amount of work: re-reading the same data, dragging bloated context from step to step, looping an extra cycle before it settles. None of that shows up as a 500 or a slow span. It shows up on the bill, and by then it is a trend, not an event.&lt;/p&gt;

&lt;p&gt;So agent observability has to record things classic APM never needed: how many reasoning cycles an agent took, which tools it called versus which it was allowed to call, the token count and dollar cost of each step, and which agent in a multi-agent crew did what. Those attributes are what make an invisible regression visible.&lt;/p&gt;

&lt;p&gt;This is not a fringe opinion. AWS's own Well-Architected &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/agentic-ai-lens/agentcost05.html" rel="noopener noreferrer"&gt;Agentic AI Lens&lt;/a&gt; frames the baseline state (its "Level 1") as exactly this problem: agent costs are visible only at the account level, Cost Explorer cannot separate agents or workflows, and "teams react to billing surprises after the fact because per-agent and per-reasoning-phase attribution is missing." The whole point of what follows is to move off Level 1: to make spending "attributable at the reasoning-cycle, agent, workflow, and tenant level rather than only at the account level," which is AWS's own words for the target.&lt;/p&gt;

&lt;p&gt;A quick vocabulary anchor, since the rest of the article leans on it. A &lt;em&gt;span&lt;/em&gt; is one timed step with attributes attached (one LLM call, one tool call, one AWS read). A &lt;em&gt;trace&lt;/em&gt; is the tree of spans for one unit of work. Classic APM records spans too, but only the loud attributes (status, latency). Agent observability is the same trace structure carrying agent-specific attributes: cycle count, tokens, cost, and which agent owned the step. That is the whole idea; everything below is just putting the right attributes on the right spans.&lt;/p&gt;

&lt;p&gt;At one run, a 1.4x overspend is a rounding error. At enterprise scale it is a budget line and a governance problem, and it shows up in four concrete ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cost control.&lt;/strong&gt; A 1.03x-to-1.4x silent overspend per run (the real range I measured across three waste patterns), multiplied across thousands of daily runs and dozens of agents, is real money leaking with no alarm attached. Per-agent, per-tool cost on the trace is the only way to attribute and cap it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accountability.&lt;/strong&gt; When a crew misbehaves, "which agent, owned by which team, cost what" needs to be answerable. Trace-level ownership metadata turns a vague incident into a routed ticket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regression detection.&lt;/strong&gt; Agents change when prompts, models, or tools change. A known-good baseline plus per-run deltas catches the day a prompt tweak silently doubled token usage, before finance does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auditability.&lt;/strong&gt; In regulated environments you need a record of what the agent read, what it decided, and what it cost. A trace is that record.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The theme throughout: a correct-looking answer is not evidence of a healthy run. The evidence lives in the trace, on attributes you put there on purpose. Here is the before and after in one line. Before, a typical demo gives you one lump token count for the whole run, and an inefficient run looks identical to an efficient one. After, every reasoning step and every AWS read is a span carrying real cost, tokens, cycle count, and the owning agent's identity, so two runs that both return the correct answer and both show 200 OK are no longer indistinguishable when one of them costs 43% more.&lt;/p&gt;




&lt;h2&gt;
  
  
  Picking the instrumentation
&lt;/h2&gt;

&lt;p&gt;Once you know you need per-agent cost, cycle counts, and tool-call attributes on every span, the next question is what to write them with. You could do a lot of this with raw OpenTelemetry, and I nearly did. The reason I did not is that agents need a vocabulary plain OTel does not ship: token counts turned into dollars, a span-level agent identity so one process can render as a real fleet, ownership metadata, and a way to view per-agent cost grouped by session. You end up building all of that yourself, or you find an SDK that already speaks it.&lt;/p&gt;

&lt;p&gt;There are options here: LangSmith, Langfuse, and Arize Phoenix all do LLM tracing, and each is worth a look depending on your stack. I went looking for one I would trust in a codebase, which for me means two hard requirements: I can read the source, and I am not locked in. &lt;a href="https://github.com/traccia-ai/traccia-py" rel="noopener noreferrer"&gt;Traccia&lt;/a&gt; cleared both cleanly. The SDK is &lt;strong&gt;open source, Apache-2.0 licensed&lt;/strong&gt;, and built on OpenTelemetry (OTel, the vendor-neutral open standard for traces and metrics, the reason you are not locked into any one backend). The spans it produces are standard OTel, the file exporter works with no account and no network, and I could read exactly what it does to my data before committing to it (I did, and the source-grounded critique later in this article is the result). It runs at $0 locally; the hosted dashboard at app.traccia.ai is optional and only comes in when you want the visualization. An open, inspectable SDK with an optional commercial backend is a split I am comfortable adopting, because the instrumentation does not trap me.&lt;/p&gt;

&lt;p&gt;That is the real reason it is in this build: agent-native plumbing I did not want to hand-roll, source I could audit, and a real $0 offline path. It also has sharp edges, and I hit several of them; those are documented in full near the end rather than glossed over.&lt;/p&gt;

&lt;p&gt;Why not Amazon Bedrock AgentCore Observability or Langfuse, the two obvious AWS-native alternatives? Both are good, and for many teams either is the right call. AgentCore Observability exports traces to CloudWatch and is the natural fit if your agents run on the AgentCore runtime, but AWS's own Well-Architected lens is blunt about the cost gap: "cost reporting stops at the AWS account level, so teams can't separate supervisor overhead from worker execution." Per-agent dollars are something you still assemble. Langfuse is the strong open-source incumbent and I would happily use it; it just was not the tool I was asked to put through its paces here. The point of this build is not "Traccia beats them." It is that whichever tracer you pick, the per-agent cost attribute and the baseline-delta detection are things you wire on purpose, and this article shows exactly how.&lt;/p&gt;




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

&lt;p&gt;Nothing exotic. Three things to run this yourself:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.10+&lt;/strong&gt; and the SDKs (&lt;code&gt;strands-agents&lt;/code&gt;, &lt;code&gt;strands-agents-tools&lt;/code&gt;, &lt;code&gt;traccia&lt;/code&gt;, &lt;code&gt;boto3&lt;/code&gt;). The repo pins the exact tested versions in &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS credentials&lt;/strong&gt; with read-only permissions for the services the crew reads (Cost Explorer, EC2, CloudWatch, S3, Lambda, IAM, GuardDuty), &lt;strong&gt;plus &lt;code&gt;bedrock:InvokeModel&lt;/code&gt;&lt;/strong&gt; so the agent can actually call the model. The repo ships a ready-to-use policy at &lt;code&gt;iam/read-only-policy.json&lt;/code&gt;; AWS's managed &lt;code&gt;SecurityAudit&lt;/code&gt; + &lt;code&gt;ViewOnlyAccess&lt;/code&gt; cover the reads, but you still add &lt;code&gt;bedrock:InvokeModel&lt;/code&gt; on top of them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Nova Pro&lt;/strong&gt;, which is two separate steps: &lt;strong&gt;(a)&lt;/strong&gt; enable model access once in the Bedrock console (&lt;code&gt;us-east-1&lt;/code&gt;, &lt;code&gt;amazon.nova-pro-v1:0&lt;/code&gt;) under &lt;em&gt;Model access&lt;/em&gt;, and &lt;strong&gt;(b)&lt;/strong&gt; allow &lt;code&gt;bedrock:InvokeModel&lt;/code&gt; in your IAM policy. The console grant is not an IAM permission, so you need both.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No Traccia account is required. With no API key it writes traces to a local file, which is the &lt;strong&gt;$0&lt;/strong&gt; path used throughout this article.&lt;/p&gt;

&lt;p&gt;Getting it running is four commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/simplynadaf/ai-agent-observability-aws.git
&lt;span class="nb"&gt;cd &lt;/span&gt;ai-agent-observability-aws
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create the least-privilege policy once (with your own admin credentials) and attach it to whoever runs the crew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam create-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; AgentObservabilityReadOnly &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://iam/read-only-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Adding Traccia to your code
&lt;/h2&gt;

&lt;p&gt;Before the crew, here is the smallest version of what "wire cost into a span" actually means, because that is the one non-obvious step. Traccia auto-instruments LangChain, CrewAI, and the OpenAI/Anthropic/Gemini clients, so on those stacks you get most of this for free. It does not yet hook Strands or raw Bedrock, so you stamp the cost yourself. It is a short function, and one attribute name will bite you (more on that below).&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stamp_llm_cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;span&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;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amazon.nova-pro-v1: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;usage&lt;/span&gt; &lt;span class="o"&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;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accumulated_usage&lt;/span&gt;
    &lt;span class="n"&gt;in_tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_tok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inputTokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outputTokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in_tok&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.0008&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out_tok&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.0032&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Nova Pro, us-east-1
&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm.model&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_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# REQUIRED. wrong key = silently zero
&lt;/span&gt;    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;span.type&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;LLM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm.usage.prompt_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in_tok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm.usage.completion_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_tok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm.cost.usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the whole idea: read the token usage the SDK already gives you, turn it into dollars against real pricing, and attach it to the span. Everything else in this article is applying this same move across a multi-agent crew and then reading the numbers back. The full wiring (init, the per-agent identity, the tool spans) is in &lt;code&gt;src/crew.py&lt;/code&gt; in the repo.&lt;/p&gt;




&lt;h2&gt;
  
  
  The stack: AWS native and read only
&lt;/h2&gt;

&lt;p&gt;The crew runs on Amazon Nova Pro (&lt;code&gt;amazon.nova-pro-v1:0&lt;/code&gt;) through AWS Strands Agents using the agents-as-tools pattern. A supervisor named &lt;code&gt;investigation_run&lt;/code&gt; delegates to three specialist sub-agents. Each specialist is a real separation of concerns, owns several read-only tools, and every one of those tools opens its own live span, so in the dashboard you see the agent, then each AWS read nested under it with a real duration. Here is the full fleet and exactly what each agent does.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. AWS Account Investigator (supervisor, &lt;code&gt;investigation_run&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The orchestrator. It does not touch AWS directly; it reads the user's question, decides which specialists are in scope, delegates to them, and synthesizes one report. On its trace span it records &lt;code&gt;agent.delegated_to&lt;/code&gt; (which specialists it called this run), and it stamps the shared &lt;code&gt;session.id&lt;/code&gt; that ties the whole investigation together.&lt;/p&gt;

&lt;p&gt;The delegation is intent-routed, not fan-out-everything. The supervisor's instructions are strict: call ONLY the specialist whose domain the user actually asked about. Ask only about cost and it delegates to the Cost Analyst alone, while Health &amp;amp; Ops and the Security Auditor never run. Ask only about security and only the Security Auditor fires. Only a whole-account question ("what's running, any risks, and where is my spend going?") lights up all three. This matters for the trace and the bill: &lt;code&gt;agent.delegated_to&lt;/code&gt; shows exactly which specialists ran, and a scoped question costs a fraction of a full sweep because the agents you did not need never spent a token. You can watch this live in the control panel: a cost-only prompt lights up one agent and leaves the other two idle.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;AWS read-only API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Plan + delegate&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cost_analyst&lt;/code&gt;, &lt;code&gt;health_ops&lt;/code&gt;, &lt;code&gt;security_ops&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;none directly (delegates)&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjym2lsarh9pf9rab64x8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjym2lsarh9pf9rab64x8.png" alt="Traccia trace for the AWS Account Investigator supervisor, showing the agent.delegated_to attribute and the shared session.id that ties the whole investigation together" width="800" height="548"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The supervisor's trace in Traccia: &lt;code&gt;agent.delegated_to&lt;/code&gt; records which specialists ran this run, and the shared &lt;code&gt;session.id&lt;/code&gt; links the four agents into one investigation.&lt;/em&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  2. Cost Analyst (&lt;code&gt;cost_analyst&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;A read-only FinOps specialist. It builds a full spend picture with three tools, and all three show up as separate tool spans in its trace.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;AWS read-only API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Month-to-date total, month-end forecast, top 5 services&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cost_forecast&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ce:GetCostAndUsage&lt;/code&gt;, &lt;code&gt;ce:GetCostForecast&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last full month's total for month-over-month change&lt;/td&gt;
&lt;td&gt;&lt;code&gt;last_month_cost&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ce:GetCostAndUsage&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily cost series to catch a spike&lt;/td&gt;
&lt;td&gt;&lt;code&gt;daily_cost_trend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ce:GetCostAndUsage&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What it reports on a real run: actual MTD spend, the account's forecasted month-end total, the top services by spend, the month-over-month direction and rough percentage, and the single most expensive day compared against the daily average (a possible spike).&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwxo8ograch7j4ucxlsds.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwxo8ograch7j4ucxlsds.png" alt="Traccia trace for the Cost Analyst agent, showing three tool spans (cost_forecast, last_month_cost, daily_cost_trend) nested under the agent, each with a real duration" width="800" height="548"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Cost Analyst trace: three Cost Explorer tool spans nested under the agent, each with its real read duration and the agent's own per-agent cost. This per-agent cost figure is exactly what turns a "successful" run into a caught overspend later.&lt;/em&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  3. Health &amp;amp; Ops (&lt;code&gt;health_ops&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;A read-only SRE specialist. It inventories the account and reads health signals with five tools, so it is usually the heaviest agent on input tokens (it chains the most reads).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;AWS read-only API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;List running EC2 instances&lt;/td&gt;
&lt;td&gt;&lt;code&gt;running_instances&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ec2:DescribeInstances&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read CPU utilization per instance&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cpu_utilization&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cloudwatch:GetMetricStatistics&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find unattached (idle) EBS volumes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;list_volumes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ec2:DescribeVolumes&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inventory Lambda functions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;list_functions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lambda:ListFunctions&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inventory S3 buckets&lt;/td&gt;
&lt;td&gt;&lt;code&gt;list_buckets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s3:ListAllMyBuckets&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What it reports: running instances with their CPU, an inventory of volumes, functions, and buckets, and any notable health finding such as an unattached EBS volume.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx5afwf6sjfej6nks42hy.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx5afwf6sjfej6nks42hy.png" alt="Traccia trace for the Health and Ops agent, showing five tool spans (running_instances, cpu_utilization, list_volumes, list_functions, list_buckets) and the highest input-token count of the fleet" width="800" height="548"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Health &amp;amp; Ops trace: five read-only tool spans and, on this run, the highest token count of the fleet (4,772) because it chains the most reads.&lt;/em&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  4. Security Auditor (&lt;code&gt;security_ops&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;A read-only security specialist. It runs four independent checks, each its own tool span.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;AWS read-only API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Security groups open to the internet (0.0.0.0/0)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;open_security_groups&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ec2:DescribeSecurityGroups&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MFA gaps on the root account and IAM users&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mfa_findings&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;iam:GetAccountSummary&lt;/code&gt;, &lt;code&gt;iam:ListUsers&lt;/code&gt;, &lt;code&gt;iam:ListMFADevices&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 buckets missing a public-access block&lt;/td&gt;
&lt;td&gt;&lt;code&gt;public_s3_buckets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;s3:ListAllMyBuckets&lt;/code&gt;, &lt;code&gt;s3:GetPublicAccessBlock&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whether GuardDuty is enabled&lt;/td&gt;
&lt;td&gt;&lt;code&gt;guardduty_enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;guardduty:ListDetectors&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What it reports: each finding stated plainly with its risk, and it explicitly says so when a check comes back clean.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1p6s237wgmofcdfiw2g8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1p6s237wgmofcdfiw2g8.png" alt="Traccia trace for the Security Auditor agent, showing four tool spans (open_security_groups, mfa_findings, public_s3_buckets, guardduty_enabled) each with a real duration" width="800" height="548"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Security Auditor trace: four independent read-only checks, each its own tool span with a real duration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each specialist stamps its own identity onto its trace span, so from a single crew run the dashboard shows four distinct agents with their own token and cost profiles, not one agent logged four times. Each agent runs as its own top-level trace, tied to the others by a shared &lt;code&gt;session.id&lt;/code&gt;, and carries production ownership (type, owner, team) from a catalog file. Every call is a describe or get. There is no create, no modify, no delete. The worst thing this agent can do is read a bit too much, which, as you will see, is exactly the waste we want to catch.&lt;/p&gt;

&lt;p&gt;Observability comes from Traccia, an OpenTelemetry-native agent-observability SDK.&lt;br&gt;
&lt;/p&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;traccia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Runs $0 by default using a local file exporter. If you set &lt;code&gt;TRACCIA_API_KEY&lt;/code&gt;, it pushes spans to app.traccia.ai. No key, no network, no cost. (The repo pins the exact tested version in &lt;code&gt;requirements.txt&lt;/code&gt;; the prose stays unpinned so it does not age.)&lt;/p&gt;

&lt;p&gt;Nova Pro pricing, pulled live from the AWS Price List API (effective 2026-08-01, us-east-1):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Token type&lt;/th&gt;
&lt;th&gt;Price per 1K&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;$0.0008&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;$0.0032&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every dollar figure below is computed from real token counts against these two numbers.&lt;/p&gt;




&lt;h2&gt;
  
  
  The cost bridge and one gotcha
&lt;/h2&gt;

&lt;p&gt;Here is the part most tutorials skip. Traccia auto-instruments several stacks out of the box (LangChain including BedrockChat, CrewAI, OpenAI Agents, and raw OpenAI/Anthropic/Gemini), and it ships a cost engine with a bundled pricing snapshot that covers Nova and Claude. But there is no Strands integration yet, and it does not hook raw Bedrock &lt;code&gt;converse&lt;/code&gt; calls. So for this specific stack, Strands agents-as-tools calling Bedrock directly, you wire the cost in yourself. That is a fair amount of the value proposition for supported frameworks arriving for free, and real manual work for an unsupported one.&lt;/p&gt;

&lt;p&gt;Strands hands you the token usage after a run. You read it, compute the cost, and stamp it onto the span, which is exactly the &lt;code&gt;stamp_llm_cost&lt;/code&gt; function from earlier. About 40 lines once you handle all four agents and the tool spans; the full version is in &lt;code&gt;src/crew.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The gotcha cost me a confused afternoon, and reading the SDK source explained exactly why. Traccia's cost-annotating processor only computes cost for a span when three things are all true: &lt;code&gt;span.type&lt;/code&gt; is &lt;code&gt;LLM&lt;/code&gt; (or unset), an &lt;code&gt;llm.model&lt;/code&gt; attribute is present, and both token counts are set. Miss any one and the processor simply returns, with no error and no warning. I first set &lt;code&gt;llm.request.model&lt;/code&gt; (which felt more semantically correct) instead of &lt;code&gt;llm.model&lt;/code&gt;, so the processor silently skipped every span, and the "LLM Calls" and "Total Tokens" tiles read zero while my spans clearly had tokens on them. Set &lt;code&gt;llm.model&lt;/code&gt;, and the tiles light up. The forgiving fail is reasonable; the fact that it is invisible is the trap. A one-line debug log ("skipping cost: no llm.model") would have saved the afternoon.&lt;/p&gt;

&lt;h3&gt;
  
  
  No double-counting across agents
&lt;/h3&gt;

&lt;p&gt;The other question that always comes up: if the supervisor calls two sub-agents, and I sum everyone's tokens, am I counting the sub-agent tokens twice?&lt;/p&gt;

&lt;p&gt;I wrote &lt;code&gt;probes/probe_doublecount.py&lt;/code&gt; to check instead of guessing. Strands runs each sub-agent in its own event loop with its own metrics object. A supervisor's &lt;code&gt;accumulated_usage&lt;/code&gt; is exclusive of its sub-agents' tokens. So the arithmetic is clean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;crew total = supervisor + sum(sub-agents)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No subtraction, no overlap, no double-count. Verified, not assumed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Modeling a multi-agent crew in traces
&lt;/h2&gt;

&lt;p&gt;Once the bridge is in, you get per-agent cost. But here is a design decision worth being explicit about, because most demos hand-wave it: how do you model a supervisor and its specialists in a trace?&lt;/p&gt;

&lt;p&gt;You have two reasonable options. You can nest everything under one trace (supervisor is the root, sub-agents are child spans). Or you can give &lt;strong&gt;each agent its own top-level trace&lt;/strong&gt; and tie them together with a shared &lt;code&gt;session.id&lt;/code&gt;. I went with the second, because it is what a real production fleet looks like: the Cost Analyst, Health &amp;amp; Ops, and Security Auditor are independently owned, independently operated services. On the Traces page they show up as their own executions, each with its own cost, tokens, and duration; "Group by session" folds them back into one investigation when you want the whole picture.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;session 4f4c1ade...  (one investigation, four independent traces)

  investigation_run   AWS Account Investigator   $0.006   delegated -&amp;gt; 3
  cost_analyst        Cost Analyst               $0.004
  health_ops          Health &amp;amp; Ops               $0.005   (highest total tokens: 4,772)
  security_ops        Security Auditor           $0.005
                                          crew total   $0.021
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(These are the real per-agent figures from the exported run shown in the trace screenshots above, rounded to the dashboard's own cost tiles; they shift run to run with token usage. The crew total is the &lt;code&gt;investigation_workflow&lt;/code&gt; roll-up span, which equals the supervisor's own synthesis plus the three sub-agents, no double-count. Health &amp;amp; Ops carries the highest token count because it chains the most reads, while the supervisor costs about the same because it writes the long final synthesis. The CLEAN and CONTEXT BLOAT numbers later in the article come from separate, labeled runs, so do not expect them to tie back to this one.)&lt;/p&gt;

&lt;p&gt;Each agent's own trace still nests its tools underneath it (agent -&amp;gt; &lt;code&gt;tool:running_instances&lt;/code&gt; -&amp;gt; the real boto3 call), so you keep the drill-down without pretending four separate services are one call stack. On the Traces page, "Group by session" folds all four agents from one run back into a single investigation, so you can move between the fleet view and the per-agent view without losing either.&lt;/p&gt;

&lt;p&gt;(This is a different run from the CLEAN baseline used later; token counts and therefore dollars shift run to run. The point is the per-agent breakdown, not the absolute number.)&lt;/p&gt;

&lt;p&gt;Good. Useful. Per-agent cost on its own is becoming common. The reason it matters here is not the number itself but the data model underneath it: once every step carries cost, tokens, cycle count, and an owning agent, you can build the thing that is still rare, which is catching a run that overspends while looking perfectly healthy. That is what the primitives let you build next.&lt;/p&gt;

&lt;p&gt;A few things here are easy to get subtly wrong, and I hit them in roughly this order over a couple of days before the trace design held. First, all four agents come from a &lt;em&gt;single&lt;/em&gt; crew run in a &lt;em&gt;single&lt;/em&gt; process. Traccia bakes the agent identity into the OpenTelemetry resource at init, which is process-level, so my first version labeled every trace with one agent name: three identical "AWS Account Investigator" rows in the dashboard. Reading the SDK's enrichment processor showed that a span-level &lt;code&gt;agent.id&lt;/code&gt; / &lt;code&gt;agent.name&lt;/code&gt; attribute takes precedence over the process default, so stamping each agent's span with its own identity makes it show up as its own agent. Static ownership (type, owner, team, org) comes from an &lt;code&gt;agent_config.json&lt;/code&gt; catalog the SDK auto-discovers, so the dashboard shows a real fleet with owners and teams, not four anonymous rows. No extra processes, no fake agents.&lt;/p&gt;

&lt;p&gt;Second, separate traces need a real correlation key or they look disconnected. Every agent stamps the run's &lt;code&gt;session.id&lt;/code&gt;, and the supervisor additionally records &lt;code&gt;agent.delegated_to&lt;/code&gt; (which specialists it called this run). That is the explicit link that makes four independent traces read as one orchestrated investigation.&lt;/p&gt;

&lt;p&gt;Third, the "each agent is its own trace" bit did not happen by wishing, and this one cost me a rebuild. Traccia's &lt;code&gt;span_scope(parent=None)&lt;/code&gt; still inherits the &lt;em&gt;current&lt;/em&gt; span if one is active, so my agents silently collapsed back into one trace until I detached the OpenTelemetry context before starting each agent's span. One small helper, verified by counting distinct trace IDs in the exported spans.&lt;/p&gt;

&lt;p&gt;Fourth, the first time I looked at the timeline every tool span was 0ms, because I was reconstructing tool spans after the fact from the metrics object. The fix was to wrap the real boto3 call in a live span while it runs, so the timeline shows each AWS read's true duration. A 0ms bar is the kind of thing that makes a viewer distrust the whole trace, and it is worth chasing down. Then a subtler follow-on bit me: those live tool spans inherited the process-level default identity, so every tool bucketed under the supervisor and the specialists looked trace-thin. I had to stamp each tool span with its calling agent's identity too. Nothing about that was in the docs; I found it by parsing the exported &lt;code&gt;traces.jsonl&lt;/code&gt; and noticing the &lt;code&gt;agent.id&lt;/code&gt; was wrong.&lt;/p&gt;

&lt;p&gt;One more touch that reads as production, not demo: each agent records both &lt;code&gt;agent.tools_available&lt;/code&gt; (the full toolset it was granted) and &lt;code&gt;agent.tools_called&lt;/code&gt; (what the model used this run). On this run, Cost Analyst had two tools available (&lt;code&gt;month_to_date_cost&lt;/code&gt; and &lt;code&gt;cost_forecast&lt;/code&gt;) and used one; Health &amp;amp; Ops had five and used all five. That gap is not a bug to hide, it is real information. "Has two, used one" is exactly the kind of thing you want visible when you are deciding whether an agent is over-provisioned.&lt;/p&gt;




&lt;h2&gt;
  
  
  The unique part: catching silent waste
&lt;/h2&gt;

&lt;p&gt;Here is my disclaimer up front. I saw versions of all three of these in real runs while building the crew, then engineered them in &lt;code&gt;src/waste_demo.py&lt;/code&gt; to trigger reliably so you can watch them on demand instead of waiting for a bad run. That reproduction is on purpose. LLM output is non-deterministic, so in production the same patterns show up on their own, just not on a schedule you can demo. And critically: detection here is delta-vs-baseline, not magic absolute thresholds. That is how real regression detection works. You capture a known-good run, then flag runs that deviate. Every number below is real Nova Pro token usage from a representative run. Your numbers will vary; the ratios are what hold.&lt;/p&gt;

&lt;p&gt;First, the clean baseline. This is the "known good" I compare everything against.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLEAN baseline (one exported run): crew total ~ $0.0083
  health_ops: 2,274 input / 199 output / 3 cycles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scenario 1: The runaway loop
&lt;/h3&gt;

&lt;p&gt;The agent gets stuck re-reasoning and re-reading the same things.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUNAWAY LOOP: ~1.2x baseline ($0.0100 vs $0.0083)
  health_ops ran 4 cycles (baseline: 3)
  re-read cpu_utilization twice (baseline: once)
  health_ops input tokens ~1.6x (3,557 vs 2,274)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same final answer. The APM span is 200 OK. What catches it: &lt;code&gt;agent.cycle_count&lt;/code&gt; and &lt;code&gt;tool.call_count&lt;/code&gt;. The agent looped more than its baseline and called the same tool repeatedly. No single number is "wrong." The delta is wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: Redundant tool calls
&lt;/h3&gt;

&lt;p&gt;Milder, sneakier. The agent calls a tool it already has the answer for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REDUNDANT TOOL CALLS: ~1.03x baseline ($0.0085 vs $0.0083)
  running_instances called 3x (baseline: 1x)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few percent on one run is the kind of thing you never notice. Multiply it across thousands of daily runs and it is a line item. The signal: &lt;code&gt;tool.call_count&lt;/code&gt; for &lt;code&gt;running_instances&lt;/code&gt; jumped from 1 to 3. Only visible per-tool, per-agent, and easy to miss precisely because the dollar delta is so small on a single run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: Context bloat (the expensive one)
&lt;/h3&gt;

&lt;p&gt;The agent drags too much context into its prompts. Every extra token in gets paid for, and it cascades.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONTEXT BLOAT: ~1.4x baseline ($0.0119 vs $0.0083)
  health_ops input tokens elevated, output nearly 4x (803 vs 199)
  supervisor synthesis cost rises too, bloat cascades
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the meanest one because it compounds. The sub-agent's bloat feeds a bigger blob to the supervisor, whose own synthesis cost then rises too (on this run the supervisor jumped from $0.0032 to $0.0044). The signal: &lt;code&gt;llm.usage.prompt_tokens&lt;/code&gt; and &lt;code&gt;llm.cost.usd&lt;/code&gt; per agent. You watch prompt tokens creep up where the work did not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Same answer, different bill
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;src/compare.py&lt;/code&gt; puts CLEAN next to BLOAT side by side.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;CLEAN&lt;/th&gt;
&lt;th&gt;CONTEXT BLOAT&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Final answer&lt;/td&gt;
&lt;td&gt;Correct&lt;/td&gt;
&lt;td&gt;Correct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crew total cost&lt;/td&gt;
&lt;td&gt;$0.0083&lt;/td&gt;
&lt;td&gt;$0.0119&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;+43% (~1.4x)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;APM status&lt;/td&gt;
&lt;td&gt;200 OK&lt;/td&gt;
&lt;td&gt;200 OK&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two runs. Both return the right answer. Both are green in any latency-and-errors dashboard. The only place the extra 43% shows up is in the trace, on the per-agent cost attribute you stamped yourself. In the Traccia dashboard this is the moment the tool earns its place: two runs sit side by side, both "successful," and the per-agent cost column is where the bloated one gives itself away. That is the whole argument for agent-native observability in one table.&lt;/p&gt;

&lt;p&gt;The detection logic is not clever. It is a delta check: for each agent, compare this run against the baseline and flag three things. More cycles than baseline means a possible runaway loop. Prompt tokens more than 1.25x baseline means possible context bloat. Any tool called more times than baseline means a possible redundant call. That is the whole detector, about fifteen lines in &lt;code&gt;src/compare.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The intelligence is in having the baseline and the per-agent attributes to compare against. The trace is what makes those attributes exist.&lt;/p&gt;

&lt;p&gt;This detector broke once during the build, and it is a good example of how instrumentation and detection are coupled. When I switched tools to emit one live span per call (the 0ms fix above), the redundant-call check stopped working, because it had been reading a &lt;code&gt;call_count&lt;/code&gt; attribute off a single reconstructed span that no longer existed. I had to change it to count span occurrences per tool name instead. The lesson that stuck: change how you record, and you can silently break how you detect. The baseline caught it, which is the whole point.&lt;/p&gt;




&lt;h2&gt;
  
  
  Watching it happen: the live control panel
&lt;/h2&gt;

&lt;p&gt;This is the panel you saw in the video above. Traces are the source of truth, but a wall of span JSON is not how you show a crew to a teammate. So the repo ships a small live control panel: a single-page UI that runs the real crew and animates the investigation as it happens.&lt;/p&gt;

&lt;p&gt;You type a prompt into a command console, hit Investigate, and the view scrolls down to a graph of the crew. The supervisor sits at the top and the three specialists fan out below it, connected by wires. As the run streams, each agent lights up like a traffic signal: idle, then running (with a live activity line, "Reading Cost Explorer", "Scanning security groups"), then done, and the report reveals at the bottom. Every agent card shows the AWS services it touches as small chips, so a viewer can see at a glance that Cost Analyst reads Cost Explorer and the forecast, Health &amp;amp; Ops reads EC2/EBS/Lambda/S3/CloudWatch, and Security Auditor checks security groups, IAM, S3, and GuardDuty.&lt;/p&gt;

&lt;p&gt;The panel has two modes. &lt;strong&gt;Live&lt;/strong&gt; runs the real crew: real Nova Pro calls, real read-only AWS reads, real dollars on the trace, about thirteen seconds. &lt;strong&gt;Replay&lt;/strong&gt; animates a saved run from a committed trace file, deterministically and for free, so you can rehearse the visual as many times as you want without spending a token. Both drive the exact same UI from the same event stream; the only difference is whether the events come from a fresh Bedrock run or a recorded one.&lt;/p&gt;

&lt;p&gt;The backend is a small FastAPI app that streams the crew's lifecycle as Server-Sent Events. The important part is that the UI is a thin viewer over the same telemetry the trace records; it is not a second, hand-maintained source of truth. What the graph shows is what the crew did.&lt;/p&gt;




&lt;h2&gt;
  
  
  Build your own, at zero cost and read only
&lt;/h2&gt;

&lt;p&gt;You do not need a paid plan or a live AWS bill to try this. The whole thing runs locally with the file exporter and read-only AWS credentials.&lt;/p&gt;

&lt;p&gt;The permission surface is deliberately small: every action is a &lt;code&gt;Get&lt;/code&gt;, &lt;code&gt;List&lt;/code&gt;, or &lt;code&gt;Describe&lt;/code&gt;, across Cost Explorer, EC2, CloudWatch, S3, Lambda, IAM, and GuardDuty. There is no create, no modify, no delete anywhere in the toolset. The full policy JSON is in the repo README; if you would rather not hand-roll it, AWS's managed &lt;code&gt;SecurityAudit&lt;/code&gt; and &lt;code&gt;ViewOnlyAccess&lt;/code&gt; policies cover the same set. Attach it, invoke Nova Pro through Strands, and you have a crew that can look but never touch. To send traces to the hosted dashboard, set &lt;code&gt;TRACCIA_API_KEY&lt;/code&gt;; leave it unset and everything writes to a local file. Same spans either way.&lt;/p&gt;

&lt;p&gt;The read-only shape is the same for every tool: wrap the real boto3 &lt;code&gt;describe&lt;/code&gt;/&lt;code&gt;get&lt;/code&gt;/&lt;code&gt;list&lt;/code&gt; call in a live span so its duration in the trace is the true AWS read time, return the fields you need, touch nothing. &lt;code&gt;src/tools.py&lt;/code&gt; in the repo has all seven; they are all this shape.&lt;/p&gt;




&lt;h2&gt;
  
  
  An honest take on Traccia
&lt;/h2&gt;

&lt;p&gt;I shipped a real crew against this SDK and read its source to understand the behavior, so here is the assessment grounded in that, not in the marketing page.&lt;/p&gt;

&lt;p&gt;What is genuinely good:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It is OpenTelemetry-native.&lt;/strong&gt; Spans, processors, and resource attributes are standard OTel underneath, so the data model is not proprietary and you are not locked in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It runs at $0 and offline by default.&lt;/strong&gt; With no API key it writes to a local file exporter; set &lt;code&gt;TRACCIA_API_KEY&lt;/code&gt; and the same spans push to the hosted dashboard. Same spans either way, which made local development and CI painless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It ships more than a tracer.&lt;/strong&gt; There is a real cost engine with a bundled pricing snapshot (covering Nova and Claude, among others), a staleness warning when that snapshot ages, and auto-instrumentation for LangChain, CrewAI, OpenAI Agents, and the raw OpenAI/Anthropic/Gemini clients. If you are on one of those stacks, a lot of what I wired by hand would have come for free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The span-level agent identity model is the best part.&lt;/strong&gt; A span-level &lt;code&gt;agent.id&lt;/code&gt; and &lt;code&gt;agent.name&lt;/code&gt; override the process default, which is precisely what let a single-process crew render as a four-agent fleet with real per-agent cost. That is a thoughtful design decision, not an accident.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where it made me work, and where it could be better:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Strands integration yet, and it does not hook raw Bedrock.&lt;/strong&gt; For this stack the cost bridge was manual. That is fine and it gives you control, but a Strands integration would remove the single biggest chunk of setup for AWS-native builders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The cost processor fails silently (at the time of writing).&lt;/strong&gt; It skips a span with no error if &lt;code&gt;llm.model&lt;/code&gt; is missing or &lt;code&gt;span.type&lt;/code&gt; is not &lt;code&gt;LLM&lt;/code&gt;. That forgiving behavior is defensible, but the silence cost me an afternoon of a zeroed dashboard. A debug log on skip would fix it outright, and it is the kind of small papercut an early-stage tool usually closes fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A couple of sharp edges are only discoverable in the source (at the time of writing).&lt;/strong&gt; &lt;code&gt;span_scope(parent=None)&lt;/code&gt; still inherits the current context (so separate agent traces silently merge unless you detach first), and &lt;code&gt;span_scope&lt;/code&gt; is not a context manager (you call &lt;code&gt;.end()&lt;/code&gt; yourself). Neither is obvious from the docs today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation is the real gap.&lt;/strong&gt; I learned the identity precedence, the &lt;code&gt;llm.model&lt;/code&gt; requirement, and the context-detach behavior by reading the SDK, not the docs. For a bootstrapped, early-version product that is understandable, and the SDK itself is readable enough that this was possible. But better docs would turn a day of spelunking into an hour.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Net: for supported frameworks you get a lot for free, and even off the beaten path the OTel foundation and the cost/identity model are solid. The capability is there; the polish that is missing is mostly documentation and a few developer-experience papercuts, which is exactly what you would expect from a product at this stage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Honest caveats
&lt;/h2&gt;

&lt;p&gt;I want to be straight about the limits, because that is the whole point of this article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I saw versions of the three waste scenarios in real runs first; the &lt;code&gt;src/waste_demo.py&lt;/code&gt; versions just make them fire on cue. LLM output is non-deterministic. In real life these patterns appear on their own, just not on a schedule you can demo.&lt;/li&gt;
&lt;li&gt;Detection is delta-vs-baseline, not fixed magic thresholds. You need a known-good run to compare against, same as any regression system.&lt;/li&gt;
&lt;li&gt;Every dollar is real Nova Pro token usage against verified us-east-1 pricing, but the exact numbers shift run to run. Do not treat any single figure as a constant. Treat the relationship (roughly 1.4x on the worst pattern I measured) as the lesson, not the exact decimals.&lt;/li&gt;
&lt;li&gt;Traccia does not auto-instrument Strands or Bedrock. The cost bridge is about 40 lines you write and own. That is a feature: you control exactly what goes on the span.&lt;/li&gt;
&lt;li&gt;I model each agent as its own trace, grouped by &lt;code&gt;session.id&lt;/code&gt;. That is a deliberate choice to match how a real fleet is owned and operated. If you prefer one nested trace per run, keep the supervisor as the parent instead of detaching the context. Both are valid; pick the one that matches how your team reasons about the system.&lt;/li&gt;
&lt;li&gt;The agent is read-only by IAM policy, not by hope.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The takeaway is not "buy an observability tool." It is that a correct-looking answer tells you nothing about whether the run was efficient, and the only place the truth lives is in the trace, on attributes you have to put there on purpose.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is AI agent observability, and how is it different from LLM monitoring?&lt;/strong&gt;&lt;br&gt;
LLM monitoring usually watches one model call: latency, errors, maybe token count. Agent observability watches a whole reasoning session: how many cycles an agent took, which tools it called, the cost of each step, and, in a multi-agent crew, which agent did what. Agent failures show up across a multi-step chain, not on a single call, so you need the full trace to see them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I track per-agent cost on Amazon Bedrock?&lt;/strong&gt;&lt;br&gt;
Bedrock returns token usage after each call. You multiply input and output tokens by the model's per-1K price (for Nova Pro in us-east-1, $0.0008 in and $0.0032 out) and attach that dollar figure to the trace span for the agent that made the call. That is the &lt;code&gt;stamp_llm_cost&lt;/code&gt; function in this article. AWS's own tag-based cost allocation in Cost Explorer works at the account and tag level; per-agent, per-reasoning-cycle attribution is what the trace adds on top.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can AWS Cost Explorer show per-agent cost by itself?&lt;/strong&gt;&lt;br&gt;
Not on its own. Per AWS's Well-Architected Agentic AI Lens, the default state is that costs are visible only at the account level and Cost Explorer cannot separate agents or workflows. Tag-based allocation plus AgentCore Observability improves this, but per-agent and per-reasoning-phase attribution comes from instrumenting the trace, which is what this build does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does my multi-agent app cost more than I expected even when it works?&lt;/strong&gt;&lt;br&gt;
Because a correct answer is not a cheap answer. Agents can loop an extra reasoning cycle, re-call a tool they already have the answer for, or drag bloated context from step to step. None of that returns an error; it just adds tokens. The overspend shows up on the bill, not in a latency-and-errors dashboard, which is the "silent waste" this article is about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need a paid tool or an AWS account to try this?&lt;/strong&gt;&lt;br&gt;
No. The whole build runs at $0 locally: the Traccia SDK writes traces to a local file with no API key, and the AWS reads use read-only credentials (or the committed replay run, which needs no AWS access at all). You only need a Bedrock model grant if you want to run the live crew against your own account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Traccia open source?&lt;/strong&gt;&lt;br&gt;
The SDK (&lt;a href="https://github.com/traccia-ai/traccia-py" rel="noopener noreferrer"&gt;traccia-py&lt;/a&gt;) is open source under Apache-2.0 and built on OpenTelemetry, so the spans are standard OTel and you are not locked in. The hosted dashboard at &lt;a href="https://traccia.ai" rel="noopener noreferrer"&gt;traccia.ai&lt;/a&gt; is the optional commercial part; you only reach for it when you want the visualization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Traccia support AWS Strands Agents out of the box?&lt;/strong&gt;&lt;br&gt;
Not at the time of writing. It auto-instruments LangChain, CrewAI, and the OpenAI/Anthropic/Gemini clients, but not Strands or raw Bedrock &lt;code&gt;converse&lt;/code&gt;, so on this stack you stamp cost onto the span yourself (about 40 lines). On a supported framework, most of that is automatic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The full code (crew, tools, waste demos, the live control panel, the compare view, and the double-count probe) is on GitHub: &lt;strong&gt;&lt;a href="https://github.com/simplynadaf/ai-agent-observability-aws" rel="noopener noreferrer"&gt;ai-agent-observability-aws&lt;/a&gt;&lt;/strong&gt;. There is also a live replay of a run you can click through in the browser: &lt;strong&gt;&lt;a href="https://simplynadaf.github.io/ai-agent-observability-aws/" rel="noopener noreferrer"&gt;https://simplynadaf.github.io/ai-agent-observability-aws/&lt;/a&gt;&lt;/strong&gt;. Clone it, run &lt;code&gt;python -m src.waste_demo&lt;/code&gt; with local export, and watch a perfect answer cost you ~1.4x. Then go instrument your own agents before your bill does the teaching for you.&lt;/p&gt;

&lt;p&gt;To put Traccia under your own agents, the on-ramp is deliberately short and free:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install the SDK&lt;/strong&gt; (open source, Apache-2.0): &lt;code&gt;pip install traccia&lt;/code&gt;. With no API key it writes traces to a local file, so you can see spans at $0 before you sign up for anything. Source and docs: &lt;strong&gt;&lt;a href="https://github.com/traccia-ai/traccia-py" rel="noopener noreferrer"&gt;github.com/traccia-ai/traccia-py&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stamp cost onto your spans&lt;/strong&gt; using the ~40-line &lt;code&gt;stamp_llm_cost&lt;/code&gt; pattern above (or get it for free if you are on LangChain, CrewAI, or the OpenAI/Anthropic/Gemini clients, which Traccia auto-instruments).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;See it in the dashboard&lt;/strong&gt; when you want the visual per-agent cost and the side-by-side run compare: set &lt;code&gt;TRACCIA_API_KEY&lt;/code&gt; and the same spans push to &lt;strong&gt;&lt;a href="https://traccia.ai" rel="noopener noreferrer"&gt;traccia.ai&lt;/a&gt;&lt;/strong&gt;. Same spans either way, so nothing about your instrumentation changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you build something with it, tell me what silent waste you found. That is the interesting part.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more on AWS architecture, DevOps, and AI Infrastructure:&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Portfolio&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://dev.to/sarvar_04"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://www.youtube.com/@sarvar-nadaf" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="mailto:simplynadaf@gmail.com"&gt;Email&lt;/a&gt; | &lt;a href="https://builder.aws.com/community/@sarvar" rel="noopener noreferrer"&gt;AWS Builder Center&lt;/a&gt; | &lt;a href="https://x.com/SarvarN_04" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>observability</category>
      <category>ai</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Built an AI Agent That Audits AWS (And It Can't Touch Anything)</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Fri, 18 Sep 2026 15:12:34 +0000</pubDate>
      <link>https://dev.to/aws-builders/i-built-an-ai-agent-that-audits-aws-and-it-cant-touch-anything-4nip</link>
      <guid>https://dev.to/aws-builders/i-built-an-ai-agent-that-audits-aws-and-it-cant-touch-anything-4nip</guid>
      <description>&lt;p&gt;An AI agent just read my AWS account and told me a bucket was open to the internet, SSH was exposed to &lt;code&gt;0.0.0.0/0&lt;/code&gt;, GuardDuty was off, and I was burning $3.65 a month on an Elastic IP attached to nothing.&lt;/p&gt;

&lt;p&gt;It did all of that in about two minutes. And here is the part that counts: it physically could not have changed anything even if it tried.&lt;/p&gt;

&lt;p&gt;That last sentence is the whole point of this build. Most "give the AI access to my cloud" ideas die on one fear: what if it deletes something, or a bad prompt tricks it into running a destructive command? We remove that fear at the permission layer, not with a polite instruction. The agent runs on a read-only IAM identity. Every write call it could imagine gets rejected by AWS before it happens.&lt;/p&gt;

&lt;p&gt;This is a walkthrough of building that agent from scratch. It is one JSON file and one Markdown checklist. By the end you will have a working AWS auditor you can point at your own account, and you will understand every field that makes it work.&lt;/p&gt;

&lt;p&gt;The full code is on GitHub: &lt;a href="https://github.com/simplynadaf/aws-auditor-agent" rel="noopener noreferrer"&gt;github.com/simplynadaf/aws-auditor-agent&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;p&gt;You use Kiro Crew or the Amazon Q Developer CLI. You know your way around AWS enough to have an account with a few things running. You have heard "AI agent" a hundred times and you want to see what one is built from, without a framework, without a vector database, without 400 lines of Python.&lt;/p&gt;

&lt;p&gt;If you can edit a JSON file and write a checklist in Markdown, you can build this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What an agent is made of (the 6 pieces)&lt;/li&gt;
&lt;li&gt;The safety foundation: read-only IAM&lt;/li&gt;
&lt;li&gt;Writing the agent config&lt;/li&gt;
&lt;li&gt;The skill: your audit checklist&lt;/li&gt;
&lt;li&gt;Wiring the AWS tools with MCP&lt;/li&gt;
&lt;li&gt;Running it, and what it produced&lt;/li&gt;
&lt;li&gt;Making it yours&lt;/li&gt;
&lt;li&gt;Why build this when Prowler and Trusted Advisor exist?&lt;/li&gt;
&lt;li&gt;What the docs do not tell you&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. What an agent is made of
&lt;/h2&gt;

&lt;p&gt;Strip away the hype and an agent is one JSON file. The filename minus &lt;code&gt;.json&lt;/code&gt; is the agent's name. The file describes a chat session: which model to use, what tools it can call, what it is allowed to do without asking you, what extra powers it plugs in, and what knowledge it carries.&lt;/p&gt;

&lt;p&gt;Six pieces. That is the entire mental model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Plain meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Identity&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;What it is called&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The brain&lt;/td&gt;
&lt;td&gt;&lt;code&gt;model&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which LLM answers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instructions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;prompt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Its personality and rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What it can do&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tools&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The toolbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What runs without asking&lt;/td&gt;
&lt;td&gt;&lt;code&gt;allowedTools&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pre-signed permission slips&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra powers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mcpServers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plug in tool servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Its knowledge&lt;/td&gt;
&lt;td&gt;&lt;code&gt;resources&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Attach skills and files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The one distinction that trips up every beginner is &lt;code&gt;tools&lt;/code&gt; versus &lt;code&gt;allowedTools&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tools&lt;/code&gt; answers "what CAN this agent use?" If a tool is not listed, it does not exist for the agent.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;allowedTools&lt;/code&gt; answers "what runs WITHOUT stopping to ask me?" A tool that is in &lt;code&gt;tools&lt;/code&gt; but not in &lt;code&gt;allowedTools&lt;/code&gt; still works, it just prompts you for approval each time it fires.&lt;/p&gt;

&lt;p&gt;Toolbox versus permission slips. Keep that image and the rest is easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The safety foundation: read-only IAM
&lt;/h2&gt;

&lt;p&gt;Before any config, we build the guardrail. This step is not optional and it is the reason the whole thing is trustworthy.&lt;/p&gt;

&lt;p&gt;We attach two AWS-managed policies to the identity the agent uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arn:aws:iam::aws:policy/SecurityAudit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;arn:aws:iam::aws:policy/job-function/ViewOnlyAccess&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;SecurityAudit&lt;/code&gt; is the policy AWS designed for exactly this job: reading security-relevant configuration across services. &lt;code&gt;ViewOnlyAccess&lt;/code&gt; fills the cost gaps, so the agent can see Elastic IPs, volumes, snapshots, and load balancers.&lt;/p&gt;

&lt;p&gt;Underneath, both policies are &lt;code&gt;Get*&lt;/code&gt;, &lt;code&gt;List*&lt;/code&gt;, and &lt;code&gt;Describe*&lt;/code&gt; only. There is no &lt;code&gt;Create&lt;/code&gt;, no &lt;code&gt;Delete&lt;/code&gt;, no &lt;code&gt;Put&lt;/code&gt;, no &lt;code&gt;Modify&lt;/code&gt; anywhere in them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam create-user &lt;span class="nt"&gt;--user-name&lt;/span&gt; aws-auditor

aws iam attach-user-policy &lt;span class="nt"&gt;--user-name&lt;/span&gt; aws-auditor &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-arn&lt;/span&gt; arn:aws:iam::aws:policy/SecurityAudit

aws iam attach-user-policy &lt;span class="nt"&gt;--user-name&lt;/span&gt; aws-auditor &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-arn&lt;/span&gt; arn:aws:iam::aws:policy/job-function/ViewOnlyAccess

aws iam create-access-key &lt;span class="nt"&gt;--user-name&lt;/span&gt; aws-auditor
&lt;span class="c"&gt;# then paste the keys into: aws configure --profile aws-auditor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why go through this instead of just telling the model "please do not change anything"?&lt;/p&gt;

&lt;p&gt;Because a prompt is a suggestion and IAM is a wall. If the model hallucinates a fix, IAM blocks it. If someone slips a "now delete that bucket" instruction into a file the agent reads, IAM blocks it. The blast radius is zero by construction. You are separating the act of detecting problems from the act of fixing them, which is a security best practice on its own.&lt;/p&gt;

&lt;p&gt;The agent has read-only glasses, not a wrench.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Writing the agent config
&lt;/h2&gt;

&lt;p&gt;Here is the complete file. Save it as &lt;code&gt;~/.kiro/agents/aws-auditor.json&lt;/code&gt;.&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;"$schema"&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://raw.githubusercontent.com/aws/amazon-q-developer-cli/refs/heads/main/schemas/agent-v1.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aws-auditor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read-only agent that audits an AWS account for security risks and wasted spend, and reports prioritized, cited findings. Never changes anything."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"auto"&lt;/span&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;"security"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"awslabs.well-architected-security-mcp-server@latest"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"AWS_PROFILE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"AWS_REGION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"FASTMCP_LOG_LEVEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ERROR"&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="nl"&gt;"cloudtrail"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"awslabs.cloudtrail-mcp-server@latest"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"AWS_PROFILE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"AWS_REGION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"FASTMCP_LOG_LEVEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ERROR"&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="nl"&gt;"pricing"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"awslabs.aws-pricing-mcp-server@latest"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"AWS_PROFILE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"AWS_REGION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"FASTMCP_LOG_LEVEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ERROR"&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="nl"&gt;"awsdocs"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"awslabs.aws-documentation-mcp-server@latest"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"FASTMCP_LOG_LEVEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ERROR"&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;span class="nl"&gt;"tools"&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="s2"&gt;"fs_read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"use_aws"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@security"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@cloudtrail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@pricing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@awsdocs"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allowedTools"&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="s2"&gt;"fs_read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"use_aws"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@security"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@cloudtrail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@pricing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@awsdocs"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"toolsSettings"&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;"use_aws"&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;"allowedServices"&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="s2"&gt;"iam"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ec2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rds"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"guardduty"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accessanalyzer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"securityhub"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"elbv2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"elasticloadbalancing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cloudwatch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts"&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;span class="nl"&gt;"resources"&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="s2"&gt;"skill://~/.kiro/skills/aws-audit/SKILL.md"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are AWS Auditor, a read-only cloud security and cost reviewer. You have ONLY read permissions (SecurityAudit + ViewOnlyAccess). You cannot and must not attempt to change anything, and you must not recommend that you run a fix yourself. Run the checks defined in your aws-audit skill using the available tools. Cite ONLY real resources you actually observed in tool output (real IDs, real names). NEVER invent a finding, a resource, or a number. If a check could not run, put it in the Gaps section as 'not verified' rather than implying it passed. Produce the report exactly in the format your skill defines. For every cost finding include an estimated monthly dollar impact computed from real pricing data, and state the assumption you used. Recommend fixes; never run them."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"welcomeMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS Auditor here (read-only). Point me at a region and I will report what is risky and what is wasteful, with real evidence. I cannot change anything."&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;p&gt;Walk through the fields that carry weight.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;model&lt;/code&gt; is set to &lt;code&gt;auto&lt;/code&gt;, which lets the CLI pick the model. You can pin one (run &lt;code&gt;/model&lt;/code&gt; in a session to see valid IDs) but &lt;code&gt;auto&lt;/code&gt; is the sensible default.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;prompt&lt;/code&gt; is where the honesty rules live. Read it closely. "Cite ONLY real resources you actually observed." "NEVER invent a finding, a resource, or a number." "If a check could not run, put it in the Gaps section as not verified rather than implying it passed." Those three lines are what separate a useful audit from a confident-sounding hallucination.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;toolsSettings.use_aws.allowedServices&lt;/code&gt; is a second wall on top of IAM. Even though IAM already blocks writes, this restricts the &lt;code&gt;use_aws&lt;/code&gt; tool to a specific list of services it can even attempt to call. Two independent limits: the tool can only reach these services, and IAM only permits reads inside them. Defense in depth.&lt;/p&gt;

&lt;p&gt;Notice &lt;code&gt;tools&lt;/code&gt; and &lt;code&gt;allowedTools&lt;/code&gt; are identical here. That means the agent runs the audit end to end without stopping to ask permission for each read. That is a deliberate trade-off: convenience for a demo, and it is safe precisely because every one of those tools is read-only. If you were doing anything with write access, you would keep the risky tools out of &lt;code&gt;allowedTools&lt;/code&gt; so they prompt you.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The skill: your audit checklist
&lt;/h2&gt;

&lt;p&gt;The agent config is the wiring. The skill is the brain of the audit. It is a plain Markdown file at &lt;code&gt;~/.kiro/skills/aws-audit/SKILL.md&lt;/code&gt;, attached through the &lt;code&gt;resources&lt;/code&gt; field, and the agent reads it on every run.&lt;/p&gt;

&lt;p&gt;This is the file you will edit most. It holds the checks, the severity model, the framework mapping, and the exact output format.&lt;/p&gt;

&lt;p&gt;The security checks, each backed by a specific read-only API:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Read-only API&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Public S3 bucket&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;s3:GetPublicAccessBlock&lt;/code&gt;, &lt;code&gt;s3:GetBucketPolicyStatus&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;CRITICAL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root account MFA off&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iam:GetAccountSummary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CRITICAL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security group open to 0.0.0.0/0 on 22 / 3389 / DB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ec2:DescribeSecurityGroups&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HIGH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unencrypted EBS or RDS&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ec2:DescribeVolumes&lt;/code&gt;, &lt;code&gt;rds:DescribeDBInstances&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;HIGH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM users without MFA&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;iam:ListUsers&lt;/code&gt;, &lt;code&gt;iam:ListMFADevices&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;HIGH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GuardDuty disabled&lt;/td&gt;
&lt;td&gt;&lt;code&gt;guardduty:ListDetectors&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HIGH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access Analyzer disabled&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accessanalyzer:ListAnalyzers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MEDIUM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security Hub standards incomplete&lt;/td&gt;
&lt;td&gt;&lt;code&gt;securityhub:GetEnabledStandards&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MEDIUM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The cost checks, each of which must carry a real dollar figure:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Read-only API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unattached EBS volume&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ec2:DescribeVolumes&lt;/code&gt; (state = available)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unassociated Elastic IP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ec2:DescribeAddresses&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old or orphaned snapshot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ec2:DescribeSnapshots&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gp2 volume that should be gp3&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ec2:DescribeVolumes&lt;/code&gt; (VolumeType = gp2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idle load balancer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;elbv2:DescribeLoadBalancers&lt;/code&gt; plus target health&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The skill tells the agent how to think about severity (a traffic-light model), which frameworks to cite (Well-Architected Security Pillar for structure, CIS AWS Foundations Benchmark for authority, Trusted Advisor for the cost category), and the precise report shape: executive summary, a Top 3 list, findings grouped by Security and Cost, passing checks, and an honest gaps section.&lt;/p&gt;

&lt;p&gt;One design choice worth calling out. The skill ships with a small "demo scope" block that tells the agent to only report resources tagged &lt;code&gt;demo-auditor=true&lt;/code&gt;. That keeps a demo repeatable and stops it from surfacing anything real. To audit your whole account, you delete that one block. That is the entire difference between "show me the demo" and "audit everything."&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Wiring the AWS tools with MCP
&lt;/h2&gt;

&lt;p&gt;The agent needs eyes. MCP (Model Context Protocol) servers are how it sees AWS. Each server in the &lt;code&gt;mcpServers&lt;/code&gt; block is a small program that exposes a set of tools, and you reference all of a server's tools with an &lt;code&gt;@&lt;/code&gt; prefix, like &lt;code&gt;@pricing&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This build uses four:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@security&lt;/code&gt; (Well-Architected security MCP): checks GuardDuty, Security Hub, Access Analyzer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@cloudtrail&lt;/code&gt;: queries account activity.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@pricing&lt;/code&gt;: pulls live rates from the AWS Price List API, which is how cost findings get real numbers instead of guesses.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@awsdocs&lt;/code&gt;: reads AWS documentation when the agent needs to confirm a detail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They run through &lt;code&gt;uvx&lt;/code&gt;, so you need &lt;code&gt;uv&lt;/code&gt; installed (&lt;code&gt;pip install uv&lt;/code&gt;). The first time you launch the agent, &lt;code&gt;uvx&lt;/code&gt; fetches each server. No manual install step, no Docker.&lt;/p&gt;

&lt;p&gt;The general-purpose &lt;code&gt;use_aws&lt;/code&gt; tool covers everything else with direct read-only API calls. Between &lt;code&gt;use_aws&lt;/code&gt; and the four MCP servers, the agent can reach every check in the skill.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Running it, and what it produced
&lt;/h2&gt;

&lt;p&gt;Install the two files, then start the agent:&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.kiro/skills/aws-audit ~/.kiro/agents
&lt;span class="nb"&gt;cp &lt;/span&gt;skill/SKILL.md ~/.kiro/skills/aws-audit/SKILL.md
&lt;span class="nb"&gt;cp &lt;/span&gt;agent/aws-auditor.json ~/.kiro/agents/aws-auditor.json

kirocrew chat &lt;span class="nt"&gt;--agent&lt;/span&gt; aws-auditor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the Amazon Q Developer CLI the command is &lt;code&gt;q chat --agent aws-auditor&lt;/code&gt;, and you put the agent in &lt;code&gt;~/.aws/amazonq/cli-agents/&lt;/code&gt; with the &lt;code&gt;resources&lt;/code&gt; entry changed from &lt;code&gt;skill://&lt;/code&gt; to a &lt;code&gt;file://&lt;/code&gt; path.&lt;/p&gt;

&lt;p&gt;Then ask it plainly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Audit us-east-1. Actually call the tools and produce the report.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran this against five resources I created on purpose to be broken (the repo has a script for that, plus a teardown script that removes 100% of them). Here is the real output, trimmed. Nothing here is edited for effect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# AWS Audit - us-east-1, 2026-09-02&lt;/span&gt;

&lt;span class="gu"&gt;## Executive summary&lt;/span&gt;
Scoped to the 5 resources tagged demo-auditor=true, this audit found 8 findings:
1 CRITICAL, 3 HIGH, 2 MEDIUM, and 3 cost items. The single most urgent problem is a
publicly readable S3 bucket (demo-auditor-public-5403) that lets anyone on the
internet download its objects.

&lt;span class="gu"&gt;## Top 3 - do these now&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Public S3 bucket demo-auditor-public-5403 - bucket policy grants s3:GetObject to
   everyone (Principal: &lt;span class="err"&gt;*&lt;/span&gt;). Enable Public Access Block and remove the public policy.
&lt;span class="p"&gt;2.&lt;/span&gt; Security group sg-055250cbcc6f3b37b - SSH port 22 is open to 0.0.0.0/0. Restrict
   to a known admin IP or use SSM Session Manager.
&lt;span class="p"&gt;3.&lt;/span&gt; GuardDuty is disabled in us-east-1 - no threat detection is running.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cost section is where the pricing MCP earns its place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### [COST] Unassociated Elastic IP - ~$3.65/month&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Resource: eipalloc-000992c9956cfaaa5 (public IP 35.173.72.149, no association)
&lt;span class="p"&gt;-&lt;/span&gt; Estimated impact: $0.005/hr (USE1-PublicIPv4:IdleAddress, us-east-1) x 730 hrs
  = $3.65/mo. Assumption: idle for a full month, on-demand.
&lt;span class="p"&gt;-&lt;/span&gt; Fix: Release the Elastic IP if not needed, or associate it with a running resource.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every resource ID is real. Every rate came from the live Price List API. The agent computed the numbers, it did not make them up.&lt;/p&gt;

&lt;p&gt;The most convincing part was not a finding, though. It was the honesty. Three moments stood out:&lt;/p&gt;

&lt;p&gt;The agent found a snapshot created that same day. The checklist looks for "old" snapshots. Instead of forcing it into the finding, the agent flagged it for completeness and refused to call a fresh snapshot old.&lt;/p&gt;

&lt;p&gt;Root MFA was on. It reported that as a PASS. A tool that only ever finds problems just confirms its own bias. Reporting passes is how you know it looked.&lt;/p&gt;

&lt;p&gt;And the gaps section listed what it did not check and why: IAM per-user MFA, RDS encryption, and idle load balancers were out of scope for the demo, so it said "not verified" rather than implying those passed. "Not verified" is not "passed." That line in the prompt did real work.&lt;/p&gt;

&lt;p&gt;The whole run cost a few cents in demo resources and about two minutes of wall time.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Making it yours
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;SKILL.md&lt;/code&gt; file is yours to own. It is a checklist, not code.&lt;/p&gt;

&lt;p&gt;Add checks your team cares about. Each row names the read-only API that backs it, so extending it is a matter of adding a row and a line of guidance. Change the severities to match your risk appetite. Rewrite the report format if your manager wants it a certain way. Delete the demo-scope block to audit the entire region.&lt;/p&gt;

&lt;p&gt;A few natural next steps once it works:&lt;/p&gt;

&lt;p&gt;Point it at more regions. The demo is &lt;code&gt;us-east-1&lt;/code&gt; only. Loop the region in the prompt or run it per region.&lt;/p&gt;

&lt;p&gt;Schedule it. A read-only agent that runs every morning and mails you a diff of new findings is a genuinely useful thing, and it cannot break anything overnight because it cannot write.&lt;/p&gt;

&lt;p&gt;Widen the checklist toward a framework you report against, like the full CIS benchmark, one row at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build this when Prowler and Trusted Advisor exist?
&lt;/h2&gt;

&lt;p&gt;Fair question. There are mature tools in this space, and you should know when to reach for them instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prowler&lt;/strong&gt; is the heavyweight: 600+ checks, mapped to CIS, PCI, HIPAA, and more. If you need exhaustive compliance coverage for an audit, use Prowler. The trade-off is that 600 findings with no narrative is a wall of text. It tells you everything and prioritizes nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ScoutSuite&lt;/strong&gt; is read-only like our agent and produces a nice HTML report. It is excellent for a point-in-time config review. It does no cost analysis and it is static, not conversational. You cannot ask it a follow-up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trusted Advisor&lt;/strong&gt; has the best native cost checks (idle load balancers, unassociated Elastic IPs, underutilized EBS). The catch: the full cost category requires a &lt;strong&gt;Business or Enterprise Support plan&lt;/strong&gt; (&lt;a href="https://aws.amazon.com/premiumsupport/technology/trusted-advisor/" rel="noopener noreferrer"&gt;AWS docs&lt;/a&gt;). On a Basic plan you do not get them, which is exactly why an agent computing the same things from raw &lt;code&gt;Describe&lt;/code&gt; calls is useful to a beginner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Hub&lt;/strong&gt; is the central dashboard, but it must be configured. Our demo run caught the trap live: the standards were subscribed but reported &lt;code&gt;NO_AVAILABLE_CONFIGURATION_RECORDER&lt;/code&gt;. Without an AWS Config recorder, most controls cannot evaluate (&lt;a href="https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-setup-prereqs.html" rel="noopener noreferrer"&gt;AWS docs&lt;/a&gt;). The dashboard was on, the checks were off. A human skims a green dashboard and moves on. The agent read the actual status and flagged it.&lt;/p&gt;

&lt;p&gt;So here is the decision framework:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reach for&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prowler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You need exhaustive, framework-mapped compliance evidence for an audit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ScoutSuite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You want a thorough static config snapshot, security only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trusted Advisor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You are on Business/Enterprise Support and want native cost checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;This agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You want security AND cost in one plain-language, prioritized report you can converse with, on any support plan, provably read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The edge of the custom agent is not raw coverage. It is clarity, ruthless prioritization (a Top 3, not 600 rows), security and cost in one voice, detecting the "configured but inert" trap, and a provable read-only guarantee you can hand to a nervous manager.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When NOT to use it:&lt;/strong&gt; if you need certified compliance evidence, if you want continuous automated remediation (this agent only reads), or if your org already runs Prowler in CI and just needs the raw findings. This is a fast, human-friendly first look, not a compliance system of record.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the docs do not tell you
&lt;/h2&gt;

&lt;p&gt;Four things I hit building this that are not in any single doc:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;use_aws&lt;/code&gt; service allowlist is a real second wall, and it is easy to forget &lt;code&gt;s3api&lt;/code&gt;.&lt;/strong&gt; S3 read calls split between &lt;code&gt;s3&lt;/code&gt; and &lt;code&gt;s3api&lt;/code&gt; depending on the operation. Leave &lt;code&gt;s3api&lt;/code&gt; out of &lt;code&gt;allowedServices&lt;/code&gt; and the public-bucket check silently cannot run. Both belong in the list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Not verified" has to be forced in the prompt or the model will paper over gaps.&lt;/strong&gt; Without the explicit "put it in the Gaps section as not verified" instruction, models tend to imply a skipped check passed. That single sentence changed the behavior in testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idle Elastic IP pricing hides behind a specific usage type.&lt;/strong&gt; The rate is not under a generic "EIP" filter. It is &lt;code&gt;USE1-PublicIPv4:IdleAddress&lt;/code&gt; in us-east-1 (since the Feb 2024 public IPv4 charge). If your cost math comes back empty, you are querying the wrong usage type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clean up your demo resources.&lt;/strong&gt; If you use the demo scripts, the teardown deletes by recorded ID and then sweeps by tag, in this order: snapshot, Elastic IP, volume, security group, bucket. Run it right after, or you keep paying the few cents a month the audit just flagged. The irony writes itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;An agent is not a mystery. It is a JSON file that names a model, lists some tools, plugs in a few MCP servers, and points at a Markdown checklist. The engineering that makes it trustworthy is not in the model at all. It is in the IAM boundary that makes destructive action impossible, and in a prompt that forbids inventing anything.&lt;/p&gt;

&lt;p&gt;Build the guardrail first. Then the agent can be as capable as you like, because the worst it can do is tell you the truth about your account.&lt;/p&gt;

&lt;p&gt;Grab the two files, attach the two read-only policies, and run it against your own account: &lt;a href="https://github.com/simplynadaf/aws-auditor-agent" rel="noopener noreferrer"&gt;github.com/simplynadaf/aws-auditor-agent&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What would you add to the checklist first, security or cost? I am curious which one bites people more in practice.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more on AWS architecture, DevOps, and AI Infrastructure:&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Portfolio&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://dev.to/sarvar_04"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://www.youtube.com/@sarvar-nadaf" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="mailto:simplynadaf@gmail.com"&gt;Email&lt;/a&gt; | &lt;a href="https://builder.aws.com/community/@sarvar" rel="noopener noreferrer"&gt;AWS Builder Center&lt;/a&gt; | &lt;a href="https://x.com/SarvarN_04" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>ai</category>
      <category>security</category>
    </item>
    <item>
      <title>AI Agent vs Agentic AI: The Distinction That Changes Your Architecture</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Fri, 11 Sep 2026 12:59:53 +0000</pubDate>
      <link>https://dev.to/aws-builders/ai-agent-vs-agentic-ai-the-distinction-that-changes-your-architecture-3o8f</link>
      <guid>https://dev.to/aws-builders/ai-agent-vs-agentic-ai-the-distinction-that-changes-your-architecture-3o8f</guid>
      <description>&lt;p&gt;I was on a call last month where a VP said "we're deploying agentic AI" and what they actually had was a single chatbot connected to a database. That's an agent. A good one, maybe. But calling it "agentic AI" is like calling a single REST endpoint "a microservices architecture." That confusion cost them three months and a budget overrun before anyone caught it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An AI agent is a thing you build. Agentic AI is how you wire many of those things together.&lt;/strong&gt; One is a worker. The other is the factory floor.&lt;/p&gt;

&lt;p&gt;If you missed &lt;a href="https://dev.to/aws-builders/ai-assistance-vs-ai-agents-understanding-the-shift-from-responses-to-autonomous-systems-pb3"&gt;Part 1 (AI Assistance vs AI Agents)&lt;/a&gt;, go read that first. It sets the foundation for what we're covering today.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why this confusion is dangerous&lt;/li&gt;
&lt;li&gt;AI agents: the specialist worker&lt;/li&gt;
&lt;li&gt;Agentic AI: the system architecture&lt;/li&gt;
&lt;li&gt;The quick comparison&lt;/li&gt;
&lt;li&gt;When agentic AI goes wrong&lt;/li&gt;
&lt;li&gt;The governance gap nobody talks about&lt;/li&gt;
&lt;li&gt;Real-world examples: AWS and beyond&lt;/li&gt;
&lt;li&gt;When to use each&lt;/li&gt;
&lt;li&gt;The bottom line&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why this confusion is dangerous
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;You buy an agent when you need a system.&lt;/strong&gt; A vendor shows a slick demo: one agent handling one task beautifully. You deploy it expecting end-to-end workflow automation. Three months later, you're wondering why it can't handle anything beyond that one task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You over-engineer when you just need an agent.&lt;/strong&gt; I've seen teams spend months building multi-agent orchestration for what was essentially a ticket-routing problem. A single well-configured agent would've shipped in a week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You skip governance entirely.&lt;/strong&gt; Deploy a fleet of agents with no coordination layer, no monitoring, no human oversight. I've watched that go sideways at 2 AM.&lt;/p&gt;

&lt;p&gt;The VP on that call? Their "agentic AI" couldn't even hand off context between conversations. It reset every session. That's a stateless function with a chat interface.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI agents: the specialist worker
&lt;/h2&gt;

&lt;p&gt;An AI agent is a piece of software that does a specific job on its own. It perceives, decides, uses tools, and takes action within clear boundaries for a defined task. One contractor, one job. They show up, do the thing, leave.&lt;/p&gt;

&lt;p&gt;What separates an agent from a fancy API call:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decides &lt;em&gt;how&lt;/em&gt; to approach the task (not just following a script)&lt;/li&gt;
&lt;li&gt;Can call tools: APIs, databases, search, code execution&lt;/li&gt;
&lt;li&gt;Works autonomously until it hits a "done" state&lt;/li&gt;
&lt;li&gt;Has guardrails but makes choices within them
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: "Summarize the latest security findings for my AWS account."

AI Agent: *connects to AWS Security Hub*
          *retrieves findings*
          *categorizes by severity*
          *generates summary*

"Here are your findings:
- 3 Critical: Unencrypted S3 buckets (bucket-a, bucket-b, bucket-c)
- 7 High: Security groups with open ports
- 12 Medium: IAM users without MFA

Would you like me to generate remediation steps?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One agent. One job. Done well.&lt;/p&gt;




&lt;h2&gt;
  
  
  Agentic AI: the system architecture
&lt;/h2&gt;

&lt;p&gt;Agentic AI isn't a single agent. It's the design approach for building systems where multiple agents work together with planning, coordination, evaluation, and governance layered on top.&lt;/p&gt;

&lt;p&gt;If an AI agent is one contractor, agentic AI is the general contractor running the entire construction site: scheduling the specialists, checking their work, handling exceptions, making sure the building doesn't fall down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it agentic&lt;/strong&gt; (not just "multiple agents"):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A planning layer that breaks goals into steps&lt;/li&gt;
&lt;li&gt;An orchestrator that sequences and delegates&lt;/li&gt;
&lt;li&gt;Evaluators that check whether output is actually good&lt;/li&gt;
&lt;li&gt;Memory that persists across runs&lt;/li&gt;
&lt;li&gt;Policy enforcement and human-in-the-loop checkpoints&lt;/li&gt;
&lt;li&gt;Self-correction when things go sideways
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────┐
│              AGENTIC AI SYSTEM               │
├─────────────────────────────────────────────┤
│                                             │
│  ┌─────────────┐    ┌──────────────────┐   │
│  │   Planner   │───▶│   Orchestrator   │   │
│  └─────────────┘    └──────────────────┘   │
│                            │                │
│         ┌──────────────────┼────────┐       │
│         ▼                  ▼        ▼       │
│  ┌────────────┐  ┌────────────┐ ┌───────┐  │
│  │  Agent A   │  │  Agent B   │ │Agent C│  │
│  │(Research)  │  │(Analysis)  │ │(Write)│  │
│  └────────────┘  └────────────┘ └───────┘  │
│         │                  │        │       │
│         └──────────────────┼────────┘       │
│                            ▼                │
│                   ┌──────────────┐          │
│                   │  Evaluator   │          │
│                   └──────────────┘          │
│                            │                │
│                   ┌──────────────┐          │
│                   │   Memory &amp;amp;   │          │
│                   │  Governance  │          │
│                   └──────────────┘          │
│                                             │
└─────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Real-world example: Incident Response System&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I helped architect this for a financial services client. Before the agentic system, their mean-time-to-resolution for P1 incidents was 47 minutes. After: 11 minutes. The system paid for itself in the first month from reduced downtime costs alone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: "Handle the production incident on the payments service."

Agentic AI System:

  Planner: "This needs investigation, diagnosis, action, 
            and communication."

  Agent 1 (Monitor): *checks metrics, spots CPU spike at 2:03 AM*
  Agent 2 (Logs):    *analyzes logs... initially flags DB connection 
                      issue, evaluator finds low confidence*
  Agent 2 (retry):   *digs deeper, finds memory leak in v2.3.1*
  Agent 3 (Git):     *identifies the commit that introduced it*

  Evaluator: "Root cause confirmed: memory leak in commit abc123. 
              Recommending rollback."

  Orchestrator: "Rollback is destructive. Paging human."

  [Human approves]

  Agent 4 (Deploy): *rolls back to v2.3.0*
  Agent 5 (Verify): *confirms service restored*
  Agent 6 (Comms):  *posts to Slack, creates JIRA ticket, 
                     updates status page*

  Memory: *stores pattern for faster detection next time*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agent 2 got it wrong the first time. The evaluator caught it. The system self-corrected. That's the difference between a collection of agents and an agentic system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it like Uber.&lt;/strong&gt; You request a ride to the airport, flight in 90 minutes. Driver cancels 2 minutes later. A single agent just says "finding new driver..." and makes you wait. An agentic system? It widens the search radius, filters for high-completion drivers, calculates you'll still make your flight, texts you proactively, applies a discount for the inconvenience, flags the cancelling driver's pattern, and remembers to prioritize reliable drivers for your airport rides next time. Six agents coordinating. Context flowing between them. That's the gap.&lt;/p&gt;




&lt;h2&gt;
  
  
  The quick comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;AI Agent&lt;/th&gt;
&lt;th&gt;Agentic AI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it is&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A software component&lt;/td&gt;
&lt;td&gt;A system design paradigm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single task&lt;/td&gt;
&lt;td&gt;Multi-step, multi-agent orchestration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Analogy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One specialist employee&lt;/td&gt;
&lt;td&gt;The entire organization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Often resets after each task&lt;/td&gt;
&lt;td&gt;Persistent across interactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Coordination&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Works alone&lt;/td&gt;
&lt;td&gt;Multiple agents collaborating&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quality control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited self-checking&lt;/td&gt;
&lt;td&gt;Built-in evaluators and critics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Governance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic guardrails&lt;/td&gt;
&lt;td&gt;Policy enforcement, audit trails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low: single model + tools&lt;/td&gt;
&lt;td&gt;Higher: orchestration overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low to medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time to value&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Days to weeks&lt;/td&gt;
&lt;td&gt;Weeks to months&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most of my clients are somewhere in between. They have 3-4 agents running independently with no coordination layer. Just agents in a room with no manager. That's where most companies are stuck in mid-2026.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The simplest analogy:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI Agent = A single microservice. Does one thing well.&lt;/p&gt;

&lt;p&gt;Agentic AI = A microservices architecture. The service mesh, orchestration, observability, and resilience patterns that make dozens of services work together.&lt;/p&gt;

&lt;p&gt;Nobody calls a single Lambda function "a serverless architecture." Same logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  When agentic AI goes wrong
&lt;/h2&gt;

&lt;p&gt;Agentic systems fail in ways that single agents don't. I've seen all of these in production:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent conflict.&lt;/strong&gt; Two agents fighting each other for 20 minutes. One scaling up EC2 instances for a traffic spike, the other scaling them down because cost threshold was breached. Back and forth. We burned $600 in compute before someone killed the loop at 2 AM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infinite loops.&lt;/strong&gt; Agent A writes a draft. Agent B reviews it, rejects. Repeat 47 times. $180 for what should have been a $2 task. Nobody set a max iteration limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cascading failures.&lt;/strong&gt; One agent failed silently, returned partial results. Every agent downstream built on garbage data. Final output looked confident and was completely wrong. Six hours before anyone noticed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to prevent this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard iteration limits and timeouts (we use max 5 retries, 120s timeout per agent)&lt;/li&gt;
&lt;li&gt;Budget caps per workflow (kill it if it exceeds $X)&lt;/li&gt;
&lt;li&gt;Clear priority rules when agents conflict (cost vs availability: which wins? Decide upfront)&lt;/li&gt;
&lt;li&gt;Circuit breakers: if an agent fails, stop the pipeline, don't feed garbage downstream&lt;/li&gt;
&lt;li&gt;Observability at every handoff (we log every inter-agent message with correlation IDs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip these in a demo. Never in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  The governance gap nobody talks about
&lt;/h2&gt;

&lt;p&gt;With a single agent, governance is simple: guardrails on access, actions, and data. Configure once.&lt;/p&gt;

&lt;p&gt;With an agentic system, governance becomes distributed. And this is where I see teams get burned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data boundaries.&lt;/strong&gt; Agent A sees customer PII for ticket resolution. Agent B handles analytics, should never see PII. If the orchestrator passes context without filtering, compliance violation. Saw this at a healthcare client. HIPAA auditor caught it before production. Fix took two weeks of re-architecting the context-passing layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approval chains.&lt;/strong&gt; The &lt;em&gt;combination&lt;/em&gt; of actions might need approval even if individual ones don't. Agent 1 finds vulnerability + Agent 2 auto-patches + Agent 3 deploys to production = nobody approved a production deployment. Each agent followed its own rules. The system violated yours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audit trails.&lt;/strong&gt; Regulators want to know which component made which decision, what data informed it, who approved. Multi-agent systems need per-agent logging with correlation IDs across the entire workflow. Retrofitting this is painful. Ask me how I know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost governance.&lt;/strong&gt; Agentic systems spawn sub-tasks, retry loops, parallel workflows that compound. I've seen a $2 expected workflow cost $200 because three agents kept spawning sub-agents to "be thorough." You need budget enforcement at the orchestrator level, not per-agent.&lt;/p&gt;

&lt;p&gt;In regulated industries (finance, healthcare, government), the governance architecture might take longer to design than the agents themselves. That's normal. That's also why most "agentic AI" demos fall apart when compliance asks questions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-world examples: AWS and beyond
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Single Agent: Amazon Bedrock Agent
&lt;/h3&gt;

&lt;p&gt;Configure one agent with a foundation model, action groups, knowledge base, and guardrails. It handles one task: answering questions, processing orders, analyzing documents. Quick to deploy, clear boundaries, predictable costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Agent: Amazon Bedrock Multi-Agent Collaboration
&lt;/h3&gt;

&lt;p&gt;Orchestrate multiple agents with a supervisor that plans and delegates, specialized sub-agents, shared memory, evaluation logic, and human approval workflows via Step Functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  My framework recommendation (opinionated)
&lt;/h3&gt;

&lt;p&gt;I've used four frameworks across client engagements this year. Here's my honest take:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Bedrock + AWS AgentCore&lt;/strong&gt; is my default for enterprise clients. Not because it's the most elegant API (it's not), but because IAM boundaries, VPC isolation, CloudWatch observability, and compliance controls come out of the box. When a CISO asks "where does my data go?" I have an answer. With other frameworks, I'm building that answer from scratch.&lt;/p&gt;

&lt;p&gt;For prototyping and proving a concept fast, &lt;strong&gt;LangGraph&lt;/strong&gt; gets you there quickest if your team already knows LangChain. But I've debugged graph state issues at 2 AM twice now. Production-hardening it takes real effort.&lt;/p&gt;

&lt;p&gt;The framework matters less than the architecture. Planning, evaluation, governance, memory, and human-in-the-loop: get those right and you can swap the underlying framework later. Get those wrong and no framework saves you.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to use each
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Single AI agent when:&lt;/strong&gt; Task is bounded. One model plus tools handles it start to finish. You want it deployed this week. Think: chatbot, code reviewer, data extractor, alert responder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agentic AI when:&lt;/strong&gt; Workflow crosses domains. Multiple specialists must coordinate. You need planning, self-evaluation, and adaptation. Governance matters. Think: incident response, claims processing, research pipelines, end-to-end DevOps automation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honest answer for 80% of teams I talk to:&lt;/strong&gt; Start with a single agent. A well-built agent delivering value today beats a half-built agentic system delivering nothing for six months.&lt;/p&gt;

&lt;h3&gt;
  
  
  The maturity spectrum
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stage 1: Single AI Agent
         → Deploy in days. Immediate ROI on one task.

Stage 2: Multiple Independent Agents  
         → Each agent owns a domain. No coordination between them.
         → (Most companies are HERE in mid-2026)

Stage 3: Coordinated Multi-Agent System
         → Shared context, handoffs, basic orchestration.
         → Where the ROI multiplier kicks in.

Stage 4: Full Agentic AI
         → Planning, evaluation, governance, memory, self-correction.
         → 47-min incident resolution → 11-min. That kind of impact.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The jump from Stage 2 to 3 is where I spend most of my consulting time. It's not a technology problem. It's a "who owns the orchestration layer" problem. That's an org chart conversation, not a code review.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AI Agent&lt;/strong&gt; = A component. One autonomous piece of software that gets a specific job done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agentic AI&lt;/strong&gt; = An architecture. The system that plans, coordinates, evaluates, and governs multiple agents working together.&lt;/p&gt;

&lt;p&gt;They're not competing. They're layers. You build agents; you architect agentic systems. The agents live inside the architecture.&lt;/p&gt;

&lt;p&gt;The progression from Part 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Assistant  →  Tells you what to do (responds)
AI Agent      →  Does it for you (executes a task)
Agentic AI    →  Orchestrates multiple agents to achieve complex goals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real question isn't "which one should I use?" It's: &lt;strong&gt;"Do I need one specialist, or a team of specialists with a manager?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not sure? Start with an agent. Prove value. Then evolve when the single agent hits a wall. You'll know because someone will say "can it also do X, Y, and Z while considering W?" That's your signal.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Check Part 3:&lt;/strong&gt; &lt;a href="https://dev.to/aws-builders/build-your-first-ai-agent-in-30-minutes-crewai-aws-bedrock-40lo"&gt;Build Your First AI Agent in 30 Minutes - CrewAI + AWS Bedrock&lt;/a&gt;. Hands-on, full code, deploy in 30 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What stage is your company at right now?&lt;/strong&gt; Stage 1, 2, 3, or 4? And what's the blocker keeping you from the next one? Drop it in the comments.&lt;/p&gt;

&lt;p&gt;If this helped, a ❤️ or 🦄 helps other devs find it too. Follow me for more on AWS architecture, FinOps, and AI infrastructure.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How I Built an AI Agent That Cut My AWS Bill by 40% (CrewAI + Bedrock)</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Wed, 09 Sep 2026 13:09:58 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-i-built-an-ai-agent-that-cut-my-aws-bill-by-40-crewai-bedrock-392d</link>
      <guid>https://dev.to/aws-builders/how-i-built-an-ai-agent-that-cut-my-aws-bill-by-40-crewai-bedrock-392d</guid>
      <description>&lt;p&gt;Last week my AWS bill hit $300. I knew there was waste hiding somewhere. Forgotten EBS volumes, idle Elastic IPs, snapshots from six months ago that nobody remembered creating.&lt;/p&gt;

&lt;p&gt;I could open Cost Explorer. Click through dashboards. Manually cross-reference resources.&lt;/p&gt;

&lt;p&gt;Or I could let three AI agents do it in 60 seconds.&lt;/p&gt;

&lt;p&gt;I built a multi-agent system with CrewAI and Amazon Bedrock that scans everything, identifies waste, and writes a prioritized report with exact dollar savings. It found $125/month I was burning for nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who This Is For
&lt;/h2&gt;

&lt;p&gt;You run workloads on AWS. Your bill has crept up. You suspect there's waste but don't have time to audit every resource manually. You have 10+ resources running and haven't done a proper audit in 3 months. You want something that scans your account and tells you exactly what to delete, release, or downsize, with dollar amounts attached.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When this won't help:&lt;/strong&gt; If your account has fewer than 5 resources or you're still in free tier, the overhead of setting this up isn't worth it. Just check Cost Explorer manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Architecture&lt;/li&gt;
&lt;li&gt;Why Not Just Use Trusted Advisor?&lt;/li&gt;
&lt;li&gt;Why CrewAI + Bedrock&lt;/li&gt;
&lt;li&gt;The Custom Tool&lt;/li&gt;
&lt;li&gt;Running It&lt;/li&gt;
&lt;li&gt;What It Found&lt;/li&gt;
&lt;li&gt;Gotchas You'll Hit&lt;/li&gt;
&lt;li&gt;Web UI (Bonus)&lt;/li&gt;
&lt;li&gt;Key Lessons&lt;/li&gt;
&lt;li&gt;Cleanup&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;Three agents, one pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Scanner → Optimizer → Report Writer
   ↓           ↓             ↓
AWS APIs    Reasoning    Executive Report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Each agent has a single job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent 1: Cost Intelligence Analyst&lt;/strong&gt; scans your account using boto3. EC2 instances, EBS volumes, Elastic IPs, snapshots, S3 buckets, and Cost Explorer data. Raw facts with exact numbers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent 2: Optimization Strategist&lt;/strong&gt; takes the scan results and identifies savings. Orphaned volumes that can be deleted. Unattached IPs burning $3.60/month each. gp2 volumes that should be gp3. Reserved Instance candidates running 24/7 on On-Demand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent 3: Executive Report Writer&lt;/strong&gt; produces a markdown report with prioritized actions, dollar amounts, and risk levels. The kind of thing you can hand to a CTO.&lt;/p&gt;

&lt;p&gt;The pipeline runs sequentially. Each agent passes context to the next through CrewAI's task handoff.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Not Just Use Trusted Advisor?
&lt;/h2&gt;

&lt;p&gt;Fair question. AWS already has cost tools. Here's why they weren't enough for me:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Limitation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trusted Advisor (free tier)&lt;/td&gt;
&lt;td&gt;Only 7 checks. Misses orphaned volumes, old snapshots, gp2→gp3 opportunities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute Optimizer&lt;/td&gt;
&lt;td&gt;EC2 and Lambda only. No EBS, no EIPs, no S3 lifecycle gaps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost Explorer&lt;/td&gt;
&lt;td&gt;Shows WHAT you spent, not WHAT TO DO about it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infracost&lt;/td&gt;
&lt;td&gt;Terraform-only. Useless if you clicked things in the console&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The multi-agent approach covers all of these in one pass AND produces an actionable report with prioritized fixes. You get "delete vol-0abc123 to save $20/month" instead of a dashboard you have to interpret yourself.&lt;/p&gt;

&lt;p&gt;Plus it runs on YOUR schedule. Cron it weekly. Get a fresh report every Monday morning.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why CrewAI + Bedrock
&lt;/h2&gt;

&lt;p&gt;I tried a single-prompt approach first. One giant prompt asking the LLM to scan AND analyze AND write a report. The results were mediocre. The model tried to do everything and did nothing well. It hallucinated resource IDs that didn't exist because it was juggling too many concerns at once.&lt;/p&gt;

&lt;p&gt;Multi-agent fixes this. Each agent has a focused role, a specific backstory, and constrained output expectations. The scanner doesn't try to optimize. The optimizer doesn't write pretty reports.&lt;/p&gt;

&lt;p&gt;Amazon Bedrock Nova Pro handles the reasoning. On EC2, the IAM role handles auth automatically. No API keys to manage:&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;crewai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLM&lt;/span&gt;

&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LLM&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;bedrock/amazon.nova-pro-v1: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;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&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;Cost per run: about $0.01. The savings it finds will be 100-1000x that.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Custom Tool: Scanning AWS
&lt;/h2&gt;

&lt;p&gt;CrewAI agents need tools to interact with the world. I built one custom tool that wraps boto3:&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;crewai.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseTool&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AWSCostScannerTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseTool&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="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS Cost Scanner&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;Scans your AWS account for running resources and costs.&lt;/span&gt;&lt;span class="sh"&gt;"&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;_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scan_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ec2_instances&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_scan_ec2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ebs_volumes&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_scan_ebs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;elastic_ips&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_scan_eips&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ebs_snapshots&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_scan_snapshots&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3_buckets&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_scan_s3&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost_data&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_scan_costs&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;results&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="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Each &lt;code&gt;_scan_*&lt;/code&gt; method is a simple boto3 call. For example, &lt;code&gt;_scan_ebs()&lt;/code&gt; runs &lt;code&gt;ec2.describe_volumes()&lt;/code&gt; and flags any volume where &lt;code&gt;Attachments&lt;/code&gt; is empty. That's an orphan. The scanner doesn't decide what to do about it. It just reports: "vol-0abc123, 20GB gp2, unattached, $2.00/month."&lt;/p&gt;

&lt;p&gt;The optimizer doesn't need tools. It's pure reasoning. Takes the scan output and applies FinOps logic: "This volume has no attachments, it's orphaned, that's $20/month wasted."&lt;/p&gt;


&lt;h2&gt;
  
  
  Running It
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/SimplyNadaf/crewai-aws-cost-optimizer-ai-agent.git
&lt;span class="nb"&gt;cd &lt;/span&gt;crewai-aws-cost-optimizer-ai-agent
pip3.11 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1
python3.11 main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You'll see each agent working in sequence. The scanner calls AWS APIs, the optimizer reasons about the results, the report writer formats the final output.&lt;/p&gt;

&lt;p&gt;Total time: under 60 seconds for a typical account.&lt;/p&gt;


&lt;h2&gt;
  
  
  What It Found on My Account
&lt;/h2&gt;

&lt;p&gt;Three orphaned EBS volumes: $33/month. Two unattached Elastic IPs: $7.20/month. A running t3.medium that should be reserved: $82/month in potential savings. Two snapshots from January that nobody needed anymore.&lt;/p&gt;

&lt;p&gt;Here's a trimmed version of the actual report output:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;╭────────────────── 📊 Cost Optimization Report ──────────────╮
│                                                              │
│  Executive Summary                                           │
│  Current spend: $300/mo → Savings: $125/mo (41.7%)          │
│                                                              │
│  Top Savings Opportunities                                   │
│                                                              │
│   Priority  Resource             Action           Savings    │
│   ─────────────────────────────────────────────────────────  │
│   1         Orphaned EBS (×3)    Delete volumes   $33.00     │
│   2         Elastic IPs (×2)     Release          $7.20      │
│   3         EC2 i-0f7b...        Reserved (1yr)   $82.00     │
│   4         EBS vol-02b...       gp2 → gp3        $3.00      │
│                                                              │
│  Quick Wins (zero risk)                                      │
│   • Delete 3 orphaned EBS volumes → save $33/month           │
│   • Release 2 unused Elastic IPs → save $7.20/month         │
│                                                              │
╰──────────────────────────────────────────────────────────────╯
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The quick wins (deleting orphaned volumes, releasing unused IPs) took five minutes to act on. $40/month saved before lunch.&lt;/p&gt;


&lt;h2&gt;
  
  
  Gotchas You'll Hit
&lt;/h2&gt;

&lt;p&gt;Saved you the debugging time:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python version matters.&lt;/strong&gt; CrewAI requires 3.11+. If you're on Amazon Linux 2023, use &lt;code&gt;python3.11&lt;/code&gt; and &lt;code&gt;pip3.11&lt;/code&gt; explicitly. The default &lt;code&gt;python3&lt;/code&gt; is 3.9 and will throw &lt;code&gt;ModuleNotFoundError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enable Nova Pro in Bedrock Console first.&lt;/strong&gt; Go to Bedrock &amp;gt; Model access &amp;gt; Request access for Amazon Nova Pro. Takes 1-2 minutes to approve. Without this you'll get &lt;code&gt;AccessDeniedException: You don't have access to the model&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost Explorer needs 24 hours.&lt;/strong&gt; If you've never used Cost Explorer before, AWS needs ~24h to start collecting data. First run might return empty cost breakdowns. The agents still find orphaned resources, just no historical spend data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set the region as an env var, not just config.&lt;/strong&gt; CrewAI reads &lt;code&gt;AWS_DEFAULT_REGION&lt;/code&gt; from the environment, not from &lt;code&gt;~/.aws/config&lt;/code&gt;. Always &lt;code&gt;export&lt;/code&gt; it explicitly or the boto3 calls will fail with &lt;code&gt;NoRegionError&lt;/code&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Web UI (Bonus)
&lt;/h2&gt;

&lt;p&gt;I also built a Streamlit dashboard that wraps the same agents. One-click scan, visual findings, and remediation buttons that delete the orphaned resources for you.&lt;/p&gt;

&lt;p&gt;But the CLI version is the core. No web server needed. SSH into any EC2 instance, clone, run, done.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Lessons
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Separate scanning from reasoning.&lt;/strong&gt; Agents with tools should gather data. Agents without tools should think. My first version had one agent doing both. It mixed up resource IDs, merged findings from different services, and produced a report with numbers that didn't add up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Temperature 0.2 for cost analysis.&lt;/strong&gt; I tried 0.7 first. The optimizer started "suggesting" resources that might exist and estimating savings based on vibes. At 0.2 it sticks to the facts the scanner reported. Nothing invented.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IAM roles over API keys.&lt;/strong&gt; On EC2, there's zero credential management. The boto3 client picks up the instance role automatically. One less thing to configure, one less secret to leak. I've seen three repos on GitHub with AWS keys in their .env files pushed by accident. Don't be that person.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CrewAI's sequential process fits pipelines.&lt;/strong&gt; When agents need each other's output, sequential beats hierarchical. Each task's output becomes the next task's context. Parallel would make sense if the agents were independent, but ours depend on each other's findings.&lt;/p&gt;


&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;If you created demo resources for testing, remove them:&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;# Delete orphaned volumes the agents found&lt;/span&gt;
aws ec2 delete-volume &lt;span class="nt"&gt;--volume-id&lt;/span&gt; vol-xxx &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1

&lt;span class="c"&gt;# Release unused Elastic IPs&lt;/span&gt;
aws ec2 release-address &lt;span class="nt"&gt;--allocation-id&lt;/span&gt; eipalloc-xxx &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1

&lt;span class="c"&gt;# Or use the included cleanup script&lt;/span&gt;
bash scripts/cleanup-demo-resources.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;⚠️ The demo script (&lt;code&gt;scripts/create-demo-resources.sh&lt;/code&gt;) creates ~$40/month in dummy resources for testing. Always run cleanup after.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/simplynadaf" rel="noopener noreferrer"&gt;
        simplynadaf
      &lt;/a&gt; / &lt;a href="https://github.com/simplynadaf/crewai-aws-cost-optimizer-ai-agent" rel="noopener noreferrer"&gt;
        crewai-aws-cost-optimizer-ai-agent
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      3 AI agents analyze your AWS account and find cost savings — powered by CrewAI + Amazon Bedrock
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;💰 AWS Cost Optimizer Crew&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;3 AI agents scan your AWS account, find waste, and produce an executive savings report - in under 60 seconds.&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://crewai.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7cdc69810a095f81345819f961753d8e7f279946f03d377b9257dae9de41c202/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4275696c74253230776974682d4372657741492d626c75653f7374796c653d666f722d7468652d6261646765" alt="CrewAI"&gt;&lt;/a&gt;
&lt;a href="https://aws.amazon.com/bedrock/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/45f0409e4350b359b91b36c447f709e2b0f1f9206fb5f1dad64e23cb498fa6dd/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c4c4d2d416d617a6f6e253230426564726f636b2d6f72616e67653f7374796c653d666f722d7468652d6261646765" alt="Amazon Bedrock"&gt;&lt;/a&gt;
&lt;a href="https://python.org" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0d5baa2bb4112d14c526944c9c1827a966b6e8c92fccd20c6634e97b733d0c38/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f507974686f6e2d332e31312b2d677265656e3f7374796c653d666f722d7468652d6261646765" alt="Python"&gt;&lt;/a&gt;
&lt;a href="https://github.com/simplynadaf/crewai-aws-cost-optimizer-ai-agent/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/2792a6b590e1b7fbcc5f7c80df8da3149453c596df80f16fa86bd82c487bec8d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f773f7374796c653d666f722d7468652d6261646765" alt="License: MIT"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/simplynadaf/crewai-aws-agents/stargazers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b9a33eba40b8f0d4a5b7b930a895cdf34516054362e0f52e62f67491a508d262/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f73696d706c796e616461662f6372657761692d6177732d6167656e74733f7374796c653d736f6369616c" alt="Stars"&gt;&lt;/a&gt;
&lt;a href="https://github.com/simplynadaf/crewai-aws-agents/network/members" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6f94678b2b5202bb6645afc739aaed00148f4d968e1932e74b527c76bdf47d6d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f73696d706c796e616461662f6372657761692d6177732d6167656e74733f7374796c653d736f6369616c" alt="Forks"&gt;&lt;/a&gt;
&lt;a href="https://github.com/simplynadaf/crewai-aws-agents/issues" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f062bbdcba27ae23e9ac6af3d822691334fdfd001e6ca09715b0afd74aec8fe9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f73696d706c796e616461662f6372657761692d6177732d6167656e7473" alt="Issues"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⭐ If this helped you, give it a star! It helps others find it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/simplynadaf/crewai-aws-cost-optimizer-ai-agent#-video-tutorial" rel="noopener noreferrer"&gt;Video Tutorial&lt;/a&gt; • &lt;a href="https://github.com/simplynadaf/crewai-aws-cost-optimizer-ai-agent#-getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt; • &lt;a href="https://github.com/simplynadaf/crewai-aws-cost-optimizer-ai-agent#-how-it-works" rel="noopener noreferrer"&gt;How It Works&lt;/a&gt; • &lt;a href="https://github.com/simplynadaf/crewai-aws-cost-optimizer-ai-agent#-example-output" rel="noopener noreferrer"&gt;Demo&lt;/a&gt; • &lt;a href="https://github.com/simplynadaf/crewai-aws-cost-optimizer-ai-agent#-contributing" rel="noopener noreferrer"&gt;Contributing&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🎬 Video Tutorial&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Watch the full demo - from zero to finding $125/month in AWS waste:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://youtu.be/BDJytOAjtlo" rel="nofollow 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%2Fraw.githubusercontent.com%2Fsimplynadaf%2Fcrewai-aws-cost-optimizer-ai-agent%2FHEAD%2Fassets%2Fyoutube-thumbnail.png" alt="I Built an AI That Finds Hidden AWS Costs"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;▶️ &lt;strong&gt;&lt;a href="https://youtu.be/BDJytOAjtlo" rel="nofollow noopener noreferrer"&gt;Watch on YouTube: I Built an AI That Finds Hidden AWS Costs (It Found $125 in 60 Seconds)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the video you'll see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Browsing the source code and architecture on GitHub&lt;/li&gt;
&lt;li&gt;Live deployment on an EC2 instance (Amazon Linux 2023)&lt;/li&gt;
&lt;li&gt;All 3 agents working in real-time (Scanner → Optimizer → Report Writer)&lt;/li&gt;
&lt;li&gt;Streamlit web dashboard with one-click remediation&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🤔 The Problem&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Your AWS bill keeps climbing. Resources get created and forgotten - orphaned EBS volumes, unattached Elastic IPs, old snapshots nobody…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/simplynadaf/crewai-aws-cost-optimizer-ai-agent" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Two minutes to set up. A penny to run. Might save you hundreds.&lt;/p&gt;




&lt;p&gt;What's eating YOUR AWS budget? Have you tried multi-agent approaches for infrastructure tasks? Drop your experience below.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more on AWS architecture, DevOps, and AI Infrastructure:&lt;/em&gt;&lt;br&gt;
&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Portfolio&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://dev.to/sarvar_04"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://www.youtube.com/@sarvar-nadaf" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="mailto:simplynadaf@gmail.com"&gt;Email&lt;/a&gt; | &lt;a href="https://builder.aws.com/community/@sarvar" rel="noopener noreferrer"&gt;AWS Builder Center&lt;/a&gt; | &lt;a href="https://x.com/SarvarN_04" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>showdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>ReachAloud: I built a multilingual voice tool that reads emergency alerts aloud for people who can't read the screen</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Mon, 07 Sep 2026 05:37:57 +0000</pubDate>
      <link>https://dev.to/sarvar_04/reachaloud-i-built-a-multilingual-voice-tool-that-reads-emergency-alerts-aloud-for-people-who-15ho</link>
      <guid>https://dev.to/sarvar_04/reachaloud-i-built-a-multilingual-voice-tool-that-reads-emergency-alerts-aloud-for-people-who-15ho</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-09-03"&gt;Weekend Challenge: Generosity Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;ReachAloud&lt;/strong&gt; is a multilingual text-to-speech accessibility tool for nonprofits and communities. It turns a written emergency alert into clear, natural, spoken audio in any language, for the people that text-only alerts leave behind: those who can't read, can't see the screen, or don't speak the local language.&lt;/p&gt;

&lt;p&gt;I want to be honest about the scope. ReachAloud is not an early-warning system. It does not detect floods, send alerts, or replace official channels. It is the last-mile comprehension layer. The warning already exists as an SMS, a banner, a siren. ReachAloud makes that warning understandable to everyone it reaches.&lt;/p&gt;

&lt;p&gt;That distinction came from thinking about one event. In August 2026, more than 1,200 people were lost in the &lt;a href="https://en.wikipedia.org/wiki/2026_Nepal%E2%80%93Tibet_floods" rel="noopener noreferrer"&gt;Nepal-Tibet floods&lt;/a&gt;, a wall of water that arrived with almost no usable warning. When a warning does go out, it usually goes out as text. But a written alert only helps the people who can read it, on a screen they can see, in a language they know. In a real crowd (an elderly villager, someone who is blind, a trekker who doesn't speak Nepali) the alert that could save a life arrives in a form they cannot use. ReachAloud is dedicated to those victims, and the site carries that dedication too. It exists so the next warning reaches everyone, in a voice they understand.&lt;/p&gt;

&lt;p&gt;Here is what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speaks any alert in any language.&lt;/strong&gt; One ElevenLabs multilingual model reads whatever text you type, in whatever language it is written.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emergency broadcast mode.&lt;/strong&gt; A full-screen, high-contrast takeover plays the alert aloud and cycles a giant caption through every language. Voice for people who can't read the screen. Huge text for people who can't hear it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Play in every language&lt;/strong&gt; back-to-back, for mixed crowds where locals and visitors need the same warning in turn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works offline.&lt;/strong&gt; Once the page has loaded, a service worker keeps the alerts playing even when the network is down. That is exactly when a flood kills connectivity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download and share&lt;/strong&gt; each alert as an MP3, for a WhatsApp group, a loudspeaker, or a printed QR poster at a trailhead.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Live: &lt;a href="https://simplynadaf.github.io/reachaloud/" rel="noopener noreferrer"&gt;simplynadaf.github.io/reachaloud&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No sign-up needed. Six pre-generated flood alerts (English, Nepali, Marathi, Hindi, Arabic, Chinese) play instantly with zero API calls. To hear your &lt;em&gt;own&lt;/em&gt; text spoken, click &lt;strong&gt;Hear it now&lt;/strong&gt; and paste a free ElevenLabs key.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/X0yQTw4slhE" width="710" height="399"&gt;
  &lt;/iframe&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F06l3oe3cm770cr025vww.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F06l3oe3cm770cr025vww.png" alt="ReachAloud landing page: a written flood alert with a Speak this alert button and language cards" width="799" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/simplynadaf" rel="noopener noreferrer"&gt;
        simplynadaf
      &lt;/a&gt; / &lt;a href="https://github.com/simplynadaf/reachaloud" rel="noopener noreferrer"&gt;
        reachaloud
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Multilingual text-to-speech accessibility tool that turns emergency alerts into natural voice in any language. Built with ElevenLabs.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🔊 ReachAloud&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Emergency alerts, spoken aloud in every language&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;The last-mile comprehension layer for emergency alerts.&lt;/strong&gt; ReachAloud turns a written
warning into clear, natural, offline-capable spoken audio in any language, for the people
that text-only alerts leave behind: low-literacy, low-vision, and non-native readers.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://simplynadaf.github.io/reachaloud/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/748711a76600a3445fdfc57c44927aeef0ad2dabe22923abd10f77465869afd5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6976655f44656d6f2d73696d706c796e616461662e6769746875622e696f2532467265616368616c6f75642d3338653066663f7374796c653d666f722d7468652d6261646765" alt="Live Demo"&gt;&lt;/a&gt;
&lt;a href="https://dev.to/sarvar_04/reachaloud-i-built-a-multilingual-voice-tool-that-reads-emergency-alerts-aloud-for-people-who-15ho" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/3034e1a58dd38d77e77104c7be1ab5bec4e08995e471540387b2fcf6f6498991/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f526561645f7468655f73746f72792d4465762e746f2d3061306130613f7374796c653d666f722d7468652d6261646765266c6f676f3d6465762e746f" alt="Read the story"&gt;&lt;/a&gt;
&lt;a href="https://elevenlabs.io" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3f60ef9049ccf011db52759ee1393a2f8034e6cd53ff3662beedab24771537df/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f566f6963655f62792d456c6576656e4c6162732d6666366234613f7374796c653d666f722d7468652d6261646765" alt="Built with ElevenLabs"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ac049ef4e7a0b7196b09add6ac2d4f180e544c0ac779c2b2ac2fd2723a209579/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/ac049ef4e7a0b7196b09add6ac2d4f180e544c0ac779c2b2ac2fd2723a209579/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666c61742d737175617265" alt="License"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e284302315e9ce2587c5fef86a5ce8306fbe7f25fd23319860ca37320d9627b5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5057412d6f66666c696e652d2d72656164792d3561353f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/e284302315e9ce2587c5fef86a5ce8306fbe7f25fd23319860ca37320d9627b5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5057412d6f66666c696e652d2d72656164792d3561353f7374796c653d666c61742d737175617265" alt="PWA"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/493a6386870c6ff953bac2d8201c0d7434d6f73213fbb92b80703bb9f1f40099/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6275696c642d6e6f6e652d6c69676874677265793f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/493a6386870c6ff953bac2d8201c0d7434d6f73213fbb92b80703bb9f1f40099/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6275696c642d6e6f6e652d6c69676874677265793f7374796c653d666c61742d737175617265" alt="No build step"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/8facbbdf35c198a6cc090dd4a30ad150028da69cdddc0e465641df178db187fe/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f64656d6f5f6c616e6775616765732d362d6666616233643f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/8facbbdf35c198a6cc090dd4a30ad150028da69cdddc0e465641df178db187fe/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f64656d6f5f6c616e6775616765732d362d6666616233643f7374796c653d666c61742d737175617265" alt="Languages"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/simplynadaf/reachaloud/assets/screenshot-hero.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsimplynadaf%2Freachaloud%2FHEAD%2Fassets%2Fscreenshot-hero.png" alt="ReachAloud landing page: a written flood alert with a Speak this alert button and language cards" width="820"&gt;&lt;/a&gt;





&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;
▶️ Watch the 2-minute demo&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://youtu.be/X0yQTw4slhE" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/571c54c2501e08031241cba0b0a95ead2b30b35b930ddcbc15b263d79dc2edf3/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f58307951547734736c68452f6d617872657364656661756c742e6a7067" alt="ReachAloud demo video"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why this exists&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;When a flash flood hits, the warning goes out as text: an SMS, a banner, a push
notification. But a written alert only helps the people who can read it, on a screen
they can see, in a language they know. In a mixed, high-stress crowd (elderly villagers,
someone who is blind, a trekker who does not speak the local language) the alert that
could save a life arrives in a form they cannot use.&lt;/p&gt;
&lt;p&gt;ReachAloud is dedicated to the more than 1,200 people lost in the
&lt;a href="https://en.wikipedia.org/wiki/2026_Nepal%E2%80%93Tibet_floods" rel="nofollow noopener noreferrer"&gt;2026 Nepal-Tibet floods&lt;/a&gt;
It does not detect disasters or send warnings.…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/simplynadaf/reachaloud" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The whole app is a single &lt;code&gt;index.html&lt;/code&gt; (Tailwind + GSAP, no build step) plus a small serverless function and the pre-generated audio. Everything is MIT licensed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The design goal was a demo that is impossible to break in front of a judge. It had to work with no API key and no network, while still proving live ElevenLabs voice on demand. That led to a two-mode architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  ElevenLabs is the load-bearing core
&lt;/h3&gt;

&lt;p&gt;The whole product &lt;em&gt;is&lt;/em&gt; the voice. For someone who can't read the screen, the audio is the deliverable, so the TTS can't be a nice-to-have bolted on the side. I used the &lt;code&gt;eleven_multilingual_v2&lt;/code&gt; model for one specific reason: it auto-detects the language of the text you give it. So the same code path speaks English, Nepali, Marathi, Hindi, Arabic, and Chinese with no per-language branching. You type the alert, ElevenLabs figures out the language, and the same reassuring voice reads it.&lt;/p&gt;

&lt;p&gt;I picked a calm, default premade voice (Sarah) on purpose. In an emergency, panic in the delivery makes things worse. A steady voice is part of the accessibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mode 1: pre-generated static clips (judge-proof)
&lt;/h3&gt;

&lt;p&gt;I wrote a small Python script (&lt;code&gt;scripts/generate_demo.py&lt;/code&gt;) that renders one flood-evacuation alert into all six languages once, then saves them as static MP3s plus a &lt;code&gt;manifest.json&lt;/code&gt; the frontend reads. The whole six-language set costs about 670 characters of the monthly free quota. Generated once, then served forever with zero API calls.&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;MODEL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eleven_multilingual_v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# auto-detects the language of the text
&lt;/span&gt;&lt;span class="n"&gt;VOICE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EXAVITQu4vr4xnSDxMaL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;     &lt;span class="c1"&gt;# Sarah: reassuring, free-tier friendly
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tts&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="n"&gt;payload&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;text&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model_id&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_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;voice_settings&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;similarity_boost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/text-to-speech/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;VOICE_ID&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;data&lt;/span&gt;&lt;span class="o"&gt;=&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;payload&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;headers&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;xi-api-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&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;application/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;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&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;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&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="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;resp&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;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what GitHub Pages serves. It works offline, needs no account, and never touches a quota.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mode 2: live TTS with no backend at all
&lt;/h3&gt;

&lt;p&gt;I wanted people to hear &lt;em&gt;their own&lt;/em&gt; alert live, but GitHub Pages has no server to hide a key behind. So the &lt;strong&gt;Hear it now&lt;/strong&gt; flow calls &lt;code&gt;api.elevenlabs.io&lt;/code&gt; directly from the browser using a key the user pastes. The key lives only in &lt;code&gt;localStorage&lt;/code&gt; (opt-in checkbox) and is sent only to ElevenLabs, never to me, because there is no "me" server in this path. The modal shows the free-tier facts (10,000 characters a month, no card) and links straight to the key page, so anyone can try it in under a minute.&lt;/p&gt;

&lt;p&gt;There is also an optional Vercel proxy (&lt;code&gt;api/speak.js&lt;/code&gt;) for anyone who would rather keep a shared key server-side. Both paths hit the same model.&lt;/p&gt;

&lt;h3&gt;
  
  
  The accessibility work is the interesting part
&lt;/h3&gt;

&lt;p&gt;The emergency broadcast was the piece I cared most about getting right, because it has to serve two opposite disabilities at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;People who &lt;strong&gt;can't see&lt;/strong&gt; the screen get the alert as loud spoken voice, looping through every language.&lt;/li&gt;
&lt;li&gt;People who &lt;strong&gt;can't hear&lt;/strong&gt; get a giant, high-contrast caption that cycles in sync with the audio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Getting it safe took work. It never auto-plays, because an unexpected siren is its own hazard. It reuses a single Web Audio context for the attention chime, since creating one per click hits the browser's context limit and throws. The loop is capped at three cycles so it always ends on its own. And it is dismissable three ways: a Stop button, the Escape key, or a click on the backdrop. Everything respects &lt;code&gt;prefers-reduced-motion&lt;/code&gt;, focus is trapped and restored, and the dialogs use proper &lt;code&gt;role="dialog"&lt;/code&gt; and &lt;code&gt;aria-live&lt;/code&gt; semantics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Offline, because that's when it matters
&lt;/h3&gt;

&lt;p&gt;A flood is exactly when connectivity dies. A service worker pre-caches the app shell and all six clips, then serves them cache-first, so after one visit the alerts still play with no internet. One honest limitation: the CDN-loaded fonts and CSS do not cache, so styling degrades offline. The core alert audio, the part that saves someone, still plays.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of ElevenLabs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The voice is not a feature of ReachAloud. It is the whole product. The &lt;code&gt;eleven_multilingual_v2&lt;/code&gt; model is what lets one alert reach a low-literacy elder, a blind neighbor, and a foreign trekker in their own language, from the same box of text. I used it three ways: pre-rendered static clips for a bulletproof offline demo, direct browser-to-ElevenLabs calls for live bring-your-own-key TTS, and an optional server-side proxy. All of it on the free tier.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In memory of the victims of the August 2026 Nepal-Tibet floods.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>a11y</category>
      <category>elevenlabs</category>
    </item>
    <item>
      <title>My Dev.to CLI Got Its First Community PR. Image Uploads From Terminal.</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Thu, 03 Sep 2026 11:36:02 +0000</pubDate>
      <link>https://dev.to/sarvar_04/my-devto-cli-got-its-first-community-pr-image-uploads-from-terminal-4562</link>
      <guid>https://dev.to/sarvar_04/my-devto-cli-got-its-first-community-pr-image-uploads-from-terminal-4562</guid>
      <description>&lt;p&gt;Three weeks after launching devpub, I got a notification I wasn't expecting: a pull request from someone I'd never talked to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/HarishTeens" rel="noopener noreferrer"&gt;Harish&lt;/a&gt; / &lt;a class="mentioned-user" href="https://dev.to/harishteens"&gt;@harishteens&lt;/a&gt; had forked the repo, read the issues, picked one that I'd been putting off for weeks, and built a complete solution. Tests included. Design decisions documented. Edge cases handled.&lt;/p&gt;

&lt;p&gt;The feature? &lt;code&gt;devpub upload&lt;/code&gt;. The one command that should have existed from day one but couldn't, because the Forem API literally doesn't support it.&lt;br&gt;
&lt;/p&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;&lt;span class="nv"&gt;devpub&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.3.0
devpub upload cover.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one command now gives you this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Uploaded: cover.png

              Uploaded Images
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ File         ┃ URL                                       ┃
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ cover.png    │ https://dev-to-uploads.s3.amazonaws.com/… │
└──────────────┴──────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more opening dev.to/new in the browser just to drag an image and copy the URL.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The problem&lt;/li&gt;
&lt;li&gt;How devpub upload works&lt;/li&gt;
&lt;li&gt;The authentication problem&lt;/li&gt;
&lt;li&gt;What Harish built&lt;/li&gt;
&lt;li&gt;Try it&lt;/li&gt;
&lt;li&gt;What's next&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The problem: no image endpoint in the API
&lt;/h2&gt;

&lt;p&gt;When I launched devpub in v0.1, the goal was to replace the Dev.to web editor entirely. Write locally, push with one command, track analytics. Done.&lt;/p&gt;

&lt;p&gt;But there was a gap. Every time I wrote an article with diagrams or a cover image, I had to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open dev.to/new in the browser&lt;/li&gt;
&lt;li&gt;Click the image upload button&lt;/li&gt;
&lt;li&gt;Select the file&lt;/li&gt;
&lt;li&gt;Wait for upload&lt;/li&gt;
&lt;li&gt;Copy the URL&lt;/li&gt;
&lt;li&gt;Paste it into my local markdown&lt;/li&gt;
&lt;li&gt;Close the tab&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Seven steps for something that should be &lt;code&gt;devpub upload figure.png&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The reason I hadn't built this: &lt;strong&gt;the Forem API V1 has no image upload endpoint.&lt;/strong&gt; It doesn't exist. You can set &lt;code&gt;cover_image&lt;/code&gt; in article frontmatter, but only to a URL that already exists somewhere. The API cannot create that URL.&lt;/p&gt;

&lt;p&gt;I filed &lt;a href="https://github.com/simplynadaf/devpub/issues/11" rel="noopener noreferrer"&gt;issue #11&lt;/a&gt; with a note saying "this requires reverse-engineering the web editor's upload mechanism" and moved on to other features.&lt;/p&gt;

&lt;p&gt;Harish didn't move on. He figured it out.&lt;/p&gt;




&lt;h2&gt;
  
  
  How devpub upload works
&lt;/h2&gt;

&lt;p&gt;The Dev.to web editor uploads images to &lt;code&gt;POST /image_uploads&lt;/code&gt;. It's a multipart form submission, same as any file upload. But it's not authenticated with your API key. It uses your browser session.&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;# Upload a single image&lt;/span&gt;
devpub upload cover.png

&lt;span class="c"&gt;# Upload multiple images&lt;/span&gt;
devpub upload &lt;span class="k"&gt;*&lt;/span&gt;.png

&lt;span class="c"&gt;# Get ready-to-paste Markdown&lt;/span&gt;
devpub upload architecture.png &lt;span class="nt"&gt;--markdown&lt;/span&gt;
&lt;span class="c"&gt;# Output: ![architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--markdown&lt;/code&gt; flag is the one I use most. Write your article with &lt;code&gt;![](./figures/diagram.png)&lt;/code&gt;, then run &lt;code&gt;devpub upload figures/*.png --markdown&lt;/code&gt; and paste the output directly over your local references.&lt;/p&gt;




&lt;h2&gt;
  
  
  The authentication problem
&lt;/h2&gt;

&lt;p&gt;Here's why this feature sat undone for three weeks. The &lt;code&gt;/image_uploads&lt;/code&gt; endpoint requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;_Devto_Forem_Session&lt;/code&gt; cookie (your login session)&lt;/li&gt;
&lt;li&gt;A CSRF token (anti-forgery protection)&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;Origin&lt;/code&gt; header matching &lt;code&gt;https://dev.to&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your API key? Useless. This endpoint doesn't accept it.&lt;/p&gt;

&lt;p&gt;So devpub needs two extra credentials beyond your API key. They live in &lt;code&gt;.devpub/.env&lt;/code&gt; (which &lt;code&gt;devpub init&lt;/code&gt; gitignores by default):&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;DEVPUB_SESSION_COOKIE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;_Devto_Forem_Session&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;abc123...
&lt;span class="nv"&gt;DEVPUB_CSRF_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;a1b2c3d4...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run &lt;code&gt;devpub upload&lt;/code&gt; without setting these, it doesn't just fail with a cryptic error. It shows you exactly where to find them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;╭─ Session Credentials Required ─────────────────────────────╮
│                                                             │
│  devpub upload needs your browser session (not API key).    │
│                                                             │
│  1. Open https://dev.to/new in Chrome                       │
│  2. Press F12 → Application → Cookies → dev.to             │
│  3. Copy _Devto_Forem_Session value                         │
│  4. View page source → find &amp;lt;meta name="csrf-token"&amp;gt;       │
│  5. Add both to .devpub/.env                                │
│                                                             │
╰─────────────────────────────────────────────────────────────╯
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One deliberate design choice: these are &lt;strong&gt;login credentials&lt;/strong&gt;, not a scoped token. The &lt;code&gt;.env&lt;/code&gt; file is gitignored, and when they expire (you'll get a 401 or 403), you just re-copy them. devpub tells you that upfront rather than leaving you to guess why uploads suddenly stopped working.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Harish built
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/simplynadaf/devpub/pull/12" rel="noopener noreferrer"&gt;Harish's PR&lt;/a&gt; wasn't a quick hack. It was a proper module with clear boundaries:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/devpub/api/uploads.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ImageUploader&lt;/code&gt; class with all HTTP logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/devpub/cli/images.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Click command + Rich table output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tests/test_uploads.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;29 tests, all HTTP-mocked with respx&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three things stood out in his implementation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Fail before the network.&lt;/strong&gt; File existence, extension validation, and size check (25 MB limit) all happen before any HTTP request fires. A typo in a filename costs zero network round-trips.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Flexible response parsing.&lt;/strong&gt; Dev.to's upload response isn't documented, so the &lt;code&gt;_extract_url&lt;/code&gt; method handles every response shape that's been observed: &lt;code&gt;links.url&lt;/code&gt;, &lt;code&gt;image.url&lt;/code&gt;, &lt;code&gt;images[0]&lt;/code&gt;, a raw string. If none match, it raises with the full response body included. A wrong URL silently landing in your article would be worse than a loud error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Cookie flexibility.&lt;/strong&gt; You can paste the full cookie header (&lt;code&gt;a=1; b=2; _Devto_Forem_Session=xyz&lt;/code&gt;) or just the session value. Both work. Because copying one value out of DevTools is easy to get wrong.&lt;/p&gt;

&lt;p&gt;The PR description was thorough. Design rationale for keeping &lt;code&gt;ImageUploader&lt;/code&gt; separate from &lt;code&gt;DevtoClient&lt;/code&gt; (different auth models shouldn't share a class). Explicit call-out that tests pin the request shape, not the live response. A suggestion to smoke-test against a real session before release.&lt;/p&gt;

&lt;p&gt;This is what good open source contributions look like. Not just code that works, but code that explains &lt;em&gt;why it works that way&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other changes in v0.3.0
&lt;/h2&gt;

&lt;p&gt;Beyond the upload feature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fixed hardcoded User-Agent&lt;/strong&gt;: Was stuck at &lt;code&gt;devpub/0.1.0&lt;/code&gt;. Now reads the actual version from &lt;code&gt;importlib.metadata&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed empty API key edge case&lt;/strong&gt;: &lt;code&gt;DevtoClient(api_key='')&lt;/code&gt; used to fall through to environment variables silently. Now it raises.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo cleanup&lt;/strong&gt;: Removed personal draft articles from tracking, updated &lt;code&gt;.gitignore&lt;/code&gt; for research docs and recordings.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&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;&lt;span class="nv"&gt;devpub&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.3.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For uploads, you need the session credentials (one-time setup):&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;# Add to .devpub/.env&lt;/span&gt;
&lt;span class="nv"&gt;DEVPUB_SESSION_COOKIE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_session_cookie_here
&lt;span class="nv"&gt;DEVPUB_CSRF_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_csrf_token_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;devpub upload cover.png              &lt;span class="c"&gt;# Upload and get URL&lt;/span&gt;
devpub upload &lt;span class="k"&gt;*&lt;/span&gt;.png &lt;span class="nt"&gt;--markdown&lt;/span&gt;       &lt;span class="c"&gt;# Ready-to-paste Markdown tags&lt;/span&gt;
devpub upload diagram.png &lt;span class="nt"&gt;--dry-run&lt;/span&gt;  &lt;span class="c"&gt;# Validate without uploading&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full source: &lt;a href="https://github.com/simplynadaf/devpub" rel="noopener noreferrer"&gt;github.com/simplynadaf/devpub&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;Two follow-ups that Harish explicitly scoped out of his PR (smart -- ship the primitive first, then build on it):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Auto-rewrite during push&lt;/strong&gt;: &lt;code&gt;devpub push&lt;/code&gt; could detect local image paths like &lt;code&gt;![](./figures/arch.png)&lt;/code&gt;, upload them automatically, and rewrite the URLs in-place before publishing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cover image shortcut&lt;/strong&gt;: &lt;code&gt;devpub push -f article.md --cover photo.png&lt;/code&gt; to upload the image and set &lt;code&gt;cover_image&lt;/code&gt; in frontmatter in one step.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both become trivial now that the upload primitive exists.&lt;/p&gt;




&lt;h2&gt;
  
  
  Shoutout
&lt;/h2&gt;

&lt;p&gt;Big thanks to &lt;strong&gt;&lt;a href="https://github.com/HarishTeens" rel="noopener noreferrer"&gt;Harish&lt;/a&gt;&lt;/strong&gt; &lt;a class="mentioned-user" href="https://dev.to/harishteens"&gt;@harishteens&lt;/a&gt; for the first external contribution to devpub. The PR was clean, well-tested, and properly documented. If you're looking for an open-source project to contribute to, devpub has &lt;a href="https://github.com/simplynadaf/devpub/issues" rel="noopener noreferrer"&gt;open issues&lt;/a&gt; ranging from beginner-friendly to architecturally interesting.&lt;/p&gt;




&lt;p&gt;What's your image workflow for Dev.to articles? Drag-and-drop in the browser? Hosted on GitHub? Imgur? I'd like to know if &lt;code&gt;devpub upload&lt;/code&gt; fills a gap people feel, or if everyone's already solved this differently.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more on AWS architecture, DevOps, and AI Infrastructure:&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Portfolio&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://dev.to/sarvar_04"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://www.youtube.com/@TechwithSarvar" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="mailto:simplynadaf@gmail.com"&gt;Email&lt;/a&gt; | &lt;a href="https://builder.aws.com/community/@sarvar" rel="noopener noreferrer"&gt;AWS Builder Center&lt;/a&gt; | &lt;a href="https://x.com/SarvarN_04" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devto</category>
      <category>opensource</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Free AWS Certification for Students: The Complete Step-by-Step Guide</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Mon, 31 Aug 2026 11:42:29 +0000</pubDate>
      <link>https://dev.to/aws-builders/free-aws-certification-for-students-the-complete-step-by-step-guide-3m1m</link>
      <guid>https://dev.to/aws-builders/free-aws-certification-for-students-the-complete-step-by-step-guide-3m1m</guid>
      <description>&lt;p&gt;👋 Hey there! I'm &lt;strong&gt;Sarvar&lt;/strong&gt;, a Cloud Architect who spends most days building on AWS and Azure, and the rest writing about it. Last year I wrote a guide on getting a free AWS certification voucher. Around 85,000 of you read it. You can still see it here: &lt;a href="https://dev.to/aws-builders/unlocking-free-aws-certifications-your-step-by-step-guide-29nc"&gt;Unlocking Free AWS Certifications: Your Step-by-Step Guide&lt;/a&gt;. Then AWS closed that program (the old AWS Educate ETC route), and for the next year my comment section became a support desk. The same questions came up again and again: "my voucher got denied," "I don't see the reward," "when is it coming back?"&lt;/p&gt;

&lt;p&gt;It's back. And this version is better.&lt;/p&gt;

&lt;p&gt;On August 20, 2026, AWS launched &lt;strong&gt;Student Rewards on AWS Builder Center&lt;/strong&gt;. If you are a student, you get 12 months of premium training, up to $30 in AWS credits, and a &lt;strong&gt;$100 certification exam voucher&lt;/strong&gt;. Total value: &lt;strong&gt;$579&lt;/strong&gt;. Your cost: &lt;strong&gt;$0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No credit card. Ever. You never pay anything and you never enter a card. If you are a student, this is as close to free money for your career as it gets.&lt;/p&gt;

&lt;p&gt;This is the step-by-step guide I promised you last year. I have added a full tips-and-tricks section so you skip the mistakes that got hundreds of people stuck. Take a breath. This is doable, and I will walk you through every step. Let's go. 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  What do you get?
&lt;/h2&gt;

&lt;p&gt;When you verify that you are a student and finish your profile, rewards unlock in stages as you earn badges on Builder Center.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;th&gt;Reward&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Verified + profile complete&lt;/td&gt;
&lt;td&gt;12 months Skill Builder premium ($449 value)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 badges&lt;/td&gt;
&lt;td&gt;$10 AWS credits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14 badges&lt;/td&gt;
&lt;td&gt;$20 AWS credits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;21 badges&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$100 Foundational exam voucher&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A quick word on those &lt;strong&gt;AWS credits&lt;/strong&gt;: they are like store money for AWS. You use them to run real services (a small server, a database, some storage) while you practice, so you can learn hands-on without paying from your own pocket.&lt;/p&gt;

&lt;p&gt;That &lt;strong&gt;$100 voucher&lt;/strong&gt; covers the full price of one Foundational exam. You pay nothing for the exam.&lt;/p&gt;




&lt;h3&gt;
  
  
  Which exam should I pick?
&lt;/h3&gt;

&lt;p&gt;The voucher works for either of AWS's two beginner certifications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Certified Cloud Practitioner (CLF-C02):&lt;/strong&gt; pick this if you are new to cloud and want the classic, most-recognized starter cert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Certified AI Practitioner:&lt;/strong&gt; pick this if you are curious about AI and cloud together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are entry-level. Both are made for people with little or no hands-on experience. Both count for the voucher. If you are unsure, go with Cloud Practitioner. It is the one recruiters know best.&lt;/p&gt;

&lt;p&gt;One honest heads-up before you start: the credits come fast, the voucher takes longer. I explain why in the timing section, so read that before you plan. The good news is you get your first win in the first two weeks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who is eligible?
&lt;/h2&gt;

&lt;p&gt;Three conditions, all confirmed on the official Student Rewards page:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You are &lt;strong&gt;18 or older&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You are a student at an &lt;strong&gt;accredited higher education institution&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Your enrollment can be &lt;strong&gt;verified&lt;/strong&gt; (AWS uses a third-party service called SheerID for this).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The program is global, open to students at accredited institutions worldwide, subject to verification and local terms. A small number of countries are excluded by AWS voucher terms (Cuba, Iran, North Korea, Syria, Russia, and Crimea), so if you are there this offer will not apply.&lt;/p&gt;




&lt;h2&gt;
  
  
  How do I verify as a student?
&lt;/h2&gt;

&lt;p&gt;This is step one, and from my experience with the old program, it is where the first batch of people get stuck. AWS hands verification to &lt;strong&gt;SheerID&lt;/strong&gt;. You sign in with your Builder ID, give SheerID a few details, and it checks enrollment records.&lt;/p&gt;

&lt;p&gt;The official page says most verifications finish &lt;strong&gt;within minutes&lt;/strong&gt;. If SheerID needs a document from you, that review &lt;strong&gt;typically takes 24 to 48 hours&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You will provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your name&lt;/li&gt;
&lt;li&gt;Your college or school name&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;school email address&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the instant check needs backup, SheerID asks for a document showing your name, your school name, and a current enrollment date. Based on SheerID's own student FAQ, these work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;School ID card &lt;strong&gt;with an expiration date&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Class schedule&lt;/li&gt;
&lt;li&gt;Tuition receipt&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  💡 Tips for passing verification on the first try
&lt;/h3&gt;

&lt;p&gt;These come from watching people fail (and fix) verification on my last guide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Match your name to your school records, not your nickname.&lt;/strong&gt; If your university has you as "Mohammed A. Khan" and you type "Mo Khan," the automatic check can fail. In my experience this is the number one reason verification bounces.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use your school email.&lt;/strong&gt; The official page asks for it specifically, and it clears the instant check far more often than a personal Gmail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You are allowed to black out sensitive info&lt;/strong&gt; on uploaded documents. Cover your ID number or address. Keep your name, school name, and the date visible. SheerID says it deletes documents after review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify on your own device.&lt;/strong&gt; Shared or public machines that already ran a verification can trip an error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you hit an error, check your details for typos first&lt;/strong&gt;, then contact SheerID support. If the reward itself is the problem after you are verified, that is an AWS/Builder Center issue, not SheerID.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How do I earn the 21 badges?
&lt;/h2&gt;

&lt;p&gt;Here I need to be honest with you about sources. The official Student Rewards page says you earn badges by continuing to take actions on Builder Center: "Publish articles, comment, and maintain streaks." AWS publishes the full badge criteria in its own FAQ at builder.aws.com/faq#badges. What AWS does &lt;strong&gt;not&lt;/strong&gt; publish in a neat table is the exact duration of every streak.&lt;/p&gt;

&lt;p&gt;So the breakdown below combines what AWS confirms (badges exist, streaks exist, these actions count) with what students are actively reporting on their profiles and what I have pieced together tracking the program. Treat the exact day counts as community-reported, not gospel, and check the official FAQ for the current criteria.&lt;/p&gt;

&lt;p&gt;Badges fall into four kinds, and understanding the mix is the whole game.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-time badges (the quick wins):&lt;/strong&gt; completing onboarding, adding a profile photo, reading content, making your first post, submitting your first Wish (a Wish is a feature request you send to AWS through the Wishlist), and publishing your first article. These are the fastest badges you will earn, and you can grab most of them on day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Streak badges (the backbone):&lt;/strong&gt; sign-in, like, and comment streaks. Students report these come in 7-day, 30-day, and 90-day tiers, so the longest ones are what stretch the timeline. I explain how to handle them below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weekly streak badges:&lt;/strong&gt; voting on Wishes and publishing articles on a weekly cadence over several weeks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Community badges (the hard part):&lt;/strong&gt; these depend on other builders reacting to you. Getting replies on your comments, collecting likes across your comments, publishing articles that get engagement, and posting Wishes that gain traction. Because they rely on other people, they take the longest to control.&lt;/p&gt;




&lt;h3&gt;
  
  
  Where are the buttons? (the part that confuses everyone on day one)
&lt;/h3&gt;

&lt;p&gt;When you first open Builder Center, you will not know where anything is. That is normal. I got a hundred messages last year that were basically "I want to do it but I cannot find the button." So here is exactly where to click:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add your profile photo:&lt;/strong&gt; open your profile, add a photo, hit save. That alone unlocks your first big reward (12 months of Skill Builder). Do this first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your first post:&lt;/strong&gt; join a Space first (a Space is just a topic room, like a group chat for a subject), then create your post inside it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your first Wish:&lt;/strong&gt; go to the Wishlist, click &lt;strong&gt;+&lt;/strong&gt;, then &lt;strong&gt;Create Wish&lt;/strong&gt;, and write one small AWS idea you wish existed. Tip: look through the Wishlist first and support an existing Wish, AWS prefers that over a duplicate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your first article:&lt;/strong&gt; click &lt;strong&gt;+&lt;/strong&gt;, then &lt;strong&gt;Create Article&lt;/strong&gt;, write, then &lt;strong&gt;Preview → Publish&lt;/strong&gt;. It does not have to be long. A short honest post about what you learned this week is enough.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;New to all of this? Bookmark the official &lt;a href="https://builder.aws.com/content/2zcHXEQEQkxFslPN4dT0Qm2CSJA/your-guide-to-builder-center" rel="noopener noreferrer"&gt;Your Guide to Builder Center&lt;/a&gt;. It walks you through the platform in plain steps.&lt;/p&gt;




&lt;h3&gt;
  
  
  Watch your number go up
&lt;/h3&gt;

&lt;p&gt;Here is the part that keeps you motivated. You do not jump from 0 to 21 in one leap. You climb it, and every stage is a small win you can feel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finish the easy day-one badges → you are already at about &lt;strong&gt;6 / 21&lt;/strong&gt;. On your first day. That fast.&lt;/li&gt;
&lt;li&gt;Hold your daily habit for a week → around &lt;strong&gt;9 / 21&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add the weekly article and Wish-vote streaks → about &lt;strong&gt;11 / 21&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Keep going to 30 days → roughly &lt;strong&gt;14 / 21&lt;/strong&gt;, and by now your $10 and $20 credits have landed. 💰&lt;/li&gt;
&lt;li&gt;Build a little community engagement → around &lt;strong&gt;18 / 21&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Finish the long streaks near 90 days → &lt;strong&gt;21 / 21&lt;/strong&gt;. Voucher unlocked. 🎉&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See it? More than half your badges come in the first few weeks. The last stretch is just showing up. If you can open an app once a day, you can do this.&lt;/p&gt;




&lt;h2&gt;
  
  
  The timing trick nobody tells you
&lt;/h2&gt;

&lt;p&gt;First, the good news, because it matters most. &lt;strong&gt;You get real rewards fast.&lt;/strong&gt; The one-time badges take a day or two. Keep a small daily habit going and you hit 7 badges within your first couple of weeks, which unlocks your first $10 in credits. Reach 14 badges over the next couple of weeks and $20 more lands. So you are not waiting three months for your first reward. You get an early win almost right away, and that early win is what keeps you going.&lt;/p&gt;

&lt;p&gt;Now the part nobody explains, and it is based on how the streaks work rather than an official AWS statement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The streaks stack. They do not reset each stage.&lt;/strong&gt; Think of it as one simple daily habit: sign in, like a post, leave a real comment. That same habit feeds the 7-day, 30-day, AND 90-day streak badges all at once. You are not doing three separate things. You are doing one small thing every day, and it counts three times.&lt;/p&gt;

&lt;p&gt;Here is the honest truth about the $100 voucher: if the longest streak really is 90 days (which is what students are reporting), then the voucher takes about 90 days of showing up daily. There is no shortcut past a time-based streak. But that is fine, because your credits already arrived early, and 90 days is exactly the time you need to study anyway. More on that later.&lt;/p&gt;

&lt;p&gt;So the plan is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Day 1:&lt;/strong&gt; grab all the one-time badges, and start your daily habit (sign in, one like, one comment). That is it for today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First 2 to 4 weeks:&lt;/strong&gt; you clear the short streaks and weekly streaks. $10 credit at 7 badges, $20 at 14 badges. Your first wins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Around 90 days:&lt;/strong&gt; the long streaks finish, you cross 21 badges, and the voucher unlocks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The single most important move: start the daily habit on day one. Everything else follows from that.&lt;/p&gt;




&lt;h3&gt;
  
  
  🎯 More badge tips and tricks
&lt;/h3&gt;

&lt;p&gt;Based on my experience running the last program and watching how this one behaves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Set a phone alarm for the daily three.&lt;/strong&gt; Sign in, one like, one comment. Ten minutes a day. On a long streak, one missed day is expensive, so protect the habit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For article streaks, keep them small.&lt;/strong&gt; A few short, honest articles beat one giant post you never finish. Write about what you learned in your course that week. That is enough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comment with substance, not "nice post."&lt;/strong&gt; The community badges need other people to like and reply to you. A real question at the end of your comment earns replies. "Great article" gets ignored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the Wishlist before posting a Wish.&lt;/strong&gt; AWS suggests commenting on an existing Wish rather than duplicating one. Better engagement, and it still counts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start the community badges early.&lt;/strong&gt; They depend on other builders, which you do not control, so give them the most runway.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How do I claim and use the voucher?
&lt;/h2&gt;

&lt;p&gt;Once you hit 21 badges, the voucher appears in your rewards. From my last guide's comment section, this is where the real confusion lived, so let me pre-answer it.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚠️ The mistakes that got people stuck last time
&lt;/h3&gt;

&lt;p&gt;I answered these same questions dozens of times in my last guide's comments. Here is what I learned, so you skip the queue.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"My reward isn't showing after I hit the milestone."&lt;/strong&gt; I lost count of how many people panicked here. The dashboard lags. Last year it took two to three days to update for most people. Wait it out before you open a ticket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"The voucher got denied."&lt;/strong&gt; One reader tried three times in a row and kept failing. The fix was almost always the same: their training account email did not match their program email, so their progress was split across two profiles. Confirm you are using one account, one email, everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"I have an existing AWS training account and can't link it."&lt;/strong&gt; A reader hit a 500 error every time he tried to reset his password to link accounts. This is a known issue. Reset your password at the AWS Training and Certification portal first, then link. If the reset still errors, use a different email or contact support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not let the voucher expire.&lt;/strong&gt; This is the one that hurts most. You will have done months of work to earn it. AWS exam vouchers carry a redemption deadline, and yours will state its own expiry date when it lands. Book your exam that same day. From my experience, people grind for months and then lose the voucher to the clock. Do not be that person.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To book: take the voucher code into the AWS Certification portal, schedule the CLF-C02 or AI Practitioner exam, and apply the code at checkout. Cost drops to $0.&lt;/p&gt;




&lt;h2&gt;
  
  
  Turn the voucher into a real certificate, for free
&lt;/h2&gt;

&lt;p&gt;A voucher is not a certificate. You still have to pass the exam. Here is the best part, though: &lt;strong&gt;the same program already gave you 12 months of premium Skill Builder the day you verified.&lt;/strong&gt; That is your study material. You do not need to buy a single course or pay for anything.&lt;/p&gt;

&lt;p&gt;And do not be scared of the exam. &lt;strong&gt;Cloud Practitioner is the easiest AWS certification.&lt;/strong&gt; It is built for people with zero hands-on experience. It tests whether you understand cloud basics, not whether you can build complex systems. Most people pass with three to four weeks of steady study. If you have been doing your daily badge habit, you already have the time built in.&lt;/p&gt;

&lt;p&gt;The exam (CLF-C02) is 65 questions, 90 minutes, and you pass at 700 out of 1000. It covers four areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Technology and Services (34%)&lt;/li&gt;
&lt;li&gt;Security and Compliance (30%)&lt;/li&gt;
&lt;li&gt;Cloud Concepts (24%)&lt;/li&gt;
&lt;li&gt;Billing, Pricing, and Support (12%)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  📚 A free 4-week study plan from your Skill Builder access
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Weeks 1 to 2:&lt;/strong&gt; work through the official CLF-C02 exam prep course in Skill Builder. Start with Security and Cloud Technology, since together they are 64% of the exam.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 3:&lt;/strong&gt; do the hands-on labs your premium access unlocks. Clicking around S3, EC2, and IAM yourself sticks far better than reading about them. Use your free credits here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 4:&lt;/strong&gt; take the official practice question set. If you score above 75% a few times in a row, book the real exam. You are ready.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice the rhythm: the badges take around 90 days, and that is also plenty of time to study. Do both together. Study a little while you keep your daily streak alive. By the time badge 21 hits, you are exam-ready and the exam costs you nothing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why bother? What this does for you
&lt;/h2&gt;

&lt;p&gt;Let me be clear about why 90 days of small daily effort is worth it. This certificate goes on your CV and your LinkedIn. Recruiters search for it. For a student with no work experience yet, an AWS certification is proof you can learn cloud, and cloud skills are in demand everywhere. You walk out with a real, verifiable credential that companies recognize, and it cost you nothing but consistency.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Last year, 80,000 people wanted a free AWS certification. The program they used closed. This one replaced it, and it gives you more: training, credits, and the voucher, all free for students.&lt;/p&gt;

&lt;p&gt;The whole path costs nothing but time and showing up. Verify today. Start your daily habit today. Study a little each week while the badges add up. In about 90 days you can sit a free exam already prepared, and walk away certified.&lt;/p&gt;

&lt;p&gt;You can do this. Thousands of students are doing it right now, and so can you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your turn: which exam are you going for, Cloud Practitioner or AI Practitioner? Drop it in the comments and tell me what day of your streak you are on. I reply to comments, and we can keep each other going.&lt;/strong&gt; 👇&lt;/p&gt;

&lt;p&gt;Happy learning. 🚀&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more on AWS architecture, DevOps, and AI Infrastructure:&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Portfolio&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://dev.to/sarvar_04"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://www.youtube.com/@sarvar-nadaf" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="mailto:simplynadaf@gmail.com"&gt;Email&lt;/a&gt; | &lt;a href="https://builder.aws.com/community/@sarvar" rel="noopener noreferrer"&gt;AWS Builder Center&lt;/a&gt; | &lt;a href="https://x.com/SarvarN_04" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>beginners</category>
      <category>career</category>
      <category>certification</category>
    </item>
    <item>
      <title>NexPath Review: The Prompt Quality Layer for Cursor, Windsurf and Claude Code</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Thu, 27 Aug 2026 13:21:53 +0000</pubDate>
      <link>https://dev.to/sarvar_04/nexpath-review-the-prompt-quality-layer-for-cursor-windsurf-and-claude-code-353n</link>
      <guid>https://dev.to/sarvar_04/nexpath-review-the-prompt-quality-layer-for-cursor-windsurf-and-claude-code-353n</guid>
      <description>&lt;p&gt;I've been building &lt;a href="https://github.com/simplynadaf/devpub" rel="noopener noreferrer"&gt;devpub&lt;/a&gt;, an open-source CLI that publishes and tracks articles on Dev.to. Last week I was in Cursor, adding a new analytics feature - full vibe coding mode, rapid-fire prompts, one after another:&lt;/p&gt;

&lt;p&gt;"add caching to the API client."&lt;/p&gt;

&lt;p&gt;"fix the rate limiter."&lt;/p&gt;

&lt;p&gt;"make the analytics faster."&lt;/p&gt;

&lt;p&gt;Three prompts, three pieces of code generated instantly. I moved on. Two days later I realized the "fix" had silently broken my retry logic, the "caching" had no invalidation strategy, and "faster" meant the agent had removed the safety throttle that prevents Dev.to from banning my API key.&lt;/p&gt;

&lt;p&gt;None of those prompts said what shouldn't change. None specified how I'd know it worked. I wrote them in flow, and the agent did exactly what I asked. Which wasn't what I meant.&lt;/p&gt;

&lt;p&gt;That's when I tried NexPath, a prompt quality layer for AI coding agents. It sits between you and your agent, catches vague prompts at the moment you submit them, and offers a stronger version. I thought: why not try this on devpub? A real codebase I know inside out, with real prompts I'd actually type. If it works here, it works anywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The vibe coding pattern nobody talks about&lt;/li&gt;
&lt;li&gt;What if something caught you before you hit Enter?&lt;/li&gt;
&lt;li&gt;What the enhanced version includes&lt;/li&gt;
&lt;li&gt;My hands-on experience with NexPath&lt;/li&gt;
&lt;li&gt;NexPath agent support: Cursor, Windsurf and Claude Code&lt;/li&gt;
&lt;li&gt;Who NexPath is for: Cursor, Windsurf and Claude Code users&lt;/li&gt;
&lt;li&gt;What NexPath costs&lt;/li&gt;
&lt;li&gt;Bottom line&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The vibe coding pattern nobody talks about
&lt;/h2&gt;

&lt;p&gt;Every developer using AI coding agents has a version of this story. Not because the agents are bad. They're incredibly good at generating code from whatever you give them. The problem is what we give them.&lt;/p&gt;

&lt;p&gt;"Fix this." "Make it work." "Clean up the code." "Add auth."&lt;/p&gt;

&lt;p&gt;These prompts feel productive. The agent responds instantly. Code appears. You move to the next thing. But six prompts later, your codebase has grown in directions you didn't plan, with assumptions you didn't state, skipping checks you didn't ask for.&lt;/p&gt;

&lt;p&gt;The same mistakes repeat: no acceptance criteria, no rollback plan, no mention of what shouldn't change. Not because we don't know better. Because momentum makes it easy to skip.&lt;/p&gt;

&lt;p&gt;The answer, I thought, was more discipline. Be better at prompting. Write longer, more detailed requests every time.&lt;/p&gt;

&lt;p&gt;That lasted about three days.&lt;/p&gt;




&lt;h2&gt;
  
  
  What if something caught you before you hit Enter?
&lt;/h2&gt;

&lt;p&gt;NexPath is not another coding agent. It doesn't generate code. It doesn't replace your agent. It doesn't try to be clever. It sits between you and your AI agent, and when you submit a vague prompt, it holds it for a second and says: "Here's a stronger version of what you meant. Want to use it instead?"&lt;/p&gt;

&lt;p&gt;The simplest way I'd describe it: "NexPath is the thing that keeps AI-generated code from becoming a mess."&lt;/p&gt;

&lt;p&gt;The workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You write your prompt in Cursor, Windsurf, or Claude Code&lt;/li&gt;
&lt;li&gt;NexPath intercepts it at submit time&lt;/li&gt;
&lt;li&gt;A popup shows your original alongside an enhanced version&lt;/li&gt;
&lt;li&gt;You pick which one gets sent&lt;/li&gt;
&lt;li&gt;The chosen version auto-submits to your agent&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your original intent stays visible throughout. Nothing auto-sends without your approval. If a prompt doesn't need enhancement, NexPath stays silent.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the enhanced version includes
&lt;/h2&gt;

&lt;p&gt;When the enhancement fires, it doesn't rewrite your prompt. It wraps your original request with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scope boundaries&lt;/strong&gt;: what should change, what shouldn't&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acceptance criteria&lt;/strong&gt;: how you'll know it worked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification steps&lt;/strong&gt;: tests to run after&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety requirements&lt;/strong&gt;: rollback plan for risky operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sequencing&lt;/strong&gt;: if the task is complex, break it into ordered steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a real example from my devpub testing. I typed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fix the rate limiter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NexPath enhanced it to something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fix the rate limiter in DevtoClient._throttle(). Scope: only modify the timestamp tracking logic in src/devpub/api/devto.py. Do not change RATE_LIMIT_REQUESTS or RATE_LIMIT_WINDOW constants. Do not modify the retry logic in _request(). Acceptance: 30 requests per 30-second window still enforced, no sleep longer than 30s. Verify: run pytest tests/test_api.py after changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's what I should have written in the first place. But I didn't, because I was in flow.&lt;/p&gt;

&lt;p&gt;Another one. I typed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;push all drafts to dev.to as published
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NexPath flagged the risk and added:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Push all draft articles to Dev.to with published=true. WARNING: This is a destructive action. Published articles are immediately visible to readers and cannot be easily unpublished. Scope: only modify the &lt;code&gt;published&lt;/code&gt; field in article payloads. Safety: list all affected articles first and confirm count before proceeding. Rollback: note all article IDs changed so they can be reverted to draft if needed. Verify: check each article URL returns 200 after publishing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The difference between "just do it" and "do it carefully," surfaced at exactly the right moment.&lt;/p&gt;




&lt;h2&gt;
  
  
  My hands-on experience with NexPath
&lt;/h2&gt;

&lt;p&gt;I tested NexPath in two environments: Cursor on my laptop for the popup experience with devpub, and Claude Code on my EC2 server to stress-test the CLI and dig into the internals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation (2-3 minutes, clean)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/hi0001234d/nexpath.git
&lt;span class="nb"&gt;cd &lt;/span&gt;nexpath
npm &lt;span class="nb"&gt;install&lt;/span&gt;        &lt;span class="c"&gt;# 16 seconds, 298 packages&lt;/span&gt;
npm run build      &lt;span class="c"&gt;# Build + 1,175 test validation&lt;/span&gt;
npm &lt;span class="nb"&gt;link
&lt;/span&gt;nexpath &lt;span class="nb"&gt;install&lt;/span&gt;    &lt;span class="c"&gt;# Auto-detected my agents, wrote hooks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;nexpath status&lt;/code&gt; command gives you a complete picture: prompt store stats, hook activity, config state, environment detection. The level of observability in the CLI surprised me. Structured JSON logs, proper error codes, debuggable output.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I liked
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Privacy holds up.&lt;/strong&gt; Everything lives in &lt;code&gt;~/.nexpath/&lt;/code&gt;. A SQLite database stores your prompts locally. The only outbound calls are to OpenAI's API (GPT-4o-mini for classification). Telemetry is disabled by default, confirmed in config. Secret redaction strips API keys from stored prompts automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The engineering is solid.&lt;/strong&gt; 1,803 commits from a team of three. 1,175 tests in the VS Code extension alone. Structured logging. Environment detection (OS, WSL, CI, devcontainer). Proper config system with keychain integration. This is not a weekend hackathon project abandoned after the demo, even though it started at one (AI Hackfest 2026 by MLH).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It knows when to shut up.&lt;/strong&gt; The system classifies your prompts into development stages (idea, architecture, implementation, testing, etc.) and only fires when it detects a transition or an absence signal: a missing spec, a skipped test strategy, a risky shortcut. When your prompts are already well-structured, it stays out of the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The popup experience just works.&lt;/strong&gt; In Cursor, you type your prompt, hit Enter, and NexPath holds it for a beat. A popup appears showing your original alongside the enhanced version. You pick one, it auto-submits. No context switch, no copy-paste, no extra windows. It feels like a natural part of the workflow, not an interruption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment awareness is thorough.&lt;/strong&gt; The &lt;code&gt;nexpath env&lt;/code&gt; command probes your OS, detects WSL, devcontainers, CI pipelines, shell type, project framework, version control, test runner, and deploy config, all locally. It uses this context to calibrate when and how it intervenes. That level of situational awareness is rare in developer tooling.&lt;/p&gt;

&lt;h3&gt;
  
  
  What needs work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;API key handling has a rough edge.&lt;/strong&gt; NexPath requires an OpenAI API key (for GPT-4o-mini). Their docs say it falls back gracefully to local classification when no key is available. In practice, after the first prompt, subsequent calls throw an unhandled &lt;code&gt;OpenAIError: Missing credentials&lt;/code&gt; exception instead of degrading silently. It's a v1 edge case, easily fixable, but worth knowing if you're setting up on a fresh machine without a key configured yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CLI advisory and the VS Code popup are different systems.&lt;/strong&gt; The submit-time popup (Cursor/Windsurf) is the primary product. It intercepts every prompt at the moment you press Enter. The CLI advisory for Claude Code is a different mechanism that builds up session history before intervening. In my CLI stress test, it captured 18 prompts and intervened zero times because it needs longer session context to detect meaningful transitions. The popup experience doesn't have this limitation. It evaluates each prompt independently. If you're on Claude Code, expect a quieter experience than the Cursor/Windsurf popup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single LLM provider for now.&lt;/strong&gt; It currently uses &lt;code&gt;gpt-4o-mini&lt;/code&gt; as the classification model, a reasonable v1 tradeoff that limits flexibility for teams using other providers. The API costs are tiny (pennies per day), but multi-provider support would make it more accessible to teams with existing Anthropic or Groq setups.&lt;/p&gt;




&lt;h2&gt;
  
  
  NexPath agent support: Cursor, Windsurf and Claude Code
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Status (Aug 2026)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Supported via CLI + MCP hooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cursor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Supported via VS Code extension (submit-time popup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Windsurf / Devin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Supported via VS Code extension (submit-time popup)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The VS Code extension (for Cursor and Windsurf) intercepts prompts at submit time and shows the popup inline. For Claude Code, it works through the CLI's hook system, firing between prompt submissions.&lt;/p&gt;

&lt;p&gt;Important distinction: the Cursor/Windsurf experience is the polished one. You type, hit Enter, NexPath catches it, shows a popup, you pick, it sends. The Claude Code experience works through terminal hooks, which is less visual but functional.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who NexPath is for: Cursor, Windsurf and Claude Code users
&lt;/h2&gt;

&lt;p&gt;NexPath makes sense if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt in short bursts ("fix this", "add that") and want guardrails without slowing down&lt;/li&gt;
&lt;li&gt;Work on production codebases where a vague prompt can cause real damage&lt;/li&gt;
&lt;li&gt;Want prompt discipline without having to be disciplined every single time&lt;/li&gt;
&lt;li&gt;Use Cursor or Windsurf as your primary agent environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's less useful if you already write detailed, structured prompts consistently, or if you're building throwaway prototypes where quality doesn't matter.&lt;/p&gt;




&lt;h2&gt;
  
  
  What NexPath costs
&lt;/h2&gt;

&lt;p&gt;NexPath itself is free (Apache 2.0, open source). The only cost is your OpenAI API key usage for GPT-4o-mini. In a typical coding session, that's $0.01 to $0.05 per day. Negligible, but not zero.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;NexPath solves a problem I have: I write lazy prompts when I'm in flow, and those lazy prompts produce code that bites me later. The idea of a quality layer that catches me at the moment of submission, not after the damage is done, is genuinely useful.&lt;/p&gt;

&lt;p&gt;I tested it on devpub, my own open-source project with a real API client, real rate limiting, real push-to-production workflows. The prompts I'd normally fire off ("fix the rate limiter", "push all drafts as published") came back stronger, scoped, and safe. That's the value.&lt;/p&gt;

&lt;p&gt;The implementation on Cursor/Windsurf (submit-time popup, choose your version, auto-submit) is well-designed. The CLI experience for Claude Code needs more polish. The engineering underneath is serious, the privacy model is honest, and the team ships fast (20+ PRs merged in the 48 hours before launch).&lt;/p&gt;

&lt;p&gt;Is it perfect? No. The API key handling has a rough edge. The single-provider model limits flexibility. The CLI advisory needs longer sessions to activate. But for a v0.1.4 open-source tool from a three-person team, it's solving the right problem in the right place, and the Cursor/Windsurf popup experience is genuinely well-executed.&lt;/p&gt;

&lt;p&gt;I'll keep it installed. The first time it catches a dangerous prompt I would have sent unthinking, it pays for itself.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Try it yourself:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/hi0001234d/nexpath" rel="noopener noreferrer"&gt;NexPath on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VS Code Marketplace: &lt;a href="https://marketplace.visualstudio.com/items?itemName=nexpath.nexpath-vscode" rel="noopener noreferrer"&gt;NexPath VS Code extension for Cursor and Windsurf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Open VSX: &lt;a href="https://open-vsx.org/extension/nexpath/nexpath-vscode" rel="noopener noreferrer"&gt;NexPath on Open VSX&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo: &lt;a href="https://youtu.be/pNejtPA5DPU" rel="noopener noreferrer"&gt;Prompt Enhancement in action&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;NexPath is running a Launch Feedback Challenge&lt;/strong&gt; (ends Sep 2, 2026). They're looking for honest feedback, not praise. If you try it and have opinions, good or bad, share them at &lt;a href="https://github.com/hi0001234d/nexpath/discussions/94" rel="noopener noreferrer"&gt;their discussion thread&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your approach to prompt quality? Do you write detailed prompts every time, or do you also fall into the "fix this" trap? Let me know in the comments.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Follow me for more on AWS architecture, DevOps, and AI Infrastructure:&lt;br&gt;
&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Portfolio&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://dev.to/sarvar_04"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://www.youtube.com/@TechwithSarvar" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="mailto:simplynadaf@gmail.com"&gt;Email&lt;/a&gt; | &lt;a href="https://builder.aws.com/community/@sarvar" rel="noopener noreferrer"&gt;AWS Builder Center&lt;/a&gt; | &lt;a href="https://x.com/SarvarN_04" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>showdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Built an AWS DevOps AI Agent Using Kiro Crew + MCP</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Mon, 24 Aug 2026 11:30:54 +0000</pubDate>
      <link>https://dev.to/aws-builders/i-built-an-aws-devops-ai-agent-using-kiro-crew-mcp-fk0</link>
      <guid>https://dev.to/aws-builders/i-built-an-aws-devops-ai-agent-using-kiro-crew-mcp-fk0</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/SPIIJ-dLN1E" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;p&gt;At 3:17 AM last Tuesday, my payment-api ECS service entered a terminal failure loop. 7,279 failed tasks since August 13th. The health check expected &lt;code&gt;/api/health&lt;/code&gt; but the container only served static content. Every 60 seconds, ECS killed the task and replaced it. Burning compute the entire time.&lt;/p&gt;

&lt;p&gt;No alarm fired. I never set one up. No PagerDuty page. No Slack alert. Just a service churning through resources that nobody was watching.&lt;/p&gt;

&lt;p&gt;I slept through the whole thing. And woke up to a solved problem.&lt;/p&gt;

&lt;p&gt;Not because I got lucky. Because my Kiro Crew agent was awake. It spawned 5 parallel investigations, called AWS DevOps Agent for a health assessment, found the root cause across ECS, CodeBuild, CodePipeline, and Lambda, and flagged everything with severity-prioritized fixes. By 3:24 AM, done.&lt;/p&gt;

&lt;p&gt;This is Part 6 of my Kiro Crew series. Parts 1 to 5 showed what Crew can do: orchestrate agents, run cron jobs, enforce security, build custom apps. This one shows what happens when you connect it to AWS's production intelligence engine.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Watch the 12-min demo above to see the full autonomous investigation in action.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The problem nobody talks about&lt;/li&gt;
&lt;li&gt;What AWS DevOps Agent actually is&lt;/li&gt;
&lt;li&gt;The integration: one MCP config block&lt;/li&gt;
&lt;li&gt;The architecture&lt;/li&gt;
&lt;li&gt;Live demo: what the agent found&lt;/li&gt;
&lt;li&gt;Applying the fixes&lt;/li&gt;
&lt;li&gt;The cron job: check every 30 minutes&lt;/li&gt;
&lt;li&gt;The investigate skill: deep root-cause analysis&lt;/li&gt;
&lt;li&gt;The security model: why this is safe&lt;/li&gt;
&lt;li&gt;The self-learning layer&lt;/li&gt;
&lt;li&gt;34 tools at your agent's fingertips&lt;/li&gt;
&lt;li&gt;Cost considerations&lt;/li&gt;
&lt;li&gt;Try it yourself (complete walkthrough)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The problem nobody talks about
&lt;/h2&gt;

&lt;p&gt;Every DevOps team I've worked with has the same gap: the space between "something went wrong" and "someone noticed."&lt;/p&gt;

&lt;p&gt;PagerDuty fires when alarms trigger. But what about the things you never set alarms for? The ECS service silently cycling through failed tasks for four days straight. The CodeBuild project that's been FAILED since last week with nobody looking at it. The Lambda with a 3-second timeout calling a service that needs 6 seconds to respond.&lt;/p&gt;

&lt;p&gt;These aren't incidents. They're slow leaks. And they only become incidents when a customer complains or the bill arrives.&lt;/p&gt;

&lt;p&gt;I've seen this pattern across a dozen client engagements. The monitoring is always incomplete. The alarms cover the obvious cases. The subtle failures accumulate silently until something visible breaks.&lt;/p&gt;

&lt;h3&gt;
  
  
  The enterprise reality today
&lt;/h3&gt;

&lt;p&gt;Here's what incident response looks like at most organizations I've consulted for:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Who&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alert fires&lt;/td&gt;
&lt;td&gt;PagerDuty/OpsGenie&lt;/td&gt;
&lt;td&gt;0 min&lt;/td&gt;
&lt;td&gt;Only works if alarm exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engineer wakes up&lt;/td&gt;
&lt;td&gt;On-call human&lt;/td&gt;
&lt;td&gt;5-15 min&lt;/td&gt;
&lt;td&gt;Context switch, fatigue, stress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Login to console&lt;/td&gt;
&lt;td&gt;Human&lt;/td&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;td&gt;MFA, VPN, finding the right account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check CloudWatch&lt;/td&gt;
&lt;td&gt;Human&lt;/td&gt;
&lt;td&gt;10 min&lt;/td&gt;
&lt;td&gt;Which metrics? Which log group? Which time window?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correlate signals&lt;/td&gt;
&lt;td&gt;Human&lt;/td&gt;
&lt;td&gt;15-30 min&lt;/td&gt;
&lt;td&gt;Was there a deployment? Config change? Upstream issue?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identify root cause&lt;/td&gt;
&lt;td&gt;Human&lt;/td&gt;
&lt;td&gt;15-60 min&lt;/td&gt;
&lt;td&gt;Experience-dependent, often wrong first guess&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write fix&lt;/td&gt;
&lt;td&gt;Human&lt;/td&gt;
&lt;td&gt;10-30 min&lt;/td&gt;
&lt;td&gt;Under pressure, at 3 AM, with fatigue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apply + verify&lt;/td&gt;
&lt;td&gt;Human&lt;/td&gt;
&lt;td&gt;10 min&lt;/td&gt;
&lt;td&gt;Hope it doesn't make things worse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total MTTR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1-3 hours&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;And that's IF an alarm existed&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The real killer: &lt;strong&gt;if no alarm was configured, this entire process never starts.&lt;/strong&gt; The failure just accumulates until someone notices manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  How our approach changes this
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Who&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cron fires (every 30 min)&lt;/td&gt;
&lt;td&gt;Kiro Crew&lt;/td&gt;
&lt;td&gt;0 min&lt;/td&gt;
&lt;td&gt;No alarm needed, proactive scanning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check all services&lt;/td&gt;
&lt;td&gt;Crew + DevOps Agent&lt;/td&gt;
&lt;td&gt;30 sec&lt;/td&gt;
&lt;td&gt;Parallel, covers everything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correlate signals&lt;/td&gt;
&lt;td&gt;DevOps Agent&lt;/td&gt;
&lt;td&gt;60-90 sec&lt;/td&gt;
&lt;td&gt;X-Ray, CloudWatch, deployments, topology&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identify root cause&lt;/td&gt;
&lt;td&gt;DevOps Agent&lt;/td&gt;
&lt;td&gt;2-3 min&lt;/td&gt;
&lt;td&gt;Consistent, no fatigue, no wrong guesses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generate mitigation plan&lt;/td&gt;
&lt;td&gt;DevOps Agent&lt;/td&gt;
&lt;td&gt;90 sec&lt;/td&gt;
&lt;td&gt;Exact CLI commands, rollback steps included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apply fixes&lt;/td&gt;
&lt;td&gt;Kiro Crew&lt;/td&gt;
&lt;td&gt;30 sec&lt;/td&gt;
&lt;td&gt;Or: open PR for human review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verify healthy&lt;/td&gt;
&lt;td&gt;Kiro Crew&lt;/td&gt;
&lt;td&gt;30 sec&lt;/td&gt;
&lt;td&gt;Automated validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total MTTR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5-7 minutes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No human woken up&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The architectural insight
&lt;/h3&gt;

&lt;p&gt;AWS designed DevOps Agent as a &lt;strong&gt;read-only investigator&lt;/strong&gt;. It observes, correlates, and produces mitigation plans with exact commands. But it never executes anything. That's intentional (security: no prompt injection risk from write operations).&lt;/p&gt;

&lt;p&gt;Kiro Crew fills that gap. It takes DevOps Agent's mitigation plan and executes it (or opens a PR for human approval in production).&lt;/p&gt;

&lt;p&gt;The separation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DevOps Agent = the brain&lt;/strong&gt; (read-only, investigates, produces exact fix commands)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kiro Crew = the hands&lt;/strong&gt; (orchestrates, executes, verifies, learns from past incidents)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither alone solves the problem. Together: autonomous incident response that runs 24/7, catches issues before customers notice, and gets smarter with every incident.&lt;/p&gt;

&lt;h3&gt;
  
  
  What enterprises gain
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No more silent failures.&lt;/strong&gt; Cron catches issues whether or not alarms exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent investigation quality.&lt;/strong&gt; DevOps Agent doesn't get tired at 3 AM or skip steps under pressure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;75% lower MTTR.&lt;/strong&gt; AWS reports 75% reduction in customers using DevOps Agent. Adding Crew automation pushes it further.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Institutional memory.&lt;/strong&gt; Crew's Knowledge base remembers every past incident. New team members inherit years of operational wisdom.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit trail by default.&lt;/strong&gt; Every investigation, every fix, every decision logged in CloudTrail and Crew sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-in-the-loop when you want it.&lt;/strong&gt; Trust mode for non-critical environments, PR approval for production.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What if an agent checked for you? Every 30 minutes. Autonomously. While you sleep, eat dinner, or take your kid to the park.&lt;/p&gt;




&lt;h2&gt;
  
  
  What AWS DevOps Agent actually is
&lt;/h2&gt;

&lt;p&gt;AWS DevOps Agent went GA in March 2026. Think of it as an always-on SRE that knows your AWS infrastructure intimately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it monitors:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon ECS (services, tasks, deployments, health checks)&lt;/li&gt;
&lt;li&gt;AWS Lambda (invocations, errors, duration, throttles, cold starts)&lt;/li&gt;
&lt;li&gt;Amazon API Gateway (5xx errors, latency, integration failures)&lt;/li&gt;
&lt;li&gt;Amazon RDS (connections, CPU, storage, replication lag)&lt;/li&gt;
&lt;li&gt;Amazon DynamoDB (throttles, capacity, latency)&lt;/li&gt;
&lt;li&gt;Amazon EC2 (status checks, CPU, network)&lt;/li&gt;
&lt;li&gt;Amazon S3 (error rates, request patterns)&lt;/li&gt;
&lt;li&gt;Amazon CloudWatch (alarms, metrics, anomalies)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What it does with that data:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discovers and maps&lt;/strong&gt; your service topology automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlates signals&lt;/strong&gt; across metrics, traces, deployments, and configuration changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Investigates incidents&lt;/strong&gt; with deep async root-cause analysis (takes 5 to 8 minutes per investigation)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generates recommendations&lt;/strong&gt; with specific, actionable mitigations prioritized by severity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviews releases&lt;/strong&gt; by checking pull requests for production risk patterns before they ship&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The feature that makes this article possible: DevOps Agent exposes all of this over &lt;strong&gt;MCP&lt;/strong&gt; (Model Context Protocol). That means any MCP-compatible client can call its 34 tools programmatically. Including Kiro Crew.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP Endpoint:&lt;/strong&gt; &lt;code&gt;https://connect.aidevops.{region}.api.aws/mcp&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A2A Endpoint (agent-to-agent):&lt;/strong&gt; &lt;code&gt;https://connect.aidevops.{region}.api.aws/a2a/*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supported Regions:&lt;/strong&gt; us-east-1, us-west-2, eu-west-1 (as of August 2026)&lt;/p&gt;




&lt;h2&gt;
  
  
  The integration: one MCP config block
&lt;/h2&gt;

&lt;p&gt;Connecting DevOps Agent to Kiro Crew takes one config block. I did this live in the terminal during the demo. Before adding it, I had 9 MCP servers configured in Crew. After: 10.&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;json&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&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;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/home/ubuntu/.kiro/settings/mcp.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;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mcpServers&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;aws-devops-agent&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="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url&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;https://connect.aidevops.us-east-1.api.aws/mcp&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;headers&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;X-Agent-Space-Id&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;7ab2314d-15e8-4744-a1a7-3f96692fcd83&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;description&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;AWS DevOps Agent (34 tools)&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;disabled&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="p"&gt;}&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;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/home/ubuntu/.kiro/settings/mcp.json&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;w&lt;/span&gt;&lt;span class="sh"&gt;'&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="c1"&gt;# Output: ✅ Added aws-devops-agent (10 total servers)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I verified the connection by calling &lt;code&gt;tools/list&lt;/code&gt; against the MCP endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tools available:
  • get_service
  • list_agent_spaces
  • get_agent_space
  • create_agent_space
  • update_agent_space
  • create_access_token
  • get_access_token
  • list_access_tokens
  • revoke_access_token
  ... and 24 more

Endpoint: https://connect.aidevops.us-east-1.api.aws/mcp
Space:    7ab2314d-15e8-4744-a1a7-3f96692fcd83
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;34 tools. Live connection confirmed. Your Crew agent can now call any of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternative: SigV4 authentication (recommended for production)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you don't want to manage bearer tokens, use AWS SigV4 via &lt;code&gt;mcp-proxy-for-aws&lt;/code&gt;:&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;"aws-devops-agent"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"mcp-proxy-for-aws@latest"&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://connect.aidevops.us-east-1.api.aws/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aidevops"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--region"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&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;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;p&gt;This uses your existing AWS credentials (from &lt;code&gt;~/.aws/credentials&lt;/code&gt; or instance profile). No separate token to rotate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;Here's the full autonomous pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────────┐
│                       KIRO CREW                               │
│                                                               │
│  ┌───────────────┐     ┌──────────────────┐                  │
│  │  Cron Job     │────▶│  Orchestrator    │                  │
│  │  (*/30 * * *) │     │  (claude-sonnet) │                  │
│  └───────────────┘     └────────┬─────────┘                  │
│                                 │                             │
│                    Spawns 5 parallel subagents                │
│                                 │                             │
│        ┌────────────────────────┼────────────────────┐       │
│        ▼            ▼           ▼          ▼         ▼       │
│  ┌──────────┐ ┌──────────┐ ┌────────┐ ┌────────┐ ┌──────┐  │
│  │   ECS    │ │  CI/CD   │ │CloudW. │ │DevOps  │ │Lambda│  │
│  │  Check   │ │  Check   │ │ Check  │ │ Agent  │ │Check │  │
│  └────┬─────┘ └────┬─────┘ └───┬────┘ └───┬────┘ └──┬───┘  │
│       │             │           │          │         │       │
│       └─────────────┴───────────┴──────────┴─────────┘       │
│                              │                                │
│                    Consolidated findings                      │
│                    (severity-prioritized)                     │
│                              │                                │
│                    ┌─────────▼──────────┐                    │
│                    │  Coding Agent      │                    │
│                    │  (writes fix, PR)  │                    │
│                    └─────────┬──────────┘                    │
└──────────────────────────────┼───────────────────────────────┘
                               │
              ┌────────────────┼────────────────┐
              ▼                                 ▼
   ┌────────────────────┐            ┌──────────────────┐
   │  AWS DevOps Agent  │            │     GitHub       │
   │  (MCP endpoint)    │            │  (Pull Request)  │
   │                    │            │                  │
   │  - chat            │            │  Human reviews   │
   │  - investigate     │            │  in the morning  │
   │  - recommend       │            │                  │
   └────────────────────┘            └──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cron fires&lt;/strong&gt; every 30 minutes (&lt;code&gt;*/30 * * * *&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orchestrator&lt;/strong&gt; (claude-sonnet-4) spawns 5 parallel subagents&lt;/li&gt;
&lt;li&gt;Each subagent checks one domain: ECS, CI/CD, CloudWatch, DevOps Agent consultation, Lambda&lt;/li&gt;
&lt;li&gt;All run simultaneously (not sequential, this matters for speed)&lt;/li&gt;
&lt;li&gt;Results consolidated into severity-prioritized findings&lt;/li&gt;
&lt;li&gt;If actionable: &lt;strong&gt;Coding Agent&lt;/strong&gt; writes the fix, opens a &lt;strong&gt;PR&lt;/strong&gt; on GitHub&lt;/li&gt;
&lt;li&gt;Human reviews the PR in the morning&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agent can investigate but never deploy. That's the safety boundary.&lt;/p&gt;




&lt;h2&gt;
  
  
  Live demo: what the agent found
&lt;/h2&gt;

&lt;p&gt;I triggered the investigation with one natural prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Something's wrong with my payment-api service. I think builds are failing and the pipeline is stuck. Check everything: ECS, CodeBuild, CodePipeline, Lambda timeouts, and CloudWatch alarms. Also try calling DevOps Agent via awscurl for a health assessment. Tell me what's broken and how to fix it, prioritized by severity."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent spawned 5 parallel subagents. I watched them work in real time:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subagent&lt;/th&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;Focus&lt;/th&gt;
&lt;th&gt;Tools Used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ECS Investigation&lt;/td&gt;
&lt;td&gt;921383af&lt;/td&gt;
&lt;td&gt;Service status, task definitions, health checks&lt;/td&gt;
&lt;td&gt;14 tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD Investigation&lt;/td&gt;
&lt;td&gt;6a99eac8&lt;/td&gt;
&lt;td&gt;CodeBuild status, CodePipeline state, build logs&lt;/td&gt;
&lt;td&gt;14 tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch Investigation&lt;/td&gt;
&lt;td&gt;7e55ac60&lt;/td&gt;
&lt;td&gt;Alarms, logs, metrics for service health&lt;/td&gt;
&lt;td&gt;14 tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DevOps Agent Consultation&lt;/td&gt;
&lt;td&gt;ae0b20b6&lt;/td&gt;
&lt;td&gt;MCP endpoint call via awscurl&lt;/td&gt;
&lt;td&gt;3 tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda Investigation&lt;/td&gt;
&lt;td&gt;488d2596&lt;/td&gt;
&lt;td&gt;Timeouts, errors, Step Functions state&lt;/td&gt;
&lt;td&gt;11 tools&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All five running simultaneously. The DevOps Agent consultation alone took 67 seconds (it calls multiple AWS APIs behind the scenes). Total investigation: under 3 minutes.&lt;/p&gt;

&lt;p&gt;Here's what came back:&lt;/p&gt;

&lt;h3&gt;
  
  
  🚨 CRITICAL OUTAGE (4+ Days Down)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ECS Service in Terminal Failure Loop&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Finding&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Failed tasks&lt;/td&gt;
&lt;td&gt;7,279 since August 13th&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root Cause&lt;/td&gt;
&lt;td&gt;Health check expects &lt;code&gt;/api/health&lt;/code&gt; but nginx:alpine only serves static content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current State&lt;/td&gt;
&lt;td&gt;4 tasks running (should be 2), all UNHEALTHY, replaced every 60-90 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource Impact&lt;/td&gt;
&lt;td&gt;Continuous failed task churn burning compute costs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster&lt;/td&gt;
&lt;td&gt;payment-api-cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;td&gt;payment-api-service&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Complete CI/CD Pipeline Breakdown&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Finding&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CodeCommit&lt;/td&gt;
&lt;td&gt;Empty repository with no branches or code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeBuild&lt;/td&gt;
&lt;td&gt;FAILED (can't build from empty repo)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodePipeline&lt;/td&gt;
&lt;td&gt;FAILED, can't find 'main' branch that doesn't exist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Impact&lt;/td&gt;
&lt;td&gt;Builds fail every time, pipeline permanently stuck&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🟡 MEDIUM PRIORITY: Configuration Hardening
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Set health check grace period to 30+ seconds (currently 0)&lt;/li&gt;
&lt;li&gt;Add proper logging and monitoring configuration&lt;/li&gt;
&lt;li&gt;Implement proper security groups and networking&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚠️ Lambda Timeout Issue
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Demo&lt;/code&gt; function has a 3-second timeout calling downstream services that need 4 to 6 seconds to respond. Every invocation times out silently. Zero alarms configured to catch it.&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 CloudWatch: Zero Observability
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Zero alarms&lt;/strong&gt; configured in the entire account. 8 Lambda functions, 5 API Gateways, 1 ECS cluster, all running completely blind. If anything fails, nobody gets notified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;None of these would have triggered a PagerDuty alert.&lt;/strong&gt; The ECS service was burning compute for four days straight with nobody noticing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Applying the fixes
&lt;/h2&gt;

&lt;p&gt;Based on the agent's severity-prioritized recommendations, I applied two immediate fixes in the terminal:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix 1: Lambda timeout 3s to 30s&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;aws lambda update-function-configuration &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; Demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout&lt;/span&gt; 30 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"[FunctionName,Timeout]"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;span class="c"&gt;# Output: Demo    30&lt;/span&gt;

aws lambda update-function-configuration &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; Demo-API &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout&lt;/span&gt; 30 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"[FunctionName,Timeout]"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;span class="c"&gt;# Output: Demo-API    30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix 2: Add CloudWatch alarms (was ZERO)&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;&lt;span class="c"&gt;# ECS task failure alarm&lt;/span&gt;
aws cloudwatch put-metric-alarm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--alarm-name&lt;/span&gt; &lt;span class="s2"&gt;"ECS-PaymentAPI-TaskFailures"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--metric-name&lt;/span&gt; CPUUtilization &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; AWS/ECS &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--statistic&lt;/span&gt; Average &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--period&lt;/span&gt; 300 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--threshold&lt;/span&gt; 0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--comparison-operator&lt;/span&gt; LessThanOrEqualToThreshold &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--evaluation-periods&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dimensions&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ClusterName,Value&lt;span class="o"&gt;=&lt;/span&gt;payment-api-cluster &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ServiceName,Value&lt;span class="o"&gt;=&lt;/span&gt;payment-api-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--alarm-description&lt;/span&gt; &lt;span class="s2"&gt;"Payment API: no tasks running"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;span class="c"&gt;# Output: ✅ ECS alarm created&lt;/span&gt;

&lt;span class="c"&gt;# Lambda error alarm&lt;/span&gt;
aws cloudwatch put-metric-alarm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--alarm-name&lt;/span&gt; &lt;span class="s2"&gt;"Lambda-Demo-Errors"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--metric-name&lt;/span&gt; Errors &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; AWS/Lambda &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--statistic&lt;/span&gt; Sum &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--period&lt;/span&gt; 300 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--threshold&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--comparison-operator&lt;/span&gt; GreaterThanOrEqualToThreshold &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--evaluation-periods&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dimensions&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;FunctionName,Value&lt;span class="o"&gt;=&lt;/span&gt;Demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--alarm-description&lt;/span&gt; &lt;span class="s2"&gt;"Demo Lambda errors"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;span class="c"&gt;# Output: ✅ Lambda alarm created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verified state after fixes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lambda Demo:     30s (was 3s)
Lambda Demo-API: 30s (was 3s)
Alarms:          2 active (was 0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the production Crew workflow, these become a PR. The agent writes the IaC change (CloudFormation, CDK, or Terraform depending on your stack), pushes to a branch, and opens the PR with investigation findings in the description. A human reviews it in the morning.&lt;/p&gt;




&lt;h2&gt;
  
  
  The cron job: check every 30 minutes
&lt;/h2&gt;

&lt;p&gt;Here's the cron that makes this autonomous. I added it through the Crew Schedule page:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;production-health-check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schedule&lt;/td&gt;
&lt;td&gt;Every 30 minutes (&lt;code&gt;*/30 * * * *&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent&lt;/td&gt;
&lt;td&gt;default (claude-sonnet-4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message&lt;/td&gt;
&lt;td&gt;Check production health: ECS, CodeBuild, Pipeline, Lambda, CloudWatch. Flag issues with severity and fixes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every 30 minutes, Crew spawns a session, the agent checks infrastructure health, and reports findings. If everything's green, session ends quietly. If something's flagged, it investigates deeper and proposes fixes.&lt;/p&gt;

&lt;p&gt;No daemon process to maintain. No EC2 instance running a cron script. No custom monitoring infrastructure. One entry in the Schedule page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaling this:&lt;/strong&gt; You can have multiple cron jobs for different concerns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;production-health    → */30 * * * *  → Full infrastructure scan
cost-anomaly-check   → 0 8 * * *    → Daily cost spike detection  
security-drift       → 0 */6 * * *  → Every 6h IAM/SG audit
release-readiness    → 0 9 * * 1-5  → Weekday pre-deploy check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The investigate skill: deep root-cause analysis
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;chat&lt;/code&gt; tool is fast (seconds). But for real incidents, DevOps Agent has an &lt;code&gt;investigate&lt;/code&gt; skill that runs 5 to 8 minutes of deep analysis across your infrastructure.&lt;/p&gt;

&lt;p&gt;What the investigation does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pulls CloudWatch metrics&lt;/strong&gt;: invocations, errors, duration, throttles, anomaly bands&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checks recent deployments&lt;/strong&gt;: CodeDeploy, ECS task definitions, Lambda versions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlates downstream latency&lt;/strong&gt;: X-Ray traces, API Gateway integration errors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analyzes temporal patterns&lt;/strong&gt;: did this start after a specific deployment?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checks resource topology&lt;/strong&gt;: which services depend on the failing component?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Returns root-cause analysis&lt;/strong&gt; with prioritized recommendations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the same analysis flow a senior SRE would do manually. Check metrics, correlate with deployments, trace downstream, identify root cause. The difference: it happens at 3 AM without waking anyone up.&lt;/p&gt;

&lt;p&gt;In the demo, the DevOps Agent subagent took 67 seconds to complete its assessment (you can see it in the recording: &lt;code&gt;174s • 3 tools&lt;/code&gt; on the subagent panel). That's because it's making multiple API calls behind the scenes to build the full picture.&lt;/p&gt;




&lt;h2&gt;
  
  
  The security model: why this is safe
&lt;/h2&gt;

&lt;p&gt;"Autonomous agent fixing production" sounds terrifying. After running this for weeks, here's why it's not:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. DevOps Agent is read-only by default&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The IAM role uses &lt;code&gt;ReadOnlyAccess&lt;/code&gt;. Full visibility, zero write permissions:&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&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="nl"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aidevops.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&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;"StringEquals"&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;"aws:SourceAccount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789012"&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;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;p&gt;Attached policy: &lt;code&gt;arn:aws:iam::aws:policy/ReadOnlyAccess&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It can observe everything. It can change nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Crew's deny patterns block destructive commands&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even if the agent tries to deploy, Crew blocks it:&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;"deny_patterns"&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="s2"&gt;"kubectl apply"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"aws deploy create-deployment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"terraform apply"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"aws ecs update-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"aws lambda update-function-code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"aws cloudformation execute-change-set"&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;p&gt;The agent can write code. It cannot execute deployments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The output is always a Pull Request&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Never a direct change to production. The agent creates a branch, writes the fix, and opens a PR with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Investigation findings in the description&lt;/li&gt;
&lt;li&gt;Severity assessment&lt;/li&gt;
&lt;li&gt;Links to relevant CloudWatch metrics&lt;/li&gt;
&lt;li&gt;Diff showing exactly what changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Human reviews and approves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Full CloudTrail audit trail&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every MCP call to DevOps Agent is logged with:&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;"eventSource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aidevops.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eventName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"InvokeMcpTool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requestParameters"&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;"agentSpaceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7ab2314d-..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"accessTokenId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"at-..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"protocol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"toolName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chat"&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;"sourceIPAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"172.31.20.246"&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;p&gt;Every action traceable. Every tool invocation recorded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Token scoping and rotation&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Control&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;read&lt;/code&gt; or &lt;code&gt;operate&lt;/code&gt; (choose minimum needed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expiration&lt;/td&gt;
&lt;td&gt;1 to 60 days (forced rotation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP Allowlist&lt;/td&gt;
&lt;td&gt;Optional, restrict to your Crew instance IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client Type&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;agent&lt;/code&gt; (for autonomous integrations)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revocation&lt;/td&gt;
&lt;td&gt;One-click disable all tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern: &lt;strong&gt;observe everything, change nothing, suggest via PR, human approves.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The self-learning layer
&lt;/h2&gt;

&lt;p&gt;Here's where Kiro Crew adds something DevOps Agent alone cannot do.&lt;/p&gt;

&lt;p&gt;First time the agent investigates the ECS failure loop, it learns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;payment-api uses nginx:alpine as the base image&lt;/li&gt;
&lt;li&gt;The health check path &lt;code&gt;/api/health&lt;/code&gt; doesn't exist in that container&lt;/li&gt;
&lt;li&gt;Health check grace period of 0 seconds means immediate failure on deploy&lt;/li&gt;
&lt;li&gt;7,279 tasks failed before anyone noticed because zero alarms were configured&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Crew stores this as a &lt;strong&gt;lesson&lt;/strong&gt; in its Knowledge base. Next time it sees the same ECS task churn pattern (running count higher than desired, tasks being replaced every 60-90 seconds), it skips the full investigation and goes straight to: "Check if health check path exists in the container image. Check grace period setting."&lt;/p&gt;

&lt;p&gt;After 30 days of running, your SRE agent has seen every failure pattern your infrastructure produces. It doesn't just find issues faster. It finds them &lt;strong&gt;immediately&lt;/strong&gt; because it's seen them before.&lt;/p&gt;

&lt;p&gt;This is the compounding advantage. PagerDuty doesn't learn from past incidents. CloudWatch Alarms don't adapt their thresholds based on patterns. Your Crew agent does.&lt;/p&gt;




&lt;h2&gt;
  
  
  34 tools at your agent's fingertips
&lt;/h2&gt;

&lt;p&gt;When you connect DevOps Agent via MCP, your Crew agent gets access to these tools:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Investigation &amp;amp; Monitoring:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Instant health check, cost analysis, architecture review, topology mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;investigate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deep async root-cause analysis across all monitored services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_investigation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start investigation with priority level (P1/P2/P3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_recommendations&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get AI-generated mitigations with severity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_recommendation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Detailed mitigation specification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_journal_records&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stream investigation findings in real-time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;start_evaluation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Evaluate against operational goals (SLOs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_tasks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Track async investigation status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_task&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check if an investigation has completed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Release &amp;amp; Deployment Safety:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_release_readiness_review&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Analyze PRs for production risk patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_release_testing_job&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run exploratory tests on deployed apps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Service &amp;amp; Space Management:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Detailed service topology, dependencies, health&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_agent_spaces&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manage multiple monitoring environments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_agent_space&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Space configuration details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_agent_space&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provision new monitoring environments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;update_agent_space&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Modify space settings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Access &amp;amp; Security:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_access_token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Issue new credentials programmatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_access_token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inspect token details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_access_tokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Audit all active tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;revoke_access_token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Revoke compromised credentials immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Plus 14 more for full CRUD on spaces, associations, and configurations.&lt;/p&gt;

&lt;p&gt;Your agent picks the right tool based on context. Ask "is anything broken?" and it calls &lt;code&gt;chat&lt;/code&gt;. Say "investigate the payment API latency" and it calls &lt;code&gt;investigate&lt;/code&gt;. No routing logic. MCP handles tool selection.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cost considerations
&lt;/h2&gt;

&lt;p&gt;Running this autonomously has cost implications worth understanding:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kiro Crew costs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Sonnet 4 token usage per cron run (approximately $0.02 to $0.08 depending on investigation depth)&lt;/li&gt;
&lt;li&gt;48 runs per day at &lt;code&gt;*/30&lt;/code&gt; = roughly $1 to $4/day for continuous monitoring&lt;/li&gt;
&lt;li&gt;Compare with: a single on-call engineer costs $200+/night&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AWS DevOps Agent costs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Included with your Agent Space (no per-API-call charges for chat/investigate as of August 2026)&lt;/li&gt;
&lt;li&gt;The IAM role uses ReadOnlyAccess, so no resource creation costs from the agent itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What this SAVES:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4 days of ECS task churn burning compute (in my case: ~$15-20 wasted before I noticed)&lt;/li&gt;
&lt;li&gt;Engineer investigation time (30-60 min per incident at $100+/hr senior rate)&lt;/li&gt;
&lt;li&gt;Customer-facing downtime (the real cost nobody measures until it happens)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The math: $2/day for continuous monitoring vs $200+ per missed incident. After one catch, it pays for itself for months.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it yourself (complete walkthrough)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Kiro Crew installed and running (&lt;a href="https://kiro.dev/docs/crew/installation/" rel="noopener noreferrer"&gt;install guide&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;AWS account with resources to monitor (at minimum: one Lambda function or ECS service)&lt;/li&gt;
&lt;li&gt;IAM permissions: &lt;code&gt;aidevops:*&lt;/code&gt; for Agent Space management&lt;/li&gt;
&lt;li&gt;AWS CLI v2 configured with credentials&lt;/li&gt;
&lt;li&gt;Region: us-east-1, us-west-2, or eu-west-1&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Create an Agent Space
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws devops-agent create-agent-space &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"production-monitoring"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Autonomous production health monitoring"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the &lt;code&gt;agentSpaceId&lt;/code&gt; from the output. You'll need it for every subsequent step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create an IAM role for DevOps Agent
&lt;/h3&gt;

&lt;p&gt;DevOps Agent needs a role to assume when accessing your account's resources:&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;# Create trust policy&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;' &amp;gt; devops-agent-trust.json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Service": "aidevops.amazonaws.com"
    },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": {
        "aws:SourceAccount": "YOUR_ACCOUNT_ID"
      }
    }
  }]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Create the role&lt;/span&gt;
aws iam create-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; DevOpsAgentSourceRole &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--assume-role-policy-document&lt;/span&gt; file://devops-agent-trust.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Read-only access for AWS DevOps Agent monitoring"&lt;/span&gt;

&lt;span class="c"&gt;# Attach ReadOnlyAccess (observe everything, change nothing)&lt;/span&gt;
aws iam attach-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; DevOpsAgentSourceRole &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-arn&lt;/span&gt; arn:aws:iam::aws:policy/ReadOnlyAccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Use &lt;code&gt;ReadOnlyAccess&lt;/code&gt; not &lt;code&gt;AdministratorAccess&lt;/code&gt;. The agent needs to observe, not modify. Least privilege applies here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Associate your AWS account with the Agent Space
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws devops-agent associate-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--agent-space-id&lt;/span&gt; YOUR_SPACE_ID &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-id&lt;/span&gt; aws &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--configuration&lt;/span&gt; &lt;span class="s1"&gt;'{
    "aws": {
      "assumableRoleArn": "arn:aws:iam::YOUR_ACCOUNT_ID:role/DevOpsAgentSourceRole",
      "accountId": "YOUR_ACCOUNT_ID",
      "accountType": "monitor"
    }
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Enable access tokens on the Agent Space
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws devops-agent update-agent-space &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--agent-space-id&lt;/span&gt; YOUR_SPACE_ID &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--access-token-configuration&lt;/span&gt; &lt;span class="s1"&gt;'{"enabled": true}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Create an access token for Kiro Crew
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws devops-agent create-access-token &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--agent-space-id&lt;/span&gt; YOUR_SPACE_ID &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"kiro-crew-monitor"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--scope&lt;/span&gt; &lt;span class="s2"&gt;"operate"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--client-type&lt;/span&gt; &lt;span class="s2"&gt;"agent"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--expires-in-days&lt;/span&gt; 60 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the token value securely. You won't see it again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Add MCP server to Kiro Crew
&lt;/h3&gt;

&lt;p&gt;In your Crew dashboard: &lt;strong&gt;Agent Capabilities&lt;/strong&gt; &amp;gt; &lt;strong&gt;Integrations (MCP)&lt;/strong&gt; &amp;gt; &lt;strong&gt;Add&lt;/strong&gt;&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;"aws-devops-agent"&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://connect.aidevops.us-east-1.api.aws/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;"headers"&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;"X-Agent-Space-Id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_SPACE_ID"&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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS DevOps Agent (34 tools)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;p&gt;Or via the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kirocrew config mcp add aws-devops-agent &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s2"&gt;"https://connect.aidevops.us-east-1.api.aws/mcp"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Space-Id=YOUR_SPACE_ID"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Add the production health cron job
&lt;/h3&gt;

&lt;p&gt;Go to &lt;strong&gt;Schedule&lt;/strong&gt; in the Crew dashboard, click &lt;strong&gt;+ Add Job&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;production-health-check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schedule&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*/30 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent&lt;/td&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message&lt;/td&gt;
&lt;td&gt;Check production health via AWS DevOps Agent. Scan ECS, Lambda, CodeBuild, CodePipeline, and CloudWatch. Flag any issues found with severity and recommended fixes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 8: Verify it works
&lt;/h3&gt;

&lt;p&gt;Trigger the cron manually by clicking &lt;strong&gt;Run&lt;/strong&gt; in the Schedule page. You should see the agent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check available MCP tools&lt;/li&gt;
&lt;li&gt;Call DevOps Agent's chat tool for a health assessment&lt;/li&gt;
&lt;li&gt;Return findings with severity levels&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If it returns "Overall: HEALTHY" with no flags, your infrastructure is in good shape. If it finds issues, you'll get severity-prioritized recommendations.&lt;/p&gt;




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

&lt;p&gt;This is the setup I run daily. DevOps Agent handles observation and intelligence. Kiro Crew handles orchestration, memory, and action. Together: autonomous ops that get smarter every week.&lt;/p&gt;

&lt;p&gt;Articles 1 to 5 built the foundation. This one connects Crew to the real world, where production issues don't wait for business hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The full Kiro Crew series:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/introducing-kiro-crew-awss-open-source-ai-agent-orchestrator-1e63"&gt;Introducing Kiro Crew&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/i-spent-a-day-with-kiro-crew-heres-what-it-actually-does-fk0"&gt;I Spent a Day With Kiro Crew&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/how-kiro-crews-cron-jobs-replaced-4-hours-of-weekly-toil-37h"&gt;Cron Jobs Replaced 4 Hours of Weekly Toil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/i-showed-my-ciso-kiro-crew-heres-the-security-model-that-got-it-approved-423j"&gt;The Security Model That Got CISO Approval&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/i-built-a-custom-kiro-crew-app-in-5-minutes-the-app-kit-nobodys-talking-about"&gt;I Built a Custom App in 5 Minutes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;This article&lt;/strong&gt;: Crew + DevOps Agent autonomous ops&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;GitHub repo with all configs:&lt;/strong&gt; &lt;a href="https://github.com/SimplyNadaf/kiro-crew-devops-agent" rel="noopener noreferrer"&gt;SimplyNadaf/kiro-crew-devops-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's eating your 3 AM pages? I'm betting DevOps Agent plus a cron job could handle half of them. Drop your scenario in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more on AWS architecture, DevOps, and AI infrastructure:&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Portfolio&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://dev.to/sarvar_04"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://www.youtube.com/@Sarvar-Nadaf" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://x.com/SarvarN_04" rel="noopener noreferrer"&gt;X&lt;/a&gt; | &lt;a href="https://builder.aws.com/community/@sarvar" rel="noopener noreferrer"&gt;AWS Builder Center&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>showdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>I Added Terminal Charts to My Dev.to CLI. Here's What My Data Looks Like.</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Fri, 21 Aug 2026 14:13:00 +0000</pubDate>
      <link>https://dev.to/sarvar_04/i-added-terminal-charts-to-my-devto-cli-heres-what-my-data-looks-like-250j</link>
      <guid>https://dev.to/sarvar_04/i-added-terminal-charts-to-my-devto-cli-heres-what-my-data-looks-like-250j</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;code&gt;pip install devpub==0.2.1&lt;/code&gt; → &lt;code&gt;devpub stats --graph&lt;/code&gt; → color-gradient bar charts, sparklines, trend arrows, and multi-period breakdowns for your Dev.to analytics. Zero new dependencies. &lt;a href="https://github.com/simplynadaf/devpub" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; / &lt;a href="https://github.com/simplynadaf/devpub/pull/10" rel="noopener noreferrer"&gt;PR #10&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;I check my Dev.to stats obsessively. Page views, reactions, which articles are climbing, which ones flatlined. But switching to the browser, clicking through dashboards, waiting for pages to load... it breaks my flow every single time.&lt;/p&gt;

&lt;p&gt;So I built charts directly into &lt;a href="https://github.com/simplynadaf/devpub" rel="noopener noreferrer"&gt;devpub&lt;/a&gt;.&lt;br&gt;
&lt;/p&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;&lt;span class="nv"&gt;devpub&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.2.1
devpub stats &lt;span class="nt"&gt;--graph&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one command now gives me this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Views (last 30 days)  ↘ -18%

     871 ┤                 █
         │                ██ ▂██     █▂
     653 ┤                ██ ███▇ ▆  █▆
         │               ▃██▅████▁█▆ ███
     435 ┤·▃····▁········███████████·███▃ ← avg (451)
         │▄█    █▆▁    ▂ ███████████▄████
     217 ┤████▇██████▅███████████████████
         │███████████████████████████████
         └───────────────────────────────
          Jul 21      Aug 05      Aug 20

  trend: ▃▄▃▃▂▃▄▃▃▃▃▂▃▃▃▄▆█▄▆▆▇▆▄▆▄▃▇▆▅▄

  Total: 14.0K │ Avg: 451/day │ Peak: 871 ▲ Aug 07

  Period Breakdown

   7 days: ▅▆▅▄█▆▆▄                         ↗ +9%   Total: 3.8K
  14 days: █▄▆▆▇▆▄▆▄▃▇▆▅▄                  ↘ -18%   Total: 8.5K
  21 days: ▃▂▃▃▃▄▆█▄▆▆▇▆▄▆▄▃▇▆▅▄           ↘ -18%   Total: 10.9K
  30 days: ▄▃▃▂▃▄▃▃▃▃▂▃▃▃▄▆█▄▆▆▇▆▄▆▄▃▇▆▅▄  ↘ -18%   Total: 13.7K
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's real data from my account. 257K total views, 24K followers, rendered in 0.3 seconds. No browser needed.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ZjexRNOdO4Y" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What changed in v0.2&lt;/li&gt;
&lt;li&gt;The color gradient system&lt;/li&gt;
&lt;li&gt;Multi-period sparklines&lt;/li&gt;
&lt;li&gt;The average line&lt;/li&gt;
&lt;li&gt;How I built it (zero new dependencies)&lt;/li&gt;
&lt;li&gt;Try it yourself&lt;/li&gt;
&lt;li&gt;What's next&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What changed in v0.2
&lt;/h2&gt;

&lt;p&gt;Version 0.1 had &lt;code&gt;--graph&lt;/code&gt; as a flag that did nothing. I shipped the flag first, then built the feature. Classic.&lt;/p&gt;

&lt;p&gt;Version 0.2.1 makes it real:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Color gradient bars&lt;/td&gt;
&lt;td&gt;Bars fade blue (low) → cyan → green → gold (peak)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sparkline one-liner&lt;/td&gt;
&lt;td&gt;Compact 30-character trend below the chart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trend indicator&lt;/td&gt;
&lt;td&gt;Arrow + percentage comparing recent vs previous period&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average line&lt;/td&gt;
&lt;td&gt;Dotted line across chart showing mean, with label&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak marker&lt;/td&gt;
&lt;td&gt;Gold highlight with date in the summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Box-drawing axes&lt;/td&gt;
&lt;td&gt;Clean tick marks using Unicode characters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12-row height&lt;/td&gt;
&lt;td&gt;More vertical resolution than before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-period breakdown&lt;/td&gt;
&lt;td&gt;7d, 14d, 21d, 30d sparklines with individual trends&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Period flag&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-p 7d&lt;/code&gt;, &lt;code&gt;-p 90d&lt;/code&gt;, &lt;code&gt;-p 3m&lt;/code&gt; for any timeframe&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All of this renders in the terminal. No image generation. No external services. No new dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  The color gradient system
&lt;/h2&gt;

&lt;p&gt;Most terminal charts use one color. Cyan. Green. Whatever. Every bar looks the same regardless of value.&lt;/p&gt;

&lt;p&gt;I wanted the peak to visually pop. So bars shift color based on their height relative to the maximum:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value range&lt;/th&gt;
&lt;th&gt;Color&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;0–25%&lt;/td&gt;
&lt;td&gt;Blue&lt;/td&gt;
&lt;td&gt;Low days, weekends&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25–50%&lt;/td&gt;
&lt;td&gt;Cyan&lt;/td&gt;
&lt;td&gt;Below average&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50–75%&lt;/td&gt;
&lt;td&gt;Green&lt;/td&gt;
&lt;td&gt;Good days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;75–95%&lt;/td&gt;
&lt;td&gt;Lime&lt;/td&gt;
&lt;td&gt;Great days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;95–100%&lt;/td&gt;
&lt;td&gt;Gold&lt;/td&gt;
&lt;td&gt;Peak day&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The implementation is simple. One function that maps a 0–1 ratio to an RGB color string:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_value_to_color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;g&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="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&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;rgb(50,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,220)&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;ratio&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r&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;ratio&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&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;rgb(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,200,200)&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;ratio&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&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="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&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;rgb(0,210,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&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;ratio&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r&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;ratio&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&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;rgb(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,220,50)&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="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rgb(255,200,0)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rich handles the RGB rendering. Works in any modern terminal that supports 24-bit color (iTerm2, Windows Terminal, Ghostty, Kitty, Alacritty, WezTerm).&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-period sparklines
&lt;/h2&gt;

&lt;p&gt;This is my favorite addition. After the main chart, you get a breakdown:&lt;br&gt;
&lt;/p&gt;

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

   7 days: ▅▆▅▄█▆▆▄                         ↗ +9%   Total: 3.8K
  14 days: █▄▆▆▇▆▄▆▄▃▇▆▅▄                  ↘ -18%   Total: 8.5K
  21 days: ▃▂▃▃▃▄▆█▄▆▆▇▆▄▆▄▃▇▆▅▄           ↘ -18%   Total: 10.9K
  30 days: ▄▃▃▂▃▄▃▃▃▃▂▃▃▃▄▆█▄▆▆▇▆▄▆▄▃▇▆▅▄  ↘ -18%   Total: 13.7K
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why this matters: the 30-day trend might be down, but the 7-day trend tells you if you're recovering. In my case, overall traffic dropped 18% (I didn't publish for a week), but the last 7 days show +9% (new articles bringing it back).&lt;/p&gt;

&lt;p&gt;Without multi-period comparison, you'd just see "down 18%" and feel bad. With it, you see the recovery in progress.&lt;/p&gt;

&lt;p&gt;The sparkline characters (&lt;code&gt;▁▂▃▄▅▆▇█&lt;/code&gt;) give you 8 levels of resolution per character. A 30-character sparkline shows 30 days of data in a single line. Your eye can instantly spot patterns: weekday/weekend cycles, post-publish spikes, gradual decay.&lt;/p&gt;




&lt;h2&gt;
  
  
  The average line
&lt;/h2&gt;

&lt;p&gt;One detail that took surprisingly long to get right: the dotted average line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     435 ┤·▃····▁········███████████·███▃ ← avg (451)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs across the chart at the mean value height. Bars above it are above average. Bars below are below. The &lt;code&gt;←&lt;/code&gt; label tells you the exact number.&lt;/p&gt;

&lt;p&gt;Why it matters: without it, you're guessing whether a day was "good" or "bad." The line gives you an instant reference. Tuesday's 500 views looks small next to Monday's 871 peak, but the average line shows it's actually above average.&lt;/p&gt;




&lt;h2&gt;
  
  
  How I built it (zero new dependencies)
&lt;/h2&gt;

&lt;p&gt;The entire chart system uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rich&lt;/strong&gt; (already a dependency) for colored output and &lt;code&gt;console.print&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python stdlib&lt;/strong&gt; for math, datetime, and string operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unicode block characters&lt;/strong&gt; (&lt;code&gt;▁▂▃▄▅▆▇█&lt;/code&gt;) for sub-character resolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No matplotlib. No plotext. No termgraph. No asciichart. I looked at all of them. They either add heavy dependencies, produce output that doesn't integrate with Rich's markup system, or require complex setup.&lt;/p&gt;

&lt;p&gt;Pure Python + Rich gives me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full control over every character&lt;/li&gt;
&lt;li&gt;Rich markup (&lt;code&gt;[bold]&lt;/code&gt;, &lt;code&gt;[rgb(r,g,b)]&lt;/code&gt;) works inline&lt;/li&gt;
&lt;li&gt;Zero import time penalty&lt;/li&gt;
&lt;li&gt;41 tests covering all helper functions&lt;/li&gt;
&lt;li&gt;Works on Python 3.10+&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole implementation is ~200 lines in one file. No architecture astronautics needed.&lt;/p&gt;

&lt;p&gt;Full implementation: &lt;a href="https://github.com/simplynadaf/devpub/pull/10" rel="noopener noreferrer"&gt;PR #10&lt;/a&gt; — 41 tests, lint clean, handles edge cases (zero data, single day, large accounts with 90+ days).&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&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;&lt;span class="nv"&gt;devpub&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.2.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set your Dev.to API key (get it at &lt;a href="https://dev.to/settings/extensions"&gt;dev.to/settings/extensions&lt;/a&gt;):&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;DEVPUB_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;devpub stats &lt;span class="nt"&gt;--graph&lt;/span&gt;              &lt;span class="c"&gt;# 30-day chart (default)&lt;/span&gt;
devpub stats &lt;span class="nt"&gt;--graph&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 7d        &lt;span class="c"&gt;# Last 7 days&lt;/span&gt;
devpub stats &lt;span class="nt"&gt;--graph&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 90d       &lt;span class="c"&gt;# Last 90 days (auto-downsamples)&lt;/span&gt;
devpub stats &lt;span class="nt"&gt;--graph&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 2w        &lt;span class="c"&gt;# Last 2 weeks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⭐ &lt;a href="https://github.com/simplynadaf/devpub" rel="noopener noreferrer"&gt;Star the repo&lt;/a&gt; if terminal analytics resonates with you.&lt;/p&gt;




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

&lt;p&gt;v0.3 will add the &lt;strong&gt;Concepts API&lt;/strong&gt; integration. Dev.to has an ML-powered topic classification system that most people don't know exists. It classifies articles into semantic concepts with daily metrics. &lt;code&gt;devpub concepts&lt;/code&gt; will let you discover trending topics, find gaps in coverage, and see which concepts your articles fit into.&lt;/p&gt;

&lt;p&gt;But that's next week. For now, try &lt;code&gt;devpub stats --graph&lt;/code&gt; and tell me what your chart looks like.&lt;/p&gt;




&lt;p&gt;What's your Dev.to stats workflow right now? Browser dashboard? Ignoring it entirely? Something else? Drop it in the comments — I'm curious how other writers track their content performance.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is part 2 of the "Building DevPub in Public" series. &lt;a href="https://dev.to/sarvar_04/introducing-devpub-open-source-devto-cli-tool-49jf"&gt;Part 1: Introducing DevPub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Sarvar Nadaf&lt;/a&gt; | Cloud Architect | Cloud, AI Infrastructure &amp;amp; DevOps&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Follow me: &lt;a href="https://dev.to/sarvar_04"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://github.com/simplynadaf" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | &lt;a href="https://www.youtube.com/@TechwithSarvar" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://linkedin.com/in/sarvarnadaf" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://x.com/SarvarN_04" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devto</category>
      <category>showdev</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Built a Kiro Crew App in 5 Minutes - Full Tutorial With Code</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Tue, 18 Aug 2026 12:05:57 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-i-built-a-kiro-crew-app-in-5-minutes-full-tutorial-with-code-3el0</link>
      <guid>https://dev.to/aws-builders/how-i-built-a-kiro-crew-app-in-5-minutes-full-tutorial-with-code-3el0</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/-TkMTNAKcAY" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;p&gt;Parts 1-4 showed you what Kiro Crew can do. Investigate incidents. Automate weekly toil. Block dangerous commands. All using the built-in agent.&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/aws-builders/introducing-kiro-crew-awss-open-source-ai-agent-orchestrator-1e63" class="crayons-story__hidden-navigation-link"&gt;Introducing Kiro Crew: AWS's Open-Source AI Agent Orchestrator&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/aws-builders/introducing-kiro-crew-awss-open-source-ai-agent-orchestrator-1e63" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Solves persistent cross-session memory limits&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/aws-builders"&gt;
            &lt;img alt="AWS Community Builders  logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2794%2F88da75b6-aadd-4ea1-8083-ae2dfca8be94.png" class="crayons-logo__image" width="350" height="350"&gt;
          &lt;/a&gt;

          &lt;a href="/sarvar_04" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1163149%2F5afa2902-591e-4944-b6fa-9bbba80c6e95.png" alt="sarvar_04 profile" class="crayons-avatar__image" width="800" height="559"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/sarvar_04" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Sarvar Nadaf
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Sarvar Nadaf
                
                
              
              &lt;div id="story-author-preview-content-4323052" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/sarvar_04" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1163149%2F5afa2902-591e-4944-b6fa-9bbba80c6e95.png" class="crayons-avatar__image" alt="" width="800" height="559"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Sarvar Nadaf&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/aws-builders" class="crayons-story__secondary fw-medium"&gt;AWS Community Builders &lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/aws-builders/introducing-kiro-crew-awss-open-source-ai-agent-orchestrator-1e63" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 5&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/aws-builders/introducing-kiro-crew-awss-open-source-ai-agent-orchestrator-1e63" id="article-link-4323052"&gt;
          Introducing Kiro Crew: AWS's Open-Source AI Agent Orchestrator
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/aws"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;aws&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/aws-builders/introducing-kiro-crew-awss-open-source-ai-agent-orchestrator-1e63" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;40&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/aws-builders/introducing-kiro-crew-awss-open-source-ai-agent-orchestrator-1e63#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              34&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            12 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;



&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/aws-builders/how-kiro-crews-cron-jobs-replaced-4-hours-of-weekly-toil-37h" class="crayons-story__hidden-navigation-link"&gt;How Kiro Crew's Cron Jobs Replaced 4 Hours of Weekly Toil&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/aws-builders/how-kiro-crews-cron-jobs-replaced-4-hours-of-weekly-toil-37h" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Unsupervised agents cost $2.10 weekly&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/aws-builders"&gt;
            &lt;img alt="AWS Community Builders  logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2794%2F88da75b6-aadd-4ea1-8083-ae2dfca8be94.png" class="crayons-logo__image" width="350" height="350"&gt;
          &lt;/a&gt;

          &lt;a href="/sarvar_04" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1163149%2F5afa2902-591e-4944-b6fa-9bbba80c6e95.png" alt="sarvar_04 profile" class="crayons-avatar__image" width="800" height="559"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/sarvar_04" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Sarvar Nadaf
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Sarvar Nadaf
                
                
              
              &lt;div id="story-author-preview-content-4341137" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/sarvar_04" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1163149%2F5afa2902-591e-4944-b6fa-9bbba80c6e95.png" class="crayons-avatar__image" alt="" width="800" height="559"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Sarvar Nadaf&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/aws-builders" class="crayons-story__secondary fw-medium"&gt;AWS Community Builders &lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/aws-builders/how-kiro-crews-cron-jobs-replaced-4-hours-of-weekly-toil-37h" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 7&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/aws-builders/how-kiro-crews-cron-jobs-replaced-4-hours-of-weekly-toil-37h" id="article-link-4341137"&gt;
          How Kiro Crew's Cron Jobs Replaced 4 Hours of Weekly Toil
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devops"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devops&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/aws-builders/how-kiro-crews-cron-jobs-replaced-4-hours-of-weekly-toil-37h" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;17&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/aws-builders/how-kiro-crews-cron-jobs-replaced-4-hours-of-weekly-toil-37h#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              5&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;But here's what nobody's talking about: Kiro Crew has an App Store. And you can build your own apps for it. In five minutes.&lt;/p&gt;

&lt;p&gt;Not plugins. Not scripts. Full apps with their own agents, skills, cron jobs, and dashboard pages. Package them. Publish them. Other users install with one click.&lt;/p&gt;

&lt;p&gt;I built one. A Daily Standup Bot. It reads my git commits every morning and generates standup notes so I never have to write "worked on X" again. Let me show you how.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What the App Kit actually is&lt;/li&gt;
&lt;li&gt;What we're building&lt;/li&gt;
&lt;li&gt;Step 1: The manifest (app.json)&lt;/li&gt;
&lt;li&gt;Step 2: The agent&lt;/li&gt;
&lt;li&gt;Step 3: The skill&lt;/li&gt;
&lt;li&gt;Step 4: The dashboard page&lt;/li&gt;
&lt;li&gt;Step 5: The cron job&lt;/li&gt;
&lt;li&gt;Install and run&lt;/li&gt;
&lt;li&gt;What it looks like live&lt;/li&gt;
&lt;li&gt;Publishing to the App Store&lt;/li&gt;
&lt;li&gt;What else you could build&lt;/li&gt;
&lt;li&gt;Try it yourself&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What the App Kit actually is
&lt;/h2&gt;

&lt;p&gt;An app is a package that contributes any combination of:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom AI agent with its own model, prompt, and tool access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skills&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;On-demand knowledge files that teach the agent specific capabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP servers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;New tools the LLM can call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cron jobs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Scheduled tasks the app owns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI pages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom pages in the dashboard sidebar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backend processes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTTP servers reverse-proxied through the gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An app that only ships a skill is one markdown file. An app that ships everything is a full project. You decide the scope.&lt;/p&gt;

&lt;p&gt;The key difference from "just adding a skill": apps are installable, versioned, publishable, and isolated. Crew manages their lifecycle. Users install from the App Store with one click.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we're building
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Daily Standup Bot&lt;/strong&gt; that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reads git commits from the last 24 hours&lt;/li&gt;
&lt;li&gt;Formats them as "What I Did / What's Blocked / What's Next"&lt;/li&gt;
&lt;li&gt;Runs every weekday at 9 AM automatically&lt;/li&gt;
&lt;li&gt;Shows standup history in a custom dashboard page&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Five files. Five minutes. A real app you'd actually use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;standup-bot/
├── app.json                    ← manifest (identity + resources)
├── agents/
│   └── standup-agent.json      ← agent definition
├── skills/
│   └── standup-format/
│       └── SKILL.md            ← formatting rules
└── ui/
    └── src/App.tsx             ← dashboard page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: The manifest (app.json)
&lt;/h2&gt;

&lt;p&gt;Every app needs one file: &lt;code&gt;app.json&lt;/code&gt;. This is the single source of truth.&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standup-bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Daily Standup Bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Auto-generates standup notes from git commits."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sarvar_04"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agents"&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="s2"&gt;"agents/standup-agent.json"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"skills"&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="s2"&gt;"skills/standup-format"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ui"&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;"entry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/index.mjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pages"&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;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/apps/standup-bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Standups"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"icon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ClipboardList"&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="nl"&gt;"crons"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"morning-standup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cron_expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0 9 * * 1-5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Generate today's standup summary from yesterday's git activity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standup-agent"&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;p&gt;That's agents, skills, a dashboard page, and a cron job. All declared in one file. Crew reads this and wires everything up.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 2: The agent
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;agents/standup-agent.json&lt;/code&gt;:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standup-agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"auto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Generates standup summaries from git activity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a standup summary assistant. Analyze git commits from the last 24 hours and generate concise standup notes. Format: What I Did, What's Blocked, What's Next."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&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="s2"&gt;"@kirocrew-core"&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;p&gt;Eight lines. The &lt;code&gt;@kirocrew-core&lt;/code&gt; tool reference gives it access to spawn processes, read files, and interact with the system. The &lt;code&gt;model: "auto"&lt;/code&gt; lets Crew pick the best available model.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 3: The skill
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;skills/standup-format/SKILL.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;standup-format&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;How to format daily standup updates&lt;/span&gt;
&lt;span class="na"&gt;triggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;standup&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;daily&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;morning&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;always&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Standup Format&lt;/span&gt;

When generating standup notes:
&lt;span class="p"&gt;
1.&lt;/span&gt; &lt;span class="gs"&gt;**What I did**&lt;/span&gt; - List completed work from git commits (group by feature/fix)
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="gs"&gt;**What's blocked**&lt;/span&gt; - Identify stale PRs, failing CI, unresolved issues
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="gs"&gt;**What's next**&lt;/span&gt; - Infer from branch names and open issues

Rules:
&lt;span class="p"&gt;-&lt;/span&gt; One line per bullet
&lt;span class="p"&gt;-&lt;/span&gt; Past tense for "did", present for "blocked", future for "next"
&lt;span class="p"&gt;-&lt;/span&gt; Group related commits into one bullet
&lt;span class="p"&gt;-&lt;/span&gt; Skip merge commits and dependency bumps
&lt;span class="p"&gt;-&lt;/span&gt; Flag anything unmerged for &amp;gt;24 hours
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Skills are markdown. They load on-demand when trigger words appear in the conversation. No code. No compilation. Just knowledge the agent uses when relevant.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 4: The dashboard page
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ui/src/App.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAppApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useAppEvents&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@kirocrew/app-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CardTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PageHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StatCard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Badge&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@kirocrew/app-sdk/ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;StandupDashboard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAppApi&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;standups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setStandups&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/apps/standup-bot/history&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setStandups&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PageHeader&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Daily Standups"&lt;/span&gt; &lt;span class="na"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Auto-generated from git activity"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"px-6 pb-8"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid gap-3.5 grid-cols-4 mb-6"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StatCard&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Today"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Pending"&lt;/span&gt; &lt;span class="na"&gt;accent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StatCard&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"This Week"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;standups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; standups`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StatCard&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Total Commits"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StatCard&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Next Run"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Mon 9:00 AM"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;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;You don't &lt;code&gt;npm install @kirocrew/app-sdk&lt;/code&gt;. The dashboard provides it at runtime. Your app stays tiny. Build with Vite, mark Crew's SDK as external, output a single &lt;code&gt;.mjs&lt;/code&gt; file.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 5: The cron job
&lt;/h2&gt;

&lt;p&gt;Already declared in &lt;code&gt;app.json&lt;/code&gt;:&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="nl"&gt;"crons"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"morning-standup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cron_expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0 9 * * 1-5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Generate today's standup summary from yesterday's git activity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standup-agent"&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;p&gt;Crew registers the cron on enable. Deregisters on disable. Every weekday at 9 AM, it spawns a session, runs the message through &lt;code&gt;standup-agent&lt;/code&gt;, and stores the result. No daemon. No systemd timer. Just a line in your manifest.&lt;/p&gt;


&lt;h2&gt;
  
  
  Install and run
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get your auth token&lt;/span&gt;
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kirocrew token | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oP&lt;/span&gt; &lt;span class="s1"&gt;'token=\K[^&amp;amp;]+'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Install (one command - point to your app directory)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:5476/api/apps/install?token=&lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &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;'{"source": "./standup-bot"}'&lt;/span&gt; | python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool

&lt;span class="c"&gt;# Enable - agents, skills, crons all activate&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:5476/api/apps/standup-bot/enable?token=&lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Response:&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;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standup-bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"enabled standup-bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"registration"&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;"agents"&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="s2"&gt;"standup-bot/standup-agent"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"skills"&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="s2"&gt;"standup-bot/standup-format"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"crons"&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="s2"&gt;"standup-bot/morning-standup"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mcp_servers"&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;"errors"&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;span class="nl"&gt;"hooks"&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;"crons_registered"&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="s2"&gt;"standup-bot/morning-standup"&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;p&gt;Agent registered. Skill loaded. Cron scheduled. Dashboard page live.&lt;/p&gt;

&lt;p&gt;Refresh the dashboard. "Standups" is now in your sidebar. That's it.&lt;/p&gt;


&lt;h2&gt;
  
  
  What it looks like live
&lt;/h2&gt;

&lt;p&gt;After installation, "Standups" appears in the sidebar. The dashboard shows stat cards and an empty state waiting for the first standup.&lt;/p&gt;

&lt;p&gt;Trigger it manually in a chat session:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use the standup-agent to generate today's standup from ~/projects/payment-api.
Run git log, analyze every commit, group by feature area.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The agent runs &lt;code&gt;git log --since="24 hours ago" --oneline --no-merges&lt;/code&gt;, analyzes each commit, and produces:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I Did:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Payment Processing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implemented rate limiting middleware for /api/payments (max 100 req/min per API key)&lt;/li&gt;
&lt;li&gt;Fixed currency conversion rounding bug - was truncating before conversion&lt;/li&gt;
&lt;li&gt;Added retry logic for failed Stripe webhook deliveries (exponential backoff, max 5)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;API &amp;amp; Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updated OpenAPI spec with new error codes (429, 503, 504)&lt;/li&gt;
&lt;li&gt;Added request validation for multi-currency checkout (USD, EUR, GBP, JPY)&lt;/li&gt;
&lt;li&gt;Refactored payment intent creation to use idempotency keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configured DynamoDB TTL for expired sessions (7-day retention)&lt;/li&gt;
&lt;li&gt;Added CloudWatch alarms for payment failure rate &amp;gt; 5%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What's Blocked:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PCI compliance security review - waiting on AppSec team (2 days)&lt;/li&gt;
&lt;li&gt;Stripe Connect onboarding - blocked on legal approval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What's Next:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subscription billing with usage-based metering&lt;/li&gt;
&lt;li&gt;Payment analytics dashboard (revenue, failure rates, top merchants)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;11 commits analyzed. 9 seconds. Navigate to the Standups page - it's already there.&lt;/p&gt;


&lt;h2&gt;
  
  
  Publishing to the App Store
&lt;/h2&gt;

&lt;p&gt;The App Store is a curated registry. Publishing means opening a PR:&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;app-registry.json:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standup-bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gitUrl"&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://github.com/simplynadaf/kiro-crew-standup-bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"branch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&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;p&gt;Once merged, your app shows up in Explore → Library for all Crew users. Search "standup" and there it is:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Daily Standup Bot
v1.0.0 · Enabled · Registry

Auto-generates standup notes from git commits. Runs daily at 9 AM Mon-Fri.

sarvar_04
1 agent · 1 skill · 1 cron · 1 page

[Open]  [Disable]  [Sync]  [Uninstall]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Your app sits alongside the built-in ones - Code Review Sage, Research Lab, Task Runner. First-class citizen. Teams can also host private registries for internal apps that shouldn't be public.&lt;/p&gt;


&lt;h2&gt;
  
  
  What else you could build
&lt;/h2&gt;

&lt;p&gt;The standup bot took 5 files and 5 minutes. Here's what's possible with the same pattern:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;App idea&lt;/th&gt;
&lt;th&gt;Components&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PR Review Bot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent + skill (code review rules) + cron (check PRs hourly)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Incident Postmortem Generator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent + skill (postmortem template) + UI (history page)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost Anomaly Alerter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent + cron (daily AWS cost check) + Slack notification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Onboarding Buddy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent + skill (team knowledge) + UI (progress tracker)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sprint Health Monitor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent + cron (daily Jira check) + UI (burndown chart)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Any workflow that's "check something + format it + deliver it on schedule" is a Crew app waiting to happen.&lt;/p&gt;


&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;Kiro Crew is open source (Apache 2.0). The standup-bot code is in this article.&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;# Install Crew&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.crew.kiro.dev/cli.sh | sh
kirocrew gateway

&lt;span class="c"&gt;# Enable third-party apps&lt;/span&gt;
&lt;span class="c"&gt;# In ~/.kiro/crew/config.json set: "apps_allow_third_party": true&lt;/span&gt;

&lt;span class="c"&gt;# Create the app&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; standup-bot/agents standup-bot/skills/standup-format standup-bot/ui/src
&lt;span class="c"&gt;# Create the 5 files shown above (app.json, agent, skill, UI, vite config)&lt;/span&gt;

&lt;span class="c"&gt;# Build UI&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;standup-bot/ui &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ../..

&lt;span class="c"&gt;# Install + enable&lt;/span&gt;
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kirocrew token | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oP&lt;/span&gt; &lt;span class="s1"&gt;'token=\K[^&amp;amp;]+'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:5476/api/apps/install?token=&lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &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;'{"source": "./standup-bot"}'&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:5476/api/apps/standup-bot/enable?token=&lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Open dashboard - "Standups" is in the sidebar&lt;/span&gt;
kirocrew open
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The full app code and docs: &lt;a href="https://kiro.dev/docs/crew/apps/build-first-app/" rel="noopener noreferrer"&gt;Build your first app&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/simplynadaf" rel="noopener noreferrer"&gt;
        simplynadaf
      &lt;/a&gt; / &lt;a href="https://github.com/simplynadaf/kiro-crew-standup-bot" rel="noopener noreferrer"&gt;
        kiro-crew-standup-bot
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Daily Standup Bot — A Kiro Crew app that reads git commits and generates standup notes. Agent + Skill + Cron + Dashboard in 5 files.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🤖 Daily Standup Bot&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;A Kiro Crew App That Writes Your Standups For You&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/kirodotdev/KiroCrew" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/21d56f0f4794eb9ee0dbba3bef3d790be4116f4e1b609b3010a7126463b6ddb0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4b69726f5f437265772d4170705f4b69742d3633363666313f7374796c653d666f722d7468652d6261646765266c6f676f3d646174613a696d6167652f7376672b786d6c3b6261736536342c50484e325a79423462577875637a30696148523063446f764c336433647935334d793576636d63764d6a41774d43397a646d6369494864705a48526f505349794e434967614756705a326830505349794e434967646d6c6c64304a76654430694d43417749444930494449304969426d6157787350534a3361476c305a53492b50484268644767675a4430695454457949444a4d4d794133646a457762446b674e5341354c5456574e3277744f53303165694976506a777663335a6e50673d3d" alt="Kiro Crew"&gt;&lt;/a&gt;
&lt;a href="https://github.com/simplynadaf/kiro-crew-standup-bot/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7a1226d14a365d288bfe51ece915ee0c7e754a16faa51ff06436504de29b33b4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e7376673f7374796c653d666f722d7468652d6261646765" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a href=""&gt;&lt;img src="https://camo.githubusercontent.com/17408e0296af2c9c306e9fcf68d55ea3b5132cdfa4212e6f591f86a5ffe21e45/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f355f46696c65732d355f4d696e757465732d3130623938313f7374796c653d666f722d7468652d6261646765" alt="5 Files"&gt;&lt;/a&gt;
&lt;a href="https://github.com/kirodotdev/KiroCrew" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/11fb7521b21216752475202a7bff7c5946f98f825b09d9dadeeab0db6868aa4c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4f70656e5f536f757263652d4170616368655f322e302d6666366236623f7374796c653d666f722d7468652d6261646765" alt="Open Source"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;&lt;strong&gt;5 files. 5 minutes. Never write "worked on X" again.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An AI agent that reads your git commits every morning and generates formatted standup notes - installed with one command on &lt;a href="https://github.com/kirodotdev/KiroCrew" rel="noopener noreferrer"&gt;Kiro Crew&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://youtu.be/-TkMTNAKcAY" rel="nofollow noopener noreferrer"&gt;📺 Watch the Demo&lt;/a&gt; · &lt;a href="https://github.com/simplynadaf/kiro-crew-standup-bot#-quick-start" rel="noopener noreferrer"&gt;🚀 Quick Start&lt;/a&gt; · &lt;a href="https://github.com/simplynadaf/kiro-crew-standup-bot#-app-structure" rel="noopener noreferrer"&gt;📦 App Structure&lt;/a&gt; · &lt;a href="https://dev.to/sarvar_04/i-built-a-custom-kiro-crew-app-in-5-minutes-the-app-kit-nobodys-talking-about" rel="nofollow"&gt;📝 Article&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;✨ Features&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🤖 &lt;strong&gt;Agent&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Reads git commits from the last 24 hours, groups by feature area&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📚 &lt;strong&gt;Skill&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Teaches the agent the standup format (What I Did / Blocked / Next)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⏰ &lt;strong&gt;Cron&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Runs every weekday at 9 AM automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📊 &lt;strong&gt;Dashboard&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Shows standup history, stats, and today's summary in the sidebar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🎬 Demo&lt;/h2&gt;

&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;&lt;a href="https://youtu.be/-TkMTNAKcAY" rel="nofollow 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%2Fraw.githubusercontent.com%2Fsimplynadaf%2Fkiro-crew-standup-bot%2FHEAD%2Fassets%2Fthumbnail.png" alt="Watch the Demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Agent analyzes 11 commits → generates standup in 9 seconds → dashboard updates live&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 Quick Start&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Prerequisites: Kiro Crew running&lt;/span&gt;
curl -fsSL https://download.crew.kiro.dev/cli.sh &lt;span class="pl-k"&gt;|&lt;/span&gt; sh
kirocrew gateway
&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/simplynadaf/kiro-crew-standup-bot" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kirodotdev" rel="noopener noreferrer"&gt;
        kirodotdev
      &lt;/a&gt; / &lt;a href="https://github.com/kirodotdev/KiroCrew" rel="noopener noreferrer"&gt;
        KiroCrew
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A persistent workspace for development work that self-improves and continues beyond one session.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/kirodotdev/KiroCrew/assets/banner.svg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fkirodotdev%2FKiroCrew%2FHEAD%2Fassets%2Fbanner.svg" alt="Kiro Crew. Keep work moving. Runs on your hardware, remembers across sessions, keeps working unattended."&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Kiro Crew&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;strong&gt;A persistent workspace for development work that self-improves and continues beyond one session.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://trendshift.io/repositories/103032" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/20d26869a6389d7fba902f5cddb75d8268977b531845622481a8d57020feaa3c/68747470733a2f2f7472656e6473686966742e696f2f6170692f62616467652f7472656e6473686966742f7265706f7369746f726965732f3130333033322f6461696c793f6c616e67756167653d507974686f6e" alt="Kiro Crew on Trendshift" width="250" height="55" class="js-gh-image-fallback"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  Kiro Crew is an open source development workspace that runs locally or remotely on
  your hardware. It is persistent, self-learning, and self-evolving. Work with it
  from the desktop app, web dashboard, and CLI, or continue the same work through
  connection tools like Slack and Discord
  Your multi-step tasks can run unattended, recurring jobs run on your schedule
  and heartbeats monitor systems until something needs attention. Kiro Crew Apps
  tailor that experience to a specific job, combining a purpose-built interface
  with agents, skills, schedules, integrations, and backend services.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/kirodotdev/KiroCrew/releases" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5c0b3bb65162f179a15d74b8289d29497db6c407a34891f009b5503bb600f86c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f776e6c6f61642d6d61634f532532302537432532304c696e757825323025374325323057696e646f77732d3266366665623f7374796c653d666c61742d737175617265" alt="Download Kiro Crew for macOS, Linux, or Windows"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/kirodotdev/KiroCrew/docs/README.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/852641c2b061138b6ee4a6d24baf3d7935ce1e3cc9f7a6b55ceedab8e7183191/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f63756d656e746174696f6e2d3166366665623f7374796c653d666c61742d737175617265" alt="Read the documentation"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/kirodotdev/KiroCrew/docs/guides/install.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/033bc31482c8749b864e135c691a7d174ab4677bceb1df2f704e9043fb29d146/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f496e7374616c6c25323067756964652d6d61634f532532302537432532304c696e757825323025374325323057696e646f77732d3665373738313f7374796c653d666c61742d737175617265" alt="Install guide for macOS, Linux, and Windows"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/kirodotdev/KiroCrew/CONTRIBUTING.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0fb57fc16e5b1e9b219f905ec9baf71c552ec6d747e3ea9aa1a19fa1cb7e1f55/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f6e747269627574696e672d3233383633363f7374796c653d666c61742d737175617265" alt="Contributing guide"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/kirodotdev/KiroCrew/SECURITY.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/dc4e9a0d3d8d49543683714c015505dd8378557c539253626f8f920f515a9beb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53656375726974792d3832353064663f7374796c653d666c61742d737175617265" alt="Security policy"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/kirodotdev/KiroCrew/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a84955b84a279eafcaeb1508bf99c1ad0929e84623fb47466c6cf875c436e866/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d417061636865253230322e302d3635366437363f7374796c653d666c61742d737175617265" alt="Apache 2.0 license"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/kirodotdev/KiroCrew#quick-start" rel="noopener noreferrer"&gt;Quick start&lt;/a&gt; ·
  &lt;a href="https://github.com/kirodotdev/KiroCrew#build-from-source" rel="noopener noreferrer"&gt;Build from source&lt;/a&gt; ·
  &lt;a href="https://github.com/kirodotdev/KiroCrew#why-kiro-crew" rel="noopener noreferrer"&gt;Why Kiro Crew&lt;/a&gt; ·
  &lt;a href="https://github.com/kirodotdev/KiroCrew#what-kiro-crew-does" rel="noopener noreferrer"&gt;Capabilities&lt;/a&gt; ·
  &lt;a href="https://github.com/kirodotdev/KiroCrew#how-it-works" rel="noopener noreferrer"&gt;How it works&lt;/a&gt; ·
  &lt;a href="https://github.com/kirodotdev/KiroCrew#security-and-control" rel="noopener noreferrer"&gt;Security&lt;/a&gt; ·
  &lt;a href="https://github.com/kirodotdev/KiroCrew#install-configure-and-operate" rel="noopener noreferrer"&gt;Install&lt;/a&gt; ·
  &lt;a href="https://github.com/kirodotdev/KiroCrew#anonymous-usage-telemetry" rel="noopener noreferrer"&gt;Telemetry&lt;/a&gt; ·
  &lt;a href="https://github.com/kirodotdev/KiroCrew#docs-and-contributing" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick start&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;You choose how to run Kiro Crew: the desktop app with automatic updates, a
one-line install on your machine or a remote…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kirodotdev/KiroCrew" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





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

&lt;p&gt;Part 6 will show the multi-interface story. Start a task on CLI. Continue it on Slack. Check progress on the dashboard. Get notified on your phone. Same agent, same memory, zero context loss.&lt;/p&gt;

&lt;p&gt;The App Kit is what turns Kiro Crew from "my AI coding assistant" into "my team's AI platform." The store is empty right now. First movers win.&lt;/p&gt;

&lt;p&gt;What would you build? A PR reviewer? A docs-from-code generator? An automated changelog? Drop it in the comments. If it's interesting enough, I'll build it in Part 7.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more on AWS architecture, DevOps, and AI Infrastructure:&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Portfolio&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://dev.to/sarvar_04"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://www.youtube.com/@TechwithSarvar" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="mailto:simplynadaf@gmail.com"&gt;Email&lt;/a&gt; | &lt;a href="https://builder.aws.com/community/@sarvar" rel="noopener noreferrer"&gt;AWS Builder Center&lt;/a&gt; | &lt;a href="https://x.com/SarvarN_04" rel="noopener noreferrer"&gt;X&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>showdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
