MainSDK
Search⌘K
How-to

Use MainSDK from Rust

Add the native mainsdk crate to a Rust service, store data and files as one owner, and return problem details from axum.

Planned for 1.5

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
Updated 2026-10-28

You will add the mainsdk crate, run it on its local drivers, and wire an axum route. It takes about 15 minutes. The crate is native Rust: it shares no code with the TypeScript SDK, only main.yaml, the error codes and the wire formats.

You need

  • Rust 1.85 or later and a main.yaml with schema: 1 (npx mainsdk init writes one).
  • The crate is pre-release and not on crates.io. Use a path or git dependency from the MainSDK repository until it is published.

1. Add the crate

Cargo.toml
[dependencies]
mainsdk = { path = "../mainsdk/packages/rust/mainsdk", features = ["kv", "files", "axum"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }

Each interface sits behind a feature of the same name. config and log are on by default.

2. Call an interface

Calls are builders you .await directly. A tenant-scoped call needs a tenant, which travels in a task-local Context.

src/main.rs
use mainsdk::prelude::*;

#[tokio::main]
async fn main() -> mainsdk::Result<()> {
    let app = App::load().await?;
    Context::new().tenant("tnt_a").user("u1").scope(async {
        app.kv().set("cart", 3).await?;
        let file = app.files().upload(b"hello".to_vec()).owner("u1").await?;
        app.log().info("uploaded").field("file", &file.id).await;
        Ok::<_, MainError>(())
    }).await
}

Spawn with mainsdk::spawn, not tokio::spawn, so the context follows the task. A call that needs a tenant and has none fails with MAIN-TENANT-0001.

3. Return problem details from axum

src/server.rs
let router = axum::Router::new().route("/cart", handler).layer(app.layer());

The layer reads traceparent, sets the context, and renders any MainError as application/problem+json with its code, fix and trace_id.

4. Cancel and time out

Drop the future to cancel a call and release what it held. To stop one on purpose, pass a token with .cancel(token); the call fails with MAIN-SDK-0001.

5. Check the project

npx mainsdk check runs clippy (MAIN-RUST-0001), cargo-audit (MAIN-RUST-0002) and cargo-deny (MAIN-RUST-0003) with the usual gates. A missing tool is a warning, MAIN-RUST-0004, never a pass. Run cargo test too.

Next

  • Key-value store, Files and the other first-cut interface pages show a Rust tab.
  • Rust agents load the mainsdk-rust skill.

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet