Skip to main content

microcld

Python SDK and CLI for microcloud: hosting (apps, managed Postgres, S3 buckets) and sandboxes for AI agents — isolated micro-VMs that start in a fraction of a second, run code, sleep for free and clone with their whole state. Execution and data stay in Mongolia.

pip install microcld
export SANDBOX_API_KEY=mc_...    # organization API key
export SANDBOX_API_URL=http://127.0.0.1:7900   # your API endpoint (this is the default)
from microcld import Sandbox

with Sandbox.create(cpus=1, memory_mb=512, egress="none") as sb:   # deleted on exit
    sb.files.write("/tmp/data.csv", "a,b\n1,2\n3,4\n")
    r = sb.exec("awk -F, 'NR>1 {s+=$2} END {print s}' /tmp/data.csv")
    print(r.check().stdout)          # 6; check() raises CommandError if the exit code is not 0

    sb.sleep()                       # memory goes to a snapshot, not billed
    sb.wake()                        # ~0.1 s, everything is still there
    with sb.fork() as clone:         # a clone with memory and disk - try another path
        clone.exec("rm /tmp/data.csv")
    print(sb.files.read_text("/tmp/data.csv"))   # the original is intact

Code interpreter (python template)

Stateful Python, like a Jupyter kernel: variables and imports live between calls, the last expression is returned, plots come back as images.

with Sandbox.create(template="python", memory_mb=1024) as sb:
    sb.files.write("/work/sales.csv", open("sales.csv", "rb").read())
    sb.run_code("import pandas as pd\ndf = pd.read_csv('sales.csv')")
    r = sb.run_code("df.describe()")
    print(r.result)                                  # repr of the last expression
    r = sb.run_code("df.plot(); import matplotlib.pyplot as plt; plt.title('Sales')")
    open("plot.png", "wb").write(r.images[0])        # PNG
    r = sb.run_code("df.nope")
    print(r.ok, r.error.name)                        # False AttributeError - an error in the result, not an exception

CodeResult: stdout, stderr, result, error (name, value, traceback), images (PNG bytes), duration_ms, .ok, .text (all text together - handy to return to a model as a tool result). timeout interrupts the code but keeps the state; reset_code() clears it. The state survives sleep/wake and is copied by fork(). A template without an interpreter raises InterpreterError.

Async works the same way, with await:

from microcld import AsyncSandbox

async with await AsyncSandbox.create() as sb:
    r = await sb.exec("echo hi")

Tools for AI agents (function calling)

A sandbox as a set of model tools: run_python (stateful interpreter), run_shell, read_file, write_file. Definitions fit the Anthropic Messages API (strict: true) and OpenAI-compatible function calling; the handler runs the call in the sandbox and returns a ready result. Errors (code crashed, no such file) go back to the model with is_error instead of raising, so the agent sees them and corrects itself. Plots from run_python and images from read_file are returned as image blocks (the model sees them). Output is truncated (max_output, 20,000 characters).

from microcld import Sandbox, tools

sb = Sandbox.create(template="python", egress="none")
t = tools.for_sandbox(sb)              # or enabled=["run_python"], timeout=..., max_output=...

response = client.messages.create(model="claude-opus-5-5", max_tokens=16000,
                                  tools=t.anthropic(), messages=messages)
messages.append({"role": "assistant", "content": response.content})
results = [t.handle_anthropic(b) for b in response.content if b.type == "tool_use"]
messages.append({"role": "user", "content": results})   # all results in one message

OpenAI-compatible APIs: tools=t.openai(), and for each tool_call messages.append(t.handle_openai(call)) (images are not included - only a note). For AsyncSandbox the methods are the same, with await.

A ready-made analyst agent: examples/data_analyst.py. The CSV is uploaded to a sandbox without internet access, Claude analyzes it with pandas, looks at its own plots and answers; the plots are saved next to the script.

pip install anthropic
python examples/data_analyst.py examples/sales.csv "How did sales change by region? Plot it."

Storage bucket

An organization bucket (created in the dashboard or with POST /v1/buckets) can be attached to a sandbox: the sandbox gets its own key, and every command sees S3_ENDPOINT, S3_BUCKET and AWS_* in its environment, which boto3 and aws understand. Storage is reachable even with egress="none". A fork gets its own key, and deleting the sandbox (including by TTL) revokes the key.

sb = Sandbox.create(template="python", egress="none", bucket="data")   # bucket_mode="read" for read-only
sb.exec("python3 -c 'import boto3, os; boto3.client(\"s3\").upload_file(\"out.csv\", os.environ[\"S3_BUCKET\"], \"out.csv\")'")

Sandbox API

Sandbox.create(template=, cpus=, memory_mb=, disk_gb=, idle_timeout_secs=, ttl_secs=, port=, egress=, bucket=, bucket_mode=) Create. Returns when the sandbox is ready for exec. egress: "internet" (default, no SMTP), "none", or a list such as ["1.1.1.1:53", "10.0.0.0/8"] (only those). ttl_secs deletes it after N seconds (insurance against forgotten sandboxes), idle_timeout_secs puts an idle one to sleep, bucket attaches a storage bucket (see above)
Sandbox.connect(id) Connect to an existing sandbox
sb.exec(cmd, env=, cwd=, timeout=60, stdin=) A string runs through sh -c, a list is used as argv. Returns ExecResult(exit_code, stdout, stderr, timed_out, truncated), .ok, .check()
sb.exec_stream(cmd, ...) · sb.exec(cmd, on_stdout=, on_stderr=) Output as it appears (no 1 MiB limit): an iterator of OutputChunk(stream, data), then .result (ExecResult). Leaving the loop kills the command
sb.files.write(path, data, mode=) · read(path) · read_text(path) Files (up to 64 MiB; binary data as bytes)
sb.run_code(code, timeout=60) · sb.reset_code() Stateful Python (python template) -> CodeResult
sb.sleep() · sb.wake() Sleep / wake; exec on a sleeping sandbox raises ConflictError
sb.fork(snapshot=None, ttl_secs=None) · sb.snapshot(name) A clone (from the current state or a snapshot) / a named snapshot
sb.set_idle_timeout(secs) Sleep after N idle seconds (0 - never, None - platform default); immediate, no restart
sb.set_ttl(secs) · sb.info.expires_at Extend the lifetime (from now) or remove it (None); when it will be deleted
sb.set_egress(...) Change the network policy immediately (drops open connections)
sb.delete() Delete (already gone is not an error); with does it for you
Client(api_key, base_url) A shared connection: client.create(), client.list(), client.get(id), client.usage(start, end)

Errors are subclasses of SandboxError (.status is the HTTP code): AuthError 401, QuotaError 403 (organization limit), NotFoundError 404, ConflictError 409, BadRequestError 400/422, CapacityError 503 (no capacity - retry), TimeoutError 504, ServerError - anything else and API unavailability; InterpreterError - run_code on a template without an interpreter.

Hosting: apps, databases, buckets

from microcld import Client

c = Client()
c.databases.create("pg", size_gb=5)
c.buckets.create("media")
c.apps.create("web", port=8080, git={"url": "https://github.com/me/web"})
c.apps.attach_database("web", "pg")            # DATABASE_URL in the app env
c.apps.attach_bucket("web", "media", env_prefix="MEDIA")
c.apps.add_domain("web", "example.com")
print(c.apps.logs("web"), c.account.billing())
Resource Methods
c.apps list get create update delete deploy rollback deployments deployment_log logs rotate_webhook · domains add_domain verify_domain remove_domain · attach_database detach_database attach_bucket detach_bucket
c.databases list get create (branch with from_database, from_snapshot) update delete · snapshots create_snapshot delete_snapshot restore · dumps download_dump
c.buckets list get create delete(force=) · keys create_key revoke_key · presign
c.account me billing pricing limits

Responses are dicts, as in the REST API. AsyncClient has the same methods; every call must be awaited (await c.apps.list()).

CLI

microcld login                      # key -> ~/.config/microcld/config.json (mode 0600)
microcld apps create web --port 8080 --git https://github.com/me/web
microcld db create pg --size-gb 5 && microcld apps attach-db web pg
microcld buckets create media && microcld buckets presign media a.csv --expires 3600
microcld sandbox create --template python      # prints the id
microcld sandbox exec <id> -- python3 -V       # exit code is the command's
microcld balance

Groups: apps (ls show create deploy rollback deployments logs rm env domain attach-db detach-db attach-bucket detach-bucket), db (ls show create rm snapshots snapshot snapshot-rm restore dumps), buckets (ls show create rm key presign), sandbox (ls create exec rm), plus whoami balance pricing limits usage. --json before the command prints JSON. The environment (SANDBOX_API_KEY, SANDBOX_API_URL) wins over the file saved by login. Exit codes: 0 success, 1 API error, 2 bad arguments.

Development

cd sdk/python
python3 -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest -q                         # fake API (httpx.MockTransport)
SANDBOX_E2E=1 SANDBOX_API_KEY=mc_... .venv/bin/python -m pytest tests/test_e2e.py -v -s   # against a real API

Metadata

Release files for microcld 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for microcld 0.1.0
File Size Uploaded Provenance Actions
microcld-0.1.0.tar.gz 26.9 kB Without provenance Details for microcld-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for microcld 0.1.0
File Interpreter ABI Platform Actions
microcld-0.1.0-py3-none-any.whl Python 3 none any Details for microcld-0.1.0-py3-none-any.whl

Total release size: 58.4 kB

Release files / microcld-0.1.0.tar.gz

Download URL microcld-0.1.0.tar.gz
Size 26.9 kB
Tags Source
SHA-256 checksum
How to use checksums
68b3cf65ac213f1cc62ecc42671eb522cd88ea6397bd97a30d883077e8ba67c3
BLAKE2b-256 checksum
How to use checksums
70179325fea32e00ce4c66332d4f8ddb4294d5de52c71524185ee2e89fff2359
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / microcld-0.1.0-py3-none-any.whl

Download URL microcld-0.1.0-py3-none-any.whl
Size 31.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a5cc53795d0ae4e5a6d4906f56a796e73c9fa8919646ec34b7f0045ff533fc82
BLAKE2b-256 checksum
How to use checksums
59eb49669560c3859d7c6311ffd5eb2c01635ba5a53972fbf1babd816b38251f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Security for microcld 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page