Skip to main content

Strands Research Agent

Research assistant with MCP web-fetch tools, a LiteLLM-backed model, and snapshot-based checkpointing.

Difficulty: Intermediate

What You'll Learn​

  • Explicit framework selection via mav-agent-config.yaml — the one place this field is load-bearing rather than an override
  • register_agent() for schema extraction and runner resolution
  • MCP tool integration via agentvisor.strands.mcp.get_mcp_tools()
  • LiteLLM-backed model configuration — the recommended route to Bedrock
  • Snapshot-based multi-turn conversation via agent.take_snapshot()/load_snapshot()
  • Credential brokering for ANTHROPIC_API_KEY (never exposed in the sandbox)

Prerequisites​

  • AgentVisor CLI installed and an Anthropic API key
  • uvx on your PATH (used by the MCP fetch tool — pip install uv)
  • --sandbox=none below runs directly on the host, not in a sandbox — it needs the Temporal CLI and the Python virtual environment + SDK wheel setup from Local Development Setup

Setup​

agentvisor template create strands/research-agent
cd research-agent

export ANTHROPIC_API_KEY=your-api-key-here
temporal server start-dev &
agentvisor serve . --sandbox=none

Test It​

THREAD=$(curl -sX POST http://localhost:8090/threads | jq -r '.thread_id')

# Research a topic
curl -sX POST "http://localhost:8090/threads/$THREAD/runs?wait=120s" \
-H "Content-Type: application/json" \
-d '{"input": {"message": "Research the history of the Python programming language"}}' \
| jq '.output'

# Follow-up — agent remembers the previous research via its Strands snapshot
curl -sX POST "http://localhost:8090/threads/$THREAD/runs?wait=120s" \
-H "Content-Type: application/json" \
-d '{"input": {"message": "What were the key design decisions that made Python popular?"}}' \
| jq '.output'

How It Works​

  1. Selection: mav-agent-config.yaml explicitly sets framework.provider: "strands" — Strands has no discovery file for AgentVisor™ to auto-detect
  2. Execution: python3 -m agentvisor.strands.runner resolves root_agent from agent.py
  3. Callback silencing: The runner replaces Strands' default stdout-printing callback handler with a no-op, since it would otherwise corrupt the runner's JSON output contract
  4. Research: The agent calls MCP fetch tools → MCP Gateway → policy check → mcp-server-fetch (stdio) → content returned
  5. Model calls: LiteLLM routes requests to api.anthropic.com through the AgentVisor HTTP proxy for credential substitution
  6. State persistence: agent.take_snapshot(preset="session") is saved via checkpoint.save() after each run and restored via checkpoint.load("")/agent.load_snapshot() on the next run in the same thread

Credential Flow​

Agent (Strands Agent + LiteLLMModel)
└─► LiteLLM → api.anthropic.com
└─► Guest Proxy (TLS termination)
└─► Host Proxy (token substitution: ANTHROPIC_API_KEY)
└─► api.anthropic.com (real key)

The real ANTHROPIC_API_KEY is never visible inside the guest sandbox.

Checkpointing​

AspectDetail
Mechanismagent.take_snapshot(preset="session") / agent.load_snapshot()
Storageagentvisor.checkpoint.save()/load(), Temporal-backed, per thread
ScopeOne snapshot per thread — "session" preset covers messages, state, conversation_manager_state, interrupt_state, model_state
CompatibilityNot cross-compatible with the LangGraph AgentVisorCheckpointer — see Checkpointing

Project Files​

strands/research-agent/
├── agent.py # Root agent: Strands Agent with MCP fetch tools
├── mav-agent-config.yaml # REQUIRED: selects the strands framework provider
├── requirements.txt # Python dependencies
├── agentvisor.yaml # AgentVisor config (proxy creds + MCP server)
└── policies/
└── domain.yml # Authorization policies

mav-agent-config.yaml​

framework:
provider: "strands"

agent.py (key section)​

from strands import Agent
from strands.models.litellm import LiteLLMModel
from agentvisor.strands import register_agent
from agentvisor.strands.mcp import get_mcp_tools

root_agent = Agent(
name="researcher",
model=LiteLLMModel(client_args={}, model_id="anthropic/claude-haiku-4-5-20251001"),
system_prompt="...",
tools=get_mcp_tools("fetch"),
)
register_agent(root_agent)

LiteLLMModel is used here rather than a direct Anthropic or Bedrock model — it's the recommended route to Bedrock, and this example simply points it at Anthropic's API directly for a runnable demo with no AWS credentials required. See the Strands Agents Guide for the full model-provider guidance, including why raw BedrockModel/boto3 is a documented known limitation behind AgentVisor's TLS-terminating proxy.

Next Steps​