Install
$ agentstack add mcp-vobase-vobase ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
English / 中文
vobase
The app framework built for AI coding agents. Own every line. Your AI already knows how to build on it.
what you get · get started · code · harness · compare · docs
A full-stack TypeScript framework that gives you auth, database, storage, jobs, and a first-class AI agent runtime in a single Bun process. Docker Compose Postgres for local dev, managed Postgres in production. Like a self-hosted Supabase — but you own every line of code. Like Pocketbase — but it's TypeScript you can read and modify.
AI coding agents (Claude Code, Cursor, Codex) understand vobase out of the box. Strict conventions and a uniform module shape mean generated code works on the first try — not the third.
You own the code. You own the data. You own the infrastructure.
what you get
One bun create vobase and you have a working full-stack app:
| Primitive | What it does | |---|---| | Runtime | Bun — native TypeScript, ~50ms startup, built-in test runner. One process, one container. | | Database | PostgreSQL via Drizzle. Docker Compose Postgres (pgvector/pg17) for local dev, managed Postgres in production. Full SQL, ACID transactions, pgvector for embeddings. | | Auth | better-auth. Sessions, passwords, email OTP, CSRF. RBAC with role guards, API keys, organizations. SSO/2FA as plugins. | | API | Hono — ~14KB, typed routing, Bun-first. Every AI coding tool already knows Hono. | | Audit | Built-in audit log, record change tracking, and auth event hooks. Every mutation is traceable. | | Sequences | Gap-free business number generation (INV-0001, PO-0042). Transaction-safe, never skips. | | Storage | File storage with virtual buckets. Local or S3/R2 backends. Metadata tracked in Postgres. | | Channels | Multi-channel messaging with pluggable adapters: WhatsApp (Cloud API), email (Resend, SMTP). Inbound webhooks, outbound sends, delivery tracking. All messages logged. | | Integrations | Encrypted credential vault for external services. AES-256-GCM at rest. Platform-aware: opt-in multi-tenant OAuth handoff via HMAC-signed JWT. | | Jobs | Background tasks with retries, cron, and job chains. pg-boss backed — Postgres only, no Redis. | | Realtime | Server-push via PostgreSQL LISTEN/NOTIFY + SSE. No WebSocket. Modules pg_notify after commit; the frontend hook invalidates matching TanStack Query keys. | | Agent harness | First-class AI agent runtime (pi-agent-core + pi-ai). Frozen system prompt per wake, byte-stable provider cache, tool budget spill, steer/abort between turns, journaled events, idle resumption, restart recovery. | | Workspace | Virtual filesystem materialized per-wake from your modules. AGENTS.md is composed from per-module fragments; agents read /staff//profile.md, /contacts//MEMORY.md, etc. RO enforcement at the FS boundary. | | CLI | @vobase/cli — standalone, catalog-driven binary. Modules register verbs via defineCliVerb; the same body runs in-process (agent bash sandbox) and over HTTP-RPC (vobase binary). | | Frontend | React + TanStack Router + shadcn/ui + ai-elements + DiceUI + Tailwind v4. Type-safe routing with codegen, code-splitting. You own the component source. | | MCP | Model Context Protocol server in the same process. AI tools can read your schema, list modules, and view logs before generating code. | | Deploy | Dockerfile + railway.json included. One railway up or docker build and you're live. |
Locally, docker compose up -d starts a pgvector/pg17 Postgres instance. bun run dev and you're building. In production, point DATABASE_URL at any managed Postgres.
quick start
bun create vobase my-app
cd my-app
docker compose up -d
bun run db:reset
bun run dev
Backend on :3001, frontend on :5173. Ships with the agent-native helpdesk template — messaging, channels, contacts, team, drive, agents — already wired up.
what you can build
Every module is a self-contained directory: schema, service, handlers, jobs, pages, and an agent.ts slot that publishes tools, materializers, RO hints, and AGENTS.md fragments to the harness. No plugins, no marketplace. Just TypeScript you own.
| Use Case | What Ships | |---|---| | Agent-native helpdesk | The default template. WhatsApp + email inbox, contact memory, staff-mention fan-out, supervisor coaching, scheduled follow-ups, approval gates, drive overlays. | | SaaS Starter | User accounts, billing integration, subscription management. Auth + jobs + webhooks handle the plumbing. | | Internal Tools | Admin panels, operations dashboards, approval workflows. Status machines enforce business logic. Audit trails track every change. | | CRM & Contacts | Companies, contacts, interaction timelines, deal tracking. Cross-module references via service imports — no FK across module boundaries. | | Project Tracker | Tasks, assignments, status workflows, notifications. Background jobs handle reminders and escalations. | | Billing & Invoicing | Invoices, line items, payments, aging reports. Integer money ensures exact arithmetic. Gap-free numbering via transactions. | | Your Vertical | Property management, fleet tracking, field services — whatever the business needs. Describe it to your AI tool. It generates the module. |
AI coding agents generate modules from your conventions. Like npx shadcn add button — files get copied, you own the code.
how it works
Vobase makes itself legible to every AI coding tool on the market.
The framework ships with one canonical module shape, one write-path discipline, and a harness that AI agents drive at runtime. When you need a new capability:
- Open your AI tool and describe the requirement
- The AI reads your existing schema, the canonical module shape, and the relevant
.claude/skills/packs - It generates a complete module — schema, service, handlers, jobs, pages, agent slot, tests, seed data
- You review the diff, run
bun run dev, and it works
Skill packs cover the parts where apps get tricky: money stored as integer cents (never floats), status transitions as explicit state machines (not arbitrary string updates), gap-free business numbers generated inside database transactions, single-write-path enforcement via check:shape, frontend bundle isolation via check:bundle.
These conventions are what make AI-generated modules work on the first try.
The thesis: your specs and domain knowledge are the asset. AI tools are the compiler. The compiler improves every quarter. Your skills compound forever.
what a module looks like
Every module is a thin aggregator over sibling files. module.ts declares the contract; everything else lives next to the code that owns the side-effect.
// modules/projects/module.ts
import type { ModuleDef } from '~/runtime'
import { projectsAgent } from './agent'
import { projectListVerb } from './verbs/project-list'
import { createProjectsService, installProjectsService } from './service/projects'
import * as web from './web'
const projects: ModuleDef = {
name: 'projects',
requires: ['team'],
web: { routes: web.routes },
jobs: [],
agent: projectsAgent,
init(ctx) {
installProjectsService(createProjectsService({ db: ctx.db }))
ctx.cli.registerAll([projectListVerb])
},
}
export default projects
modules/projects/
module.ts ← thin aggregator (above)
schema.ts ← Drizzle table definitions
state.ts ← status transitions, state machine
service/ ← transactional write-path (sole writer of this module's tables)
handlers/ ← Hono routes (HTTP API)
web.ts ← route barrel mounted under /api/projects
pages/ ← React pages — list, detail, create
components/ ← React components owned by this module
hooks/ ← TanStack Query hooks
jobs.ts ← pg-boss handlers
agent.ts ← agent slot: tools, materializers, roHints, AGENTS.md fragments
tools/ ← defineAgentTool — colocated with the service
verbs/ ← defineCliVerb — runs in agent bash and the CLI binary
cli.ts ← barrel exporting Verbs
seed.ts ← demo data
defaults/ ← *.agent.yaml, *.schedule.yaml — opt-in starter content
skills/ ← inline skill bodies the agent reads via drive overlay
*.test.ts ← colocated bun test
schema example — Drizzle + PostgreSQL with typed columns, timestamps, status enums
// modules/projects/schema.ts
import { pgTable, text, integer, timestamp, check } from 'drizzle-orm/pg-core'
import { sql } from 'drizzle-orm'
import { nanoidPrimaryKey } from '@vobase/core'
export const projects = pgTable('projects', {
id: nanoidPrimaryKey(),
name: text('name').notNull(),
description: text('description'),
status: text('status').notNull().default('active'),
ownerId: text('owner_id').notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
}, (t) => [
check('projects_status_chk', sql`${t.status} in ('active','archived','deleted')`),
])
export const tasks = pgTable('tasks', {
id: nanoidPrimaryKey(),
projectId: text('project_id').references(() => projects.id),
title: text('title').notNull(),
status: text('status').notNull().default('todo'),
assigneeId: text('assignee_id'),
priority: integer('priority').notNull().default(0),
}, (t) => [
check('tasks_status_chk', sql`${t.status} in ('todo','in_progress','done')`),
])
check:shape enforces that only service/projects.ts writes to projects — handlers and jobs go through the service.
handler example — Hono routes with Zod validation, typed RPC client
// modules/projects/handlers/list.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
import { getCtx } from '~/runtime'
import { projectsService } from '../service/projects'
export const listRoute = new Hono().get(
'/',
zValidator('query', z.object({ status: z.enum(['active','archived']).optional() })),
async (c) => {
const ctx = getCtx(c)
const { status } = c.req.valid('query')
const items = await projectsService().list({ ownerId: ctx.user.id, status })
return c.json(items)
},
)
The frontend gets fully typed API calls via the Hono RPC client (src/lib/api-client.ts):
import { useQuery } from '@tanstack/react-query'
import { api } from '@/lib/api-client'
export function useProjects() {
return useQuery({
queryKey: ['projects'],
queryFn: async () => {
const res = await api.projects.$get({ query: {} })
return await res.json() // fully typed
},
})
}
use-realtime-invalidation.ts maps pg_notify table payloads onto the first element of TanStack queryKey — services emit notifies after commit and the UI re-fetches automatically.
job example — background tasks via pg-boss, no Redis
// modules/projects/jobs.ts
import { defineJob } from '@vobase/core'
import { projectsService } from './service/projects'
export const sendReminder = defineJob('projects:send-reminder',
async (data: { taskId: string }) => {
await projectsService().notifyAssignee(data.taskId)
},
)
Schedule from handlers or services: ctx.scheduler.add('projects:send-reminder', { taskId }, { delay: '1d' }). Retries, cron scheduling, and priority queues — all Postgres-backed via pg-boss.
agent slot example — tools, materializers, AGENTS.md fragments
// modules/projects/agent.ts
import { defineAgentTool, defineIndexContributor } from '@vobase/core'
import { projectsService } from './service/projects'
const createTask = defineAgentTool({
name: 'create_task',
audience: 'internal',
lane: 'standalone',
// schema: zod input/output…
async handler({ input, ctx }) {
return await projectsService().createTask(input)
},
})
export const projectsAgent = {
agentsMd: [defineIndexContributor({
file: 'AGENTS.md',
priority: 50,
name: 'projects.overview',
render: () => '## Projects\n\n- `create_task` to add a task to a project.',
})],
materializers: [/* WorkspaceMaterializerFactory[] */],
roHints: [/* explain why /projects//* paths are read-only */],
tools: [createTask],
}
The wake builder filters tools by lane and audience, runs each materializer factory against the wake context, chains roHints, and feeds the AGENTS.md contributors into the harness. One agent slot per module — no central registry to update.
the ctx object
Every HTTP handler gets a context object with runtime capabilities. Current surface:
| Property | What it does | |---|---| | ctx.db | Drizzle instance. Full PostgreSQL — reads, writes, transactions. | | ctx.user | { id, email, name, role, activeOrganizationId? }. From better-auth session. RBAC middlewares: requireRole(), requirePermission(), requireOrg(). | | ctx.scheduler | Job queue. add(jobName, data, options) to schedule background work. | | ctx.storage | StorageService — virtual buckets with local/S3/R2 backends. | | ctx.channels | ChannelsService — email and WhatsApp sends. All messages logged. | | ctx.integrations | Encrypted credential vault. ctx.integrations.getActive(provider) returns decrypted config or null. | | ctx.http | Typed HTTP client with retries, timeouts, and circuit breakers. | | ctx.realtime | RealtimeService — notify({ table, id?, action? }, tx?) after mutations. SSE subscribers receive the event; the frontend hook invalidates matching TanStack queries. |
Modules can declare an init(ctx: ModuleInitCtx) hook that runs at boot with { db, realtime, jobs, scheduler, auth, cli }. Cross-module callers import from @modules//service/* directly — no port shim, no plugin system. Unconfigured services use throw-proxies that produce descriptive errors if accessed.
App-level config:
// vobase.config.ts
export default defineConfig({
database: process.env.DATABASE_URL,
integrations: { enabled: true }, // opt-in: encrypted credential store
storage: { // opt-in: file storage
provider: { type: 'local', basePath: './data/files' },
buckets: { avatars: { maxSize: 5_000_000 }, documents: {} },
},
channels: { // opt-in: email + WhatsApp
email: { provider: 'resend', from: 'noreply@example.com', resend: { apiKey: '...' } },
},
http: {
timeout: 10_000,
retries: 3,
circuitBreaker: { threshold: 5, resetTimeout: 30_000 },
},
webhooks: {
'stripe-events': {
path: '/webhooks/stripe',
secret: process.env.STRIPE_WEBHOOK_SECRET,
handler: 'system:processWebhook',
signatureHeader: 'stripe-signature',
dedup: true,
},
},
})
Credentials stay in .env. Config declares the shape.
agent harness
The harness is the AI runtime in core. It runs on top of @mariozechner/pi-agent-core + @mariozechner/pi-ai and ships as createHarness({...}) from @vobase/core. Each "wake" is one bounded run of an agent over a frozen system prompt.
Lanes — the template ships two:
- Conversation — bound to
(contactId, channelInstanceId, conversationId). Triggered byinbound_message,supervisor,approval_resumed,scheduled_followup,manual. - Standalone — operator threads + heartbeats. Triggered by
operator_thread,heartbeat. Customer-facing tools are filtered out.
Invariants baked into core:
- Frozen snapshot. System prompt computed once at
agent_start; thesystemHashis identical every turn so the provider's prefix cache stays warm. Mid-wake writes surface in the next turn's side-load. - Steer/abort between turns. Customer messages append to
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: vobase
- Source: vobase/vobase
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.