Typed host-side helpers for Convex backends — builders, errors, validators, relationships, pagination, HTTP, env, and observability.
import { asyncMap, pruneNull, nullThrows } from "@vllnt/convex-helpers";
const docs = pruneNull(await asyncMap(ids, (id) => ctx.db.get(id)));
const doc = nullThrows(await ctx.db.get(id), `Document ${id} not found`);A helpers library (not a sandboxed Convex component): pure functions and host-ctx glue that run with
the host's ctx — no own tables, no app.use() mounting. Stateful concerns (rate limiting,
idempotency, flags) belong in @convex-dev/* or @vllnt/convex-* components.
See ROADMAP.md for the planned module surface.
asyncMap— parallel async map that preserves orderpruneNull— filter null/undefined from arraysnullThrows— non-null assertion with typedNullDocumentError./mcp— expose Convex functions as MCP tools (createMCPServer+query/mutation/action/resource), default-deny auth, optional cursor pagination- [planned]
./builders—customQuery/customMutation/customAction/customCtx - [planned]
./errors— typedAppError+ HTTP-status map - [planned]
./auth— provider-agnosticrequireIdentity/getCurrentSubject - [planned]
./env—defineEnv(zodSchema)cold-start validation - [planned]
./tracing— span emit +traceparentpropagation via@vllnt/logger - [planned]
./testing— fixture factories +withIdentity - [planned]
./relationships,./validators,./pagination,./rls,./triggers,./http - [planned]
./react— optional tree-shakeable front-end hooks layer
pnpm add @vllnt/convex-helpersconvex is a peer dependency:
pnpm add conveximport { asyncMap, pruneNull, nullThrows } from "@vllnt/convex-helpers";
// Parallel fetch with null filtering
const docs = pruneNull(await asyncMap(ids, (id) => ctx.db.get(id)));
// Non-null assertion
const doc = nullThrows(await ctx.db.get(id), `Document ${id} not found`);Expose Convex functions to LLM agents over the Model Context Protocol. The ./mcp entry is
tree-shakeable — backend-only consumers pull zero MCP code. It needs two optional peer deps:
pnpm add @modelcontextprotocol/sdk zodimport { createMCPServer, query, mutation } from "@vllnt/convex-helpers/mcp";
import { api } from "./_generated/api";
import { v } from "convex/values";
export const mcp = createMCPServer({
auth: { validate: async (key) => key === process.env.MCP_API_KEY },
tools: {
list_projects: query(api.projects.list, { args: v.object({}), description: "List all projects" }),
create_project: mutation(api.projects.create, {
args: v.object({ name: v.string() }),
description: "Create a project",
}),
},
});
// Mount the route handler (e.g. Next.js App Router)
export const { GET, POST } = mcp.handler();Migrating from @vllnt/convex-mcp? Change the import to @vllnt/convex-helpers/mcp — the API is
identical. See docs/API.md for the full ./mcp reference.
See docs/API.md for full API reference with signatures and examples.
pnpm test
pnpm test:coverage # must reach 100%Tests use vitest with the node environment. 100% coverage is enforced via
vitest.config.mts thresholds.
See CONTRIBUTING.md for development setup, code style, and PR guidelines.
Built by bntvllnt · bntvllnt.com · X @bntvllnt
Part of the @vllnt Convex component fleet — vllnt.com
If this is useful, sponsor the work.