docs(byo-mcp): how to use with custom domains (#51085)

## Problem

As part of my investigation of this
[issue](https://linear.app/supabase/issue/AI-1263/test-byo-mcp-with-custom-domains)
I realized that, in order for byo-mcp to work with custom domains,
there's a tweak needed, and I'm documenting it here.

The long term use to fix it lives
[here](https://linear.app/supabase/issue/FDBKIN-20212/use-custom-domain-in-oidc-and-oauth-well-known-discovery-endpoints).
With that one in place, we could remove the clarification and the
experience would be much much simpler.

Fixes AI-1263

## Solution

I'm documenting for now, and will follow up if something else needs a
change.

## Review instructions

Provide a clear numbered procedure that the PR reviewer can walk
through.

1. Visit `docs/guides/ai-tools/byo-mcp` and read the added text. 
2. See if it all makes sense.
3. Ask @raulb if something's not clear or confusing. 

## Checklist

Check all before review:

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [x] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Added guidance for configuring MCP authorization metadata with a
custom domain, including setting the authorization server to the
Supabase Auth project issuer and checking it against the advertised
metadata.
* Clarified that the resource URL continues to use the domain requested
by the client, and that leaving the issuer setting unset locally retains
the default.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Raúl BarrosoandClaude Sonnet 5 authored and GitHub committed 2026-10-02 19:07:46 +02:00
1 parent 4344fdea3d
commit 11488801f7
2 files changed
+32

No files matched your search

@@ -343,6 +343,37 @@ Every client that implements the MCP authorization specification discovers your
Users can review and revoke connected clients through the [OAuth grant management](/docs/guides/auth/oauth-server/oauth-flows#managing-user-grants) endpoints. The [Headless App block](/library/docs/tanstack/headless-app) ships an `/agents` page that does this.
## Use a custom domain
With a [custom domain](/docs/guides/platform/custom-domains) active, clients can reach your MCP server at `https://api.example.com/functions/v1/mcp`. Supabase Auth keeps its issuer on the project domain, `https://<your-project-ref>.supabase.co/auth/v1`. `withOAuthProtectedResource()` builds the Auth URL from the domain the request arrived on, so on the custom domain it advertises `https://api.example.com/auth/v1`. That doesn't match the issuer, and MCP clients that validate the Auth metadata stop at discovery.
If your clients can use the project URL, `https://<your-project-ref>.supabase.co/functions/v1/mcp` needs no changes. To serve MCP on the custom domain too, set the Auth server explicitly:
1. Point `authorizationServer` at an environment variable instead of letting it default to the request's domain:
```ts
[
withOAuthProtectedResource({ authorizationServer: Deno.env.get('MCP_AUTH_ISSUER') }),
withSupabase<Database>({ auth: 'user' }),
],
```
2. Store the issuer as a secret, and redeploy:
```bash
supabase secrets set MCP_AUTH_ISSUER=https://<your-project-ref>.supabase.co/auth/v1
supabase functions deploy mcp
```
Leave `MCP_AUTH_ISSUER` unset locally. An unset value falls back to the default, which is correct on the local stack. The `resource` URL still follows the domain each client uses, so the project URL and the custom domain both keep working.
To confirm the fix, compare `authorization_servers` from the resource metadata with `issuer` from Auth's discovery document. They must match:
```bash
curl https://api.example.com/functions/v1/mcp/oauth-protected-resource
curl https://<your-project-ref>.supabase.co/auth/v1/.well-known/oauth-authorization-server
```
## Run it outside Edge Functions
The same pipeline mounts in any runtime that speaks `Request` in, `Response` out: a Next.js route handler, a SvelteKit endpoint, Cloudflare Workers, or a plain Node, Bun, or Deno server. Off Edge Functions there are no forwarded headers to derive the public URLs from, so pass them explicitly:
@@ -102,6 +102,7 @@ Before you activate your domain, prepare your applications and integrations for
- Supabase Auth will use the custom domain immediately once activated.
- OAuth flows will advertise the custom domain as a callback URL.
- SAML will use the custom domain instead. This means that the `EntityID` of your project has changed, and this may cause SAML with existing identity providers to stop working.
- MCP servers that use Supabase Auth need `authorizationServer` set explicitly to the project Auth issuer, not the custom domain. See [Use a custom domain](/docs/guides/ai-tools/byo-mcp#use-a-custom-domain) in the BYO-MCP guide.
To prevent issues for your users, follow these steps: