Agentic AI on AWS: Strands Agents, Tool Calling, and MCP (AWS GenAI Developer Pro, Module 7)
This is Module 7 of AWS GenAI Pro Mastery, a free 16-module course that takes you from your first Amazon Bedrock call to AWS-certified.
A CloudCart customer writes in: “Where is order #1042? It was supposed to arrive Monday.” Relay, as you built it through Module 6, does the easy part perfectly. Intake normalizes the email, and triage returns {"intent": "shipping", "priority": "high", "sentiment": "negative"} — exactly right. Then Relay answers, and the answer is a paragraph about CloudCart’s standard delivery windows, because that is all Relay can do: paraphrase a document. The customer did not ask how shipping works. They asked where their order is, and that fact does not live in any doc — it lives in the order book, a DynamoDB table Relay has never been allowed to touch. A system that can only generate text cannot resolve this ticket. It has to read a business system and, when it is done, write to one. This module turns Relay from a thing that answers into an agentic AI — an agent that decides which tool to call, calls it, and acts on the result, without you scripting a single path.
In this module
You’ll learn how to:
- Develop an autonomous single agent with proper state management in Strands Agents (skill 2.1.1 — the single-agent half; multi-agent is Module 8).
- Explain the structured reasoning loop that lets a foundation model decompose and solve a problem, and place it against a Step Functions orchestration (skill 2.1.2).
- Implement reliable tool integrations: standardized function definitions, parameter validation, and in-tool error handling (skill 2.1.6).
- Safeguard execution with stop conditions, timeouts, and IAM resource boundaries on what the agent can actually touch (skill 2.1.3).
- Build a stateless Model Context Protocol (MCP) server on AWS Lambda exposing CloudCart’s tools, and consume it from the agent as an MCP client (skill 2.1.7; completes the tool/retrieval leg of 1.5.6 from Module 5).
You’ll build: Relay as a Strands agent with search_kb, lookup_order, and create_ticket — the two business tools served by a stateless CloudCart MCP server on AWS Lambda.
Exam domains covered: D2 — Implementation and Integration (26% of the exam). This module covers the single-agent half of Task 2.1; Module 8 covers multi-agent systems and human-in-the-loop. Domain 2 is shared with Modules 8 and 11.
Prerequisites: Modules 1–6. An AWS_PROFILE that resolves to your course account, and us-east-1 everywhere. You need the Module 5 Knowledge Base relay-kb active (the search_kb tool retrieves from it), the Module 3 converse() layer, and the Module 2 Triage schema.
Where you are in the build
This is one stop on a 16-module road. M1–M6 are behind you; M7 is here; M8–M16 are ahead.
- ✅ M1–M6 — secured account, structured triage, the
converse()model router, RAG ingestion, a cited Knowledge Base, and a multimodal intake pipeline. - 👉 M7 — the agent. A Strands agent that reasons over tools, reads the order book, searches the docs, and persists a ticket record.
- ⬜ M8–M16 — managed runtime and memory, multi-agent handoff, safety, security, deployment, cost, evaluation, observability, and the capstone.
Relay before this module: a pipeline that triages a ticket and answers cited questions from the docs, with zero side effects — it reads, it never acts. Relay after this module: a tooled Strands agent that looks up real orders, searches the Knowledge Base, and writes a TicketRecord to DynamoDB — still running locally (the managed runtime is Module 8).
From completion to agent: the reasoning–action loop
Everything you have built so far is a completion: you call converse() once and get one response. You decide every step; the model fills in text. An agent inverts that control. You give the model a goal and a set of tools, and the model decides what to do next — the shift this module is about.
The mechanism is the ReAct loop — reason, act, observe, repeat — the pattern from the ReAct paper that interleaves reasoning with tool calls. On each turn the model reasons about the goal, optionally emits a tool calling request (the structured “call lookup_order with order_id='1042'” your code then executes), reads the result back as an observation, and reasons again — until it can answer directly. For Relay’s order question that is two or three turns: reason that an order-status question needs the order book, call lookup_order, observe the real status, call create_ticket, then answer. You never wrote that sequence. The model chose it.
flowchart TD
U[Customer ticket] --> M[Foundation model reasons]
M --> Q{Tool call needed?}
Q -->|yes| T[Execute tool<br/>search_kb · lookup_order · create_ticket]
T --> O[Observe result] --> M
Q -->|no| A[Final answer to customer]
G[Stop condition · timeout] -. caps the loop .-> M
Two things make this safe rather than reckless. The loop is bounded — the dashed guard is a hard cap on turns — and the tools your code exposes are the only things the agent can do; it cannot invent a fourth action. The Strands SDK runs the loop for you: you declare the tools and the system prompt, and it handles the turn-by-turn Converse calls, the tool dispatch, and the stop condition. (If you came through this course’s NCP-AAI track, this is the same idea as a LangGraph agent loop — but the AWS-native, blueprint-named path is Strands, and that is what the exam tests.)
Choosing your orchestrator: Strands vs Step Functions vs Bedrock Agents
This is the single most testable decision in Domain 2, and it has three contenders. The mistake is treating them as competitors for one slot. They sit at different points on one axis: who decides the control flow — the model, or you?
| Strands Agents | AWS Step Functions | Bedrock Agents (classic) | |
|---|---|---|---|
| Who decides the flow | the model, at runtime (ReAct) | you, at design time (a state machine) | the model, inside a managed service |
| Determinism / auditability | low — path varies per run | high — every transition is defined and logged | medium — managed traces, less control |
| State / sessions | in-process conversation state (managed memory = Module 8) | execution state in the service | managed sessions |
| Latency | model-call-bound (N turns = N calls) | low per state; no model unless a state calls one | model-call-bound |
| Lock-in / portability | open-source SDK, runs anywhere | AWS service | AWS service, first-generation |
| Infra effort | a Python process (deploy later) | define states + IAM | console/SDK config, action groups |
| Choose it when | the path is unpredictable and needs dynamic decisions | the path is fixed and must be auditable | you want a managed agent and accept first-generation ergonomics |
Read the rows top to bottom and the rule falls out. When a support flow is unpredictable — one ticket needs a doc lookup, the next an order check, a third both, and you cannot enumerate the branches — let the model drive: Strands. When a flow is a compliance process that must follow the same audited path every time — verify, charge, notify, record, every transition logged — encode it as a deterministic Step Functions workflow and call a model only from the states that need one. A state machine is one way to bound an agentic process — just a non-model-driven one.
Bedrock Agents (classic) is the first-generation managed agent service: you configure action groups and let Bedrock run the loop. Recognize it as the first-generation managed-agent option and know where it sits — but note the AIP-C01 Domain 2 blueprint names Strands Agents, Agent Squad, and MCP, not classic Bedrock Agents, and AWS’s current investment points at Strands Agents plus Bedrock AgentCore (the managed runtime, Module 8). We build on Strands and treat classic Bedrock Agents as orientation, not lab code.
⚠️ Common misconception: “Strands and Step Functions are competitors; pick the better one.” Strands gives the model control of the path; Step Functions gives you control. The right answer to an exam scenario is whichever the problem demands — dynamic and unpredictable means the model decides (Strands); fixed and must-be-auditable means you decide (Step Functions). A real system often uses both: a deterministic workflow that calls an agent from one of its states.
Reliable tools: definitions, validation, and guarded execution
An agent is only as good as the tools it can call, and a tool is only useful if the model can both understand it and trust it. Relay gets exactly three: search_kb, lookup_order, create_ticket. No synonyms.
A tool’s docstring is its spec; its type hints are its schema. Strands’ @tool decorator turns search_kb(query: str) -> str plus the function’s docstring into the exact tool definition the model sees — the description, the parameter names, the types. One source of truth, no hand-written JSON schema to drift. Write a clear docstring and you have written the tool’s contract.
Validate parameters; return clean errors to the model. This is the line between a toy and a production tool. When lookup_order gets a blank ID, or an order that does not exist, it does not raise a stack trace and it does not return a silent empty result. It returns a short, model-facing message — "No order '9999' found ... it may be mistyped" — so the model can recover: ask the customer for the right number, try another tool, or give up gracefully. A failing tool reports; it does not crash the loop. No silent try/except swallows the cause.
Every tool the agent calls is journaled as an AgentAction — the frozen schema that turns the agent’s behavior into an auditable trail. It is exactly four fields:
class AgentAction(BaseModel):
tool: str
tool_input: dict
result: str
approved: bool | None = None
approved stays None everywhere in this module. The human-approval flow that uses it is Module 8; here, the agent proposes actions and nothing approves or rejects them. The journal of every AgentAction lands in the persisted TicketRecord, which carries the whole outcome of a handled ticket — its status, the triage, the answer, the action log, and bookkeeping.
Now the part that links straight to cost. An agent without a budget is a billing incident waiting to happen, so Relay’s execution is guarded by three layers (skill 2.1.3):
- Stop conditions. The ReAct loop is N model calls, and N billed calls is N times the cost. A cap on turns —
limits={"turns": MAX_ITERATIONS}, withMAX_ITERATIONS = 6— bounds it. A model stuck on a failing tool stops at the cap instead of looping forever and burning tokens. - Timeouts. A wall-clock ceiling on the bedrock-runtime client, so a hung call cannot pin the process. The turn cap fires between cycles; the timeout covers a stall inside one.
- IAM resource boundaries. Enforced by the cloud, not by convention. The Lambda role can read only
relay-ordersand write onlyrelay-tickets; any other table is denied by IAM, full stop. (Full least-privilege per component is Module 10; this is the agent-scoped slice.) One honest caveat: the agent’s tool-sidecreate_tickettraverses this bounded Lambda role — the boundary the demo proves — whilehandle()’s authoritative, journal-bearing write torelay-ticketsis a local control-plane writer (keyed idempotently onticket_id) that runs under the developer’s profile, not the Lambda role; both hitrelay-tickets, neverrelay-orders.
⚠️ Common misconception: “Tool calling means the tool runs at the model provider.” It does not. When the model emits a tool-call request, it is your code — here, a Lambda with its own IAM role — that executes. The model only asks. That is precisely why the IAM boundaries matter: the model can request a write to
relay-orders, but your Lambda’s role refuses it. The boundary is on your side, where it belongs.
MCP: one protocol for your tools
You could wire lookup_order straight into the agent as a local function. So why a protocol? Because integrations do not stay at one agent and one tool. N agents times M tools is N×M bespoke connectors — a combinatorial mess of glue. The Model Context Protocol (MCP) collapses it: a client/server wire where a client connects to a server and discovers its tools at runtime. Write a tool once on an MCP server, and any MCP client can call it, with the agent listing the server’s tools rather than hard-coding them.
Relay splits its tools deliberately. search_kb stays a local Strands tool — Relay’s own read path into its own Knowledge Base, no business side effect, so it needs no wire (the skill 1.5.6 pattern: retrieval exposed as a standardized tool the model can choose to call). The two business tools move onto MCP. They run on a stateless CloudCart MCP server (FastMCP, stateless_http=True) deployed to AWS Lambda, and Relay is the MCP client that connects, discovers them, and hands them to the agent.
flowchart LR
subgraph Local["Relay (Strands agent, runs locally)"]
AG[ReAct loop] --> SK[search_kb<br/>local @tool]
AG --> MC[MCP client]
end
SK --> KB[(Bedrock Knowledge Base<br/>relay-kb)]
MC -->|Streamable HTTP| MS[CloudCart MCP server<br/>AWS Lambda · stateless]
MS --> LO[lookup_order] --> ORD[(DynamoDB<br/>relay-orders · read)]
MS --> CT[create_ticket] --> TIX[(DynamoDB<br/>relay-tickets · write)]
Why Lambda for this server? The workload is lightweight and stateless — each MCP request is self-contained, no server-side session to keep warm, so a short-lived invocation serves a request and exits, billing ~$0 idle. That is the canonical Lambda fit. A heavy or stateful tool server — one holding a warm model in memory, or a large in-process cache — would not fit Lambda’s short-lived model; that goes on Amazon ECS (or a long-running container). Lambda for stateless and light, ECS for heavy and stateful: a direct exam question, and the wrong half is always tempting.
⚠️ Common misconception: “MCP is an agent-to-agent protocol.” It is not. MCP standardizes the connection between an agent and its tools/data — an agent is the client of tool servers. Communication between agents (handoffs, A2A) is a different mechanism entirely — Module 8’s material. Perfect matching-question bait: “standardizing how an agent reaches its tools” is MCP; “one agent delegating to another” is not.
Build it: Relay becomes an agent
The lab turns Relay into a Strands agent whose business tools are served by a stateless MCP server on Lambda, under a stop condition, a timeout, and an IAM-bounded role. Asking “Where is order 1042?” prints the agent’s ReAct loop, an answer citing the order’s real status, and the persisted TicketRecord. The full code is in the lab; here are the load-bearing excerpts. This lab cost about $0.02 on June 2026 prices, well under the Module 7 budget of under $2.
Step 1 — Carry state forward and pin the agent libraries. Module 7 starts from Module 6’s relay/ byte-for-byte, plus the Module 5 Knowledge Base. The two new libraries move fast, so they are re-verified on PyPI on generation day:
uv sync # adds strands-agents~=1.43 (latest 1.43.0) and mcp~=1.27 (latest 1.27.2)
# Model Context Protocol spec revision 2025-06-18; uv.lock committed
Step 2 — A reliable tool: docstring as spec, validation, clean errors. The local search_kb tool is one decorated function. Its docstring is the description the model reads; its type hint is the schema. Note the error path — it returns a message, it does not crash:
@tool
def search_kb(query: str) -> str:
"""Search CloudCart's help-center documentation for an answer.
Use this for HOW-TO and POLICY questions — refunds, plan changes, error
codes, shipping policy. Do NOT use it to look up a specific order's status.
Args:
query: the customer's question, in natural language.
"""
if not query or not query.strip():
return "No query was given. Pass the customer's question text to search_kb."
try:
passages = kb.retrieve(query.strip(), top_k=SEARCH_KB_TOP_K)
except kb.KBError as err:
return (f"The knowledge base could not be searched right now ({err}). "
"Answer from what you know, or ask the customer for more detail.")
...
Step 3 — The two business tools, on the MCP server. lookup_order and create_ticket are @mcp.tool() functions on a stateless FastMCP server. The store layer beneath them raises model-facing errors — OrderNotFound, ToolInputError — that the server turns into a clean string the model reads. It touches only relay-orders (read) and relay-tickets (write), matching the Lambda role:
@mcp.tool()
def lookup_order(order_id: str) -> str:
"""Look up the real status of a CloudCart order by its order id.
Use this for "where is my order?" — the live order book, not the docs.
Returns the order's status, key dates, total, and line items, or a clear
message if no such order exists.
"""
try:
order = store.lookup_order(order_id) # reads relay-orders only
except store.OrderNotFound as err:
return str(err) # model-facing, recoverable
return json.dumps(order, ensure_ascii=False)
Step 4 — Assemble the agent under its budget. build_agent() wires the smart-tier model (resolved through relay.config — no model ID here, ever), the system prompt, and the tools. handle() runs it with the stop condition and journals every call:
AGENT_TIER = "smart" # a NAME — the model ID lives only in relay.config
MAX_ITERATIONS = 6 # the stop condition: caps the ReAct loop (and the bill)
def handle(customer_message, *, ticket_id, agent, journal,
persist=None, max_iterations=MAX_ITERATIONS):
result = agent(prompt, limits={"turns": max_iterations}) # <- stop condition
stop_reason = getattr(result, "stop_reason", "") or ""
if stop_reason == "limit_turns":
status, summary = "failed", f"Stopped at the {max_iterations}-turn limit."
else:
status = "answered"
# persist the frozen TicketRecord (actions[] = journal, cost_cents=0.0 at M7)
...
Step 5 — Run it. Real output from the live account (DynamoDB order 1042, status in_transit, ETA 2026-06-15, tracking CCX-1042-88213):
$ uv run python -m relay.agent "Where is order 1042? It was supposed to arrive Monday."
--- agent actions (the ReAct loop) ---
1. lookup_order({'order_id': '1042'}) -> {"order_id": "1042", "status": "in_transit", ...}
2. create_ticket({'ticket_id': 'ticket-137f0ab0', 'status': 'answered', ...}) -> Ticket ticket-137f0ab0 stored in relay-tickets ...
--- final answer (stop reason: end_turn) ---
Your order 1042 is in transit. The current delivery estimate is 2026-06-15 (a carrier
delay pushed it past the original Monday estimate). Tracking number: CCX-1042-88213.
--- TicketRecord persisted to relay-tickets ---
{ "ticket_id": "ticket-137f0ab0", "status": "answered", "actions": [ ... ],
"cost_cents": 0.0, "escalated": false, ... }
The agent cited the order’s real status, not a doc generality. One instrumented run measured 3 smart-tier model calls (lookup_order → create_ticket → final answer), 3,567 in / 97 out tokens = $0.0013. With no code change, a documentation question takes the other path — the model picks search_kb:
$ uv run python -m relay.agent "How do refunds work?"
--- agent actions --- 1. search_kb({'query': 'how do refunds work'}) -> [1] s3://...
That answer came from billing-duplicate-charge.md and technical-webhooks.md with real s3:// citations — same agent, different path, chosen by the model.
Step 6 — Prove the safeguards. Two demonstrations the exam cares about. First, a runaway loop cut by the stop condition — the offline test drives a scripted model that keeps calling a failing tool, and the cap fires:
$ uv run pytest -k test_stop_condition_cuts_a_runaway_agent -q
# -> stop_reason == "limit_turns", status == "failed", <= max_iterations tool calls. PASSED
Second, the IAM boundary, proven live with simulate-principal-policy on relay-mcp-lambda-role: dynamodb:PutItem on relay-orders is implicitDeny, PutItem on relay-tickets is allowed, GetItem on relay-orders is allowed. The role can read orders and write tickets — nothing else — enforced by IAM, not by a comment in the code.
Step 7 — Tests, then teardown. Offline pytest runs 142 passed, 6 skipped (no creds, no network — Modules 2–7). The live marker makes at most seven sub-cent calls including one capped agent run: RELAY_LIVE_TESTS=1 uv run pytest -m live ran 6 passed. Teardown deletes the MCP Lambda, its Function URL, and its role, and keeps the on-demand tables and the Knowledge Base — Module 8 reuses both and they idle at ~$0.
Try it yourself. (a) Add a get_shipping_policy() tool to the MCP server, redeploy, and ask the agent about shipping — it discovers and uses the new tool with no change to the agent or relay/tools.py. That is the point of MCP. (b) Lower max_iterations to 1 and ask the order question: with one turn the agent cannot both look up the order and answer — the stop condition fires, the record is failed, and you see why the budget must leave room for the loop the task needs.
In production
A few things change when this is not a laptop demo. Observability of tool calling becomes essential: at scale you need to know which tool ran, how long it took, and how often it failed — a failing tool the model retries is both a bad experience and a runaway bill (dashboards and traces are Module 14). Version your tool definitions the way you version an API; a model is sensitive to a docstring and signature, and changing them silently changes behavior. Make create_ticket idempotent — an agent that retries must not write two tickets, which is why Relay keys the write on ticket_id and lets a retry overwrite the same row. Authenticate exposed MCP servers: the lab fronts the Lambda with a public Function URL for simplicity, but a real server needs SigV4 or an identity layer (AgentCore Gateway/Identity, Module 8).
And the cost line you cannot un-see: every iteration of the loop is a billed model call. A completion is one call; an agent is N. That is not a reason to avoid agents — it is the reason stop conditions are not optional. I would never deploy an agent without a turn cap and a per-run cost ceiling. The exam likes this Domain 2 ↔ Domain 4 link: the safeguard that keeps your agent correct is the same one that keeps it affordable.
Exam corner
What the exam tests here. The single-agent slice of Task 2.1: an autonomous agent with proper state management (2.1.1), structured reasoning placed against deterministic orchestration (2.1.2), guarded execution with stopping conditions, timeouts, and IAM resource boundaries (2.1.3), reliable tool integrations with validation and clean error handling (2.1.6), and extending models with MCP — stateless Lambda servers, heavy ECS servers, MCP clients (2.1.7). Expect an orchestrator-choice scenario and a Lambda-vs-ECS placement scenario.
Quiz.
-
A support workflow must handle unpredictable tickets — some need a doc lookup, some an order check, some both — and you cannot enumerate the branches ahead of time. A separate refund-approval workflow must follow the same audited path every time and log every transition. Which pair of tools fits?
- A. Step Functions for the support workflow; Strands for the refund workflow.
- B. Strands for the support workflow; Step Functions for the refund workflow.
- C. Bedrock Agents classic for both.
- D. Strands for both, with extra logging on the refund flow.
-
An agent gets stuck calling a tool that keeps returning an error and never reaches a final answer, burning tokens on every turn. What is the correct first safeguard, and where does it apply?
- A. A larger context window so it can hold more retries.
- B. A stop condition (max turns) on the agent loop, which cuts the run at the cap.
- C. A higher-tier model so it reasons its way out of the loop.
- D. A try/except around the tool that returns a default answer.
-
In production, your agent’s tools occasionally receive invalid parameters — a blank order ID, a malformed date. What should the tool do so the agent stays robust?
- A. Raise an unhandled exception so the run fails fast and is retried.
- B. Return
Nonesilently so the model moves on. - C. Validate the input and return a short, model-facing error message the agent can recover from.
- D. Log the error and return the last successful result.
-
You need to expose two tool servers to your agent: one is a lightweight, stateless order lookup; the other holds a warm model in memory and a large cache, and must stay running. Where should each MCP server go?
- A. Both on AWS Lambda.
- B. Both on Amazon ECS.
- C. The stateless lookup on Lambda; the heavy, stateful server on ECS.
- D. The heavy server on Lambda; the lookup on ECS.
-
Your agent must read the order book and write tickets, and nothing else — a teammate worries it could one day modify orders. How do you enforce that the agent cannot write to
relay-orders?- A. A system-prompt rule telling the agent never to modify orders.
- B. An IAM role on the tool’s execution environment scoped to read
relay-ordersand writerelay-ticketsonly. - C. A stop condition that limits the number of writes.
- D. A guardrail that filters the agent’s output.
Answers. 1 — B. Unpredictable, dynamic decision-making means let the model drive the path (Strands); a fixed, must-be-auditable compliance path means encode it deterministically (Step Functions). 2 — B. A stop condition on the agent loop is the runaway safeguard; A, C, and D do not bound the loop, and D hides the failure. 3 — C. A reliable tool validates and returns a clean, model-facing error so the model can recover — never a silent None, never a swallowed exception. 4 — C. Stateless and light fits Lambda’s short-lived model; heavy and stateful needs a long-running container on ECS. 5 — B. IAM resource boundaries on the execution role enforce what the agent can touch; a prompt rule (A) is not a control, and stop conditions/output filters (C, D) solve different problems.
Traps to avoid.
- MCP is not agent-to-agent, and tool calling is not “the model runs your tool.” MCP standardizes agent↔tools; agent↔agent (handoffs, A2A) is a different layer (Module 8). And tool calling means your code executes the tool — which is why IAM boundaries are on your side.
- Don’t pick Bedrock Agents classic by reflex because “it’s the managed service.” Know where it sits — first-generation managed agents — and know that AWS’s current investment is Strands plus AgentCore. The Domain 2 blueprint names Strands, Agent Squad, and MCP, so place it as orientation rather than defaulting to it.
- Stop conditions are not a luxury. Without them an agent’s cost is unbounded; one stuck loop is a billing incident. The exam loves this Domain 2 ↔ Domain 4 link between a correctness safeguard and a cost safeguard.
Key takeaways
- The agent decides; your tools act. A completion is one model call you control; an agent is a ReAct loop the model drives over the tools you expose — and only those tools.
- Strands, Step Functions, and Bedrock Agents sit on one axis: who controls the flow. Model-driven and dynamic → Strands; deterministic and auditable → Step Functions; managed first-generation → Bedrock Agents classic.
- A reliable tool validates its parameters and fails cleanly. Its docstring is its spec and its type hints are its schema; on bad input it returns a model-facing error, never a crash or a silent empty result.
- Stop conditions, timeouts, and IAM boundaries are the real execution safeguards. They bound the loop, the wall clock, and what the agent can touch — and the turn cap is what makes an agent’s cost bounded.
- MCP standardizes agent↔tools. A client discovers a server’s tools at runtime, so the same agent can consume any MCP server with no bespoke glue.
- Lambda for stateless, light tool servers; ECS for heavy, stateful ones. The placement is a direct exam question, and the wrong half is always the tempting one.
AgentActionandTicketRecordare frozen here. The full seven-status enum andcost_centsare present from the start even though M7 exercises only four statuses;approvedstaysNoneuntil Module 8.
What’s next
Relay can act now — but it still runs on your laptop, forgets every conversation, and refunds nobody. Module 8 deploys it on Bedrock AgentCore Runtime, gives it memory, adds a Billing specialist to hand off to, and puts a human approval gate in front of any refund — the moment AgentAction.approved finally stops being None.
Want the full AIP-C01 question bank and the next module in your inbox? Subscribe here — it’s free, like everything in this series.
Links: Module 7 lab · ← Module 6 · Course index · Module 8 →
References
- Strands Agents documentation — the model-driven agent SDK, the
@tooldecorator, the agent loop, and MCP client support. - Strands Agents Python SDK — source, examples, and the
1.43.0release used in this lab. - Model Context Protocol specification (revision 2025-06-18) — the client/server architecture, tool discovery, and the Streamable HTTP transport.
- AWS Prescriptive Guidance — choosing an AWS agentic AI framework — Strands, Step Functions, and managed agents, with selection criteria.
- ReAct: Synergizing Reasoning and Acting in Language Models (arXiv 2210.03629) — the reason–act–observe loop the agent runs.
- AWS Certified Generative AI Developer - Professional (AIP-C01) exam guide — Task 2.1 and skill 1.5.6, the vocabulary this module covers.