Every action is a typed tool
Each #[action] is an API endpoint and a tool for AI agents. Its JSON schema comes from the input type, its description from the doc comment.
Alpha. Not on crates.io yet.
AI agents write the code. People review it. The framework keeps it safe.
Each #[action] is an API endpoint and a tool for AI agents. Its JSON schema comes from the input type, its description from the doc comment.
Agents act as the signed in user. ai = false hides an action from models, confirm makes the user approve it first, and every agent call is audited.
A missing permission, a missing translation or a wrong field type fails at compile time, with a message that says what to change.
zeyn mcp gives coding agents the docs, a structural check of the app and its tests as tools.
axum handles HTTP. Zeyn adds what a business app needs on top of it, the way Django sits on WSGI.
| You get | axum | Zeyn |
|---|---|---|
| Routing, middleware, async HTTP | Yes | Yes (it is axum) |
| Models, validation on deserialize, row versions | No | Yes |
| Server rendered screens, DataGrid, forms, pickers | No | Yes |
| Permissions on every action, CSRF, strict CSP, audit, masking | No | Yes |
| Translations checked at compile time, time zones, money | No | Yes |
| API with OpenAPI, MCP tools, embedded assistant | No | Yes |
#[action(permission = "sales.order.read")]
pub async fn search_orders(q: DataQuery, store: OrderStore) -> Result<Page<Order>> {
Ok(q.apply(store.all()?))
}
#[screen(route = "/", permission = "sales.order.read")]
pub fn order_list() -> Screen {
Screen::new(t!("orders.title")).body(
DataGrid::of(search_orders).column(Order::code).filter().column(Order::total).sum(),
)
}
No HTML, CSS or JavaScript to write. The grid pages, filters and sums on the server, and the same action answers the API and AI agents.