DEV Community

Cover image for Strands Agents now speaks TypeScript: A side-by-side guide
Danilo Poccia for AWS

Posted on

Strands Agents now speaks TypeScript: A side-by-side guide

The Strands Agents SDK, which was released as open source in May 2025, recently added TypeScript support as a public preview. Developers who prefer Python have had access to Strands since launch. Now TypeScript developers can build AI agents using the same model-driven approach, with full type safety and modern async patterns.

This article compares the Python and TypeScript implementations side by side, building a practical example along the way.

Because the Strands Agents TypeScript SDK is currently in public preview, APIs may change as the SDK is refined, and some features available in the Strands Agents Python SDK are not yet implemented. The core functionality—agents, tools, MCP integration, streaming—works as expected, but this is an early release. Feedback and contributions are welcome via the GitHub repository.

The Model-Driven Approach

Many agent frameworks are workflow-driven: you define explicit chains of actions where the agent does A, then B, then C. You're essentially programming the agent's behavior in advance. This works well for predictable tasks but becomes brittle when dealing with novel situations.

Strands Agents takes a different approach. Instead of prescribing steps, you give the agent a goal and a set of tools and let the LLM figure out how to accomplish the task. The model's reasoning capabilities—its ability to plan, reflect, and adapt—drive the behavior. This is what Strands calls the "model-driven" approach.

At the core of every Strands agent is the agent loop: the model receives context (conversation history, system prompt, tool descriptions), decides what to do next, optionally calls tools, observes the results, and repeats until it produces a final response.

flowchart LR
    A[User Request] --> B[Agent]
    B --> C{LLM Reasoning}
    C --> D[Tool Calls]
    D --> C
    C --> E[Response]
Enter fullscreen mode Exit fullscreen mode

For details on how this works, see the Agent Loop documentation.

Building a Task Planning Agent

Let's build something practical: an agent that helps break down a project into tasks. We'll give it two tools—one to add tasks and one to list them—and let it figure out how to help the user.

Defining Tools

The two Strands SDKs take different approaches to tool definition that reflect each language's idioms.

Python uses the @tool decorator with docstrings and type hints:

from strands import Agent, tool

tasks = []

@tool
def add_task(description: str, priority: str = "medium") -> str:
    """Add a task to the plan.

    Args:
        description: What needs to be done
        priority: Priority level (high, medium, low)
    """
    tasks.append({"description": description, "priority": priority})
    return f"Added: {description} [{priority}]"

@tool
def list_tasks() -> str:
    """Show all tasks in the current plan."""
    if not tasks:
        return "No tasks yet."
    return "\n".join(
        f"- [{t['priority'].upper()}] {t['description']}" 
        for t in tasks
    )
Enter fullscreen mode Exit fullscreen mode

Strands extracts the tool specification from the function signature, type hints, and docstring automatically.

TypeScript uses Zod schemas:

import { Agent, tool } from '@strands-agents/sdk'
import { z } from 'zod'

const tasks: Array<{description: string, priority: string}> = []

const addTask = tool({
    name: 'add_task',
    description: 'Add a task to the plan.',
    inputSchema: z.object({
        description: z.string().describe('What needs to be done'),
        priority: z.string().default('medium').describe('Priority level (high, medium, low)')
    }),
    callback: async (input) => {
        tasks.push(input)
        return `Added: ${input.description} [${input.priority}]`
    }
})

const listTasks = tool({
    name: 'list_tasks',
    description: 'Show all tasks in the current plan.',
    inputSchema: z.object({}),
    callback: async () => {
        if (tasks.length === 0) return 'No tasks yet.'
        return tasks
            .map(t => `- [${t.priority.toUpperCase()}] ${t.description}`)
            .join('\n')
    }
})
Enter fullscreen mode Exit fullscreen mode

Zod is a TypeScript-first schema validation library that solves two problems at once: runtime validation and type inference. TypeScript's type system only works at compile time, but when your agent receives input from an LLM, you need runtime validation. Zod validates the data and automatically infers TypeScript types from your schema—define once, get both.

Creating and Running the Agent

Python:

agent = Agent(
    system_prompt="You are a planning assistant. Help users break down projects into actionable tasks.",
    tools=[add_task, list_tasks]
)

result = agent("Help me plan a surprise birthday party for next Saturday")
print(result.message)
Enter fullscreen mode Exit fullscreen mode

TypeScript:

const agent = new Agent({
    systemPrompt: 'You are a planning assistant. Help users break down projects into actionable tasks.',
    tools: [addTask, listTasks]
})

const result = await agent.invoke('Help me plan a surprise birthday party for next Saturday')
console.log(result.message)
Enter fullscreen mode Exit fullscreen mode

The agent will reason through what's needed—venue, guest list, food, decorations, timeline—call add_task multiple times with appropriate priorities, then call list_tasks to show the plan. You didn't program that sequence; the model figured it out.

Sample Output

I've created a plan for the surprise birthday party:

- [HIGH] Confirm venue and time for Saturday
- [HIGH] Create guest list and send invitations ASAP
- [HIGH] Arrange for someone to bring the birthday person to the venue
- [MEDIUM] Order or bake birthday cake
- [MEDIUM] Plan food and drinks menu
- [MEDIUM] Buy decorations (balloons, banner, etc.)
- [LOW] Create a party playlist
- [LOW] Plan games or activities
- [LOW] Arrange for someone to take photos

Given the short timeline, I've prioritized the items that need immediate action. 
Would you like me to break any of these down further?
Enter fullscreen mode Exit fullscreen mode

How the Two Languages Differ

The most visible difference is in tool definition. Python extracts schemas from docstrings and type hints—you write documentation as you normally would, and Strands uses it. TypeScript relies on Zod schemas, which provide both runtime validation and compile-time type inference in a single definition.

Invocation patterns also differ slightly. In Python, you call the agent directly with agent("prompt"), and async is optional. In TypeScript, you use await agent.invoke("prompt"), and async is the default model throughout. Both approaches describe the same thing to the LLM—they just do it in ways idiomatic to each language.

Streaming Responses

For interactive applications, both Strands SDKs support streaming so users see output as it's generated.

Python:

async for event in agent.stream_async("Plan a weekend hiking trip"):
    if "data" in event:
        print(event["data"], end="", flush=True)
Enter fullscreen mode Exit fullscreen mode

TypeScript:

for await (const event of agent.stream('Plan a weekend hiking trip')) {
    if (event.type === 'text') {
        process.stdout.write(event.data)
    }
}
Enter fullscreen mode Exit fullscreen mode

Model Context Protocol (MCP)

The Model Context Protocol lets you connect agents to external tools and services. Both Strands SDKs have built-in MCP support.

Python:

from mcp import stdio_client, StdioServerParameters
from strands import Agent
from strands.tools.mcp import MCPClient

mcp_client = MCPClient(lambda: stdio_client(
    StdioServerParameters(command="uvx", args=["awslabs.aws-documentation-mcp-server@latest"])
))

with mcp_client:
    agent = Agent(tools=mcp_client.list_tools_sync())
    agent("How do I configure an S3 bucket?")
Enter fullscreen mode Exit fullscreen mode

TypeScript:

import { Agent, McpClient, StdioClientTransport } from '@strands-agents/sdk'

const mcpClient = new McpClient({
    transport: new StdioClientTransport({
        command: 'uvx',
        args: ['awslabs.aws-documentation-mcp-server@latest']
    })
})

const agent = new Agent({ tools: [mcpClient] })
await agent.invoke('How do I configure an S3 bucket?')
Enter fullscreen mode Exit fullscreen mode

See the MCP Tools documentation for more transport options.

Beyond the Basics

The example above covers the core workflow, but both Strands SDKs offer more. Long-running conversations can exhaust the model's context window, so both provide conversation managers that automatically maintain a sliding window of recent messages. The Strands Agents Python SDK goes further with a summarizing conversation manager that compresses old messages rather than discarding them—useful when you need to preserve context over very long interactions.

For complex problems where a single agent isn't enough, the Strands Agents Python SDK provides multi-agent patterns. You can wrap specialized agents as tools that an orchestrator calls, create swarms where agents hand off tasks to each other autonomously, or define explicit graphs with deterministic execution order. These patterns aren't yet available in the TypeScript SDK. See the Multi-Agent Patterns documentation for details.

The Strands Agents Python SDK also supports structured output through Pydantic models, letting you constrain the agent's response to a specific schema. This is particularly useful when you need to parse the agent's output programmatically.

Feature Comparison

The table below summarizes what's available in each SDK. The Strands Agents Python SDK is stable with the full feature set, and the Strands Agents TypeScript SDK is in preview with core functionality available.

Feature Strands Python (stable) Strands TypeScript (preview)
Core agent loop
Custom tools ✅ (decorator) ✅ (Zod)
MCP integration
Amazon Bedrock
OpenAI
Anthropic API
Ollama
Streaming
Conversation management
Structured output
Multi-agent (Swarm/Graph)
Community tools package
Session persistence
OpenTelemetry traces

When to Choose Which

The choice between SDKs often comes down to your existing stack and requirements. If your application is already in JavaScript or TypeScript and you're comfortable with preview-stage software, the Strands Agents TypeScript SDK gives you the core agent functionality with compile-time type safety and modern async patterns. It works in both Node.js and browser environments.

If you need the full feature set—multi-agent orchestration, structured output, more model providers, or the community tools package—the Strands Agents Python SDK is the more complete option for now.

Getting Started

Python:

uv init my-agent && cd my-agent
uv add strands-agents strands-agents-tools
Enter fullscreen mode Exit fullscreen mode

TypeScript:

mkdir my-agent && cd my-agent
npm init -y
npm install @strands-agents/sdk zod
Enter fullscreen mode Exit fullscreen mode

Both SDKs default to Amazon Bedrock as the model provider, which requires valid AWS credentials. If you're new to AWS, the AWS Free Tier provides $100 in credits at sign-up plus up to $100 more by completing onboarding activities— one of which involves using Amazon Bedrock. The free plan lasts six months, and you won't be charged unless you upgrade. See the Strands Agents Quickstart guide for setup details.

Resources

The Strands Agents documentation is the best starting point, with guides for both SDKs. The source code is available on GitHub for both the Python SDK and the TypeScript SDK. For Python, the community tools package provides ready-to-use tools, and the samples repository has complete example agents.

Wrapping Up

The Strands Agents TypeScript SDK brings the model-driven approach to the JavaScript ecosystem. While the Python SDK currently remains the fully featured option, the TypeScript preview delivers the core experience: define tools, create an agent, and let the model reason through problems.

The Strands Agents TypeScript SDK's preview status means now is a good time to experiment, provide feedback, and help shape its development.

Top comments (0)