Optional add-on
Optional: Browser
Let the agent read public web pages through a managed, sandboxed headless browser.
The “What Are Competitors Doing?” request
Before a board meeting Alice asks: “What does this vendor say on their product page?” The database can't answer that. AgentCore Browser gives the agent a managed headless Chromium in the cloud, isolated from your runtime.
What the platform provisions for you
ENABLE_BROWSER=trueuv run bootcamp.py deploy- Session permissions for your agent role on the AWS-managed
aws.browser.v1 - The
browse_webtool, handed to your orchestrator automatically
Flags default to false and need stage 4 or higher. uv run bootcamp.py status shows CODE_INTERPRETER_ENABLED and BROWSER_ENABLED.
What you do as a developer
Read the tool
phase2/app/agent/sandbox_tools.py@tool def browse_web(url: str) -> str: """Open a public web page in a secure AgentCore Browser sandbox and return its title, final URL and visible text. Args: url: Full http(s) URL of the page to open. """ if urlparse(url).scheme not in {"http", "https"}: return "Only http(s) URLs are supported." with browser_session(REGION, identifier=BROWSER_ID) as client: ws_url, headers = client.generate_ws_headers() signed = {name: value for name, value in headers.items() if name in SIGNED_HEADERS} with connect(ws_url, additional_headers=signed, max_size=None, open_timeout=CDP_TIMEOUT_SECONDS) as socket: page = read_page(CdpConnection(socket), url) return f"Title: {page['title']}\nURL: {page['url']}\n\n{page['text']}"No Playwright: the tool talks Chrome DevTools Protocol over the browser's SigV4-signed websocket (four CDP commands: open a tab, navigate, wait for load, read title and text). It returns the title, the final URL and the first 4,000 characters of visible text.
terminaluv run bootcamp.py invoke "Open https://example.com and tell me the page title." --actor alice-chenAdd a research specialist
Challenge
In
phase2/app/agent/agent.py, add aresearch_agentspecialist that reads public pages withbrowse_weband summarises them with the source URL, and register it with the orchestrator.Where: edit
phase2/app/agent/agent.pyin place, runuv run bootcamp.py deploy(~1.5 min), then check withuv run bootcamp.py invoke "Summarise https://aws.amazon.com/bedrock/agentcore/ in three bullets" --actor alice-chen --stream: the tool line shows[calling research_agent], notbrowse_web.Hint 1
Same pattern as
weather_agent, different tool. AgentCore Browser docs.Hint 2
Import
browse_webfromsandbox_tools, passtools=[browse_web], and put guardrails in the prompt: cite the URL, don't guess content the page didn't show.Solution
phase2/app/agent/agent.py (adds research_agent) · replace build_specialists()def build_specialists(gateway: MCPClient, results: list[str]) -> list: """The data and weather specialists as tools; each searches the Gateway for its tools, then records its answer.""" @tool def data_agent(query: str) -> str: """Query and analyze the DataStream Corp database (employees, departments, projects). Args: query: A database or data analysis question. """ agent = Agent( model=make_model(), system_prompt=f"{DATA_PROMPT}\n{guardrail.prompt_hint()}", tools=search_tools(gateway, DATA_TOOLS), hooks=[ReadOnlyGuardHook(), *guardrail.hooks()], callback_handler=None, ) return consult(agent, query, "data") @tool def weather_agent(query: str) -> str: """Get the weather forecast for a US location (National Weather Service); says so for non-US places. Args: query: A weather-related question, including the place. """ agent = Agent( model=make_model(), system_prompt=nws_weather.WEATHER_PROMPT, hooks=guardrail.hooks(), tools=search_tools(gateway, WEATHER_TOOLS), callback_handler=None, ) return consult(agent, query, "weather") @tool def research_agent(question: str) -> str: """Research public web pages (product pages, news, docs) and summarise them with their URL. Args: question: What to find out, including the URL if known. """ from sandbox_tools import browse_web agent = Agent( model=make_model(), system_prompt="You research public web pages with browse_web. Cite the URL you used. Never guess content.", tools=[browse_web], hooks=guardrail.hooks(), callback_handler=None, ) return consult(agent, question, "research") def consult(agent: Agent, query: str, specialist: str) -> str: """Ask a specialist; an exception (e.g. the gateway's 502 after its retries) becomes an explicit failure.""" try: answer = str(agent(query)) except Exception as error: answer = SPECIALIST_FAILED.format(specialist=specialist, reason=f"{type(error).__name__}: {error}") return remember(answer, specialist) def remember(answer: str, specialist: str = "requested") -> str: """Record a specialist's answer for `final_answer`; an empty one is recorded as an explicit failure.""" answer = answer.strip() or SPECIALIST_FAILED.format(specialist=specialist, reason="it returned an empty answer") results.append(answer) return answer return [data_agent, weather_agent, research_agent]If you already added
analysis_agent(Code Interpreter page), keep it: paste only the newresearch_agentfunction and add it to the returned list. Then add a routing line toorchestrator_prompt, e.g."Route questions about public web pages to research_agent and keep the URL it cites in your answer.\n".One more change, or your specialist never runs: with
ENABLE_BROWSER=truethe orchestrator itself also getsbrowse_web(plus a prompt hint to use it), and a model given the tool and a specialist picks the tool. Takebrowse_webaway from the orchestrator inbuild_orchestrator;research_agentimports it directly, and the hint goes with it.phase2/app/agent/agent.py (browse_web only in research_agent) · replace build_orchestrator()def build_orchestrator(gateway: MCPClient, session_id: str, actor_id: str, specialist_results: list[str]) -> Agent: """The orchestrator with both specialists, any enabled sandbox tools and this actor's memory.""" sandbox_tools = [t for t in optional_tools() if t.tool_name != "browse_web"] # research_agent has it return Agent( name="Executive Assistant", model=make_model(), system_prompt=orchestrator_prompt(actor_id, sandbox_tools), hooks=guardrail.hooks(), tools=[*build_specialists(gateway, specialist_results), *sandbox_tools], session_manager=memory_session_manager(session_id, actor_id), callback_handler=None, )Check with
invoke --stream(ortraces) thatresearch_agentis the tool called, notbrowse_web.Deploy and research
terminaluv run bootcamp.py deploy uv run bootcamp.py invoke "Summarise https://aws.amazon.com/bedrock/agentcore/ in three bullets" --actor alice-chenExperiments
- Compare
browse_webwith the weather agent'sget_us_forecast(one purpose-built API call): which pages need a real browser (JavaScript-rendered content)? - Only http(s) URLs are accepted. What else would you restrict (domains, page size) and where: prompt, tool or policy?
- Check the cost of a browsing question with
uv run bootcamp.py llm.
See it in the console: AgentCore → Observability: the trace of a research question: the
research_agentspan with itsbrowse_webcall inside.- Compare
Check your work
With the flag on, test --only 4 adds a browser tool check. It sends exactly this prompt:
uv run bootcamp.py invoke "DataStream's network team needs the current AWS IP ranges version: use the browse_web tool (directly or through your research specialist) to open https://ip-ranges.amazonaws.com/ip-ranges.json and reply with the exact value of its syncToken field." --actor alice-chenIt passes when the answer contains the live syncToken of that file: a ~10-digit number such as 1791300000 that AWS changes several times a week, so the model can't know it without loading the page. The check fetches the file itself before and after the agent runs and accepts either value.
uv run bootcamp.py test --only 4[PASS] stage 4 ...
[PASS] stage 4 browser tool: The syncToken is 1791300000.The check asks as a fresh actor in a fresh session and retries once on an empty answer. The prompt is framed as DataStream work so a Task 4 “decline off-topic requests” prompt keeps it; if your prompt still refuses it, loosen the scope rule.
This check depends on the model's wording, which varies from run to run. If it fails, rerun it once; check with uv run bootcamp.py traces --args which tool ran (the trace, not the wording, says whether the agent did the work); if the shipped agent does the same (git stash, deploy, rerun, git stash pop, deploy), it's the model, not your change.
Under the hood
Both tools use the AWS-managed built-ins aws.codeinterpreter.v1 and aws.browser.v1. Every call starts a fresh, isolated sandbox session and stops it afterwards, so nothing leaks between participants or requests. When a flag is on, your agent role gets only the session permissions it needs, in the inline policy awsworkshop-<you>-sandbox-tools; the platform sets CODE_INTERPRETER_ENABLED / BROWSER_ENABLED on the runtime, and optional_tools() plus prompt_hints() hand the tools to the orchestrator with a one-line usage hint each.
def optional_tools() -> list:
"""The sandbox tools this runtime is configured for (empty when both capabilities are off)."""
tools = []
if capability_enabled("CODE_INTERPRETER_ENABLED"):
tools.append(run_python)
if capability_enabled("BROWSER_ENABLED"):
tools.append(browse_web)
return tools