# Human-in-the-Loop (HITL) for Agentic Workflows Sample code accompanying the blog post on implementing Human-in-the-Loop constructs for AI agents in healthcare and life sciences using [Strands Agents](https://github.com/strands-agents/sdk-python), [Amazon Bedrock AgentCore Runtime](https://docs.aws.amazon.com/bedrock/latest/userguide/agentcore.html), and the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/). ## Overview Four standalone methods for adding human oversight to sensitive agent tool calls: | Method | Approach | Approval Model | |--------|----------|---------------| | 1 — Hook System | Agent framework hook intercepts tool calls before execution | Centralized, blanket policy | | 2 — Tool Context | Approval logic embedded inside each tool with role-based access | Per-tool, fine-grained | | 3 — Step Functions | Async workflow sends email to external approver via SNS | Third-party, asynchronous | | 4 — MCP Elicitation | MCP server uses `ctx.elicit()` for real-time interactive approval | Protocol-native, real-time | ## Prerequisites - Python 3.11+ - [uv](https://docs.astral.sh/uv/) package manager - AWS account with permissions for AgentCore, Lambda, Step Functions, SNS, Cognito - [AgentCore CLI](https://docs.aws.amazon.com/bedrock/latest/userguide/agentcore-cli.html) (`agentcore`) ## Setup 1. **Configure AWS credentials** Create a `.env` file in the project root: ``` AWS_PROFILE=your-profile AWS_REGION=us-west-2 NOTIFICATION_EMAIL=approver@example.com ``` 2. **Install dependencies** ```bash uv sync ``` 3. **Deploy infrastructure** (Lambda, Step Functions, SNS, Cognito) ```bash cd infra && bash deploy.sh ``` This deploys a CloudFormation stack (`hitl-stack`) with: - A Lambda function simulating a sensitive patient vitals tool - A Step Functions state machine for external approval workflows (Method 3) - An SNS topic for email notifications (Method 3) - A Cognito User Pool for MCP server authentication (Method 4) ## Architecture ### Methods 1 & 2: Hook System / Tool Context ![Architecture diagram for Methods 1 and 2 showing a Healthcare Application calling into Amazon Bedrock AgentCore Runtime, where a StrandsAgent exposes patient tools with hook-based or tool-context-based approval](images/method1_2.png) ### Method 3: Step Functions ![Architecture diagram for Method 3 showing a Healthcare Application calling into Amazon Bedrock AgentCore Runtime, where a StrandsAgent delegates discharge approval to an AWS Step Functions authorization workflow](images/method3.png) ### Method 4: MCP Elicitation ![Architecture diagram for Method 4 showing a Healthcare Application connected via WebSockets to Amazon Bedrock AgentCore Runtime, where a StrandsAgent communicates with an MCP Server over the MCP Protocol for real-time elicitation-based approval](images/method4.png) ## Deploying & Testing Each Method Each method has its own directory with an `agent.py` (AgentCore entrypoint), `deploy.sh`, and `test.py`. ### Method 1: Hook System ```bash bash method1_hook/deploy.sh uv run method1_hook/test.py ``` ### Method 2: Tool Context ```bash bash method2_tool_context/deploy.sh uv run method2_tool_context/test.py ``` ### Method 3: Step Functions ```bash bash method3_remote_sfn/deploy.sh uv run method3_remote_sfn/test.py ``` When the agent requests a discharge, an email is sent to the configured `NOTIFICATION_EMAIL`. Use the approval script to approve/deny: ```bash uv run method3_remote_sfn/approve.py ``` ### Method 4: MCP Elicitation Deploy the MCP server first, then the agent: ```bash bash method4_mcp_elicitation/deploy.sh # MCP server bash method4_mcp_elicitation/agent_deploy.sh # Agent (WebSocket) uv run method4_mcp_elicitation/test.py ``` A local-only version (no AgentCore deployment needed) is also available: ```bash uv run method4_mcp_elicitation/main.py ``` ## Project Structure ``` ├── images/ │ ├── method1_2.png # Architecture diagram for Methods 1 & 2 │ ├── method3.png # Architecture diagram for Method 3 │ └── method4.png # Architecture diagram for Method 4 ├── infra/ │ ├── cloudformation.yaml # Lambda, Step Functions, SNS, Cognito │ └── deploy.sh ├── method1_hook/ │ ├── agent.py # BeforeToolCallEvent hook │ ├── deploy.sh │ └── test.py ├── method2_tool_context/ │ ├── agent.py # tool_context.interrupt() with RBAC │ ├── deploy.sh │ └── test.py ├── method3_remote_sfn/ │ ├── agent.py # Step Functions + SNS async approval │ ├── approve.py # CLI script to approve/deny pending executions │ ├── deploy.sh │ └── test.py ├── method4_mcp_elicitation/ │ ├── server.py # MCP server with ctx.elicit() │ ├── agent.py # WebSocket agent relaying elicitation │ ├── main.py # Local version (no deployment needed) │ ├── deploy.sh # MCP server deploy │ ├── agent_deploy.sh # Agent deploy │ └── test.py └── pyproject.toml ``` ## Cleanup To remove deployed agents: ```bash uv run agentcore delete --agent ``` To remove infrastructure: ```bash aws cloudformation delete-stack --stack-name hitl-stack --region us-west-2 ```