Skip to main content
This page builds an agent end-to-end from the command line. No app, no clicking. By the end, you’ll have:
  1. Created the agent in xpander’s control plane.
  2. Run its handler locally against real prompts.
If you’d rather start in Xpander Chat, the User guide walks through the same flow inside Xpander Chat. Agents created there run on Claude Code, Codex or OpenCode by default; the SDK path on this page runs an Agno agent loop in your own process instead.

Prerequisites

  • Node.js 20+ for the CLI.
  • Python 3.12+ for the local dev server.
  • An LLM provider key in your shell (OPENAI_API_KEY, ANTHROPIC_API_KEY, or whichever provider you’ll use). The agent runs against this key locally; in production, you can override it from the agent config.

1. Install and authenticate

All three commands are required:
  1. The CLI ships via npm.
  2. The SDK ships via pip.
  3. login writes credentials that both the CLI and the SDK read.

2. Create the agent

xpander agent new does two things at once:
  1. It creates the agent in xpander’s control plane (giving it an ID, a default model, and an empty skill list).
  2. It scaffolds the project files into the folder you point at.
Drop the flags to use the interactive wizard, which asks for the same three values one at a time. When the command finishes, the current directory contains:
Set up a virtual environment and install the dependencies:

3. Read the handler

The scaffolded xpander_handler.py is the canonical pattern for an xpander agent. Every example in the rest of these docs is a variation on this:
xpander_handler.py
Here’s what’s happening:
  1. Backend(...).aget_args(task=task) calls the xpander control plane and returns a dict containing the full agent configuration: instructions, skills (passed to Agno as tools, the name agent loops use for them), model with credentials, knowledge bases, session storage, memory settings.
  2. That dict gets splatted into agno.agent.Agent(...), which gives you a real Agno agent ready to run.
  3. You run it with arun(), capture the output, and write it back to task.result.
  4. The @on_task decorator stands up an HTTP server on port 59321 and subscribes to xpander’s task event stream, so any task created for this agent (from the API, Slack, Xpander Chat, anywhere) is routed to your handler.

4. Run it locally

The simplest “just boot it” command, copy-paste with zero typing:
Tasks created for this agent (from any channel) will route to your handler. Hit Ctrl+C to stop. For a one-shot test with a specific prompt, no server:
For an interactive dev session with extra CLI affordances (auto-reload prompts, log formatting):
What you get:
  1. A local URL you can chat with.
  2. Once your dev process is running, every task created for this agent (REST API, Slack, Xpander Chat, MCP, any channel) is routed to your local handler, not just locally-initiated tests.
  3. To iterate: edit xpander_handler.py, save, restart.
Routing cloud traffic to a local instance is a preview feature.When a local instance is running via xpander agent dev, it takes over and all tasks route to your locally running agent instead. Only one can be active at a time.

5. Customize the agent (Optional)

The agent created by the CLI starts blank: a default system prompt, no skills. Open agent_instructions.json to rewrite the role, goal, and general description; the next xpander agent dev syncs it to the control plane.
agent_instructions.json
role and goal are arrays so you can add or remove individual statements without rewriting the prompt. general is a free-form description that wraps around them. Beyond that, each customization has its own page:
  1. Custom skills wrapped with @register_tool. See Custom skills.
  2. Prebuilt skills from the skills catalog (Slack, Gmail, GitHub, and more). See Pre-built skills.
  3. Knowledge bases for RAG. See Document Management.
  4. Memory (session storage, user memories, agent memories). See Memory & State.
  5. Skill hooks for logging, metrics, and guardrails around skill calls. See Skill hooks.
  6. A different agent loop (OpenAI Agents SDK, LangChain, AWS Strands). See SDK integrations.

Troubleshooting

The handler imports before .env is loaded. The scaffolded handler already includes from dotenv import load_dotenv; load_dotenv() at the top. If you wrote your own entry point, add it before any xpander_sdk import.
zsh expands the brackets. Quote the package name: pip install "xpander-sdk[agno]".
Another @on_task process is running. Either kill it, or set a different port for this one: XPANDER_STREAMING_PORT=59322 python xpander_handler.py.
The /invoke endpoint requires the x-api-key header on every request. The CLI sets it for you; if you’re using curl or Postman, set it explicitly to your XPANDER_API_KEY.
When a local dev instance is running, it takes over inbound traffic. If another instance holds the slot, run xpander agent stop first, then start xpander agent dev again.
xpander agent invoke posts to the agent’s webhook and waits for a sync response. If your handler is taking more than ~30 seconds, the webhook times out even though the task keeps running on xpander. Check the task in Xpander Chat, or invoke through the async REST endpoint (/v1/agents/{agent_id}/invoke/async) for long-running tasks.

Next steps

Things you can do now that you couldn’t from Xpander Chat alone:

Custom skills

Wrap a private API as a skill with @register_tool instead of a REST skill from the catalog.

Local skills over MCP

Run skills served over MCP that need filesystem or process access.

Lifecycle hooks

Warmup, graceful shutdown, and observability around skill calls.

Core Concepts

The SDK class names and how they map to agents, tasks, threads, and memory.

SDK integrations: Agno

What Backend.aget_args() actually wires up.