{"id":47566441,"url":"https://github.com/strands-compose/sdk-python","last_synced_at":"2026-04-12T07:03:41.035Z","repository":{"id":346446625,"uuid":"1190006652","full_name":"strands-compose/sdk-python","owner":"strands-compose","description":"Zero-code YAML-driven agent orchestration over strands-agents","archived":false,"fork":false,"pushed_at":"2026-04-12T04:05:30.000Z","size":568,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-12T04:08:21.274Z","etag":null,"topics":["ai","ai-agents","aws","gen-ai","llm","mcp","multi-agent","ollama","orchestration","python","strands-agents","yaml"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/strands-compose.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-23T22:02:29.000Z","updated_at":"2026-04-12T02:05:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/strands-compose/sdk-python","commit_stats":null,"previous_names":["strands-compose/sdk-python"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/strands-compose/sdk-python","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strands-compose%2Fsdk-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strands-compose%2Fsdk-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strands-compose%2Fsdk-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strands-compose%2Fsdk-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/strands-compose","download_url":"https://codeload.github.com/strands-compose/sdk-python/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strands-compose%2Fsdk-python/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31706766,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-12T06:22:27.080Z","status":"ssl_error","status_checked_at":"2026-04-12T06:21:52.710Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-agents","aws","gen-ai","llm","mcp","multi-agent","ollama","orchestration","python","strands-agents","yaml"],"created_at":"2026-03-30T06:00:24.600Z","updated_at":"2026-04-12T07:03:41.029Z","avatar_url":"https://github.com/strands-compose.png","language":"Python","funding_links":[],"categories":["Community Projects"],"sub_categories":["For PyPI Packages"],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/strands-compose/sdk-python/main/docs/img/logo.png\" width=\"180\" alt=\"strands-compose\"\u003e\n\n  # Strands Compose\n\n  **Declarative multi-agent orchestration for [strands-agents](https://github.com/strands-agents/sdk-python) — wire entire agent systems with YAML**\n\n  \u003cp\u003e\n    \u003ca href=\"https://www.python.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/python-3.11+-blue.svg\" alt=\"Python 3.11+\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://pypi.org/project/strands-compose/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/v/strands-compose.svg\" alt=\"PyPI version\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/strands-agents/sdk-python\"\u003e\u003cimg src=\"https://img.shields.io/badge/strands--agents-1.35.0-green.svg\" alt=\"Strands Agents\"\u003e\u003c/a\u003e\n    \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-Apache--2.0-blue.svg\" alt=\"License\"\u003e\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n\u003e [!IMPORTANT]\n\u003e Community project — not affiliated with AWS or the strands-agents team. Bugs here? [Open an issue](https://github.com/strands-compose/sdk-python/issues). Bugs in the underlying SDK? Head to [strands-agents](https://github.com/strands-agents/sdk-python).\n\n## What is this?\n\n\u003e **Think Docker Compose, but for AI agents**\n\n[Strands](https://github.com/strands-agents/sdk-python) is a powerful agent SDK. But once you have more than one agent, a few MCP servers, safety hooks, and shared models — you end up writing the same plumbing over and over. **strands-compose kills that boilerplate.**\n\nYou describe the shape of your agent system in YAML, and strands-compose resolves, validates, and starts everything — models, MCP servers \u0026 clients, hooks, tools, orchestration topology — as a live, fully wired multi-agent system.\n\n**Already working with strands? Guess what — you already know strands-compose.** After `load()` resolves your YAML, what you get back are plain strands objects. Every agent **is** a `strands.Agent`. Every MCP client **is** a `strands.tools.mcp.MCPClient`. Every orchestrator **is** a `strands.multiagent.Swarm` or `Graph` or just `strands.Agent`. No wrappers, no subclasses, no magic. Just the real deal, fully wired and ready to go.\n\n```yaml\nmodels:\n  default:\n    provider: bedrock\n    model_id: us.anthropic.claude-sonnet-4-6-v1:0\n\nagents:\n  researcher:\n    model: default\n    system_prompt: \"You research topics.\"\n    tools: [strands_tools.http_request]\n\n  writer:\n    model: default\n    system_prompt: \"You write reports.\"\n\n  coordinator:\n    model: default\n    system_prompt: \"Coordinate research and writing.\"\n\norchestrations:\n  team_leader:\n    mode: delegate\n    entry_name: coordinator\n    connections:\n      - agent: researcher\n        description: \"Research a topic.\"\n      - agent: writer\n        description: \"Write the report.\"\n\nentry: team_leader\n```\n\n```python\nfrom strands_compose import load\n\nresolved = load(\"config.yaml\")\n\nresult = resolved.entry(\"Write a report about quantum computing.\")\nprint(result)\n```\n\nThree agents, orchestration wiring, model sharing — **zero plumbing code**.\n\n---\n\n## Why this changes everything\n\nYour entire agent network — models, prompts, tools, hooks, MCP servers, orchestration topology — captured in a single YAML file and maybe a few Python files for custom tools or hooks. That's it. That's your agent environment. Here's what that unlocks:\n\n### 🔖 Version it\n\nPush to Git. Tag it. Diff two versions and see exactly what changed — which prompt was tweaked, which model was swapped, which hook was added. Your agent system gets the same auditability as your infrastructure code. No more \"I think someone changed the system prompt last Tuesday.\"\n\n### 📦 Build a registry\n\nA folder of YAML configs — one per agent environment. `production.yaml`, `staging.yaml`, `experiment-42.yaml`. Each is a complete, self-contained snapshot of an agent system. Load any of them with `load(\"experiment-42.yaml\")`. That's your agent environments registry — no platform needed.\n\n### 🧪 Automate experiments\n\nYour entire config is data, so you can *generate* it. Build 20 variations — different models, different prompts, different tool combinations — and run them all in CI. With session persistence, every agent interaction is tracked. Point another strands-compose pipeline at those session logs to analyze results, compare quality, compute metrics. You're benchmarking agent systems *with agent systems*.\n\n### 🔁 Reproduce anything\n\nA bug report comes in. You have the exact YAML config, the session ID, the full conversation trace. Load it, replay it, debug it. No \"works on my machine\" — the config *is* the machine.\n\n### CRAZY, right?!\n\n---\n\n## What's in the box\n\n| Feature | What it does |\n|---------|-------------|\n| **YAML-first config** | Models, agents, tools, hooks, MCP, orchestrations — all in one file |\n| **Full YAML power** | Variables (`${VAR:-default}`), anchors (`\u0026ref` / `*ref`), `x-` scratch pads, multi-file merge |\n| **Multi-model support** | Bedrock, OpenAI, Ollama, Gemini — swap with one line |\n| **MCP servers \u0026 clients** | Launch local servers from Python files, connect to remote HTTP endpoints, or spawn stdio subprocesses |\n| **MCP lifecycle management** | Startup ordering, readiness polling, graceful shutdown — servers before clients, always |\n| **Orchestration modes** | Delegate (agent-as-tool), Swarm (peer handoffs), Graph (DAG pipelines) — arbitrarily nestable |\n| **Event streaming** | Unified async event queue across any orchestration depth — tokens, tool calls, handoffs, completions |\n| **Session persistence** | File, S3, or [Bedrock AgentCore Memory](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/memory.html) — agents remember across restarts |\n| **Custom agent factories** | Plug your own `Agent` subclass or factory via `type:` |\n| **Deployment-agnostic** | Pure core library — no HTTP server, no deployment opinions baked in |\n\n---\n\n## How it works — the loading pipeline\n\nWhen you call `load(\"config.yaml\")`, strands-compose runs a deterministic pipeline:\n\n```\nYAML source(s)\n  │\n  ├─ Parse \u0026 strip x-* anchors\n  ├─ Interpolate ${VAR:-default} variables\n  ├─ Sanitize collection keys\n  ├─ Merge (if multi-file)\n  │\n  ├─ Validate against Pydantic schema\n  │\n  ├─ Resolve infrastructure (models, MCP servers/clients, session managers)\n  ├─ Start MCP lifecycle (servers up → clients connect)\n  │\n  ├─ Create agents (with tools, hooks, MCP clients attached)\n  ├─ Wire orchestrations (delegate/swarm/graph, topological sort)\n  │\n  └─ Return ResolvedConfig — ready to call\n```\n\nEvery step is explicit. Every error is caught early with a clear message. The pipeline is the same whether you load one file or merge five — `load([\"base.yaml\", \"agents.yaml\", \"mcp.yaml\"])` just works.\n\n---\n\n## YAML superpowers\n\n**strands-compose** gives you Docker Compose-style variable interpolation **plus** full YAML anchor/alias support. DRY configs that adapt to any environment:\n\n```yaml\nvars:\n  MODEL: ${MODEL:-us.anthropic.claude-sonnet-4-6-v1:0}\n  TONE:  ${TONE:-friendly}\n\nx-base: \u0026base_prompt |\n  You are a ${TONE} assistant.\n  Keep answers clear and concise.\n\nx-hooks: \u0026safety_hooks\n  - type: strands_compose.hooks:MaxToolCallsGuard\n    params: { max_calls: 15 }\n  - type: strands_compose.hooks:ToolNameSanitizer\n\nmodels:\n  default:\n    provider: bedrock\n    model_id: ${MODEL}\n\nagents:\n  assistant:\n    model: default\n    system_prompt: *base_prompt\n    hooks: *safety_hooks\n\nentry: assistant\n```\n\nOverride at runtime: `TONE=formal MODEL=us.anthropic.claude-sonnet-4-6-v1:0 python main.py`\n\nSplit large configs across files — models in one, agents in another, MCP in a third — and merge them with `load([\"base.yaml\", \"agents.yaml\"])`. Each file interpolates its own `vars:` independently, collections merge, and duplicates are caught.\n\n---\n\n## Getting started\n\nInstall with [uv](https://docs.astral.sh/uv/):\n\n```bash\nuv add strands-compose                   # Bedrock (default)\nuv add strands-compose[ollama]           # + Ollama\nuv add strands-compose[openai]           # + OpenAI\nuv add strands-compose[gemini]           # + Gemini\n```\n\nOr with pip:\n\n```bash\npip install strands-compose              # Bedrock (default)\npip install strands-compose[ollama]      # + Ollama\npip install strands-compose[openai]      # + OpenAI\npip install strands-compose[gemini]      # + Gemini\n```\n\nCreate a `config.yaml`:\n\n```yaml\nmodels:\n  default:\n    provider: bedrock\n    model_id: us.anthropic.claude-sonnet-4-6-v1:0\n\nagents:\n  assistant:\n    model: default\n    system_prompt: \"You are a helpful assistant.\"\n\nentry: assistant\n```\n\nRun it:\n\n```python\nfrom strands_compose import load\n\nresolved = load(\"config.yaml\")\n\nwith resolved.mcp_lifecycle:\n    result = resolved.entry(\"Hello!\")\n    print(result)\n```\n\n### CLI\n\nstrands-compose ships a CLI to validate and debug configs without writing Python.\n\n**`check`** — fast, static validation (YAML syntax, schema, variable interpolation, cross-references). No side-effects, safe for CI. Will **not** catch runtime issues like bad credentials, unreachable MCP servers, or missing Python modules.\n\n```bash\nstrands-compose check config.yaml\nstrands-compose check base.yaml agents.yaml   # merge multiple files\nstrands-compose check config.yaml --json       # JSON output for scripts\nstrands-compose check config.yaml --quiet      # exit code only\n```\n\n**`load`** *(recommended)* — full end-to-end validation. Builds real Python objects, starts MCP servers, and probes connectivity. Catches everything `check` catches plus import errors, auth failures, and MCP health issues.\n\n```bash\nstrands-compose load config.yaml\nstrands-compose load config.yaml --json\nstrands-compose load config.yaml --quiet\n```\n\n---\n\n## Examples\n\nEvery example is a self-contained folder with a `README.md`, `config.yaml`, and `main.py`. Start from the top and work your way down — each one builds on concepts from the previous.\n\n| # | Example | What it shows |\n|---|---------|---------------|\n| 01 | [Minimal](examples/01_minimal/) | `load()` one-liner — the simplest possible agent |\n| 02 | [Vars \u0026 Anchors](examples/02_vars_and_anchors/) | `${VAR:-default}` interpolation and YAML `\u0026anchor` / `*alias` reuse |\n| 03 | [Tools](examples/03_tools/) | `tools:` — auto-load `@tool` functions from Python files |\n| 04 | [Session](examples/04_session/) | `session_manager:` — persistent memory across turns and restarts |\n| 05 | [Hooks](examples/05_hooks/) | `hooks:` — `MaxToolCallsGuard`, `ToolNameSanitizer`, and custom hooks |\n| 06 | [MCP](examples/06_mcp/) | All three MCP modes: local server, remote URL, stdio subprocess |\n| 07 | [Delegate](examples/07_delegate/) | `mode: delegate` — coordinator routes work to specialist agents |\n| 08 | [Swarm](examples/08_swarm/) | `mode: swarm` — peer agents hand off to each other autonomously |\n| 09 | [Graph](examples/09_graph/) | `mode: graph` — deterministic DAG pipeline between agents |\n| 10 | [Nested](examples/10_nested/) | Nested orchestration — Swarm inside a Delegate |\n| 11 | [Multi-file](examples/11_multi_file_config/) | Split config across files — infra in one YAML, agents in another |\n| 12 | [Streaming](examples/12_streaming/) | `wire_event_queue()` — stream every token, tool call, and handoff live |\n| 13 | [Graph conditions](examples/13_graph_conditions/) | Conditional edges — `condition:`, `reset_on_revisit`, `max_node_executions` |\n| 14 | [Agent factory](examples/14_agent_factory/) | `type:` + `agent_kwargs:` — custom agent factory instead of `Agent()` |\n\n```bash\n# Run any example\nuv run python examples/01_minimal/main.py\n```\n\n---\n\n## Multi-agent orchestration\n\n**strands-compose** supports 3 orchestration modes from strands. They can be nested arbitrarily — a delegate target can be a swarm, a graph node can be a delegate.\n\n### Delegate — agent as a tool\n\nThe coordinator calls sub-agents like tool functions. Best for hub-and-spoke patterns:\n\n```yaml\norchestrations:\n  team_leader:\n    mode: delegate\n    entry_name: coordinator  # Agent declared in `agents:`\n    connections:\n      - agent: researcher\n        description: \"Research the topic.\"\n      - agent: writer\n        description: \"Write the report.\"\n```\n\n### Swarm — autonomous handoffs\n\nPeer agents pass control to each other. No central coordinator — agents decide when to hand off:\n\n```yaml\norchestrations:\n  review_team:\n    mode: swarm\n    entry_name: drafter                     # Swarm entry - agent name\n    agents: [drafter, reviewer, tech_lead]  # Agents declared in `agents:`\n    max_handoffs: 10\n```\n\n### Graph — deterministic DAG pipeline\n\nAgents execute in dependency order. Independent nodes run in parallel. Supports conditional edges:\n\n```yaml\norchestrations:\n  blog:\n    mode: graph\n    entry_name: writer               # Graph entry - agent name\n    edges:                 # We use agent names to define edges\n      - from: writer\n        to: reviewer\n      - from: reviewer\n        to: writer\n        condition: ./conditions.py:needs_revision\n      - from: reviewer\n        to: publisher\n        condition: ./conditions.py:is_approved\n```\n\n### Nested orchestrations\n\nNamed orchestrations reference each other. A swarm becomes a delegate tool, a delegate becomes a graph node — compose them however you want:\n\n```yaml\norchestrations:\n  content_team:   # This swarm is plugged in as a tool for the team_leader\n    mode: swarm\n    entry_name: researcher\n    agents: [researcher, writer, reviewer]\n\n  team_leader:\n    mode: delegate\n    entry_name: coordinator\n    connections:\n      - agent: content_team     # Nested swarm as a delegate tool\n        description: \"Content creation team.\"\n      - agent: qa_bot           # Nested agent as a delegate tool\n        description: \"Quality assurance.\"\n\nentry: team_leader\n```\n\n**strands-compose** topologically sorts all orchestrations, builds inner ones first, then wires them as tools or nodes for outer ones. Circular dependencies are caught at load time.\n\n---\n\n## Streaming-ready by design\n\nWhen you have a 3-level nested orchestration — a delegate calling a swarm that uses graph nodes — you still want to know exactly what's happening. Which agent is thinking? What tool just fired? When did a handoff occur?\n\n**`EventPublisher`** is a strands `HookProvider` that captures every lifecycle event and publishes it to a shared async queue. The trick: `wire_event_queue()` attaches publishers to **every agent in your entire system** — no matter how deeply nested — so all events flow to one place.\n\n```python\nimport asyncio\nfrom strands_compose import AnsiRenderer, load\n\nasync def main():\n    resolved = load(\"config.yaml\")\n    queue = resolved.wire_event_queue()\n\n    async def invoke():\n        try:\n            await resolved.entry.invoke_async(\"Analyse LLM trends.\")\n        finally:\n            await queue.close()\n\n    asyncio.create_task(invoke())\n\n    renderer = AnsiRenderer()\n    while (event := await queue.get()) is not None:\n        renderer.render(event)\n    renderer.flush()\n\nasyncio.run(main())\n```\n\nEvent types: `AGENT_START`, `TOKEN`, `REASONING`, `TOOL_START`, `TOOL_END`, `NODE_START`, `NODE_STOP`, `HANDOFF`, `COMPLETE`, `MULTIAGENT_START`, `MULTIAGENT_COMPLETE`, `ERROR` — each carrying `{type, agent_name, timestamp, data}`. Enough for a real-time frontend, a log aggregator, or a debugging dashboard. The `AnsiRenderer` gives you coloured terminal output out of the box — agent names, tool calls, reasoning traces, all streaming live.\n\n---\n\n## Developer setup\n\n```bash\ngit clone https://github.com/strands-compose/sdk-python\ncd sdk-python\nuv run just install      # install deps + wire git hooks (run once after clone)\n\nuv run just check        # lint + type check + security scan\nuv run just test         # pytest with coverage\nuv run just format       # auto-format (Ruff)\n```\n\n\u003e Re-install hooks after a fresh clone or if hooks stop running: `uv run just install-hooks`\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for the full contribution guide and [CHANGELOG.md](CHANGELOG.md) for release history.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrands-compose%2Fsdk-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstrands-compose%2Fsdk-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrands-compose%2Fsdk-python/lists"}