<?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: Ajit</title>
    <description>The latest articles on DEV Community by Ajit (@ajitnk2006).</description>
    <link>https://dev.to/ajitnk2006</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1214886%2F4def6a73-9fa4-4fa4-a91d-b4465bb62077.png</url>
      <title>DEV Community: Ajit</title>
      <link>https://dev.to/ajitnk2006</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ajitnk2006"/>
    <language>en</language>
    <item>
      <title>Lambda MicroVM Architecture: A Deep Dive into Strengths, Limitations, and Real-World Patterns</title>
      <dc:creator>Ajit</dc:creator>
      <pubDate>Mon, 07 Sep 2026 12:45:53 +0000</pubDate>
      <link>https://dev.to/ajitnk2006/lambda-microvm-architecture-a-deep-dive-into-strengths-limitations-and-real-world-patterns-75l</link>
      <guid>https://dev.to/ajitnk2006/lambda-microvm-architecture-a-deep-dive-into-strengths-limitations-and-real-world-patterns-75l</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When AWS Lambda processes your function invocation, something remarkable happens beneath the surface. Your code doesn't run on a bare metal server or inside a traditional virtual machine — it executes within a MicroVM, a purpose-built virtualization technology called &lt;strong&gt;Firecracker&lt;/strong&gt; that AWS open-sourced in 2018. Understanding how Lambda's MicroVM architecture works isn't just an academic exercise. It directly influences how you design functions, manage cold starts, optimize performance, and make architectural trade-offs in serverless applications.&lt;/p&gt;

&lt;p&gt;This post explores Lambda's MicroVM model through a practical lens: what makes it powerful, where it falls short, and how real-world projects on GitHub are working around its constraints. Whether you're building event-driven pipelines, containerized workloads, or latency-sensitive APIs, knowing what's happening at the virtualization layer helps you write better serverless code.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Lambda MicroVM, and Why Does It Exist?
&lt;/h2&gt;

&lt;p&gt;Traditional hypervisors like KVM or Xen were designed for long-running, general-purpose workloads. They carry significant overhead — both in memory footprint and boot time — that makes them poorly suited for functions that may execute for milliseconds and then sit idle for hours.&lt;/p&gt;

&lt;p&gt;Firecracker, the engine behind Lambda's MicroVM model, was engineered specifically to solve this problem. It's a Virtual Machine Monitor (VMM) written in Rust that uses Linux's KVM interface but strips away everything unnecessary: no BIOS emulation, no legacy device support, no GUI subsystems. The result is a hypervisor that can boot a minimal Linux kernel and launch a process in &lt;strong&gt;under 125 milliseconds&lt;/strong&gt;, with a memory overhead as low as &lt;strong&gt;5 MB per MicroVM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each Lambda execution environment runs inside its own dedicated MicroVM, providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hardware-level isolation&lt;/strong&gt; between tenants on the same physical host&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A dedicated kernel&lt;/strong&gt; per execution environment (not just a container namespace)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A minimal attack surface&lt;/strong&gt; — Firecracker exposes fewer than 20 emulated devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture is what allows AWS to safely run millions of customer functions on shared infrastructure without compromising security boundaries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Strengths: Where Lambda MicroVMs Excel
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Security Isolation Without Compromise
&lt;/h3&gt;

&lt;p&gt;The most significant advantage of the MicroVM model is the security boundary it creates. Unlike container-based isolation (which relies on Linux namespaces and cgroups), each Lambda execution environment has its own dedicated kernel. A kernel exploit in one tenant's environment cannot propagate to another because the attack surface is bounded by the virtualization layer.&lt;/p&gt;

&lt;p&gt;This matters in multi-tenant environments where functions from different AWS accounts may coexist on the same physical hardware. The Firecracker threat model explicitly addresses this: even if an attacker achieves arbitrary code execution inside a MicroVM, they cannot escape to the host or to neighboring VMs.&lt;/p&gt;

&lt;p&gt;For regulated workloads — financial services, healthcare, government — this isolation model provides a compliance-friendly foundation that pure container runtimes struggle to match.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Fast Boot Times Enable True Serverless Economics
&lt;/h3&gt;

&lt;p&gt;Firecracker's sub-125ms boot time is what makes Lambda's pricing model viable. AWS can spin up a new execution environment on demand, route a single invocation through it, and reclaim those resources — all without the economics breaking down.&lt;/p&gt;

&lt;p&gt;From a developer perspective, this translates to cold start times that, while noticeable, are measured in hundreds of milliseconds rather than seconds. A Python 3.12 Lambda function with no dependencies can cold-start in roughly &lt;strong&gt;200–400ms&lt;/strong&gt; end-to-end, with the MicroVM initialization representing only a fraction of that total.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Consistent, Predictable Resource Allocation
&lt;/h3&gt;

&lt;p&gt;Each MicroVM receives a fixed allocation of vCPU and memory based on the Lambda configuration you specify. There's no noisy-neighbor CPU contention at the virtualization layer — Firecracker's jailer process enforces strict resource limits using cgroups before the MicroVM even starts.&lt;/p&gt;

&lt;p&gt;This predictability is valuable when you're running CPU-intensive workloads like image processing, ML inference, or data transformation. The performance characteristics you observe in testing will closely match production behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Snapshots and the SnapStart Optimization
&lt;/h3&gt;

&lt;p&gt;AWS Lambda SnapStart (available for Java runtimes) leverages Firecracker's snapshot capability to dramatically reduce cold start latency. When you publish a SnapStart-enabled function version, Lambda initializes the execution environment, runs your initialization code, and then takes a snapshot of the MicroVM's memory state.&lt;/p&gt;

&lt;p&gt;On subsequent cold starts, Lambda restores from this snapshot rather than booting a fresh MicroVM and re-running initialization. The result is cold start improvements of up to &lt;strong&gt;90%&lt;/strong&gt; for Java workloads that previously suffered 5–10 second initialization times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lambda SnapStart - annotate your handler to signal initialization work&lt;/span&gt;
&lt;span class="nd"&gt;@Slf4j&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderProcessor&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;RequestHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// This static initialization block runs ONCE during snapshot creation&lt;/span&gt;
    &lt;span class="c1"&gt;// not on every cold start when SnapStart is enabled&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;DynamoDbClient&lt;/span&gt; &lt;span class="n"&gt;dynamoClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initializing heavyweight resources during snapshot phase"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;dynamoClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DynamoDbClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;US_EAST_1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;mapper&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;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handler logic benefits from pre-initialized resources&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dynamoClient&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Limitations: The Real Constraints You Need to Plan Around
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Cold Starts Remain a Structural Reality
&lt;/h3&gt;

&lt;p&gt;Despite Firecracker's fast boot times, cold starts are an inherent characteristic of the MicroVM model. Every time Lambda needs to create a new execution environment — whether due to scaling, a deployment, or a long idle period — a MicroVM must be initialized, the runtime must start, and your initialization code must run.&lt;/p&gt;

&lt;p&gt;For latency-sensitive applications, this creates architectural pressure. Common mitigation strategies include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Provisioned Concurrency&lt;/strong&gt;: Pre-warms a specified number of execution environments, keeping MicroVMs initialized and ready to serve requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduled warming&lt;/strong&gt;: Using EventBridge rules to invoke functions on a schedule, though this is less reliable than Provisioned Concurrency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture redesign&lt;/strong&gt;: Moving latency-sensitive paths to always-warm services like ECS or App Runner
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# serverless.yml - Configure Provisioned Concurrency to eliminate cold starts&lt;/span&gt;
&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;orderApi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/handler.main&lt;/span&gt;
    &lt;span class="na"&gt;memorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;provisionedConcurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;  &lt;span class="c1"&gt;# Keep 10 MicroVMs pre-initialized&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;httpApi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/orders&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Execution Duration and Stateless Constraints
&lt;/h3&gt;

&lt;p&gt;Lambda functions have a hard maximum execution timeout of &lt;strong&gt;15 minutes&lt;/strong&gt;. This isn't a Firecracker limitation per se, but it reflects the design philosophy of the MicroVM model: short-lived, stateless compute. Any workload that requires persistent state, long-running processes, or execution beyond 15 minutes needs a different compute model.&lt;/p&gt;

&lt;p&gt;Additionally, the MicroVM's filesystem is ephemeral. The &lt;code&gt;/tmp&lt;/code&gt; directory provides up to &lt;strong&gt;10 GB of ephemeral storage&lt;/strong&gt;, but this disappears when the execution environment is recycled. Workloads that need durable local storage must externalize state to S3, EFS, or DynamoDB.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Limited System-Level Access
&lt;/h3&gt;

&lt;p&gt;Because Lambda runs your code inside a locked-down MicroVM, you don't have access to many operating system primitives that you might take for granted on EC2:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No raw socket access&lt;/strong&gt; (limits certain networking use cases)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No ability to load custom kernel modules&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Restricted &lt;code&gt;/proc&lt;/code&gt; and &lt;code&gt;/sys&lt;/code&gt; filesystem access&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No systemd or init system&lt;/strong&gt; — your function is the only process (beyond the runtime)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates friction for workloads that depend on system-level tools, custom kernel features, or specific OS configurations. Container image deployments (up to 10 GB) provide more flexibility, but the fundamental MicroVM constraints still apply.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Networking Latency in VPC Configurations
&lt;/h3&gt;

&lt;p&gt;When Lambda functions run inside a VPC, each execution environment requires an Elastic Network Interface (ENI). Historically, ENI attachment was a major source of cold start latency — adding 10+ seconds in some cases. AWS largely resolved this with &lt;strong&gt;Hyperplane ENIs&lt;/strong&gt; (VPC-to-VPC NAT), which pre-allocate network interfaces and dramatically reduce VPC cold start overhead.&lt;/p&gt;

&lt;p&gt;However, VPC-attached Lambda functions still experience higher cold starts than non-VPC functions, and the complexity of VPC configuration (subnets, security groups, NAT gateways) adds operational overhead that teams should factor into their architecture decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Memory as the Single Scaling Dimension
&lt;/h3&gt;

&lt;p&gt;Lambda allocates CPU proportionally to memory. You cannot independently configure vCPU allocation — if you need more CPU, you increase memory, which also increases cost. For CPU-bound workloads, this creates a cost inefficiency: you may need to allocate 3008 MB of memory not because your function needs that RAM, but because it needs the proportional CPU allocation.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/alexcasalboni/aws-lambda-power-tuning" rel="noopener noreferrer"&gt;AWS Lambda Power Tuning&lt;/a&gt; tool (discussed below) helps navigate this trade-off empirically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Notable GitHub Projects That Work With Lambda MicroVM Constraints
&lt;/h2&gt;

&lt;p&gt;The open-source community has built a rich ecosystem of tools that either expose Firecracker's capabilities or help developers work around Lambda's MicroVM limitations. Here are four projects worth knowing:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Firecracker (aws/firecracker)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/firecracker-microvm/firecracker" rel="noopener noreferrer"&gt;github.com/alexcasalboni/aws-lambda-power-tuning&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Firecracker VMM itself is open source and actively maintained. Beyond Lambda, it powers AWS Fargate and can be run independently for custom MicroVM workloads. If you're building a platform that needs Lambda-like isolation without Lambda's constraints, Firecracker is the foundation.&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;# Launch a Firecracker MicroVM directly (for platform engineering use cases)&lt;/span&gt;
&lt;span class="c"&gt;# Download the Firecracker binary&lt;/span&gt;
curl &lt;span class="nt"&gt;-Lo&lt;/span&gt; firecracker https://github.com/firecracker-microvm/firecracker/releases/download/v1.6.0/firecracker-v1.6.0-x86_64

&lt;span class="nb"&gt;chmod&lt;/span&gt; +x firecracker

&lt;span class="c"&gt;# Start Firecracker with an API socket&lt;/span&gt;
./firecracker &lt;span class="nt"&gt;--api-sock&lt;/span&gt; /tmp/firecracker.socket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. AWS Lambda Power Tuning (alexcasalboni/aws-lambda-power-tuning)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/alexcasalboni/aws-lambda-power-tuning" rel="noopener noreferrer"&gt;github.com/alexcasalboni/aws-lambda-power-tuning&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This Step Functions-based tool runs your Lambda function across multiple memory configurations and visualizes the cost/performance trade-off. Given that MicroVM resource allocation scales with memory, this tool is essential for finding the optimal configuration.&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;"lambdaARN"&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:lambda:us-east-1:123456789:function:my-function"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"powerValues"&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="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3008&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"num"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parallelInvocation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"strategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cost"&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;
  
  
  3. AWS Lambda Web Adapter (awslabs/aws-lambda-web-adapter)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/awslabs/aws-lambda-web-adapter" rel="noopener noreferrer"&gt;github.com/awslabs/aws-lambda-web-adapter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This project lets you run conventional web frameworks (Express, FastAPI, Spring Boot) inside Lambda without modifying your application code. It works by running your HTTP server as a process inside the MicroVM and proxying Lambda invocations to it — effectively treating the MicroVM as a lightweight container runtime.&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;# Dockerfile - Run a FastAPI app inside Lambda MicroVM using Web Adapter&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; public.ecr.aws/lambda/python:3.12&lt;/span&gt;

&lt;span class="c"&gt;# Copy the Lambda Web Adapter binary&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.1 \&lt;/span&gt;
    /lambda-adapter /opt/extensions/lambda-adapter

&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; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

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

&lt;span class="c"&gt;# Web Adapter will start this process and proxy requests to port 8000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Serverless Spy (ServerlessLife/serverless-spy)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/ServerlessLife/serverless-spy" rel="noopener noreferrer"&gt;github.com/ServerlessLife/serverless-spy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Testing event-driven Lambda architectures is notoriously difficult because the MicroVM execution model makes traditional debugging approaches (attaching a debugger, inspecting process state) impractical. Serverless Spy intercepts Lambda invocations and publishes execution data to WebSockets, enabling real-time test assertions against live Lambda functions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architectural Best Practices for MicroVM-Aware Lambda Design
&lt;/h2&gt;

&lt;p&gt;Understanding the MicroVM model should directly inform how you structure Lambda functions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initialize outside the handler&lt;/strong&gt;: Code in the global scope runs during MicroVM initialization and is reused across warm invocations. Database connections, SDK clients, and configuration loading belong here.&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;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="c1"&gt;# Initialized ONCE during MicroVM setup — reused across warm invocations
&lt;/span&gt;&lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Orders&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;handler&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;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# This code runs on every invocation
&lt;/span&gt;    &lt;span class="c1"&gt;# but benefits from the pre-initialized `table` client
&lt;/span&gt;    &lt;span class="n"&gt;order_id&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pathParameters&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;orderId&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="n"&gt;table&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;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&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;response&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="p"&gt;{}))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Right-size memory for your workload type&lt;/strong&gt;: Use Lambda Power Tuning to find the memory configuration where cost and performance intersect optimally. CPU-bound functions often benefit from higher memory; I/O-bound functions often don't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Externalize all state&lt;/strong&gt;: Design functions assuming the MicroVM will be recycled after every invocation. Use S3 for files, DynamoDB or ElastiCache for application state, and SQS/EventBridge for inter-function communication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use container images for complex dependencies&lt;/strong&gt;: If your function requires system libraries, custom binaries, or a large dependency tree, package it as a container image (up to 10 GB). The MicroVM still provides the same isolation guarantees, but you gain full control over the filesystem layout.&lt;/p&gt;




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

&lt;p&gt;Lambda's MicroVM architecture represents a genuine engineering achievement: hardware-level tenant isolation with boot times measured in milliseconds. Firecracker makes the economics of serverless compute work, and understanding its design helps you make better decisions about when Lambda is the right tool and how to use it effectively.&lt;/p&gt;

&lt;p&gt;The strengths — fast initialization, strong isolation, predictable resource allocation, and SnapStart optimization — make Lambda MicroVMs compelling for event-driven workloads, API backends, and data processing pipelines. The limitations — cold starts, 15-minute execution caps, restricted system access, and the memory-CPU coupling — define the boundaries where you should consider ECS, Fargate, or EC2 instead.&lt;/p&gt;

&lt;p&gt;The open-source ecosystem around Firecracker and Lambda tooling continues to mature rapidly. Projects like Lambda Web Adapter are blurring the line between "serverless functions" and "containerized services," while Power Tuning gives teams empirical data for cost optimization decisions.&lt;/p&gt;

&lt;p&gt;The most effective serverless architects aren't the ones who avoid Lambda's constraints — they're the ones who understand them deeply enough to design around them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;For further reading, explore the &lt;a href="https://github.com/firecracker-microvm/firecracker/blob/main/docs/design.md" rel="noopener noreferrer"&gt;Firecracker design documentation&lt;/a&gt; and the &lt;a href="https://docs.aws.amazon.com/lambda/latest/operatorguide/intro.html" rel="noopener noreferrer"&gt;AWS Lambda operator guide&lt;/a&gt; for production best practices.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>technology</category>
      <category>cloud</category>
      <category>aws</category>
    </item>
    <item>
      <title>How I Used Kiro Web + Graviton Migration Power to Audit My Production SaaS in 21 Seconds</title>
      <dc:creator>Ajit</dc:creator>
      <pubDate>Tue, 12 May 2026 06:06:58 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-i-used-kiro-web-graviton-migration-power-to-audit-my-production-saas-in-21-seconds-49ji</link>
      <guid>https://dev.to/aws-builders/how-i-used-kiro-web-graviton-migration-power-to-audit-my-production-saas-in-21-seconds-49ji</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I run a production SaaS on AWS Lambda (Python 3.12, 7 source files, boto3 + MCP dependencies). All functions on x86_64. AWS Graviton2 offers 20% cheaper duration charges — but switching without &lt;br&gt;
verifying ARM64 compatibility is risky.&lt;/p&gt;

&lt;p&gt;The manual audit process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check every pip dependency for native C extensions&lt;/li&gt;
&lt;li&gt;Verify no inline assembly or architecture-specific intrinsics&lt;/li&gt;
&lt;li&gt;Confirm container base images support linux/arm64&lt;/li&gt;
&lt;li&gt;Cross-reference AWS docs for runtime support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a small codebase, this takes a few hours. For larger projects with numpy, pandas, or compiled extensions — days.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup: Kiro Web + Graviton Migration Power
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://kiro.dev/web/" rel="noopener noreferrer"&gt;Kiro Web&lt;/a&gt; is AWS's agentic IDE in public preview. It runs an autonomous agent in a remote sandbox with Docker support, MCP servers, and installable "Powers" (domain-&lt;br&gt;
specific tool packages).&lt;/p&gt;

&lt;p&gt;Step 1: Install the Graviton Migration Power&lt;/p&gt;

&lt;p&gt;From the &lt;a href="https://github.com/aws/agent-toolkit-for-aws" rel="noopener noreferrer"&gt;AWS Agent Toolkit&lt;/a&gt;, I installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Graviton Migration Power (includes arm-mcp Docker container)&lt;/li&gt;
&lt;li&gt;AWS MCP server (for account access)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Power adds these tools to the agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;migrate_ease_scan — scans code for ARM64 compatibility issues&lt;/li&gt;
&lt;li&gt;check_image (skopeo) — verifies container images support linux/arm64&lt;/li&gt;
&lt;li&gt;knowledge_base_search — queries Arm documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 2: Connect your repo&lt;/p&gt;

&lt;p&gt;Fork/connect your GitHub repo to Kiro Web. The agent checks out your code in the sandbox.&lt;/p&gt;

&lt;p&gt;Step 3: Run the analysis&lt;/p&gt;

&lt;p&gt;Prompt:&lt;br&gt;
Run the full Graviton Migration Power analysis on this codebase.&lt;br&gt;
If required, install Docker in the sandbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Agent Did (Autonomously)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Verified Docker was running in the sandbox&lt;/li&gt;
&lt;li&gt;Found the arm-mcp container active&lt;/li&gt;
&lt;li&gt;Sent JSON-RPC requests to the MCP server:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;json&lt;br&gt;
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}&lt;br&gt;
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"migrate_ease_scan","arguments":{"path":"/workspace","language":"python","target":"armv8-a"}}}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Scanned all Python files for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PythonPackageIssue (native extensions)&lt;/li&gt;
&lt;li&gt;PythonLinkLibraryIssue (linked libraries)&lt;/li&gt;
&lt;li&gt;PythonInlineAsmIssue (inline assembly)&lt;/li&gt;
&lt;li&gt;PythonIntrinsicIssue (architecture intrinsics)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checked python:3.12-slim base image:&lt;br&gt;
Architectures: amd64, arm64, arm, 386, ppc64le, riscv64, s390x&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Queried knowledge base for Lambda Graviton pricing data&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;Scanner:              Python (armv8-a target)&lt;br&gt;
Files scanned:        7&lt;br&gt;
Total issues found:   0&lt;br&gt;
PythonPackageIssue:   0&lt;br&gt;
PythonLinkLibraryIssue: 0&lt;br&gt;
PythonInlineAsmIssue: 0&lt;br&gt;
PythonIntrinsicIssue: 0&lt;/p&gt;

&lt;p&gt;All dependencies verified:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dependency&lt;/th&gt;
&lt;th&gt;ARM64 Compatible&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;boto3&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Pure Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mcp &amp;gt;= 1.19.0&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Pure Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;python:3.12-slim&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Multi-arch image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Migration: 1 Line
&lt;/h2&gt;

&lt;p&gt;yaml&lt;/p&gt;

&lt;h1&gt;
  
  
  CloudFormation
&lt;/h1&gt;

&lt;p&gt;ApiFunction:&lt;br&gt;
  Type: AWS::Lambda::Function&lt;br&gt;
  Properties:&lt;br&gt;
    Architectures:&lt;br&gt;
      - arm64    # This is the entire migration&lt;br&gt;
    Runtime: python3.12&lt;br&gt;
    Handler: handler.handler&lt;/p&gt;

&lt;p&gt;Or in CDK:&lt;br&gt;
typescript&lt;br&gt;
new lambda.Function(this, 'Api', {&lt;br&gt;
  architecture: lambda.Architecture.ARM_64,  // Add this&lt;br&gt;
  runtime: lambda.Runtime.PYTHON_3_12,&lt;br&gt;
  handler: 'handler.handler',&lt;br&gt;
});&lt;/p&gt;

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

&lt;p&gt;From AWS documentation:&lt;br&gt;
│ Duration charges are 20% lower than x86 pricing with millisecond granularity. Up to 34% better price-performance.&lt;/p&gt;

&lt;p&gt;For functions with 900-second timeouts running complex reviews, this is meaningful savings with zero code changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  When This Gets Interesting
&lt;/h2&gt;

&lt;p&gt;My codebase was pure Python — easy case. The Graviton Power becomes truly valuable when you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;numpy/pandas&lt;/strong&gt; — need to verify wheels exist for linux_aarch64&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compiled extensions&lt;/strong&gt; (cryptography, lxml) — may need different build&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Docker images&lt;/strong&gt; — need multi-arch builds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline assembly&lt;/strong&gt; — rare in Python, common in C extensions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scanner catches all of these. That's the point — you don't need to know what to look for.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Install Graviton Power&lt;/td&gt;
&lt;td&gt;2 min (one-time)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run analysis&lt;/td&gt;
&lt;td&gt;21 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apply migration&lt;/td&gt;
&lt;td&gt;1 line change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost savings&lt;/td&gt;
&lt;td&gt;20% on Lambda duration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kiro.dev/web/" rel="noopener noreferrer"&gt;Kiro Web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aws/agent-toolkit-for-aws" rel="noopener noreferrer"&gt;AWS Agent Toolkit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html" rel="noopener noreferrer"&gt;AWS Lambda Graviton2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>kiro</category>
      <category>agents</category>
    </item>
    <item>
      <title>I Used an AI "Skill" to Audit My CloudFormation Stack — Found a Broken Alarm Running for 3 Months</title>
      <dc:creator>Ajit</dc:creator>
      <pubDate>Wed, 06 May 2026 05:30:43 +0000</pubDate>
      <link>https://dev.to/ajitnk2006/i-used-an-ai-skill-to-audit-my-cloudformation-stack-found-a-broken-alarm-running-for-3-months-1ocn</link>
      <guid>https://dev.to/ajitnk2006/i-used-an-ai-skill-to-audit-my-cloudformation-stack-found-a-broken-alarm-running-for-3-months-1ocn</guid>
      <description>&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;Kiro CLI has a feature called "skills" — domain-specific expertise packages you can load on demand. I loaded the aws-cloudformation skill to validate my production Spot Fleet template. It found 4 critical issues including a CloudWatch alarm that literally never worked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Situation
&lt;/h3&gt;

&lt;p&gt;I run a development environment on AWS Spot instances — Kiro CLI, VS Code Server, persistent EBS, the works. The CloudFormation stack has been deployed since February 2026. It's been updated once, runs daily, and I considered it production-ready.&lt;/p&gt;

&lt;p&gt;I was about to upgrade stack  and wanted to validate my base template first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task
&lt;/h3&gt;

&lt;p&gt;Verify whether my "security-hardened" template actually follows CloudFormation best practices before using it as the foundation for upgrade.&lt;/p&gt;

&lt;h3&gt;
  
  
  Action
&lt;/h3&gt;

&lt;p&gt;Step 1 — Discover the skill:&lt;br&gt;
search_documentation("CloudFormation deployment", topics=["agent_skills"])&lt;/p&gt;

&lt;p&gt;This returned 5 relevant skills. I picked aws-cloudformation.&lt;/p&gt;

&lt;p&gt;Step 2 — Load the skill:&lt;br&gt;
retrieve_skill("aws-cloudformation")&lt;/p&gt;

&lt;p&gt;What I got back wasn't generic advice. It was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A structured 10-step authoring checklist&lt;/li&gt;
&lt;li&gt;3-layer validation pipeline (syntax → security → pre-deploy)&lt;/li&gt;
&lt;li&gt;Troubleshooting SOPs for failed stacks&lt;/li&gt;
&lt;li&gt;Decision frameworks for template vs environment fixes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 3 — Load the reference SOP:&lt;br&gt;
retrieve_skill("aws-cloudformation", file="references/author-cloudformation-best-practices.script.md")&lt;/p&gt;

&lt;p&gt;This gave me the detailed checklist: resource naming, parameter design, security defaults, deletion policies, cross-stack references, conditions, outputs.&lt;/p&gt;

&lt;p&gt;Step 4 — Apply it to my template:&lt;/p&gt;

&lt;p&gt;I asked Kiro CLI to review CFN yaml against the skill's checklist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;4 critical, 5 recommended, 1 strict finding.&lt;/p&gt;

&lt;p&gt;Here's what hit hardest:&lt;/p&gt;

&lt;h4&gt;
  
  
  🔴 Critical #1: Broken CloudWatch Alarm (3 months undetected)
&lt;/h4&gt;

&lt;p&gt;yaml&lt;br&gt;
HighCPUAlarm:&lt;br&gt;
  Dimensions:&lt;br&gt;
    - Name: InstanceId&lt;br&gt;
      Value: !Ref SpotFleetRequest  # ← This is a Fleet ID, not an Instance ID&lt;/p&gt;

&lt;p&gt;My "cryptomining detection" alarm was monitoring a dimension that doesn't exist. It never evaluated. Never fired. I had zero protection for 3 months while thinking I was&lt;br&gt;
covered.&lt;/p&gt;

&lt;p&gt;Fix: Reference the actual instance ID (requires a Lambda or instance self-registration pattern since Spot Fleet instances are dynamic).&lt;/p&gt;

&lt;h3&gt;
  
  
  What Are "Skills" Exactly?
&lt;/h3&gt;

&lt;p&gt;They're not prompts. They're not RAG documents. They're structured domain expertise packages containing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Workflows&lt;/td&gt;
&lt;td&gt;Step-by-step procedures with decision points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checklists&lt;/td&gt;
&lt;td&gt;Deterministic validation (like the 10-step authoring review)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SOPs&lt;/td&gt;
&lt;td&gt;Standard operating procedures for troubleshooting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decision trees&lt;/td&gt;
&lt;td&gt;"If X, do Y; if Z, do W" frameworks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reference files&lt;/td&gt;
&lt;td&gt;Architecture docs, schemas, examples&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The CloudFormation skill has SOPs for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authoring with secure defaults&lt;/li&gt;
&lt;li&gt;3-layer pre-deployment validation (cfn-lint → cfn-guard → change set)&lt;/li&gt;
&lt;li&gt;Troubleshooting failed stacks (using describe-events --filters FailedEvents=true)&lt;/li&gt;
&lt;li&gt;Resource property lookup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key insight: The skill told me to use describe-events (newer API with filter support) instead of describe-stack-events (legacy, no filters). I didn't know this API existed.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Use Skills in Kiro CLI
&lt;/h3&gt;

&lt;p&gt;bash&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Search for relevant skills
&lt;/h1&gt;

&lt;h1&gt;
  
  
  (you can't guess skill names — they must be discovered)
&lt;/h1&gt;

&lt;p&gt;search_documentation("your task description", topics=["agent_skills"])&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Load the skill
&lt;/h1&gt;

&lt;p&gt;retrieve_skill("exact-skill-name-from-search")&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Load reference files if the skill links to them
&lt;/h1&gt;

&lt;p&gt;retrieve_skill("skill-name", file="references/some-sop.md")&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Apply — ask the AI to use the loaded expertise
&lt;/h1&gt;

&lt;p&gt;"Review my template against the authoring checklist"&lt;br&gt;
"Troubleshoot my failed stack using the SOP"&lt;br&gt;
"Validate before I deploy"&lt;/p&gt;

&lt;p&gt;Available skills I found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aws-cloudformation — authoring, validation, troubleshooting&lt;/li&gt;
&lt;li&gt;launching-ec2-instance-with-best-practices — secure EC2 launches&lt;/li&gt;
&lt;li&gt;creating-production-vpc-multi-az — VPC design&lt;/li&gt;
&lt;li&gt;creating-ec2-image-builder-pipeline — AMI automation&lt;/li&gt;
&lt;li&gt;aws-cdk — CDK patterns and deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;"It works" ≠ "It's correct." My stack ran fine for 3 months with a broken alarm. Functional doesn't mean secure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Validate infrastructure like you lint code. We wouldn't ship code without tests. Why do we ship CloudFormation without a structured review?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AI skills &amp;gt; AI chat. A general "review my template" prompt gives generic advice. A loaded skill applies a deterministic, comprehensive checklist. The difference is like&lt;br&gt;
asking a random person vs. asking a CloudFormation specialist with a clipboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The scariest bugs are silent ones. A broken alarm doesn't throw errors. It just... doesn't protect you.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The skill doesn't just find problems — it prevents them in new templates.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>The Model Is the Brain. The Harness Is the Body. Here's Why That Matters</title>
      <dc:creator>Ajit</dc:creator>
      <pubDate>Mon, 04 May 2026 05:18:31 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-model-is-the-brain-the-harness-is-the-body-heres-why-that-matters-2961</link>
      <guid>https://dev.to/aws-builders/the-model-is-the-brain-the-harness-is-the-body-heres-why-that-matters-2961</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built the same browser agent twice — once with 500 lines of Python, once with 7 lines of JSON. The second one took 5 minutes. The agent harness layer is becoming the real competitive advantage, not the model.&lt;/p&gt;

&lt;p&gt;Last month, I built a browser automation agent. Playwright. Custom orchestration. Login handlers. Error retries. Session management. React-aware form filling. Anti-detection scripts. &lt;strong&gt;500+ lines of Python.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This week, I built the same thing:&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;"model"&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;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bedrock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"modelId"&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.anthropic.claude-sonnet-4-6"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="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;"agentcore_browser"&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;"browser"&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;"systemPrompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a web browsing assistant."&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;Deploy. Invoke. It browses websites, extracts data, fills forms. &lt;strong&gt;Seven lines. Zero orchestration code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But here's the thing most people miss: &lt;strong&gt;I kept both versions.&lt;/strong&gt; And that's the real insight.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed (and What Didn't)
&lt;/h2&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;500-Line Script&lt;/th&gt;
&lt;th&gt;7-Line Harness&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it does&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automates a specific multi-site workflow&lt;/td&gt;
&lt;td&gt;Browses any website, extracts info&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;How it decides&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;I wrote every step&lt;/td&gt;
&lt;td&gt;AI decides the steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost per run&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0 (Playwright, local)&lt;/td&gt;
&lt;td&gt;~$0.10-0.50 (Bedrock tokens)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reliability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;95%+ (deterministic)&lt;/td&gt;
&lt;td&gt;~80% (AI reasoning varies)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only does what I coded&lt;/td&gt;
&lt;td&gt;Handles any browsing task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time to build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3 days of debugging&lt;/td&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The 500-line script is better for its specific job.&lt;/strong&gt; It runs faster, cheaper, and more reliably. Because it doesn't need AI — the steps are known.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 7-line harness is better for everything else.&lt;/strong&gt; Research tasks. Data extraction from unfamiliar sites. Competitive analysis. Anything where the steps aren't known in advance.&lt;/p&gt;

&lt;p&gt;This is my POV: &lt;strong&gt;deterministic + AI is the right architecture.&lt;/strong&gt; Don't use a $0.03/call model to click a button you can click with Playwright for free. But don't write 500 lines of Playwright when 7 lines of config can handle it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Harness Is the New Battleground
&lt;/h2&gt;

&lt;p&gt;Everyone's talking about which model is best. Claude vs GPT vs Gemini. Benchmarks. Context windows. Reasoning scores.&lt;/p&gt;

&lt;p&gt;That conversation is becoming irrelevant.&lt;/p&gt;

&lt;p&gt;Models are commoditizing. Claude Sonnet 4.6 and GPT-5.5 are both "good enough" for most agent tasks. The real question is: &lt;strong&gt;what wraps around the model to make it actually work in production?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's the harness — the orchestration loop, tool execution, memory, security, compute isolation. And every cloud provider is racing to own it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Harness Product&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AgentCore Harness&lt;/td&gt;
&lt;td&gt;Preview (Apr 2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bedrock Managed Agents (OpenAI-specific)&lt;/td&gt;
&lt;td&gt;Limited Preview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Google&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gemini Enterprise Agent Platform&lt;/td&gt;
&lt;td&gt;GA (Apr 2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Microsoft&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Azure AI Agent Service&lt;/td&gt;
&lt;td&gt;GA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Salesforce&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agentforce&lt;/td&gt;
&lt;td&gt;GA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is the container orchestration war all over again. In 2015, everyone had containers. The question was who manages running them. Kubernetes won, and whoever controlled K8s controlled where workloads ran.&lt;/p&gt;

&lt;p&gt;In 2026, everyone has models. The question is who manages running agents. &lt;strong&gt;Whoever controls the harness controls the next decade of cloud spend.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How AgentCore Harness Works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You (prompt) → AgentCore Harness → Bedrock Model (reasoning)
                    ↓                      ↓
              Firecracker microVM    Tool selection
              (isolated per session)       ↓
                    ↓              AgentCore Browser / Shell / Code
              Persistent memory    
              (across sessions)    
                    ↓
              Streamed response → You
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What AWS handles: compute, orchestration loop, tool invocation, memory, auth, observability.&lt;br&gt;
What you handle: a JSON config and a prompt.&lt;/p&gt;

&lt;p&gt;Each session runs in its own &lt;strong&gt;Firecracker microVM&lt;/strong&gt; — the same isolation technology behind Lambda. Not a container. A VM. One session can't see another's data, cookies, or credentials.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Started (I Actually Ran This)
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install CLI&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @aws/agentcore@preview

&lt;span class="c"&gt;# Create project&lt;/span&gt;
agentcore create &lt;span class="nt"&gt;--name&lt;/span&gt; browseragent &lt;span class="nt"&gt;--model-provider&lt;/span&gt; bedrock
&lt;span class="nb"&gt;cd &lt;/span&gt;browseragent

&lt;span class="c"&gt;# Add browser tool&lt;/span&gt;
agentcore add tool &lt;span class="nt"&gt;--harness&lt;/span&gt; browseragent &lt;span class="nt"&gt;--type&lt;/span&gt; agentcore_browser &lt;span class="nt"&gt;--name&lt;/span&gt; browser

&lt;span class="c"&gt;# Set target account + region&lt;/span&gt;
&lt;span class="c"&gt;# Edit agentcore/aws-targets.json: [{"name":"default","region":"us-west-2","account":"YOUR_ACCOUNT"}]&lt;/span&gt;

&lt;span class="c"&gt;# Deploy (~3 min)&lt;/span&gt;
agentcore deploy &lt;span class="nt"&gt;--yes&lt;/span&gt;

&lt;span class="c"&gt;# Use it&lt;/span&gt;
agentcore invoke &lt;span class="nt"&gt;--harness&lt;/span&gt; browseragent &lt;span class="nt"&gt;--stream&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"Go to example.com and describe what you see"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Output from my actual run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔧 Tool: browser
⚡ 6005 in · 110 out · 2.2s
Here's what's on the page at example.com:
### Example Domain
The page contains: "Example Domain" heading, body text about documentation use,
and a "Learn more" link to IANA documentation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real. Not a demo. Not a screenshot from someone else's blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Considerations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;What I Found&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No harness charge. You pay for Bedrock tokens + Browser session time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Regions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;us-west-2, us-east-1, eu-central-1, ap-southeast-2 (preview)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Models&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Any Bedrock model, plus OpenAI and Gemini. Switch mid-session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Firecracker microVM isolation, IAM execution role, Cedar policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Limitation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Preview — not for production workloads yet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Gotcha I hit:&lt;/strong&gt; The harness execution role needs &lt;code&gt;bedrock:Converse&lt;/code&gt; and &lt;code&gt;bedrock:ConverseStream&lt;/code&gt; permissions, plus &lt;code&gt;aws-marketplace:ViewSubscriptions&lt;/code&gt; for 3P models. The default CDK policy only includes &lt;code&gt;bedrock:InvokeModel&lt;/code&gt;. I had to add permissions manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  When NOT to Use Harness
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic automation&lt;/strong&gt; (same steps every time) → Playwright. Cheaper, faster, more reliable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex multi-agent workflows&lt;/strong&gt; → Strands Agents SDK with AgentCore Runtime. More control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existing framework investment&lt;/strong&gt; (LangChain/CrewAI) → Use AgentCore tools standalone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production workloads&lt;/strong&gt; → Wait for GA. It's preview.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;The model is the brain. The harness is the body. Most teams are spending all their time picking the brain and hand-building the body from scratch every time.&lt;/p&gt;

&lt;p&gt;AgentCore Harness lets you stop building bodies and start building solutions. For 80% of agent use cases, config beats code. For the other 20%, write code — but use the harness infrastructure underneath.&lt;/p&gt;

&lt;p&gt;The teams still hand-coding agent orchestration loops are building technical debt. The same way teams hand-coding REST APIs built technical debt before API Gateway existed.&lt;/p&gt;

&lt;p&gt;The question isn't whether to adopt managed agent infrastructure. It's whether you'll be building on it — or competing against someone who already is.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Ajit NK — AWS Community Builder, APN FasTrack Partner. Building AI agent solutions at CloudNestle.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"The model is the brain. The harness is the body. I build the body."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;📚 &lt;strong&gt;Sources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness.html" rel="noopener noreferrer"&gt;AgentCore Harness docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/04/agentcore-new-features-to-build-agents-faster/" rel="noopener noreferrer"&gt;What's New announcement (Apr 22, 2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://strandsagents.com" rel="noopener noreferrer"&gt;Strands Agents SDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>aws</category>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How I Track 44 New AWS Repos Per Week Automatically with EventBridge, Bedrock, and FAISS</title>
      <dc:creator>Ajit</dc:creator>
      <pubDate>Wed, 01 Apr 2026 09:38:28 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-i-track-44-new-aws-repos-per-week-automatically-with-eventbridge-bedrock-and-faiss-ke8</link>
      <guid>https://dev.to/aws-builders/how-i-track-44-new-aws-repos-per-week-automatically-with-eventbridge-bedrock-and-faiss-ke8</guid>
      <description>&lt;p&gt;AWS publishes new sample repos almost daily — reference architectures, agent patterns, CDK constructs, security blueprints — spread across 5 different GitHub organizations.&lt;/p&gt;

&lt;p&gt;Most developers never see them until weeks later.&lt;/p&gt;

&lt;p&gt;I built a system that catches them the same day. Here's how.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;AWS maintains 9,657+ repos across these orgs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;aws-samples&lt;/strong&gt; (8,044 repos)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;awslabs&lt;/strong&gt; (992 repos)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;aws-ia&lt;/strong&gt; (234 repos)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;aws-solutions&lt;/strong&gt; (153 repos)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;aws-solutions-library-samples&lt;/strong&gt; (234 repos)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When AWS publishes a new repo — say, a production-ready Bedrock AgentCore sample or a CDK construct for EKS — it's buried. GitHub search doesn't surface it. Google takes days to index it. By the time you find it, someone else already built on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Auto-Indexing Pipeline
&lt;/h2&gt;

&lt;p&gt;I built AWS Solution Finder with a twice-daily auto-indexing pipeline that detects and classifies new repos automatically.&lt;/p&gt;

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

&lt;p&gt;EventBridge (cron: 2 AM + 2 PM UTC daily)&lt;br&gt;
   ↓&lt;br&gt;
5 separate rules (one per org)&lt;br&gt;
   ↓&lt;br&gt;
Indexer Lambda (Docker, Python 3.12, 3GB, 15-min timeout)&lt;br&gt;
   ↓&lt;br&gt;
For each org:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetch ALL repos from GitHub API (paginated)&lt;/li&gt;
&lt;li&gt;Compare with master index in S3 → find NEW + REMOVED repos&lt;/li&gt;
&lt;li&gt;Classify new repos with Bedrock Nova Pro (22 metadata fields)&lt;/li&gt;
&lt;li&gt;Generate Titan Embed v2 embeddings (1024-dim)&lt;/li&gt;
&lt;li&gt;Incrementally update FAISS index&lt;/li&gt;
&lt;li&gt;Save to S3 + publish new Lambda version&lt;/li&gt;
&lt;li&gt;Write run record to DynamoDB&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The 22 Metadata Fields
&lt;/h3&gt;

&lt;p&gt;Every repo gets classified by Bedrock Nova Pro:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solution type, competency area, AWS services used&lt;/li&gt;
&lt;li&gt;Primary/secondary language, deployment tools&lt;/li&gt;
&lt;li&gt;Cost range, setup time, complexity&lt;/li&gt;
&lt;li&gt;Business value, target audience, freshness status&lt;/li&gt;
&lt;li&gt;Agentic capabilities (yes/no/partial)&lt;/li&gt;
&lt;li&gt;And more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what makes search actually useful — you're not just matching keywords, you're matching intent against structured metadata.&lt;/p&gt;

&lt;h3&gt;
  
  
  Safety Guards
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If &amp;gt;50% of repos would be removed in a single run → &lt;strong&gt;blocked&lt;/strong&gt; (likely GitHub API error)&lt;/li&gt;
&lt;li&gt;If GitHub returns 0 repos → master index NOT updated&lt;/li&gt;
&lt;li&gt;Ghost repo detection: repos in FAISS but not on GitHub get cleaned up&lt;/li&gt;
&lt;li&gt;Rate limiting on GitHub API calls (1 sec between classifications)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The New Feature: "New Repos This Week"
&lt;/h2&gt;

&lt;p&gt;Today I shipped a badge on the search page showing repos added in the last 7 days:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🆕 &lt;strong&gt;44 new repos this week&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Grouped by org (aws-samples, awslabs, etc.)&lt;/li&gt;
&lt;li&gt;Direct GitHub links for each&lt;/li&gt;
&lt;li&gt;Rolling 7-day window, updated twice daily&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%2Fh4cr3c31z8h6jv2lg0wj.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%2Fh4cr3c31z8h6jv2lg0wj.png" alt=" " width="800" height="315"&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%2Ftd49qdpmmfktkljorytc.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%2Ftd49qdpmmfktkljorytc.png" alt=" " width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gives users a reason to come back daily — not just when they need to search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&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;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scheduling&lt;/td&gt;
&lt;td&gt;Amazon EventBridge (5 rules, twice daily)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute&lt;/td&gt;
&lt;td&gt;AWS Lambda (Docker, Python 3.12, 3GB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Classification&lt;/td&gt;
&lt;td&gt;Amazon Bedrock Nova Pro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Embeddings&lt;/td&gt;
&lt;td&gt;Amazon Bedrock Titan Embed v2 (1024-dim)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector Search&lt;/td&gt;
&lt;td&gt;FAISS (IndexFlatL2, in-memory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;Amazon S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit Trail&lt;/td&gt;
&lt;td&gt;Amazon DynamoDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IaC&lt;/td&gt;
&lt;td&gt;AWS CDK (TypeScript)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Total cost for the indexing pipeline: almost nothing. The Lambda runs for ~5 minutes twice a day. Bedrock calls are only for NEW repos (not re-classifying existing ones).&lt;/p&gt;

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

&lt;p&gt;The 🆕 badge is live now. Free to try — 3 searches without registration.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://awssolutionfinder.solutions.cloudnestle.com/search?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=new-repos-feature" rel="noopener noreferrer"&gt;awssolutionfinder.solutions.cloudnestle.com/search&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What AWS repos have you discovered recently that more people should know about?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by Ajit NK — AWS Community Builder (Dev Tools). Building AI-powered developer tools at CloudNestle.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How I Search 10,000+ AWS GitHub Repos in 10 Seconds</title>
      <dc:creator>Ajit</dc:creator>
      <pubDate>Tue, 31 Mar 2026 12:51:14 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-i-search-10000-aws-github-repos-in-10-seconds-4hi2</link>
      <guid>https://dev.to/aws-builders/how-i-search-10000-aws-github-repos-in-10-seconds-4hi2</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Every AWS developer knows this pain: you need a reference architecture or sample code, and you end up with 47 browser tabs open across GitHub, AWS docs, Stack Overflow, and random blog posts from 2019.&lt;/p&gt;

&lt;p&gt;GitHub search gives you 2,000 results with zero context. ChatGPT confidently hallucinates repos that don't exist. Stack Overflow answers are outdated.&lt;/p&gt;

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

&lt;p&gt;I built an AI-powered search engine that indexes 10,000+ repos from AWS's official GitHub organizations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;aws-samples&lt;/strong&gt;: 8,031 repos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;awslabs&lt;/strong&gt;: 993 repos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;aws-solutions-library-samples&lt;/strong&gt;: 315 repos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;aws-ia&lt;/strong&gt;: 234 repos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;aws-solutions&lt;/strong&gt;: 72 repos&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The search uses a hybrid approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;70% BM25&lt;/strong&gt; (keyword matching) — catches exact AWS service names&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;30% FAISS&lt;/strong&gt; (semantic vector search) — understands what you mean, not just what you type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each repo is classified by Amazon Bedrock (Nova Pro) across &lt;strong&gt;22 metadata fields&lt;/strong&gt;: solution type, AWS services used, complexity, freshness, setup time estimate, and more.&lt;/p&gt;

&lt;p&gt;Auto-indexed twice daily via EventBridge — new AWS repos are searchable within 12 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Bedrock (Nova Pro) for AI classification&lt;/li&gt;
&lt;li&gt;FAISS with Titan Embed v2 (1024-dim) for vector search&lt;/li&gt;
&lt;li&gt;AWS Lambda (Docker, Python 3.12)&lt;/li&gt;
&lt;li&gt;API Gateway&lt;/li&gt;
&lt;li&gt;DynamoDB for usage tracking&lt;/li&gt;
&lt;li&gt;CloudFront + S3 for frontend&lt;/li&gt;
&lt;li&gt;EventBridge for scheduled indexing&lt;/li&gt;
&lt;li&gt;AWS CDK (TypeScript) for infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Architecture Decisions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why hybrid search over pure vector?&lt;/strong&gt;&lt;br&gt;
AWS service names are precise — "Lambda" means something specific. Pure semantic search sometimes returns "serverless compute" results when you specifically want Lambda. BM25 catches exact matches, FAISS catches intent. The 70/30 split was tuned through testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why FAISS over OpenSearch?&lt;/strong&gt;&lt;br&gt;
Cost. OpenSearch Serverless has a minimum cost that runs 24/7. FAISS on Lambda + S3 costs under $6/month for the search component. For a bootstrapped product, that matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why 22 metadata fields?&lt;/strong&gt;&lt;br&gt;
Developers don't just want to find a repo — they want to know: Is it maintained? How complex is it? What AWS services does it use? How long will setup take? Nova Pro classifies each repo across all 22 fields automatically.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Hybrid search beats pure vector for domain-specific queries&lt;/li&gt;
&lt;li&gt;FAISS on Lambda is surprisingly cost-effective&lt;/li&gt;
&lt;li&gt;The 22-field classification is what makes results actually useful — not just the search itself&lt;/li&gt;
&lt;li&gt;Auto-indexing twice daily keeps results fresh without manual work&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Free to use — 3 searches without registration, 10 per day with a free account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://awssolutionfinder.solutions.cloudnestle.com/search?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=launch" rel="noopener noreferrer"&gt;AWS Solution Finder&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Exploring ways to improve relevance scoring and add more metadata fields. If you've worked with FAISS or hybrid search, I'd love to hear your approach.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built solo by an AWS Community Builder. Questions? Drop a comment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>devtools</category>
      <category>serverless</category>
    </item>
    <item>
      <title># Building a Mobile AI Assistant with Kiro CLI and the Agent Client Protocol</title>
      <dc:creator>Ajit</dc:creator>
      <pubDate>Tue, 24 Mar 2026 11:14:41 +0000</pubDate>
      <link>https://dev.to/aws-builders/-building-a-mobile-ai-assistant-with-kiro-cli-and-the-agent-client-protocol-258d</link>
      <guid>https://dev.to/aws-builders/-building-a-mobile-ai-assistant-with-kiro-cli-and-the-agent-client-protocol-258d</guid>
      <description>&lt;div&gt;
    &lt;iframe src="https://www.youtube.com/embed/mxfAifdPCJE"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;


&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;AI coding assistants are powerful — but they're trapped. Locked inside your terminal or IDE. Step away from your desk and you lose access to your AI agent, your workspace context, your tools.&lt;/p&gt;

&lt;p&gt;I work across multiple environments — laptop, phone, sometimes just a quick check between meetings. My AI assistant (Kiro CLI) knows my codebase, has access to MCP tools (web search, AWS docs), and can read/write files. But only when I'm sitting at my terminal.&lt;/p&gt;

&lt;p&gt;The question: &lt;strong&gt;Can I take my AI agent mobile without rebuilding it from scratch?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;Build a bridge that connects Kiro CLI to Telegram — so I can message my AI assistant from my phone and get the same capabilities as the terminal: file access, tool execution, MCP servers, streamed responses.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;No modifications to Kiro CLI itself — use its public protocol&lt;/li&gt;
&lt;li&gt;Channel-agnostic architecture — Telegram today, Slack/Discord tomorrow&lt;/li&gt;
&lt;li&gt;Real tool access — not just a chatbot wrapper, but actual file reads, terminal commands, web search&lt;/li&gt;
&lt;li&gt;Production-ready — auth, error handling, message splitting, typing indicators&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Discovering ACP
&lt;/h3&gt;

&lt;p&gt;Kiro CLI implements the &lt;a href="https://agentclientprotocol.com" rel="noopener noreferrer"&gt;Agent Client Protocol (ACP)&lt;/a&gt; — an open standard for AI agent communication, similar to how LSP standardized language servers. It uses JSON-RPC 2.0 over stdio.&lt;/p&gt;

&lt;p&gt;This means any process that can spawn &lt;code&gt;kiro-cli acp&lt;/code&gt; and pipe JSON through stdin/stdout becomes an ACP client. Not just IDEs — anything.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tivhhu73df8hekoyr40.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%2F8tivhhu73df8hekoyr40.png" alt="Architecture Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three layers, cleanly separated using the &lt;strong&gt;Adapter Pattern&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Telegram Adapter&lt;/strong&gt; — Auth, typing indicators, message splitting. Knows nothing about ACP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACP Client&lt;/strong&gt; — JSON-RPC transport. Manages protocol lifecycle. Knows nothing about Telegram.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kiro CLI&lt;/strong&gt; — AI agent runtime. Model inference, tool execution, MCP servers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Swap Telegram for Slack by writing a new adapter. The ACP client stays identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the ACP Handshake
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkiey53wvzvbb1pj8x30r.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%2Fkiey53wvzvbb1pj8x30r.png" alt="Protocol Flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ACP has a clear lifecycle — initialize, create session, prompt, stream:&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;// Step 1: Spawn Kiro CLI as ACP agent&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kiro-cli&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;acp&lt;/span&gt;&lt;span class="dl"&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;--trust-all-tools&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;stdio&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;pipe&lt;/span&gt;&lt;span class="dl"&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;pipe&lt;/span&gt;&lt;span class="dl"&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;pipe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/path/to/workspace&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="c1"&gt;// Step 2: Initialize — exchange capabilities&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;init&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;initialize&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;protocolVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;clientCapabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;readTextFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;writeTextFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;clientInfo&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;my-bot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&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="c1"&gt;// Step 3: Create session&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&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;session/new&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;cwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/path/to/workspace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;mcpServers&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;The &lt;code&gt;clientCapabilities&lt;/code&gt; declaration is the key insight — it tells Kiro what your client can handle. Declare &lt;code&gt;fs.readTextFile: true&lt;/code&gt; and Kiro will send file read requests to your client:&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;// Kiro sends requests TO us when it needs tools&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleServerRequest&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs/readTextFile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf-8&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="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terminal/execute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;execSync&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf-8&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="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;
  
  
  Streaming Responses
&lt;/h3&gt;

&lt;p&gt;ACP streams responses via &lt;code&gt;session/update&lt;/code&gt; notifications — each containing a text chunk:&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;function&lt;/span&gt; &lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nx"&gt;acp&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;notification&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;method&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="o"&gt;=&amp;gt;&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;sessionUpdate&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent_message_chunk&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;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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;update&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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;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;session/prompt&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;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;prompt&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;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&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="dl"&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;h3&gt;
  
  
  Adding MCP Tools
&lt;/h3&gt;

&lt;p&gt;MCP servers extend the agent with external capabilities. One JSON config gives the bot web search:&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;"my-agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"web-search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"duckduckgo-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask the bot "What's the weather in Tokyo?" — Kiro automatically invokes the search tool, processes results, and responds. All within a single &lt;code&gt;session/prompt&lt;/code&gt; call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wiring Telegram
&lt;/h3&gt;

&lt;p&gt;The adapter is the simplest layer — receive, forward, respond:&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="nx"&gt;bot&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;message&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;msg&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;allowedUsers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&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="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendChatAction&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;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typing&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&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;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &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;part&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;splitMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&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;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;part&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;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;~200 lines of code.&lt;/strong&gt; A fully functional mobile AI assistant that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Connects to Kiro CLI via ACP (JSON-RPC 2.0 over stdio)&lt;/li&gt;
&lt;li&gt;✅ Full workspace access — reads files, writes files, runs terminal commands&lt;/li&gt;
&lt;li&gt;✅ MCP tool integration — web search, AWS docs, custom tools&lt;/li&gt;
&lt;li&gt;✅ Streamed responses — no waiting for the full response to generate&lt;/li&gt;
&lt;li&gt;✅ Auth, typing indicators, automatic message splitting for Telegram's 4096 char limit&lt;/li&gt;
&lt;li&gt;✅ Channel-agnostic — adding Slack or Discord means writing one new adapter file&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Patterns That Made It Work
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Adapter Pattern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Telegram adapter is decoupled from ACP. New channels = new adapter, same core.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JSON-RPC 2.0 / stdio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Same transport as LSP. Battle-tested, no HTTP overhead, works with any language.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Capability Negotiation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Client declares what it supports during &lt;code&gt;initialize&lt;/code&gt;. Forward-compatible.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bidirectional Requests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Client sends prompts, agent sends tool requests back. Both sides are client and server.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Streaming via Notifications&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent streams chunks as generated. No polling, no buffering.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Key Learnings
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ACP is underutilized.&lt;/strong&gt; Most people use Kiro in the terminal or IDE. But ACP is a general-purpose protocol — any client that speaks JSON-RPC can connect.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MCP servers are the real power.&lt;/strong&gt; The bot isn't just a chatbot — it's an agent with tools. Web search, AWS docs, file operations. MCP turns text generation into action.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;stdio is elegant.&lt;/strong&gt; No HTTP servers, no WebSocket complexity, no ports. Spawn a process, pipe JSON, done.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Adapter Pattern pays off immediately.&lt;/strong&gt; Started with Telegram. Slack would take an afternoon because the ACP client is completely separate.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Multi-channel&lt;/strong&gt; — Slack and WhatsApp adapters using the same ACP client&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Voice messages&lt;/strong&gt; — Speech-to-text → prompt → response → voice note&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Serverless&lt;/strong&gt; — Lambda + API Gateway with Telegram webhooks&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Session persistence&lt;/strong&gt; — Resume conversations across restarts&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;The full source is open: &lt;strong&gt;&lt;a href="https://github.com/ajitnk-lab/kiro-acp-telegram-bot" rel="noopener noreferrer"&gt;kiro-acp-telegram-bot&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MIT-0 licensed. Fork it, extend it, build your own channels.&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/ajitnk-lab/kiro-acp-telegram-bot.git
&lt;span class="nb"&gt;cd &lt;/span&gt;kiro-acp-telegram-bot
npm &lt;span class="nb"&gt;install
cp&lt;/span&gt; .env.example .env  &lt;span class="c"&gt;# add your Telegram token&lt;/span&gt;
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;I'd love your feedback:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What channels would you connect next?&lt;/li&gt;
&lt;li&gt;Would a serverless (Lambda) deployment be useful?&lt;/li&gt;
&lt;li&gt;What MCP servers would you want on your phone?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop a comment or open an issue on the repo. If you build something with this — a Slack bot, a Discord integration, a voice assistant — let me know. 🚀&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with &lt;a href="https://kiro.dev/cli/" rel="noopener noreferrer"&gt;Kiro CLI&lt;/a&gt; · &lt;a href="https://agentclientprotocol.com" rel="noopener noreferrer"&gt;Agent Client Protocol&lt;/a&gt; · &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://kiro.dev/docs/cli/" rel="noopener noreferrer"&gt;Kiro CLI Documentation&lt;/a&gt; — Installation, authentication, usage&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kiro.dev/docs/cli/acp/" rel="noopener noreferrer"&gt;Kiro ACP Integration Guide&lt;/a&gt; — Supported methods, session updates, Kiro extensions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://agentclientprotocol.com" rel="noopener noreferrer"&gt;Agent Client Protocol Specification&lt;/a&gt; — The open standard for AI agent-editor communication&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kiro.dev/docs/cli/mcp/" rel="noopener noreferrer"&gt;Kiro MCP Configuration&lt;/a&gt; — Setting up MCP servers (stdio + HTTP)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kiro.dev/docs/cli/custom-agents/" rel="noopener noreferrer"&gt;Kiro Custom Agents&lt;/a&gt; — Agent JSON config, hooks, MCP server selection&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io/introduction" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; — Anthropic's open standard for AI tool integration&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jsonrpc.org/specification" rel="noopener noreferrer"&gt;JSON-RPC 2.0 Specification&lt;/a&gt; — The transport protocol ACP is built on&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://core.telegram.org/bots/api" rel="noopener noreferrer"&gt;Telegram Bot API&lt;/a&gt; — Bot creation, message handling, polling vs webhooks&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kiro.dev/changelog/cli/1-25/" rel="noopener noreferrer"&gt;ACP Changelog — Kiro 1.25&lt;/a&gt; — ACP launch, MCP tools, session management&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/blogs/devops/exploring-the-latest-features-of-the-amazon-q-developer-cli/" rel="noopener noreferrer"&gt;Exploring the latest features of Amazon Q Developer CLI&lt;/a&gt; — Background on CLI + MCP architecture&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>aws</category>
      <category>devtools</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
