Build an AI Research Agent with Web Search on Amazon Bedrock AgentCore
A working demo using Strands Agents SDK to connect an AI agent to the fully managed Web Search tool on AgentCore Gateway, with zero data egress, and server-side domain filtering for regulated industries
Build an AI Research Agent with Web Search on Amazon Bedrock AgentCore
AWS announced the general availability of Web Search on Amazon Bedrock AgentCoreΒ , a fully managed tool that enables agents to ground responses in current, cited web knowledge with zero data egress from customerβs secured AWS environment.
Web Search uses a built-in connector target on Bedrock AgentCore Gateway using the Model Context Protocol (MCP). Your agent sends a natural-language query, and Web Search returns most relevant snippets, source URLs, titles, and publication dates that the model can reason over to produce a grounded response. This post walks through a working demo that connects a Strands Agents SDK agent to Web Search via AgentCore Gateway, with an industry lens on why zero-egress web grounding matters for regulated workloads.
Why Agents Need Real-Time Web Grounding
Every production AI agent eventually needs to answer questions about right now, because training data has a shelf life that gets shorter every quarter. Stock prices change by the minute, regulatory filings drop without warning, and product launches happen daily.
Adding web search to an agent typically means managing external API keys, handling data residency considerations, and transforming raw HTML into something an LLM can consume. Web Search on AgentCore simplifies this into a single managed MCP-compatible tool that returns semantic snippets and knowledge graph facts, purpose-built for LLM consumption, with all queries staying within AWS infrastructure.
The Industry Lens
This is a compliance and governance story as much as a developer productivity one.
| Industry | What Zero-Egress Web Search Unlocks |
|---|---|
| Financial Services | Market data grounding without query leakage to third parties. Regulatory filings stay within your AWS boundary. |
| Healthcare & Life Sciences | Research agents pull the latest clinical trial results, drug approvals, and guidelines without PHI-adjacent queries leaving AWS. |
| Legal | Case law lookups and regulatory change monitoring with a full audit trail via CloudTrail. |
| Media & Publishing | Fact-checking agents grounded in current events, with domain filtering that excludes unreliable sources at the infrastructure layer. |
| Retail & CPG | Competitive intelligence agents track pricing, reviews, and product launches in real-time without exposing search patterns externally. |
Domain filtering is enforced server-side at the Gateway level, which means the LLM never sees results from blocked domains. This is governance you cannot achieve with client-side filtering, because your compliance team can approve a domain allowlist and know it is enforced before the model ever processes a response.
Architecture

The agent communicates with the Gateway over standard MCP protocol (
tools/list + tools/call), authenticated. While this demo uses Strands Agents, any MCP-compatible client works identically.Prerequisites
You need an AgentCore Gateway with the Web Search connector attached. Follow the Create a Gateway guide and add the Web Search managed connector as a targetΒ . Once created, copy your Gateway URL, which will look like:
1
https://<your-gateway-id>.gateway.bedrock-agentcore.<region>.amazonaws.com/mcpInstall the Python dependencies:
1
pip install strands-agents mcp botocore httpxCode Walkthrough
SigV4 Authentication
AgentCore Gateway supports multiple authorizer types. This demo uses
AWS_IAM for simplicity, but you can configure OAuth/Cognito-based auth for multi-tenant or external client scenarios. See the Gateway authorization documentationΒ for all options.The MCP SDK's
streamablehttp_client accepts an httpx.Auth subclass, which makes SigV4 signing straightforward:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import httpx
import botocore.session
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
class SigV4HttpxAuth(httpx.Auth):
def __init__(self):
session = botocore.session.get_session()
self._credentials = session.get_credentials()
def auth_flow(self, request: httpx.Request):
creds = self._credentials.get_frozen_credentials()
aws_request = AWSRequest(
method=request.method,
url=str(request.url),
headers={k: v for k, v in request.headers.items()},
data=request.content,
)
SigV4Auth(creds, "bedrock-agentcore", "us-east-1").add_auth(aws_request)
for key, value in aws_request.headers.items():
request.headers[key] = value
yield requestThis class intercepts every outgoing HTTP request, signs it with your local AWS credentials, and injects the authorization headers before the request hits the Gateway. The
yield request pattern is how httpx's auth flow works: you modify the request in-flight and pass it along.Connecting to the Gateway
The MCP client wraps the signed HTTP transport and exposes every tool configured on the Gateway (in this case, Web Search) to the Strands agent:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from mcp.client.streamable_http import streamablehttp_client
from strands import Agent
from strands.tools.mcp import MCPClient
from strands.models.bedrock import BedrockModel
GATEWAY_URL = "https://<your-gateway-id>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp"
def create_signed_client():
return streamablehttp_client(url=GATEWAY_URL, auth=SigV4HttpxAuth())
web_search = MCPClient(create_signed_client)
agent = Agent(
model=BedrockModel(model_id="us.anthropic.claude-sonnet-4-6", region_name="us-east-1"),
tools=[web_search],
system_prompt="You are a research assistant with access to web search. Always search before answering.",
)Using the Agent
Once the agent is initialized, every query automatically triggers a web search before generating a response:
1
2
3
4
5
6
7
8
# Real-time grounding: answers about events from today
response = agent("What new features did AWS announce this week?")
# Knowledge graph: high-confidence entity facts
response = agent("Who is the CEO of Amazon and when was the company founded?")
# Multi-query synthesis: the agent searches multiple times and combines results
response = agent("Compare the market cap of Apple, Microsoft, and NVIDIA as of today")The agent decides how many results to request (5 for factual lookups, 10-15 for research, 20-25 for surveys) and whether to issue multiple searches for comparison queries. All of this behavior comes from the system prompt, not from hard-coded logic.
What the Demo Shows
| Capability | How It Works |
|---|---|
| Real-time grounding | The web index refreshes within minutes of publication |
| Semantic snippets | Returns relevant passages optimized for LLM context, not raw HTML |
| Knowledge graph | Entity lookups (CEO, founding date, market cap) with high confidence |
| Source citations | Every answer includes [Title](URL) inline citations |
| Domain filtering | Configured once at the Gateway, enforced server-side on every query |
| Zero data egress | All queries stay within AWS infrastructure |
| maxResults control | The agent adjusts result count (5-25) based on query complexity |
Domain Filtering Configuration
When creating the Web Search target, you can exclude domains that your organization considers unreliable or off-limits:
1
2
3
4
5
"parameterValues": {
"domainFilter": {
"exclude": ["reddit.com", "twitter.com", "tiktok.com"]
}
}This filtering happens at the Gateway before results reach the model, so there is no prompt injection path that can bypass it. For a healthcare company, you might exclude social media entirely and only allow results from
.gov, PubMed, and approved journal domains. For a financial services firm, you might block user-generated content sites and allow only SEC filings, Bloomberg, and Reuters.Full Demo Code
Here is the complete
websearch_agent_demo.py you can copy and run directly:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
"""
Demo: Web Search on Amazon Bedrock AgentCore
A Strands Agents demo showing how an AI agent uses the fully managed
Web Search tool on AgentCore Gateway to ground responses in current,
cited web knowledge β with zero data egress from AWS.
"""
import httpx
import botocore.session
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
from mcp.client.streamable_http import streamablehttp_client
from strands import Agent
from strands.tools.mcp import MCPClient
from strands.models.bedrock import BedrockModel
# =============================================================================
# CONFIGURATION
# =============================================================================
GATEWAY_URL = "https://<your-gateway-id>.gateway.bedrock-agentcore.<region>.amazonaws.com/mcp"
AWS_REGION = "us-east-1"
AWS_SERVICE = "bedrock-agentcore"
MODEL = BedrockModel(
model_id="us.anthropic.claude-sonnet-4-6",
region_name="us-east-1",
)
# =============================================================================
# SigV4 Auth
# =============================================================================
class SigV4HttpxAuth(httpx.Auth):
def __init__(self):
session = botocore.session.get_session()
self._credentials = session.get_credentials()
def auth_flow(self, request: httpx.Request):
creds = self._credentials.get_frozen_credentials()
aws_request = AWSRequest(
method=request.method,
url=str(request.url),
headers={k: v for k, v in request.headers.items()},
data=request.content,
)
SigV4Auth(creds, AWS_SERVICE, AWS_REGION).add_auth(aws_request)
for key, value in aws_request.headers.items():
request.headers[key] = value
yield request
def create_signed_client():
return streamablehttp_client(url=GATEWAY_URL, auth=SigV4HttpxAuth())
# =============================================================================
# SYSTEM PROMPT
# =============================================================================
SYSTEM_PROMPT = """You are a research assistant with access to web search.
## How You Work
1. ALWAYS search before answering. Never rely on training data for facts.
2. For comparisons, make MULTIPLE searches and synthesize.
3. Use maxResults strategically: 5 for factual lookups, 10-15 for research, 20-25 for surveys.
## Response Format
- Keep answers concise: max 5-8 bullet points.
- Cite claims inline as: [Title](URL)
- No separate "Sources" section at the end.
"""
# =============================================================================
# DEMO QUERIES
# =============================================================================
DEMO_QUERIES = [
{
"label": "π΄ REAL-TIME GROUNDING",
"query": "What new features did AWS announce this week?",
"feature": "Freshness β index refreshed within minutes of publication",
},
{
"label": "π‘ KNOWLEDGE GRAPH",
"query": "Who is the CEO of Amazon and when was the company founded?",
"feature": "Built-in knowledge graph for high-confidence entity facts",
},
{
"label": "π’ MULTI-QUERY SYNTHESIS",
"query": "What are the top 3 programming languages in 2025 and why?",
"feature": "Agent searches multiple times and synthesizes across sources",
},
{
"label": "π΅ SEMANTIC SNIPPETS",
"query": "How does Amazon Bedrock AgentCore Web Search work?",
"feature": "Returns relevant passages optimized for LLM context, not raw HTML",
},
{
"label": "π£ STALE KNOWLEDGE FIX",
"query": "What is the latest stable version of Python and its key features?",
"feature": "Current facts beyond model training cutoff",
},
]
def main():
web_search = MCPClient(create_signed_client)
agent = Agent(
model=MODEL,
tools=[web_search],
system_prompt=SYSTEM_PROMPT,
)
print("""
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β Web Search on Amazon Bedrock AgentCore β
β ββββββββββββββββββββββββββββββββββββββ β
β Strands Agents SDK Demo β
β β
β Features: β
β β’ Real-time web index (refreshed within minutes) β
β β’ Knowledge graph for entity facts β
β β’ Semantic snippet extraction β
β β’ Domain governance (server-side denylist) β
β β’ Zero data egress β queries stay in AWS β
β β’ MCP-compliant tool discovery β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
""")
print("Choose mode:")
print(" [1] Guided demo (pre-built queries showcasing each feature)")
print(" [2] Interactive (ask anything)")
print()
mode = input("Select (1/2): ").strip()
if mode == "1":
run_guided_demo(agent)
else:
run_interactive(agent)
def run_guided_demo(agent: Agent):
print("\n" + "=" * 60)
print("GUIDED DEMO β Each query highlights a Web Search feature")
print("=" * 60)
for i, demo in enumerate(DEMO_QUERIES, 1):
print(f"\n{'β' * 60}")
print(f" Demo {i}/5: {demo['label']}")
print(f" Feature: {demo['feature']}")
print(f"{'β' * 60}")
print(f"\n Query: \"{demo['query']}\"\n")
input(" Press Enter to run... ")
print()
response = agent(demo["query"])
print(f"\n{response}")
if i < len(DEMO_QUERIES):
input("\n Press Enter for next demo... ")
print("\n" + "=" * 60)
print("DEMO COMPLETE")
print("=" * 60)
print("""
Features demonstrated:
β Real-time grounding β Knowledge graph facts
β Multi-query synthesis β Semantic snippets
β Stale knowledge fix β Fact verification
β Zero data egress β Domain filtering (infra)
β MCP tool discovery β Framework-agnostic
""")
def run_interactive(agent: Agent):
print("\n Ready. Type 'quit' to exit.\n")
while True:
query = input("You: ").strip()
if query.lower() in ("quit", "exit", "q"):
break
if not query:
continue
response = agent(query)
print(f"\n{response}\n")
if __name__ == "__main__":
main()
Resources
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article