ACTIONS
Write it once.
Ship it everywhere.
One Quickback Action becomes a typed REST endpoint, an MCP server tool, and an admin UI surface — all with the same compiled security. No glue code. No drift. No duplicate wiring.
The primitive
Actions are how Quickback handles the business logic CRUD can't — approvals, state transitions, webhook handlers, external API calls, async jobs. Anything typed, gated, and audited. You define one. Quickback compiles it into every surface your users, admins, and their agents need to reach it.
export default defineTable(applications, {
firewall: { organization: {} },
actions: {
advance: {
description: 'Advance an application to the next stage.',
input: z.object({
notes: z.string().optional(),
}),
access: {
roles: ['recruiter', 'hiring-manager'],
record: { stage: { not: 'hired' } },
},
execute: async ({ db, ctx, record, input }) => {
// your business logic — never overwritten on recompile
return { stage: 'interview', advancedBy: ctx.user.id };
},
},
},
}); That's the whole definition. Below is what Quickback compiles from it.
Surface 1
A typed REST endpoint
For your web app, your mobile client, your internal tools — a fully typed, validated, role-gated route.
POST /api/v1/applications/:id/actions/advance Auth, Zod validation, role checks, and record-level preconditions are all wired in. Your frontend calls it. The compiler enforces the rules.
Surface 2
An MCP server tool
For Claude, Cursor, ChatGPT Desktop, or any agent host that speaks Model Context Protocol. The mapping is 1:1:
- ✓Your Zod schema → the tool input schema
- ✓Your
descriptionfield → the tool description - ✓Your role + record rules → preconditions the LLM can reason about
- ✓Your structured errors → agent-debuggable feedback
npx @kardoe/quickback compile --target mcp No separate MCP server to build. No drift between your API and the manifest. No extra surface to keep in sync.
Surface 3
An admin UI, via Quickback CMS
Every Action compiles into a schema-driven admin panel. Forms built from your Zod inputs. Buttons gated by your access rules. Record-level conditions enforced automatically.
Your ops team advances candidates from the dashboard. Your support team approves invoices. Your admins trigger workflows without waiting for a deploy.
No admin UI to build. No separate auth. No "will this button actually enforce our rules" anxiety — it enforces the same rules as the REST endpoint, because it's the same compiled Action.
Bonus Surface
A typed tool for any agent framework
Because the output is standard TypeScript with Zod schemas, every Action works as a tool definition in:
bindTools directlytools arraytool() accepts your Zod schema as-isNot through an adapter. Not through a wrapper. Because a Quickback Action is a typed tool definition.
Same security. Every surface.
The four security pillars — Firewall, Access, Guards, Masking — are compiled into the Action itself, not layered onto any one surface. So:
- ✓A recruiter calls
advancefrom the web app → role check runs - ✓An LLM calls it via MCP → the same role check runs
- ✓An admin clicks the button in the CMS → the same role check runs
- ✓A LangChain agent fires it as a tool → the same role check runs
There's no "MCP mode" that bypasses your rules. No admin panel that elevates privileges. No REST flag that disables masking. The compiler refuses to emit an Action that can be called without its declared security.
Agents don't get a broader surface area than a human caller with the same role. Same rules, same route handlers, same compiled checks.
Errors agents can read
When an access check fails, Quickback returns a structured error with the layer, the reason, and the required conditions. An LLM can read this, reason about it, and adjust — instead of retrying blindly.
{
"error": "ACCESS_DENIED",
"layer": "access",
"resource": "applications",
"operation": "advance",
"reason": "User role 'member' cannot advance applications.",
"requiredRoles": ["recruiter", "hiring-manager"]
}
Compare to 403 Forbidden. A human can debug one. An agent can debug both.
Why this matters now
Model Context Protocol was donated to the Linux Foundation's Agentic AI Foundation in December 2025. Anthropic reports over 10,000 active public MCP servers and 97M monthly SDK downloads across Python and TypeScript. Forrester predicts 30% of enterprise app vendors will launch their own MCP servers — and the ones that don't won't be reachable by their customers' agents.
Building an MCP server from scratch means building a tool manifest, input validation, auth-aware execution, tenant scoping, structured errors, and a server you keep in sync with your main API, forever.
Or you compile one from the Actions you've already defined.
Recompile-safe by design
Your definitions are the source of truth. The generated src/ folder is a build artifact. Your execute() handler — the business logic you wrote by hand — lives in your own files and is never overwritten by the compiler.
Recompile to tighten a rule, add a new surface, or switch deployment targets. Your handlers stay exactly where you left them.
"Why not build this myself?"
You can. It's not hard to stand up a REST endpoint. It's not hard to write an MCP server. It's not hard to build an admin panel.
It's hard to build all three and keep them in sync when your rules change. It's very hard to prove to an auditor that they all enforce the same security. It's nearly impossible to do it without the three surfaces drifting apart six months in.
A compiler doesn't drift. That's the argument.
Try it
Scaffold a Hono API with Actions in under 60 seconds. Add the MCP target with one flag. Add the CMS with one command. Same definitions, every surface.