KavoTurn models into APIs
Define an entity once and get a complete REST, GraphQL, and MCP CRUD API with filtering, sorting, pagination, realtime events, and generated routes. Vibe code it in minutes, on a fraction of the tokens.
Define an entity once and get a complete REST, GraphQL, and MCP CRUD API with filtering, sorting, pagination, realtime events, and generated routes. Vibe code it in minutes, on a fraction of the tokens.
Works with your stack
Stop writing repetitive CRUD endpoints.
No pagination, filtering, sorting, or field selection.
@Controller("books")
export class BooksController {
constructor(private readonly repository: BookRepository) {}
@Post()
create(@Body() body: { title: string; author: string }) {
return this.repository.create({
data: {
title: body.title,
author: body.author,
},
});
}
@Get()
findAll() {
return this.repository.findMany({
where: {
deletedAt: null,
},
orderBy: {
id: "desc",
},
});
}
@Get(":id")
findOne(@Param("id", ParseIntPipe) id: number) {
return this.repository.findOne({
where: {
id,
deletedAt: null,
},
});
}
@Patch(":id")
update(@Param("id", ParseIntPipe) id: number, @Body() body: { title?: string; author?: string }) {
return this.repository.update({
where: {
id,
deletedAt: null,
},
data: {
title: body.title,
author: body.author,
},
});
}
@Delete(":id")
remove(@Param("id", ParseIntPipe) id: number) {
return this.repository.delete({
where: {
id,
},
data: {
deletedAt: new Date(),
},
});
}
@Patch(":id/restore")
restore(@Param("id", ParseIntPipe) id: number) {
return this.repository.restore({
where: {
id,
},
data: {
deletedAt: null,
},
});
}
}Pagination, filtering, sorting, field selection, and more, all included.
@Kavo(Book)
@Controller("books")
export class BooksController {}Built for agentic development
Built with Claude Code, and shipped with skills so Claude, Codex, Antigravity, and other coding agents move just as fast.
npx skills add kavo-labs/kavoFewer tokens, ship faster.
Everything a CRUD API needs
One decorator, the full surface, configurable at global, entity, operation, and per-call scope.
Expose your API to agents with MCP
The same engine behind REST and GraphQL exposes every entity as an MCP toolset, with no second registry and no hand-written schemas.
Every standard operation, for every @Kavo entity, unconditionally: an agent gets the same filtering, pagination, and soft-delete semantics a REST or GraphQL client does, because it calls the same engine.
The query grammar, on the wire
Filtering, sorting, pagination, and includes are all driven by the query string. No extra code to write.
GET /books
?filter[status][eq]=published
&filter[publishedAt][gte]=2020-01-01
&sort=-publishedAt
&include=author
&limit=2{
"items": [
{
"id": 42,
"title": "The Left Hand of Darkness",
"status": "published",
"author": { "id": 7, "name": "Ursula K. Le Guin" }
},
{
"id": 41,
"title": "Kindred",
"status": "published",
"author": { "id": 3, "name": "Octavia E. Butler" }
}
],
"limit": 2,
"offset": 0,
"total": 128
}Realtime, without a second system
Every create, update, patch, and delete already flows through one engine, so publishing it as an event is a config flag, not a new pipeline. SSE ships today; WebSocket, RabbitMQ, and Kafka plug into the same RealtimeTransport interface.
Where teams put it to work
Not a replacement for your ORM
Kavo doesn't own your data model. It sits on top of the entity you already defined.
Swap ORMs later and the API surface doesn't move. RepositoryAdapter is the only seam that changes.