{"id":94763,"date":"2026-10-08T16:32:30","date_gmt":"2026-10-08T14:32:30","guid":{"rendered":"https:\/\/www.skaylink.com\/?p=94763"},"modified":"2026-10-21T10:35:42","modified_gmt":"2026-10-21T08:35:42","slug":"getting-started-with-aws-agentcore","status":"publish","type":"post","link":"https:\/\/www.skaylink.com\/en\/insights\/blog\/getting-started-with-aws-agentcore\/","title":{"rendered":"Getting started with AWS AgentCore"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"94763\" class=\"elementor elementor-94763\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-407c07e5 header-keyvisual-container e-flex e-con-boxed e-con e-parent\" data-id=\"407c07e5\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-5d886fd5 elementor-align-left elementor-widget elementor-widget-breadcrumbs\" data-id=\"5d886fd5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"breadcrumbs.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<p id=\"breadcrumbs\"><span><span><a href=\"https:\/\/www.skaylink.com\/en\/\">Home<\/a><\/span><\/span><\/p>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-169f44b e-con-full e-flex e-con e-child\" data-id=\"169f44b\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t<div class=\"elementor-element elementor-element-12cca5bd header-keyvisual-content e-con-full e-flex e-con e-child\" data-id=\"12cca5bd\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2cfe5f4a elementor-widget elementor-widget-image\" data-id=\"2cfe5f4a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"2560\" height=\"1707\" src=\"https:\/\/www.skaylink.com\/wp-content\/uploads\/2026\/10\/MG_4866-johanna-lohr-fotografie-skaylink-scaled.jpg\" class=\"attachment-full size-full wp-image-94772\" alt=\"Frau sitzt an einem Schreibtisch in einem B\u00fcro mit Blick auf die Stadt, arbeitet an einer Tastatur, auf dem Tisch liegen ein Laptop, Kopfh\u00f6rer und eine Pflanze\" srcset=\"https:\/\/www.skaylink.com\/wp-content\/uploads\/2026\/10\/MG_4866-johanna-lohr-fotografie-skaylink-scaled.jpg 2560w, https:\/\/www.skaylink.com\/wp-content\/uploads\/2026\/10\/MG_4866-johanna-lohr-fotografie-skaylink-300x200.jpg 300w, https:\/\/www.skaylink.com\/wp-content\/uploads\/2026\/10\/MG_4866-johanna-lohr-fotografie-skaylink-1024x683.jpg 1024w, https:\/\/www.skaylink.com\/wp-content\/uploads\/2026\/10\/MG_4866-johanna-lohr-fotografie-skaylink-768x512.jpg 768w, https:\/\/www.skaylink.com\/wp-content\/uploads\/2026\/10\/MG_4866-johanna-lohr-fotografie-skaylink-1536x1024.jpg 1536w, https:\/\/www.skaylink.com\/wp-content\/uploads\/2026\/10\/MG_4866-johanna-lohr-fotografie-skaylink-2048x1365.jpg 2048w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-67ee9e2e e-con-full e-flex e-con e-child\" data-id=\"67ee9e2e\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-69caeba8 elementor-widget elementor-widget-text-editor\" data-id=\"69caeba8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Blog<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-78855362 elementor-widget elementor-widget-heading\" data-id=\"78855362\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h1 class=\"elementor-heading-title elementor-size-default\">Getting started with AWS AgentCore<\/h1>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-33749e53 elementor-widget elementor-widget-text-editor\" data-id=\"33749e53\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tLearn how to develop, deploy, and test a scalable AI agent using AWS AgentCore and Terraform.\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-767b246a e-flex e-con-boxed e-con e-parent\" data-id=\"767b246a\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-4943555c e-flex e-con-boxed e-con e-child\" data-id=\"4943555c\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-16e29517 e-flex e-con-boxed e-con e-child\" data-id=\"16e29517\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-6aac6ab6 elementor-widget elementor-widget-text-editor\" data-id=\"6aac6ab6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tOctober 8, 2026\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-36a3e580 e-flex e-con-boxed e-con e-child\" data-id=\"36a3e580\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-7de1387d e-flex e-con-boxed e-con e-child\" data-id=\"7de1387d\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-59006115 elementor-author-box--image-valign-middle elementor-widget elementor-widget-author-box\" data-id=\"59006115\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"author-box.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-author-box\">\n\t\t\t\t\t\t\t<div  class=\"elementor-author-box__avatar\">\n\t\t\t\t\t<img decoding=\"async\" src=\"https:\/\/www.skaylink.com\/wp-content\/uploads\/2025\/09\/Screenshot_20240710_101648-219x300.png\" alt=\"Picture of Claudia M\u00fcller\" loading=\"lazy\">\n\t\t\t\t<\/div>\n\t\t\t\n\t\t\t<div class=\"elementor-author-box__text\">\n\t\t\t\t\t\t\t\t\t<div >\n\t\t\t\t\t\t<span class=\"elementor-author-box__name\">\n\t\t\t\t\t\t\tClaudia M\u00fcller\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/div>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<div class=\"elementor-author-box__bio\">\n\t\t\t\t\t\t<p>Senior AWS Consultant<\/p>\n\t\t\t\t\t<\/div>\n\t\t\t\t\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-540b7c46 e-flex e-con-boxed e-con e-child\" data-id=\"540b7c46\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-117f44f7 elementor-widget elementor-widget-author-box\" data-id=\"117f44f7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"author-box.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-author-box\">\n\t\t\t\n\t\t\t<div class=\"elementor-author-box__text\">\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-7be3aae5 e-flex e-con-boxed e-con e-parent\" data-id=\"7be3aae5\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-21794327 e-con-full e-flex e-con e-child\" data-id=\"21794327\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-3afe5a39 elementor-widget elementor-widget-heading\" data-id=\"3afe5a39\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">What is an AI Agent?<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6ed6b542 elementor-widget elementor-widget-text-editor\" data-id=\"6ed6b542\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>An AI agent is a program that uses a large language model (LLM) not just to answer questions, but to <strong>take actions<\/strong>. Instead of generating text and stopping, an agent can decide to call external functions \u2013 query a database, fetch a file, call an API \u2013 and then use the results to formulate its final answer.<\/p>\n<p>The basic loop looks like this:<\/p>\n<ol>\n<li>User sends a question.<\/li>\n<li>The LLM reads the question and the list of available tools.<\/li>\n<li>The LLM decides: can I answer directly, or do I need to call a tool first?<\/li>\n<li>If it calls a tool, it receives the tool&#8217;s output and continues reasoning.<\/li>\n<li>Steps 3\u20134 repeat until the LLM has enough information to respond.<\/li>\n<li>The LLM produces a final answer for the user.<\/li>\n<\/ol>\n<p>This \u201creason \u2192 act \u2192 observe \u2192 repeat\u201d loop is what separates an agent from a simple chatbot. The LLM becomes the decision-maker that orchestrates tool calls autonomously.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c6867a1 elementor-widget elementor-widget-heading\" data-id=\"c6867a1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">AgentCore vs Bedrock Agents: Why a new service?<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-33dd20d4 elementor-widget elementor-widget-text-editor\" data-id=\"33dd20d4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>AWS is retiring <strong>Bedrock Agents<\/strong>, which was already a fully managed agent service where you configure tools via the console, attach action groups, and AWS handles orchestration. So why a new service? Bedrock Agents works well for standard use cases, but the orchestration logic is a black box. You can&#8217;t change how the agent loops, which framework it uses, or how tools are routed.<\/p><p><strong>Bedrock AgentCore<\/strong> is the infrastructure layer underneath. It gives you:<\/p><ul><li><strong>Your own container<\/strong>\u00a0\u2013 run any agent framework (Strands, LangChain, CrewAI, or custom code)<\/li><li><strong>Full orchestration control<\/strong>\u00a0\u2013 custom hooks, iteration limits, short-circuit logic, anything<\/li><li><strong>MCP protocol for tools<\/strong>\u00a0\u2013 the open Model Context Protocol standard, not a proprietary format<\/li><li><strong>Managed compute<\/strong>\u00a0\u2013 you don&#8217;t run servers; AWS scales the containers for you<\/li><\/ul><table width=\"760\"><tbody><tr><td>\u00a0<\/td><td><p>Bedrock Agents<\/p><\/td><td><p>Bedrock AgentCore<\/p><\/td><\/tr><tr><td><p><strong>Orchestration<\/strong><\/p><\/td><td><p>AWS-managed (fixed)<\/p><\/td><td><p>Your code, any framework<\/p><\/td><\/tr><tr><td><p><strong>Tool protocol<\/strong><\/p><\/td><td><p>Action Groups (OpenAPI)<\/p><\/td><td><p>MCP (open standard)<\/p><\/td><\/tr><tr><td><p><strong>Deployment<\/strong><\/p><\/td><td><p>Config-only (no container)<\/p><\/td><td><p>Your Docker image<\/p><\/td><\/tr><tr><td><p><strong>Customization<\/strong><\/p><\/td><td><p>Console knobs<\/p><\/td><td><p>Unlimited \u2013 it&#8217;s your code<\/p><\/td><\/tr><\/tbody><\/table>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-a755311 elementor-widget elementor-widget-heading\" data-id=\"a755311\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">AgentCore components explained<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-533b2681 elementor-widget elementor-widget-text-editor\" data-id=\"533b2681\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>AgentCore might look overwhelming with all it&#8217;s different parts, so here I&#8217;m just focusing on the bare minimum of resources you need to get an agent up and running. AgentCore has three core components. Understanding what each one does (and doesn&#8217;t do) is the key to getting your first agent running.<\/p><ol><li><strong> The Runtime<\/strong><\/li><\/ol><p>The <strong>Runtime<\/strong> is your Docker container running on AWS-managed compute. It&#8217;s where your agent logic lives \u2013 the Python code, the LLM calls, the orchestration loop.<\/p><p>Think of it as a Fargate task that AWS manages for you: it pulls your image from ECR, starts the container, and routes incoming invocations to it. You don&#8217;t manage instances, scaling, or networking (unless you choose VPC mode for private resources).<\/p><p><strong>What it does:<\/strong><\/p><ul><li>Runs your agent code (any language, any framework)<\/li><li>Receives invocations via the invoke-agent-runtime API (IAM-authenticated)<\/li><li>Has an IAM role that lets it call Bedrock (for the LLM) and the Gateway (for tools)<\/li><\/ul><p><strong>What it doesn&#8217;t do:<\/strong><\/p><ul><li>It doesn\u2019t decide which tools to call \u2013 that&#8217;s your LLM<\/li><li>It doesn\u2019t route tool calls \u2013 that&#8217;s the Gateway<\/li><li>It doesn\u2019t execute tools \u2013 that&#8217;s the tool backend (Lambda, an API, or another service)<\/li><\/ul><p><strong>Configuration that matters:<\/strong><\/p><ul><li>server_protocol = &#8220;HTTP&#8221; \u2013 your container is an HTTP server, not an MCP server<\/li><li>network_mode = &#8220;PUBLIC&#8221; \u2013 AWS handles outbound internet; use &#8220;VPC&#8221; only if you need private network access<\/li><li>Container must be ARM64 architecture<\/li><\/ul><ol start=\"2\"><li><strong> The Gateway<\/strong><\/li><\/ol><p>The <strong>Gateway<\/strong> is an MCP (Model Context Protocol) endpoint that routes tool calls from your Runtime to the backends that execute them. It&#8217;s the router between &#8220;the LLM decided to call a tool&#8221; and &#8220;the tool actually runs.&#8221;<\/p><p>When your agent&#8217;s MCP client sends a tool call, the Gateway:<\/p><ol><li>Verifies the request (AWS_IAM SigV4 authentication)<\/li><li>Looks up which target matches the tool name<\/li><li>Invokes that target&#8217;s backend with the tool&#8217;s parameters<\/li><li>Returns the response back to your agent<\/li><\/ol><p><strong>What it does:<\/strong><\/p><ul><li>MCP protocol endpoint (tool discovery + tool execution)<\/li><li>Routes each tool name to the correct backend (Lambda, an external MCP server, or other AWS services)<\/li><li>Authenticates every request using IAM<\/li><\/ul><p><strong>What it doesn&#8217;t do:<\/strong><\/p><ul><li>It doesn\u2019t decide which tool to call \u2013 that&#8217;s the LLM<\/li><li>It doesn\u2019t run tool logic \u2013 that&#8217;s whatever backend the target points to<\/li><li>It has no knowledge of your agent\u2019s conversation or state<\/li><\/ul><ol start=\"3\"><li><strong> Gateway Targets<\/strong><\/li><\/ol><p>A <strong>Gateway Target<\/strong> is the binding between a tool name and a backend that executes it. For each tool you want your agent to use, you create one target that says: \u201cWhen the MCP client calls tool X, route it to backend Y.\u201d<\/p><p>The backend doesn\u2019t have to be a Lambda function, Gateway Targets support multiple target types. Lambda is the most common for custom logic, but you can also point targets at other MCP-compatible endpoints or AWS services. In this tutorial, we use Lambda because it\u2019s the simplest way to run custom code without managing servers, but keep in mind that the Gateway is a generic MCP router, not a Lambda-specific feature.<\/p><p>Each target includes a <strong>tool schema<\/strong>\u00a0\u2013 the name, description, and input parameters that get advertised to the LLM. When your agent calls list_tools_sync(), it receives these schemas, and that\u2019s how the LLM knows what tools are available and what they do.<\/p><p><strong>Pattern for Lambda targets:<\/strong> one Lambda, many aliases, many targets. You don\u2019t need a separate Lambda per tool \u2013 use aliases (alias name = tool name) and dispatch inside the handler.<\/p><p><strong>What we\u2019re building in this tutorial<\/strong><\/p><p>A minimal agent that:<\/p><ul><li>Runs Claude Sonnet in a container managed by AgentCore<\/li><li>Has one MCP tool (GetCakeRecipe) backed by a Lambda function<\/li><li>Deploys entirely via terraform apply<\/li><\/ul><p>Ask it\u00a0 <em>\u201cCan you give me a cake recipe?\u201d<\/em>\u00a0and it calls the tool, gets a Schwarzw\u00e4lder Kirschtorte recipe, and formats the answer. Ask it anything else and it answers from its own knowledge without calling the tool.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3eed2aab elementor-widget elementor-widget-heading\" data-id=\"3eed2aab\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Architecture<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-087b441 elementor-widget elementor-widget-code-highlight\" data-id=\"087b441\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>invoke-agent-runtime (AWS API, SigV4-signed)\r\n        \u2502\r\n        \u25bc\r\nAgentCore Runtime (your Docker container)\r\n  \u2514\u2500 Strands Agent + Claude Sonnet via Bedrock\r\n        \u2502\r\n        \u2502  MCP over HTTPS (SigV4-signed automatically)\r\n        \u25bc\r\nAgentCore Gateway (AWS_IAM auth, MCP protocol)\r\n        \u2502\r\n        \u2502  Invokes tool backend\r\n        \u25bc\r\nTool Backend (Lambda function in this example)\r\n  \u2514\u2500 Returns static recipe JSON\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-bc3d832 elementor-widget elementor-widget-heading\" data-id=\"bc3d832\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">The Python code in detail<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6f79db3b elementor-widget elementor-widget-text-editor\" data-id=\"6f79db3b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h4><strong>The tool function<\/strong><\/h4><p>The simplest possible tool receives whatever parameters the LLM passes and returns a JSON response:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fdb53ad elementor-widget elementor-widget-code-highlight\" data-id=\"fdb53ad\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp># src\/lambda\/tool\/handler.py\r\ndef handler(event, context):\r\n    return {\r\n        \"status\": \"ok\",\r\n        \"recipe\": {\r\n            \"name\": \"Oma's Schwarzw\u00e4lder Kirschtorte\",\r\n            \"servings\": 12,\r\n            \"ingredients\": [\r\n                \"200g dark chocolate\", \"200g butter\", \"200g sugar\",\r\n                \"5 eggs\", \"150g flour\", \"2 tsp baking powder\",\r\n                \"500ml heavy cream\", \"3 tbsp kirsch (cherry brandy)\",\r\n                \"1 jar sour cherries (drained)\", \"chocolate shavings\",\r\n            ],\r\n            \"steps\": [\r\n                \"Melt chocolate and butter, let cool.\",\r\n                \"Beat eggs and sugar until fluffy, fold in chocolate.\",\r\n                \"Fold in flour + baking powder. Bake 175\u00b0C, 25 min.\",\r\n                \"Slice into 3 layers. Whip cream with kirsch.\",\r\n                \"Layer: cake, cream, cherries. Repeat. Decorate.\",\r\n            ],\r\n        },\r\n    }\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-48f51442 elementor-widget elementor-widget-text-editor\" data-id=\"48f51442\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>In a real agent, this function would query a database, call an API, or do anything you need. We use a Lambda function here because it&#8217;s the simplest serverless option, but Gateway Targets can also point to other backends. The contract is the same regardless: receive parameters, return JSON.<\/p>\n<h4><strong>The Agent container (app.py)<\/strong><\/h4>\n<p>This is the brain. Let&#8217;s walk through each piece:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-66e7e0d elementor-widget elementor-widget-code-highlight\" data-id=\"66e7e0d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>app = BedrockAgentCoreApp()\r\n_agent = None\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5070ae9c elementor-widget elementor-widget-text-editor\" data-id=\"5070ae9c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The app is the HTTP server AgentCore talks to. The _agent is cached globally so we don&#8217;t rebuild it (and re-discover tools) on every invocation \u2013 the agent\u2013s MCP connection persists across requests within the same container lifetime.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8c8da16 elementor-widget elementor-widget-code-highlight\" data-id=\"8c8da16\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>def _get_agent():\r\n    global _agent\r\n    if _agent is not None:\r\n        return _agent\r\n\r\n    mcp_tools = []\r\n    if GATEWAY_URL:\r\n        mcp = MCPClient(lambda: aws_iam_streamablehttp_client(\r\n            endpoint=GATEWAY_URL,\r\n            aws_region=AWS_REGION,\r\n            aws_service=\"bedrock-agentcore\",\r\n        ))\r\n        mcp.start()\r\n        mcp_tools = list(mcp.list_tools_sync())\r\n\r\n    _agent = Agent(\r\n        model=model,\r\n        tools=mcp_tools,\r\n        system_prompt=\"You are a helpful assistant. Use the GetCakeRecipe tool when the user asks for a recipe.\",\r\n    )\r\n    return _agent\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-19c72d3f elementor-widget elementor-widget-text-editor\" data-id=\"19c72d3f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>MCPClient takes a factory function (the lambda:) that creates the transport connection. This is lazy \u2013 the actual HTTPS connection to the Gateway is only established when mcp.start() is called.<\/p><p>mcp.start() opens the connection and keeps it alive for subsequent calls.<\/p><p>mcp.list_tools_sync() asks the Gateway \u201cwhat tools do you have?\u201d and gets back the tool schemas you defined in Terraform. This is how the agent discovers tools dynamically \u2013 add a new tool in Terraform, and the agent picks it up on next startup without a code change.<\/p><p>Agent(&#8230;) wires everything together: the LLM (model), the available actions (tools), and the behavioral instructions (system_prompt). The system prompt tells the LLM <em>when<\/em> to use the tool \u2013 without it, the LLM might answer recipe questions from its own training data instead of calling GetCakeRecipe.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7a86504 elementor-widget elementor-widget-code-highlight\" data-id=\"7a86504\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>@app.entrypoint\r\ndef invoke_agent(payload: dict):\r\n    prompt = payload.get(\"prompt\", \"\")\r\n    agent = _get_agent()\r\n    result = agent(prompt)\r\n    return {\"result\": str(result)}\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-de21afc elementor-widget elementor-widget-text-editor\" data-id=\"de21afc\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>@app.entrypoint registers this function as the handler for incoming invocations. When someone calls invoke-agent-runtime, AgentCore delivers the payload here.<\/p><p>agent(prompt) \u2013 this single line triggers the full agent loop:<\/p><ol><li>The prompt is sent to Claude along with the tool schemas<\/li><li>Claude decides whether to call GetCakeRecipe or answer directly<\/li><li>If it calls the tool, the MCP client sends the request to the Gateway, the Gateway invokes the tool backend, and the result comes back<\/li><li>Claude sees the tool result and formulates its final answer<\/li><li>The result object contains the final text<\/li><\/ol><h4><strong>Dockerfile<\/strong><\/h4>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-53534d5 elementor-widget elementor-widget-code-highlight\" data-id=\"53534d5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>FROM python:3.12-slim\r\nWORKDIR \/app\r\nRUN apt-get update && apt-get install -y --no-install-recommends gcc g++ \\\r\n    && rm -rf \/var\/lib\/apt\/lists\/*\r\nCOPY requirements.txt .\r\nRUN pip install --no-cache-dir -r requirements.txt\r\nCOPY app.py .\r\nEXPOSE 8080\r\nCMD [\"python\", \"app.py\"]\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-de4ce29 elementor-widget elementor-widget-text-editor\" data-id=\"de4ce29\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>gcc\/g++ are needed because some Python packages (cryptography, certain boto3 dependencies) compile C extensions. The image must be built for ARM64 \u2013 AgentCore doesn&#8217;t support x86.<\/p><h3><strong>The Terraform<\/strong><\/h3><h4><strong>IAM: Three roles, three boundaries<\/strong><\/h4>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fa69df4 elementor-widget elementor-widget-code-highlight\" data-id=\"fa69df4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp># Runtime role \u2014 AgentCore runs your container with this identity\r\nresource \"aws_iam_role\" \"runtime\" {\r\n  assume_role_policy = jsonencode({\r\n    Statement = [{ Principal = { Service = \"bedrock-agentcore.amazonaws.com\" }, ... }]\r\n  })\r\n}\r\n\r\n# Gateway role \u2014 AgentCore uses this to invoke your tool backends\r\nresource \"aws_iam_role\" \"gateway\" {\r\n  assume_role_policy = jsonencode({\r\n    Statement = [{ Principal = { Service = \"bedrock-agentcore.amazonaws.com\" }, ... }]\r\n  })\r\n}\r\n\r\n# Lambda role \u2014 execution role for our tool function (this example uses Lambda)\r\nresource \"aws_iam_role\" \"lambda\" {\r\n  assume_role_policy = jsonencode({\r\n    Statement = [{ Principal = { Service = \"lambda.amazonaws.com\" }, ... }]\r\n  })\r\n}\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7a26cb7 elementor-widget elementor-widget-text-editor\" data-id=\"7a26cb7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The Runtime role needs these permissions:<\/p><ul><li>bedrock:InvokeModel \u2013 call Claude<\/li><li>bedrock-agentcore:InvokeGateway \u2013 call the Gateway (THE ONE EVERYONE FORGETS)<\/li><li>ecr:BatchGetImage \u2013 pull the container image<\/li><li>logs:PutLogEvents \u2013 write CloudWatch logs<\/li><\/ul><p>The Gateway role needs:<\/p><ul><li>Permissions to invoke whatever backend your targets use (in this example: lambda:InvokeFunction)<\/li><\/ul><h4><strong>The gateway and tool binding<\/strong><\/h4>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-433713a elementor-widget elementor-widget-code-highlight\" data-id=\"433713a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>resource \"aws_bedrockagentcore_gateway\" \"main\" {\r\n  name            = \"myagent-gateway\"\r\n  role_arn        = aws_iam_role.gateway.arn\r\n  authorizer_type = \"AWS_IAM\"\r\n  protocol_type   = \"MCP\"\r\n}\r\n\r\nresource \"aws_lambda_alias\" \"get_cake_recipe\" {\r\n  name             = \"GetCakeRecipe\"\r\n  function_name    = aws_lambda_function.tool.function_name\r\n  function_version = \"$LATEST\"\r\n}\r\n\r\nresource \"aws_bedrockagentcore_gateway_target\" \"get_cake_recipe\" {\r\n  gateway_identifier = aws_bedrockagentcore_gateway.main.gateway_id\r\n  target_configuration {\r\n    mcp {\r\n      lambda {\r\n        lambda_arn = aws_lambda_alias.get_cake_recipe.arn\r\n        tool_schema {\r\n          inline_payload {\r\n            name        = \"GetCakeRecipe\"\r\n            description = \"Returns a cake recipe. Call this when the user asks about baking.\"\r\n            input_schema { type = \"object\" }\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d69002a elementor-widget elementor-widget-text-editor\" data-id=\"d69002a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The description in tool_schema is what the LLM sees. Write it clearly \u2013 this is how Claude decides whether to call your tool. A vague description means the tool never gets used; an overly broad one means it gets called when it shouldn&#8217;t be.<\/p>\n<h4><strong>The runtime<\/strong><\/h4>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ccc625d elementor-widget elementor-widget-code-highlight\" data-id=\"ccc625d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>resource \"aws_bedrockagentcore_agent_runtime\" \"main\" {\r\n  agent_runtime_name = \"myagent_runtime\"\r\n  agent_runtime_artifact {\r\n    container_configuration {\r\n      container_uri = \"${aws_ecr_repository.agent.repository_url}:latest\"\r\n    }\r\n  }\r\n  role_arn = aws_iam_role.runtime.arn\r\n  protocol_configuration { server_protocol = \"HTTP\" }\r\n  network_configuration  { network_mode = \"PUBLIC\" }\r\n  environment_variables = {\r\n    MODEL_ID    = \"eu.anthropic.claude-sonnet-4-5-20250929-v1:0\"\r\n    AWS_REGION  = \"eu-central-1\"\r\n    GATEWAY_URL = aws_bedrockagentcore_gateway.main.gateway_url\r\n  }\r\n}<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-11155a1 elementor-widget elementor-widget-text-editor\" data-id=\"11155a1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>GATEWAY_URL is computed from the Gateway resource \u2013 Terraform wires them together automatically.<\/p><h3><strong>Deploy and test<\/strong><\/h3>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-de73c6f elementor-widget elementor-widget-code-highlight\" data-id=\"de73c6f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>terraform init && terraform apply<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-50a8af7 elementor-widget elementor-widget-text-editor\" data-id=\"50a8af7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The Terraform includes a null_resource that builds the ARM64 Docker image and pushes it to ECR before creating the Runtime. One command, no manual steps.<\/p><p>After apply (2\u20133 minutes for the container to start):<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-dd198c5 elementor-widget elementor-widget-code-highlight\" data-id=\"dd198c5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-javascript line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-javascript\">\n\t\t\t\t\t<xmp>aws bedrock-agentcore invoke-agent-runtime \\\r\n  --agent-runtime-arn \"$(terraform output -raw runtime_arn)\" \\\r\n  --payload '{\"prompt\": \"Give me a cake recipe\"}' \\\r\n  --content-type application\/json \\\r\n  \/dev\/stdout<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-523af4f5 elementor-widget elementor-widget-heading\" data-id=\"523af4f5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Things that will bite you<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-edec8fa elementor-widget elementor-widget-text-editor\" data-id=\"edec8fa\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<ol><li><strong>Missing <\/strong>InvokeGateway permission. Agent works but never calls tools. No error in logs \u2013 the MCP client gets a silent 403. Add bedrock-agentcore:InvokeGateway to the Runtime role.<\/li><li>server_protocol = \u201cMCP\u201d on the Runtime. Your container speaks HTTP (BedrockAgentCoreApp is an HTTP server). MCP is only the protocol between Runtime \u2192 Gateway. Set \u201cHTTP\u201d.<\/li><li><strong>x86 container image.<\/strong> AgentCore only runs ARM64. You need &#8211;platform linux\/arm64 and QEMU\/buildx for cross-compilation on Intel\/AMD machines.<\/li><li><strong>Image not refreshing after push.<\/strong> AgentCore caches the pulled image. Change any environment variable to force a restart with the fresh image.<\/li><li><strong>VPC mode with no outbound route.<\/strong> network_mode = \u201cVPC\u201d requires the subnets to have internet access (NAT or VPC endpoints). Use \u201cPUBLIC\u201d unless you specifically need private network access.<\/li><\/ol>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e24df0b elementor-widget elementor-widget-heading\" data-id=\"e24df0b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">What you can build with this<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-f06f0b5 elementor-widget elementor-widget-text-editor\" data-id=\"f06f0b5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The cake recipe is one tool returning static data. But the same architecture supports agents that reason across multiple tool calls to answer complex questions.<\/p>\n<p>Consider a billing data assistant where a user asks: \u201c<em>Which of my customers have unpaid invoices from last quarter, and what\u2019s their total outstanding amount?\u201d<\/em><\/p>\n<p>No single database query answers that. The agent breaks it down:<\/p>\n<ol>\n<li>First it calls a tool that lists unpaid invoices filtered by date range<\/li>\n<li>Then it calls a tool that aggregates amounts grouped by customer<\/li>\n<li>It combines both results and presents a ranked list with totals<\/li>\n<\/ol>\n<p>The LLM decides this sequence on its own \u2013 you don\u2019t hardcode the chain. You just provide the tools (each one a parameterized query) and a system prompt that describes when to use which. The agent figures out that answering the user\u2019s question requires two steps, picks the right tool for each, passes the right parameters (date range, paid status filter), and synthesizes the results into one coherent answer.<\/p>\n<p>Scale that to 10 or 15 tools covering different aspects of a database \u2013 customers, invoices, payments, revenue aggregations, full-text search \u2013 and the agent can answer almost any analytical question a business user throws at it.<\/p>\n<p>That\u2019s the jump from \u201cone tool, one response\u201d to a real production agent: the LLM becomes an autonomous query planner that chains multiple data retrievals together, each one scoped and parameterized, to answer questions that no single query could handle alone.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Learn how to develop, deploy, and test a scalable AI agent using AWS AgentCore and Terraform.<\/p>\n","protected":false},"author":23,"featured_media":94772,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[100],"tags":[182,168],"class_list":["post-94763","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","tag-ai-ml-2","tag-aws"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/posts\/94763","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/users\/23"}],"replies":[{"embeddable":true,"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/comments?post=94763"}],"version-history":[{"count":45,"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/posts\/94763\/revisions"}],"predecessor-version":[{"id":95090,"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/posts\/94763\/revisions\/95090"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/media\/94772"}],"wp:attachment":[{"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/media?parent=94763"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/categories?post=94763"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.skaylink.com\/en\/wp-json\/wp\/v2\/tags?post=94763"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}