Agent class with first-class Bedrock support and a callable run interface. xpander supplies the agent’s identity (instructions, skills, model, knowledge bases); this page wires the two together. In the SDK, skills are exposed as tools, the name agent loops use for them. This SDK integration is optional: agents created in Xpander Chat or through the API run on Claude Code, Codex or OpenCode by default.
The steps below run a native strands.Agent whose instructions, skills and model come from the xpander agent definition.
What doesn’t come built in
Unlike the Agno path, Strands doesn’t have a one-call shortcut for pulling everything in at once, so we grab the agent definition from xpander and hand the pieces to Strands ourselves. It’s only a few extra lines, but a few capabilities aren’t auto-wired and you wire them yourself:Prerequisites
- Complete the Quickstart so the CLI, SDK, and
xpander loginare already set up. - Python 3.12+ for the local handler.
- AWS credentials in your shell (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_REGION, or an instance profile). Strands defaults to AWS Bedrock whenmodel=is a string. If you wire a non-Bedrock client instead, an alternate provider key (OPENAI_API_KEY,ANTHROPIC_API_KEY) takes its place. Strands does not pick up the LLM credentials configured on the agent in Xpander Chat; if you’ve set a custom key on the agent, mirror it into your.envso the runner uses it.
1. Install
Both packages are required. Strands ships under thestrands-agents distribution but imports as strands.
2. Set up scaffolding
xpander_config.json reference
xpander_config.json
3. Create task handler
The full pattern, wrapped in@on_task so xpander routes tasks to it. The highlighted lines are the integration’s load-bearing reads:
xpander_handler.py
Agents(configuration=task.configuration).aget(agent_id=task.agent_id)calls the xpander control plane and returns a fully-hydratedAgentobject. Its instructions, skill repository, model, and knowledge-base links are all populated.xpander_agent.instructions.fullis a single string that wraps the agent’sgeneraldescription,rolelist, andgoallist in<description>,<instructions>, and<goals>tags. Drop it straight into Strands’system_prompt=kwarg (note the kwarg name; it isn’tinstructions=).xpander_agent.strands_toolsis a computed property that wraps every xpander skill (catalog skills, custom@register_toolfunctions, skills served over MCP) with@strands.tool. Each wrapper’s underlying callable invokes xpander’s skill execution path, so connected-account auth, observability, and retries still work.xpander_agent.model_nameis the model identifier configured on the agent (e.g.anthropic.claude-sonnet-4-5-20250929-v1:0,gpt-4o). Strands wraps a string asBedrockModel(model_id=...)automatically. For non-Bedrock providers, swap in an explicit model client (see the Troubleshooting section).native.invoke_async(task.to_message())drives the LLM loop.task.to_message()returns the task’s user message in the shape Strands expects.- Writing back to
task.resultlets xpander store the output and surface it in the API, Xpander Chat, and any wired channels.str(result)concatenates the text blocks fromresult.messageinto a single string.
4. Edit the agent’s system prompt
agent_instructions.json contains the agent’s system prompt and has exactly three fields:
agent_instructions.json
xpander agent dev syncs it to the control plane. general is also exposed as xpander_agent.instructions.description, which the handler passes to Strands’ description= kwarg so other agents that wrap this one as a skill see the right summary.
5. Wire knowledge-base retrieval (optional)
Strands doesn’t auto-wire xpander’s knowledge bases, so expose the retriever as a@strands.tool the agent can call. The highlighted lines show the two integration points: building the retriever and concatenating it onto the auto-wired skill list.
xpander_handler.py
6. Set up streaming (optional)
For token-by-token output, decorate anasync def that yields TaskUpdateEvent objects instead of returning a Task. The decorator detects the difference automatically. Strands exposes agent.stream_async(...), which yields a sequence of dict events; text deltas arrive on events that carry a "data" key.
streaming_handler.py
native.stream_async(task.to_message())returns an async iterator. Each event is a dict; text deltas carry a"data"key, skill-use events carry"current_tool_use", and a final completion event carries theAgentResultunder"result".- The
Chunkevent forwards each text delta to xpander’s SSE stream so clients render output as it arrives. - The
TaskFinishedevent signals the end of the stream and carries the final task back to xpander.
POST /invoke, returning Server-Sent Events. xpander’s SSE listener for cloud-deployed agents expects a regular handler that returns a Task. So if you need both an interactive streaming experience and xpander-routed tasks, run two handlers, or have your streaming endpoint proxy through a regular handler.
7. Test local development
Run the handler with the dev server. Tasks created from any channel (REST, Slack, Xpander Chat) route to your laptop:--output_format and --output_schema are useful for testing structured output without changing the agent’s settings in the control plane.
Troubleshooting
Why doesn't Backend.aget_args() work for Strands?
Why doesn't Backend.aget_args() work for Strands?
Backend.aget_args() currently dispatches only to the Agno builder and raises NotImplementedError for any other agent loop. For Strands, you load the Agent yourself with Agents().aget(...) and read the fields you need (instructions, skills, model name) onto Strands’ Agent constructor.TypeError: Agent.__init__() got an unexpected keyword argument 'instructions'
TypeError: Agent.__init__() got an unexpected keyword argument 'instructions'
Strands names the system-prompt kwarg
system_prompt=, not instructions=. Pass system_prompt=xpander_agent.instructions.full to the Agent constructor. The xpander side reads from instructions (the Pydantic field on the SDK’s Agent); the Strands side accepts system_prompt. The two are not the same kwarg.Wrong model, wrong region, or NoCredentialsError from boto3
Wrong model, wrong region, or NoCredentialsError from boto3
Strands wraps a string
model= as BedrockModel(model_id=...) and the underlying boto3 client reads standard AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, or an instance profile). It does not pick up a custom LLM key configured on the agent in Xpander Chat. If the runner can’t authenticate or hits the wrong region, set the AWS env vars in your local .env and confirm the model ID is enabled in that region’s Bedrock model catalog.How do I use a non-AWS model?
How do I use a non-AWS model?
Pass an explicit Strands model client instead of a string:Any model provider works as long as the underlying model supports function calling for the integration to work end to end.
How do I keep conversation history across turns?
How do I keep conversation history across turns?
Strands has its own
SessionManager and conversation_manager for in-process history. For durable cross-process state, use Strands’ built-in session managers (or a custom one), or switch to the Agno integration for an auto-wired Postgres store. The convenience helpers xpander_agent.get_user_sessions() and xpander_agent.get_session() raise NotImplementedError outside Agno.Why does the skill input show payload as the top-level field?
Why does the skill input show payload as the top-level field?
The
strands_tools wrapper’s input schema is {"payload": <tool.parameters>}, so the LLM is asked to nest its skill arguments under payload. This mirrors how xpander stores skill schemas internally and keeps the same shape across every SDK integration. You don’t need to do anything in your handler; the wrapper unpacks payload before invoking the skill.Next steps
Quickstart
The 10-minute scaffold-to-deploy walkthrough that produced the handler shown above.
Custom skills
Wrap private APIs as skills with
@register_tool and ship them through strands_tools.Compare with Agno
What you’d gain by switching: session storage, knowledge-base auto-wiring,
Backend.aget_args().Core Concepts
The SDK class names mapped onto agents, tasks, threads, and memory.
SDK integrations overview
What’s auto-wired vs. manual for Agno, OpenAI Agents SDK, LangChain, and AWS Strands.

