NocoDB: read and write records in bases and tables, follow links, and inspect schemas and views.
NocoDB ships in the w6w first-party pack. It declares 13 actions, 3 health checks, and the host runs its code in a sandbox that never sees the credential.
io.w6w.nocodbNocoDB turns an existing database into something that reads and writes like a spreadsheet, and this app is built around exactly that: reading and writing records in its bases and tables, following the links between them, and inspecting the schema and saved views that shape what a table actually looks like.
Because NocoDB enforces a modest request budget per account, this app is shaped around using it efficiently rather than working around it after the fact — writes accept many rows in a single request instead of one at a time, reads default to a larger page than the API itself defaults to, and a dedicated counting action answers “how many rows match” without paging through them all. Linked records — the equivalent of a lookup between two tables — get their own actions, since NocoDB returns them from a separate endpoint rather than alongside the row that references them.
NocoDB is open source and just as often self-hosted as it is used through its cloud offering, and this app works the same way against either. A base built on top of an existing production database is flagged as such, since writing to one of those reaches further than an ordinary NocoDB-native table would.
Three routes to the same 13 actions. The Workflow tab is generated from NocoDB's own manifest and carries its real ids, so it is copy-pasteable; the Code and CLI examples are the same call for any action on any app, so every app-specific value in them is a blank you fill in.
base-list The bases this token reaches — which is whatever its creator reaches, since a NocoDB token has no scope of its own. Flags bases backed by an EXTERNAL DATABASE, where a delete goes through to the customer's production Postgres.
link-list The records on the other end of a link — which do NOT come back with the parent record, so their absence looks like an empty link. One request per record per field, against a budget of 60 a minute, so filtering the child table is often cheaper.
link-set Connect records across tables, which `record-update` CANNOT do — links have their own endpoint. Adding is additive; `replace` reads first and reconciles, costing three requests against a budget of 60 a minute.
record-count How many rows match, in one request rather than by paging — which matters when the budget is 60 requests a minute. The `where` trap is quieter here: a filter with spaces returns ZERO, which is a plausible answer.
record-create Insert one row or many in a single request — which is the difference between an import taking a second and seventeen minutes, at 60 requests a minute. Returns the generated IDS IN ORDER, which is the only way to get them.
record-delete Remove rows by id — NocoDB deliberately offers no delete-by-filter here, so a workflow has to list first and decide. Deletion is final: the interface's undo is a session thing, and an external-database base loses the row too.
record-get One row by its PRIMARY KEY — not its position in a view, which changes whenever anybody sorts. Unlike a filtered list, a missing record here is a 404 rather than an empty result.
record-list Read rows, filtered and sorted. NocoDB's `where` syntax takes NO SPACES between the parts of a condition — with them the request succeeds and returns nothing — so this checks first. A `viewId` applies your filter ON TOP of the view's own.
record-update Change one row or many in one request — each carries its own primary key in the body. It is a PATCH, so fields you omit are left alone and clearing one means sending `null`. There is no conditional update, so concurrent writes race silently.
table-get The columns a write has to satisfy. Separates WRITABLE columns from computed ones — a formula or rollup is rejected in terms of the column rather than the rule — reports the select options, and gives the LINK FIELD IDS the link actions need.
table-list The tables in a base with their IDS — which is what every record action takes, since nothing accepts a table's title. Ids survive a rename, so a workflow keyed on one does not break the day somebody tidies the base.
view-list A table's saved filters and sorts — queries somebody already wrote and maintains, which is usually better than reimplementing them in a workflow. Counts SHARED views, which serve their rows to anybody with the link.
webhook-list What fires when this table changes — the thing to check before a bulk write, since inserting a thousand rows fires a thousand webhooks and nothing in the data API mentions it. Separates the disabled ones, which are still listed.
A workflow step names the app and the action, and the editor fills in the
connection when you pick one. This is the Step shape from the
workflow spec, carrying NocoDB's real ids.
{
"manifestVersion": "2",
"name": "nocodb-example",
"steps": [
{
"id": "link-list",
"uses": {
"app": "io.w6w.nocodb",
"action": "link-list",
"connection": "conn_YOUR_CONNECTION_ID"
},
"with": {
"tableId": "<tableId>",
"linkFieldId": "<linkFieldId>",
"recordId": "<recordId>"
}
}
]
}link-list base-list link-set record-count record-create +8 more actions available
Every app-specific value here is a blank you have to fill in. An
app action is reached through the connection that authenticates it, so the
address is a connection id, not the app id — and connections belong to your account,
so a public page cannot know yours. Create one for NocoDB, then fill in
the three blanks: conn_YOUR_CONNECTION_ID, the action key, and the
parameters that action declares. The call itself is real — the shape is transcribed
from the studio's own snippet builder, which prints the same kind of blanks — but
nothing in it is specific to NocoDB. The Workflow tab is where this app's
real ids are.
npm install @w6w/sdkyarn add @w6w/sdkpnpm add @w6w/sdkdeno add npm:@w6w/sdkimport { W6wClient, isActionRun } from "@w6w/sdk";
// Reads W6W_BASE_URL and W6W_TOKEN from the environment when omitted.
const client = new W6wClient();
const envelope = await client.run({
urn: "conn_YOUR_CONNECTION_ID",
action: "link-list",
payload: {
tableId: "<value>",
linkFieldId: "<value>",
recordId: "<value>",
// fields: "<value>",
// limit: "<value>",
// offset: "<value>",
},
});
if (isActionRun(envelope)) console.log(envelope.value); npm install -g @w6w/cli w6w run conn_YOUR_CONNECTION_ID --action link-list --payload '{"tableId":"<value>","linkFieldId":"<value>","recordId":"<value>"}' Give an AI agent NocoDB — without giving it NocoDB's credentials. One MCP endpoint exposes every app, function and workflow the caller is entitled to, as tools it can discover and run. Access is granted per team while we onboard.
One tool call{
"name": "w6w_invoke",
"arguments": {
"ref": "app:io.w6w.nocodb#link-list",
"input": {
"tableId": "<tableId>",
"linkFieldId": "<linkFieldId>",
"recordId": "<recordId>"
}
}
}
Every tool names its target with a single ref. The
app: form above doesn't name a connection at all — the
host resolves which of the caller's NocoDB connections to sign
with, and refuses rather than guesses when the answer is ambiguous.
The token is attached host-side, at the moment of the call. It is never a tool argument, never in the model's context, and never in a transcript — so a prompt injection has nothing to exfiltrate.
Tools are derived per end user from what that person has actually connected and is entitled to — not one shared bot identity carrying the union of everyone's access.
Multi-step work runs on the workflow engine and returns a run handle the agent can poll — retries, branching and state survive the conversation that started them.
NocoDB's declared health checks are on the surface too, so an agent can tell "the vendor is down" from "your credential expired" before it burns a retry on either.
The MCP surface is part of the hosted platform. NocoDB itself is MIT, and the runtime that executes it is source-available (FSL).
NocoDB declares its own checks, so its health is a property of the app rather than something the host guesses at.
Declared unavailable twice over — NocoDB's status page publishes no machine-readable feed, and it would speak only for the cloud, while most NocoDB is self-hosted. The `instance` check reads the connection's own server instead.
Reads this connection's own server through the UNAUTHENTICATED `/api/v1/health`, so an outage cannot hide behind a credential problem. Reports the process UPTIME, because a repeatedly small one is a container crash-looping — a pattern no single check would show.
Reads NocoDB's own `x-ratelimit-remaining`, which is a real and current number — and a small one: 60 requests a minute per caller, shared by every workflow on this connection. The probe itself spends one of them.