# Agent SDK quickstart

> Install the Python agent SDK and run your first agent.

Source: https://maincode.com/docs/python-agent-sdk-overview
Section: Agent SDK · Matilda documentation

---

A Python SDK for building agentic applications on Matilda. Provides agent abstractions, client-side tool execution, multi-turn sessions, automatic retry with exponential backoff, DSML tool-call interception, and durable stream resume — all on top of the Matilda-native chat contract. Async-first (`asyncio` + `httpx`). Requires Python 3.12 or later. This guide covers SDK version 0.1.0.

The agent SDK wraps the [client SDK](https://maincode.com/docs/python-client-sdk-overview) and adds:

- **Agent** — a named, configurable persona with dynamic instructions and purpose-based routing
- **Runner** — runs agent turns with streaming, retry, and a client-side tool execution loop
- **Session** — multi-turn conversations with auto-managed `conversation_id` and turn accumulation
- **Client tools** — register handlers the agent can invoke mid-turn; the SDK handles the roundtrip loop
- **DSML interception** — tool calls emitted as text tokens (`<｜DSML｜tool_call>`) are automatically captured and surfaced as native tool events
- **Durable stream resume** — reconnect to a detached stream from the last cursor

Agent runs send `persist: false` by default — conversations do not appear in the Matilda web app's chat history.

## What's included

- **Agent** — named persona with static or dynamic instructions, purpose-based routing
- **Runner** — `run()`, `stream()`, `stream_text()`, `run_text()`, `run_object()`, `stream_object()` with retry and tool execution
- **Session** — multi-turn conversations with automatic `conversation_id` reuse and client-side transcript replay
- **Client tools** — `ToolHandlers` with automatic roundtrip loop and advertised-tool guard
- **Files** — upload (single and parallel), retrieve metadata
- **Conversations** — list, retrieve, rename, and set message feedback
- **Auth** — managed PKCE browser login, RFC 8628 device flow, token restore, persistent token storage

## What's NOT included

- **Server-side tool execution** — server-side tools (web search, code execution, etc.) are handled by Matilda core. The agent SDK's tool loop is for client-side tools only.
- **Model/provider selection** — Matilda core owns routing, safety, and policy.

## Installation

**pip**

```bash
pip install matilda-agent-sdk
```

**uv**

```bash
uv add matilda-agent-sdk
```

Requires Python 3.12 or later. The client SDK (`matilda-client`) is installed automatically as a dependency.

`pydantic` is an optional dependency — install it alongside the SDK if you want schema-validated structured output ([Structured output](https://maincode.com/docs/python-agent-sdk-structured-output)). The SDK detects pydantic models by duck-typing, so any version works:

```bash
pip install pydantic
```

### Import

```python title="main.py"
from matilda_agent_sdk import Agent, Runner, run, stream
```

## Quick start

### Minimal: run a single agent turn

```python title="run.py"
import asyncio

from matilda_agent_sdk import run


async def main():
    result = await run(
        {"name": "greeter", "instructions": "Be friendly and concise."},
        "Say hello in three languages.",
    )

    print(result.final_output)
    print(result.usage)


asyncio.run(main())
```

### Minimal: streaming

```python title="stream.py"
from matilda_agent_sdk import stream


async def main():
    async for event in stream(
        {"name": "storyteller", "instructions": "Write a short sci-fi haiku."},
        "Write about a Dyson sphere.",
    ):
        if event.type == "response.output_text.delta":
            print(event.delta, end="", flush=True)
        if event.type == "response.completed":
            print("\n[done]")
```

### Authenticated: device flow + run

```python title="device_flow.py"
import asyncio

from matilda_agent_sdk import MatildaClient, Runner


async def main():
    runner = Runner(MatildaClient(base_url="https://matilda.maincode.com/api"))

    # Authenticate via RFC 8628 device flow — prints a code to stderr
    if not await runner.auth.get_tokens():
        await runner.auth.login_with_device_flow(client_id="matilda-code")

    # Token is now managed automatically — refresh on 401 comes for free
    result = await runner.run(
        {"name": "helper", "instructions": "Be concise."},
        "What is the capital of Australia?",
    )
    print(result.final_output)


asyncio.run(main())
```
