<?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: AWS</title>
    <description>The latest articles on DEV Community by AWS (@aws).</description>
    <link>https://dev.to/aws</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1726%2F2a73f1e6-7995-4348-ae37-44b064274c59.png</url>
      <title>DEV Community: AWS</title>
      <link>https://dev.to/aws</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aws"/>
    <language>en</language>
    <item>
      <title>How I built MCP Apps based Sales Analytics Agentic UI &amp; deployed it on Amazon Bedrock AgentCore</title>
      <dc:creator>Ashita Prasad</dc:creator>
      <pubDate>Wed, 01 Apr 2026 05:38:59 +0000</pubDate>
      <link>https://dev.to/aws/how-i-built-mcp-apps-based-sales-analytics-agentic-ui-deployed-it-on-amazon-bedrock-agentcore-4e9i</link>
      <guid>https://dev.to/aws/how-i-built-mcp-apps-based-sales-analytics-agentic-ui-deployed-it-on-amazon-bedrock-agentcore-4e9i</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: Learn how to add interactive forms, charts, visualization &amp;amp; PDF viewer - natively in your AI Agent chat window using MCP Apps.&lt;/p&gt;

&lt;p&gt;AI Agents are getting smarter with each passing day. But, their interfaces? Not so much. But, what if there is a way to turn the AI chat from a place where you converse into a place where you can actually work? &lt;/p&gt;

&lt;p&gt;That's where MCP Apps, an extension of Model Context Protocol, comes to our rescue. &lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/ashita/a-practical-guide-to-building-mcp-apps-1bfm"&gt;previous article&lt;/a&gt;, we explored the foundational building blocks of MCP Apps in detail and witnessed how it provides a way to deliver rich, bidirectional UIs. Through a series of minimal examples, we covered various concepts such as the handshake protocol and host-aware theming, to CSP policies and calling tools from within an iframe.&lt;/p&gt;

&lt;p&gt;Now, it's time to stitch those pieces together into something real.&lt;/p&gt;

&lt;p&gt;In this article, we'll walk through a complete, production-style MCP Apps chatflow (&lt;em&gt;chat + workflow&lt;/em&gt;) and build &lt;strong&gt;an interactive sales analytics UI&lt;/strong&gt; where a user can select sales region, pick a metric like revenue or conversion rate, fetch live data, visualize it with charts, and create &amp;amp; download a PDF report - &lt;strong&gt;all without ever leaving the AI Agent chat window&lt;/strong&gt;. We will also learn how to deploy the developed TypeScript MCP server powering our sales analytics MCP Apps on &lt;a href="https://aws.amazon.com/bedrock/agentcore/?trk=2c88fb57-1294-4a6d-8290-a7dfb773a582&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon Bedrock AgentCore Runtime&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find the entire source code of this article in the Github repo link provided below 👇&lt;br&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/ashitaprasad" rel="noopener noreferrer"&gt;
        ashitaprasad
      &lt;/a&gt; / &lt;a href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow" rel="noopener noreferrer"&gt;
        sample-mcp-apps-chatflow
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Sample MCP Apps Chatflow&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Sample MCP server that renders interactive sales analytics UIs inside an MCP Apps-compatible chat client. It includes a sales metric selector, chart-based visualization, and PDF report generation.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Articles&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This project is part of the MCP Apps article series published on dev.to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/ashita/a-practical-guide-to-building-mcp-apps-1bfm" rel="nofollow"&gt;Part 1: How I render interactive UI in my AI Agent chatflows using MCP Apps&lt;/a&gt; - Covers the core architectural patterns for declaring UI resources, practical design principles, and how to handle sandboxed host–server communication.&lt;/li&gt;
&lt;li&gt;
&lt;a href=""&gt;Part 2: How I built MCP Apps based Sales Analytics Agentic UI &amp;amp; deployed it on Amazon Bedrock AgentCore&lt;/a&gt; - Walks through a complete, production-style MCP Apps chatflow (chat + workflow) and build an interactive sales analytics UI where a user can select sales region, pick a metric like revenue or conversion rate, fetch live data, visualize it with charts, and create &amp;amp; download a PDF report - all without…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  The Problem: When chat isn't enough!
&lt;/h2&gt;

&lt;p&gt;It all started when my friend, who is a sales analyst, started exploring AI Agents after reading everywhere that agents can unlock productivity by automating workflows and are also capable of performing data analysis.&lt;/p&gt;

&lt;p&gt;He prepared sales analytics reports on a monthly and quarterly basis and felt AI Agents could accelerate this workflow greatly. He started his Agentic journey by uploading CSVs, interacting with the AI Agent &amp;amp; assigning it tasks. The agent would frequently ask him clarifying questions, like the required sales metrics, sales regions he was interested in and more follow-up questions. Often, the AI agent would assume certain parameters and produce random results which meant he had to start the entire workflow all over again.&lt;/p&gt;

&lt;p&gt;Although the resulting analysis was accurate, but from the presentation standpoint a simple markdown result was quite underwhelming. After discussing his entire Agentic Experience (AX), I realised that the core friction was due to the &lt;strong&gt;limitations of plain chat&lt;/strong&gt;. While my friend kept answering and re-prompting, the model kept guessing and re-analyzing. &lt;strong&gt;The experience never quite graduated from "assistant response" to an actual "working interface"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And, MCP Apps solved exactly this gap!&lt;/p&gt;

&lt;p&gt;Instead of returning only text, the server returns a rich, interactive HTML interface rendered directly inside the chat which lets the user &lt;em&gt;drive&lt;/em&gt; the experience like an application, not just an answer.&lt;/p&gt;

&lt;p&gt;Let's see how.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: A Chatflow powered by MCP Apps
&lt;/h2&gt;

&lt;p&gt;An end-to-end demo of the complete chatflow is shown below:&lt;/p&gt;

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

&lt;p&gt;Our chatflow involves &lt;strong&gt;three MCP Apps&lt;/strong&gt; (UI resources) orchestrated by &lt;strong&gt;four tools&lt;/strong&gt;. Each MCP App is a self-contained HTML page served by the MCP server, running in a sandboxed iframe, communicating with the host via &lt;code&gt;postMessage&lt;/code&gt; JSON-RPC.&lt;/p&gt;

&lt;p&gt;The new Agentic experience (AX) unfolds like a real workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User asks for sales insights.&lt;/li&gt;
&lt;li&gt;AI agent opens a Sales Metric Selector MCP App which allows user to easily select the sales metric, reporting period, reporting year &amp;amp; sales regions, instead of forcing the user to specify everything in text.&lt;/li&gt;
&lt;li&gt;On clicking Submit, the app fetches data through an internal MCP tool and adds it as a structured JSON data in the context.&lt;/li&gt;
&lt;li&gt;With the relevant data added in context, user can prompt to generate a visualization or prepare a PDF report.&lt;/li&gt;
&lt;li&gt;Upon requesting for a visualization, the Sales Visualization MCP App is rendered which presents the entire data in an insightful &amp;amp; interactive visualization dashboard.&lt;/li&gt;
&lt;li&gt;Upon requesting for a PDF report, the AI agent triggers a tool which creates a PDF report server side, and then displays it via a PDF Report Viewer MCP App. The MCP app displays the pdf in the form of an interactive PDF and allows the user to download the report.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most important thing about this whole new experience is not just that these steps happen, but they happen &lt;strong&gt;inside one continuous chat experience&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let us now go through the individual tools and take a look at some of their salient features:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;select-sales-metric&lt;/code&gt; tool
&lt;/h3&gt;

&lt;p&gt;Code - &lt;a href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow/blob/7f8577249d07e81715400ff6a7325e9a1e622928/src/index.ts#L31-L74" rel="noopener noreferrer"&gt;Tool Registration&lt;/a&gt; and &lt;a href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow/blob/main/src/ui/sales-form.ts" rel="noopener noreferrer"&gt;MCP App&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;This is where the interactive chatflow begins. When the user prompts - "get sales data", instead of asking the user to specify every parameter via text, the agent calls this tool. VS Code (Host) then reads the linked resource and renders a rich HTML form with dropdown menus, toggle buttons, search region &amp;amp; multi-select state cards inside an iframe.&lt;/p&gt;

&lt;p&gt;The Sales metric selector MCP App resides in &lt;a href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow/blob/main/src/ui/sales-form.ts" rel="noopener noreferrer"&gt;&lt;code&gt;sales-form.ts&lt;/code&gt;&lt;/a&gt;, where server-side data such as the list of valid sales regions (Indian states), sales metric and top sales regions are serialized directly into the HTML template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;metricSelect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;metricOptions&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/select&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;indianStates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indianStates&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;topStates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topStates&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The makes the UI entirely self-contained as the page arrives already primed with the valid states and metrics. And, no further external API calls are required just to render the form or for data validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1. &lt;code&gt;get-sales-data&lt;/code&gt; tool
&lt;/h3&gt;

&lt;p&gt;Code - &lt;a href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow/blob/7f8577249d07e81715400ff6a7325e9a1e622928/src/index.ts#L80-L106" rel="noopener noreferrer"&gt;Tool Registration&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;When the user clicks "Submit" button in the Sales metric selector MCP App, the form doesn't just update the context — it calls another MCP tool from within the iframe itself by executing the below communication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toolResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tools/call&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get-sales-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;states&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectedStates&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentMetric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentPeriod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentYear&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="nx"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toolResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;structuredContent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We trigger the data fetch manually via this tool as it allows us to have a deterministic control over the most critical element ("data") and we can explicitly define inputs without relying on agent reasoning. The tool is only visible to the app, and not the agent so it cannot call it accidentally. Only the MCP App iframe can invoke it via tools/call. This is a &lt;a href="https://dev.to/ashita/a-practical-guide-to-building-mcp-apps-1bfm#:~:text=9.%20Calling%20MCP%20Tools"&gt;pattern I introduced in my previous article&lt;/a&gt;, and here you can see how powerful it becomes in a real workflow.&lt;/p&gt;

&lt;p&gt;After fetching the result, &lt;a href="https://dev.to/ashita/a-practical-guide-to-building-mcp-apps-1bfm#:~:text=5.%20Updating%20Model%20Context%20via%20MCP%20App"&gt;&lt;code&gt;ui/update-model-context&lt;/code&gt; request is sent to the host&lt;/a&gt; with the entire structured data (user input + data obtained for report) is added into the conversation context, so the agent can use it in subsequent turns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui/update-model-context&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;structuredContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;selections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentMetric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentPeriod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentYear&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;states&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectedStates&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;selectedStateNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selectedStateNames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;report&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;report&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="nf"&gt;updateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✅ Context updated successfully.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;code&gt;visualize-sales-data&lt;/code&gt; tool
&lt;/h3&gt;

&lt;p&gt;Code - &lt;a href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow/blob/7f8577249d07e81715400ff6a7325e9a1e622928/src/index.ts#L108-L203" rel="noopener noreferrer"&gt;Tool Registration&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow/blob/main/src/ui/sales-visualization.ts" rel="noopener noreferrer"&gt;MCP App&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Once the Agent has the structured data in its context, when the user prompts "visualize", the agent calls &lt;code&gt;visualize-sales-data&lt;/code&gt; tool which renders a visualization dashboard inside the chat highlighting some key stats. Three additional complementary chart views are also presented in the dashboard for identifying deeper trends:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A stacked bar or line view for period-by-period comparison.&lt;/li&gt;
&lt;li&gt;A doughnut chart to gauge a state's contribution share (%) in the sales metric.&lt;/li&gt;
&lt;li&gt;A horizontal bar chart for ranked state comparison.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two implementation details are especially worth noticing:&lt;/p&gt;

&lt;h4&gt;
  
  
  i) CSP is not a footnote, it is part of the Security Contract
&lt;/h4&gt;

&lt;p&gt;The visualization MCP App depends on chart.js from a CDN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://dev.to/ashita/a-practical-guide-to-building-mcp-apps-1bfm#:~:text=8.%20Content%20Security%20Policy%20(CSP)"&gt;As we discussed it in our previous article&lt;/a&gt;, the resource domain dependency is declared explicitly in the UI resource metadata while registering it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;csp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;resourceDomains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.jsdelivr.net&lt;/span&gt;&lt;span class="dl"&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;Without this declaration, the host sandbox will block the script. This demonstrates one of the most practical aspects of MCP Apps - &lt;strong&gt;the app states what it needs, and the host enforces that contract&lt;/strong&gt;. An app cannot silently inherit broad network access just because it is an embedded HTML.&lt;/p&gt;

&lt;h4&gt;
  
  
  ii) MCP App receives tool input through notification
&lt;/h4&gt;

&lt;p&gt;When the host invokes the &lt;code&gt;visualize-sales-data&lt;/code&gt; tool, it forwards the structured payload to the iframe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui/notifications/tool-input&lt;/span&gt;&lt;span class="dl"&gt;"&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;sc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;structuredContent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sc&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;reportData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;selections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;renderDashboard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This hydrates the UI with all the data it needs to render summary cards and charts. There is no string parsing or scraping of the tool response text. Just clean structured data flowing from tool to host to app.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;show-sales-pdf-report&lt;/code&gt; tool
&lt;/h3&gt;

&lt;p&gt;Code - &lt;a href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow/blob/7f8577249d07e81715400ff6a7325e9a1e622928/src/index.ts#L205-L305" rel="noopener noreferrer"&gt;Tool Registration&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow/blob/main/src/ui/sales-pdf-report.ts" rel="noopener noreferrer"&gt;MCP App&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Once the user has explored the interactive visualization dashboard, a PDF report generation can be triggered via &lt;code&gt;show-sales-pdf-report&lt;/code&gt; tool using &lt;code&gt;"generate pdf"&lt;/code&gt; prompt. &lt;/p&gt;

&lt;p&gt;The following actions happen after the tool is triggered: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server generates a PDF report using &lt;code&gt;jsPDF&lt;/code&gt; and &lt;code&gt;jspdf-autotable&lt;/code&gt;, and sends it to the client as a &lt;code&gt;base64 string&lt;/code&gt; via &lt;code&gt;structuredContent&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;The PDF viewer MCP App decodes the base64 payload and renders it using PDF.js (from CDN). This interactive PDF viewer includes page navigation (previous/next) &amp;amp; zoom controls (keyboard shortcuts included).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The user also has the option to save the PDF report via a "Download PDF" button as shown below:&lt;/p&gt;

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

&lt;p&gt;On clicking the "Download PDF" button a &lt;code&gt;ui/download-file&lt;/code&gt; request is triggered which sends the resource (PDF) to VS Code which displays the save dialog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui/download-file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;contents&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resource&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file:///&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;pdfFileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pdfBase64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That pattern is important as sandboxed iframes should not get to ability to save any file without informing the host which thereby informs the end user. Thus, the host stays in control, and the user gets a normal, expected download flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serving MCP Apps Over HTTP
&lt;/h2&gt;

&lt;p&gt;Currently, we are running the MCP server locally using Express with the Streamable HTTP transport - a departure from the stdio approach demonstrated in the &lt;a href="https://dev.to/ashita/a-practical-guide-to-building-mcp-apps-1bfm"&gt;previous article&lt;/a&gt;. This lets us run the server as an HTTP endpoint as we are moving closer to a deployable shape, making it ideal for web-based clients and remote deployments.&lt;/p&gt;

&lt;p&gt;We can run the MCP server locally on node by executing:&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;cd&lt;/span&gt; /path/to/sample-mcp-apps-chatflow

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Development mode (with hot reload)&lt;/span&gt;
npm run dev

&lt;span class="c"&gt;# Test with MCP Inspector&lt;/span&gt;
npm run inspector:http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which successfully displays&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="o"&gt;&amp;gt;&lt;/span&gt; sample-mcp-apps-chatflow@1.0.0 dev
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tsx src/index.ts

🚀 MCP Apps Chatflow server running at http://localhost:3000
📡 MCP endpoint: http://localhost:3000/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To connect to this endpoint from VS Code Insiders, add the following config to &lt;code&gt;.vscode/mcp.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;"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;"sample-mcp-apps-chatflow"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="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;"http://localhost:3000/mcp"&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;h2&gt;
  
  
  Deploying to Amazon Bedrock AgentCore Runtime (AgentCore CLI)
&lt;/h2&gt;

&lt;p&gt;What is the value of building an awesome MCP server if you can't take it beyond the localhost? To be truly useful, it needs to be hosted so that your team or your users can connect to it from anywhere, without having to run it on their own system.&lt;/p&gt;

&lt;p&gt;That's where &lt;a href="https://aws.amazon.com/bedrock/agentcore/?trk=2c88fb57-1294-4a6d-8290-a7dfb773a582&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon Bedrock AgentCore Runtime&lt;/a&gt; comes in. It provides a managed, scalable runtime for your MCP server with built-in OAuth and session isolation.&lt;/p&gt;

&lt;p&gt;Let us go ahead and deploy our TypeScript MCP server to Amazon Bedrock AgentCore Runtime.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This guide uses the &lt;a href="https://github.com/aws/agentcore-cli" rel="noopener noreferrer"&gt;AgentCore CLI&lt;/a&gt; (&lt;code&gt;@aws/agentcore&lt;/code&gt;), the current Node.js-based CLI. If you have the older Python-based &lt;code&gt;bedrock-agentcore-starter-toolkit&lt;/code&gt; installed, uninstall it first to avoid command conflicts -- both tools use the &lt;code&gt;agentcore&lt;/code&gt; command name.&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;pip uninstall bedrock-agentcore-starter-toolkit   &lt;span class="c"&gt;# if installed via pip&lt;/span&gt;
pipx uninstall bedrock-agentcore-starter-toolkit  &lt;span class="c"&gt;# if installed via pipx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Software prerequisites
&lt;/h3&gt;

&lt;p&gt;Make sure you have the following softwares installed in your system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;AWS CLI&lt;/em&gt; with configured credentials (&lt;code&gt;aws sts get-caller-identity&lt;/code&gt; must work)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Node.js 20+&lt;/em&gt; and npm&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Docker&lt;/em&gt; (or podman/finch) for container builds&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html" rel="noopener noreferrer"&gt;&lt;em&gt;AWS CDK&lt;/em&gt;&lt;/a&gt; bootstrapped in your target account/region (&lt;code&gt;npx cdk bootstrap aws://ACCOUNT_ID/REGION&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;VS Code&lt;/em&gt; or any other MCP Apps compatible Host or even terminal for testing the deployed MCP server&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1 - Prepare the server for AgentCore Runtime
&lt;/h3&gt;

&lt;p&gt;AgentCore Runtime expects your MCP server to bind to &lt;code&gt;0.0.0.0:8000&lt;/code&gt; and expose a &lt;code&gt;POST /mcp&lt;/code&gt; endpoint using Streamable HTTP transport. The container must target &lt;code&gt;linux/arm64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/ashitaprasad/sample-mcp-apps-chatflow/blob/main/src/index.ts" rel="noopener noreferrer"&gt;&lt;code&gt;src/index.ts&lt;/code&gt;&lt;/a&gt;, update the port and host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;8000&lt;/span&gt;&lt;span class="dl"&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;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HOST&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.0.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`🚀 MCP Apps Chatflow server running at http://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&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;port&lt;/span&gt;&lt;span class="p"&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`📡 MCP endpoint: http://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&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;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/mcp`&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;h3&gt;
  
  
  Step 2: Add a Dockerfile
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;Dockerfile&lt;/code&gt; in the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--platform=linux/arm64 node:22-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--ignore-scripts&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; tsconfig.json ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/ ./src/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# Production stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; --platform=linux/arm64 node:22-slim&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev &lt;span class="nt"&gt;--ignore-scripts&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist/ ./dist/&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PORT=8000&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; HOST=0.0.0.0&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "dist/index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a &lt;code&gt;.dockerignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules
dist
agentcore
.git
*.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it builds and starts locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/arm64 &lt;span class="nt"&gt;-t&lt;/span&gt; sales-mcp-apps &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/arm64 &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:8000 sales-mcp-apps
&lt;span class="c"&gt;# Test with MCP Inspector:&lt;/span&gt;
npx @modelcontextprotocol/inspector http://localhost:8000/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Install the AgentCore CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @aws/agentcore
agentcore &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Create the AgentCore project
&lt;/h3&gt;

&lt;p&gt;From the project root, scaffold the AgentCore configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agentcore create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; salesmcpapps &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--protocol&lt;/span&gt; MCP &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build&lt;/span&gt; Container &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-agent&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--skip-python-setup&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--skip-git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;agentcore/
  agentcore.json      # Project and runtime configuration
  aws-targets.json    # AWS account and region target
  .cli/               # CLI state (deployed-state.json)
  cdk/                # CDK app for deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Configure the runtime
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;agentcore create --no-agent&lt;/code&gt; command creates an empty &lt;code&gt;runtimes&lt;/code&gt; array. You must manually add the runtime configuration to &lt;code&gt;agentcore/agentcore.json&lt;/code&gt;. The important fields are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;codeLocation&lt;/code&gt;&lt;/strong&gt; - path to the directory containing your Dockerfile, &lt;strong&gt;relative to the project root&lt;/strong&gt; (the parent of &lt;code&gt;agentcore/&lt;/code&gt;). If your Dockerfile is in the project root, use &lt;code&gt;"."&lt;/code&gt;. If you isolate your app code in a subdirectory (recommended to avoid CDK bundling issues), use that path (e.g., &lt;code&gt;"app"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;entrypoint&lt;/code&gt;&lt;/strong&gt; - the file that starts your server (e.g., &lt;code&gt;"dist/index.js"&lt;/code&gt;). Required even for Container builds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;runtimeVersion&lt;/code&gt;&lt;/strong&gt; - must be &lt;code&gt;"NODE_22"&lt;/code&gt; for Node.js projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;protocol&lt;/code&gt;&lt;/strong&gt; - &lt;code&gt;"MCP"&lt;/code&gt; for MCP servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example &lt;code&gt;agentcore/agentcore.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://schema.agentcore.aws.dev/v1/agentcore.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;"salesmcpapps"&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="mi"&gt;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;"managedBy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CDK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&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;"agentcore:created-by"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"agentcore-cli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"agentcore:project-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;"salesmcpapps"&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;"runtimes"&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;"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;"salesmcpapps"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Container"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entrypoint"&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.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"codeLocation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"runtimeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NODE_22"&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;"tags"&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;span class="nl"&gt;"memories"&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;"credentials"&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;"evaluators"&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;"onlineEvalConfigs"&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;"agentCoreGateways"&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;"policyEngines"&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;h4&gt;
  
  
  Configure deployment target
&lt;/h4&gt;

&lt;p&gt;Edit &lt;code&gt;agentcore/aws-targets.json&lt;/code&gt; with your account ID and region:&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="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;"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;"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;"Production deployment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"account"&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_ACCOUNT_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If your Dockerfile is in the project root alongside &lt;code&gt;agentcore/&lt;/code&gt;, the CDK asset bundler may hit path-length errors because it recursively copies the &lt;code&gt;cdk.out&lt;/code&gt; directory into itself. To avoid this, put your application code (Dockerfile, package.json, src/, tsconfig.json) in a subdirectory (e.g., &lt;code&gt;app/&lt;/code&gt;) and set &lt;code&gt;"codeLocation": "app"&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 5: Set up Cognito authentication
&lt;/h3&gt;

&lt;p&gt;The AgentCore CLI does not have a built-in command to set up Cognito (unlike the deprecated toolkit's &lt;code&gt;agentcore identity setup-cognito&lt;/code&gt;). You need to create the Cognito resources manually.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create a Cognito User Pool
&lt;/h4&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;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1

&lt;span class="c"&gt;# Create user pool&lt;/span&gt;
&lt;span class="nv"&gt;POOL_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws cognito-idp create-user-pool &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--pool-name&lt;/span&gt; &lt;span class="s2"&gt;"salesmcpapps-pool"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'UserPool.Id'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Pool ID: &lt;/span&gt;&lt;span class="nv"&gt;$POOL_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Add a domain for the OAuth token endpoint&lt;/span&gt;
aws cognito-idp create-user-pool-domain &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; &lt;span class="nv"&gt;$POOL_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--domain&lt;/span&gt; &lt;span class="s2"&gt;"salesmcpapps-auth"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$REGION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create a Resource Server and App Client
&lt;/h4&gt;

&lt;p&gt;For machine-to-machine authentication (client_credentials flow):&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 resource server with a custom scope&lt;/span&gt;
aws cognito-idp create-resource-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; &lt;span class="nv"&gt;$POOL_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--identifier&lt;/span&gt; &lt;span class="s2"&gt;"salesmcpapps-auth"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"Sales MCP Apps"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--scopes&lt;/span&gt; &lt;span class="s1"&gt;'[{"ScopeName":"invoke","ScopeDescription":"Invoke MCP server"}]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$REGION&lt;/span&gt;

&lt;span class="c"&gt;# Create app client with client_credentials flow&lt;/span&gt;
&lt;span class="nv"&gt;CLIENT_OUTPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws cognito-idp create-user-pool-client &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; &lt;span class="nv"&gt;$POOL_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--client-name&lt;/span&gt; &lt;span class="s2"&gt;"salesmcpapps-client"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--generate-secret&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--explicit-auth-flows&lt;/span&gt; ALLOW_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allowed-o-auth-flows&lt;/span&gt; client_credentials &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allowed-o-auth-scopes&lt;/span&gt; &lt;span class="s2"&gt;"salesmcpapps-auth/invoke"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allowed-o-auth-flows-user-pool-client&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--supported-identity-providers&lt;/span&gt; COGNITO &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$CLIENT_OUTPUT&lt;/span&gt; | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import sys,json; print(json.load(sys.stdin)['UserPoolClient']['ClientId'])"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$CLIENT_OUTPUT&lt;/span&gt; | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import sys,json; print(json.load(sys.stdin)['UserPoolClient']['ClientSecret'])"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Client ID: &lt;/span&gt;&lt;span class="nv"&gt;$CLIENT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Client Secret: &lt;/span&gt;&lt;span class="nv"&gt;$CLIENT_SECRET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Discovery URL: https://cognito-idp.&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;.amazonaws.com/&lt;/span&gt;&lt;span class="nv"&gt;$POOL_ID&lt;/span&gt;&lt;span class="s2"&gt;/.well-known/openid-configuration"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Add JWT auth to agentcore.json
&lt;/h4&gt;

&lt;p&gt;Update the runtime in &lt;code&gt;agentcore/agentcore.json&lt;/code&gt; to add &lt;code&gt;authorizerType&lt;/code&gt; and &lt;code&gt;authorizerConfiguration&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;"salesmcpapps"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Container"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entrypoint"&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.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"codeLocation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"runtimeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NODE_22"&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;"authorizerType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUSTOM_JWT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"authorizerConfiguration"&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;"customJwtAuthorizer"&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;"discoveryUrl"&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://cognito-idp.us-east-1.amazonaws.com/YOUR_POOL_ID/.well-known/openid-configuration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowedClients"&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;"YOUR_CLIENT_ID"&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;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6 - Deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agentcore deploy &lt;span class="nt"&gt;--target&lt;/span&gt; default &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Synthesize a CloudFormation template via CDK&lt;/li&gt;
&lt;li&gt;Build your Docker container via AWS CodeBuild and push it to Amazon ECR&lt;/li&gt;
&lt;li&gt;Create the AgentCore Runtime&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The deployment takes roughly 5-10 minutes. On completion, the Runtime ARN is saved to &lt;code&gt;agentcore/.cli/deployed-state.json&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 - Test the deployment
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Get a Bearer token
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&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;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://salesmcpapps-auth.auth.&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;.amazoncognito.com/oauth2/token"&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/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"grant_type=client_credentials&amp;amp;client_id=&lt;/span&gt;&lt;span class="nv"&gt;$CLIENT_ID&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;client_secret=&lt;/span&gt;&lt;span class="nv"&gt;$CLIENT_SECRET&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;scope=salesmcpapps-auth/invoke"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import sys,json; print(json.load(sys.stdin)['access_token'])"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Invoke the MCP server
&lt;/h4&gt;

&lt;p&gt;URL-encode the Runtime ARN and call the invocations endpoint:&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;# Get runtime ARN from deployed state&lt;/span&gt;
&lt;span class="nv"&gt;RUNTIME_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import json
with open('agentcore/.cli/deployed-state.json') as f:
    state = json.load(f)
rt = list(state['targets']['default']['resources']['runtimes'].values())[0]
print(rt['runtimeArn'])
"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# URL-encode the ARN&lt;/span&gt;
&lt;span class="nv"&gt;ENCODED_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import urllib.parse; print(urllib.parse.quote('&lt;/span&gt;&lt;span class="nv"&gt;$RUNTIME_ARN&lt;/span&gt;&lt;span class="s2"&gt;', safe=''))"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Test MCP initialize&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="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://bedrock-agentcore.&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;.amazonaws.com/runtimes/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ENCODED_ARN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/invocations"&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;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/json, text/event-stream"&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;"Authorization: Bearer &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;"User-Agent: test-client/1.0"&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;'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}'&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;You should see a response with &lt;code&gt;serverInfo&lt;/code&gt;, &lt;code&gt;capabilities&lt;/code&gt; including &lt;code&gt;resources&lt;/code&gt; and &lt;code&gt;tools&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  List tools and resources
&lt;/h4&gt;

&lt;p&gt;After initialize, you can send &lt;code&gt;tools/list&lt;/code&gt; and &lt;code&gt;resources/list&lt;/code&gt; requests (include the &lt;code&gt;mcp-session-id&lt;/code&gt; header from the initialize response).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8 - Connect from VS Code
&lt;/h3&gt;

&lt;p&gt;In VS Code with an &lt;a href="https://modelcontextprotocol.io/extensions/client-matrix" rel="noopener noreferrer"&gt;MCP Apps-compatible client&lt;/a&gt;, create a &lt;code&gt;.vscode/mcp.json&lt;/code&gt; in your workspace:&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;"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;"sales-mcp-apps"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="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://bedrock-agentcore.REGION.amazonaws.com/runtimes/DOUBLE_ENCODED_ARN/invocations"&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;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer YOUR_TOKEN"&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important: double-encode the &lt;code&gt;/&lt;/code&gt; in the ARN.&lt;/strong&gt; VS Code's URL parser decodes &lt;code&gt;%2F&lt;/code&gt; back to &lt;code&gt;/&lt;/code&gt; before sending the request, which breaks the ARN path segment. Use &lt;code&gt;%252F&lt;/code&gt; instead of &lt;code&gt;%2F&lt;/code&gt; so that after VS Code decodes once, the server receives the correct &lt;code&gt;%2F&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, if your ARN is:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/my-server-abc123
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The URL in &lt;code&gt;mcp.json&lt;/code&gt; should be:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/arn%3Aaws%3Abedrock-agentcore%3Aus-east-1%3A123456789012%3Aruntime%252Fmy-server-abc123/invocations
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Note &lt;code&gt;%252F&lt;/code&gt; (not &lt;code&gt;%2F&lt;/code&gt;) before the runtime name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once connected, VS Code will discover the tools and render the MCP Apps UIs (sales metric selector, visualization charts, PDF report viewer) as interactive HTML panels in the chat as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff12snhvgvifwlu0oi13f.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff12snhvgvifwlu0oi13f.gif" alt="Adding Remote MCP server in VS Code" width="760" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjofn6yo9jmwhppfxd6a3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjofn6yo9jmwhppfxd6a3.gif" alt="Running Remote MCP server App in VS Code" width="760" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Clean up
&lt;/h3&gt;

&lt;p&gt;To remove deployed resources, delete the CloudFormation stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation delete-stack &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; AgentCore-salesmcpapps-default &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$REGION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also clean up Cognito:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cognito-idp delete-user-pool-domain &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; &lt;span class="nv"&gt;$POOL_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--domain&lt;/span&gt; &lt;span class="s2"&gt;"salesmcpapps-auth"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$REGION&lt;/span&gt;

aws cognito-idp delete-user-pool &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; &lt;span class="nv"&gt;$POOL_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$REGION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;MCP Apps is no longer a theoretical specification - it's a practical framework for building rich, interactive experiences inside AI chatflows. This sales analytics case study demonstrates the full spectrum: forms for structured input, Chart.js for visualization, jsPDF for document generation, and the MCP Apps protocol gluing it all together.&lt;/p&gt;

&lt;p&gt;The patterns described here — app-only tools, structured content flow, CSP declarations, server-side PDF generation - are directly transferable to your own use cases. Whether you're building a CRM dashboard, a data annotation tool, or an interactive report builder, the approach remains the same. We also saw how Amazon Bedrock AgentCore can be used for deploying MCP remote servers securely.&lt;/p&gt;

&lt;p&gt;I would love to hear your experience with MCP Apps or about any issues you faced while going through this article. Please mention it in the comment section below and I will definitely address it. Also, in case you have any other suggestion, feel free to add it in the comments.&lt;/p&gt;

&lt;p&gt;by Ashita Prasad (&lt;a href="https://github.com/ashitaprasad" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/ashitaprasad/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://x.com/ashitaprasad" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.instagram.com/ashitaprasad.in" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: The opinions expressed here are my own and do not necessarily represent those of current or past employers. Please note that you are solely responsible for your judgement on checking facts. This post does not monetize via any advertising.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>mcp</category>
    </item>
    <item>
      <title>5 Techniques to Stop AI Agent Hallucinations in Production</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Mon, 30 Mar 2026 23:58:02 +0000</pubDate>
      <link>https://dev.to/aws/5-techniques-to-stop-ai-agent-hallucinations-in-production-oik</link>
      <guid>https://dev.to/aws/5-techniques-to-stop-ai-agent-hallucinations-in-production-oik</guid>
      <description>&lt;p&gt;&lt;strong&gt;AI agent hallucinations&lt;/strong&gt; occur when an LLM-powered agent fabricates data, selects the wrong tool, or ignores business rules during autonomous task execution. This post deploys 5 production-ready techniques to stop them — using managed hosting, serverless tools, database-driven guardrails, semantic tool routing, and a knowledge graph. Everything deploys as infrastructure as code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR — 5 techniques, one production stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Graph-RAG&lt;/strong&gt; on Neo4j AuraDB eliminates fabricated aggregations with Cypher queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic tool routing&lt;/strong&gt; via AgentCore Gateway replaces custom FAISS indexes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-agent validation&lt;/strong&gt; on Lambda + DynamoDB catches errors single agents miss&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database-driven steering rules&lt;/strong&gt; update agent behavior without redeploying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard hooks + soft steers&lt;/strong&gt; separate financial constraints from operational adjustments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every demo in this series on stopping AI agent hallucinations ran on a laptop. Hardcoded data, in-memory rules, a single user. The techniques worked, but the infrastructure did not scale. Moving to production means replacing local files with managed databases, in-process tools with serverless functions, and hardcoded rules with data you can update without redeploying.&lt;/p&gt;

&lt;p&gt;This post walks through a production hotel booking agent that applies every technique from the series. We use &lt;a href="https://strandsagents.com/" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt; for its native support of &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/" rel="noopener noreferrer"&gt;tool calling&lt;/a&gt;, &lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/hooks/" rel="noopener noreferrer"&gt;hooks&lt;/a&gt; for guardrail enforcement, and &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/" rel="noopener noreferrer"&gt;MCP (Model Context Protocol)&lt;/a&gt; integration for connecting to &lt;a href="https://aws.amazon.com/bedrock/agentcore/" rel="noopener noreferrer"&gt;Amazon Bedrock AgentCore&lt;/a&gt; Gateway. Similar patterns can be applied in LangGraph, AutoGen, CrewAI, or other agent frameworks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Complexity note:&lt;/strong&gt; This guide assumes familiarity with &lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt; and core AWS services (Lambda, DynamoDB, S3). New to CDK? Start with the &lt;a href="https://cdkworkshop.com/" rel="noopener noreferrer"&gt;CDK Workshop&lt;/a&gt; first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Working code:&lt;/strong&gt; &lt;a href="https://github.com/elizabethfuentes12/why-agents-fail-sample-for-amazon-agentcore/tree/main/06-agentcore-production-demo" rel="noopener noreferrer"&gt;github.com/elizabethfuentes12/why-agents-fail-sample-for-amazon-agentcore&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;You need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS account&lt;/strong&gt; with &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html" rel="noopener noreferrer"&gt;CDK bootstrapped&lt;/a&gt; in your target region&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt;&lt;/strong&gt; installed and configured&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;AWS CDK v2&lt;/a&gt;&lt;/strong&gt; (Cloud Development Kit) — infrastructure as code framework for deploying all resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://python.org/downloads" rel="noopener noreferrer"&gt;Python 3.11+&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt;&lt;/strong&gt; package manager&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;OpenAI API key&lt;/a&gt;&lt;/strong&gt; — the agent uses GPT-4o-mini as the LLM (Large Language Model) provider, swappable for &lt;a href="https://aws.amazon.com/bedrock/" rel="noopener noreferrer"&gt;Amazon Bedrock&lt;/a&gt; or other providers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://neo4j.com/cloud/aura-free/" rel="noopener noreferrer"&gt;Neo4j AuraDB Free&lt;/a&gt;&lt;/strong&gt; account — only if deploying GraphRAG (Stack 2)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Series Overview
&lt;/h2&gt;

&lt;p&gt;This is the final post in the series on stopping AI agent hallucinations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/aws/rag-vs-graphrag-when-agents-hallucinate-answers-3chm"&gt;RAG vs Graph-RAG: When Agents Hallucinate Answers&lt;/a&gt;&lt;/strong&gt; — Knowledge graphs prevent hallucinated aggregations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/aws/reduce-agent-errors-and-token-costs-with-semantic-tool-selection-1o04"&gt;Reduce Agent Errors and Token Costs with Semantic Tool Selection&lt;/a&gt;&lt;/strong&gt; — Vector filtering reduces wrong tool choices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/aws/how-to-stop-ai-agents-from-hallucinating-silently-with-multi-agent-validation-1nkp"&gt;How to Stop AI Agents from Hallucinating Silently with Multi-Agent Validation&lt;/a&gt;&lt;/strong&gt; — Cross-validation catches errors single agents miss&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/aws/ai-agent-guardrails-rules-that-llms-cannot-bypass-596d"&gt;AI Agent Guardrails: Rules That LLMs Cannot Bypass&lt;/a&gt;&lt;/strong&gt; — Symbolic rules enforced at the framework level&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/aws/runtime-guardrails-for-ai-agents-steer-dont-block-36kd"&gt;Runtime Guardrails for AI Agents — Steer, Don't Block&lt;/a&gt;&lt;/strong&gt; — Agent self-corrects instead of failing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From Demo to Production&lt;/strong&gt; (this post) — Deploy all techniques on AWS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each post can be read independently. The &lt;a href="https://github.com/elizabethfuentes12/why-agents-fail-sample-for-amazon-agentcore" rel="noopener noreferrer"&gt;repository&lt;/a&gt; contains all demos with runnable code.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Does Each Anti-Hallucination Technique Map to Production?
&lt;/h2&gt;

&lt;p&gt;The table below shows what each demo built locally and what replaces it in production:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technique&lt;/th&gt;
&lt;th&gt;Demo implementation&lt;/th&gt;
&lt;th&gt;Production replacement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Graph-RAG&lt;/strong&gt; (demo 01)&lt;/td&gt;
&lt;td&gt;Local Neo4j Desktop + manual APOC scripts&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://neo4j.com/cloud/aura-free/" rel="noopener noreferrer"&gt;Neo4j AuraDB Free&lt;/a&gt; + automated Lambda builder using &lt;a href="https://neo4j.com/docs/neo4j-graphrag-python/current/user_guide_kg_builder.html" rel="noopener noreferrer"&gt;SimpleKGPipeline&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Semantic tool selection&lt;/strong&gt; (demo 02)&lt;/td&gt;
&lt;td&gt;In-memory FAISS (Facebook AI Similarity Search) index rebuilt on every run&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://aws.amazon.com/bedrock/agentcore/" rel="noopener noreferrer"&gt;AgentCore Gateway&lt;/a&gt; with MCP semantic routing — no custom index needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Multi-agent validation&lt;/strong&gt; (demo 03)&lt;/td&gt;
&lt;td&gt;Hardcoded validator agents with in-memory state&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;validate_booking_rules&lt;/code&gt; Lambda backed by DynamoDB — same safety, lower latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Neurosymbolic guardrails&lt;/strong&gt; (demo 04)&lt;/td&gt;
&lt;td&gt;Python hooks with hardcoded thresholds&lt;/td&gt;
&lt;td&gt;Steering rules in DynamoDB — change rules without redeploying&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Agent Control steering&lt;/strong&gt; (demo 05)&lt;/td&gt;
&lt;td&gt;Local Agent Control server with config files&lt;/td&gt;
&lt;td&gt;STEER messages stored in DynamoDB rules — agent self-corrects from database-driven guidance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The anti-hallucination techniques remain the same. The infrastructure becomes managed, scalable, and updatable without code changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Does the Production Architecture Look Like?
&lt;/h2&gt;

&lt;p&gt;The production architecture runs on &lt;a href="https://aws.amazon.com/bedrock/agentcore/" rel="noopener noreferrer"&gt;Amazon Bedrock AgentCore&lt;/a&gt;. Runtime hosts the agent code, and Gateway routes tool calls to &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; functions via MCP. Two independent &lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt; stacks deploy the full infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AgentCore Runtime&lt;/strong&gt; — Hosts and runs your agent code in a managed environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AgentCore Gateway&lt;/strong&gt; — MCP-based semantic tool routing that connects agents to serverless functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft148lh3q3jsonzlso6ca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft148lh3q3jsonzlso6ca.png" alt="Production architecture: AgentCore Runtime connects to Gateway via MCP, which routes to Lambda tools backed by DynamoDB and Neo4j" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack 1 — &lt;code&gt;HotelBookingAgentStack&lt;/code&gt;&lt;/strong&gt; (deploy first, works on its own):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&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;3 &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt; tables&lt;/td&gt;
&lt;td&gt;Hotels inventory, bookings, steering rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;OpenAI API key stored securely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AgentCore Runtime&lt;/td&gt;
&lt;td&gt;Managed agent hosting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AgentCore Gateway&lt;/td&gt;
&lt;td&gt;MCP semantic routing to Lambda tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; functions&lt;/td&gt;
&lt;td&gt;search, book, get_booking, process_payment, confirm, cancel, validate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Stack 2 — &lt;code&gt;GraphRAGStack&lt;/code&gt;&lt;/strong&gt; (deploy when ready, adds FAQ capabilities):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&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;
&lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;Amazon S3&lt;/a&gt; bucket&lt;/td&gt;
&lt;td&gt;300 hotel FAQ documents auto-uploaded during deploy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda build_graph&lt;/td&gt;
&lt;td&gt;Builds knowledge graph from documents using &lt;a href="https://neo4j.com/docs/neo4j-graphrag-python/current/user_guide_kg_builder.html" rel="noopener noreferrer"&gt;SimpleKGPipeline&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda query_knowledge_graph&lt;/td&gt;
&lt;td&gt;Executes &lt;a href="https://neo4j.com/docs/cypher-manual/current/" rel="noopener noreferrer"&gt;Cypher&lt;/a&gt; queries against Neo4j AuraDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://neo4j.com/cloud/aura-free/" rel="noopener noreferrer"&gt;Neo4j AuraDB Free&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Managed graph database ($0/month free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  How Do Steering Rules Work as Data Instead of Code?
&lt;/h2&gt;

&lt;p&gt;In demo 04, business rules were hardcoded in Python. Changing a threshold meant changing code and redeploying. In production, rules live in DynamoDB. Update a row and the agent's behavior changes immediately.&lt;/p&gt;

&lt;p&gt;Each rule has two messages: a &lt;code&gt;fail_message&lt;/code&gt; that describes the violation and a &lt;code&gt;steer_message&lt;/code&gt; that tells the agent how to self-correct:&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;"rule_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;"max-guests"&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;"book"&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_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"guests"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"operator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fail_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;"Guest count exceeds maximum of 10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"steer_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;"Booking for 15 guests is not available, but you CAN book for up to 10. Adjust to 10 guests, proceed, and tell the user."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&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="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 &lt;code&gt;validate_booking_rules&lt;/code&gt; tool reads rules from DynamoDB before every booking action. When a rule is violated, the agent receives the STEER guidance and self-corrects, completing the task instead of blocking:&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;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_booking_rules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;guests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;check_in&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="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;check_out&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="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;booking_id&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="p"&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Validate business rules BEFORE executing a booking action.
    ALWAYS call this before book_hotel, confirm_booking, or cancel_booking.
    Rules are loaded from the SteeringRules database, changeable without redeploying.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_get_rules_for_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# DynamoDB scan
&lt;/span&gt;    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_build_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# Derive nights, days_until_checkin
&lt;/span&gt;    &lt;span class="n"&gt;violated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_evaluate_rules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# Symbolic evaluation
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;violated&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;PASS: All &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rules passed for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;. Proceed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;violated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fail_message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;  STEER: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;steer_message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&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;FAIL: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;violated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rule(s) violated:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Changing a rule takes one command.&lt;/strong&gt; No redeploy, no PR, takes effect immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws dynamodb update-item &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--table-name&lt;/span&gt; HotelBookingAgentStack-SteeringRules &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--key&lt;/span&gt; &lt;span class="s1"&gt;'{"rule_id": {"S": "max-guests"}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--update-expression&lt;/span&gt; &lt;span class="s2"&gt;"SET threshold = :t"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--expression-attribute-values&lt;/span&gt; &lt;span class="s1"&gt;'{":t": {"N": "8"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 6 steering rules deployed by default:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What it catches&lt;/th&gt;
&lt;th&gt;How the agent self-corrects&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;max-guests&lt;/td&gt;
&lt;td&gt;book&lt;/td&gt;
&lt;td&gt;More than 10 guests&lt;/td&gt;
&lt;td&gt;Adjusts to 10, informs user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;valid-dates&lt;/td&gt;
&lt;td&gt;book&lt;/td&gt;
&lt;td&gt;Check-out before check-in&lt;/td&gt;
&lt;td&gt;Swaps dates, informs user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;advance-booking&lt;/td&gt;
&lt;td&gt;book&lt;/td&gt;
&lt;td&gt;Same-day booking&lt;/td&gt;
&lt;td&gt;Moves check-in to tomorrow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;payment-before-confirm&lt;/td&gt;
&lt;td&gt;confirm&lt;/td&gt;
&lt;td&gt;Unpaid booking&lt;/td&gt;
&lt;td&gt;Processes payment first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cancellation-window&lt;/td&gt;
&lt;td&gt;cancel&lt;/td&gt;
&lt;td&gt;Less than 48h to check-in&lt;/td&gt;
&lt;td&gt;Suggests modification instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;already-cancelled&lt;/td&gt;
&lt;td&gt;cancel&lt;/td&gt;
&lt;td&gt;Booking already cancelled&lt;/td&gt;
&lt;td&gt;Offers to create new booking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  When Should You Block vs Steer?
&lt;/h2&gt;

&lt;p&gt;Not every rule should be steerable. Payment before confirmation is a financial integrity constraint: the LLM must never bypass it, regardless of how it interprets a STEER message. The production agent uses a two-layer approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — Hard hooks (framework-level, cannot be bypassed):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.hooks.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.hooks.registry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HookRegistry&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookingGuardrailsHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Hard guardrails for financial and contractual constraints.
    The LLM cannot bypass these. They execute before the tool runs.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_hooks&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;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HookRegistry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeforeToolCallEvent&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;_validate&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;_validate&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;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&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;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confirm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;booking&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="n"&gt;_bookings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;Key&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;booking_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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;booking_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="p"&gt;)}&lt;/span&gt;
            &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item&lt;/span&gt;&lt;span class="sh"&gt;"&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;booking&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;booking&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PAID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel_tool&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;BLOCKED: Payment must be processed before confirmation. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ask the user if they want to proceed with payment.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Layer 2 — Soft steering (DynamoDB rules, agent self-corrects):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;validate_booking_rules&lt;/code&gt; tool shown above. Rules can be updated, disabled, or added without touching agent code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to decide which layer to use:&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;Layer&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;th&gt;Can the LLM bypass it?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hard hook&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;event.cancel_tool&lt;/code&gt; blocks execution&lt;/td&gt;
&lt;td&gt;Financial, legal, compliance&lt;/td&gt;
&lt;td&gt;No — framework intercepts before the tool runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Soft steer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;STEER message guides correction&lt;/td&gt;
&lt;td&gt;Capacity limits, date adjustments, preferences&lt;/td&gt;
&lt;td&gt;No bypass — but the agent adapts and completes the task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt rule&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;System prompt instruction&lt;/td&gt;
&lt;td&gt;Workflow order, communication style&lt;/td&gt;
&lt;td&gt;Yes — the LLM may ignore under ambiguous input&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use hard hooks for rules where failure means financial or legal risk. Use soft steering for everything else. It reduces user friction without sacrificing safety.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Does the MCP Integration with AgentCore Gateway Work?
&lt;/h2&gt;

&lt;p&gt;The agent connects to &lt;a href="https://aws.amazon.com/bedrock/agentcore/" rel="noopener noreferrer"&gt;AgentCore Gateway&lt;/a&gt; via MCP at startup and discovers all available tools at runtime, with no hardcoded tool list needed. In demo 02, we built a custom FAISS index to pre-filter tools by semantic similarity. In production, the Gateway replaces that entirely, routing each tool call to the right Lambda function based on semantic matching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bedrock_agentcore&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BedrockAgentCoreApp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.tools.mcp.mcp_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MCPClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.client.streamable_http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamablehttp_client&lt;/span&gt;

&lt;span class="c1"&gt;# Using OpenAI-compatible interface via Strands SDK (not direct OpenAI usage)
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.models.openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAIModel&lt;/span&gt;

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

&lt;span class="nd"&gt;@app.entrypoint&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Entry point for AgentCore Runtime invocations.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAIModel&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;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;client_args&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;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;_openai_api_key&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;BookingGuardrailsHook&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

    &lt;span class="c1"&gt;# Gateway handles semantic tool routing via MCP
&lt;/span&gt;    &lt;span class="n"&gt;mcp_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MCPClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;streamablehttp_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GATEWAY_URL&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;mcp_client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mcp_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_tools_sync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent does not define tools inline. It connects to the Gateway, which discovers available Lambda functions and routes calls based on semantic matching. Adding a new tool (such as &lt;code&gt;query_knowledge_graph&lt;/code&gt;) means registering it in the Gateway. No agent code changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Does GraphRAG Work in Production?
&lt;/h2&gt;

&lt;p&gt;Demo 01 demonstrates that traditional RAG (Retrieval-Augmented Generation) hallucinates when answering aggregation queries. "How many hotels have a pool?" gets a guess instead of a count. Graph-RAG eliminates this by executing &lt;a href="https://neo4j.com/docs/cypher-manual/current/" rel="noopener noreferrer"&gt;Cypher&lt;/a&gt; queries on a knowledge graph that computes exact results.&lt;/p&gt;

&lt;p&gt;In production, the knowledge graph runs on &lt;a href="https://neo4j.com/cloud/aura-free/" rel="noopener noreferrer"&gt;Neo4j AuraDB Free&lt;/a&gt;, a managed graph database with a $0/month free tier (200K nodes, no credit card required). The build pipeline is fully automated:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;300 hotel FAQ documents&lt;/strong&gt; upload to S3 during &lt;code&gt;cdk deploy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;build_graph Lambda&lt;/strong&gt; reads documents, calls OpenAI to extract entities and relationships via &lt;a href="https://neo4j.com/docs/neo4j-graphrag-python/current/user_guide_kg_builder.html" rel="noopener noreferrer"&gt;SimpleKGPipeline&lt;/a&gt;, and loads them into Neo4j AuraDB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;query_knowledge_graph Lambda&lt;/strong&gt; executes Cypher queries and returns structured results&lt;/li&gt;
&lt;li&gt;The query Lambda registers in &lt;strong&gt;AgentCore Gateway&lt;/strong&gt; — the agent discovers it via MCP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Questions like "What amenities does the Grand Hotel have?" now traverse the graph (following connections between nodes) instead of guessing from text chunks.&lt;/p&gt;

&lt;p&gt;The CDK stack supports two build modes via &lt;code&gt;-c graph_mode=lite|full&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Build mode (&lt;code&gt;graph_mode&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Documents&lt;/th&gt;
&lt;th&gt;Build time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;lite&lt;/strong&gt; (default)&lt;/td&gt;
&lt;td&gt;Uploads a subset of docs, builds graph in a single Lambda&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;~15 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;full&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uploads all docs, uses Step Functions to batch-process&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;~1-2 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk deploy GraphRAGStack                      &lt;span class="c"&gt;# lite mode (30 docs)&lt;/span&gt;
cdk deploy GraphRAGStack &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;graph_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;full   &lt;span class="c"&gt;# full mode (300 docs)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The booking agent works without GraphRAG. Deploy Stack 2 when hotel FAQ questions are frequent.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Deploy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 — Clone and install
&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/elizabethfuentes12/why-agents-fail-sample-for-amazon-agentcore
&lt;span class="nb"&gt;cd &lt;/span&gt;why-agents-fail-sample-for-amazon-agentcore/06-agentcore-production-demo

uv venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv 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;h3&gt;
  
  
  Step 2 — Build and deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./create_deployment_package.sh

&lt;span class="nb"&gt;cd &lt;/span&gt;cdk
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;source&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
cdk bootstrap   &lt;span class="c"&gt;# Only needed once per AWS account/region&lt;/span&gt;
cdk deploy HotelBookingAgentStack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 — Store your OpenAI API key
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://console.aws.amazon.com/secretsmanager/" rel="noopener noreferrer"&gt;AWS Secrets Manager Console&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Find &lt;code&gt;/HotelBookingAgentStack/openai-api-key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set the secret value to your &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;OpenAI API key&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 4 — Seed hotel data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&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 uv run python seed_data.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5 — Test
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;test_agent_local.ipynb&lt;/code&gt; and run all cells. The notebook tests every anti-hallucination layer — see the Try It Yourself section for the full scenario list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding GraphRAG (optional, separate stack)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Create a free Neo4j AuraDB instance at neo4j.com/cloud/aura-free&lt;/span&gt;
&lt;span class="c"&gt;# 2. Deploy the GraphRAG stack&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;cdk &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;INCLUDE_GRAPHRAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 cdk deploy GraphRAGStack

&lt;span class="c"&gt;# 3. Store Neo4j credentials in Secrets Manager (4 secrets)&lt;/span&gt;
&lt;span class="c"&gt;# 4. Build the knowledge graph&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; graphrag-build-graph &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="nt"&gt;--cli-read-timeout&lt;/span&gt; 900 &lt;span class="se"&gt;\&lt;/span&gt;
    /tmp/build-graph-output.json

&lt;span class="c"&gt;# 5. Connect GraphRAG to the booking agent&lt;/span&gt;
cdk deploy HotelBookingAgentStack &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;graphrag_query_lambda_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;QueryLambdaArn from GraphRAGStack output&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;The repository includes &lt;a href="https://github.com/elizabethfuentes12/why-agents-fail-sample-for-amazon-agentcore/blob/main/06-agentcore-production-demo/test_agent_local.ipynb" rel="noopener noreferrer"&gt;&lt;code&gt;test_agent_local.ipynb&lt;/code&gt;&lt;/a&gt; — a notebook that tests every anti-hallucination layer against the deployed agent:&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;Scenario&lt;/th&gt;
&lt;th&gt;Technique (from series)&lt;/th&gt;
&lt;th&gt;What the agent does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Full booking flow&lt;/td&gt;
&lt;td&gt;All layers&lt;/td&gt;
&lt;td&gt;validate → book → pay → validate → confirm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;15 guests (max 10)&lt;/td&gt;
&lt;td&gt;Soft steering (demo 05)&lt;/td&gt;
&lt;td&gt;Self-corrects to 10, informs user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Confirm without payment&lt;/td&gt;
&lt;td&gt;Hard hook (demo 04)&lt;/td&gt;
&lt;td&gt;Blocks confirmation, asks to pay first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Sold-out hotel&lt;/td&gt;
&lt;td&gt;Grounded retrieval (demo 01)&lt;/td&gt;
&lt;td&gt;Returns "no rooms available" from DynamoDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;City with no hotels&lt;/td&gt;
&lt;td&gt;Grounded retrieval&lt;/td&gt;
&lt;td&gt;Returns "no hotels found", no fabrication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Non-existent hotel&lt;/td&gt;
&lt;td&gt;Grounded retrieval&lt;/td&gt;
&lt;td&gt;Returns error, does not invent a hotel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Hotel amenities query&lt;/td&gt;
&lt;td&gt;Graph-RAG (demo 01)&lt;/td&gt;
&lt;td&gt;Cypher query returns real data from Neo4j&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;06-agentcore-production-demo
uv venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
jupyter notebook test_agent_local.ipynb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run all cells top to bottom. The notebook reads the &lt;code&gt;AgentRuntimeArn&lt;/code&gt; from CloudFormation outputs automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Built-in Observability
&lt;/h2&gt;

&lt;p&gt;AgentCore provides &lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability.html" rel="noopener noreferrer"&gt;built-in observability&lt;/a&gt; when you include the OpenTelemetry dependencies in your agent package. Add &lt;code&gt;strands-agents[otel]&lt;/code&gt; and &lt;code&gt;aws-opentelemetry-distro&lt;/code&gt; to your agent's &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;strands-agents[openai,otel]&amp;gt;=1.27.0
aws-opentelemetry-distro&amp;gt;=0.7.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these dependencies, AgentCore automatically instruments Strands Agents — capturing invocation logs, tool call traces (which Lambda was called, input/output, latency), and error tracking (failed tool calls, guardrail blocks). You can monitor how the agent handles each anti-hallucination layer (steering, hooks, grounded retrieval) in &lt;a href="https://aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;Amazon CloudWatch&lt;/a&gt; without adding custom logging.&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-get-started.html" rel="noopener noreferrer"&gt;observability getting started guide&lt;/a&gt; for details.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Fast Are the Anti-Hallucination Tools?
&lt;/h2&gt;

&lt;p&gt;Tool latency matters in production agents — every tool call adds to the end-to-end response time. If a&lt;br&gt;
  validation check takes 2 seconds, users notice. We benchmarked the deployed Lambda functions to measure real&lt;br&gt;
  execution duration (from CloudWatch REPORT lines, not CLI round-trip time).&lt;/p&gt;
&lt;h3&gt;
  
  
  DynamoDB-Backed Tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Cold Start&lt;/th&gt;
&lt;th&gt;Warm p50&lt;/th&gt;
&lt;th&gt;Warm p90&lt;/th&gt;
&lt;th&gt;Memory Used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validate_booking_rules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~550–620 ms&lt;/td&gt;
&lt;td&gt;~25 ms&lt;/td&gt;
&lt;td&gt;~105 ms&lt;/td&gt;
&lt;td&gt;88 MB / 256 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search_available_hotels&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~540–660 ms&lt;/td&gt;
&lt;td&gt;~16 ms&lt;/td&gt;
&lt;td&gt;~43 ms&lt;/td&gt;
&lt;td&gt;88 MB / 256 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The steering rules check (&lt;code&gt;validate_booking_rules&lt;/code&gt;) runs before every booking action. At &lt;strong&gt;~25 ms warm&lt;/strong&gt;, it adds negligible latency while preventing hallucinated bookings that violate business rules — a worthwhile trade-off vs. fixing bad bookings after the fact.&lt;/p&gt;
&lt;h3&gt;
  
  
  Neo4j AuraDB (GraphRAG)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Cold Start&lt;/th&gt;
&lt;th&gt;Warm p50&lt;/th&gt;
&lt;th&gt;Warm p90&lt;/th&gt;
&lt;th&gt;Memory Used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;query_knowledge_graph&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~1.9–2.0 s&lt;/td&gt;
&lt;td&gt;~13 ms&lt;/td&gt;
&lt;td&gt;~75 ms&lt;/td&gt;
&lt;td&gt;122 MB / 256 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neo4j AuraDB Free runs outside your VPC, but the &lt;a href="https://neo4j.com/docs/python-manual/current/" rel="noopener noreferrer"&gt;neo4j Python driver&lt;/a&gt; reuses TCP connections across warm invocations. After the first call resolves credentials from Secrets Manager and establishes the connection, subsequent Cypher queries execute in &lt;strong&gt;11–30 ms&lt;/strong&gt; — comparable to DynamoDB.&lt;/p&gt;
&lt;h3&gt;
  
  
  DynamoDB vs Neo4j AuraDB: Side by Side
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;DynamoDB (steering rules)&lt;/th&gt;
&lt;th&gt;Neo4j AuraDB (knowledge graph)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Warm p50&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~16–25 ms&lt;/td&gt;
&lt;td&gt;~13 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Warm p90&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~43–105 ms&lt;/td&gt;
&lt;td&gt;~75 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cold start&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~550–660 ms&lt;/td&gt;
&lt;td&gt;~1.9–2.0 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Driver reuse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;boto3 (local)&lt;/td&gt;
&lt;td&gt;neo4j driver (TCP keepalive)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both backends deliver sub-100 ms warm latency, which means tool calls add minimal overhead to the agent's end-to-end response time. The cold start difference (~600 ms vs ~2 s) is only relevant for the first invocation after an idle period.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Benchmark methodology:&lt;/strong&gt; 10 consecutive invocations per function using &lt;code&gt;aws lambda invoke&lt;/code&gt; against the deployed stack in &lt;code&gt;us-east-1&lt;/code&gt;. Duration values extracted from CloudWatch Lambda REPORT log lines. Cold start = Init Duration + Duration. Warm = Duration only (no Init).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  5 Lessons from Moving Anti-Hallucination Techniques to Production
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Separate hard blocks from soft steers.&lt;/strong&gt; Payment-before-confirmation is a hard block: the agent cannot work around it. Guest count limits are a soft steer where the agent adjusts and completes the task. Mixing them in the same mechanism (all hooks or all prompts) either blocks too much or steers too little.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Store rules in a database, not in code.&lt;/strong&gt; Business rules change more frequently than agent code. When the maximum guest count changes from 10 to 8, an operations team should update a DynamoDB item, not open a pull request and wait for CI/CD.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Deploy stacks independently.&lt;/strong&gt; The booking agent works without GraphRAG. Independent stacks mean you can ship the core agent quickly and add capabilities when the use case demands it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Test locally against the same data.&lt;/strong&gt; The &lt;code&gt;test_agent_local.ipynb&lt;/code&gt; notebook calls the same DynamoDB tables as production. Run all 7 test scenarios locally before deploying to AgentCore. The behavior is identical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Keep tools as pure CRUD (Create, Read, Update, Delete).&lt;/strong&gt; Lambda functions do one thing: read or write data. All business rule enforcement happens in the guardrail layers (hooks and validate tool). This keeps tools reusable and guardrails centralized.&lt;/p&gt;


&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hard hooks block&lt;/strong&gt; what the LLM must never bypass (financial, legal, compliance constraints)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soft steering corrects&lt;/strong&gt; what the agent can adapt (capacity, dates, preferences) — reducing user friction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB steering rules&lt;/strong&gt; let operations teams change agent behavior without code deploys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AgentCore Gateway&lt;/strong&gt; replaces custom tool-selection indexes with managed MCP-based semantic routing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graph-RAG on Neo4j AuraDB&lt;/strong&gt; eliminates fabricated aggregations by computing exact results with Cypher queries&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Clean Up
&lt;/h2&gt;

&lt;p&gt;To avoid ongoing charges:&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;cd &lt;/span&gt;cdk
cdk destroy HotelBookingAgentStack
&lt;span class="nv"&gt;INCLUDE_GRAPHRAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 cdk destroy GraphRAGStack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Amazon Bedrock AgentCore?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/bedrock/agentcore/" rel="noopener noreferrer"&gt;Amazon Bedrock AgentCore&lt;/a&gt; is a managed service for deploying and running AI agents. It provides two components: Runtime (hosts agent code) and Gateway (MCP-based tool routing that connects agents to serverless functions). AgentCore handles scaling, networking, and credential management so you do not need to build that infrastructure yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use Amazon Bedrock models instead of OpenAI?
&lt;/h3&gt;

&lt;p&gt;Yes. The agent supports any provider compatible with Strands Agents, including &lt;a href="https://aws.amazon.com/bedrock/" rel="noopener noreferrer"&gt;Amazon Bedrock&lt;/a&gt;, Anthropic, or Ollama. Change the model in &lt;code&gt;booking_agent.py&lt;/code&gt;. The anti-hallucination techniques work independently of the LLM provider.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between hard hooks and soft steering?
&lt;/h3&gt;

&lt;p&gt;Hard hooks use &lt;code&gt;event.cancel_tool&lt;/code&gt; to block tool execution at the framework level. The LLM cannot bypass them. Soft steering returns a STEER message that instructs the agent how to self-correct: the agent adjusts parameters and completes the task. Use hard hooks for financial and compliance rules. Use soft steering for operational rules where the agent can adapt.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much does this architecture cost?
&lt;/h3&gt;

&lt;p&gt;All AWS services used (DynamoDB on-demand, Lambda, Secrets Manager) are eligible for the &lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;AWS Free Tier&lt;/a&gt;. Neo4j AuraDB Free has a $0/month tier. For current pricing details, see the &lt;a href="https://aws.amazon.com/pricing/" rel="noopener noreferrer"&gt;AWS Pricing&lt;/a&gt; page. For a demo or low-traffic deployment, total infrastructure cost is minimal, excluding LLM API costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need GraphRAG for the agent to work?
&lt;/h3&gt;

&lt;p&gt;No. Stack 1 (&lt;code&gt;HotelBookingAgentStack&lt;/code&gt;) deploys a fully functional booking agent with search, booking, payment, and validation tools. GraphRAG (Stack 2) adds hotel FAQ capabilities. Deploy it when you need answers about amenities, policies, and services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I apply these patterns with other agent frameworks?
&lt;/h3&gt;

&lt;p&gt;Yes. The guardrail patterns (database-driven rules, hard hooks, and soft steering) are framework-agnostic. Strands provides native hook and MCP support, but LangGraph, AutoGen, and CrewAI offer similar extension points for tool interception and pre-validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does the agent discover tools via MCP at runtime?
&lt;/h3&gt;

&lt;p&gt;The agent connects to AgentCore Gateway using &lt;code&gt;MCPClient&lt;/code&gt; from Strands. On startup, it calls &lt;code&gt;list_tools_sync()&lt;/code&gt; to discover all available Lambda tools registered in the Gateway. When the agent needs a tool, the Gateway routes the call based on semantic matching. There is no hardcoded tool list in the agent code. Adding a new tool means registering it in the Gateway; the agent discovers it automatically on the next invocation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Research Background
&lt;/h2&gt;

&lt;p&gt;The techniques in this series are grounded in recent research on AI agent reliability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/pdf/2503.13514" rel="noopener noreferrer"&gt;RAG-KG-IL: Multi-Agent Hybrid Framework for Reducing Hallucinations&lt;/a&gt; — Knowledge graphs reduce hallucinations compared to standalone LLMs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2601.05214" rel="noopener noreferrer"&gt;Internal Representations as Indicators of Hallucinations in Agent Tool Selection&lt;/a&gt; — Tool selection errors increase with tool count; semantic routing mitigates this&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/pdf/2510.19507" rel="noopener noreferrer"&gt;Teaming LLMs to Detect and Mitigate Hallucinations&lt;/a&gt; — Multi-agent validation detects errors that single agents miss&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/pdf/2509.09360" rel="noopener noreferrer"&gt;MetaRAG: Metamorphic Testing for Hallucination Detection&lt;/a&gt; — Hallucinations are inherent to LLMs without structured grounding&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The complete code — CDK stacks, Lambda tools, agent runtime, steering rules, test notebook, and 300 hotel FAQ documents — is in the &lt;a href="https://github.com/elizabethfuentes12/why-agents-fail-sample-for-amazon-agentcore/tree/main/06-agentcore-production-demo" rel="noopener noreferrer"&gt;repository&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Gracias!&lt;/p&gt;

&lt;p&gt;🇻🇪🇨🇱 &lt;a href="https://dev.to/elizabethfuentes12"&gt;Dev.to&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/lizfue/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; &lt;a href="https://github.com/elizabethfuentes12/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://twitter.com/elizabethfue12" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/elifue.tech" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; &lt;a href="https://www.youtube.com/channel/UCr0Gnc-t30m4xyrvsQpNp2Q" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Tiempo real en aplicaciones web: Polling vs Subscriptions</title>
      <dc:creator>Ramses Mata</dc:creator>
      <pubDate>Mon, 30 Mar 2026 18:48:03 +0000</pubDate>
      <link>https://dev.to/aws/tiempo-real-en-aplicaciones-web-polling-vs-subscriptions-2790</link>
      <guid>https://dev.to/aws/tiempo-real-en-aplicaciones-web-polling-vs-subscriptions-2790</guid>
      <description>&lt;p&gt;En este artículo vas a entender las dos estrategias principales para mantener datos frescos en tu aplicación: &lt;strong&gt;Polling&lt;/strong&gt; (tu app pregunta constantemente al servidor) y &lt;strong&gt;Subscriptions&lt;/strong&gt; (el servidor avisa a tu app cuando hay cambios). Vamos a compararlas lado a lado y darte un framework para elegir la correcta según tu caso de uso. Sin atarnos a ninguna tecnología específica, los conceptos aplican ya sea que uses GraphQL, REST, WebSockets, o cualquier otro protocolo o estilo de API.&lt;/p&gt;

&lt;p&gt;Imagina que estás usando una app de chat. Envías un mensaje y... ¿cómo sabe tu app que la otra persona ya respondió? ¿Cómo aparecen las notificaciones sin que tú hagas nada? ¿Cómo se actualizan los marcadores de un partido en vivo?&lt;/p&gt;

&lt;p&gt;Todas estas experiencias tienen algo en común: necesitan datos frescos sin que el usuario tenga que recargar la página. A esto le llamamos &lt;strong&gt;tiempo real&lt;/strong&gt; (o algo muy cercano a ello). Y hay dos formas principales de lograrlo: que tu app le pregunte al servidor constantemente si hay algo nuevo (&lt;strong&gt;Polling&lt;/strong&gt;), o que el servidor le avise a tu app en el momento que algo cambia (&lt;strong&gt;Subscriptions&lt;/strong&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Qué es Polling?
&lt;/h2&gt;

&lt;p&gt;Polling es la estrategia más simple: tu aplicación le pregunta al servidor "¿hay algo nuevo?" cada cierto tiempo.&lt;/p&gt;

&lt;p&gt;Piénsalo como cuando eras niño en un viaje largo por carretera y le preguntabas a tus papás cada 5 minutos: "¿Ya llegamos? ¿Ya llegamos? ¿Ya llegamos?" No importa si la respuesta cambió o no, tú sigues preguntando.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo funciona?
&lt;/h3&gt;

&lt;p&gt;Si eres más visual, este diagrama te puede ayudar a entender el flujo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzepv05hpl46foz76d53h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzepv05hpl46foz76d53h.png" alt="Diagrama: Cómo funciona Polling - timeline de peticiones" width="800" height="1008"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tu app envía una petición al servidor (por ejemplo, cada 5 segundos)&lt;/li&gt;
&lt;li&gt;El servidor responde con los datos más recientes&lt;/li&gt;
&lt;li&gt;Tu app actualiza la interfaz si hay cambios&lt;/li&gt;
&lt;li&gt;Espera el intervalo definido y repite&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;En JavaScript se ve algo así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Polling: preguntar cada 5 segundos&lt;/span&gt;
&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;datos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/mensajes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;actualizarInterfaz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;datos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, ¿verdad? Y esa es precisamente su mayor fortaleza.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ventajas y desventajas del Polling
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ventajas&lt;/th&gt;
&lt;th&gt;Desventajas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Fácil de implementar&lt;/strong&gt;: Es una petición HTTP normal en un loop&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Desperdicio de recursos&lt;/strong&gt;: Si no hay datos nuevos, la petición fue innecesaria&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Funciona con cualquier infraestructura&lt;/strong&gt;: No necesitas nada especial en el servidor&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Latencia&lt;/strong&gt;: Hay un retraso entre que el dato cambia y tu app se entera. Con un intervalo de 5s, en el peor caso tu usuario ve el dato 5 segundos tarde. Con subscriptions, la latencia típica es de ~50-150ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Stateless&lt;/strong&gt;: Cada petición es independiente, no hay conexiones persistentes&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Carga en el servidor&lt;/strong&gt;: Muchos clientes haciendo polling = muchas peticiones constantes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Fácil de debuggear&lt;/strong&gt;: Puedes ver cada petición en las herramientas de desarrollo del navegador&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Tradeoff del intervalo&lt;/strong&gt;: Intervalo corto = más carga, intervalo largo = más retraso&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip importante:&lt;/strong&gt; El intervalo de polling es una decisión de diseño clave. Un dashboard que muestra métricas cada minuto no necesita polling cada segundo. Ajusta el intervalo a lo que tu usuario realmente necesita ver.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ¿Qué son las Subscriptions?
&lt;/h2&gt;

&lt;p&gt;Las Subscriptions invierten el modelo: en lugar de que tu app pregunte constantemente, el servidor le &lt;strong&gt;avisa&lt;/strong&gt; cuando hay algo nuevo.&lt;/p&gt;

&lt;p&gt;Siguiendo con la analogía del viaje: en vez de preguntar "¿ya llegamos?" cada 5 minutos, le dices a tus papás "avísenme cuando lleguemos" y te pones a hacer otra cosa. Ellos te avisan solo cuando es necesario.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo funcionan?
&lt;/h3&gt;

&lt;p&gt;Si eres más visual, este diagrama te puede ayudar a ver la diferencia con polling:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhuh65pppcm6w524je05r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhuh65pppcm6w524je05r.png" alt="Diagrama: Cómo funcionan las Subscriptions - timeline de eventos" width="800" height="841"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tu app establece una &lt;strong&gt;conexión persistente&lt;/strong&gt; con el servidor (generalmente usando &lt;a href="https://developer.mozilla.org/es/docs/Web/API/WebSockets_API" rel="noopener noreferrer"&gt;WebSockets&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Tu app le dice al servidor "avísame cuando cambien los mensajes"&lt;/li&gt;
&lt;li&gt;El servidor mantiene esa conexión abierta&lt;/li&gt;
&lt;li&gt;Cuando hay datos nuevos, el servidor los envía inmediatamente por esa conexión&lt;/li&gt;
&lt;li&gt;Tu app recibe los datos y actualiza la interfaz&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;En JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Subscription: el servidor te avisa&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conexion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/mensajes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;conexion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nuevoDato&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;datos&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="nf"&gt;actualizarInterfaz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;datos&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;Fíjate en la diferencia: no hay intervalo, no hay loop. Tu app simplemente reacciona cuando llegan datos nuevos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ventajas y desventajas de las Subscriptions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ventajas&lt;/th&gt;
&lt;th&gt;Desventajas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Tiempo real verdadero&lt;/strong&gt;: Los datos llegan al instante, sin retraso&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Más complejo de implementar&lt;/strong&gt;: Necesitas manejar conexiones persistentes, reconexiones, y estado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Eficiente&lt;/strong&gt;: Solo se transfieren datos cuando realmente hay cambios&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Infraestructura más sofisticada&lt;/strong&gt;: El servidor necesita soportar WebSockets o protocolos similares&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Menos carga innecesaria&lt;/strong&gt;: No hay peticiones vacías cuando nada cambió&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Stateful&lt;/strong&gt;: El servidor mantiene el estado de cada conexión activa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Mejor experiencia de usuario&lt;/strong&gt;: La app se siente viva y responsiva&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Escalabilidad diferente&lt;/strong&gt;: Miles de conexiones abiertas simultáneas requieren planificación&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip importante:&lt;/strong&gt; Las subscriptions no son magia, por detrás y mientras no vemos a simple vista, tecnologías como &lt;a href="https://developer.mozilla.org/es/docs/Web/API/WebSockets_API" rel="noopener noreferrer"&gt;WebSockets&lt;/a&gt; mantienen una conexión &lt;a href="https://www.fortinet.com/lat/resources/cyberglossary/tcp-ip" rel="noopener noreferrer"&gt;TCP&lt;/a&gt; abierta entre el cliente y el servidor. Esto permite comunicación bidireccional, pero también significa que el servidor necesita gestionar el estado de cada conexión. La buena noticia es que no estamos hablando de tecnología experimental, WebSockets es &lt;a href="https://caniuse.com/websockets" rel="noopener noreferrer"&gt;soportado por más del 97% de los navegadores&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 Conexión con nuestra serie:&lt;/strong&gt; Si vienes siguiendo la serie de GraphQL, te adelanto algo: cuando implementemos subscriptions con &lt;a href="https://aws.amazon.com/appsync/" rel="noopener noreferrer"&gt;AWS AppSync&lt;/a&gt;, no vamos a tener que preocuparnos por manejar WebSockets directamente. AppSync se encarga de toda esa infraestructura por nosotros: conexiones, reconexiones, escalabilidad. Nosotros solo definimos &lt;em&gt;qué&lt;/em&gt; datos queremos escuchar.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Polling vs Subscriptions: Comparación lado a lado
&lt;/h2&gt;

&lt;p&gt;Este diagrama muestra ambos modelos con el mismo escenario para que veas la diferencia de un vistazo:&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspecto&lt;/th&gt;
&lt;th&gt;Polling&lt;/th&gt;
&lt;th&gt;Subscriptions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dirección&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cliente pregunta → Servidor responde&lt;/td&gt;
&lt;td&gt;Servidor avisa → Cliente recibe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latencia&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Depende del intervalo (segundos)&lt;/td&gt;
&lt;td&gt;Casi instantánea (milisegundos)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Carga en servidor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Constante (aunque no haya cambios)&lt;/td&gt;
&lt;td&gt;Solo cuando hay datos nuevos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complejidad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Baja&lt;/td&gt;
&lt;td&gt;Media-Alta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infraestructura&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTTP estándar&lt;/td&gt;
&lt;td&gt;WebSockets u otro protocolo persistente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Estado&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stateless&lt;/td&gt;
&lt;td&gt;Stateful&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Escalabilidad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Más peticiones = más carga&lt;/td&gt;
&lt;td&gt;Más conexiones = más memoria&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fácil (peticiones HTTP normales)&lt;/td&gt;
&lt;td&gt;Más complejo (conexiones persistentes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Para ponerlo en perspectiva: si tienes 1,000 usuarios conectados con polling cada 5 segundos, tu servidor recibe 12,000 peticiones por minuto aunque no haya cambiado absolutamente nada. Con subscriptions, esas 12,000 peticiones se convierten en 0 hasta que realmente haya datos nuevos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuándo gana Polling?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Datos que cambian con poca frecuencia (dashboard de métricas diarias)&lt;/li&gt;
&lt;li&gt;Infraestructura simple sin soporte para WebSockets&lt;/li&gt;
&lt;li&gt;Cuando la latencia de unos segundos es aceptable&lt;/li&gt;
&lt;li&gt;Prototipos rápidos donde la simplicidad importa más&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ¿Cuándo ganan las Subscriptions?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Datos que cambian constantemente (chat, colaboración en tiempo real)&lt;/li&gt;
&lt;li&gt;La latencia baja es crítica (trading, juegos, notificaciones)&lt;/li&gt;
&lt;li&gt;Muchos eventos por segundo donde polling sería ineficiente&lt;/li&gt;
&lt;li&gt;Experiencias donde el usuario espera actualizaciones instantáneas&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip:&lt;/strong&gt; La complejidad de implementar subscriptions se reduce significativamente cuando usas servicios administrados que manejan la infraestructura de WebSockets por ti.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ¿Cuándo usar Polling y cuándo usar Subscriptions?
&lt;/h2&gt;

&lt;p&gt;No hay un ganador universal. La mejor opción depende de tu caso de uso específico. Aquí tienes algunos ejemplos para guiarte:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Caso de uso&lt;/th&gt;
&lt;th&gt;Enfoque recomendado&lt;/th&gt;
&lt;th&gt;¿Por qué?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chat en tiempo real&lt;/td&gt;
&lt;td&gt;Subscriptions&lt;/td&gt;
&lt;td&gt;Los usuarios esperan ver mensajes al instante&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dashboard de analytics&lt;/td&gt;
&lt;td&gt;Polling&lt;/td&gt;
&lt;td&gt;Los datos se actualizan cada minutos u horas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feed de redes sociales&lt;/td&gt;
&lt;td&gt;Polling (o híbrido)&lt;/td&gt;
&lt;td&gt;No necesitas ver cada like al instante&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marcadores deportivos en vivo&lt;/td&gt;
&lt;td&gt;Subscriptions&lt;/td&gt;
&lt;td&gt;Cada segundo cuenta para la experiencia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notificaciones&lt;/td&gt;
&lt;td&gt;Subscriptions&lt;/td&gt;
&lt;td&gt;El usuario espera recibirlas inmediatamente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reporte de inventario&lt;/td&gt;
&lt;td&gt;Polling&lt;/td&gt;
&lt;td&gt;Se consulta periódicamente, no en tiempo real&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor colaborativo (tipo Google Docs)&lt;/td&gt;
&lt;td&gt;Subscriptions&lt;/td&gt;
&lt;td&gt;Cada keystroke necesita sincronizarse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  El enfoque híbrido
&lt;/h3&gt;

&lt;p&gt;Algo que vale la pena mencionar: no siempre tienes que elegir uno u otro. Muchas aplicaciones usan &lt;strong&gt;ambos enfoques&lt;/strong&gt; dependiendo de la funcionalidad.&lt;/p&gt;

&lt;p&gt;Por ejemplo, una app de delivery podría usar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Subscriptions&lt;/strong&gt; para el tracking en tiempo real del repartidor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polling&lt;/strong&gt; para actualizar el catálogo de restaurantes cada cierto tiempo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La clave es preguntarte: &lt;strong&gt;¿Qué tan rápido necesita mi usuario ver este cambio?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Si la respuesta es "inmediatamente" → Subscriptions&lt;/li&gt;
&lt;li&gt;Si la respuesta es "en los próximos segundos o minutos está bien" → Polling&lt;/li&gt;
&lt;li&gt;Si no estás seguro → Empieza con polling (es más simple) y migra a subscriptions si la experiencia lo requiere&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué es long polling?
&lt;/h3&gt;

&lt;p&gt;Long polling es un punto intermedio entre polling y subscriptions. En vez de preguntar cada X segundos y recibir una respuesta inmediata (aunque esté vacía), tu app hace una petición y el servidor &lt;strong&gt;la mantiene abierta&lt;/strong&gt; hasta que tenga datos nuevos. Cuando responde, tu app inmediatamente hace otra petición. Es más eficiente que polling tradicional pero no tan eficiente como subscriptions con WebSockets.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Las subscriptions consumen más recursos que polling?
&lt;/h3&gt;

&lt;p&gt;Depende. Las subscriptions mantienen conexiones abiertas, lo que consume memoria en el servidor por cada cliente conectado. Polling consume ancho de banda y CPU por cada petición repetida. Con pocos usuarios y cambios frecuentes, subscriptions es más eficiente. Con muchos usuarios y cambios poco frecuentes, polling puede ser más simple de escalar.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Se pueden combinar polling y subscriptions?
&lt;/h3&gt;

&lt;p&gt;Sí, y es más común de lo que parece. Muchas aplicaciones usan subscriptions para funcionalidades que necesitan tiempo real (como chat o notificaciones) y polling para datos que cambian con menos frecuencia (como un catálogo de productos). No tienes que elegir uno para toda tu aplicación, puedes usar el enfoque que mejor se adapte a cada funcionalidad.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Recapitulemos lo que aprendimos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polling&lt;/strong&gt; = tu app pregunta constantemente. Simple, stateless, pero puede ser ineficiente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscriptions&lt;/strong&gt; = el servidor te avisa. Eficiente y en tiempo real, pero más complejo&lt;/li&gt;
&lt;li&gt;No hay un "ganador" cada enfoque tiene su lugar dependiendo de tu caso de uso&lt;/li&gt;
&lt;li&gt;Los enfoques híbridos son completamente válidos y muy comunes en aplicaciones reales&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La próxima vez que estés diseñando una funcionalidad que necesite datos frescos, ya tienes las herramientas conceptuales para tomar una decisión informada.&lt;/p&gt;

&lt;p&gt;En el próximo artículo de la serie, vamos a poner estos conceptos en práctica implementando &lt;strong&gt;subscriptions en nuestra API de GraphQL con AWS AppSync&lt;/strong&gt;. ¿Recuerdas toda la complejidad que mencionamos sobre manejar WebSockets, conexiones persistentes y reconexiones? AppSync se encarga de todo eso por nosotros, por debajo usa los mismos WebSockets que explicamos aquí, pero nosotros solo necesitamos definir qué datos queremos escuchar. Vamos a ver cómo pasar de la teoría a código funcionando.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
      <category>spanish</category>
    </item>
    <item>
      <title>8 Agents Wrote Perfect Components - And Nothing Worked</title>
      <dc:creator>Dennis Traub</dc:creator>
      <pubDate>Fri, 27 Mar 2026 22:50:44 +0000</pubDate>
      <link>https://dev.to/aws/8-agents-wrote-perfect-components-and-nothing-worked-2176</link>
      <guid>https://dev.to/aws/8-agents-wrote-perfect-components-and-nothing-worked-2176</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Parallel AI agents don't coordinate on shared contracts, such as column names, URL paths, parameter formats, or identifiers. Extract those contracts into a single reference file before generation, and run a review agent that traces end-to-end data flows once the parallel agents are done. This single step fixed all 17 bugs in one pass.&lt;/p&gt;




&lt;p&gt;I launched 8 AI agents in parallel to build a full-stack app on AWS: infrastructure stacks, a React frontend, and a Java backend. Each agent owned one piece, and they all delivered clean, compiling code. The CDK type-checked, the Java backend followed Spring Boot conventions, the React UI looked nice.&lt;/p&gt;

&lt;p&gt;But when I tried to wire them together I hit bugs at every single boundary.&lt;/p&gt;

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

&lt;p&gt;A full-stack app on AWS with a lot of moving parts. Multiple CDK stacks for the infrastructure (IAM, VPC, DB with seed functions, Cognito, CodePipeline, CloudFront/WAF), a Spring Boot backend on ECS Fargate, and a React frontend hosted on S3.&lt;/p&gt;

&lt;p&gt;The implementation plan was thorough and covered every component. But it wasn't detailed enough for agents that need to agree on shared contracts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bugs
&lt;/h2&gt;

&lt;p&gt;The first two block everything. Bugs 3 through 5 only show up after you fix the previous ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 1: The Spring Boot app won't even start
&lt;/h3&gt;

&lt;p&gt;The seed data function creates a schema with &lt;code&gt;passenger_id&lt;/code&gt; and &lt;code&gt;full_name&lt;/code&gt;, but the Spring Boot entity maps to &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Agent 1: seed data function creates the schema&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;passengers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;passenger_id&lt;/span&gt;   &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;full_name&lt;/span&gt;      &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Agent 2: The Spring Boot entity maps the table&lt;/span&gt;
&lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;// Schema says "passenger_id"&lt;/span&gt;
&lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;// Schema says "full_name"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;ddl-auto: validate&lt;/code&gt;, Hibernate checks the mapping on startup. But the columns don't exist, so the ECS task crashes before serving a single request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 2: Every call returns 404
&lt;/h3&gt;

&lt;p&gt;The CDK stack registers ALB routes for /approve and /generate while the Java client sends requests to /voucher/approve and /voucher/generate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CDK ALB routes:  /approve, /generate
Java client:     /voucher/approve, /voucher/generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both agents wrote correct, working code in isolation, but the CDK stack used clean paths while the Java client added a service prefix. Neither checked the other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 3: Missing request fields
&lt;/h3&gt;

&lt;p&gt;A downstream service validates four required fields. The Java client sends three:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lambda expects:  escalationId, passengerId, amount, situation
Java sends:      escalationId, passengerId, amount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even with the URLs from bug 2 fixed, every approval returns 400.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 4: User lookup doesn't work
&lt;/h3&gt;

&lt;p&gt;This one was the most interesting: three systems work with the user, and each of them created their own identifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cognito custom attribute:  custom:passenger_id = "pax-a1b2c3d4-e5f6-..."
RDS seed data:             passenger_id = "PAX-a1b2c3d4-e5f6-..."
JWT subject claim:         sub = "a1b2c3d4-e5f6-..."  (Cognito UUID)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend uses &lt;code&gt;jwt.getSubject()&lt;/code&gt; to look up the user. That's a Cognito UUID - neither prefixed with &lt;code&gt;pax-&lt;/code&gt; nor with &lt;code&gt;PAX-&lt;/code&gt;. No user lookup ever returns a result.&lt;/p&gt;

&lt;p&gt;Three agents. Three naming conventions. Zero coordination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 5: Every status lookup returns "not found"
&lt;/h3&gt;

&lt;p&gt;A downstream service returns JSON. The Java client parses XML:&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="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FOUND_LOCAL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Warehouse-B-Shelf-47"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extractXmlElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Looks for &amp;lt;status&amp;gt;...&amp;lt;/status&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No XML tags in a JSON string. &lt;code&gt;extractXmlElement&lt;/code&gt; returns empty for every single request.&lt;/p&gt;

&lt;p&gt;The agent that wrote the downstream service followed one spec (JSON). The agent that wrote the Java client followed a different spec (XML).&lt;/p&gt;

&lt;h3&gt;
  
  
  Bugs 6 to 17: SSM parameter path mismatches
&lt;/h3&gt;

&lt;p&gt;One CDK stack writes an SSM parameter. Another CDK stack reads it. But they never coordinated on paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Producer stack writes:  /${AppName}/test/data/rds-secret-arn
Consumer stack reads:   /${AppName}/${Env}/data/rds-password-secret-arn

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

&lt;/div&gt;



&lt;p&gt;Twelve SSM parameters mismatched between producer and consumer stacks. The app fails on every one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why parallel agents can't catch this
&lt;/h2&gt;

&lt;p&gt;Each agent had context about the overall plan and its own component. But none of them could see the implementation details that the others came up with.&lt;/p&gt;

&lt;p&gt;When I write an app, I hold the contracts in working memory. "The column is &lt;code&gt;passenger_id&lt;/code&gt;, so I'll use that in both the migration and the entity." But an AI agent writing the migration doesn't know what the entity agent chose for its column name - and vice versa.&lt;/p&gt;

&lt;p&gt;The plan contained all the high-level information, but the agents were reading different sections and making their own calls on the shared details.&lt;/p&gt;

&lt;p&gt;Each agent wrote correct code that followed good conventions. But they never coordinated. Like digging a tunnel from two sides of a mountain - without ever checking in with each other. &lt;/p&gt;

&lt;h2&gt;
  
  
  How I found all of them at once
&lt;/h2&gt;

&lt;p&gt;After generation, before actually deploying the app. I ran an architecture review agent with a simple instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trace the actual data flow from user login through form submission to the downstream service calls, following every cross-component boundary.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It found every one of the bugs in a single pass.&lt;/p&gt;

&lt;p&gt;The review agent started at the user-facing entry point, traced the request through every boundary, and at each one checked whether what one component sent actually matched what the next one expected. Same thing integration tests do after deployment, but you catch it before deploying anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prevent seam bugs
&lt;/h2&gt;

&lt;p&gt;Before launching parallel agents, pull every shared contract out of the plan into a single reference file and pass it to every agent as mandatory context.&lt;/p&gt;

&lt;p&gt;Then, after your parallel agents did their thing, run a review agent that traces a few real user flows across all the boundaries.&lt;/p&gt;

&lt;p&gt;Fix the seam bugs in one pass, then deploy.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  What are seam bugs in AI-generated code?
&lt;/h3&gt;

&lt;p&gt;Seam bugs are integration defects at the boundaries between components built by different AI agents. Each agent writes correct, working code in isolation, but the components don't fit together because the agents each made their own decisions about shared details - things like what a column is called, what path an API lives at, or what format an identifier uses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does parallel AI code generation produce integration bugs?
&lt;/h3&gt;

&lt;p&gt;Each agent only sees its own component and the plan it was given. When two agents need to agree on something - say, what a database column is called - they each pick a reasonable name independently. Those names often don't match. The plan says what the column should represent, but not necessarily the exact string both sides should use.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you catch integration bugs from parallel AI agents?
&lt;/h3&gt;

&lt;p&gt;Run a single review agent after generation that traces real user flows across all the boundaries. Give it a prompt like "trace the data flow from user login through the frontend, backend, to databases and downstream service calls, checking every boundary." It will catch the mismatches in one pass.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
    <item>
      <title>My 8 Agents Wrote Perfect Components - And Nothing Worked</title>
      <dc:creator>Dennis Traub</dc:creator>
      <pubDate>Fri, 27 Mar 2026 22:50:44 +0000</pubDate>
      <link>https://dev.to/aws/8-agents-wrote-perfect-components-and-nothing-worked-5h2b</link>
      <guid>https://dev.to/aws/8-agents-wrote-perfect-components-and-nothing-worked-5h2b</guid>
      <description>&lt;p&gt;I launched 8 AI agents in parallel to build a full-stack app on AWS: infrastructure stacks, a React frontend, and a Java backend. Each agent owned one piece, and they all delivered clean, compiling code. The CDK type-checked, the Java backend followed Spring Boot conventions, the React UI looked nice.&lt;/p&gt;

&lt;p&gt;But when I tried to wire them together I hit bugs at every single boundary.&lt;/p&gt;

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

&lt;p&gt;A full-stack app on AWS with a lot of moving parts. Multiple CDK stacks for the infrastructure (IAM, VPC, DB with seed functions, Cognito, CodePipeline, CloudFront/WAF), a Spring Boot backend on ECS Fargate, and a React frontend hosted on S3.&lt;/p&gt;

&lt;p&gt;The implementation plan was thorough and covered every component. But it wasn't detailed enough for agents that need to agree on shared contracts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bugs
&lt;/h2&gt;

&lt;p&gt;The first two block everything. Bugs 3 through 5 only show up after you fix the previous ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 1: The Spring Boot app won't even start
&lt;/h3&gt;

&lt;p&gt;The seed data function creates a schema with &lt;code&gt;passenger_id&lt;/code&gt; and &lt;code&gt;full_name&lt;/code&gt;, but the Spring Boot entity maps to &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Agent 1: seed data function creates the schema&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;passengers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;passenger_id&lt;/span&gt;   &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;full_name&lt;/span&gt;      &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Agent 2: The Spring Boot entity maps the table&lt;/span&gt;
&lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;// Schema says "passenger_id"&lt;/span&gt;
&lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;// Schema says "full_name"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;ddl-auto: validate&lt;/code&gt;, Hibernate checks the mapping on startup. But the columns don't exist, so the ECS task crashes before serving a single request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 2: Every call returns 404
&lt;/h3&gt;

&lt;p&gt;The CDK stack registers ALB routes for /approve and /generate while the Java client sends requests to /voucher/approve and /voucher/generate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CDK ALB routes:  /approve, /generate
Java client:     /voucher/approve, /voucher/generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both agents wrote correct, working code in isolation, but the CDK stack used clean paths while the Java client added a service prefix. Neither checked the other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 3: Missing request fields
&lt;/h3&gt;

&lt;p&gt;A downstream service validates four required fields. The Java client sends three:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lambda expects:  escalationId, passengerId, amount, situation
Java sends:      escalationId, passengerId, amount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even with the URLs from bug 2 fixed, every approval returns 400.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 4: User lookup doesn't work
&lt;/h3&gt;

&lt;p&gt;This one was the most interesting: three systems work with the user, and each of them created their own identifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cognito custom attribute:  custom:passenger_id = "pax-a1b2c3d4-e5f6-..."
RDS seed data:             passenger_id = "PAX-a1b2c3d4-e5f6-..."
JWT subject claim:         sub = "a1b2c3d4-e5f6-..."  (Cognito UUID)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend uses &lt;code&gt;jwt.getSubject()&lt;/code&gt; to look up the user. That's a Cognito UUID - neither prefixed with &lt;code&gt;pax-&lt;/code&gt; nor with &lt;code&gt;PAX-&lt;/code&gt;. No user lookup ever returns a result.&lt;/p&gt;

&lt;p&gt;Three agents. Three naming conventions. Zero coordination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 5: Every status lookup returns "not found"
&lt;/h3&gt;

&lt;p&gt;A downstream service returns JSON. The Java client parses XML:&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="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FOUND_LOCAL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Warehouse-B-Shelf-47"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extractXmlElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Looks for &amp;lt;status&amp;gt;...&amp;lt;/status&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No XML tags in a JSON string. &lt;code&gt;extractXmlElement&lt;/code&gt; returns empty for every single request.&lt;/p&gt;

&lt;p&gt;The agent that wrote the downstream service followed one spec (JSON). The agent that wrote the Java client followed a different spec (XML).&lt;/p&gt;

&lt;h3&gt;
  
  
  Bugs 6 to 17: SSM parameter path mismatches
&lt;/h3&gt;

&lt;p&gt;One CDK stack writes an SSM parameter. Another CDK stack reads it. But they never coordinated on paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Producer stack writes:  /${AppName}/test/data/rds-secret-arn
Consumer stack reads:   /${AppName}/${Env}/data/rds-password-secret-arn

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

&lt;/div&gt;



&lt;p&gt;Twelve SSM parameters mismatched between producer and consumer stacks. The app fails on every one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why parallel agents can't catch this
&lt;/h2&gt;

&lt;p&gt;Each agent had context about the overall plan and its own component. But none of them could see the implementation details that the others came up with.&lt;/p&gt;

&lt;p&gt;When I write an app, I hold the contracts in working memory. "The column is &lt;code&gt;passenger_id&lt;/code&gt;, so I'll use that in both the migration and the entity." But an AI agent writing the migration doesn't know what the entity agent chose for its column name - and vice versa.&lt;/p&gt;

&lt;p&gt;The plan contained all the high-level information, but the agents were reading different sections and making their own calls on the shared details.&lt;/p&gt;

&lt;p&gt;Each agent wrote correct code that followed good conventions. But they never coordinated. Like digging a tunnel from two sides of a mountain - without ever checking in with each other. &lt;/p&gt;

&lt;h2&gt;
  
  
  How I found all of them at once
&lt;/h2&gt;

&lt;p&gt;After generation, before actually deploying the app. I ran an architecture review agent with a simple instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trace the actual data flow from user login through form 
submission to the downstream service calls, following every 
cross-component boundary.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It found every one of the bugs in a single pass.&lt;/p&gt;

&lt;p&gt;The review agent started at the user-facing entry point, traced the request through every boundary, and at each one checked whether what one component sent actually matched what the next one expected. Same thing integration tests do after deployment, but you catch it before deploying anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prevent seam bugs
&lt;/h2&gt;

&lt;p&gt;Before launching parallel agents, pull every shared contract out of the plan into a single reference file and pass it to every agent as mandatory context.&lt;/p&gt;

&lt;p&gt;Then, after your parallel agents did their thing, run a review agent that traces a few real user flows across all the boundaries.&lt;/p&gt;

&lt;p&gt;Fix the seam bugs in one pass, then deploy.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  What are seam bugs in AI-generated code?
&lt;/h3&gt;

&lt;p&gt;Seam bugs are integration defects at the boundaries between components built by different AI agents. Each agent writes correct, working code in isolation, but the components don't fit together because the agents each made their own decisions about shared details - things like what a column is called, what path an API lives at, or what format an identifier uses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does parallel AI code generation produce integration bugs?
&lt;/h3&gt;

&lt;p&gt;Each agent only sees its own component and the plan it was given. When two agents need to agree on something - say, what a database column is called - they each pick a reasonable name independently. Those names often don't match. The plan says what the column should represent, but not necessarily the exact string both sides should use.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you catch integration bugs from parallel AI agents?
&lt;/h3&gt;

&lt;p&gt;Run a single review agent after generation that traces real user flows across all the boundaries. Give it a prompt like "trace the data flow from user login through the frontend, backend, to databases and downstream service calls, checking every boundary." It will catch the mismatches in one pass.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I trained a computer vision model on the AWS Free Tier</title>
      <dc:creator>Esin Saribudak</dc:creator>
      <pubDate>Fri, 27 Mar 2026 01:20:43 +0000</pubDate>
      <link>https://dev.to/aws/how-i-trained-a-computer-vision-model-on-the-aws-free-tier-4823</link>
      <guid>https://dev.to/aws/how-i-trained-a-computer-vision-model-on-the-aws-free-tier-4823</guid>
      <description>&lt;p&gt;&lt;em&gt;Last updated: March 26, 2026&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Developers hide Easter eggs in code. Filmmakers hide them in scenes. I hid them in AI-generated images and asked Amazon Rekognition to find them.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you've ever wondered whether &lt;a href="https://aws.amazon.com/free/?trk=5ccc714b-b822-49c5-920c-aaaeda832ce7&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AWS Free Tier&lt;/a&gt; gives you enough credits to build something cool, this post is for you. I built an Easter egg detector powered by Amazon Rekognition, tested its standard object detection API on 37 images, and trained a custom model, all with my AWS Free Tier credits.&lt;/p&gt;

&lt;p&gt;(If you haven't set up a Free Tier account, check out this great &lt;a href="https://builder.aws.com/content/2lYwQJ47Sq8C76VkFUDisXB7VnN/secure-foundation-setting-up-your-aws-free-tier-account" rel="noopener noreferrer"&gt;guide&lt;/a&gt;!)&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;I wanted to build a simple and fun app where I can upload a photo and have the app tell me if there's an Easter egg in it. It sounds kind of silly, but the technical problem it maps to — locating a small, specific object in a complex scene — is a foundational one for the field of &lt;a href="https://en.wikipedia.org/wiki/Computer_vision" rel="noopener noreferrer"&gt;computer vision&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;AWS has a lot of services for different AI/ML use cases, including &lt;a href="https://aws.amazon.com/rekognition/" rel="noopener noreferrer"&gt;Amazon Rekognition&lt;/a&gt; for computer vision. I learned that &lt;a href="https://aws.amazon.com/rekognition/customers/" rel="noopener noreferrer"&gt;organizations&lt;/a&gt; like C-SPAN and the NFL use it to understand what's in their images and video. And Amazon Rekognition is available on the AWS Free Tier, which makes experimenting with it easier. &lt;/p&gt;

&lt;p&gt;The service gives you different ways to figure out what's in images. The &lt;a href="https://docs.aws.amazon.com/rekognition/latest/APIReference/API_DetectLabels.html" rel="noopener noreferrer"&gt;&lt;code&gt;DetectLabels&lt;/code&gt;&lt;/a&gt; API works out of the box and recognizes thousands of common objects and concepts, everything from aircraft to zucchini to birthday parties. &lt;a href="https://docs.aws.amazon.com/rekognition/latest/customlabels-dg/what-is.html" rel="noopener noreferrer"&gt;Custom Labels&lt;/a&gt; let you train your own computer vision model when the built-in labels fall short. With this project, I wanted to find out where the first one stops being useful and the other is needed. &lt;/p&gt;

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

&lt;p&gt;The app I built to explore that question is a &lt;a href="https://streamlit.io/" rel="noopener noreferrer"&gt;Streamlit&lt;/a&gt; app with two modes. Standard mode sends your image to the &lt;code&gt;DetectLabels&lt;/code&gt; API and checks if it returns "Egg" or "Easter Egg" in the labels. Custom Labels mode uses a custom model I trained on my own images. Both draw bounding boxes around any eggs they find.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwdt2gzfsx07nwe4cuc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwdt2gzfsx07nwe4cuc8.png" alt="App detecting 4 Easter eggs in an astronaut scene, with bounding boxes and confidence scores from 52% to 66.8%" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The application code is about 200 lines of Python, 100 lines of which is UI-related, and you can check it out at this &lt;a href="https://github.com/esin87/easter-egg-detector-sample-for-amazon-rekognition" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;. You upload an image, the app calls Rekognition using the AWS SDK for Python (Boto3), checks the response, and draws some boxes around any eggs that it finds. &lt;/p&gt;

&lt;p&gt;Here’s an example of code that handles regular &lt;code&gt;DetectLabels&lt;/code&gt; requests.&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rekognition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detect_labels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Image&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;Bytes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;image_bytes&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;MaxLabels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MinConfidence&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;30.0&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;h2&gt;
  
  
  Generating test images
&lt;/h2&gt;

&lt;p&gt;I needed images with scenes where Easter eggs would be unexpected. I used an &lt;a href="https://partyrock.aws/" rel="noopener noreferrer"&gt;image generator&lt;/a&gt; to create 19 images: a messy desk with an egg tucked next to the monitor, a toy box with a painted Easter egg between two teddy bears, a picnic spread with a couple eggs in between the sandwiches.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqx660jousw8lyn6lbb1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqx660jousw8lyn6lbb1e.png" alt="AI-generated picnic scene with colorful Easter eggs hidden among sandwiches, chips, and lemonade on a blanket" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I generated 18 images with no eggs at all, but similar to the other scenes. That gave me a relatively balanced dataset to start with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3bw55rmy3z1tds3ebtk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3bw55rmy3z1tds3ebtk.png" alt="Test image: sunny picnic scene with sandwiches, chips, and a straw hat but no hidden Easter eggs" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 1: DetectLabels
&lt;/h2&gt;

&lt;p&gt;I wrote a batch test script (also in the GitHub repo) that ran every image I had through &lt;code&gt;DetectLabels&lt;/code&gt; and checked whether Rekognition found an egg.&lt;/p&gt;

&lt;p&gt;The results at a 30% confidence threshold:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Accuracy&lt;/td&gt;
&lt;td&gt;75.70%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Precision&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recall&lt;/td&gt;
&lt;td&gt;52.60%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;100% precision means it never hallucinated an egg where there wasn't one in the no-egg images. But 52.6% recall means it missed about half the eggs in the egg images. Imagine you're doing an Easter egg hunt, and there are 19 eggs hidden in the yard. A recall of 52.6% means you only found 10 of them.&lt;/p&gt;

&lt;p&gt;At a stricter 70% confidence threshold, recall dropped to 36.8%, so you'd only find about 7 out of 19.&lt;/p&gt;

&lt;p&gt;I added logging to see what objects Rekognition detected for the missed eggs. The logged objects were pretty accurate, which told me that Rekognition identified the gist of the images well but couldn't "see" the Easter eggs. &lt;/p&gt;

&lt;p&gt;I realized the out-of-the-box API was great for detecting Easter eggs in more typical scenes, like this one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F754iz80q7j2bwc7yaxtn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F754iz80q7j2bwc7yaxtn.png" alt="Colorful painted Easter eggs nestled in green grass on a sunny day" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Script output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ✅ 1774389521556_img.png eggs: Egg (99.9%), Easter Egg (99.1%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;DetectLabels&lt;/code&gt; had a harder time finding Easter eggs in less obvious scenarios or with more competing objects, like this one. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ektpc6903gcyj1b3xi8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ektpc6903gcyj1b3xi8.png" alt="A painted Easter egg hidden among teddy bears, crayons, and building blocks in a toy box" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Script output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ MISS 1774389716587_img.png eggs: none
↳ saw instead: Toy (98.7%), Teddy Bear (93.5%), Lego Set (69.8%), 
   Plush (53.7%), Doll (48.1%), Play Area (48.0%), Handicraft (47.7%), 
   Hedgehog (46.9%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Round 2: Custom Labels
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/rekognition/latest/customlabels-dg/what-is.html" rel="noopener noreferrer"&gt;Custom Labels&lt;/a&gt; exist for situations where you need to detect specific objects in specific scenes that go beyond the capabilities of &lt;code&gt;DetectLabels&lt;/code&gt;, like my Easter egg images. With Custom Labels, you can train a computer vision model on your own images. The model can learn to spot what you care about, even when the object is small, partially hidden, or surrounded by lots of visual noise. The benefit here is that rather than training a computer vision model from scratch on tens of thousands of images, you can build on existing capabilities by just uploading a relatively small set of training images. Then, Rekognition handles the model training and evaluation process for you. &lt;/p&gt;

&lt;p&gt;I did this all directly in the Amazon Rekognition console, where I created a new project and uploaded my dataset of images. Rekognition automatically split my data 80/20 for training and testing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kcswjoxp62qf0i7lfgy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kcswjoxp62qf0i7lfgy.png" alt="Amazon Rekognition Custom Labels console showing the dataset page with 37 uploaded training images" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I created my custom label of "Easter egg" and drew bounding boxes around the Easter eggs in my dataset images, then hit Train. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9juns5ixxh9kpmgm2j7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9juns5ixxh9kpmgm2j7o.png" alt="Custom Labels console labeling view with bounding boxes drawn around Easter eggs on a cluttered desk scene" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Training took around 30 minutes. When it finished, I got a model ARN and performance metrics. Here's how the custom model did on the test dataset compared to the standard DetectLabels API:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;DetectLabels (30% threshold)&lt;/th&gt;
&lt;th&gt;Custom Labels&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Precision&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;77.80%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recall&lt;/td&gt;
&lt;td&gt;52.60%&lt;/td&gt;
&lt;td&gt;75%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F1 score&lt;/td&gt;
&lt;td&gt;(not calculated)&lt;/td&gt;
&lt;td&gt;0.764&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Recall jumped from 52.6% to 75%. This meant that the custom model was finding eggs that &lt;code&gt;DetectLabels&lt;/code&gt; missed entirely. Precision dropped from 100% to 77.8% (more false positives), meaning it occasionally saw an egg where there wasn’t one, but I think that's a reasonable trade-off for catching 22% more of the eggs.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/F-score" rel="noopener noreferrer"&gt;F1 score&lt;/a&gt; is a metric that shows how well the model balances precision and recall, indicating its overall accuracy. A score of 0.764 from a dataset of 37 images is a good starting point. &lt;/p&gt;

&lt;p&gt;Going back to the Easter egg hunt analogy, &lt;code&gt;DetectLabels&lt;/code&gt; was the careful egg hunter who only picked up real eggs but walked past half of them. The custom label found most of the eggs but also grabbed a few eggish-looking rocks along the way in the test dataset.&lt;/p&gt;

&lt;p&gt;Considering the fact that I only had 37 total images in the dataset, these are promising results, but there's definitely room to improve. I'd expect the model's performance to climb if I added more training data. But this experiment was for learning and my own curiosity, and that F1 score worked for me. I plugged my custom model’s ARN into my Streamlit app, and uploaded a few images. The custom model did a pretty good job of detecting Easter eggs on new images that weren’t part of the training dataset. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqdjwvhxzj54uc03mvgb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqdjwvhxzj54uc03mvgb.png" alt="Egg or Not app in Custom Labels mode detecting one Easter egg in a grocery store aisle at 75% confidence" width="800" height="685"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also was pretty good about not registering false positives, even with temptingly bright round objects in the image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vejawc2r9af4wrady0b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vejawc2r9af4wrady0b.png" alt="Egg or Not app correctly reporting no eggs found in a grocery store produce aisle image" width="800" height="669"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost
&lt;/h2&gt;

&lt;p&gt;Everything in this project fits comfortably within the AWS Free Tier credits for new accounts. Here are some more details on the &lt;a href="https://aws.amazon.com/rekognition/pricing/" rel="noopener noreferrer"&gt;pricing&lt;/a&gt; for training and inferencing with a custom model in the us-east-1 region: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DetectLabels (Group 2 API)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;74 images × $0.001/image = $0.074 

&lt;ul&gt;
&lt;li&gt;I had 37 images, but I ran the batch analysis script twice (first checking for egg presence, then again to log the labels that were found for false negatives).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Custom Labels training:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;0.52 hours × $1/hour = $0.52

&lt;ul&gt;
&lt;li&gt;The docs note that Amazon Rekognition may run multiple compute resources in parallel, so actual billed hours can be higher than elapsed time for model training. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Custom Labels inferencing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1.41 hours x $4/hour = $5.71&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build this in your own account, be sure to STOP the model when you’re done inferencing to avoid accumulating further charges. In total for this project, I was billed for 85.7 inference minutes and 31 training minutes, which amounted to $6.24. This amount was deducted from my Free Tier credits. (The charges for other services in the following cost breakdown image are related to other Free Tier projects.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq8hmuudsj9ovtb86hfqd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq8hmuudsj9ovtb86hfqd.png" alt="AWS Cost Explorer showing March 2026 breakdown: Rekognition $6.24, EC2 $0.27, VPC $0.13, S3 $0.01" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned, and what I'd do differently
&lt;/h2&gt;

&lt;p&gt;The jump from &lt;code&gt;DetectLabels&lt;/code&gt; to Custom Labels was the most interesting part of this project for me. With Custom Labels, you get to label your own data, train a model, evaluate its performance, and iterate. That loop (data → train → evaluate → improve) is the same loop behind every ML system. Custom Labels just makes it accessible without needing to have ML experience or having to spin up your own dedicated hardware.&lt;/p&gt;

&lt;p&gt;I also came away with a better sense of when to use what. You could send an image to one of the newer multimodal foundation models and ask, "Are there any Easter eggs in this picture?" It would probably give you a very good answer. But for a tightly-scoped detection task like this one, I think a purpose-built service is a better fit. A single API call returns predictable JSON with labels, confidence scores, and bounding box coordinates. No system prompt to write, no natural language to parse. And the whole project cost me $6.24 in Free Tier credits.&lt;/p&gt;

&lt;p&gt;A couple things I'd change if I were starting over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create more images for the dataset. The docs say that most custom models need a few hundred images or less, but I only had 37. I'd aim for 50+ per class (Easter egg and no Easter egg). I'd also set up a script to generate text for image prompts and call an image generating API to quickly create a bunch of training and test images. &lt;/li&gt;
&lt;li&gt;Test with real photos, not just AI-generated ones. Generated images have a certain look to them. Real phone photos with unexpected lighting and odd angles would be a tougher test.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The full code and installation directions are on GitHub &lt;a href="https://github.com/esin87/easter-egg-detector-sample-for-amazon-rekognition" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to skip the training and just play with DetectLabels, the standard mode works immediately. Upload a photo of your breakfast and see what happens. 🍳&lt;/p&gt;

&lt;p&gt;If you want more ideas for Free Tier projects, let me know in the comments!&lt;/p&gt;

</description>
      <category>computervision</category>
      <category>aws</category>
      <category>beginners</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>AI Agents Don’t Need Complex Workflows. Build One in Python in 10 Minutes</title>
      <dc:creator>Morgan Willis</dc:creator>
      <pubDate>Thu, 26 Mar 2026 22:16:10 +0000</pubDate>
      <link>https://dev.to/aws/ai-agents-dont-need-complex-workflows-build-one-in-python-in-10-minutes-2m5d</link>
      <guid>https://dev.to/aws/ai-agents-dont-need-complex-workflows-build-one-in-python-in-10-minutes-2m5d</guid>
      <description>&lt;p&gt;Building an AI agent in Python can be as easy as giving a model some tools and letting it figure out the rest.&lt;/p&gt;

&lt;p&gt;Most agent setups start the same way: you wire up tool calls, manage retries, track state, and write the routing logic that decides what happens when. It works, but it's brittle. Every time the workflow changes, you're back in the code rewiring the sequence.&lt;/p&gt;

&lt;p&gt;Strands is an open-source Python SDK built around a different idea.&lt;/p&gt;

&lt;p&gt;Instead of you hardcoding the orchestration, you let the model handle it. You give it tools and a goal, and the SDK takes care of the agent loop, tool execution, and conversation state. You can go from zero to a working agent in about 10 minutes, and the same primitives that make a simple agent easy to build can be combined to give you more complex setups when you need them.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Model Driven Approach to AI Agents
&lt;/h2&gt;

&lt;p&gt;The Strands team calls this a model-driven approach. The LLM is the orchestrator and you define the capabilities it can use.&lt;/p&gt;

&lt;p&gt;In practice, your agent code is mostly plugging in the different desired components. Here's what a basic agent looks like:&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;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the capital of France?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a working agent. It uses Amazon Bedrock as the model provider by default, but you can swap in any supported provider. We'll use OpenAI for the rest of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up a Python AI Agent with OpenAI
&lt;/h2&gt;

&lt;p&gt;Install the SDK with the OpenAI extension:&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="s1"&gt;'strands-agents[openai]'&lt;/span&gt; strands-agents-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set your API key:&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;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_api_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create an agent that uses OpenAI:&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;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.models.openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAIModel&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAIModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;client_args&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;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;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_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;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;gpt-4o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the capital of France?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with &lt;code&gt;python agent.py&lt;/code&gt; and you should get a response. The agent handles the API call to the OpenAI model and response parsing for you. &lt;/p&gt;

&lt;p&gt;So far, this doesn't have any tools it can use to interact with the real world. It does however have a main agent loop handled, and is the starting point from which you will build a more capable agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Building Blocks of an AI Agent
&lt;/h2&gt;

&lt;p&gt;Strands has a couple of main building blocks you should be aware of: agents, tools, models, and hooks. Understanding how they fit together is most of what you need to know.&lt;/p&gt;

&lt;h3&gt;
  
  
  Models
&lt;/h3&gt;

&lt;p&gt;A model is the LLM provider. Strands supports Bedrock (the default), OpenAI, Anthropic, Google Gemini, Meta Llama, Ollama for local models, and several others. You configure the model once and pass it to your agent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAIModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;client_args&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;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;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_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;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;gpt-4o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;params&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;temperature&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.3&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 can set inference parameters using &lt;code&gt;params&lt;/code&gt;. One good one to note is temperature. Use a lower temperature for factual tasks or a higher temperature for creative ones. Other supported inference parameters depend on the model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Giving Your Agent Tools
&lt;/h3&gt;

&lt;p&gt;Tools are Python functions that extend what the agent can do beyond generating text. &lt;/p&gt;

&lt;p&gt;Here's a custom tool to return weather data:&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;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get the current weather for a location.

    Args:
        location: City name, e.g. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Seattle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# In a real app, this would call a weather API
&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;Weather in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: Sunny, 72°F&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@tool&lt;/code&gt; decorator is all you need. The docstring matters because the model uses it along with the function's type hints to decide when to call this function and what arguments to pass. Clear docstrings lead to better tool usage. &lt;/p&gt;

&lt;p&gt;Strands also ships with a community tools package that includes common utilities:&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;strands_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;python_repl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http_request&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These give your agent the ability to do math, run Python code, and make HTTP requests out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wiring It All Together
&lt;/h3&gt;

&lt;p&gt;The agent brings everything together. You give it a model, tools, and a system prompt:&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;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant that can check weather and do math.&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you call the agent with a message, it enters an agent loop. The model reads the message, decides if it needs to use any tools, calls them if it does, reads the results, and either calls more tools or generates a final response. This loop continues until the model decides it has enough information to answer.&lt;/p&gt;

&lt;p&gt;You don't write any of that loop logic, the SDK handles it for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hooking into the Agent Lifecycle
&lt;/h3&gt;

&lt;p&gt;Hooks let you subscribe to lifecycle events in the agent loop without modifying the agent's core logic. The agent emits events at specific points during execution: before and after model calls, before and after tool calls, when messages are added, and at the start and end of each invocation. You register callbacks for the events you care about.&lt;/p&gt;

&lt;p&gt;Here's a hook that logs every tool call:&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;strands.hooks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AfterToolCallEvent&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_tool_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Calling tool: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;With input: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="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;log_tool_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AfterToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tool &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&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="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_hook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_tool_call&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_hook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_tool_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AfterToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The available events cover the full lifecycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;BeforeInvocationEvent&lt;/code&gt; / &lt;code&gt;AfterInvocationEvent&lt;/code&gt; for the overall request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BeforeModelCallEvent&lt;/code&gt; / &lt;code&gt;AfterModelCallEvent&lt;/code&gt; for LLM calls&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BeforeToolCallEvent&lt;/code&gt; / &lt;code&gt;AfterToolCallEvent&lt;/code&gt; for tool execution&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MessageAddedEvent&lt;/code&gt; when messages are added to conversation history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hooks are useful for logging, metrics, basic guardrails, and adding logic to your agents lifecycle. You can also cancel a tool call from a &lt;code&gt;BeforeToolCallEvent&lt;/code&gt; hook by setting &lt;code&gt;event.cancel_tool&lt;/code&gt; to a message, which stops the tool from executing and sends that message back to the model as an error. For example, you can check the tool name and arguments, and block it if something looks wrong.&lt;/p&gt;

&lt;p&gt;Once you start building more complex agents, you'll find yourself wanting to bundle related hooks and tools into reusable packages. We'll get to that later in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Multi-Tool Agent
&lt;/h2&gt;

&lt;p&gt;Here's a more complete example. This agent has a few tools as examples to do things like look up weather, do calculations, and count letters in words. The actual tools themselves don't matter much for learning how to build an agent, as those will be unique to your specific use case. For now, we are just exploring how to wire all of the pieces together into an agent that does things:&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;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.models.openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAIModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get the current weather for a location.

    Args:
        location: City name, e.g. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Seattle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="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;Weather in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: Sunny, 72°F&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;letter_counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Count occurrences of a specific letter in a word.

    Args:
        word: The word to search in
        letter: The single letter to count
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&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="nc"&gt;OpenAIModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;client_args&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;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;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_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;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;gpt-4o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;letter_counter&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;I have a few questions:
1. What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the weather in Seattle?
2. What is 1547 * 382?
3. How many r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s are in &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;strawberry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent will call each tool as needed, collect the results, and give you a single coherent response. You didn't have to write any routing logic or decide which tool to call for which question. The model handles that.&lt;/p&gt;

&lt;h2&gt;
  
  
  How AI Agents Remember Conversations
&lt;/h2&gt;

&lt;p&gt;Agents maintain conversation context automatically within a running process. Each call to the agent adds to the conversation history, so the model remembers what was said earlier:&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="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My name is Morgan.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s my name?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Will remember "Morgan"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works across multiple turns without any extra code because the SDK manages the message history internally.&lt;/p&gt;

&lt;p&gt;That said, this memory only lives as long as the agent's process lives. If you're running a script locally and it exits, the history is gone. If you're hosting an agent behind an API, the process restarts with every request so message history is not maintained. &lt;/p&gt;

&lt;p&gt;This is because LLMs are stateless by default, and the conversation history that makes them feel stateful is just a list of messages that gets sent with every request.&lt;/p&gt;

&lt;p&gt;For anything beyond a local script, you need to persist that history somewhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Session Managers
&lt;/h3&gt;

&lt;p&gt;Strands provides session managers that save and restore conversation state across invocations. The simplest option is &lt;code&gt;FileSessionManager&lt;/code&gt;, which writes session data to the local filesystem:&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;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.session.file_session_manager&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileSessionManager&lt;/span&gt;

&lt;span class="n"&gt;session_manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FileSessionManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;session_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;my-session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;storage_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./sessions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;session_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session_manager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# First run
&lt;/span&gt;&lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My name is Morgan.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Later, even after a restart, the agent remembers
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;session_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;FileSessionManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;session_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;my-session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;storage_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./sessions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s my name?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Still remembers "Morgan"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FileSessionManager&lt;/code&gt; stores each message as a JSON file on disk. It works well for local development. For hosted setups, you'd swap in a session manager backed by a database or a managed memory service like Amazon Bedrock AgentCore Memory. The integration pattern is the same, but you'd need to provision the infrastructure for the data store.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing the Context Window
&lt;/h3&gt;

&lt;p&gt;There's another problem that shows up in longer conversations. Every LLM has a context window, which is the maximum amount of tokens it can process in a single request. Your system prompt, the full conversation history, tool definitions, and the model's response all have to fit inside that window.&lt;/p&gt;

&lt;p&gt;For short conversations this isn't an issue. But if your agent runs for dozens of turns, or if tools return large results, the conversation history can grow past what the model can handle.&lt;/p&gt;

&lt;p&gt;Strands provides a few out of the box conversation managers to deal with this:&lt;/p&gt;

&lt;p&gt;The sliding window manager keeps the most recent messages and drops the oldest ones when the history gets too long:&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;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.agent.conversation_manager.sliding_window_conversation_manager&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SlidingWindowConversationManager&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;conversation_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;SlidingWindowConversationManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;window_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# keep the last 40 messages
&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;This is simple and predictable: old messages fall off the end. The downside is that the agent loses context from earlier in the conversation. If a user said something important 50 messages ago, it's gone.&lt;/p&gt;

&lt;p&gt;The summarizing manager takes a different approach. Instead of dropping old messages, it summarizes them and then keeps the summary:&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;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.agent.conversation_manager.summarizing_conversation_manager&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SummarizingConversationManager&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;conversation_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;SummarizingConversationManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;summary_ratio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# summarize the oldest 30% of messages
&lt;/span&gt;        &lt;span class="n"&gt;preserve_recent_messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# always keep the last 10
&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;When the context gets too large, the summarizing manager takes the context and generates a summary using the LLM. It then replaces those messages with the summary. The agent keeps the gist of what happened earlier without the full verbatim history. This costs an extra model call when summarization triggers, but it preserves more context than a simple sliding window.&lt;/p&gt;

&lt;p&gt;Which one you pick depends on your use case. For short, focused interactions, the sliding window is fine. For longer sessions where earlier context matters, the summarizing manager is worth the extra cost.&lt;/p&gt;

&lt;p&gt;There are also more advanced techniques you can use to manage your context window. Figuring out all the ways to manage context is called context engineering, and is an entire discipline in the AI engineering world. For simple agents, sliding window or summarization are good places to start.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Agent Loop Ties It All Together
&lt;/h2&gt;

&lt;p&gt;Stack these pieces together and you get a pretty capable agent without writing much code. A model handles reasoning and tool selection. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Tools extend what the agent can do. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hooks give you control over the lifecycle. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Session managers persist state across restarts. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Conversation managers keep the context window under control.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent loop ties it all together: the model calls tools, reads results, handles errors by trying a different approach, and returns the final response. &lt;/p&gt;

&lt;p&gt;This is the starting point. Once you start building more advanced agents you'll need more capabilities. That's where plugins come in.&lt;/p&gt;

&lt;p&gt;Plugins are classes that bundle hooks and tools together into behavioral modifications you can attach to any agent. The SDK ships with a few built-in plugins that show what this looks like in practice, and you can build your own custom plugins as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending Your Agent with Plugins
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Steering
&lt;/h3&gt;

&lt;p&gt;Steering is a plugin that evaluates the agent's output and sends corrective feedback when the response drifts from your guidelines. You give it a system prompt that defines the rules, and it uses a separate LLM call to judge each response before it reaches the user.&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;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.vended_plugins.steering&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMSteeringHandler&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;LLMSteeringHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ensure all responses are professional and concise. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reject any response that includes speculation or unverified claims.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, the steering plugin hooks into the agent's lifecycle using a &lt;code&gt;BeforeToolCallEvent&lt;/code&gt; hook. It intercepts tool calls, runs them through the evaluator, and returns one of three actions: proceed (let it through), guide (reject with feedback so the agent retries), or interrupt (escalate to a human). You don't write any of that logic. You just describe the rules in the system prompt for the steering handler.&lt;/p&gt;

&lt;p&gt;This is useful for enforcing tone in customer-facing agents, preventing agents from calling tools with dangerous arguments, or evaluating if agents are following directions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skills
&lt;/h3&gt;

&lt;p&gt;Skills are modular instructions that agents discover and activate at runtime. They follow the &lt;a href="https://skills.sh" rel="noopener noreferrer"&gt;Agent Skills specification&lt;/a&gt;, an open standard for packaging agent capabilities as folders containing instructions, scripts, and resources.&lt;/p&gt;

&lt;p&gt;A skill might teach an agent how to perform a code review following your team's conventions, how to deploy to a specific environment, or how to write content in a particular style.&lt;/p&gt;

&lt;p&gt;The agent only loads a skill's metadata (name and description) initially. When the agent decides a skill is relevant to the current task, it activates it and pulls in the full instructions. This keeps the context window clean since the agent only loads what it needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building Your Own Plugins
&lt;/h3&gt;

&lt;p&gt;You can also build custom plugins. A plugin is a class that extends &lt;code&gt;Plugin&lt;/code&gt; and uses &lt;code&gt;@hook&lt;/code&gt; and &lt;code&gt;@tool&lt;/code&gt; decorators:&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;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.plugins&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.hooks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AfterToolCallEvent&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoggingPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Plugin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A plugin that logs all tool calls and provides a utility tool.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logging-plugin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="nd"&gt;@hook&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_before_tool&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;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Called before each tool execution.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[LOG] Calling tool: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[LOG] Input: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@hook&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_after_tool&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;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AfterToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Called after each tool execution.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[LOG] Tool completed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@tool&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;debug_print&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;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Print a debug message.

        Args:
            message: The message to print
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[DEBUG] &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&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="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;Printed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Using the plugin
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;LoggingPlugin&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;
&lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Calculate 2 + 2 and print the result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the agent initializes, it scans the plugin for &lt;code&gt;@hook&lt;/code&gt; and &lt;code&gt;@tool&lt;/code&gt; methods and registers them automatically. You can stack multiple plugins on the same agent, and each one manages its own hooks and state without interfering with the others.&lt;/p&gt;

&lt;p&gt;Beyond plugins, Strands supports multi-agent patterns where agents invoke other agents as tools, MCP (Model Context Protocol) servers for connecting to external tool providers, and structured output for getting typed responses. These are all topics for another post. The point is that the same primitives (agents, tools, models, hooks) compose into more complex setups without requiring you to learn a different API. &lt;/p&gt;

&lt;h2&gt;
  
  
  Install and Build Your First AI Agent
&lt;/h2&gt;

&lt;p&gt;The fastest path from here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install: &lt;code&gt;pip install 'strands-agents[openai]' strands-agents-tools&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set your API key: &lt;code&gt;export OPENAI_API_KEY=your_key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Write a simple agent with one custom tool&lt;/li&gt;
&lt;li&gt;Run it and see what happens&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;a href="https://strandsagents.com/docs/user-guide/quickstart/overview/" rel="noopener noreferrer"&gt;Strands documentation&lt;/a&gt; has more examples, including multi-agent setups, observability, and production deployment patterns. The &lt;a href="https://github.com/strands-agents/sdk-python" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; has the source and community tools.&lt;/p&gt;

&lt;p&gt;The SDK is open source and actively developed. If you've been putting off building an agent because the frameworks felt heavy, give Strands a look. The barrier to entry is low, and it provides enough composability to keep up with you as your use case gets more complex.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>python</category>
      <category>aws</category>
    </item>
    <item>
      <title>JavaScript e SDK: Como sua aplicação web pode falar com a nuvem</title>
      <dc:creator>Giovana Armani</dc:creator>
      <pubDate>Thu, 26 Mar 2026 19:29:54 +0000</pubDate>
      <link>https://dev.to/aws/javascript-e-sdk-como-sua-aplicacao-web-pode-falar-com-a-nuvem-cn4</link>
      <guid>https://dev.to/aws/javascript-e-sdk-como-sua-aplicacao-web-pode-falar-com-a-nuvem-cn4</guid>
      <description>&lt;p&gt;Em artigos passados, falei sobre &lt;a href="https://dev.to/aws/sdk-as-ferramentas-para-seu-codigo-se-conectar-com-o-mundo-5d0e"&gt;o que é SDK e por que ele é importante&lt;/a&gt; e &lt;a href="https://dev.to/aws/seu-primeiro-sistema-serverless-com-aws-lambda-7ai"&gt;construímos uma aplicação back-end&lt;/a&gt; para conversão de documentos usando o SDK da AWS. Vamos usar o SDK mais uma vez, mas agora para construir nosso front-end!&lt;/p&gt;




&lt;h2&gt;
  
  
  O que é SDK mesmo?
&lt;/h2&gt;

&lt;p&gt;Podemos pensar SDK (Software Development Kit) como uma caixa de ferramentas para construir aplicações sobre uma plataforma, sistema operacional ou linguagem de programação. Ele pode incluir bibliotecas, APIs, documentação, exemplos de código, debuggers, entre outras coisas. Caso queira saber mais sobre SDK e sua diferença para APIs e bibliotecas, veja &lt;a href="https://dev.to/aws/sdk-as-ferramentas-para-seu-codigo-se-conectar-com-o-mundo-5d0e"&gt;esse artigo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;O SDK da AWS para JavaScript permite fácil acesso a serviços da AWS a partir de seu código JS. Você verá como com algumas importações e comandos rápidos podemos conversar diretamente com a nuvem!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzbggmlowz77qwyei0qpq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzbggmlowz77qwyei0qpq.png" alt="Diagrama ilustrando código javascript usando o SDK para acessar a nuvem" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tudo que construiremos nesse tutorial, assim como o tutorial do back-end, pode ser reproduzido com uma conta Free Tier da AWS. Com ela, você ganha 100 dólares em créditos na conta e pode ganhar até 100 dólares adicionais ao explorar os diferentes serviços da AWS e não será cobrado a não ser que explicitamente escolha trocar de plano. Se quiser saber mais sobre como criar sua conta e as melhores configurações de segurança, veja &lt;a href="https://builder.aws.com/content/2lYwQJ47Sq8C76VkFUDisXB7VnN/secure-foundation-setting-up-your-aws-free-tier-account?trk=4c2f66ff-5d75-4a07-aa36-eb6219a429db&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;esse artigo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que vamos construir
&lt;/h2&gt;

&lt;p&gt;No nosso backend, temos uma função Lambda que converte arquivos CSV para JSON. Ela é acionada quando colocamos um arquivo no &lt;a href="https://docs.aws.amazon.com/pt_br/AmazonS3/latest/userguide/Welcome.html?trk=4c2f66ff-5d75-4a07-aa36-eb6219a429db&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;bucket S3&lt;/a&gt; de input e coloca os arquivos convertidos em um outro bucket do S3 de output. Como queremos fazer o front-end para essa aplicação, vamos construir uma tela que rodará no navegador. Ela será dividida em duas partes: uma seção para fazer o upload de arquivos CSV e outra para o download dos arquivos JSON. &lt;/p&gt;

&lt;h2&gt;
  
  
  Primeiros passos: rodando a página web
&lt;/h2&gt;

&lt;p&gt;Para esse projeto, vamos usar &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt;, uma biblioteca do JavaScript para criação de interfaces. Para isso, é preciso ter o node.js. Caso não tenha node na sua máquina, comece baixando no &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;site oficial&lt;/a&gt;. Selecione "Get Node.js" para baixar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/gi-armani/CSV-JSON-Converter-sample-for-aws-SDK" rel="noopener noreferrer"&gt;Nesse repositório&lt;/a&gt; você encontrará o código completo do front-end na pasta "front", assim como o back-end que já construímos. Para começar a reproduzir a aplicação, clone ele para editar o código. Siga os passos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crie uma pasta no seu computador para o projeto&lt;/li&gt;
&lt;li&gt;No terminal, navegue até a pasta criada através do comando &lt;code&gt;cd &amp;lt;caminho-ate-a-pasta&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Clone o repositório com o comando &lt;code&gt;git clone https://github.com/gi-armani/CSV-JSON-Converter-sample-for-aws-SDK&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Para rodar a aplicação, navegue até a raiz do projeto com o comando &lt;code&gt;cd &amp;lt;caminho-ate-a-pasta-criada&amp;gt;/CSV-JSON-Converter-sample-for-aws-SDK/front&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Execute &lt;code&gt;npm install&lt;/code&gt; para instalar as dependências e &lt;code&gt;npm start&lt;/code&gt; para iniciar&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Você deverá ver uma mensagem parecida com a mensagem abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc9g7hxw8j7npmhaphl2m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc9g7hxw8j7npmhaphl2m.png" alt="Mensagem de sucesso da aplicação React iniciada" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora você pode abrir &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;http://localhost:3000/&lt;/a&gt; no seu navegador de preferência e ver os elementos visuais da aplicação. Lembre-se que ela ainda não está conectada na nuvem, mas vamos cuidar disso!&lt;/p&gt;

&lt;h3&gt;
  
  
  Entendendo a estrutura da página
&lt;/h3&gt;

&lt;p&gt;Quando iniciamos um projeto React, o principal arquivo da aplicação estará localizado em &lt;code&gt;CSV-JSON-Converter-sample-for-aws-SDK/front/src/App.js&lt;/code&gt; que contém a seguinte estrutura:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// importação de bibliotecas, imagens e outros elementos&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;logo&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;./logo.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Aqui colocamos a lógica (variáveis e funções que precisaremos usar)&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// Aqui estarão os diferentes elementos do site, declarados em componentes similares ao html&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&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="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O trecho de código acima tem indicativos de onde estarão os nossos elementos visuais (textos, inputs, botões, etc) e onde estará a lógica dos processos. Veja que no código que você clonou tudo isso já está criado, mas ainda vamos precisar substituir algumas constantes para conectar com a nuvem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configurando permissões
&lt;/h2&gt;

&lt;p&gt;Pausando um pouco no código e indo para a conexão com a nuvem, precisamos configurar permissões para nosso código conseguir acessar os arquivos. Agora vamos conectar o front-end com o back-end que criamos anteriormente. Caso você não tenha os buckets do S3 e a função Lambda na sua conta, recomendo criá-los primeiro usando as instruções do &lt;a href="https://dev.to/aws/seu-primeiro-sistema-serverless-com-aws-lambda-7ai"&gt;artigo anterior&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Para isso, vamos usar o IAM (AWS Identity and Access Management). Esse é um serviço da AWS que permite controlar acesso a permissões dentro da sua conta. Se quiser entender mais sobre como funciona o IAM e os diferentes tipos de permissão, veja &lt;a href="https://builder.aws.com/content/35z2TS8V8AIpEqHm1bdM7LLa2hz/desmistificando-permissoes-na-aws?trk=4c2f66ff-5d75-4a07-aa36-eb6219a429db&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;esse artigo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Na minha conta AWS, estou na região de us-west-2 (Estados Unidos Oregon). É importante criar todos os seus recursos na mesma região para evitar problemas de comunicação entre eles. Você pode identificar a região que você está a qualquer momento no menu superior da conta ao lado do nome e número da conta.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando a política de acesso ao S3
&lt;/h3&gt;

&lt;p&gt;Vamos começar criando a política de acesso, ou seja, o documento que diz o que é permitido fazer e onde.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No console da AWS da sua conta Free Tier, navegue até a página do IAM e selecione "Políticas" no menu da esquerda&lt;/li&gt;
&lt;li&gt;Clique em "Criar política"&lt;/li&gt;
&lt;li&gt;No canto superior direito do editor de políticas, selecione a opção JSON e cole o conteúdo abaixo. Não se esqueça de substituir os locais indicados em "Resource" com os nomes dos seus buckets
&lt;/li&gt;
&lt;/ol&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowWriteInput"&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;"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;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::&amp;lt;NOME-DO-SEU-BUCKET-DE-INPUT&amp;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;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowReadOutput"&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;"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;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::&amp;lt;NOME-DO-SEU-BUCKET-DE-OUTPUT&amp;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;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowListOutput"&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;"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;"s3:ListBucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::gio-csv-converter-output"&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;Aqui temos uma política que permite subir arquivos no bucket de input (operação PutObject) e ler arquivos do bucket de output (operação GetObject). Temos também uma última permissão que permite listar os objetos de um bucket (operação ListBucket). Essa última é importante porque caso a conversão do json ainda não tenha sido terminada, fará com que o S3 retorne um erro mais amigável dizendo que o arquivo ainda não existe. Sem essa permissão, o S3 retornaria um erro de acesso negado.&lt;/p&gt;

&lt;p&gt;Na opção "Visual" do editor de políticas, é possível construir essa mesma política de forma interativa selecionando os serviços e as operações que serão usadas (deixo para a curiosidade de quem quiser explorar 😉). &lt;/p&gt;

&lt;p&gt;Para terminar a criação, basta selecionar "Próximo", dar um nome à sua política na próxima página (chamei a minha de "jsWebClientS3Access") e então selecionar "Criar política" no canto inferior direito.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando um identity pool
&lt;/h3&gt;

&lt;p&gt;Para essa parte, vamos usar o &lt;a href="https://docs.aws.amazon.com/pt_br/cognito/latest/developerguide/what-is-amazon-cognito.html?trk=4c2f66ff-5d75-4a07-aa36-eb6219a429db&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon Cognito&lt;/a&gt;, um serviço da AWS de autenticação para aplicações. Ele nos ajuda a identificar quem é o usuário que está acessando nossa aplicação e gerar credenciais de acesso que são atualizadas automaticamente.&lt;/p&gt;

&lt;p&gt;Os Identity pools (ou grupos de identidade) são diretórios de identidades que podem ser usados para obter credenciais para acessar outros serviços da AWS através do nosso front-end. Para criá-lo, siga os passos abaixo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Na sua conta Free Tier, navegue até o &lt;a href="https://us-west-2.console.aws.amazon.com/cognito/v2/home?region=us-west-2&amp;amp;trk=4c2f66ff-5d75-4a07-aa36-eb6219a429db&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon Cognito&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Selecione "Grupos de identidades" no menu esquerdo e em seguida "Criar grupo de identidades" no botão laranja à direita&lt;/li&gt;
&lt;li&gt;Na página de criação, selecione "Acesso de convidado". Isso permitirá que nosso código front-end acesse os recursos que queremos sem precisar de um login. Clique em "Próxima" para continuar&lt;/li&gt;
&lt;li&gt;Em seguida, vamos criar um perfil do IAM que usaremos para declarar as permissões. Selecione "Criar um novo perfil do IAM" e dê um nome a ele. Chamei o meu de "jsWebClientRole". Clique em "Próxima" para continuar&lt;/li&gt;
&lt;li&gt;Dê um nome ao grupo de identidades. Chamei o meu de "jsWebClientIdentity". Clique em "Próxima" para continuar&lt;/li&gt;
&lt;li&gt;Revise os detalhes do grupo de identidade que estamos criando e selecione "Criar grupo de identidades" para concluir.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;De volta na página dos Grupos de identidade do Amazon Cognito, você verá seu grupo de identidade criado. Salve o ID dele pois será usado logo mais para autenticação no código.&lt;/p&gt;

&lt;p&gt;Por último, precisamos ligar a política de acesso que criamos na seção anterior ao grupo de identidade:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navegue até o &lt;a href="https://us-west-2.console.aws.amazon.com/iam/home?trk=4c2f66ff-5d75-4a07-aa36-eb6219a429db&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;console de IAM&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;No menu esquerdo, selecione "Funções" &lt;/li&gt;
&lt;li&gt;Busque pelo perfil que criamos junto ao Grupo de identidade. O meu é o "jsWebClientRole"&lt;/li&gt;
&lt;li&gt;Na aba de "Permissões", selecione "Adicionar permissões" e "Anexar políticas"&lt;/li&gt;
&lt;li&gt;Busque pela política de acesso ao S3 que criamos anteriormente e clique em "Adicionar permissão"&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Configurando CORS
&lt;/h2&gt;

&lt;p&gt;Um conceito clássico que vemos quando integramos aplicações, como integração entre front e back-end, é CORS. CORS significa Cross-Origin Resource Sharing, ou compartilhamento de recursos entre origens, e é um mecanismo para compartilhar conteúdo entre diferentes domínios de forma segura. Com ele, declaramos o que pode ser compartilhado e quais domínios podem enviar conteúdo. Precisamos configurar CORS para o bucket de input e de output.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No console da AWS, navegue até a &lt;a href="https://us-west-2.console.aws.amazon.com/s3?trk=4c2f66ff-5d75-4a07-aa36-eb6219a429db&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;página do S3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Selecione o nome do bucket de input que criamos no último tutorial quando construímos o back-end&lt;/li&gt;
&lt;li&gt;Clique em "Permissões" e desça até o final da página onde encontrará "Compartilhamento de recursos de origem cruzada (CORS)"&lt;/li&gt;
&lt;li&gt;Clique em editar e cole o conteúdo abaixo:
&lt;/li&gt;
&lt;/ol&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"AllowedHeaders"&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;"*"&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;"AllowedMethods"&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;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"PUT"&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;"AllowedOrigins"&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;"http://localhost:3000"&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;"ExposeHeaders"&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;"ETag"&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;blockquote&gt;
&lt;p&gt;⚠️ Note que adicionamos especificamente o localhost na porta 3000 em "AllowedOrigins" o que significa que o bucket aceitará receber recursos apenas desse domínio. A melhor prática de segurança é sempre restringir acesso para o mínimo necessário. Para um site que estivesse deployado na internet, adicionaríamos o domínio específico da aplicação que faz o envio.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Repita os mesmos passos para habilitar CORS no bucket de output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conectando tudo: chamando o SDK da AWS
&lt;/h2&gt;

&lt;p&gt;Agora que temos as permissões configuradas na conta AWS, vamos fazer a conexão com o S3 no código.&lt;/p&gt;

&lt;p&gt;No começo do arquivo App.js do repositório clonado, você encontrará o seguinte trecho:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-west-2&lt;/span&gt;&lt;span class="dl"&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;IDENTITY_POOL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&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;clienteS3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REGION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;fromCognitoIdentityPool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;clientConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REGION&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
    &lt;span class="na"&gt;identityPoolId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IDENTITY_POOL_ID&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;O IdentityPoolId usado para criar o clienteS3 será o ID do Grupo de Identidades que criamos com Amazon Cognito. Preencha esse valor na constante &lt;code&gt;IDENTITY_POOL_ID&lt;/code&gt;. Note que a constante &lt;code&gt;REGION&lt;/code&gt;, que indica a região, está declarada como &lt;code&gt;us-west-2&lt;/code&gt;. Caso seus recursos estejam em outra região da AWS, mude essa constante para a região correspondente.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Essa é a forma recomendada pela AWS para obter credenciais para o navegador. &lt;strong&gt;Muito cuidado com expor suas credenciais abertamente, jamais publique elas na internet!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Por fim, preencha as constantes que representam os buckets de input e output com os nomes dos seus buckets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BUCKET_ENTRADA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&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;BUCKET_SAIDA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto! Se seu back-end também estiver configurado, você terá um sistema completo incluindo front e back-end que utiliza o SDK da AWS para comunicação com os serviços em nuvem. Você pode usar &lt;a href="https://github.com/gi-armani/CSV-JSON-Converter-sample-for-aws-SDK/blob/main/exemplo-produtos.csv" rel="noopener noreferrer"&gt;esse csv de exemplo&lt;/a&gt; para testar a aplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Agora você já sabe como construir uma aplicação front-end que se comunica diretamente com serviços da AWS. Nesse tutorial, você aprendeu a:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instalar e usar o AWS SDK para JavaScript para interagir com o S3&lt;/li&gt;
&lt;li&gt;Configurar permissões no IAM para que sua aplicação acesse serviços da AWS de forma segura&lt;/li&gt;
&lt;li&gt;Configurar CORS para permitir a comunicação entre seu front-end e a nuvem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reproduza esse tutorial na sua máquina e dê seu próprio estilo ao design da página. Experimente trocar cores, fontes e layout, deixe com a sua carinha. Com o front-end e o back-end que construímos no artigo anterior, você tem uma aplicação serverless completa funcionando na AWS!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>development</category>
      <category>programming</category>
    </item>
    <item>
      <title>Escala Inteligente con Microservicios Serverless</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Tue, 24 Mar 2026 17:57:16 +0000</pubDate>
      <link>https://dev.to/aws/escala-inteligente-con-microservicios-serverless-3lbe</link>
      <guid>https://dev.to/aws/escala-inteligente-con-microservicios-serverless-3lbe</guid>
      <description>&lt;p&gt;&lt;em&gt;Cuándo dividir, cómo conectar y qué patrones usar para no crear un monolito distribuido.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Hace un tiempo me tocó trabajar en un proyecto donde todo funcionaba. Un monolito. Un deploy, un repo, un equipo. Respondía rápido, los usuarios estaban contentos, el jefe estaba contento.&lt;/p&gt;

&lt;p&gt;Hasta que alguien en una conferencia dijo: &lt;em&gt;"los microservicios son el futuro"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Y miramos nuestro monolito y pensamos: &lt;em&gt;"esto no escala, necesitamos microservicios"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Tres meses después teníamos 15 servicios. Un request del usuario pasaba por 7 de ellos antes de responder. Si inventario iba lento, el checkout se caía. Si notificaciones fallaba, nadie sabía por qué el pedido se quedó a medias.&lt;/p&gt;

&lt;p&gt;Teníamos un &lt;strong&gt;monolito distribuido&lt;/strong&gt;. Los mismos problemas de antes, pero ahora con latencia de red y 12 dashboards que nadie entendía.&lt;/p&gt;

&lt;p&gt;La complejidad no desapareció. Solo cambió de forma.&lt;/p&gt;

&lt;p&gt;Si alguna vez sentiste que migrar a microservicios iba a resolver todos tus problemas y terminó creando nuevos... este artículo es para ti.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Realmente necesitas microservicios?
&lt;/h2&gt;

&lt;p&gt;Antes de hablar de &lt;em&gt;cómo&lt;/em&gt;, hablemos de &lt;em&gt;cuándo&lt;/em&gt;. Porque la decisión más inteligente a veces es &lt;strong&gt;no dividir&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Cuando empecé a investigar más sobre el tema, me di cuenta de que muchas migraciones a microservicios no nacen de un problema real. Nacen del hype. De escuchar que "Netflix usa microservicios" y asumir que nosotros también deberíamos.&lt;/p&gt;

&lt;p&gt;Pero Netflix llegó a microservicios después de años de crecimiento y con miles de ingenieros dedicados a esa transición. El contexto importa: lo que funciona para una empresa con esa escala no necesariamente aplica para todos los equipos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Señales reales vs. falsas alarmas
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅ Señales reales para dividir&lt;/th&gt;
&lt;th&gt;❌ Falsas alarmas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Equipos se bloquean entre sí para hacer deploy&lt;/td&gt;
&lt;td&gt;"Netflix usa microservicios"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Un componente necesita escalar 100x más que el resto&lt;/td&gt;
&lt;td&gt;"Mi monolito es legacy"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dominios de negocio claramente independientes&lt;/td&gt;
&lt;td&gt;"Quiero usar tecnologías diferentes por servicio"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ciclos de release muy diferentes entre componentes&lt;/td&gt;
&lt;td&gt;"Los microservicios son más modernos"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Un fallo en un módulo tumba todo el sistema&lt;/td&gt;
&lt;td&gt;"Mi equipo de 3 personas necesita agilidad"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Si tu equipo es pequeño y todo cabe en un deploy, no necesitas microservicios. Necesitas un &lt;strong&gt;monolito bien estructurado&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Divide cuando el dolor es real: equipos que se bloquean, componentes que necesitan escalar de forma radicalmente diferente, dominios de negocio que evolucionan a ritmos distintos.&lt;/p&gt;

&lt;p&gt;No dividas porque suena moderno.&lt;/p&gt;

&lt;h3&gt;
  
  
  No es binario — hay un espectro
&lt;/h3&gt;

&lt;p&gt;Algo que me costó entender al principio es que no es "monolito o microservicios". Hay un espectro completo entre ambos:&lt;/p&gt;

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

&lt;p&gt;La mayoría de los sistemas no necesitan ir directo a microservicios.&lt;/p&gt;

&lt;p&gt;Empieza con un &lt;strong&gt;monolito modular&lt;/strong&gt;: un solo deploy, pero con límites claros entre dominios. Cuando un módulo necesite escalar o evolucionar independientemente, lo extraes.&lt;/p&gt;

&lt;p&gt;Eso es escalar inteligente: dividir cuando hay evidencia, no cuando hay hype.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless cambia las reglas
&lt;/h2&gt;

&lt;p&gt;Cuando trabajas con microservicios tradicionales, cada servicio necesita su servidor, su scaling, su service discovery. Es mucha carga operativa.&lt;/p&gt;

&lt;p&gt;Serverless cambia eso por completo:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Microservicios Tradicionales&lt;/th&gt;
&lt;th&gt;Microservicios Serverless&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tú manejas servidores por servicio&lt;/td&gt;
&lt;td&gt;Sin servidores que gestionar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tú configuras auto-scaling&lt;/td&gt;
&lt;td&gt;Escala automática por función&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagas por servidor encendido&lt;/td&gt;
&lt;td&gt;Pagas por ejecución&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tú manejas service discovery&lt;/td&gt;
&lt;td&gt;Eventos y colas conectan servicios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes, Docker, load balancers&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.aws.amazon.com/lambda?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/apigateway?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;API Gateway&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/eventbridge?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;EventBridge&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pero hay algo que quiero dejar claro porque es una confusión muy común:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Un microservicio serverless no es UNA Lambda.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Es la colección de Lambdas detrás de un API Gateway que resuelven un dominio de negocio. Pedidos, inventario, pagos, cada uno es un microservicio compuesto por varias funciones pequeñas y enfocadas.&lt;/p&gt;

&lt;p&gt;Si metes toda la lógica en una sola Lambda gigante, estás creando un monolito dentro de una función.&lt;/p&gt;

&lt;p&gt;La pregunta ya no es &lt;em&gt;"cómo escalo este servicio"&lt;/em&gt; sino &lt;em&gt;"cómo conecto estos servicios de forma inteligente"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Y ahí es donde entran los patrones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los 3 patrones clave
&lt;/h2&gt;

&lt;p&gt;Después de haber pasado por varias migraciones (algunas exitosas, otras no tanto), identifiqué tres patrones que marcan la diferencia entre una arquitectura de microservicios que funciona y un monolito distribuido disfrazado.&lt;/p&gt;

&lt;p&gt;Los presento aquí a nivel conceptual. Cada uno tendrá su artículo dedicado con implementación práctica y código real.&lt;/p&gt;

&lt;h3&gt;
  
  
  Patrón 1: Comunicación desacoplada — eventos, no llamadas directas
&lt;/h3&gt;

&lt;p&gt;Este fue el error que más me costó entender.&lt;/p&gt;

&lt;p&gt;Cuando empezamos a dividir servicios, lo primero que hicimos fue conectar Lambdas en cadena. Lambda A llama a B, B llama a C. Parecía lógico.&lt;/p&gt;

&lt;p&gt;El problema:&lt;/p&gt;

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

&lt;p&gt;Si C falla, todo falla. Las latencias se suman. Estás recreando el monolito pero con más latencia.&lt;/p&gt;

&lt;p&gt;El patrón correcto es usar &lt;strong&gt;eventos asíncronos&lt;/strong&gt;:&lt;/p&gt;

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

&lt;p&gt;Tu microservicio responde al usuario y emite un evento. Los demás microservicios reaccionan al evento a su propio ritmo.&lt;/p&gt;

&lt;p&gt;Desacoplados. Independientes. Resilientes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/eventbridge/latest/userguide?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon EventBridge&lt;/a&gt; actúa como un bus de eventos serverless que desacopla la lógica de enrutamiento de tus microservicios. &lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon SQS&lt;/a&gt; te da colas de mensajes para procesamiento asíncrono con garantías de entrega. Ambos son piezas fundamentales para este patrón.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;En el próximo artículo de la serie vamos a implementar este patrón paso a paso con EventBridge y SQS, incluyendo las trampas que te puedes encontrar.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Patrón 2: Cada servicio, su data
&lt;/h3&gt;

&lt;p&gt;Este es directo y no tiene vuelta:&lt;/p&gt;

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

&lt;p&gt;Si todos tus servicios leen y escriben en la misma tabla de DynamoDB, no tienes microservicios. Tienes un &lt;strong&gt;monolito disfrazado&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;El patrón correcto:&lt;/p&gt;

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

&lt;p&gt;Cada servicio es dueño de su data. Si otro servicio necesita esa información, la pide por evento o por API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nunca por query directo a la base de datos ajena.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;¿Por qué? Porque si compartes la base de datos, cualquier cambio de schema puede romper múltiples servicios al mismo tiempo. Y ahí se fue tu independencia.&lt;/p&gt;

&lt;p&gt;Esto se conoce como &lt;em&gt;database per service&lt;/em&gt; y suena simple, pero tiene implicaciones importantes, como qué pasa cuando una transacción involucra múltiples servicios (spoiler: necesitas el patrón Saga).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;En el tercer artículo de la serie vamos a profundizar en este patrón y en cómo manejar transacciones distribuidas con Sagas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Patrón 3: Observabilidad desde el día 1
&lt;/h3&gt;

&lt;p&gt;En un monolito, un error aparece en un log. En microservicios, un error puede estar en cualquiera de 10 servicios.&lt;/p&gt;

&lt;p&gt;Sin observabilidad, estás debuggeando a ciegas. Y te lo digo por experiencia: no hay nada más frustrante que saber que algo falló y no poder encontrar dónde.&lt;/p&gt;

&lt;p&gt;Los tres pilares que necesitas desde el día 1:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Logs estructurados con correlation ID&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota importante:&lt;/strong&gt; Cada request que entra a tu sistema debe llevar un ID único que viaje por todos los servicios. Cuando algo falla, buscas ese ID y ves el camino completo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://docs.powertools.aws.dev/lambda/python/latest/" rel="noopener noreferrer"&gt;Lambda Powertools&lt;/a&gt; te lo da casi gratis, inyecta correlation IDs, estructura tus logs en JSON y agrega contexto automáticamente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Traces distribuidos&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/xray/latest/devguide?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AWS X-Ray&lt;/a&gt; y &lt;a href="https://aws.amazon.com/otel?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AWS Distro for OpenTelemetry (ADOT)&lt;/a&gt; te permiten ver el viaje completo de cada request a través de todos los servicios. Puedes identificar exactamente dónde está el bottleneck.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Nota importante:&lt;/strong&gt; AWS está convergiendo hacia OpenTelemetry como estándar de instrumentación. ADOT te da una distribución optimizada para AWS que envía traces y métricas a X-Ray, CloudWatch y otros destinos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Métricas de negocio&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No solo "cuántos errores 500" sino "cuántos pedidos se perdieron por minuto". Las métricas técnicas te dicen que algo falló. Las métricas de negocio te dicen cuánto te costó.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Si no puedes ver qué pasa, no puedes arreglar lo que falla. En el cuarto artículo de la serie vamos a implementar observabilidad completa con Lambda Powertools, X-Ray y métricas custom.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  El framework de decisión
&lt;/h2&gt;

&lt;p&gt;Después de todo lo que vimos, quiero dejarte algo práctico. Un framework que puedes usar la próxima vez que alguien diga "necesitamos microservicios":&lt;/p&gt;

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

&lt;p&gt;No empieces por la tecnología. Empieza por las preguntas.&lt;/p&gt;

&lt;p&gt;Si la respuesta a todo es no, quédate con tu monolito. Si es sí, extrae con estrategia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que quiero que te lleves
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;🧠 &lt;strong&gt;Decide con evidencia, no con hype&lt;/strong&gt;: Microservicios no son el default. Son una herramienta para problemas específicos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;📐 &lt;strong&gt;Empieza modular, extrae después&lt;/strong&gt;: Monolito modular → macro-servicios → microservicios. En ese orden.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔌 &lt;strong&gt;Eventos sobre llamadas directas&lt;/strong&gt;: Desacopla con EventBridge/SQS. Nunca encadenes Lambdas síncronamente.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🗄️ &lt;strong&gt;Cada servicio, su data&lt;/strong&gt;: Base de datos compartida = monolito disfrazado.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;👀 &lt;strong&gt;Observabilidad no es opcional&lt;/strong&gt;: Correlation IDs, X-Ray/OpenTelemetry, métricas de negocio. Desde el día 1.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Este artículo es el mapa conceptual. Los siguientes artículos de la serie van a profundizar en cada patrón con implementación práctica y código real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Comunicación desacoplada con EventBridge y SQS&lt;/strong&gt;: Implementación paso a paso, trampas comunes y demo funcional&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database per service y el patrón Saga&lt;/strong&gt;: Cómo manejar transacciones distribuidas con acciones compensatorias&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observabilidad en microservicios serverless&lt;/strong&gt;: Lambda Powertools, X-Ray/OpenTelemetry y métricas de negocio en acción&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Escalar inteligente no es tener más servicios. Es tomar mejores decisiones."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;p&gt;Si quieres profundizar más, estos son los recursos oficiales de AWS que recomiendo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/whitepapers/latest/microservices-on-aws/microservices-on-aws.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Implementing Microservices on AWS&lt;/a&gt; — Whitepaper completo sobre patrones API-driven, event-driven y data streaming&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/whitepapers/latest/microservices-on-aws/microservices-on-serverless-technologies.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Microservices on Serverless Technologies&lt;/a&gt; — Arquitecturas serverless con Lambda, API Gateway y Fargate&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/modernization-integrating-microservices/introduction.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Integrating Microservices by Using AWS Serverless Services&lt;/a&gt; — Guía prescriptiva sobre comunicación síncrona y asíncrona entre microservicios&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/introduction.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Cloud Design Patterns, Architectures, and Implementations&lt;/a&gt; — Patrones como Saga, circuit breaker, event sourcing y strangler fig aplicados con servicios AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;¿Te resultó útil este artículo?&lt;/strong&gt; Compártelo con tu equipo. Y si ya pasaste por una migración a microservicios exitosa o no me encantaría escuchar tu experiencia.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>microservices</category>
      <category>architecture</category>
      <category>aws</category>
    </item>
    <item>
      <title>AI Agents Are Your API's Biggest Consumer. Do They Care About Good Design?</title>
      <dc:creator>Morgan Willis</dc:creator>
      <pubDate>Tue, 24 Mar 2026 13:50:14 +0000</pubDate>
      <link>https://dev.to/aws/ai-agents-are-your-apis-biggest-consumer-do-they-care-about-good-design-31l2</link>
      <guid>https://dev.to/aws/ai-agents-are-your-apis-biggest-consumer-do-they-care-about-good-design-31l2</guid>
      <description>&lt;p&gt;We've always designed APIs for humans. A well built API means obsessing over naming conventions, RESTful patterns, and clear documentation because the goal is simple: make systems easy for developers to understand. But AI is changing who the consumer of software is, and developers are asking whether the rules we've followed for decades still hold up.&lt;/p&gt;

&lt;p&gt;When the primary user of an API is an AI system that reads documentation, adapts to unfamiliar patterns, and experiments when something fails, maybe consistent APIs and clean abstractions don't matter anymore.&lt;/p&gt;

&lt;p&gt;Everything in me wants to reject this concept. My gut instinct is to say "my APIs need to be pretty or I'll die".&lt;/p&gt;

&lt;p&gt;I've been thinking about this a lot lately, and unfortunately I think good arguments are made from both angles on this. Let's walk through both sides.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Case for "Abstractions Don't Matter Anymore"
&lt;/h3&gt;

&lt;p&gt;Some developers believe AI will reduce the importance of traditional abstractions. I've heard this take a lot.&lt;/p&gt;

&lt;p&gt;LLMs are extremely good at pattern recognition. They can read documentation, inspect responses, experiment, and adapt when things fail. From this perspective, messy systems don't seem like a big problem. A human might struggle with inconsistent naming or poor documentation, but an AI can simply figure it out through trial, error, and being smarter than me.&lt;/p&gt;

&lt;p&gt;So maybe if abstractions exist to make code understandable to humans, and the primary consumer is no longer human, then the old rules don't apply.&lt;/p&gt;

&lt;p&gt;In practice, you can build agent systems that work around poorly designed APIs. Say you're integrating with an API where half the endpoints return errors as HTTP status codes and the other half always return 200 with an error field buried in the response body.&lt;/p&gt;

&lt;p&gt;The agent pulls the docs, writes the code, and it looks reasonable. Then it runs the tests and it breaks because the code is checking status codes on an endpoint that never returns them.&lt;/p&gt;

&lt;p&gt;The agent reads the error, adds response body parsing, and tries again. Maybe it over-corrects and starts modifying the way it's handling status codes everywhere, breaking a different call. So, it adjusts again. Then finally, the third try works. These systems exist today and they get there eventually.&lt;/p&gt;

&lt;p&gt;As models continue to improve, API design will matter less and less as the models can brute force their way to working code. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Case for "Abstractions Still Matter"
&lt;/h3&gt;

&lt;p&gt;Now for the other side of the argument.&lt;/p&gt;

&lt;p&gt;A human developer interacts with an API occasionally. An AI system like a coding agent might interact with it hundreds of times in a single session. When something is poorly designed, the problems compound fast in the form of unnecessary retries, token-heavy debugging loops, and ugly workarounds.&lt;/p&gt;

&lt;p&gt;I ran into this with an API that had inconsistent naming across its endpoints. I was debugging an issue with an app I was building, and my coding agent kept thinking it had identified the issue because a parameter name for an API I was using didn't align with historical patterns for this type of API. That wasn't the issue at all, it was completely irrelevant, but the agent kept getting hung up on it.&lt;/p&gt;

&lt;p&gt;Every time I debug something that uses this specific API my coding agent always says "I found it! The parameter name should be X instead of Y!" Then it changes it and deploys again and it doesn't work because that wasn't the issue. It kept making the same wrong assumption across sessions.&lt;/p&gt;

&lt;p&gt;Unlike a human who hits a weird error and remembers it next time, LLMs are stateless by default. Every new session starts fresh, and agents can spin up tons of sessions in a single workflow. Each of which will run into the same problem.&lt;/p&gt;

&lt;p&gt;Every ambiguity in an API has a token cost and poor API design has direct financial consequences in a way that wasn't true when the only cost was developer frustration.&lt;/p&gt;

&lt;p&gt;And another thing I've noticed: if you watch a coding agent work through a problem like this, it often gives up and tries a different approach entirely after a few tries. It'll swap libraries, use a different endpoint, or cobble together a workaround using some other approach. That's the AI doing exactly what it should do, adapting.&lt;/p&gt;

&lt;p&gt;From the developer perspective, I don't always like the workarounds the AI chooses. Sometimes one unclear API makes my AI think it needs to redesign my entire component. Other times it bypasses the API in a way where it looks like it's working but it's actually relying on some weird hardcoded values somewhere.&lt;/p&gt;

&lt;p&gt;And from your perspective of an API owner, the AI just decided not to use your API. Your messy design just cost you a new user.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Human Compatibility Problem
&lt;/h3&gt;

&lt;p&gt;There's another angle to this too. The abstractions we built for humans are now embedded in how AI systems learn.&lt;/p&gt;

&lt;p&gt;Modern software ecosystems contain decades of common coding conventions. These were originally created to help humans understand systems. But those same patterns now appear throughout model training data, and that has consequences.&lt;/p&gt;

&lt;p&gt;When you name your endpoint &lt;code&gt;/api/v2/users/{id}&lt;/code&gt;, the model has seen that pattern millions of times. It knows what to expect. When you name it &lt;code&gt;/backend/person/fetch?identifier={id}&lt;/code&gt;, you're fighting against the weight of its training. The model can learn your pattern, but there's friction.&lt;/p&gt;

&lt;p&gt;Coding assistants are increasingly abstracting away the act of writing syntax from developers which is great until you need to peek under the abstraction.&lt;/p&gt;

&lt;p&gt;If an agent generates code using unfamiliar patterns or unconventional APIs, a human still has to review it, debug it, and maintain it. We wouldn't want agents writing assembly language even if it ran faster, because most of us can't read it. The same logic applies to API conventions. Familiar patterns keep the code understandable for the humans who still have to live with it.&lt;/p&gt;

&lt;p&gt;The patterns we created to help humans are now baked into how AI understands software, and that path dependence matters in both directions. Breaking conventions costs you in AI effectiveness and in human readability.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Can Engineer Around It (If You Can Afford It)
&lt;/h3&gt;

&lt;p&gt;Writing this blog turned my question from "does API design and thoughtful abstraction matter anymore?" into "how much money do you have?"&lt;/p&gt;

&lt;p&gt;Every time an AI system has to figure out how something works, that's tokens being consumed and a potentially hacky workaround making its way into your code base. The adaptability is real, but anyone who's been ripping Claude Opus 4.6 with the 1 million token context window using agent teams knows that this is not free.&lt;/p&gt;

&lt;p&gt;You can throw tokens at bad abstractions, build sophisticated systems to work around them, add layers of verification, validation, and correction. Multiple agents checking each other's work. Memory and caching layers to avoid repeated discovery. But wouldn't it be nice to just get it right the first try? Clean abstractions and good API design can give that to you.&lt;/p&gt;

&lt;p&gt;Ideally you have both things in place. Clean APIs, meaningful abstractions, and clear documentation means the agent has minimal friction and looping. Then add in enough scaffolding around the agent to recover when an API it's using doesn't have all of those things. That combination reduces the cost of code generation, keeps code human readable and debuggable, and the adaptability is still built in.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why AI Agents Fail: 3 Failure Modes That Cost You Tokens and Time</title>
      <dc:creator>Elizabeth Fuentes L</dc:creator>
      <pubDate>Tue, 24 Mar 2026 02:14:33 +0000</pubDate>
      <link>https://dev.to/aws/why-ai-agents-fail-3-failure-modes-that-cost-you-tokens-and-time-1flb</link>
      <guid>https://dev.to/aws/why-ai-agents-fail-3-failure-modes-that-cost-you-tokens-and-time-1flb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;AI agents don't fail like traditional software — they don't crash with a stack trace. They fail silently: returning incomplete answers, freezing on slow APIs, or burning tokens calling the same tool over and over. The agent appears to work, but the output is wrong, late, or expensive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This series covers the three most common failure modes with research-backed solutions. Each technique has a runnable demo that measures the before/after difference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Working code:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens" rel="noopener noreferrer"&gt;github.com/aws-samples/sample-why-agents-fail&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demos use &lt;a href="https://strandsagents.com" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt; with &lt;a href="https://platform.openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; (GPT-4o-mini). The patterns are framework-agnostic — they apply to &lt;a href="https://langchain-ai.github.io/langgraph/" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt;, &lt;a href="https://github.com/microsoft/autogen" rel="noopener noreferrer"&gt;AutoGen&lt;/a&gt;, &lt;a href="https://github.com/crewAIInc/crewAI" rel="noopener noreferrer"&gt;CrewAI&lt;/a&gt;, or any framework that supports tool calling and lifecycle hooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Series: 3 Essential Fixes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Context Window Overflow&lt;/strong&gt; — Memory Pointer Pattern for large data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Tools That Never Respond&lt;/strong&gt; — Async handleId pattern for slow external APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Agent Reasoning Loops&lt;/strong&gt; — DebounceHook + clear tool states to block repeated calls&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What Happens When Tool Outputs Overflow the Context Window?
&lt;/h2&gt;

&lt;p&gt;Context window overflow occurs when a tool returns more data than the LLM can process — server logs, database results, or file contents that exceed the token limit. The agent does not crash with an error. It silently degrades: truncating data, losing context, or producing incomplete answers.&lt;/p&gt;

&lt;p&gt;Research from &lt;a href="https://arxiv.org/html/2511.22729v1" rel="noopener noreferrer"&gt;IBM&lt;/a&gt; quantifies this: a Materials Science workflow consumed &lt;strong&gt;20M tokens and failed&lt;/strong&gt;. The same workflow with memory pointers used &lt;strong&gt;1,234 tokens and succeeded&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz197w37u0rmuq1w3y3n9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz197w37u0rmuq1w3y3n9.jpg" alt="Comparison of an AI agent without Memory Pointer Pattern versus with it, showing how large data stays outside the context window"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix — Memory Pointer Pattern:&lt;/strong&gt; Store large data in &lt;code&gt;agent.state&lt;/code&gt;, return a short pointer to the context. The next tool resolves the pointer to access the full data:&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;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;fetch_application_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fetch logs. Stores large data as pointer to avoid context overflow.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Could be 200KB+
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20_000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logs-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logs&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;Data stored as pointer &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;. Use analyze tools to query it.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;analyze_error_patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pointer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Analyze errors — resolves pointer from agent.state.&lt;/span&gt;&lt;span class="sh"&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;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pointer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR&lt;/span&gt;&lt;span class="sh"&gt;"&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;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; errors across &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; services&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The LLM never sees the 200KB — it only sees &lt;code&gt;"Data stored as pointer 'logs-payment-service'"&lt;/code&gt; (52 bytes).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;a href="https://strandsagents.com" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;?&lt;/strong&gt; The &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/custom-tools/" rel="noopener noreferrer"&gt;&lt;code&gt;ToolContext&lt;/code&gt;&lt;/a&gt; API provides &lt;code&gt;agent.state&lt;/code&gt; as a native key-value store scoped to each agent — no global dictionaries, no external infrastructure. For multi-agent workflows, &lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/multi-agent-patterns/" rel="noopener noreferrer"&gt;&lt;code&gt;invocation_state&lt;/code&gt;&lt;/a&gt; shares data across agents in a &lt;a href="https://strandsagents.com/docs/user-guide/concepts/multi-agent/swarm/" rel="noopener noreferrer"&gt;Swarm&lt;/a&gt; with the same API.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Without pointers&lt;/th&gt;
&lt;th&gt;With Memory Pointers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data in context&lt;/td&gt;
&lt;td&gt;214KB (full logs)&lt;/td&gt;
&lt;td&gt;52 bytes (pointer)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent behavior&lt;/td&gt;
&lt;td&gt;Truncates or fails&lt;/td&gt;
&lt;td&gt;Processes all data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Errors detected&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Complete&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vsn5ydchfhy6lu831d0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vsn5ydchfhy6lu831d0.png" alt="Bar chart showing token usage across context management strategies"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full demo:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/01-context-overflow-demo" rel="noopener noreferrer"&gt;01-context-overflow-demo&lt;/a&gt; — single-agent and multi-agent (Swarm) implementations with notebooks.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Do AI Agents Freeze When Calling External APIs?
&lt;/h2&gt;

&lt;p&gt;AI agents freeze when MCP tools call slow or unresponsive external APIs. The agent blocks on the tool call, the user sees no progress, and after 7 seconds many implementations return a &lt;a href="https://community.openai.com/t/call-remote-mcp-server-tool-timed-out-resulting-in-error-424/1364167" rel="noopener noreferrer"&gt;424 error&lt;/a&gt;. &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/" rel="noopener noreferrer"&gt;MCP (Model Context Protocol)&lt;/a&gt; enables agents to call external tools, but does not handle timeout or retry by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frlvwpo26agw84bvi620c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frlvwpo26agw84bvi620c.jpg" alt="Synchronous MCP tool call showing agent blocked while waiting for slow API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix — Async handleId pattern:&lt;/strong&gt; The tool returns immediately with a job ID. The agent polls a separate &lt;code&gt;check_status&lt;/code&gt; tool:&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;mcp.server.fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;

&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timeout-demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;JOBS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_long_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return handle immediately — prevents timeout.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;job_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;JOBS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;job_id&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;status&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;processing&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;task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_process_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Background work
&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;Job started. Handle: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Use check_job_status to poll.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_job_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Poll job status — returns &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;processing&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; with result.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JOBS&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="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;job&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;FAILED: Job &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; not found&lt;/span&gt;&lt;span class="sh"&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;result&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;Still processing...&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Response time&lt;/th&gt;
&lt;th&gt;UX&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fast API (1s)&lt;/td&gt;
&lt;td&gt;3s total&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow API (15s)&lt;/td&gt;
&lt;td&gt;18s blocked&lt;/td&gt;
&lt;td&gt;Agent frozen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failing API&lt;/td&gt;
&lt;td&gt;424 error after 7s&lt;/td&gt;
&lt;td&gt;Agent crashes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async handleId&lt;/td&gt;
&lt;td&gt;~4s (immediate + poll)&lt;/td&gt;
&lt;td&gt;Agent responsive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1ef742j0hk1dcvuna54.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1ef742j0hk1dcvuna54.jpg" alt="Timeline visualization showing four MCP response patterns"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;a href="https://strandsagents.com" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;?&lt;/strong&gt; The &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/" rel="noopener noreferrer"&gt;&lt;code&gt;MCPClient&lt;/code&gt;&lt;/a&gt; connects to any MCP server with one line. The agent discovers tools at runtime via &lt;code&gt;list_tools_sync()&lt;/code&gt; — no hardcoded tool list. When the MCP server implements the async pattern, the agent automatically polls without extra orchestration code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full demo:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/02-mcp-timeout-demo" rel="noopener noreferrer"&gt;02-mcp-timeout-demo&lt;/a&gt; — local MCP server with all 4 scenarios and notebook.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Do AI Agents Repeat the Same Tool Call?
&lt;/h2&gt;

&lt;p&gt;AI agent reasoning loops happen when the agent calls the same tool repeatedly with identical parameters, making no progress. The root cause is ambiguous tool feedback — responses like "more results may be available" make the agent think another call will produce better results. &lt;a href="https://the-decoder.com/language-models-can-overthink-and-get-stuck-in-endless-thought-loops/" rel="noopener noreferrer"&gt;Research shows&lt;/a&gt; agents can loop hundreds of times without delivering an answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76bs0qj49uby5z0t06np.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76bs0qj49uby5z0t06np.jpg" alt="Diagram showing how ambiguous tool feedback causes loops versus how clear states and DebounceHook prevent them"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix 1 — Clear terminal states:&lt;/strong&gt; Tools return explicit &lt;code&gt;SUCCESS&lt;/code&gt; or &lt;code&gt;FAILED&lt;/code&gt; instead of ambiguous messages:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Ambiguous (causes loops)
&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;Found flights: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. More results may be available.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Clear (agent stops)
&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;SUCCESS: Booked flight &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;conf_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;passenger&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Confirmation sent.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Fix 2 — DebounceHook:&lt;/strong&gt; Detect and block duplicate tool calls at the framework level:&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;strands.hooks.registry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HookRegistry&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.hooks.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DebounceHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HookProvider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Block duplicate tool calls in a sliding window.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&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;window_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&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;call_history&lt;/span&gt; &lt;span class="o"&gt;=&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;window_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;window_size&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_hooks&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;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HookRegistry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeforeToolCallEvent&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;check_duplicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_duplicate&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;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BeforeToolCallEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&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;if&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;call_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel_tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BLOCKED: Duplicate call to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_use&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;call_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&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;call_history&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="n"&gt;call_history&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="n"&gt;window_size&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Tool calls&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ambiguous feedback (baseline)&lt;/td&gt;
&lt;td&gt;14 calls&lt;/td&gt;
&lt;td&gt;No definitive answer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DebounceHook&lt;/td&gt;
&lt;td&gt;12 calls (2 blocked)&lt;/td&gt;
&lt;td&gt;Completes with blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear SUCCESS states&lt;/td&gt;
&lt;td&gt;2 calls&lt;/td&gt;
&lt;td&gt;Immediate completion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;&lt;strong&gt;Why &lt;a href="https://strandsagents.com" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;?&lt;/strong&gt; The &lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/hooks/" rel="noopener noreferrer"&gt;&lt;code&gt;HookProvider&lt;/code&gt;&lt;/a&gt; API intercepts tool calls via &lt;code&gt;BeforeToolCallEvent&lt;/code&gt; before they execute. Setting &lt;code&gt;event.cancel_tool&lt;/code&gt; blocks execution at the framework level — the LLM cannot bypass it. This makes hooks composable: stack DebounceHook, LimitToolCounts, and custom validators on the same agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full demo:&lt;/strong&gt; &lt;a href="https://github.com/aws-samples/sample-why-agents-fail/tree/main/stop-ai-agents-wasting-tokens/03-reasoning-loops-demo" rel="noopener noreferrer"&gt;03-reasoning-loops-demo&lt;/a&gt; — all 4 scenarios with hooks and notebook.&lt;/p&gt;


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

&lt;p&gt;You need &lt;a href="https://python.org/downloads" rel="noopener noreferrer"&gt;Python 3.9+&lt;/a&gt;, &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt; (a fast Python package manager), and an &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;OpenAI API key&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;git clone https://github.com/aws-samples/sample-why-agents-fail
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-why-agents-fail/stop-ai-agents-wasting-tokens

&lt;span class="c"&gt;# Pick any demo&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;01-context-overflow-demo   &lt;span class="c"&gt;# or 02-mcp-timeout-demo, 03-reasoning-loops-demo&lt;/span&gt;
uv venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv pip &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;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-key-here"&lt;/span&gt;

uv run python test_&lt;span class="k"&gt;*&lt;/span&gt;.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Each demo is self-contained with its own dependencies, test script, and &lt;a href="https://jupyter.org/" rel="noopener noreferrer"&gt;Jupyter&lt;/a&gt; notebook.&lt;/p&gt;


&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What are the most common AI agent failure modes?
&lt;/h3&gt;

&lt;p&gt;The three most common failure modes are context window overflow (tool returns more data than the LLM can process), MCP tool timeouts (external APIs block the agent indefinitely), and reasoning loops (agent repeats the same tool call without progress). Each failure mode causes token waste and degrades response quality.&lt;/p&gt;
&lt;h3&gt;
  
  
  How do I reduce AI agent token costs?
&lt;/h3&gt;

&lt;p&gt;The two most effective techniques are memory pointers and clear tool states. The Memory Pointer Pattern stores large tool outputs in external state and passes short references to the LLM context — reducing token usage from 200KB+ to under 100 bytes per tool call. Clear terminal states (&lt;code&gt;SUCCESS&lt;/code&gt;/&lt;code&gt;FAILED&lt;/code&gt;) in tool responses prevent the agent from retrying completed operations, which can reduce tool calls from 14 to 2.&lt;/p&gt;
&lt;h3&gt;
  
  
  Can I use these patterns with frameworks other than Strands Agents?
&lt;/h3&gt;

&lt;p&gt;Yes. The Memory Pointer Pattern works with any framework that supports tool context (passing state between tools). The async handleId pattern is an MCP server design pattern — it works with any MCP-compatible agent. DebounceHook requires lifecycle hooks, which are available in &lt;a href="https://langchain-ai.github.io/langgraph/" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt;, &lt;a href="https://github.com/microsoft/autogen" rel="noopener noreferrer"&gt;AutoGen&lt;/a&gt;, and &lt;a href="https://github.com/crewAIInc/crewAI" rel="noopener noreferrer"&gt;CrewAI&lt;/a&gt; with different APIs.&lt;/p&gt;


&lt;h2&gt;
  
  
  References
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Research
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/html/2511.22729v1" rel="noopener noreferrer"&gt;Solving Context Window Overflow in AI Agents&lt;/a&gt; — IBM Research, Nov 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/pdf/2412.05449" rel="noopener noreferrer"&gt;Towards Effective GenAI Multi-Agent Collaboration&lt;/a&gt; — Amazon, Dec 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://octopus.com/blog/mcp-timeout-retry" rel="noopener noreferrer"&gt;Resilient AI Agents With MCP&lt;/a&gt; — Octopus, May 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://the-decoder.com/language-models-can-overthink-and-get-stuck-in-endless-thought-loops/" rel="noopener noreferrer"&gt;Language models can overthink&lt;/a&gt; — The Decoder, Jan 2025&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/state/" rel="noopener noreferrer"&gt;Strands Agent State&lt;/a&gt; — ToolContext and agent.state&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/" rel="noopener noreferrer"&gt;Strands MCP Tools&lt;/a&gt; — Connect any MCP server&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/docs/user-guide/concepts/agents/hooks/" rel="noopener noreferrer"&gt;Strands Hooks&lt;/a&gt; — Lifecycle events and tool cancellation&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Which failure mode have you hit in your agents? Share in the comments.&lt;/p&gt;



&lt;p&gt;Gracias!&lt;/p&gt;

&lt;p&gt;🇻🇪🇨🇱 &lt;a href="https://dev.to/elizabethfuentes12"&gt;Dev.to&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/lizfue/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; &lt;a href="https://github.com/elizabethfuentes12/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://twitter.com/elizabethfue12" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &lt;a href="https://www.instagram.com/elifue.tech" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; &lt;a href="https://www.youtube.com/channel/UCr0Gnc-t30m4xyrvsQpNp2Q" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;&lt;br&gt;
&lt;a href="https://linktr.ee/elizabethfuentesleone" rel="noopener noreferrer"&gt;Linktr&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__717518"&gt;
    &lt;a href="/elizabethfuentes12" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F717518%2Fb550b165-b8b9-405d-acfb-e5dc846765b0.png" alt="elizabethfuentes12 image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;Elizabeth Fuentes L&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/elizabethfuentes12"&gt;I help developers build production-ready AI applications through hands-on tutorials and open-source projects.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>ai</category>
      <category>python</category>
      <category>agents</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>When Did Every AWS Service Launch?</title>
      <dc:creator>Brooke Jamieson</dc:creator>
      <pubDate>Fri, 20 Mar 2026 20:49:31 +0000</pubDate>
      <link>https://dev.to/aws/when-did-every-aws-service-launch-3kc7</link>
      <guid>https://dev.to/aws/when-did-every-aws-service-launch-3kc7</guid>
      <description>&lt;p&gt;Last updated: 20 March 2026&lt;/p&gt;

&lt;p&gt;Last week I made a &lt;a href="https://www.linkedin.com/posts/brookejamieson_aws-s3-cloudcomputing-activity-7438195130861400064-2COu" rel="noopener noreferrer"&gt;6-minute video about the history of AWS and S3&lt;/a&gt; to celebrate S3's 20th birthday (our baby is all grown up!). I did extensive research for this and as part of this I kept finding myself looking up  things like "when did Lambda launch?" and "was DynamoDB before or after Redshift?" and getting frustrated that there wasn't a single page with all the dates in one place.&lt;/p&gt;

&lt;p&gt;I've always wanted to put one together, but I was too lazy to manually go through hundreds of launch announcements across 20 years of &lt;a href="https://aws.amazon.com/new/" rel="noopener noreferrer"&gt;What's New&lt;/a&gt; posts. But now we live in the future!&lt;/p&gt;

&lt;p&gt;Then I started using the &lt;a href="https://docs.aws.amazon.com/aws-mcp/latest/userguide/what-is-mcp-server.html" rel="noopener noreferrer"&gt;AWS MCP Server&lt;/a&gt; - a remote Model Context Protocol server that gives AI assistants authenticated access to AWS documentation, APIs, and pre-built workflows. One of its tools (&lt;code&gt;search_documentation&lt;/code&gt;) lets you search across all AWS documentation, including the "What's New" announcements. This is super handy for keeping track of launches too - if you want to learn more, I have a whole blog on this called &lt;a href="https://builder.aws.com/content/34ym3tOqN1EFbSYGXAXjrBEwU5D/you-should-just-use-the-docs-mcp-to-keep-up-with-launches-during-reinvent-because-everything-else-is-too-annoying" rel="noopener noreferrer"&gt;"You Should Just Use the Docs MCP to Keep Up with Launches During re:Invent Because Everything Else Is Too Annoying"&lt;/a&gt;. (&lt;em&gt;You can tell I did not use AI to write that title because it breaks every rule there is!&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;So buckle up, here we go - every AWS service launch date I could find, from SQS in 2004 to Route 53 Global Resolver in 2026. Some services have both a preview and a general availability (GA) date so I've included everything that was available. And every launch links back to the original announcement so you can read it yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Looking for a specific service?
&lt;/h3&gt;

&lt;p&gt;Here are the most-searched AWS service launch dates. Click through to the full entry below for links to the original announcements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon SQS&lt;/strong&gt; - 3 November 2004 (preview), 11 July 2006 (GA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon S3&lt;/strong&gt; - 13 March 2006&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon EC2&lt;/strong&gt; - 24 August 2006 (preview), 23 October 2008 (GA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon RDS&lt;/strong&gt; - 27 October 2009&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon DynamoDB&lt;/strong&gt; - 18 January 2012&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Redshift&lt;/strong&gt; - 14 February 2013&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Lambda&lt;/strong&gt; - 13 November 2014 (preview), 9 April 2015 (GA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon ECS&lt;/strong&gt; - 13 November 2014 (preview), 9 April 2015 (GA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon API Gateway&lt;/strong&gt; - 9 July 2015&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Aurora&lt;/strong&gt; - 12 November 2014 (preview), 27 July 2015 (GA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon EKS&lt;/strong&gt; - 29 November 2017 (preview), 5 June 2018 (GA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Fargate&lt;/strong&gt; - 29 November 2017&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon SageMaker&lt;/strong&gt; - 29 November 2017&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon EventBridge&lt;/strong&gt; - 11 July 2019&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CDK&lt;/strong&gt; - 11 July 2019&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock&lt;/strong&gt; - 13 April 2023 (preview), 28 September 2023 (GA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Aurora DSQL&lt;/strong&gt; - 3 December 2024 (preview), 28 May 2025 (GA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt; - 21 July 2025 (preview), 14 October 2025 (GA)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2004?
&lt;/h2&gt;

&lt;p&gt;This is my favourite trivia question to ask user groups - did you know that the first AWS Service was SQS? It all started with a queue!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Simple Queue Service (SQS)&lt;/strong&gt; - 3 November 2004 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2004/11/03/introducing-the-amazon-simple-queue-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
A managed message queue service. SQS was the very first AWS service, launching in preview more than a year before S3 existed. It went GA on 11 July 2006. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2006/07/11/amazon-simple-queue-service-amazon-sqs-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2006?
&lt;/h2&gt;

&lt;p&gt;S3 and EC2. Two services, and suddenly you could store objects and run virtual servers without owning hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon S3&lt;/strong&gt; - 13 March 2006 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2006/03/13/announcing-amazon-s3---simple-storage-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Object storage. The service that started it all (well, after SQS). S3 turned 20 in 2026.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon EC2&lt;/strong&gt; - 24 August 2006 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2006/08/24/announcing-amazon-elastic-compute-cloud-amazon-ec2---beta/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Virtual servers in the cloud. Launched in beta in 2006, went GA on 23 October 2008. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2008/10/23/amazon-ec2-exits-beta-and-now-offers-a-service-level-agreement/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2008?
&lt;/h2&gt;

&lt;p&gt;EC2 went GA, and EBS gave instances persistent storage for the first time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Elastic Block Store (EBS)&lt;/strong&gt; - 20 August 2008 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2008/08/20/amazon-elastic-block-store-now-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Persistent block storage for EC2 instances. Before EBS, your data disappeared when your instance stopped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon CloudFront&lt;/strong&gt; - 18 November 2008 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2008/11/18/whats-new-cloudfront/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Content delivery network. Went GA on 9 November 2010 with custom origin support. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2010/11/09/cloudfront-adds-support-for-custom-origins-and-sla/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2009?
&lt;/h2&gt;

&lt;p&gt;Monitoring, managed databases, and networking. You could now run an app on EC2, put it in a VPC, store data in RDS, and watch it all with CloudWatch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon EMR&lt;/strong&gt; - 2 April 2009 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2009/04/02/announcing-amazon-elastic-mapreduce/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Hadoop (and now Spark, Hive, Presto, and more). Originally called Elastic MapReduce - the name tells you what era of big data this came from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon CloudWatch&lt;/strong&gt; - 17 May 2009 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2009/05/17/monitoring-auto-scaling-elastic-load-balancing/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Monitoring and observability. Launched alongside Auto Scaling and Elastic Load Balancing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon VPC&lt;/strong&gt; - 26 August 2009 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2009/08/26/introducing-amazon-virtual-private-cloud/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Virtual private networking. Originally optional - now every AWS resource lives inside a VPC by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon RDS&lt;/strong&gt; - 27 October 2009 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2009/10/27/introducing-amazon-relational-database-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed relational databases. Started with MySQL, now supports PostgreSQL, MariaDB, Oracle, SQL Server, and (later) Aurora.&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2010?
&lt;/h2&gt;

&lt;p&gt;SNS, Route 53, and IAM. Messaging, DNS, and the service that controls who can do what.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon SNS&lt;/strong&gt; - 7 April 2010 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2010/04/07/announcing-amazon-simple-notification-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Pub/sub messaging and push notifications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Identity and Access Management (IAM)&lt;/strong&gt; - 2 September 2010 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2010/09/02/announcing-aws-identity-and-access-management-iam-preview-beta/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
The service that controls who can do what in your AWS account. Went GA on 3 May 2011. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2011/05/03/announcing-IAM/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Route 53&lt;/strong&gt; - 6 December 2010 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2010/12/06/announcing-amazon-route-53-dns-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
DNS and domain registration. Named after the port number for DNS (53).&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2011?
&lt;/h2&gt;

&lt;p&gt;CloudFormation, Elastic Beanstalk, SES, Direct Connect, and ElastiCache. Deploying and managing apps got a lot easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Elastic Beanstalk&lt;/strong&gt; - 19 January 2011 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2011/01/19/introducing-aws-elastic-beanstalk-beta/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Platform-as-a-service for deploying web apps. You give it your code, it handles the infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon SES&lt;/strong&gt; - 25 January 2011 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2011/01/25/introducing-amazon-simple-email-service-beta/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Email sending service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CloudFormation&lt;/strong&gt; - 25 February 2011 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2011/02/25/introducing-aws-cloudformation/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Infrastructure as code using JSON/YAML templates. The original IaC service on AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Direct Connect&lt;/strong&gt; - 3 August 2011 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2011/08/03/Announcing-AWS-Direct-Connect/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Dedicated network connection from your data centre to AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon ElastiCache&lt;/strong&gt; - 22 August 2011 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2011/08/22/announcing-amazon-elasticache/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Redis and Memcached.&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2012?
&lt;/h2&gt;

&lt;p&gt;DynamoDB, Glacier, SWF, Trusted Advisor, and Storage Gateway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon DynamoDB&lt;/strong&gt; - 18 January 2012 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2012/01/18/aws-announces-dynamodb/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed NoSQL database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Trusted Advisor&lt;/strong&gt; - 30 January 2012 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2012/01/30/amazon-web-services-introduces-new-premium-support-features/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Automated best-practice checks for your AWS account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Simple Workflow Service (SWF)&lt;/strong&gt; - 21 February 2012 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2012/02/21/aws-announces-swf/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Workflow coordination service. Largely superseded by Step Functions these days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon S3 Glacier&lt;/strong&gt; - 20 August 2012 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2012/08/20/announcing-amazon-glacier/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Cold archival storage. Originally a standalone service, now integrated as S3 storage classes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Storage Gateway&lt;/strong&gt; - 29 October 2012 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-storage-gateway-new-gateway-cached-volume-model/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Hybrid cloud storage that connects on-premises environments to AWS.&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2013?
&lt;/h2&gt;

&lt;p&gt;Redshift, CloudTrail, Kinesis, WorkSpaces, and a few others. Analytics and streaming showed up alongside the first virtual desktops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Elastic Transcoder&lt;/strong&gt; - 28 January 2013 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2013/01/28/announcing-amazon-elastic-transcoder/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Media transcoding service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Redshift&lt;/strong&gt; - 14 February 2013 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2013/02/14/amazon-redshift-available-to-all-customers/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed data warehouse. Launched on Valentine's Day, which feels appropriate for a service data teams love.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS OpsWorks&lt;/strong&gt; - 18 February 2013 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2013/02/18/announcing-aws-opsworks/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Configuration management using Chef and Puppet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CloudHSM&lt;/strong&gt; - 26 March 2013 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2013/03/26/announcing-aws-cloudhsm/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Hardware security modules in the cloud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CloudTrail&lt;/strong&gt; - 13 November 2013 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2013/11/13/announcing-aws-cloudtrail/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
API call logging. Records every action taken in your AWS account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon WorkSpaces&lt;/strong&gt; - 13 November 2013 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2013/11/13/introducing-amazon-workspaces/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Virtual desktops. Went GA on 26 March 2014. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2014/03/26/amazon-workspaces-available-for-all-customers/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Kinesis Data Streams&lt;/strong&gt; - 14 November 2013 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2013/11/14/introducing-amazon-kinesis/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Real-time data streaming. Went GA on 16 December 2013. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2013/12/16/amazon-kinesis-available-for-all-customers/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2014?
&lt;/h2&gt;

&lt;p&gt;Lambda, Aurora, ECS, Cognito, KMS, Config, and CodeDeploy. A big November - Lambda and ECS both previewed on the 13th.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Cost Explorer&lt;/strong&gt; - 8 April 2014 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2014/04/08/introducing-cost-explorer-view-and-analyze-your-historical-aws-spend/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Visualise and analyse your AWS spending over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Cognito&lt;/strong&gt; - 10 July 2014 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2014/07/10/introducing-amazon-cognito/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
User authentication and identity management for apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon WorkDocs&lt;/strong&gt; - 10 July 2014 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2014/07/10/introducing-amazon-zocalo/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Enterprise document storage and sharing. Originally launched as Amazon Zocalo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Directory Service&lt;/strong&gt; - 21 October 2014 · &lt;a href="https://aws.amazon.com/blogs/aws/new-aws-directory-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Microsoft Active Directory in the cloud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CodeDeploy&lt;/strong&gt; - 12 November 2014 · &lt;a href="https://aws.amazon.com/blogs/aws/code-management-and-deployment/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Automated code deployments to EC2, Lambda, and on-premises servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Config&lt;/strong&gt; - 12 November 2014 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2014/11/12/introducing-aws-config/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Tracks configuration changes across your AWS resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Key Management Service (KMS)&lt;/strong&gt; - 12 November 2014 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2014/11/12/introducing-aws-key-management-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed encryption keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Aurora&lt;/strong&gt; - 12 November 2014 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2014/11/12/introducing-amazon-rds-for-aurora/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
MySQL and PostgreSQL-compatible relational database built for the cloud. Went GA on 27 July 2015. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/07/amazon-aurora-mysql-compatible-database-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Lambda&lt;/strong&gt; - 13 November 2014 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2014/11/13/introducing-aws-lambda/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Run code without provisioning servers. Went GA on 9 April 2015. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/04/aws-lambda-brings-scalable-event-computing-to-mobile-and-iot-backends/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Elastic Container Service (ECS)&lt;/strong&gt; - 13 November 2014 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2014/11/13/introducing-amazon-ec2-container-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed container orchestration. Went GA on 9 April 2015. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/04/amazon-ec2-container-service-is-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2015?
&lt;/h2&gt;

&lt;p&gt;API Gateway, IoT Core, OpenSearch (then Elasticsearch), WAF, Snowball, CodeCommit, CodePipeline, and more. Eighteen services across the year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon WorkMail&lt;/strong&gt; - 28 January 2015 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/01/28/introducing-amazon-workmail/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed business email and calendar. Went GA on 4 January 2016. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/01/amazon-workmail-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Elastic File System (EFS)&lt;/strong&gt; - 9 April 2015 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/04/introducing-amazon-efs/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed NFS file storage for EC2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Budgets&lt;/strong&gt; - 29 June 2015 · &lt;a href="https://aws.amazon.com/blogs/aws/new-aws-budgets-and-forecasts/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Set custom cost and usage budgets with alerts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CodeCommit&lt;/strong&gt; - 9 July 2015 · &lt;a href="https://aws.amazon.com/blogs/aws/now-available-aws-codecommit/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Git repositories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CodePipeline&lt;/strong&gt; - 9 July 2015 · &lt;a href="https://aws.amazon.com/blogs/aws/now-available-aws-codepipeline/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Continuous delivery pipeline service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Device Farm&lt;/strong&gt; - 9 July 2015 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/07/deliver-high-quality-apps-by-testing-them-against-real-phones-and-tablets-in-the-aws-cloud-with-device-farm/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Test mobile apps on real devices in the cloud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Service Catalog&lt;/strong&gt; - 9 July 2015 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/07/amazon-web-services-launches-aws-service-catalog/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Create and manage approved IT service catalogues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon API Gateway&lt;/strong&gt; - 9 July 2015 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/07/introducing-amazon-api-gateway/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Create, publish, and manage REST and WebSocket APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon OpenSearch Service&lt;/strong&gt; - 1 October 2015 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/10/introducing-amazon-elasticsearch-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed search and analytics. Originally launched as Amazon Elasticsearch Service, rebranded in 2021.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS WAF&lt;/strong&gt; - 6 October 2015 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/10/introducing-aws-waf/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Web application firewall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Snowball&lt;/strong&gt; - 7 October 2015 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/10/aws-import-export-snowball/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Physical data transport device. Ship your data to AWS on a rugged appliance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Kinesis Data Firehose&lt;/strong&gt; - 7 October 2015 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/10/introducing-amazon-kinesis-firehose/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Load streaming data into data stores and analytics services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Inspector&lt;/strong&gt; - 7 October 2015 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/10/announcing-amazon-inspector/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Automated security assessment. Went GA on 19 April 2016. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/04/amazon-inspector-security-vulnerability-assessment-service-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon QuickSight&lt;/strong&gt; - 7 October 2015 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2015/10/introducing-amazon-quicksight-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Business intelligence and data visualisation. Went GA on 15 November 2016. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/11/amazon-quicksight-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT Core&lt;/strong&gt; - 8 October 2015 (preview) · &lt;a href="https://aws.amazon.com/blogs/aws/aws-iot-cloud-services-for-connected-devices/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Connect IoT devices to the cloud. Went GA on 18 December 2015. (&lt;a href="https://aws.amazon.com/blogs/aws/aws-iot-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Elastic Container Registry (ECR)&lt;/strong&gt; - 21 December 2015 · &lt;a href="https://aws.amazon.com/blogs/aws/ec2-container-registry-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Docker container registry.&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2016?
&lt;/h2&gt;

&lt;p&gt;Certificate Manager and DMS shipped in Q1, Flink arrived mid-year, and then November brought Lightsail, Athena, Polly, Rekognition, Step Functions, and a dozen more. Over 25 services this year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Certificate Manager&lt;/strong&gt; - 21 January 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/new-aws-certificate-manager-deploy-ssltls-based-apps-on-aws/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Free SSL/TLS certificates for AWS services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon GameLift&lt;/strong&gt; - 9 February 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/lumberyard-amazon-gamelift-twitch-for-games-on-aws/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed game server hosting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Lumberyard&lt;/strong&gt; - 9 February 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/lumberyard-amazon-gamelift-twitch-for-games-on-aws/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Game engine. (Later evolved into Open 3D Engine.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Database Migration Service (DMS)&lt;/strong&gt; - 15 March 2016 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/03/aws-database-migration-service-is-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Migrate databases to AWS with minimal downtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Application Discovery Service&lt;/strong&gt; - 19 April 2016 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/04/aws-application-discovery-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Discover on-premises applications for migration planning. Went GA on 12 May 2016. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/05/now-available-aws-application-discovery-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Managed Service for Apache Flink&lt;/strong&gt; - 11 August 2016 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/08/introducing-amazon-kinesis-analytics/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Real-time stream processing. Originally launched as Amazon Kinesis Analytics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Server Migration Service&lt;/strong&gt; - 24 October 2016 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/10/new-aws-server-migration-service-sms-makes-it-easier-and-faster-to-migrate-on-premises-servers-to-aws/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Migrate on-premises workloads to AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Organizations&lt;/strong&gt; - 29 November 2016 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/11/announcing-aws-organizations-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Centrally manage multiple AWS accounts. Went GA on 27 February 2017. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/02/aws-organizations-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Athena&lt;/strong&gt; - 30 November 2016 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/11/introducing-amazon-athena-a-pay-as-you-go-interactive-query-service-that-makes-it-easy-to-analyze-data-in-amazon-s3-using-standard-sql/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Query data in S3 using SQL. No infrastructure to manage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Lightsail&lt;/strong&gt; - 30 November 2016 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/11/introducing-amazon-lightsail/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Simple virtual private servers with fixed monthly pricing. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Polly&lt;/strong&gt; - 30 November 2016 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/11/introducing-amazon-polly/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Text-to-speech service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Rekognition&lt;/strong&gt; - 30 November 2016 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/11/introducing-amazon-rekognition/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Image and video analysis using deep learning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Lex&lt;/strong&gt; - 30 November 2016 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/11/introducing-amazon-lex-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Build conversational interfaces (the tech behind Alexa). Went GA on 19 April 2017. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/04/aws-mobile-hub-integration-with-amazon-lex-is-now-ga/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT Greengrass&lt;/strong&gt; - 30 November 2016 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2016/11/announcing-aws-greengrass-now-in-limited-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Run AWS Lambda functions on edge devices. Went GA on 7 June 2017. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/06/aws-greengrass-is-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Snowball Edge&lt;/strong&gt; - 30 November 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-snowball-edge-more-storage-local-endpoints-lambda-functions/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Snowball with local compute capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Snowmobile&lt;/strong&gt; - 30 November 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-snowmobile-move-exabytes-of-data-to-the-cloud-in-weeks/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
A literal shipping container truck for exabyte-scale data transfer. My favourite AWS Service of all time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CodeBuild&lt;/strong&gt; - 1 December 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-codebuild-fully-managed-build-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed build service for compiling and testing code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Personal Health Dashboard&lt;/strong&gt; - 1 December 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/new-aws-personal-health-dashboard-status-you-can-relate-to/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Personalised view of AWS service health events affecting your resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Shield&lt;/strong&gt; - 1 December 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-shield-protect-your-applications-from-ddos-attacks/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
DDoS protection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Step Functions&lt;/strong&gt; - 1 December 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/new-aws-step-functions-build-distributed-applications-using-visual-workflows/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Visual workflow orchestration for distributed applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon AppStream 2.0&lt;/strong&gt; - 1 December 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/amazon-appstream-2-0-stream-desktop-apps-from-aws/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Stream desktop applications to any device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Pinpoint&lt;/strong&gt; - 1 December 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/amazon-pinpoint-hit-your-targets-with-aws/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Targeted push notifications and campaign management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Glue&lt;/strong&gt; - 1 December 2016 (preview) · &lt;a href="https://aws.amazon.com/blogs/aws/launch-aws-glue-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Serverless ETL (extract, transform, load) service. Went GA on 14 August 2017. (&lt;a href="https://aws.amazon.com/blogs/aws/launch-aws-glue-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Artifact&lt;/strong&gt; - 7 December 2016 · &lt;a href="https://aws.amazon.com/blogs/security/introducing-aws-artifact-speeding-access-to-compliance-reports/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
On-demand access to AWS compliance reports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Managed Services&lt;/strong&gt; - 12 December 2016 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-managed-services-infrastructure-operations-management-for-the-enterprise/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
AWS operates your infrastructure for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2017?
&lt;/h2&gt;

&lt;p&gt;Connect and X-Ray shipped in the spring. The Elemental media suite arrived in November. Fargate, SageMaker, EKS, and GuardDuty followed days later. Over 30 services this year - the most in a single year until that point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Batch&lt;/strong&gt; - 5 January 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/01/aws-batch-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Run batch computing workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Chime&lt;/strong&gt; - 13 February 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/02/announcing-amazon-chime-frustration-free-online-meetings-with-exceptional-audio-and-video-quality/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Video conferencing and online meetings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Connect&lt;/strong&gt; - 28 March 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/03/introducing-amazon-connect/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Cloud-based contact centre.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CodeStar&lt;/strong&gt; - 19 April 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/04/introducing-aws-codestar/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Unified interface for managing software development activities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS X-Ray&lt;/strong&gt; - 19 April 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/04/aws-x-ray-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Distributed tracing for debugging production applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Migration Hub&lt;/strong&gt; - 14 August 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/08/introducing-aws-migration-hub/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Track application migrations across multiple AWS tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Macie&lt;/strong&gt; - 14 August 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/08/introducing-amazon-macie/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Discover and protect sensitive data using machine learning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VMware Cloud on AWS&lt;/strong&gt; - 28 August 2017 · &lt;a href="https://aws.amazon.com/blogs/aws/vmware-cloud-on-aws-now-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Run VMware workloads on AWS infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS PrivateLink&lt;/strong&gt; - 8 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-privatelink-for-aws-services/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Private connectivity between VPCs and AWS services without traversing the public internet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Amplify&lt;/strong&gt; - 21 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-amplify-a-declarative-javascript-library-for-cloud-development-with-mobile-or-web-applications/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Build full-stack web and mobile apps. Started as a JavaScript library, now a full development platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Elemental MediaConvert&lt;/strong&gt; - 26 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-elemental-mediaconvert/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
File-based video transcoding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Elemental MediaLive&lt;/strong&gt; - 26 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-elemental-medialive/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Live video encoding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Elemental MediaPackage&lt;/strong&gt; - 26 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-elemental-mediapackage/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Video origination and packaging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Elemental MediaStore&lt;/strong&gt; - 26 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-elemental-mediastore/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Storage optimised for media.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Elemental MediaTailor&lt;/strong&gt; - 26 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-elemental-mediatailor/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Server-side ad insertion for video.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon GuardDuty&lt;/strong&gt; - 28 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/announcing-amazon-guardduty-intelligent-threat-detection/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Threat detection for your AWS account. Analyses CloudTrail, VPC Flow Logs, and DNS logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon MQ&lt;/strong&gt; - 28 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-amazon-mq/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed message broker for Apache ActiveMQ and RabbitMQ.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS AppSync&lt;/strong&gt; - 28 November 2017 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-appsync-a-managed-graphql-service-with-real-time-data-and-offline-programming/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed GraphQL APIs with real-time data and offline support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Fargate&lt;/strong&gt; - 29 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-fargate-a-technology-to-run-containers-without-managing-infrastructure/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Run containers without managing servers. Serverless compute for ECS and EKS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT Device Management&lt;/strong&gt; - 29 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/announcing-aws-iot-device-management/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Onboard, organise, and remotely manage IoT devices at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Systems Manager&lt;/strong&gt; - 29 November 2017 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-systems-manager/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Operational hub for managing AWS resources. Patch management, parameter store, run commands, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Comprehend&lt;/strong&gt; - 29 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-amazon-comprehend-discover-insights-from-text/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Natural language processing - extract insights from text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon FreeRTOS&lt;/strong&gt; - 29 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/amazon-freertos-iot-operation-system-for-microcontrollers/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Operating system for microcontrollers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Kinesis Video Streams&lt;/strong&gt; - 29 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-amazon-kinesis-video-streams/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Stream video from connected devices to AWS for analytics and ML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon SageMaker&lt;/strong&gt; - 29 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-amazon-sagemaker/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Build, train, and deploy machine learning models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS DeepLens&lt;/strong&gt; - 29 November 2017 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-deeplens-the-worlds-first-deep-learning-enabled-video-camera-for-developers/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Deep learning-enabled video camera for developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT 1-Click&lt;/strong&gt; - 29 November 2017 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/aws-iot-one-click-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Trigger Lambda functions from simple IoT devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT Analytics&lt;/strong&gt; - 29 November 2017 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/announcing-aws-iot-analytics/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Analytics for IoT data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Transcribe&lt;/strong&gt; - 29 November 2017 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-amazon-transcribe-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Automatic speech-to-text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Translate&lt;/strong&gt; - 29 November 2017 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-amazon-translate-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Neural machine translation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Neptune&lt;/strong&gt; - 29 November 2017 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/amazon-neptune-fast-reliable-graph-database-built-for-the-cloud/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed graph database. Went GA on 30 May 2018. (&lt;a href="https://aws.amazon.com/blogs/aws/amazon-neptune-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Elastic Kubernetes Service (EKS)&lt;/strong&gt; - 29 November 2017 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-amazon-elastic-container-service-for-kubernetes/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Kubernetes. Went GA on 5 June 2018. (&lt;a href="https://aws.amazon.com/blogs/aws/amazon-eks-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT Device Defender&lt;/strong&gt; - 29 November 2017 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/aws-iot-device-defender-helps-you-manage-device-security/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Security management for IoT devices. Went GA on 2 August 2018. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/08/aws-iot-device-defender-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Serverless Application Repository&lt;/strong&gt; - 30 November 2017 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/aws-serverless-application-repository-enables-customers-to-discover-deploy-and-publish-serverless-applications/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Discover and deploy serverless applications. Went GA on 21 February 2018. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/02/aws-serverless-application-repository-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Cloud9&lt;/strong&gt; - 30 November 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-aws-cloud9/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Cloud-based IDE.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IAM Identity Center&lt;/strong&gt; - 7 December 2017 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/12/announcing-aws-single-sign-on--sso/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Centrally manage single sign-on access to multiple AWS accounts. Originally launched as AWS SSO.&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2018?
&lt;/h2&gt;

&lt;p&gt;Secrets Manager, Private CA, and Firewall Manager shipped in April. Then November and December brought a wave of preview announcements — many with GA dates stretching into 2019 and even 2020. Transit Gateway and the FSx family also arrived.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Firewall Manager&lt;/strong&gt; - 4 April 2018 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-firewall-manager-central-management-for-your-web-application-portfolio/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Centrally manage firewall rules across accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Private CA&lt;/strong&gt; - 4 April 2018 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-certificate-manager-launches-private-certificate-authority/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed private certificate authority.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Secrets Manager&lt;/strong&gt; - 4 April 2018 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-secrets-manager-store-distribute-and-rotate-credentials-securely/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Store, rotate, and manage secrets like database credentials and API keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Corretto&lt;/strong&gt; - 14 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-amazon-corretto/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
No-cost, production-ready distribution of OpenJDK. Went GA on 31 January 2019. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/01/amazon-corretto-is-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Resource Access Manager (RAM)&lt;/strong&gt; - 21 November 2018 · &lt;a href="https://aws.amazon.com/blogs/aws/new-aws-resource-access-manager-cross-account-resource-sharing/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Share AWS resources across accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Transfer Family&lt;/strong&gt; - 25 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/aws-transfer-for-sftp-fully-managed-sftp-for-s3/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed SFTP, FTPS, and FTP for S3 and EFS. Originally launched as AWS Transfer for SFTP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS DataSync&lt;/strong&gt; - 26 November 2018 · &lt;a href="https://aws.amazon.com/blogs/aws/new-aws-datasync-automated-and-accelerated-data-transfer/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Automated data transfer between on-premises storage and AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Global Accelerator&lt;/strong&gt; - 26 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-aws-global-accelerator/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Improve availability and performance using the AWS global network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS RoboMaker&lt;/strong&gt; - 26 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/announcing-aws-robomaker-a-new-cloud-robotics-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Develop, test, and deploy robotics applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Transit Gateway&lt;/strong&gt; - 26 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-aws-transit-gateway/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Connect VPCs and on-premises networks through a central hub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT Events&lt;/strong&gt; - 26 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-aws-iot-events-now-available-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Detect and respond to events from IoT sensors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT Things Graph&lt;/strong&gt; - 26 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/iot-things-graph-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Visually connect devices and web services for IoT applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT SiteWise&lt;/strong&gt; - 26 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/announcing-aws-iot-sitewise-now-available-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Collect and analyse industrial equipment data. Went GA on 9 July 2020. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/07/aws-iot-sitewise-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Elemental MediaConnect&lt;/strong&gt; - 27 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-aws-elemental-mediaconnect/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Reliable live video transport.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Cloud Map&lt;/strong&gt; - 28 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-aws-cloud-map/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Service discovery for cloud resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Inferentia&lt;/strong&gt; - 28 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/announcing-amazon-inferentia-machine-learning-inference-microchip/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Custom ML inference chip designed by AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS License Manager&lt;/strong&gt; - 28 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/announcing-aws-license-manager/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Manage software licences across AWS and on-premises.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Elastic Inference&lt;/strong&gt; - 28 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-amazon-elastic-inference/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Attach GPU-powered inference acceleration to any EC2 or SageMaker instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon FSx for Lustre&lt;/strong&gt; - 28 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/amazon-fsx-lustre/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
High-performance file system for compute-intensive workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon FSx for Windows File Server&lt;/strong&gt; - 28 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/amazon-fsx-windows/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Windows-native file system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS App Mesh&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-aws-app-mesh---service-mesh-for-microservices-on-aws/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Service mesh for microservices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS DeepRacer&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-aws-deepracer/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Autonomous 1/18th scale race car for learning reinforcement learning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Managed Blockchain&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-amazon-managed-blockchain/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Create and manage blockchain networks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Managed Streaming for Apache Kafka (MSK)&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-amazon-managed-streaming-for-kafka-in-public-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Apache Kafka.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Textract&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-amazon-textract-now-in-preview-easily-extract-text-and-data-from-virtually-any-document/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Extract text and data from documents using ML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Personalize&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-amazon-personalize-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
ML-powered personalisation. Went GA on 10 June 2019. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/06/amazon-personalize-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Security Hub&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-aws-security-hub/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Centralised security findings. Went GA on 24 June 2019. (&lt;a href="https://aws.amazon.com/blogs/aws/aws-security-hub-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Forecast&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-amazon-forecast-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Time-series forecasting using ML. Went GA on 22 August 2019. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/08/amazon-forecast-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon QLDB&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-amazon-qldb/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Quantum Ledger Database - immutable, cryptographically verifiable transaction log. Went GA on 10 September 2019. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/09/announcing-general-availability-qldb/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Outposts&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/announcing-aws-outposts/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Run AWS infrastructure on-premises. Went GA on 3 December 2019. (&lt;a href="https://aws.amazon.com/blogs/aws/aws-outposts-now-available-order-your-racks-today/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Timestream&lt;/strong&gt; - 28 November 2018 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/announcing-amazon-timestream/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed time-series database. Went GA on 30 September 2020. (&lt;a href="https://aws.amazon.com/blogs/aws/store-and-access-time-series-data-at-any-scale-with-amazon-timestream-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Well-Architected Tool&lt;/strong&gt; - 29 November 2018 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-aws-well-architected-tool/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Review your workloads against AWS best practices.&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2019?
&lt;/h2&gt;

&lt;p&gt;CDK and EventBridge went GA in July. Control Tower shipped in June. Lake Formation arrived in August. December brought another batch of previews including Braket, Detective, and Kendra.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon DocumentDB&lt;/strong&gt; - 8 January 2019 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/01/amazon-documentdb-with-mongodb-compatibility-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
MongoDB-compatible managed document database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Backup&lt;/strong&gt; - 16 January 2019 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-backup-automate-and-centrally-manage-your-backups/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Centralised backup across AWS services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon WorkLink&lt;/strong&gt; - 23 January 2019 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/01/introducing-amazon-worklink/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Secure mobile access to internal websites and web apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Ground Station&lt;/strong&gt; - 23 May 2019 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/05/announcing-general-availability-of-aws-ground-station-/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Communicate with satellites. Yes, actual satellites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Control Tower&lt;/strong&gt; - 24 June 2019 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/06/aws-control-tower-is-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Set up and govern a multi-account AWS environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Cloud Development Kit (CDK)&lt;/strong&gt; - 11 July 2019 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/07/the-aws-cloud-development-kit-aws-cdk-is-now-generally-available1/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Define cloud infrastructure using familiar programming languages instead of YAML/JSON.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon EventBridge&lt;/strong&gt; - 11 July 2019 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/07/introducing-amazon-eventbridge/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Serverless event bus. Connect applications using events from AWS services, SaaS apps, and your own code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Chatbot&lt;/strong&gt; - 24 July 2019 · &lt;a href="https://aws.amazon.com/blogs/devops/introducing-aws-chatbot-chatops-for-aws/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
ChatOps for AWS - get notifications and run commands in Slack and Teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Elemental Appliances &amp;amp; Software&lt;/strong&gt; - 7 August 2019 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/08/aws-elemental-appliances-and-software-now-available-in-aws-management-console/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
On-premises video processing hardware and software.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Lake Formation&lt;/strong&gt; - 9 August 2019 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/08/aws-lake-formation-is-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Build, secure, and manage data lakes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Data Exchange&lt;/strong&gt; - 13 November 2019 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/11/introducing-aws-data-exchange/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Find, subscribe to, and use third-party data in the cloud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Launch Wizard&lt;/strong&gt; - 13 November 2019 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/11/easily-deploy-sql-server-always-on-solutions-using-aws-launch-wizard/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Guided deployments for enterprise applications like SQL Server and SAP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS DeepComposer&lt;/strong&gt; - 1 December 2019 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/introducing-aws-deepcomposer/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
ML-powered music composition. A musical keyboard for learning generative AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IAM Access Analyzer&lt;/strong&gt; - 2 December 2019 · &lt;a href="https://aws.amazon.com/blogs/aws/identify-unintended-resource-access-with-aws-identity-and-access-management-iam-access-analyzer/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Identify resources shared with external entities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Braket&lt;/strong&gt; - 2 December 2019 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/introducing-amazon-braket/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Quantum computing service. Went GA on 13 August 2020. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/08/quantum-computing-available-aws-through-amazon-braket/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon EC2 Image Builder&lt;/strong&gt; - 2 December 2019 · &lt;a href="https://aws.amazon.com/blogs/aws/automate-os-image-build-pipelines-with-ec2-image-builder/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Automate the creation and maintenance of AMIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Compute Optimizer&lt;/strong&gt; - 3 December 2019 · &lt;a href="https://aws.amazon.com/blogs/aws/aws-compute-optimizer-your-customized-resource-optimization-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
ML-powered recommendations for right-sizing compute resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Augmented AI (A2I)&lt;/strong&gt; - 3 December 2019 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/announcing-amazon-augmented-ai/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Human review workflows for ML predictions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon CodeGuru&lt;/strong&gt; - 3 December 2019 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/aws-announces-amazon-codeguru-for-automated-code-reviews-and-application-performance-recommendations/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
ML-powered code reviews and application performance recommendations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Detective&lt;/strong&gt; - 3 December 2019 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/introducing-amazon-detective/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Investigate security findings. Went GA on 31 March 2020. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/03/amazon-detective-is-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Keyspaces (for Apache Cassandra)&lt;/strong&gt; - 3 December 2019 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/announcing-amazon-managed-apache-cassandra-service-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Cassandra-compatible database. Went GA on 23 April 2020. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/04/amazon-keyspaces-for-apache-cassandra-is-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Kendra&lt;/strong&gt; - 3 December 2019 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/announcing-amazon-kendra-reinventing-enterprise-search-with-machine-learning/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
ML-powered enterprise search. Went GA on 11 May 2020. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/05/amazon-kendra-is-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Fraud Detector&lt;/strong&gt; - 3 December 2019 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/introducing-amazon-fraud-detector-now-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Detect online fraud using ML. Went GA on 28 July 2020. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/07/detect-fraud-faster-using-machine-learning-with-amazon-fraud-detector/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Wavelength&lt;/strong&gt; - 3 December 2019 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/announcing-aws-wavelength-delivering-ultra-low-latency-applications-5g/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Ultra-low latency applications at 5G network edges. Went GA on 6 August 2020. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/08/announcing-the-general-availability-of-aws-wavelength-in-boston-and-the-san-francisco-bay-area/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2020?
&lt;/h2&gt;

&lt;p&gt;Fewer net-new services than previous years. Network Firewall, CloudShell, MWAA (managed Airflow), and a batch of ML services. A lot of 2019 previews also went GA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon AppFlow&lt;/strong&gt; - 22 April 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/04/introducing-amazon-appflow/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Transfer data between SaaS apps and AWS without code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CodeArtifact&lt;/strong&gt; - 10 June 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/06/introducing-aws-codeartifact-a-fully-managed-software-artifact-repository-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed artifact repository for software packages (npm, PyPI, Maven, etc.).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Snowcone&lt;/strong&gt; - 17 June 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/06/aws-announces-aws-snowcone/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
The smallest Snow family device. Portable, rugged edge computing and data transfer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Honeycode&lt;/strong&gt; - 24 June 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/06/announcing-amazon-honeycode/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Build mobile and web apps without code using a spreadsheet-like interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon RDS Proxy&lt;/strong&gt; - 30 June 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/06/amazon-rds-proxy-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed database proxy that makes applications more scalable and resilient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS App2Container&lt;/strong&gt; - 1 July 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/07/announcing-aws-app2container/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Containerise existing .NET and Java applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Interactive Video Service (IVS)&lt;/strong&gt; - 15 July 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/07/introducing-amazon-ivs/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed live streaming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Distro for OpenTelemetry&lt;/strong&gt; - 21 October 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/10/announcing-aws-distro-for-opentelemetry-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
AWS-supported distribution of the OpenTelemetry project for distributed tracing and metrics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Glue DataBrew&lt;/strong&gt; - 11 November 2020 · &lt;a href="https://aws.amazon.com/blogs/aws/announcing-aws-glue-databrew-a-visual-data-preparation-tool-that-helps-you-clean-and-normalize-data-faster/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Visual data preparation tool - clean and normalise data without writing code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Network Firewall&lt;/strong&gt; - 19 November 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/11/introducing-aws-network-firewall/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed network firewall for VPCs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon MWAA&lt;/strong&gt; - 24 November 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/11/introducing-amazon-managed-workflows-for-apache-airflow-mwaa/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Apache Airflow for workflow orchestration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Monitron&lt;/strong&gt; - 1 December 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/introducing-amazon-monitron/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
End-to-end machine monitoring system using sensors and ML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon ECS Anywhere&lt;/strong&gt; - 1 December 2020 (preview) · &lt;a href="https://aws.amazon.com/blogs/containers/introducing-amazon-ecs-anywhere/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Run ECS tasks on your own infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Lookout for Vision&lt;/strong&gt; - 1 December 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/aws-announces-amazon-lookout-for-vision-to-automate-quality-inspection/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Spot product defects using computer vision. Went GA on 24 February 2021. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/02/automate-quality-inspection-amazon-lookout-vision-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Lookout for Equipment&lt;/strong&gt; - 1 December 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/announcing-amazon-lookout-for-equipment-a-service-which-detects-abnormal-equipment-behavior-and-enable-predictive-maintenance/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Detect abnormal equipment behaviour for predictive maintenance. Went GA on 8 April 2021. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/04/detect-abnormal-equipment-behavior-amazon-lookout-equipment-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon DevOps Guru&lt;/strong&gt; - 1 December 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/aws-announces-amazon-devops-guru-in-preview-an-ml-powered-cloud-operations-service-to-improve-application-availability-for-aws-workloads/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
ML-powered operations service that identifies application issues. Went GA on 4 May 2021. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/05/amazon-devops-guru-now-generally-available-with-additional-capabilities/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Proton&lt;/strong&gt; - 1 December 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/amazon-web-services-announces-aws-proton/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Automated infrastructure provisioning for container and serverless deployments. Went GA on 9 June 2021. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/06/aws-announces-the-general-availability-of-aws-proton/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Panorama&lt;/strong&gt; - 1 December 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/introducing-aws-panorama-for-computer-vision-at-the-edge/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Add computer vision to existing cameras. Went GA on 20 October 2021. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/10/aws-panorama-appliance-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Audit Manager&lt;/strong&gt; - 8 December 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/aws-announces-aws-audit-manager/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Continuously audit your AWS usage for compliance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Lookout for Metrics&lt;/strong&gt; - 8 December 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/announcing-amazon-lookout-for-metrics/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Detect anomalies in business metrics. Went GA on 25 March 2021. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/03/amazon-lookout-for-metrics-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon HealthLake&lt;/strong&gt; - 8 December 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/introducing-amazon-healthlake-to-make-sense-of-health-data/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Store, transform, and analyse health data. Went GA on 15 July 2021. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/07/amazon-healthlake-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS CloudShell&lt;/strong&gt; - 15 December 2020 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/introducing-aws-cloudshell/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Browser-based shell with the AWS CLI pre-installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Managed Grafana&lt;/strong&gt; - 15 December 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/aws-announces-amazon-managed-service-for-grafana-in-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Grafana for operational dashboards. Went GA on 31 August 2021. (&lt;a href="https://aws.amazon.com/blogs/aws/amazon-managed-grafana-is-now-generally-available-with-many-new-features/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Managed Service for Prometheus&lt;/strong&gt; - 15 December 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/aws-announces-amazon-managed-service-for-prometheus/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Prometheus for container monitoring. Went GA on 29 September 2021. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/09/amazon-managed-service-prometheus-alert-manager-rules/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Location Service&lt;/strong&gt; - 16 December 2020 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/12/aws-announces-amazon-location-service-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Maps, geofencing, and location-based services. Went GA on 1 June 2021. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/06/amazon-location-service-is-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2021?
&lt;/h2&gt;

&lt;p&gt;App Runner, MemoryDB, FSx for OpenZFS, Fault Injection Simulator, Resilience Hub, and Elastic Disaster Recovery. Fewer services than the 2017–2019 peak, but this year had a lot of smaller feature launches that are not listed here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Fault Injection Simulator&lt;/strong&gt; - 16 March 2021 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/03/aws-announces-service-aws-fault-injection-simulator/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Chaos engineering - run controlled experiments to test resilience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Nimble Studio&lt;/strong&gt; - 28 April 2021 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/04/aws-announces-general-availability-amazon-nimble-studio/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Cloud-based content creation for visual effects and animation studios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon FinSpace&lt;/strong&gt; - 3 May 2021 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/05/introducing-amazon-finspace-a-fully-managed-service-to-store-prepare-and-analyze-data-for-the-financial-services-industry-fsi/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Data management and analytics for the financial services industry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS App Runner&lt;/strong&gt; - 18 May 2021 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/05/aws-announces-aws-app-runner/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Deploy containerised web apps and APIs from source code or a container image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Application Migration Service&lt;/strong&gt; - 18 May 2021 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/05/announcing-general-availability-of-aws-application-migration-service/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Lift-and-shift migration to AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon MemoryDB for Redis&lt;/strong&gt; - 19 August 2021 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/08/amazon-memorydb-redis/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Redis-compatible, durable, in-memory database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon FSx for NetApp ONTAP&lt;/strong&gt; - 2 September 2021 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/09/amazon-fsx-netapp-ontap/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed NetApp ONTAP file system on AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon MSK Connect&lt;/strong&gt; - 16 September 2021 · &lt;a href="https://aws.amazon.com/blogs/aws/introducing-amazon-msk-connect-stream-data-to-and-from-your-apache-kafka-clusters-using-managed-connectors/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed Kafka Connect connectors for streaming data in and out of Apache Kafka.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Cloud Control API&lt;/strong&gt; - 30 September 2021 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/09/aws-announces-cloud-control-api/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
A uniform API for creating, reading, updating, and deleting AWS resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Resilience Hub&lt;/strong&gt; - 10 November 2021 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/aws-resilience-hub-general-availability/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Define, validate, and track the resilience of your applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Elastic Disaster Recovery&lt;/strong&gt; - 17 November 2021 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/aws-elastic-disaster-recovery-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Scalable, cost-effective disaster recovery to AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon FSx for OpenZFS&lt;/strong&gt; - 30 November 2021 · &lt;a href="https://aws.amazon.com/blogs/aws/new-amazon-fsx-for-openzfs/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed OpenZFS file system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT TwinMaker&lt;/strong&gt; - 30 November 2021 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/aws-iot-twinmaker-build-digital-twins/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Build digital twins of real-world systems. Went GA on 21 April 2022. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/04/aws-iot-twinmaker-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Mainframe Modernization&lt;/strong&gt; - 30 November 2021 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/introducing-aws-mainframe-modernization/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Migrate and modernise mainframe applications. Went GA on 8 June 2022. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/06/aws-mainframe-modernization-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Private 5G&lt;/strong&gt; - 30 November 2021 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/preview-aws-private-5g/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Deploy and manage your own private 5G network. Went GA on 11 August 2022. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/08/aws-private-5g-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT FleetWise&lt;/strong&gt; - 30 November 2021 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/aws-iot-fleetwise-transferring-vehicle-data-cloud/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Collect and transfer vehicle data to the cloud. Went GA on 27 September 2022. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/09/aws-iot-fleetwise-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon VPC IPAM&lt;/strong&gt; - 1 December 2021 · &lt;a href="https://aws.amazon.com/blogs/aws/network-address-management-and-auditing-at-scale-with-amazon-vpc-ip-address-manager/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
IP address management at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Cloud WAN&lt;/strong&gt; - 2 December 2021 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/12/introducing-aws-cloud-wan/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Build and manage a global wide area network. Went GA on 12 July 2022. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/07/general-availability-aws-cloud-wan/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2022?
&lt;/h2&gt;

&lt;p&gt;CodeWhisperer arrived in preview mid-year just before I started my job with AWS. Trainium went GA in October. The end of the year brought Verified Permissions, Supply Chain, Security Lake, and Application Composer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Billing Conductor&lt;/strong&gt; - 17 March 2022 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/03/aws-announces-aws-billing-conductor/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Customise your billing data for showback and chargeback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon GameSparks&lt;/strong&gt; - 23 March 2022 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/03/amazon-gamesparks-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Game backend service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon EMR Serverless&lt;/strong&gt; - 1 June 2022 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/06/amazon-emr-serverless-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Run Spark and Hive without managing clusters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon CodeWhisperer&lt;/strong&gt; - 23 June 2022 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/06/aws-announces-amazon-codewhisperer-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
AI-powered code suggestions. Went GA on 13 April 2023. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/04/amazon-codewhisperer-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Redshift Serverless&lt;/strong&gt; - 12 July 2022 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/07/amazon-redshift-serverless-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Run analytics without managing data warehouse infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Wickr&lt;/strong&gt; - 26 July 2022 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/07/aws-announces-aws-wickr-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
End-to-end encrypted messaging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Trainium&lt;/strong&gt; - 10 October 2022 · &lt;a href="https://aws.amazon.com/blogs/aws/amazon-ec2-trn1-instances-for-high-performance-model-training-are-now-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Custom ML training chip. EC2 Trn1 instances went GA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Verified Permissions&lt;/strong&gt; - 28 November 2022 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/11/amazon-verified-permissions-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Fine-grained authorisation using Cedar policy language. Went GA on 13 June 2023. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/06/amazon-verified-permissions-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS SimSpace Weaver&lt;/strong&gt; - 29 November 2022 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/11/aws-simspace-weaver-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Run large-scale spatial simulations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Omics&lt;/strong&gt; - 29 November 2022 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/11/amazon-omics-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Store, query, and analyse genomic and biological data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Supply Chain&lt;/strong&gt; - 29 November 2022 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/11/aws-supply-chain-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
ML-powered supply chain management. Went GA on 4 April 2023. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/04/aws-supply-chain-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Verified Access&lt;/strong&gt; - 29 November 2022 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/11/aws-verified-access-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Zero-trust access to corporate applications without a VPN. Went GA on 28 April 2023. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/04/aws-verified-access-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Security Lake&lt;/strong&gt; - 29 November 2022 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/11/amazon-security-lake-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Centralise security data in OCSF format. Went GA on 30 May 2023. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/05/amazon-security-lake-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Application Composer&lt;/strong&gt; - 1 December 2022 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/12/aws-application-composer-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Visually design serverless applications. Went GA on 7 March 2023. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/03/aws-application-composer-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon CodeCatalyst&lt;/strong&gt; - 1 December 2022 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/12/announcing-amazon-codecatalyst-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Unified software development service. Went GA on 20 April 2023. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/04/general-availability-amazon-codecatalyst/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2023?
&lt;/h2&gt;

&lt;p&gt;Bedrock went GA in September. Amazon Q previewed in November. Clean Rooms, VPC Lattice, and Payment Cryptography also shipped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Telco Network Builder&lt;/strong&gt; - 21 February 2023 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/02/aws-telco-network-builder/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Deploy and manage telco networks on AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Clean Rooms&lt;/strong&gt; - 21 March 2023 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/03/aws-clean-rooms-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Analyse combined datasets without sharing raw data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon VPC Lattice&lt;/strong&gt; - 31 March 2023 · &lt;a href="https://aws.amazon.com/blogs/aws/simplify-service-to-service-connectivity-security-and-monitoring-with-amazon-vpc-lattice-now-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Service-to-service connectivity, security, and monitoring across VPCs and accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Bedrock&lt;/strong&gt; - 13 April 2023 (preview) · &lt;a href="https://aws.amazon.com/blogs/machine-learning/announcing-new-tools-for-building-with-generative-ai-on-aws/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Access foundation models from AI21 Labs, Anthropic, Cohere, Meta, Stability AI, and Amazon through a single API. Went GA on 28 September 2023. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/09/amazon-bedrock-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Payment Cryptography&lt;/strong&gt; - 12 June 2023 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/06/aws-payment-cryptography/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed cryptographic operations for payment processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS AppFabric&lt;/strong&gt; - 27 June 2023 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/06/announcing-aws-appfabric/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Connect SaaS applications for security and management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS HealthImaging&lt;/strong&gt; - 25 July 2023 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/07/general-availability-aws-healthimaging/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Store, access, and analyse medical imaging data at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS HealthScribe&lt;/strong&gt; - 26 July 2023 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/07/aws-healthscribe-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Generate clinical documentation from patient-clinician conversations. Went GA on 27 November 2023. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/11/aws-healthscribe-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Entity Resolution&lt;/strong&gt; - 26 July 2023 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/07/aws-entity-resolution-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Match and link related records across data sources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon DataZone&lt;/strong&gt; - 4 October 2023 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/10/amazon-datazone-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Data management service for cataloguing, discovering, and governing data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS B2B Data Interchange&lt;/strong&gt; - 27 November 2023 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/11/aws-b2b-data-interchange/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Automate EDI (electronic data interchange) document transformations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Q&lt;/strong&gt; - 28 November 2023 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/11/aws-amazon-q-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Generative AI assistant for software development and AWS operations. Went GA on 30 April 2024. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/04/amazon-q-developer-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2024?
&lt;/h2&gt;

&lt;p&gt;Eight new services including Deadline Cloud, App Studio, Nova, Aurora DSQL (in preview), and a lot of ‘feature launches’ that are not full services so they’re not on this list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Deadline Cloud&lt;/strong&gt; - 2 April 2024 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/04/announcing-aws-deadline-cloud/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Managed render farm for visual effects and animation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS App Studio&lt;/strong&gt; - 10 July 2024 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/07/aws-app-studio-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Build enterprise applications using natural language. Went GA on 18 November 2024. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/11/aws-app-studio-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS End User Messaging&lt;/strong&gt; - 29 July 2024 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/07/aws-end-user-messaging/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Send and manage messages across SMS, MMS, and push channels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Data Transfer Terminal&lt;/strong&gt; - 1 December 2024 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/12/aws-data-transfer-terminal-high-speed-data-uploads/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Physical locations for high-speed data uploads to AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Security Incident Response&lt;/strong&gt; - 1 December 2024 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/12/aws-security-incident-response/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Triage and respond to security incidents in your AWS account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Nova&lt;/strong&gt; - 3 December 2024 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/12/amazon-nova-foundation-models-bedrock/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Amazon's own family of foundation models, available through Bedrock. Includes text, multimodal, and image generation models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Aurora DSQL&lt;/strong&gt; - 3 December 2024 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/12/amazon-aurora-dsql-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Serverless, distributed SQL database with active-active high availability. PostgreSQL-compatible. Went GA on 28 May 2025. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/05/amazon-aurora-dsql-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon S3 Tables&lt;/strong&gt; - 3 December 2024 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/12/amazon-s3-metadata-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Cloud object store with built-in Apache Iceberg support, optimised for analytics workloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  What did AWS launch in 2025?
&lt;/h2&gt;

&lt;p&gt;AWS Transform went GA in May for mainframe, VMware, and .NET workloads. Bedrock AgentCore previewed in July and went GA in October. December brought Nova Act, S3 Vectors, and the next generation of Nova models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Transform&lt;/strong&gt; - 28 May 2025 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/05/aws-transform-mainframe-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Automates modernisation of mainframe, VMware, and .NET workloads using AI. Launched with support for mainframe, VMware, and .NET workloads, with a custom transformation capability added at re:Invent 2025. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/05/aws-transform-vmware-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;VMware GA&lt;/a&gt;) (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/05/aws-transform-net-generally-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;.NET GA&lt;/a&gt;) (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/12/transform-custom-organization-wide-modernization/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Custom GA&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt; - 21 July 2025 (preview) · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/07/amazon-bedrock-agentcore-preview/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Infrastructure for deploying and running AI agents. Includes runtime, memory, gateway, identity, and observability components. Went GA on 14 October 2025. (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/10/amazon-bedrock-agentcore-available/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;GA announcement&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Nova Act&lt;/strong&gt; - 2 December 2025 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/12/build-automate-production-ui-workflows-nova-act/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Build AI agents that automate browser-based UI workflows - form filling, search and extract, QA testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon S3 Vectors&lt;/strong&gt; - 2 December 2025 · &lt;a href="https://aws.amazon.com/blogs/aws/amazon-s3-vectors-now-generally-available-with-increased-scale-and-performance?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Vector storage and querying built into S3.&lt;/p&gt;




&lt;h2&gt;
  
  
  What has AWS launched in 2026 so far?
&lt;/h2&gt;

&lt;p&gt;We're only a few months in, but new services are already arriving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Elemental Inference&lt;/strong&gt; - 24 February 2026 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/02/aws-elemental-inference-generally-avail/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
AI-powered live video processing. Automatically generates vertical content and highlight clips for mobile and social platforms in real time, alongside encoding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Connect Health&lt;/strong&gt; - 5 March 2026 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/03/amazon-connect-health-agentic-ai-healthcare/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Agentic AI for healthcare, built on Amazon Connect. Five AI agents handle patient verification, appointment management, patient insights, ambient documentation, and medical coding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Route 53 Global Resolver&lt;/strong&gt; - 9 March 2026 · &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/03/amazon-route-53-global-resolver/?trk=0fc6058e-ef5a-4fc9-bc07-6efe2c3c9de4&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;&lt;br&gt;
Anycast DNS resolver across 30 AWS Regions. Resolves public and private domains from anywhere, with DNS query filtering and centralised logging. Previewed at re:Invent 2025.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s missing from this list?
&lt;/h2&gt;

&lt;p&gt;I am definitely sure I have missed something on this list, but I hope you’ll let me know in the comments! There were so many to get through, but then there’s also the thing where AWS has a habit of launching features that blur the line between “new service” and “major feature of an existing service.” So, I decided to try and include things that would have their own console entry, or their own pricing page, or their own “What’s New” launch announcement. There’s also some services I deliberately left out because they’re features rather than standalone services (So things like S3 Intelligent-Tiering, Lambda Layers, EC2 Auto Scaling). And there’s probably others I left out because it was a skill issue on my part. Live laugh love launches!&lt;/p&gt;

&lt;p&gt;If you spot a gap, please let me know - I am extremely online and you can find me on &lt;a href="https://www.linkedin.com/in/brookejamieson/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://x.com/brooke_jamieson" rel="noopener noreferrer"&gt;X&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to do your own research like this, the AWS MCP Server is a great place to start! You can use it in &lt;a href="https://kiro.dev/" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;, Claude Code, Cursor, or whatever AI Agent is your current favourite. It’s the reason this post exists - I’d been meaning to compile this list for years and never had the time until I had a tool that could help me find 250 announcements without opening 250 browser tabs.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Author:
&lt;/h2&gt;

&lt;p&gt;Brooke Jamieson is a Senior Developer Advocate at AWS, focused on AI agents and developer tools. A former fashion model turned mathematician turned AI Engineer, Brooke moved from Australia to New York City for this role. They make byte-sized tech content about AI and AWS, and you can find them on &lt;a href="https://www.linkedin.com/in/brookejamieson/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://www.instagram.com/brooke.bytes/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; or &lt;a href="https://x.com/brooke_jamieson" rel="noopener noreferrer"&gt;X&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>programming</category>
      <category>news</category>
      <category>history</category>
    </item>
  </channel>
</rss>
