Taruvi backend provisioning
Use the Taruvi MCP server to provision and modify backend resources. This skill covers tool selection, invocation order, the shapes the tools expect, and the gotchas that don't come through in tool descriptions.
This skill is the control plane. If you're writing Python that runs inside a deployed function, switch to taruvi-functions. If you're building a Refine frontend, switch to taruvi-refine-frontend.
Core principles
- Trust the MCP tool, not your memory. The MCP server is authoritative for Taruvi's current behavior. Read tool responses carefully — they return structured IDs, slugs, and status you need for the next step.
- Verify before destructive ops.
delete_datatable, schema changes that drop columns, policy overwrites, and raw SQL DDL all cause irreversible or hard-to-reverse changes. Always plan → validate → execute for these.
- Frictionless schemas are upserts, not merges.
createupdateschema replaces table definitions — fields missing from your new payload are dropped, not preserved. Always inspect the current schema first.
- Cerbos policies are REPLACEMENTS.
managepolicies(action="createupdate") fully replaces the policy body, not merged.
- Tenant context is ambient. The MCP server resolves tenant + app from the session context. Do not try to pass tenant slugs in tool arguments unless a specific tool accepts
app_slug.
Tool index (24 tools)
Grouped by domain. See [references/mcp-tool-quickref.md](references/mcp-tool-quickref.md) for one-line signatures.
Datatables (5): getdatatableschema, createupdateschema, datatabledata, datatableedges, deletedatatable Storage (1): managestorage (sub-actions: listbuckets, createbucket, listobjects, getquota) Secrets (4): listsecrets, getsecret, createupdatesecret, managesecrettypes Users (3): listusers, createuser, updateuser User attributes (1): userattributesschema Roles (2): manageroles, manageroleassignments Functions (2): managefunction, executefunction Policies (1): managepolicies Analytics (2): managequery, executequery Raw SQL (1): executerawsql Meta (2): managetags, getaidocs
Datatable provisioning
The most common workflow. See [references/datatable-schema-patterns.md](references/datatable-schema-patterns.md) for the full Frictionless reference.
Creating a new datatable
- Inspect existing schema (if the table might exist):
`` getdatatableschema(tablename="orders") `` Returns the current definition or a NOTFOUND error.
- Prepare a Frictionless Data Package. Minimal shape:
``json { "resources": [ { "name": "orders", "schema": { "fields": [ {"name": "id", "type": "integer", "constraints": {"required": true}}, {"name": "customerid", "type": "integer"}, {"name": "total", "type": "number"}, {"name": "createdat", "type": "datetime"} ], "primaryKey": ["id"] } } ] } ``
- Apply:
`` createupdateschema(datapackage={...}) `` Materializes the physical PostgreSQL table automatically. Returns created/updated/error counts.
Type mapping (Frictionless → Postgres)
| Frictionless |
Postgres |
string |
TEXT |
integer |
INTEGER |
number |
NUMERIC |
boolean |
BOOLEAN |
date |
DATE |
datetime |
TIMESTAMP WITH TIME ZONE |
object, array |
JSONB |
Adding indexes, FKs, search, graph
Advanced features (indexes, foreign keys, populate, search, hierarchy/graph edges, column renames) live in [references/datatable-schema-patterns.md](references/datatable-schema-patterns.md). Read that file before authoring non-trivial schemas — the Frictionless shape has a lot of Taruvi-specific extensions (indexes, hierarchy, graph, search_fields, x-rename-from).
CRUD on rows
datatable_data(action="query", table_name="orders", filters={"total__gte": 100}, limit=100)
datatable_data(action="upsert", table_name="orders", data=[{...}, {...}], unique_fields="id")
datatable_data(action="delete", table_name="orders", ids=[1, 2, 3])
(limit defaults to 100, capped at 1000. Omit the arg to use the default.)
Filter operators follow DRF conventions: fieldgte, fieldin=1,2,3, fieldcontains=foo, fieldnull=true, etc.
Graph edges (for hierarchy/graph-enabled tables)
datatable_edges(action="list", table_name="categories")
datatable_edges(action="create", table_name="categories", edges=[{"from_id": 1, "to_id": 2, "type": "parent"}])
datatable_edges(action="delete", table_name="categories", edge_ids=[10])
The table must have been created with graph/hierarchy enabled in its Frictionless schema.
Deleting a datatable
Destructive. Drops the physical table, the edges table (if any), and the metadata row.
delete_datatable(table_name="orders") # fails on FK dependencies
delete_datatable(table_name="orders", force=True) # bypass FK checks — DANGEROUS
Always confirm with the user before passing force=True. Prefer the non-force call first so the error tells you what depends on the table.
Users, roles, and policies
See [references/cerbos-policy-cookbook.md](references/cerbos-policy-cookbook.md) for policy authoring.
Creating a user
createuser accepts username, email, optional password (generate one if omitted), attributes dict, and roleslugs list.
If the user hasn't specified roles, list them first:
manage_roles(action="list")
Pick an appropriate role from the response and assign via role_slugs=["..."] in the create call.
Roles
manage_roles(action="list")
manage_roles(action="create", name="editor", description="...", parent_slug="viewer")
manage_roles(action="bulk_create", roles=[{"name": "..."}, ...])
manage_roles(action="delete", role_slug="editor") # fails if role has members or children
Role assignments
manage_role_assignments(action="assign", roles=["editor"], usernames=["alice", "bob"])
manage_role_assignments(action="revoke", roles=["editor"], usernames=["alice"])
Both roles and usernames accept a single value or list. Assignment supports expires_at (ISO datetime).
User attributes schema
user_attributes_schema(action="get")
user_attributes_schema(action="update", schema={...}) # REPLACES the entire schema
Requires managesite cloud permission. The schema is JSON Schema Draft 2020-12 and is tenant-wide (singleton). Call getai_docs(category="users", topic="attributes") if you need the full authoring guide.
Cerbos policies
manage_policies(action="create_update", policy_data={...}) # REPLACES, does not merge
manage_policies(action="get", policy_id="...")
manage_policies(action="get", name_regexp="order.*") # list with filter
manage_policies(action="enable", policy_id="...")
manage_policies(action="disable", policy_id="...")
Policy authoring is non-trivial. Before writing a policy from scratch, load [references/cerbos-policy-cookbook.md](references/cerbos-policy-cookbook.md) or call getaidocs(category="policies", topic="guide").
Storage
manage_storage(action="list_buckets")
manage_storage(action="create_bucket", name="uploads", visibility="private", app_category="attachments", max_size_bytes=10485760)
manage_storage(action="list_objects", bucket_slug="uploads", prefix="2026/", limit=50)
manage_storage(action="get_quota", bucket_slug="uploads")
appcategory is required for createbucket and must be "assets" or "attachments". The bucket's RLS policy is created automatically by the serializer.
Secrets
See [references/secrets-and-types.md](references/secrets-and-types.md).
Before creating a secret, a secret type must exist that matches the value's shape. System types (OAuth creds, API keys, etc.) are pre-provisioned; create custom types as needed:
manage_secret_types(action="list")
manage_secret_types(action="create", name="stripe-cred", description="...", schema={...}, sensitivity_level="sensitive")
Then:
create_update_secret(key="STRIPE_KEY", value="sk_live_...", secret_type="stripe-cred", tags=["prod"])
get_secret(key="STRIPE_KEY") # returns [ENCRYPTED] for non-public sensitivities
list_secrets(secret_type="stripe-cred")
Sensitivity levels: public (value returned verbatim), private, sensitive (value masked in responses).
Functions (registration)
manage_function(action="list")
manage_function(
action="create_update",
name="send-email",
execution_mode="app",
code="def main(params, user_data, sdk_client): ...",
description="...",
is_active=True,
is_public=False, # if True, function is callable without authentication
async_mode=False, # if True, execution returns a task_id instead of a result
config={...}, # optional runtime config (timeouts, env)
auth_config={...}, # optional auth policy override
headers={...}, # optional default request headers
tags=["notifications"], # optional tag slugs
)
manage_function(action="get", function_slug="send-email")
manage_function(action="delete", function_slug="send-email")
execute_function(function_slug="send-email", params={...}, async_mode=False)
Execution modes: app (Python body runs in Taruvi runtime), proxy (forwards to webhook_url), system (privileged).
Security note on ispublic=True: public functions run unauthenticated (userdata is None). Verify a shared secret or signature from the caller in the function body before trusting params. The function body should assume the input is adversarial.
For writing the actual function body, switch to taruvi-functions. This skill only registers the metadata; the body must follow the runtime conventions documented there.
Analytics queries
manage_query(action="create", name="daily-revenue", query_text="SELECT ...", connection_type="internal", tags=["reporting"])
manage_query(action="list")
execute_query(query_slug="daily-revenue", params={"date": "2026-04-17"})
connectiontype is "internal" (against tenant DB) or "external" (requires a secretkey that resolves to a DB credential). Default is "external" — pass connection_type="internal" explicitly when you want to query the tenant DB. For external queries, create the credential secret first.
Raw SQL
See [references/raw-sql-safety.md](references/raw-sql-safety.md).
execute_raw_sql(sql="SELECT ...", params={"min_total": 100}, max_rows=1000)
execute_raw_sql(sql="ALTER TABLE orders ADD COLUMN ...", auto_reflect=True)
- Tool rejects cross-tenant access, system schemas, view/trigger/function DDL.
- DDL commits before any following DML in the same batch (don't mix in one call).
- DML is audited to
alembicrevisionhistory.
- Prefer
datatabledata over raw DML. Prefer createupdate_schema over raw DDL. Use raw SQL only when MCP tools can't express what you need.
Destructive-op protocol
Always plan-validate-execute for:
delete_datatable (especially with force=True)
managepolicies(action="createupdate") on an existing policy (full replacement)
userattributesschema(action="update") (replaces the whole schema)
managesecrettypes(action="delete")
executerawsql containing DROP, TRUNCATE, destructive ALTER, or DELETE without WHERE
deletefunction, manageroles(action="delete")
Procedure:
- Plan — state what will be deleted/replaced and what depends on it. Use
getdatatableschema, manage_policies(action="get"), etc. to inspect current state.
- Validate — surface the blast radius to the user in plain language ("This will drop the
orders table and 3 dependent FK constraints in invoices"). Ask for explicit confirmation.
- Execute — only after confirmation. Report back with the tool's response verbatim.
Gotchas
createupdateschema drops missing fields. Always getdatatableschema first so your new payload preserves fields that must stay.
createuser without roleslugs creates an unroled user. List roles first with manage_roles(action="list") and pick one based on context. Generate a password (12+ chars, mix of classes) if the user didn't provide one.
managepolicies(action="createupdate") replaces, not merges. Get the existing policy first if you only want to change part of it.
delete_datatable without force=True fails on FK deps. The error lists what depends on the table — read it carefully before suggesting force=True.
- Secret types cannot be renamed post-create and system types cannot be modified at all. Choose names carefully.
executerawsql DDL commits immediately, even if a later statement in the same batch fails. The tool warns when you mix DDL + DML.
managestorage(action="createbucket") requires app_category. Pick "assets" (public-ish static content) or "attachments" (user-uploaded files).
- User updates don't accept password changes. Use a separate password-reset flow (outside this MCP surface).
manageroleassignments accepts strings or lists for both roles and usernames. Single-value semantics are fine; check the is_bulk flag in the response.
- The
isactive=True default on listusers silently hides inactive users. Pass is_active=False or a different filter if you need all users.
Verification checklist
After any provisioning task, confirm before reporting done:
If any item fails, fix it before presenting the work as done.
When you get stuck
- Tool-level docs:
getaidocs(category="policies"|"sdk"|"users", topic="guide"|"attributes").
- MCP quickref: [references/mcp-tool-quickref.md](references/mcp-tool-quickref.md).
- Frictionless schema detail: [references/datatable-schema-patterns.md](references/datatable-schema-patterns.md).
- Cerbos policy examples: [references/cerbos-policy-cookbook.md](references/cerbos-policy-cookbook.md).
- Secret type schemas: [references/secrets-and-types.md](references/secrets-and-types.md).
- Raw SQL rules: [references/raw-sql-safety.md](references/raw-sql-safety.md).
- Analytics queries: [references/analytics-queries.md](references/analytics-queries.md).
- Drift check:
bash scripts/check-versions.sh in this skill directory.