AgenticWire test record: claude-agent-sdk-vs-strands Article: https://www.agenticwire.news/article/claude-agent-sdk-vs-strands Machine and versions: see results.json (machine field). Harness script (experiment/run.sh): #!/usr/bin/env bash # Claude Agent SDK vs AWS Strands Agents: what a plain install and the public package/repo # metadata actually say today. # # Self-contained: builds its own venvs, assumes only python3 + uv + curl on PATH. # GitHub/PyPI download-stat figures depend on public APIs and will drift on re-run by nature # (that's the point: they're live numbers, not fixed facts) but version/license/import checks # are fully reproducible. # # Re-run: node blog-app/scripts/pipeline/bench.mjs drafts/claude-agent-sdk-vs-strands set -uo pipefail cd "$(dirname "$0")" log() { echo "$@" >&2; } emit() { echo "$1"; } WORK="${TMPDIR:-/tmp}/strands-bench-$$" mkdir -p "$WORK" trap 'rm -rf "$WORK"' EXIT log "building venvs in $WORK" uv venv --python 3.13 "$WORK/claude" >&2 2>/dev/null uv venv --python 3.13 "$WORK/strands" >&2 2>/dev/null VIRTUAL_ENV="$WORK/claude" uv pip install claude-agent-sdk >&2 2>/dev/null VIRTUAL_ENV="$WORK/strands" uv pip install strands-agents strands-agents-tools >&2 2>/dev/null PY_C="$WORK/claude/bin/python" PY_S="$WORK/strands/bin/python" # --- versions, from a plain install today --- V_CLAUDE=$($PY_C -c "import importlib.metadata as m; print(m.version('claude-agent-sdk'))") V_STRANDS=$($PY_S -c "import importlib.metadata as m; print(m.version('strands-agents'))") emit "{\"metric\":\"claude_agent_sdk_version_resolved\",\"value\":\"$V_CLAUDE\",\"note\":\"pip install claude-agent-sdk\"}" emit "{\"metric\":\"strands_agents_version_resolved\",\"value\":\"$V_STRANDS\",\"note\":\"pip install strands-agents\"}" PYREQ=$($PY_C -c "import importlib.metadata as m; print(m.metadata('claude-agent-sdk')['Requires-Python'])") emit "{\"metric\":\"claude_agent_sdk_python_requirement\",\"value\":\"$PYREQ\"}" # --- licenses: PyPI classifier is the code license, distinct from API usage terms --- LIC_CLAUDE=$($PY_C -c "import importlib.metadata as m; print(m.metadata('claude-agent-sdk').get('License') or 'unset')") LIC_STRANDS=$($PY_S -c "import importlib.metadata as m; print(m.metadata('strands-agents').get('License') or 'unset')") emit "{\"metric\":\"claude_agent_sdk_pypi_license\",\"value\":\"$LIC_CLAUDE\",\"note\":\"code license; using the Claude API is separately governed by Anthropic Commercial Terms of Service\"}" emit "{\"metric\":\"strands_agents_pypi_license\",\"value\":\"$LIC_STRANDS\"}" # --- both constructors: what's actually required with no arguments? --- CLAUDE_REQ=$($PY_C -c " import inspect from claude_agent_sdk import ClaudeAgentOptions sig = inspect.signature(ClaudeAgentOptions) req = [p.name for p in sig.parameters.values() if p.default is inspect._empty] print(len(req)) ") STRANDS_REQ=$($PY_S -c " import inspect from strands import Agent sig = inspect.signature(Agent.__init__) req = [p.name for p in sig.parameters.values() if p.default is inspect._empty and p.name not in ('self','args','kwargs')] print(len(req)) ") emit "{\"metric\":\"claude_agent_options_required_fields\",\"value\":$CLAUDE_REQ,\"note\":\"ClaudeAgentOptions() with zero args\"}" emit "{\"metric\":\"strands_agent_required_fields\",\"value\":$STRANDS_REQ,\"note\":\"Agent() with zero args; model defaults to None, still needs a provider choice before it can actually run\"}" # --- live public data: GitHub repo (follow redirect: the repo was renamed) --- REPO_JSON=$(curl -sS -L -m 20 https://api.github.com/repos/strands-agents/sdk-python) FULL_NAME=$(echo "$REPO_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('full_name','unknown'))") STARS=$(echo "$REPO_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('stargazers_count','null'))") emit "{\"metric\":\"strands_repo_current_name\",\"value\":\"$FULL_NAME\",\"note\":\"strands-agents/sdk-python redirects here now\"}" emit "{\"metric\":\"strands_repo_stars\",\"value\":$STARS,\"note\":\"GitHub API, live figure, will drift on re-run\"}" LATEST_PY_TAG=$(curl -sS -L -m 20 "https://api.github.com/repos/strands-agents/sdk-python/releases?per_page=30" | python3 -c " import json,sys rels = json.load(sys.stdin) py = [r['tag_name'] for r in rels if r.get('tag_name','').startswith('python/')] print(py[0] if py else 'unknown') ") emit "{\"metric\":\"strands_latest_python_release_tag\",\"value\":\"$LATEST_PY_TAG\",\"note\":\"most recent python/vX.Y.Z tag in the monorepo's release list\"}" # --- live public data: PyPI download stats --- DL=$(curl -sS -m 20 "https://pypistats.org/api/packages/strands-agents/recent") DL_MONTH=$(echo "$DL" | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['last_month'])" 2>/dev/null || echo "null") if [ "$DL_MONTH" != "null" ]; then emit "{\"metric\":\"strands_agents_pypi_downloads_last_month\",\"value\":$DL_MONTH,\"note\":\"pypistats.org, live figure, will drift on re-run\"}" fi log "done"