Verified against aomi-sdk@main on 2026-06-04.
Throughout this page, App (capitalized) means the deployed unit: a
cdylib crate the runtime loads. The earlier docs talked about aomi_chat::CoreAppBuilder. That is an internal runtime crate and is not how you author a plugin. The real authoring model is the one below: the dyn_aomi_app! macro plus the DynAomiTool trait from the public aomi-sdk crate.What you are building
The Aomi SDK lets you wrap any crypto API as a dynamic plugin. The runtime loads your compiled plugin and routes chat to it. The apps in the SDK repo show the range you can cover:DeFi protocols
Wrap a DEX, lending market, or staking protocol as tools the chat can drive. See
defillama, morpho.Prediction markets
Market discovery, search, and trading flows. See
polymarket, kalshi.Cross-chain intents
Bridge and intent-order clients. See
khalani, across, lifi.Social and accounts
Feeds, posts, user data, wallet and account tooling. See
x, neynar, para.The standard file split
Every App follows the same layout across three files. Keep this split even for small apps. It keeps the surface the model sees easy to scan and keeps API details out of your tool logic.| File | What it owns |
|---|---|
src/lib.rs | The preamble string and the dyn_aomi_app! registration. This is your manifest surface: app name, version, the tool list, and the host namespaces you depend on. |
src/client.rs | HTTP client setup, auth headers, response models, and the typed argument structs your tools accept. Outside API details stay here. |
src/tool.rs | The tool implementations. Each tool implements DynAomiTool, carries a description the model reads, and maps client responses into stable JSON. |
sdk/examples/app-template-http from the SDK repo and adapt it. It is the canonical reference for this layout.
src/lib.rs: registration and preamble
lib.rs is short on purpose. It declares the other modules, holds the preamble, and calls dyn_aomi_app! to register the App. Here is the full lib.rs from the HTTP template:
dyn_aomi_app! macro is the single registration point. It generates the plugin entry the runtime looks for, so you never write #[no_mangle] by hand. Its fields:
| Field | Meaning |
|---|---|
app | Your app type. It is a small struct (often unit-like) that holds any shared state. |
name | The app slug. Kebab-case. This is how the runtime and the URL refer to your App. |
version | The app version string. |
preamble | The system prompt that shapes behavior (see below). |
tools | The list of tool types the model can call. Keep it small: 3 to 8 tools is typical. |
namespaces | The host capability sets your App depends on. [] for a read only HTTP wrapper (see “Declaring host namespaces”). |
Writing the preamble
The preamble is the system prompt. It is the single most important thing you author, because it decides whether the model picks the right tool at the right time. Write it as plain prose with clear headings, not as code. A strong preamble does four things:States the role
One or two sentences on what the App is and what it must never do. The DeFiLlama app opens with “You are a read only analyst… never to execute trades.”
Maps capabilities to tools
List what the App can do and name the exact tool for each job, so the model learns the mapping. “Token prices:
defillama_get_token_price for current price.”Pins down identifiers and conventions
Spell out the formats the API expects: coin id schemes, protocol slugs, chain names, timestamp units. This is where most tool call errors are prevented.
defillama app:
Defining tools
src/tool.rs. Each tool is a type that implements DynAomiTool. The trait ties together your app type, the typed argument struct, the name and description the model reads, and a run function that does the work and returns JSON.
NAMEis the function name the model calls. Prefer names shaped by intent likesearch_*,get_*,build_*, andsubmit_*over raw endpoint wraps.DESCRIPTIONis read by the model when it decides whether to call the tool. Write it for the model, not for a human reader.runreturnsResult<Value, String>. On error, return a short actionable string. Normalize upstream API errors so the model gets a clean message, not a raw stack.
Typed, documented arguments
The argument struct lives insrc/client.rs. Derive Deserialize and JsonSchema, and put a doc comment on every field. Those doc comments become the parameter descriptions the model sees, so they directly shape how it fills the call.
Doc comments on argument fields are read by the model. A vague field comment is a vague tool. Give a concrete example value in each one, the way the template does.
The client and models
src/client.rs owns the HTTP plumbing so your tools stay readable. Build the client once, set a timeout, normalize errors into strings, and keep the response shapes here.
For full trait signatures (
DynAomiTool, DynToolCallCtx, the dyn_aomi_app! macro, and the aomi_sdk::testing helpers like TestCtxBuilder and run_tool), see the SDK Reference.Declaring host namespaces
Thenamespaces field in dyn_aomi_app! declares which host capabilities your App needs. A read only HTTP wrapper needs none, so it uses namespaces = [], like the template above. An App that reads chain state or stages transactions declares a namespace, the way the defillama app does:
| Host tool | What it does |
|---|---|
view_state | Encode calldata from ABI arguments and run a read only eth_call. |
run_tx | Encode calldata and simulate a call that changes state without staging it. |
stage_tx | Stage an EVM transaction for later simulation and signing. |
simulate_batch | Simulate one or more staged transactions before prompting a wallet. |
commit_tx | Ask the user wallet to sign and broadcast one staged transaction. |
evm_commit_message | Ask the user wallet to sign an EIP-712 typed-data message. |
Apps that execute across several steps (like
polymarket or khalani) return a ToolReturn with route hints instead of a bare JSON value, so the host can chain a wallet signature back into the next tool call. The full route hint contract lives in the SDK repo’s docs/host-interop.md and the SDK Reference.The aomi.toml manifest
If you are building a community or partner App in your own source repo, you also author anaomi.toml. This manifest tells the deploy tooling where your App ships and how the backend should load it. (Official Apps that live in the SDK repo do not use aomi.toml; they ship through the repo’s release pipeline instead.)
| Field | Required | Meaning |
|---|---|---|
name | yes | The App slug. Kebab-case. Becomes the release tag. |
display_name | yes | A name shown to people, easy to read. |
platform | yes | The publishing platform, for example community. |
git | yes | The platform repo your App ships into. |
public | yes | Whether the App is visible to all backend users. |
server_tags | no | The backend tiers this build may load on. Defaults to ["staging"]. |
access_token | no | Only for private platform repos. A $ENV_VAR reference, never a literal. |
Pin the SDK version
YourCargo.toml must pin aomi-sdk to the exact version the platform expects, and your crate must build as a cdylib:
=0.1.20 is the pin the community-apps platform expects today. Always confirm against platform.json’s required_sdk_version in the platform repo before you deploy; a mismatch fails CI with an sdk_version mismatch error.Authoring guidelines
- Prefer one App crate per external product or ecosystem.
- Keep the toolset small. 3 to 8 tools per App is typical for a clean workflow.
- Keep tool arguments typed and documented with
JsonSchema. The doc comments are read by the model. - Normalize upstream API errors into short actionable strings.
- Keep assumptions about one host out of your prompts.
- If your App needs signing or execution, depend only on the public host capabilities above.
Next steps
You have authored the crate. Now compile it, test it, and ship it.Compile and run
Use the
aomi-build CLI to compile your plugin and aomi-run to exercise it locally before you ship.Deploy and activate
Use
aomi-git deploy to stage your source into the platform repo, then request activation so the backend loads it.SDK Reference
Full trait definitions:
DynAomiTool, the dyn_aomi_app! macro, the host-interop contract, and the testing helpers.How it works
See the full request flow once your App is loaded on the runtime.