Setting Up Xano MCP and WeWeb MCP to Build Together: A Step-by-Step Guide
August 11, 2026
Connecting Xano's CLI and MCP server, WeWeb's MCP server, and an AI assistant with live context into both — and the build order that avoids rework.
Most AI-assisted no-code builds fail at the handoff, not at either individual tool. Xano handles the backend fine on its own. WeWeb handles the frontend fine on its own. The part that actually determines whether a build goes smoothly is whether your AI assistant has real, live context into both systems at once, and whether you give it a clear enough brief before it starts touching either one.
This guide walks through that setup end to end, connecting Xano's CLI and MCP server, connecting WeWeb's MCP server, briefing an AI assistant on the actual product, and then running the build in the order that avoids rework, using one real build as the thread the whole way through: an enquiry intake and CRM system for a small business. Multi-step public form, internal dashboard, kanban pipeline, analytics. Backend on Xano, frontend on WeWeb, no custom code on either side.
Because this is a Xano-first stack, backend before frontend, we start there.
Part 1: Setting up Xano
1.1 Install the Xano CLI
The CLI is what lets an AI assistant, or you, read and write Xano's schema, functions, and API endpoints as plain text files, instead of clicking through the visual builder for every change.
npm install -g @xano/cliAuthenticate with browser-based login, the fastest path:
xano authThis opens a browser to the Xano login page and saves credentials to ~/.xano/credentials.yaml, setting that profile as your default. If you're scripting this for CI/CD or a non-interactive environment, use the Profile Wizard with a pre-generated access token instead.
Once authenticated, confirm you can see your workspaces:
xano workspace listFull CLI reference: Xano CLI: Get Started
1.2 Connect the Xano MCP server
The CLI handles push and pull of code. The MCP server is what gives an AI assistant live, structured access to your workspace inside the conversation itself: schema, existing endpoints, XanoScript syntax references, and validation, without you copy-pasting any of it manually.
In your Xano instance, go to Instance Settings → Metadata API and MCP Server, and choose the connection type your client supports (SSE or streaming).
To connect from Claude Code specifically:
claude mcp add --transport http <url> --header "Authorization: Bearer <token>"Claude Pro or Max users connecting via the hosted app instead of Claude Code: go to Settings → Connectors → Add custom connector and paste the remote MCP server URL there.
One thing worth knowing before you rely on this: Xano's MCP server does not currently support OAuth, and it doesn't work from Claude on the web, since that runs server-side and can't reach a locally-scoped connection. Claude Desktop or Claude Code are the supported paths.
Full MCP setup and troubleshooting: Xano Docs: Connecting Clients · Introducing the Xano MCP Server
With both pieces connected, your assistant can read your live schema, pull existing endpoints into local .xs files, validate XanoScript syntax before you push it, and write new schema and logic directly, subject to whatever review gate your workspace has configured. On this build, that gate turned out to matter a lot, and it's covered in full in Part 5.
Part 2: Setting up WeWeb
2.1 Connect the WeWeb MCP server
WeWeb's MCP connection is a single config block, no separate CLI to install. Add this to your MCP client's server configuration:
{
"mcpServers": {
"weweb-ai": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://ai-api.weweb.io/v1/mcp"]
}
}
}After adding it, your client will prompt you to sign in and authorize access. Once authorized, your assistant can list your workspaces and projects, switch into the correct one, and start reading and writing pages, workflows, tables, and design system tokens.
A practical note before you start prompting: keep your assistant scoped to one page, workflow, or design-system change at a time, and review what it built inside the WeWeb editor before stacking the next request on top. WeWeb MCP runs on your account, your model, and your tokens, and multi-step builds compound fast if you let the assistant chain five unreviewed changes before you look at any of them.
Quick overview: WeWeb MCP: Build in WeWeb With Any AI Agent
Full documentation and detailed setup: WeWeb AI Documentation
2.2 Decide, up front, whether WeWeb owns any data itself
This is the one setup decision worth making explicitly before your first prompt, because it's expensive to reverse mid-build. WeWeb ships with its own optional backend module: Postgres tables, built-in auth, storage.
On this build, that module was explicitly ruled out. The instruction, stated directly: "I am using Xano. No need for WeWeb backend, just use the Xano request." Every read and write from that point on went through WeWeb's plain xano/request action, pointed at Xano's own API groups, with xano/auth-set-session handling login state instead of WeWeb's built-in auth provider.
The consequence worth knowing in advance: once you're wired this way, a large chunk of WeWeb's built-in tooling, the workflow debugger, the table-view bindings, the CRUD helpers, becomes inapplicable, because all of it is scoped to WeWeb's own storage layer. Every one of those tools will report backend not installed, and that's expected behavior for this setup, not a misconfiguration to chase down. It's a much smoother build if you state the constraint on day one than if your assistant installs the module by default and you have to unwind it after pages are already built against it.
Part 3: Brief the assistant on the actual product
With both MCP connections live, the temptation is to start prompting features immediately. Don't. The single highest-leverage step in this entire process is writing a real brief before either tool gets touched, and having your assistant turn it into a concrete plan you approve before it writes anything.
The brief for this build covered five things:
- What the product does. An SME receives enquiries from multiple channels (website, referral, social, email, phone, walk-in), an internal team manages them through a pipeline, and the business needs visibility into where enquiries come from and how well they convert.
- The exact surfaces, not just "a CRM": a public multi-step intake form, an internal dashboard, a searchable enquiries table, a kanban pipeline board, and an analytics page.
- Explicit tech boundaries. Xano as the only backend, WeWeb frontend styled to look like shadcn/ui without importing shadcn or writing any custom components, every button and card rebuilt natively from WeWeb primitives.
- A request for a plan before building. Schema, endpoint contracts, design tokens, page structure, and key workflows, written out and reviewed before any table existed.
- Explicit review and approval of that plan before the build started.
That last step wasn't a formality. Reviewing the written plan surfaced one decision immediately, before any table was touched: confirming explicitly, up front, that WeWeb's backend module stayed off the table (covered above). A second decision changed mid-build, after the stages lookup table (id, name, slug, color, sort order, the textbook-normalized approach) had already been created and populated. Once it became clear the pipeline stages were purely a frontend concern with a fixed, small set of values, the table was dropped entirely and WeWeb took over owning stage names, colors, and ordering as static config instead.
Both changes were cheap specifically because there was a written plan to compare the new decision against. Neither would have surfaced from a one-line prompt straight into building.
Part 4: Build the backend on the plan
Once the plan was approved, the build followed strict dependency order: schema before logic, logic before endpoints, endpoints tested and confirmed working before anything in WeWeb touched them.
4.1 Database first
The core tables went in before a single line of business logic: enquiries (name, email, source, subject, message, budget range, pipeline stage, status), enquiry_activity as an audit trail logging every stage change, note, and assignment as its own event rather than overwriting a field, rate_limits for the public form, and the existing user table extended with a role field for basic access control.
The one field that changed after the schema went in confirmed the rule from Part 3: enquiries.stage started as stage_id, an integer foreign key into that now-cancelled stages table. Once the table was dropped from the plan, stage_id became a plain stage enum instead, new, contacted, qualified, proposal, converted, lost, and every function or endpoint that would have joined against the lookup table was written against the enum directly from the start. A stage-change function that would otherwise have queried whether a stage was "terminal" via a join instead just checks if new_stage == "converted" in code. Simpler, one less join, and it only needed to know six fixed values, not an open-ended list a non-technical user might someday edit.
The rule that held up: don't create a lookup table by default. Ask what breaks if the value is just a fixed enum. If the answer is nothing yet, it's an enum.
4.2 Shared logic as functions, not copy-paste
Before any endpoint was written, three pieces of logic were identified as needed in more than one place and pulled into standalone Xano functions, called via function.run, rather than duplicated: fn_check_rate_limit (incrementing and checking the per-IP submission counter), fn_log_activity (the single insert point for the audit trail), and fn_change_stage (updating status and converted_at and writing the audit entry whenever an enquiry moves stages).
That last one changed twice during the build, once when stage_id became stage, and once during a bug fix, and both times the fix was a single file, not a search-and-replace across every endpoint that called it.
4.3 Endpoints, split public and internal, with rate limiting on the server
Two API groups: a public group with no auth for POST /enquiries and GET /form-meta (the form's dropdown options), and an internal group requiring a valid auth token for everything else, listing enquiries, fetching a single record, changing stage, adding notes, the dashboard summary, and analytics.
The public form's "disable the submit button while in flight" was treated as UX polish, full stop, not the actual defense. The real limit, five submissions per ten minutes per IP, lives in fn_check_rate_limit, called as the first line inside POST /enquiries, backed by the rate_limits table. If a request comes in past the limit, the function throws before the enquiry is ever written.
One practical cost of that design worth planning for: the rate limiter doesn't know a real user from a developer curl-testing the same endpoint during QA. A handful of manual test submissions in a row burned the entire quota, and the next real test got a 429 that looked like a bug until it was traced back to the limiter working exactly as designed.
4.4 Test against the live endpoint, not just the syntax check
A syntax validator confirming XanoScript parses correctly is not the same as confirming the endpoint returns the right data. Two real bugs shipped in the first pass here and only surfaced once real HTTP requests hit the deployed endpoints.
The first: GET /dashboard returned a 500 with an unusually precise error: "Invalid syntax. When using ternary expressions with filters, make sure to wrap them with parentheses." The offending line computed a conversion rate inline:
($total > 0) ? (($converted_count / $total) * 100)|round:1 : 0The filter was binding to only part of the ternary. Adding one more layer of parentheses around the whole true branch fixed it:
($total > 0) ? ((($converted_count / $total) * 100)|round:1) : 0The second was quieter and took longer to run down: GET /enquiries returned zero results for every query, including no filters at all, against a table that clearly had rows. The where clause chained several null-safe conditions across one long line, and the instinct was to reach for the same fix, wrap everything in explicit parentheses. That didn't resolve it. Stripping the clause down to nothing didn't either; the endpoint still returned zero rows with no filtering applied at all, which ruled out the filter logic entirely and pointed at something more fundamental still being isolated. The honest lesson from that thread: a fix that worked on one bug doesn't automatically transfer to a bug that merely looks similar, and the fastest way to find the real boundary is stripping the query to nothing and rebuilding complexity one clause at a time, not pattern-matching the last error message.
4.5 Sandbox, review, and promote
This particular Xano workspace had direct push disabled, a safety default on anything that resembles production. Every schema change, every function edit, every endpoint fix went through the same loop instead of landing immediately:
xano sandbox push, sending local.xsfiles to a personal sandbox environment.xano sandbox review --url-only, generating a one-time impersonation link into that sandbox's dashboard.- A human opens the link, reviews the diff, and clicks "Review & Push" to promote it into the live workspace.
That third step doesn't get automated away, by design. It's a deliberate checkpoint before anything touches a workspace that might hold real data, and treating it as a batching opportunity, grouping several related changes into one promotion instead of promoting after every single edit, matters in practice, since each review is a context switch for whoever's clicking the button.
Two sharp edges from this loop worth knowing before you hit them. Sample data pushed with a records-seeding flag reported success repeatedly while a follow-up pull of the same sandbox showed zero records; the fix was seeding real data through the live, already-deployed public endpoint instead and verifying with a follow-up curl request, slower but actually verifiable. And re-pulling a workspace into a directory that already has local edits can produce two files for the same logical endpoint, since the server nests path-parameterized routes into subfolders while an earlier hand-authored flat file sits alongside carrying the same internal ID, which then fails the next push with a duplicate-ID error that gives no hint it's a file-organization issue rather than a logic one. Diffing pulled files against an existing directory before touching anything catches it immediately.
Only once POST /enquiries, the internal listing and detail endpoints, the kanban feed, the dashboard summary, and analytics were all live and confirmed against real requests did the build move to WeWeb.
Part 5: Build the frontend against the live backend, in dependency order
With the backend confirmed working, WeWeb built against the real deployed API, never a mocked one. Design tokens and reusable component classes (buttons, cards, badges, form fields, table rows, kanban columns) went in first, matching shadcn/ui's spacing and radius conventions natively, before a single page existed. One design-system bug is worth flagging on its own: WeWeb's class editor silently routes any kebab-case CSS property, border-radius, background-color, into an inert fallback bucket instead of applying it, because the underlying style object expects camelCase keys. No error at creation time; the class simply doesn't render most of what it claims to. The fix was rewriting every affected class with camelCase properties and re-fetching each one afterward to confirm the fix actually landed, since the tool's success response gave no indication anything had gone wrong the first time either.
Pages went in by dependency: the public, unauthenticated /enquiry form first (three steps, one shared context object holding all field state, progress indicator, success and rate-limit-aware error states), then /login and /signup (Xano's auth/login and auth/signup endpoints, xano/auth-set-session storing the token, a page workflow fetching auth/me afterward), then the pages that depend on being logged in: /dashboard, /enquiries, /kanban, /analytics.
The enquiry detail view, full record, stage-change control, activity timeline, add-note form, needed to open from three separate places: the dashboard's recent list, the full enquiries table, and the kanban cards. Rather than three copies, it was built once as its own page section and reused structurally on all three pages via WeWeb's section-reuse feature. The part that reuse doesn't hand you for free: every workflow the shared panel triggers still has to be wired independently on each page that hosts it, since a reused section shares structure and styling, not event bindings. Each page's "open detail" click handler sets the same shared "selected enquiry" variable and calls the same shared "fetch detail" function, but that wiring happens once per page, not once total.
The kanban board itself used WeWeb's native ww-kanban element rather than hand-rolled drag-and-drop divs, bound directly to the flat enquiry list grouped by the stage enum, with the drag-drop item:moved event calling PATCH /enquiries/{id}/stage to persist the move. The analytics page used a Chart.js wrapper element with one binding-format trap worth flagging for anyone using the same component: complex properties like a chart's data and options have to be wrapped as an explicit JS binding even when fully static, or the editor silently drops the value, and chart colors have to be resolved to literal hex values in JavaScript rather than referenced as CSS custom properties, since a <canvas> element has no CSS engine to resolve them against.
What this setup actually buys you
None of these steps are exotic on their own. What they add up to is an assistant with real, live context into two connected systems at once instead of you manually relaying what one tool did to the other, and a build order that catches structural mistakes, a table that should have been an enum, a security check left client-side, while they're still cheap to fix, instead of after three pages have already been built on top of them.
The setup takes maybe twenty minutes. The plan-and-review step before building takes maybe ten more. Both are the reason the rest of this particular build, five pages, four API groups, one shared component, went from a written brief to a working system without having to unwind a wrong decision after the fact.