> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mesa.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

The Mesa Python SDK is the ergonomic async client for Mesa. It wraps the generated `mesa-rest` client, resolves the default organization for you, and exposes a native virtual filesystem for repo I/O and shell execution.

Python 3.10 or newer is required.

## Installation

```bash theme={null}
pip install mesa-sdk
```

## Create a client

```python theme={null}
import asyncio
import os
from mesa_sdk import Mesa

mesa = Mesa(private_key=os.environ["MESA_PRIVATE_KEY"])

async def main():
    repos = await mesa.repos.list()
    print(f"found {len(repos.repos)} repos")

asyncio.run(main())
```

<Tip>
  Set `MESA_PRIVATE_KEY` in a trusted environment to omit `private_key` from the constructor.
</Tip>

## Client options

```python theme={null}
from mesa_sdk import Mesa

mesa = Mesa(
    private_key="mesa_private_key_acme_...",
    api_url="https://api.mesa.dev/v1",
    user_agent="my-app/1.0.0",
)
```

<ParamField path="private_key" type="str | None">
  Ed25519 private key. The SDK reads `MESA_PRIVATE_KEY` when no explicit credential is supplied.
</ParamField>

<ParamField path="auth" type="MesaAuth | None">
  A credential group holding either a private key or an existing access token, never both. A client built from an access token forwards that token unchanged and cannot mint another one.
</ParamField>

<ParamField path="api_url" type="str">
  REST API base URL. Defaults to `https://api.mesa.dev/v1`. `http` and `https` are accepted. Trailing slashes are stripped.
</ParamField>

<ParamField path="user_agent" type="str | None">
  Appended to the SDK user agent. The default user agent starts with `mesa-sdk-python`.
</ParamField>

## Client lifecycle

Create one `Mesa` client for your process or application and reuse it across request handlers. If your framework has a lifespan hook and you want explicit cleanup, wrap the client in `async with Mesa(...)` at application lifespan, not inside each handler.

```python theme={null}
from mesa_sdk import Mesa

mesa = Mesa()

async def handler():
    repos = await mesa.repos.list()
    return repos
```

## Organization resolution

Private-key and access-token clients read the organization from the credential. Resource methods always use that organization and do not accept an `org` value.

```python theme={null}
print(mesa.org.slug)
await mesa.repos.list()
```

Use `await mesa.org.get()` when you need organization metadata from the API.

## Resource namespaces

| Namespace              | Purpose                                                                                                                                                        |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mesa.repos`           | Create, read, update, delete, sync upstreams, and read upstream sync history on repositories.                                                                  |
| `mesa.content`         | Read files, symlinks, and directory listings without mounting.                                                                                                 |
| `mesa.changes`         | Create, patch, and inspect Mesa changes.                                                                                                                       |
| `mesa.diffs`           | Inspect diffs and conflicts between changes.                                                                                                                   |
| `mesa.bookmarks`       | Manage branch-like bookmark refs.                                                                                                                              |
| `mesa.api_keys`        | Create, list, and revoke Mesa API keys (deprecated).                                                                                                           |
| `mesa.tokens`          | Sign short-lived, scoped access tokens locally from a private key.                                                                                             |
| `mesa.webhook_targets` | Manage outbound webhook targets.                                                                                                                               |
| `mesa.fs`              | Define a layout and mount it as a virtual filesystem, or mint its scoped token. Call `mesa.fs(layout=..., authors=..., ttl=...)` then `.mount()` / `.token()`. |
| `mesa.org`             | Read the credential's organization slug with `mesa.org.slug`, or fetch organization metadata with `await mesa.org.get()`.                                      |

## Response objects

The high-level SDK returns model instances generated by `mesa-rest`. Use attribute access, not dictionary access.

```python theme={null}
repos = await mesa.repos.list()
for repo in repos.repos:
    print(repo.name, repo.head_change_id)
```

## Common types

Import common dataclasses and native result types from `mesa_sdk`.

```python theme={null}
import base64
from mesa_sdk import Author, FileDelete, FileUpsert

files = [
    FileUpsert(path="README.md", content=base64.b64encode(b"hello").decode()),
    FileDelete(path="old.txt"),
]
committer = Author(name="Build Bot", email="build@example.com")
```

| Type                         | Purpose                                                                                                     |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `Author`                     | Committer identity for change creation and updates.                                                         |
| `FileUpsert`                 | Create or replace one file in a change. `content` must be base64-encoded.                                   |
| `FileDelete`                 | Delete one file in a change.                                                                                |
| `WholeFileResolution`        | Resolve a conflicted path by replacing full content or taking one side.                                     |
| `HunkResolution` / `HunkFix` | Resolve individual conflict hunks.                                                                          |
| `LayoutDefinition`           | Layout bundled with its `layout()`, `mount()`, and `token()` operations, returned by `mesa.fs(layout=...)`. |
| `Layout`                     | Prepared layout the definition exposes. `str(...)` serializes it for `mesa mount --layout`.                 |
| `repo`                       | Helper that builds one layout repository declaration (`mode` required).                                     |
| `DiskCacheConfig`            | Configure on-disk MesaFS cache placement and size (passed to `definition.mount(...)`).                      |
| `FsStat`                     | File metadata returned by `stat` and `lstat`.                                                               |
| `ChangeInfo`                 | Change metadata returned by mounted filesystem change operations.                                           |
| `ExecResult`                 | Output from `fs.bash().exec(...)`.                                                                          |

Upstream configuration types live in `mesa_sdk.types`:

```python theme={null}
from mesa_sdk.types import TokenAuth, UpstreamConfig, UsernamePasswordAuth

public = UpstreamConfig(url="https://github.com/acme/app.git")
token = UpstreamConfig(
    url="https://github.com/acme/app.git",
    auth=TokenAuth(token="github_pat_...", token_username="bot"),
)
password = UpstreamConfig(
    url="https://git.example.com/acme/app.git",
    auth=UsernamePasswordAuth(username="bot", password="secret"),
)
```

On `mesa.repos.update(...)`, `UpstreamConfig.auth` is tri-state: omit to preserve existing credentials, pass `None` to clear credentials, or pass `TokenAuth` / `UsernamePasswordAuth` to set credentials.

## Error model

REST API operations raise `MesaError` subclasses.

| Exception             | Status       | Meaning                                                               |
| --------------------- | ------------ | --------------------------------------------------------------------- |
| `ValidationError`     | `400`, `406` | Invalid request parameters or unacceptable response variant.          |
| `AuthenticationError` | `401`        | Missing or invalid credential.                                        |
| `AuthorizationError`  | `403`        | The credential does not have the required scope or repository access. |
| `NotFoundError`       | `404`        | Requested resource does not exist.                                    |
| `ConflictError`       | `409`        | Resource conflict, optimistic concurrency failure, or merge conflict. |
| `RateLimitError`      | `429`        | Rate limit exceeded.                                                  |
| `ServerError`         | `5xx`        | Server-side failure.                                                  |

SDK setup errors include `MissingCredentialError`, `InvalidApiUrlError`, `OrgResolutionError`, and `InvalidOptionsError`.

Filesystem and Bash operations raise built-in Python exceptions such as `FileNotFoundError`, `FileExistsError`, `IsADirectoryError`, `NotADirectoryError`, `PermissionError`, `NotImplementedError`, and `OSError`.

## Raw generated client

`mesa.raw` exposes the authenticated generated `mesa-rest` client. Use it when the high-level SDK does not expose a generated REST operation or option yet.

```python theme={null}
from mesa_rest.api.repo import list_repos

response = await list_repos.asyncio_detailed("acme", client=mesa.raw)
if response.status_code == 200:
    print(response.parsed.repos)
```

Raw generated calls return a `Response[T]` wrapper with `status_code`, `parsed`, and `headers`. High-level SDK methods unwrap successful responses and raise typed errors for non-2xx responses.

## Complete example

```python theme={null}
import asyncio
import base64
import os
from mesa_sdk import FileUpsert, Mesa, repo

mesa = Mesa(private_key=os.environ["MESA_PRIVATE_KEY"])

async def main():
    created = await mesa.repos.create(name="demo")

    change = await mesa.changes.create(
        repo=created.name,
        base_change_id=created.head_change_id,
        message="Add README",
        authors=[{"name": "Docs Bot", "email": "docs@example.com"}],
        files=[
            FileUpsert(
                path="README.md",
                content=base64.b64encode(b"# Demo\n").decode(),
            )
        ],
    )

    await mesa.bookmarks.move(
        repo=created.name,
        bookmark=created.default_bookmark,
        change_id=change.id,
    )

    async with mesa.fs(
        layout={"/workspace": repo(created.name, mode="rw")},
        authors=[{"name": "Docs Bot", "email": "docs@example.com"}],
    ).mount() as fs:
        data = await fs.read("/workspace/README.md")
        print(data.decode())

asyncio.run(main())
```
