Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -148,68 +148,100 @@
"%%writefile agents/strands_aws_docs.py\n",
"import os\n",
"import logging\n",
"import asyncio\n",
"from mcp import stdio_client, StdioServerParameters\n",
"from strands import Agent\n",
"from strands.multiagent.a2a import A2AServer\n",
"from strands.tools.mcp import MCPClient\n",
"from fastapi import FastAPI\n",
"import uvicorn\n",
"\n",
"\n",
"logging.basicConfig(level=logging.INFO)\n",
"app = FastAPI()\n",
"logger = logging.getLogger(__name__)\n",
"\n",
"# Use the complete runtime URL from environment variable, fallback to local\n",
"app = FastAPI()\n",
"runtime_url = os.environ.get('AGENTCORE_RUNTIME_URL', 'http://127.0.0.1:9000/')\n",
"host, port = \"0.0.0.0\", 9000\n",
"\n",
"\n",
"\n",
"stdio_mcp_client = MCPClient(\n",
" lambda: stdio_client(\n",
" StdioServerParameters(\n",
" command=\"uvx\", args=[\"awslabs.aws-documentation-mcp-server@latest\"]\n",
" )\n",
" )\n",
")\n",
"\n",
"stdio_mcp_client.start()\n",
"# Global MCP client with lazy initialization\n",
"_mcp_client = None\n",
"\n",
"async def get_mcp_client():\n",
" \"\"\"Lazy initialization of MCP client with timeout\"\"\"\n",
" global _mcp_client\n",
" if _mcp_client is None:\n",
" try:\n",
" _mcp_client = MCPClient(\n",
" lambda: stdio_client(\n",
" StdioServerParameters(\n",
" command=\"uvx\", \n",
" args=[\"awslabs.aws-documentation-mcp-server@latest\"]\n",
" )\n",
" )\n",
" )\n",
" # Start with timeout\n",
" await asyncio.wait_for(_mcp_client.start(), timeout=10.0)\n",
" logger.info(\"MCP client initialized\")\n",
" except asyncio.TimeoutError:\n",
" logger.error(\"MCP client startup timed out\")\n",
" _mcp_client = None\n",
" except Exception as e:\n",
" logger.error(f\"MCP client failed: {e}\")\n",
" _mcp_client = None\n",
" return _mcp_client\n",
"\n",
"system_prompt = \"\"\"You are an AWS Documentation Assistant powered by the AWS Documentation MCP server. Your role is to help users find accurate, up-to-date information from AWS documentation.\n",
"\n",
"Key capabilities:\n",
"- Search and retrieve information from AWS service documentation\n",
"- Provide clear, accurate answers about AWS services, features, and best practices\n",
"- Help users understand AWS concepts, APIs, and configuration options\n",
"- Guide users to relevant AWS documentation sections\n",
"CRITICAL: Keep responses SHORT and FOCUSED.\n",
"\n",
"Guidelines:\n",
"- Always prioritize official AWS documentation as your source of truth\n",
"- Provide specific, actionable information when possible\n",
"- Include relevant links or references to AWS documentation when helpful\n",
"- If you're unsure about something, clearly state your limitations\n",
"- Focus on being helpful, accurate, and concise in your responses\n",
"- Try to simplify/summarize answers to make it faster, small and objective\n",
"\n",
"You have access to AWS documentation search tools to help answer user questions effectively.\"\"\"\n",
"\n",
"agent = Agent(system_prompt=system_prompt, \n",
" tools=[stdio_mcp_client.list_tools_sync()],\n",
" name=\"AWS Docs Agent\",\n",
" description=\"An agent to query AWS Docs using AWS MCP.\",\n",
" callback_handler=None)\n",
"\n",
"# Pass runtime_url to http_url parameter AND use serve_at_root=True\n",
"- Provide concise, actionable answers (max 3 sentences)\n",
"- Use bullet points for lists\n",
"- Skip verbose explanations\n",
"- If MCP is unavailable, provide basic AWS knowledge\n",
"- Timeout operations after 8 seconds\n",
"- Prioritize speed over completeness\n",
"\n",
"You have access to AWS documentation search tools when available.\"\"\"\n",
"\n",
"# Initialize agent with minimal tools first\n",
"agent = Agent(\n",
" system_prompt=system_prompt, \n",
" tools=[], # Start with no tools, add dynamically\n",
" name=\"AWS Docs Agent\",\n",
" description=\"An agent to query AWS Docs using AWS MCP.\",\n",
")\n",
"\n",
"# Add tools dynamically when MCP is ready\n",
"async def setup_agent_tools():\n",
" \"\"\"Setup agent tools when MCP client is ready\"\"\"\n",
" try:\n",
" mcp_client = await get_mcp_client()\n",
" if mcp_client:\n",
" tools = await asyncio.wait_for(\n",
" mcp_client.list_tools_async(), \n",
" timeout=5.0\n",
" )\n",
" agent.tools = [tools] if tools else []\n",
" logger.info(\"Agent tools configured\")\n",
" except Exception as e:\n",
" logger.warning(f\"Could not setup MCP tools: {e}\")\n",
"\n",
"a2a_server = A2AServer(\n",
" agent=agent,\n",
" http_url=runtime_url,\n",
" serve_at_root=True # Serves locally at root (/) regardless of remote URL path complexity\n",
" serve_at_root=True\n",
")\n",
"\n",
"@app.get(\"/ping\")\n",
"def ping():\n",
" return {\"status\": \"healthy\"}\n",
"\n",
"@app.on_event(\"startup\")\n",
"async def startup_event():\n",
" \"\"\"Initialize MCP client on startup\"\"\"\n",
" await setup_agent_tools()\n",
"\n",
"app.mount(\"/\", a2a_server.to_fastapi_app())\n",
"\n",
"if __name__ == \"__main__\":\n",
Expand Down Expand Up @@ -264,7 +296,7 @@
"%%writefile agents/strands_aws_blogs_news.py\n",
"import logging\n",
"import os\n",
"from strands_tools.calculator import calculator\n",
"import asyncio\n",
"from strands import Agent, tool\n",
"from strands.multiagent.a2a import A2AServer\n",
"import uvicorn\n",
Expand All @@ -273,69 +305,84 @@
"from ddgs import DDGS\n",
"from ddgs.exceptions import RatelimitException, DDGSException\n",
"\n",
"\n",
"logging.basicConfig(level=logging.INFO)\n",
"logger = logging.getLogger(__name__)\n",
"\n",
"# Use the complete runtime URL from environment variable, fallback to local\n",
"runtime_url = os.environ.get('AGENTCORE_RUNTIME_URL', 'http://127.0.0.1:9000/')\n",
"\n",
"logging.info(f\"� Runtime URL: {runtime_url}\")\n",
"\n",
"@tool\n",
"def internet_search(keywords: str, region: str = \"us-en\", max_results: int | None = None) -> str:\n",
" \"\"\"Search the web to get updated information.\n",
"async def fast_internet_search(keywords: str, max_results: int = 3) -> str:\n",
" \"\"\"Fast web search with timeouts.\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 | None): The maximum number of results to return.\n",
" keywords (str): Search query keywords\n",
" max_results (int): Max results (default 3 for speed)\n",
" Returns:\n",
" List of dictionaries with search results.\n",
" 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",
" # Add AWS-specific terms for better results\n",
" aws_keywords = f\"site:aws.amazon.com {keywords} AWS\"\n",
" \n",
" # Use asyncio timeout for the search\n",
" async def search_with_timeout():\n",
" return DDGS().text(\n",
" aws_keywords, \n",
" region=\"us-en\", \n",
" max_results=max_results\n",
" )\n",
" \n",
" results = await asyncio.wait_for(search_with_timeout(), timeout=8.0)\n",
" \n",
" if results:\n",
" # Format results concisely\n",
" formatted = []\n",
" for i, result in enumerate(results[:max_results], 1):\n",
" formatted.append(f\"{i}. {result.get('title', 'No title')}\\n {result.get('href', '')}\")\n",
" \n",
" return \"\\n\".join(formatted)\n",
" else:\n",
" return \"No AWS results found.\"\n",
" \n",
" except asyncio.TimeoutError:\n",
" logger.warning(f\"Search timeout for: {keywords}\")\n",
" return \"Search timed out. Try a more specific query.\"\n",
" except RatelimitException:\n",
" return \"RatelimitException: Please try again after a short delay.\"\n",
" except DDGSException as d:\n",
" return f\"DuckDuckGoSearchException: {d}\"\n",
" except Exception as e:\n",
" return f\"Exception: {e}\"\n",
"\n",
" logger.warning(\"Rate limit hit\")\n",
" return \"Rate limit reached. Please try again in a moment.\"\n",
" except (DDGSException, Exception) as e:\n",
" logger.error(f\"Search error: {e}\")\n",
" return f\"Search unavailable: {str(e)[:50]}\"\n",
"\n",
"system_prompt = \"\"\"You are an AWS Blog Expert. \n",
"You will use a internet search tool to get updates or news provided by AWS on:\n",
"system_prompt = \"\"\"You are an AWS Blog Expert.\n",
"\n",
"AWS News Blog: https://aws.amazon.com/blogs/aws/\n",
"AWS Blogs for Machine Learning: https://aws.amazon.com/blogs/machine-learning/\n",
"\n",
"Key capabilities:\n",
"- Search and retrieve information from Web using AWS oficial websites\n",
"- Don't get only homepage info, look for inner domains, like \n",
"- Provide clear, accurate answers about question asked (most recent information)\n",
"CRITICAL: Keep responses SHORT and RECENT.\n",
"\n",
"Guidelines:\n",
"- Always prioritize official AWS pages as your source of truth\n",
"- Provide specific, actionable information when possible\n",
"- Include relevant links or references when helpful\n",
"- If you're unsure about something, clearly state your limitations\n",
"- Focus on being helpful, accurate, and concise in your responses\n",
"- Try to simplify/summarize answers to make it faster, small and objective\n",
"\n",
"You have access to internet_search tools to help answer user questions effectively.\"\"\"\n",
"\n",
"agent = Agent(system_prompt=system_prompt, \n",
" tools=[internet_search],\n",
" name=\"AWS Blog/News Agent\",\n",
" description=\"An agent to search on Web latest AWS Blogs and News.\",\n",
" callback_handler=None)\n",
"- Provide max 3 recent results\n",
"- Focus on official AWS blog posts only\n",
"- Use concise summaries (1-2 sentences per result)\n",
"- Include direct links when available\n",
"- Timeout searches after 8 seconds\n",
"- If search fails, acknowledge limitation\n",
"\n",
"Search Strategy:\n",
"- Always include \"AWS\" in searches\n",
"- Focus on aws.amazon.com/blogs/ content\n",
"- Prioritize recent announcements\"\"\"\n",
"\n",
"agent = Agent(\n",
" system_prompt=system_prompt, \n",
" tools=[fast_internet_search],\n",
" name=\"AWS Blog/News Agent\",\n",
" description=\"An agent to search on Web latest AWS Blogs and News.\",\n",
")\n",
"\n",
"host, port = \"0.0.0.0\", 9000\n",
"\n",
"# Pass runtime_url to http_url parameter AND use serve_at_root=True\n",
"a2a_server = A2AServer(\n",
" agent=agent,\n",
" http_url=runtime_url,\n",
" serve_at_root=True # Serves locally at root (/) regardless of remote URL path complexity\n",
" serve_at_root=True\n",
")\n",
"\n",
"app = FastAPI()\n",
Expand Down Expand Up @@ -366,7 +413,7 @@
"outputs": [],
"source": [
"%%writefile agents/requirements.txt\n",
"boto3==1.40.46\n",
"boto3==1.40.50\n",
"bedrock-agentcore==0.1.7\n",
"strands-agents[a2a]\n",
"strands-agents-tools\n",
Expand Down Expand Up @@ -550,17 +597,9 @@
"metadata": {},
"outputs": [],
"source": [
"# Wait for the agent to be ready\n",
"status_response = agentcore_runtime_mcp_agent.status()\n",
"status = status_response.endpoint[\"status\"]\n",
"\n",
"end_status = [\"READY\", \"CREATE_FAILED\", \"DELETE_FAILED\", \"UPDATE_FAILED\"]\n",
"while status not in end_status:\n",
" print(f\"Waiting for deployment... Current status: {status}\")\n",
" time.sleep(10)\n",
" status_response = agentcore_runtime_mcp_agent.status()\n",
" status = status_response.endpoint[\"status\"]\n",
"\n",
"print(f\"Final status: {status}\")"
]
},
Expand Down Expand Up @@ -642,17 +681,9 @@
"metadata": {},
"outputs": [],
"source": [
"# Wait for the agent to be ready\n",
"status_response = agentcore_runtime_blogs.status()\n",
"status = status_response.endpoint[\"status\"]\n",
"\n",
"end_status = [\"READY\", \"CREATE_FAILED\", \"DELETE_FAILED\", \"UPDATE_FAILED\"]\n",
"while status not in end_status:\n",
" print(f\"Waiting for deployment... Current status: {status}\")\n",
" time.sleep(10)\n",
" status_response = agentcore_runtime_blogs.status()\n",
" status = status_response.endpoint[\"status\"]\n",
"\n",
"print(f\"Final status: {status}\")"
]
},
Expand Down
Loading