mirror of
https://github.com/awslabs/amazon-bedrock-agentcore-samples.git
synced 2025-09-08 20:50:46 +00:00
* feat: e2e tutorial lab5 * docs: Add README.md for 05-AgentCore Observability lab * feat: Add Lab 6 of E2E tutorial * fix: Fix Agent ECR repository typo * docs: Update Lab 6 Guidelines * feat: cleanup guardrails * docs: fix step name * added lab4 * Add Lab 3 Identity Notebook and README * added memory and updated lab 1 * pushing all of the helper files from original use case. Remove as needed * feat: update lab1 helper file * chore: restructure utils * feat: update memory helper * chore: restructure identity * chore: append to agent definition from the helper * Renamed agentcore identity to lab6 * Renamed Gateway notebook to Lab 3 and reviewed with fixes * Fixed typo in delete_memory * Lab 1: review and minor fixes * Lab 1: cleanup * Lab 2: refactored * fix: change model to Claude 3.7 * added TODOs * updated lab1 notebook * update runtime intro * refactor utils file * minor_update to memory * memory return client * revert change. * feat: update runtime lab * feat: add helper for bedrock guardrails * fix: fix typos * docs: minor update * update lab1 tools * update memory * update - runtime * updated lab3 + lambda * removed outputs * changed sh * removed zip * added one missing piece * chore: rm observability old lab * Updates to Lab6 Identity * Updates to Lab6 Identity * updated arch. diagram * update docs lab1 * rename-lab-5-6 * update arch doc * lab 03 * fixed lab 3 docs * Fix Lab 4 * Lab 7 frontend * Fix lab7 * Fix prereq issues and update gitignore * adding lab 3 tool removal * removed checkpoints * merged * chore: Update Lab 4 documentation * fix: Update AgentCore IAM Role to access memory * Lab 7 fixed invoke to runtime * minor changes * removed guardrails + minor edits * Deleting files and folders. * Rename, Refactor and deletion Added sagemaker_helper * fixing Client * Removing guardrails code * remove unused arch * remove unused files * updating lab01 * remove policies * updating lab02 * docs: Update lab 4 markdown * chore: Update Lab 4 * update cleanup * cleaning up DS_Store files * frontend * updates to lab1 notebook * updating architectures * Lab5: fixed response formatting in streamlit app * updating lab3 * updated lab3 * Lab 5 and Lab 6 and Helper Scripts Updates Lab 5: Added the architecture diagram Lab 6: Updated the notebook Utils: Added helper functions Sagemaker_helper: Cosmetic Updates * Updating lab 4 * removing clean up from lab 3 * added lab3 changes * Streamlit Fixes, Cosmetic Updates, Notebook Updates * add maira's changes * update lab2+3 * minor updates * sync labs * fix runtime docs * refactoring end-to-end tutorials * remove guardrail ss --------- Co-authored-by: Aleksei Iancheruk <aianch@amazon.fr> Co-authored-by: EugeneSel <youdjin.sel15@gmail.com> Co-authored-by: Aidan Ricci <riaidan@amazon.com> Co-authored-by: Achintya <pinnintiachintya@gmail.com> Co-authored-by: naresh rajaram <nareshrd@amazon.com> Co-authored-by: Lorenzo Micheli <lorenzo.micheli@gmail.com> Co-authored-by: Achintya <apinnint@amazon.com> Co-authored-by: HT <hardikvt@amazon.com> Co-authored-by: HT <hardik.thakkar00@gmail.com> Co-authored-by: Maira Ladeira Tanke <mttanke@amazon.com>
660 lines
28 KiB
Plaintext
660 lines
28 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "83d8ecde-5c0b-4f95-827c-5c4b7209e17f",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Lab 3: Securely connect tools to your Agent with AgentCore Gateway \n",
|
||
"\n",
|
||
"## Overview\n",
|
||
"\n",
|
||
"In this Lab, you will learn how to integrate tools available in your organization with the Customer Support Agent using the Amazon Bedrock Gateway.\n",
|
||
"\n",
|
||
"The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is an open protocol that standardizes how applications provide tools and context to Large Language Models (LLMs).\n",
|
||
"\n",
|
||
"With [Amazon Bedrock Agent Core Gateway](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html), developers can convert APIs, Lambda functions, and existing services into MCP-compatible tools and make them available to agents through Gateway endpoints with just a few lines of code.\n",
|
||
"\n",
|
||
"\n",
|
||
"**Workshop Journey:**\n",
|
||
"\n",
|
||
"- **Lab 1 (Done):** Create Agent Prototype - Built a functional customer support agent\n",
|
||
"- **Lab 2 (Done):** Enhance with Memory - Added conversation context and personalization\n",
|
||
"- **Lab 3 (Current):** Scale with Gateway & Identity - Shared tools across agents securely\n",
|
||
"- **Lab 4:** Deploy to Production - Used AgentCore Runtime with observability\n",
|
||
"- **Lab 5:** Build User Interface - Create a customer-facing application\n",
|
||
"\n",
|
||
"\n",
|
||
"### Why AgentCore Gateway & Tool Sharing Matter\n",
|
||
"\n",
|
||
"Current State (Lab 1-2): Each agent has its own copy of tools. I practice that is not scalable and leads to:\n",
|
||
"\n",
|
||
"- Code duplication across different agents\n",
|
||
"- Inconsistent tool behavior and maintenance overhead\n",
|
||
"- No centralized security or access control\n",
|
||
"- Difficulty scaling to multiple use cases\n",
|
||
"\n",
|
||
"After this lab, we will have centralized, reusable tools that can serve:\n",
|
||
"\n",
|
||
"- Customer Support Agent (our current use case)\n",
|
||
"- Sales Agent (needs same product info and customer data)\n",
|
||
"- Inventory Agent (needs same product info and warranty checking)\n",
|
||
"- Returns Processing Agent (needs return policies and customer profiles)\n",
|
||
"\n",
|
||
"and other use cases. \n",
|
||
"\n",
|
||
"### Adding secure authentication with AgentCore Identity\n",
|
||
"\n",
|
||
"Additionally, AgentCore Gateway requires you to securely authenticate both inbound and outbound connections. [AgentCore Identity](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/identity.html) provides seamless agent identity and access management across AWS services and third-party applications such as Slack and Zoom while supporting any standard identity providers such as Okta, Entra, and Amazon Cognito. In this lab we will see how AgentCore Gateway integrates with AgentCore Identity to provide secure connections via inbound and outbound authentication. \n",
|
||
"\n",
|
||
"For the inbound authentication, the AgentCore Gateway analyzes the OAuth token passed during invocation to decide allow or deny the access to a tool in the gateway. If a tool needs access to external resources, the AgentCore Gateway can use outbound authentication via API Key, IAM or OAuth Token to allow or deny the access to the external resource.\n",
|
||
"\n",
|
||
"During the inbound authorization flow, an agent or the MCP client calls an MCP tool in the AgentCore Gateway adding an OAuth access token (generated from the user’s IdP). AgentCore Gateway then validates the OAuth access token and performs inbound authorization.\n",
|
||
"\n",
|
||
"If the tool running in AgentCore Gateway needs to access external resources, OAuth will retrieve credentials of downstream resources using the resource credential provider for the Gateway target. AgentCore Gateway pass the authorization credentials to the caller to get access to the downstream API.\n",
|
||
"\n",
|
||
"\n",
|
||
"## Architecture for Lab 3\n",
|
||
"\n",
|
||
"<div style=\"text-align:left\">\n",
|
||
" <img src=\"images/architecture_lab3_gateway.png\" width=\"75%\"/>\n",
|
||
"</div>\n",
|
||
"\n",
|
||
"*Web search tool is now centralized in AgentCore Gateway with secure identity-based access control. Multiple agents and use cases can share the same tool securely. We will also reuse the `check_warranty()` and `get_customer_profile()` tools built for other applications. `get_return_policy()` and `get_product_info()` remain as local tools as they are specific to the customer support use case* \n",
|
||
"\n",
|
||
"### Key Features\n",
|
||
"- **Seamlessly integrate AWS Lambda functions:** This example shows how to integrate your Agent with existing AWS Lambda functions to check the warranty of an item and to get the customer profile using Amazon Bedrock AgentCore Gateway.\n",
|
||
"- **Secure your Gateway endpoint with Inbound Auth**: Only an Agent providing a valid JWT token can connect to the endpoint to use the tools\n",
|
||
"- **Configure the Agent to use the MCP endpoint**: The Agent gets a valid JWT token and uses it to connect to the MCP endpoint provided by AgentCore Gateway\n",
|
||
"\n",
|
||
"## Prerequisites\n",
|
||
"\n",
|
||
"* Python 3.12+\n",
|
||
"* AWS credentials configured\n",
|
||
"* Anthropic Claude 3.7 enabled on [Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html)\n",
|
||
"* Complete Lab 2 Add memory to the Customer Support Agent\n",
|
||
"* These resources are created for you within an AWS workshop account\n",
|
||
" - AWS Lambda function \n",
|
||
" - AWS Lambda Execution IAM Role\n",
|
||
" - AgentCore Gateway IAM Role\n",
|
||
" - DynamoDB tables used by the AWS Lambda function. \n",
|
||
" - Cognito User Pool and User Pool Client\n",
|
||
"#### Not using an AWS workshop account? \n",
|
||
"\n",
|
||
"**Note:** If you are running this as a self-paced lab you must run create the cloudformation resources as shown in the workshop self-paced steps. If you have not, then uncomment and run the below code segment"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"id": "3dc894f0-8014-4451-8e4d-0cad8dd1b486",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"#!bash scripts/prereq.sh"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "ee591955-83e2-4271-a596-b4a408ee6946",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Step 1: Install and import required libraries"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "1d54201a-4a1b-4947-85fd-20c90d488f99",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Install required packages\n",
|
||
"%pip install strands-agents \"boto3>=1.39.15\" strands-agents-tools bedrock_agentcore ddgs -q"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "78256720-8d5c-4abb-afa8-62e349b8063d",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Import libraries\n",
|
||
"from strands import Agent\n",
|
||
"from strands.models import BedrockModel\n",
|
||
"from strands.tools.mcp import MCPClient\n",
|
||
"import os\n",
|
||
"import sys\n",
|
||
"import boto3\n",
|
||
"import json\n",
|
||
"from bedrock_agentcore.identity.auth import requires_access_token\n",
|
||
"from mcp.client.streamable_http import streamablehttp_client\n",
|
||
"import requests\n",
|
||
"\n",
|
||
"from scripts.utils import get_ssm_parameter, put_ssm_parameter, load_api_spec, get_cognito_client_secret\n",
|
||
"\n",
|
||
"sts_client = boto3.client('sts')\n",
|
||
"\n",
|
||
"# Get AWS account details\n",
|
||
"REGION = boto3.session.Session().region_name\n",
|
||
"\n",
|
||
"gateway_client = boto3.client(\n",
|
||
" \"bedrock-agentcore-control\",\n",
|
||
" region_name=REGION,\n",
|
||
")\n",
|
||
"\n",
|
||
"print(\"✅ Libraries imported successfully!\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "118c2f26-0ae5-4a4c-8852-2e632c424fd8",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Step 2: Give our agent a tool to access existing customer data\n",
|
||
"AgentCore Gateway simplifies agent tool integration in three key ways:\n",
|
||
"\n",
|
||
"Universal MCP Support: Instantly make your tools compatible with any agent framework by exposing them through AgentCore Gateway's MCP standard\n",
|
||
"\n",
|
||
"Simple REST Integration: Transform existing REST services into agent tools by just adding them as AgentCore Gateway targets\n",
|
||
"\n",
|
||
"Lambda Flexibility: Expose Lambda functions as MCP endpoints that can call any API - demonstrated here with a function that checks warranty status\n",
|
||
"\n",
|
||
"AgentCore Gateway populates the Lambda context with the name of the tool to invoke, while the parameters passed to the tool are provided in the Lambda event:\n",
|
||
"\n",
|
||
"```\n",
|
||
"extended_tool_name = context.client_context.custom[\"bedrockAgentCoreToolName\"]\n",
|
||
"resource = extended_tool_name.split(\"___\")[1]\n",
|
||
"```\n",
|
||
"\n",
|
||
"[Lambda function](./prerequisite/lambda/python/lambda_function.py)\n",
|
||
"\n",
|
||
"```\n",
|
||
"def lambda_handler(event, context):\n",
|
||
" if get_tool_name(event) == \"check_warranty_status\":\n",
|
||
" serial_number = get_named_parameter(event=event, name=\"serial_number\")\n",
|
||
" customer_email = get_named_parameter(event=event, name=\"customer_email\")\n",
|
||
"\n",
|
||
" warranty_status = check_warranty_status(serial_number, customer_email)\n",
|
||
" return {\"statusCode\": 200, \"body\": warranty_status}\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "4a406fbb-190d-4779-b117-5bdd9086195a",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Step 3 Convert your web search tool to MCP\n",
|
||
"Now that we are developing an MCP server using AgentCore Gateway, we can MCP-ify any tools which we think we'll use for multiple Agents. One of these tools might be a web search tool like we built in Lab1. As a result, we also converted the web search tool from Lab 1 into a Lambda tool within our AgentCore Gateway:\n",
|
||
"\n",
|
||
"[Web search Lambda](./prerequisite/lambda/python/web_search.py)\n",
|
||
"```\n",
|
||
"from ddgs import DDGS\n",
|
||
"\n",
|
||
"\n",
|
||
"def web_search(keywords: str, region: str = \"us-en\", max_results: int = 5) -> str:\n",
|
||
" \"\"\"Search the web for updated information.\n",
|
||
" \n",
|
||
" Args:\n",
|
||
" keywords (str): The search query keywords.\n",
|
||
" region (str): The search region: wt-wt, us-en, uk-en, ru-ru, etc.\n",
|
||
" max_results (int): The maximum number of results to return.\n",
|
||
" \n",
|
||
" Returns:\n",
|
||
" List of dictionaries with search results.\n",
|
||
" \"\"\"\n",
|
||
" try:\n",
|
||
" results = DDGS().text(keywords, region=region, max_results=max_results)\n",
|
||
" return results if results else \"No results found.\"\n",
|
||
" except Exception as e:\n",
|
||
" return f\"Search error: {str(e)}\"\n",
|
||
"\n",
|
||
"\n",
|
||
"print(\"✅ Web search tool ready\")\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "57beaeaa-be61-415b-9fed-5741ac70670b",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Step 4 Create your function definition metadata\n",
|
||
"Lastly, we need to write tool schema which describes the tools implemented by your Lambda function.\n",
|
||
"\n",
|
||
"This file has been already defined in [prerequisite/lambda/api_spec.json](./prerequisite/lambda/api_spec.json)\n",
|
||
"\n",
|
||
"```\n",
|
||
"[\n",
|
||
" {\n",
|
||
" \"name\": \"check_warranty_status\",\n",
|
||
" \"description\": \"Check the warranty status of a product using its serial number and optionally verify via email\",\n",
|
||
" \"inputSchema\": {\n",
|
||
" \"type\": \"object\",\n",
|
||
" \"properties\": {\n",
|
||
" \"serial_number\": {\n",
|
||
" \"type\": \"string\"\n",
|
||
" },\n",
|
||
" \"customer_email\": {\n",
|
||
" \"type\": \"string\"\n",
|
||
" }\n",
|
||
" },\n",
|
||
" \"required\": [\n",
|
||
" \"serial_number\"\n",
|
||
" ]\n",
|
||
" }\n",
|
||
" },\n",
|
||
" {\n",
|
||
" \"name\": \"web_search\",\n",
|
||
" \"description\": \"Search the web for updated information using DuckDuckGo\",\n",
|
||
" \"inputSchema\": {\n",
|
||
" \"type\": \"object\",\n",
|
||
" \"properties\": {\n",
|
||
" \"keywords\": {\n",
|
||
" \"type\": \"string\",\n",
|
||
" \"description\": \"The search query keywords\"\n",
|
||
" },\n",
|
||
" \"region\": {\n",
|
||
" \"type\": \"string\",\n",
|
||
" \"description\": \"The search region (e.g., us-en, uk-en, ru-ru)\"\n",
|
||
" },\n",
|
||
" \"max_results\": {\n",
|
||
" \"type\": \"integer\",\n",
|
||
" \"description\": \"The maximum number of results to return\"\n",
|
||
" }\n",
|
||
" },\n",
|
||
" \"required\": [\n",
|
||
" \"keywords\"\n",
|
||
" ]\n",
|
||
" }\n",
|
||
" }\n",
|
||
"]\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "587ed30f-c097-4c15-b58d-3138328134de",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Step 5. Create your AgentCore Gateway\n",
|
||
"\n",
|
||
"Now let's create the AgentCore Gateway to expose the Lambda function as MCP-compatible endpoint.\n",
|
||
"\n",
|
||
"To validate the callers authorized to invoke our tools we need to configure the Inbound Auth.\n",
|
||
"\n",
|
||
"Inbound Auth works using OAuth authorization, the standard for MCP servers. With OAuth the client application must authenticate with the OAuth authorizer before using the Gateway. Your client would receive an access token which is used at runtime.\n",
|
||
"\n",
|
||
"You need to specify an OAuth discovery server and client IDs. The Cloudformation provided with the workshop already provisioned the Cognito UserPool and UserPoolClient and it stored the discovery URL and the Client ID in dedicated SSM parameters."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "e3977b2b",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"gateway_name = \"customersupport-gw\"\n",
|
||
"\n",
|
||
"auth_config = {\n",
|
||
" \"customJWTAuthorizer\": {\n",
|
||
" \"allowedClients\": [\n",
|
||
" get_ssm_parameter(\"/app/customersupport/agentcore/machine_client_id\")\n",
|
||
" ],\n",
|
||
" \"discoveryUrl\": get_ssm_parameter(\"/app/customersupport/agentcore/cognito_discovery_url\")\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"try:\n",
|
||
" # create new gateway\n",
|
||
" print(f\"Creating gateway in region {REGION} with name: {gateway_name}\")\n",
|
||
"\n",
|
||
" create_response = gateway_client.create_gateway(\n",
|
||
" name=gateway_name,\n",
|
||
" roleArn= get_ssm_parameter(\"/app/customersupport/agentcore/gateway_iam_role\"),\n",
|
||
" protocolType=\"MCP\",\n",
|
||
" authorizerType=\"CUSTOM_JWT\",\n",
|
||
" authorizerConfiguration=auth_config,\n",
|
||
" description=\"Customer Support AgentCore Gateway\",\n",
|
||
" )\n",
|
||
"\n",
|
||
" gateway_id = create_response[\"gatewayId\"]\n",
|
||
"\n",
|
||
" gateway = {\n",
|
||
" \"id\": gateway_id,\n",
|
||
" \"name\": gateway_name,\n",
|
||
" \"gateway_url\": create_response[\"gatewayUrl\"],\n",
|
||
" \"gateway_arn\": create_response[\"gatewayArn\"],\n",
|
||
" }\n",
|
||
" put_ssm_parameter(\"/app/customersupport/agentcore/gateway_id\", gateway_id)\n",
|
||
"\n",
|
||
" print(f\"✅ Gateway created successfully with ID: {gateway_id}\")\n",
|
||
"\n",
|
||
"except Exception as e:\n",
|
||
" # If gateway exists, collect existing gateway ID from SSM\n",
|
||
" existing_gateway_id = get_ssm_parameter(\"/app/customersupport/agentcore/gateway_id\")\n",
|
||
" print(f\"Found existing gateway with ID: {existing_gateway_id}\")\n",
|
||
" \n",
|
||
" # Get existing gateway details\n",
|
||
" gateway_response = gateway_client.get_gateway(gatewayIdentifier=existing_gateway_id)\n",
|
||
" gateway = {\n",
|
||
" \"id\": existing_gateway_id,\n",
|
||
" \"name\": gateway_response[\"name\"],\n",
|
||
" \"gateway_url\": gateway_response[\"gatewayUrl\"],\n",
|
||
" \"gateway_arn\": gateway_response[\"gatewayArn\"],\n",
|
||
" }\n",
|
||
" gateway_id = gateway['id']"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "eb6c6345",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Step 6. Add the Lambda function Target\n",
|
||
"Now we will use the previously defined function definitions from [prerequisite/lambda/api_spec.json](./prerequisite/lambda/api_spec.json) to create a Lambda target within our Agent Gateway. This will define the tools that your gateway will host.\n",
|
||
"\n",
|
||
"Gateway allows you to attach multiple targets to a Gateway and you can change the targets / tools attached to a gateway at any point. Each target can have its own credential provider, but Gateway becomes a single MCP URL enabling access to all of the relevant tools for an agent across myriad APIs."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "aee7aa8f-ad48-4f77-be81-36fa18e2f3aa",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def load_api_spec(file_path: str) -> list:\n",
|
||
" with open(file_path, \"r\") as f:\n",
|
||
" data = json.load(f)\n",
|
||
" \n",
|
||
" if not isinstance(data, list):\n",
|
||
" raise ValueError(\"Expected a list in the JSON file\")\n",
|
||
" return data\n",
|
||
"\n",
|
||
"try:\n",
|
||
" api_spec_file = \"./prerequisite/lambda/api_spec.json\"\n",
|
||
"\n",
|
||
" # Validate API spec file exists\n",
|
||
" if not os.path.exists(api_spec_file):\n",
|
||
" print(f\"❌ API specification file not found: {api_spec_file}\")\n",
|
||
" sys.exit(1)\n",
|
||
"\n",
|
||
" api_spec = load_api_spec(api_spec_file)\n",
|
||
" \n",
|
||
" # Use Cognito for Inbound OAuth to our Gateway\n",
|
||
" lambda_target_config = {\n",
|
||
" \"mcp\": {\n",
|
||
" \"lambda\": {\n",
|
||
" \"lambdaArn\": get_ssm_parameter(\"/app/customersupport/agentcore/lambda_arn\"),\n",
|
||
" \"toolSchema\": {\"inlinePayload\": api_spec},\n",
|
||
" }\n",
|
||
" }\n",
|
||
" }\n",
|
||
"\n",
|
||
"\n",
|
||
" # Create gateway target\n",
|
||
" credential_config = [{\"credentialProviderType\": \"GATEWAY_IAM_ROLE\"}]\n",
|
||
"\n",
|
||
" create_target_response = gateway_client.create_gateway_target(\n",
|
||
" gatewayIdentifier=gateway_id,\n",
|
||
" name=\"LambdaUsingSDK\",\n",
|
||
" description=\"Lambda Target using SDK\",\n",
|
||
" targetConfiguration=lambda_target_config,\n",
|
||
" credentialProviderConfigurations=credential_config,\n",
|
||
" )\n",
|
||
"\n",
|
||
" print(f\"✅ Gateway target created: {create_target_response['targetId']}\")\n",
|
||
"\n",
|
||
"except Exception as e:\n",
|
||
" print(f\"❌ Error creating gateway target: {str(e)}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "4fbd855b-71cb-42df-a6b9-d739e18f6767",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Step 7: Add our new MCP-based tools to our support agent\n",
|
||
"Here we integrate our authentication token from Cognito into an MCPClient from Strands SDK to create an MCP Server object to integrate with our Strands Agent"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "6fec14df-e398-4321-9903-21a5f5f76c3a",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def get_token(client_id: str, client_secret: str, scope_string: str, url: str) -> dict:\n",
|
||
" try:\n",
|
||
" headers = {\"Content-Type\": \"application/x-www-form-urlencoded\"}\n",
|
||
" data = {\n",
|
||
" \"grant_type\": \"client_credentials\",\n",
|
||
" \"client_id\": client_id,\n",
|
||
" \"client_secret\": client_secret,\n",
|
||
" \"scope\": scope_string,\n",
|
||
"\n",
|
||
" }\n",
|
||
" response = requests.post(url, headers=headers, data=data)\n",
|
||
" response.raise_for_status()\n",
|
||
" return response.json()\n",
|
||
"\n",
|
||
" except requests.exceptions.RequestException as err:\n",
|
||
" return {\"error\": str(err)}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "bff0a6b8-fdd3-4536-8012-6d0ab34f568f",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Step 7.1. Set up a secure MCP client object"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "95d2d619-9659-4733-adf9-634ee24b10c0",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"gateway_access_token = get_token(\n",
|
||
" get_ssm_parameter(\"/app/customersupport/agentcore/machine_client_id\"),\n",
|
||
" get_cognito_client_secret(),\n",
|
||
" get_ssm_parameter(\"/app/customersupport/agentcore/cognito_auth_scope\"),\n",
|
||
" get_ssm_parameter(\"/app/customersupport/agentcore/cognito_token_url\"))\n",
|
||
"\n",
|
||
"print(f\"Gateway Endpoint - MCP URL: {gateway['gateway_url']}\")\n",
|
||
"\n",
|
||
"# Set up MCP client\n",
|
||
"mcp_client = MCPClient(\n",
|
||
" lambda: streamablehttp_client(\n",
|
||
" gateway['gateway_url'],\n",
|
||
" headers={\"Authorization\": f\"Bearer {gateway_access_token['access_token']}\"},\n",
|
||
" )\n",
|
||
")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "14395b40-7ede-4d7d-be86-fc31536a7929",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Step 7.2. Access tools in our agent using our MCP client\n",
|
||
"Now we will create our Strands Agent using the AgentCore Gateway we built along with the resources from previous labs. Our agent now uses a mix of local tools via our Strands Agent and MCP tools via AgentCore Gateway"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "b1a773ea-f82b-4324-8fee-aeb6d246eb69",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"from lab_helpers.lab1_strands_agent import get_product_info, get_return_policy, SYSTEM_PROMPT\n",
|
||
"from lab_helpers.lab2_memory import CustomerSupportMemoryHooks,create_or_get_memory_resource \n",
|
||
"import uuid\n",
|
||
"from bedrock_agentcore.memory import MemoryClient\n",
|
||
"\n",
|
||
"memory_client = MemoryClient(region_name=REGION)\n",
|
||
"\n",
|
||
"memory_id = create_or_get_memory_resource()\n",
|
||
"SESSION_ID = str(uuid.uuid4())\n",
|
||
"CUSTOMER_ID = \"customer_001\"\n",
|
||
"memory_hooks = CustomerSupportMemoryHooks(memory_id, memory_client, CUSTOMER_ID, SESSION_ID)\n",
|
||
"\n",
|
||
"# Initialize the Bedrock model\n",
|
||
"model_id = \"us.anthropic.claude-3-7-sonnet-20250219-v1:0\"\n",
|
||
"model = BedrockModel(\n",
|
||
" model_id=model_id,\n",
|
||
" temperature=0.3, # Balanced between creativity and consistency\n",
|
||
" region_name=REGION\n",
|
||
")\n",
|
||
"\n",
|
||
"try:\n",
|
||
" mcp_client.start()\n",
|
||
"except Exception as e:\n",
|
||
" print(f\"Error initializing agent: {str(e)}\")\n",
|
||
"\n",
|
||
"tools = (\n",
|
||
" [\n",
|
||
" get_product_info,\n",
|
||
" get_return_policy,\n",
|
||
" ]\n",
|
||
" + mcp_client.list_tools_sync()\n",
|
||
" )\n",
|
||
"\n",
|
||
"# Create the customer support agent\n",
|
||
"agent = Agent(\n",
|
||
" model=model,\n",
|
||
" tools=tools,\n",
|
||
" hooks=[memory_hooks],\n",
|
||
" system_prompt=SYSTEM_PROMPT\n",
|
||
")\n",
|
||
"\n",
|
||
"print(\"✅ Customer support agent created successfully!\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "94c15c67-9e7f-428d-8d05-c5b2c245cc76",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Step 8: Test the agent with MCP tool access to existing APIs”\n",
|
||
"\n",
|
||
"Let's test our agent with sample queries to ensure all features work correctly."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "64d41a97-ff1f-4962-b298-e302e6c846b4",
|
||
"metadata": {
|
||
"scrolled": true
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"test_prompts = [\n",
|
||
" # Warranty Checks\n",
|
||
" \"List all of your tools\",\n",
|
||
" \"I have a Gaming Console Pro device , I want to check my warranty status, warranty serial number is MNO33333333.\",\n",
|
||
" \"What are the warranty support guidelines?\",\n",
|
||
" \"How can I fix Lenovo Thinkpad with a blue screen\",\n",
|
||
" \"Tell me detailed information about the technical documentation on installing a new CPU\"\n",
|
||
"]\n",
|
||
"\n",
|
||
"# Function to test the agent\n",
|
||
"def test_agent_responses(agent, prompts):\n",
|
||
" for i, prompt in enumerate(prompts, 1):\n",
|
||
" print(f\"\\nTest Case {i}: {prompt}\")\n",
|
||
" print(\"-\" * 50)\n",
|
||
" try:\n",
|
||
" response = agent(prompt)\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"Error: {str(e)}\")\n",
|
||
" print(\"-\" * 50)\n",
|
||
"\n",
|
||
"# Run the tests\n",
|
||
"test_agent_responses(agent, test_prompts)\n",
|
||
"\n",
|
||
"print(\"\\\\n✅ Basic testing completed!\")\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "81ab1e33-04ae-4469-91d5-e85a0a0e4070",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Congratulations! 🎉\n",
|
||
"\n",
|
||
"\n",
|
||
"You have successfully completed Lab 3: Securely connect tools to your Agent with AgentCore Gateway\n",
|
||
"\n",
|
||
"What You Accomplished:\n",
|
||
"\n",
|
||
"##### Tool Centralization & Reusability:\n",
|
||
"\n",
|
||
"- Migrated web search from local tool to centralized AgentCore Gateway\n",
|
||
"- Integrated existing enterprise Lambda functions (warranty check, customer profile)\n",
|
||
"- Created a shared tool infrastructure that multiple agent types can access\n",
|
||
"\n",
|
||
"##### Enterprise-Grade Security:\n",
|
||
"\n",
|
||
"- Implemented JWT-based authentication with Cognito integration\n",
|
||
"- Configured secure inbound authorization for gateway access\n",
|
||
"- Established identity-based access control for tool usage\n",
|
||
"\n",
|
||
"##### Scalable Architecture Foundation:\n",
|
||
"\n",
|
||
"- Built reusable tools that serve multiple use cases (customer support, sales, returns processing)\n",
|
||
"- Eliminated code duplication across different agents\n",
|
||
"- Created centralized management for tool updates and maintenance\n",
|
||
"\n",
|
||
"##### Current Limitations (We'll fix these next!):\n",
|
||
"\n",
|
||
"- **Local Development Environment** - Still running on your laptop, not production-ready\n",
|
||
"- **Limited Observability** - No comprehensive monitoring of agent behavior and performance\n",
|
||
"- **Manual Scaling** - Cannot automatically handle increased load or multiple concurrent users\n",
|
||
"\n",
|
||
"##### Next Up: Lab 4 - Deploying to Production with AgentCore Runtime\n",
|
||
"\n",
|
||
"In Lab 4, you'll transform your prototype into a production-ready system with:\n",
|
||
"\n",
|
||
"- AgentCore Runtime for scalable agent deployment\n",
|
||
"- Comprehensive observability with metrics, logging, and tracing\n",
|
||
"- Auto-scaling capabilities to handle real-world traffic\n",
|
||
"\n",
|
||
"### Resources\n",
|
||
"- [Amazon Bedrock Agent Core Gateway](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html)\n",
|
||
"- [Strands Agents Documentation](https://github.com/strands-agents/sdk-python)\n",
|
||
"- [Official Customer Support Sample](https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/02-use-cases/customer-support-assistant)"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "Python 3 (ipykernel)",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.12.9"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|