KavoTurn models into APIs
Define an entity once and get a complete REST, GraphQL, and MCP CRUD API with filtering, sorting, pagination, 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, 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 your agent moves just as fast.
/plugin marketplace add kavo-labs/kavo
/plugin install kavo-skills@kavo-marketplaceFewer 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 — no second registry, 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 — all driven by the query string, no extra code.
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
}Customize it to fit your needs
Every setting is optional at every scope — start with the defaults and override only where your app needs to differ.
Global→Entity→Operation→Per-call
No config at all still works — built-in defaults cover every setting until you decide to change one.
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.