Build an MCP server for your app with Supabase
Host an MCP endpoint on Supabase Edge Functions so compatible AI clients can call tools your app defines. Start with public data, or use Supabase Auth and Row Level Security for user-specific data. A running endpoint still needs client setup and a verified tool result.
Choose your first tool ↓01 · Client
The user chooses a tool in their AI client.
02 · Your MCP endpoint
The Edge Function validates the request and, for private tools, the user token.
03 · Your app data
The user-scoped query runs under your RLS policies; the tool returns a result.
The public example below has no user data or database query. The authenticated route adds sign-in and per-user access checks.
1. Choose what your users should be able to do
This is for an app owner who wants users to call the app from an AI client. If you want a coding agent to inspect or manage your Supabase project, use the existing Supabase MCP listing instead. Supabase explicitly separates these two products in its deployment guide.
Public tools: return information anyone may read, such as a public product catalogue or help topics. Every caller gets the same access. Keep private tables and privileged operations out of this route.
Authenticated tools: return a signed-in user's records or perform an approved action for that user. Write a one-sentence contract before coding: “list only the current user's saved items.” Being read-only does not make private data safe to expose anonymously.
Choose one small tool first. Agree on its input, expected result and failure response before connecting a conversational client. This gives you something concrete to check when the model calls it.
2. Prepare a disposable project
The current Supabase MCP guide lists Supabase CLI 2.117.0+, Node.js 20+, Deno and Docker or a compatible running container runtime for local work. Use a development project with synthetic data. Prerequisites.
For a new local project:
mkdir app-mcp-demo
cd app-mcp-demo
supabase init
supabase functions new mcp
Already have a Supabase app? Work in its development branch and use its existing setup. Review generated files before combining them with your app. These are commands for the reader to run; this guide did not execute them. Edge Functions quickstart.
3. Try a public tool before connecting real data
For a first transport check, put this original teaching example in supabase/functions/mcp/index.ts. It returns a fixed list of public help topics, with no database access. It follows the server and handler shape in the official example.
import 'jsr:@supabase/functions-js/edge-runtime.d.ts'
import { createMcpHandler, McpServer } from 'npm:@modelcontextprotocol/server@^2.0.0'
import { z } from 'npm:zod@^4.3.6'
const handler = createMcpHandler(() => {
const server = new McpServer({ name: 'app-help', version: '0.1.0' })
server.registerTool('list_help_topics', {
title: 'List public help topics',
description: 'Return public help topic names; never reads user records.',
inputSchema: z.object({}),
annotations: { readOnlyHint: true },
}, () => ({
content: [{ type: 'text', text: JSON.stringify({
topics: ['Getting started', 'Exporting your data']
}) }],
}))
return server
})
Deno.serve((request) => handler.fetch(request))
For this public, non-sensitive tool, set the following in supabase/config.toml. This disables the gateway JWT check for this function; it is not a recipe for exposing private records.
[functions.mcp]
verify_jwt = false
Run supabase start, then supabase functions serve mcp. The local endpoint is http://127.0.0.1:54321/functions/v1/mcp. The next step checks the tool through MCP rather than treating an HTTP response as proof that it works.
4. Check discovery and the actual return value
Open the MCP Inspector, select Streamable HTTP and enter the local endpoint. You can start the Inspector with:
npx -y @modelcontextprotocol/inspector
Check that the tool list contains list_help_topics, inspect its schema, and call it with an empty input object. Expected content is the two topic names from the code. This expected output is not a captured test result.
Record three separate outcomes: connection established, correct tool discovered, correct result returned. If the first succeeds but the third fails, inspect the function logs and tool input instead of reinstalling your AI client. Keep this read-only fixture until the connection works; only then replace it with app behavior.
5. Add sign-in before serving user data
Skip this section only if your tools remain genuinely public. For app accounts, use Supabase Auth as the OAuth authorization server and host a sign-in and consent screen in your own frontend. Enable the OAuth server and dynamic client registration, and set the Auth Site URL to the frontend that serves the consent route. The official setup guide describes the registration, authorization and consent steps. OAuth setup.
Rather than attaching a privileged database key to the public sample, start from Supabase's authenticated MCP block in your development app:
npx shadcn@latest add https://supabase.com/library/r/mcp.json
Review the scaffold's diff before accepting it. Pair it with the official OAuth Consent block and the framework-specific instructions in the authenticated deployment guide. The consent block assumes your app already has sign-in routes. Preserve the return path after login so authorization can finish. The scaffold still needs your app-specific tools.
The documented middleware uses withOAuthProtectedResource() for discovery and withSupabase({ auth: 'user' }) to verify the caller and provide a user-scoped client. It requires asymmetric signing keys, such as ES256 or RS256; legacy HS256 is not accepted by that verification route. Its verify_jwt = false setting lets discovery reach the function, while middleware still authenticates private tool calls. Removing both checks would expose the function. MCP authentication.
Use RLS policies for the records each user may read or change. Do not use a service-role client in the user-facing tool to bypass those policies, and do not trust a user ID supplied as a tool argument as proof of identity. OAuth scopes do not replace row-level rules. Token security and RLS.
6. Prove user isolation before adding writes
Use two development accounts and synthetic records: account A owns “A-only”; account B owns “B-only.” These are proposed acceptance fixtures, not results from our testing.
- Without signing in, a private call must fail. Check that the authorization challenge leads to the correct discovery endpoint, rather than switching off authentication to hide a 401.
- As A, list records: see “A-only,” not “B-only.” Repeat as B. Also try a request that names the other user's record; it must not reveal it.
- Deny consent once and check that the client cannot call the protected tool. Test the service's documented grant-revocation behavior separately; an already issued token may remain valid until it expires.
- Inspect logs for the expected user and result without storing raw tokens or private payloads unnecessarily.
- Add a write tool only after reads are correct. Give the user a clear confirmation, return the created record identifier, and verify the saved state from the app. If a write times out, look up that operation before retrying it.
This distinguishes “the AI answered” from “the app performed the right action for the right user.” A model's prose cannot establish authorization or persistence. Use the app's own records as your acceptance evidence.
7. Deploy the endpoint and connect one client
After the local checks, choose the intended development project and inspect pending configuration changes:
supabase link --project-ref YOUR_PROJECT_REF
supabase config push
supabase functions deploy mcp
config push changes the linked project's configuration. Review the destination and changes before running it; do not use a production project as a connectivity test. Deploy the consent frontend too if using authentication. Start with the project-domain endpoint:
https://YOUR_PROJECT_REF.supabase.co/functions/v1/mcp
For Claude Code, add that URL with claude mcp add --transport http app-tools URL, then use /mcp to complete authentication. Verify a read-only result before permitting writes. Claude Code instructions.
For ChatGPT, follow the current custom MCP connection guide and OpenAI instructions. Eligible users can add the server through Plugins, configure authentication, then create and install the plugin. Workspace policy and account access still apply. Supabase's client table may show a different UI route; use the client's own current documentation for those steps.
This connects a particular user or workspace. It does not publish your app to a public marketplace or automatically connect every customer. Ask the client to use the named tool, then compare the returned data with the app.
8. Budget for hosting, usage and the client
As checked on 23 October 2026, Supabase advertises Free at $0/month and Pro from $25/month. Free projects can pause after one week of inactivity and are limited to two active projects. A free development setup is not an always-on production promise. Plan pricing.
Edge Function quotas list 500,000 invocations on Free and 2 million on Pro/Team. Paid-plan overage is $2 per million invocations above the included quota; the Free row does not offer that overage option. Usage is counted across the organization. Overages are rounded up to whole one-million-invocation packages; failed requests also count, while OPTIONS preflights are excluded. Check the billing cycle and Spend Cap in your account. How usage is billed. Invocation pricing.
Do not budget one function invocation per conversation. Tool discovery, tool calls and retries can produce separate requests. Measure actual invocations during your trial. Database, storage, network transfer, extra compute and any external APIs can add costs, while ChatGPT or Claude access has separate plan conditions.
For the first trial, cap the test inputs, avoid paid downstream APIs and inspect usage before broadening access. This guide used no paid product calls and does not estimate your app's bill.
9. Find the failed layer
401 before sign-in: expected for a protected endpoint if it includes the correct authorization challenge. If discovery itself is blocked, compare the gateway JWT setting and the function's authentication middleware. Never remove user verification merely to get a 200.
Consent loops or the wrong account: check Auth Site URL, the deployed consent route and the client's allowed redirect configuration. Retry with the intended test account; do not reuse someone else's access token.
Custom-domain discovery fails: the MCP resource URL and the Auth issuer need not share a hostname. Supabase documents explicitly setting the Auth server when using a custom domain. Keep the project URL until it works, then follow the custom-domain section.
Correct tool name, wrong data: inspect the validated user identity, RLS policy and any application cache. Connection success is not data-isolation success.
Tool absent or stale: inspect the server's tool list first, then refresh or reconnect the client. A changed implementation does not prove the client has refreshed its metadata.
A workflow needs the server to ask the client for a model completion: this stateless Edge Function route does not support MCP sampling. Return the information the caller needs, or choose a runtime designed for that requirement. Documented limitations.
Your next step
Choose one outcome: prove the public help tool locally, or prove a private read with two development accounts. Keep the resulting tool name, expected output and permission checks next to your app's own tests.
Continue with MCP Inspector testing for transport problems, connecting an existing server to ChatGPT for client setup, or the MCP security checklist before widening access. If the missing piece is reusable instructions rather than a callable API, use the SKILL.md authoring guide.
FAQ
Is this the same as the official Supabase MCP server?
No. This guide is about an MCP server your own app exposes to its users. The official Supabase MCP server is for connecting a coding agent to Supabase projects.
Can I make a private read-only tool public?
Read-only describes what a tool changes, not who may see its output. A tool that reads private records still needs authentication and authorization.
Does every user get access after deployment?
No. Users still need an eligible MCP client, permission to add the connection, and authorization for your app. Deployment is not marketplace publication.
Did AgentSkillsHub test the service end to end?
No. We checked documentation and this guide page. We did not deploy a Supabase service, connect a customer account, verify a real RLS policy or perform billable calls.
Primary sources
Source-checked 2026-10-23. Published documentation, account availability and a hands-on workflow are separate evidence. Examples and checklists here are editorial material.