Skip to main content

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.

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

pip install mesa-sdk

Create a client

import asyncio
from mesa_sdk import Mesa

mesa = Mesa(api_key="mk_...")

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

asyncio.run(main())
Set MESA_API_KEY in your environment to omit api_key from the constructor.

Client options

from mesa_sdk import Mesa

mesa = Mesa(
    api_key="mk_...",
    api_url="https://api.mesa.dev/v1",
    vcs_url="https://vcs.mesa.dev",
    org="acme",
    user_agent="my-app/1.0.0",
)
api_key
str | None
API key used as a bearer token. If omitted, the SDK reads MESA_API_KEY from the environment. If neither is present, construction raises MissingApiKeyError.
api_url
str
REST API base URL. Defaults to https://api.mesa.dev/v1. http and https are accepted. Trailing slashes are stripped.
vcs_url
str | None
VCS service URL used by mesa.fs.mount(...). Leave unset for the default Mesa deployment. Set this when using a non-default API deployment with MesaFS.
org
str | None
Default organization slug. When provided, SDK calls skip /whoami for organization resolution.
user_agent
str | None
Appended to the SDK user agent. The default user agent starts with mesa-sdk-python.

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.
from mesa_sdk import Mesa

mesa = Mesa()

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

Organization resolution

The SDK chooses an organization in this order:
SourceWhen it applies
Per-call orgThe method call passes org="...".
Constructor orgThe client was created with Mesa(org="...").
mesa.whoami()No per-call or constructor org was supplied. The result is cached.
from mesa_sdk import Mesa

mesa = Mesa(org="acme")

await mesa.repos.list()
await mesa.repos.list(org="other-org")

Resource namespaces

NamespacePurpose
mesa.reposCreate, read, update, delete, pull, and push repositories.
mesa.sync_runsRead repository upstream sync history.
mesa.contentRead files, symlinks, and directory listings without mounting.
mesa.changesCreate, patch, and inspect Mesa changes.
mesa.diffsInspect diffs and conflicts between changes.
mesa.bookmarksManage branch-like bookmark refs.
mesa.api_keysCreate, list, and revoke Mesa API keys.
mesa.webhook_targetsManage outbound webhook targets.
mesa.fsMount repos as a virtual filesystem and run Bash commands.
mesa.orgFetch organization metadata.

Response objects

The high-level SDK returns model instances generated by mesa-rest. Use attribute access, not dictionary access.
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.
import base64
from mesa_sdk import Author, FileDelete, FileUpsert

files = [
    FileUpsert(path="README.md", content=base64.b64encode(b"hello").decode()),
    FileDelete(path="old.txt"),
]
author = Author(name="Alice", email="alice@example.com")
TypePurpose
AuthorCommit author or committer identity.
FileUpsertCreate or replace one file in a change. content must be base64-encoded.
FileDeleteDelete one file in a change.
WholeFileResolutionResolve a conflicted path by replacing full content or taking one side.
HunkResolution / HunkFixResolve individual conflict hunks.
RepoConfigPin a mounted repo to a bookmark or change.
DiskCacheConfigConfigure on-disk MesaFS cache placement and size.
FsStatFile metadata returned by stat and lstat.
ChangeInfoChange metadata returned by mounted filesystem change operations.
ExecResultOutput from fs.bash().exec(...).
Upstream configuration types live in mesa_sdk.types:
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.
ExceptionStatusMeaning
ValidationError400, 406Invalid request parameters or unacceptable response variant.
AuthenticationError401Missing or invalid API key.
AuthorizationError403The API key does not have the required scope.
NotFoundError404Requested resource does not exist.
ConflictError409Resource conflict, optimistic concurrency failure, or merge conflict.
RateLimitError429Rate limit exceeded.
ServerError5xxServer-side failure.
SDK setup errors include MissingApiKeyError, 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.
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

import asyncio
import base64
from mesa_sdk import Author, FileUpsert, Mesa

mesa = Mesa(org="acme")

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

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

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

    async with mesa.fs.mount(repos=[repo.name]) as fs:
        data = await fs.read(f"/{repo.org}/{repo.name}/README.md")
        print(data.decode())

asyncio.run(main())