Skip to content

Move MCP server to its own repo so branch preview protection can be re-enabled #74

Description

@rickstaa

Context

Vercel Deployment Protection blocks MCP OAuth tests on branch previews, so we have disabled preview protection on this project for now. This issue tracks the proper fix.

Why protection had to go

An MCP client that speaks OAuth does discovery per RFC 9728: it takes the origin of the MCP URL and fetches https://<host>/.well-known/oauth-protected-resource[/path]. Two consequences:

  • The query string is discarded, so ?x-vercel-protection-bypass=<secret> never reaches the discovery request.
  • Custom headers set in the client's MCP server config are applied only to the transport POST, not to the protected-resource metadata fetch, the AS metadata fetch, dynamic client registration, or /token.

So four unauthenticated request classes hit Vercel's edge gate and get a login page instead of JSON. Vercel's own bypass mechanisms are header- or query-based and therefore cannot help.

Whitelisting was considered and does not cover this:

Whitelist Matches on Works here?
OPTIONS Allowlist path prefix, OPTIONS method only No, discovery is a GET
Trusted IPs IP / CIDR No, hosted MCP clients use rotating cloud IPs
Deployment Protection Exceptions domain Yes, but Enterprise or Pro + Advanced Deployment Protection
Trusted Sources OIDC token in a header No, still a header
WAF System Bypass Rules IP / CIDR No, bypasses DDoS mitigations, not Deployment Protection

There is no path-level whitelist for GET, by design.

Interim guardrails

While protection is off, branch previews are publicly reachable and their URLs are not hard to discover:

  • Do not put secrets, credentials, or customer data in preview environments.
  • Preview deployments must not point at production data stores.
  • Assume anything visible on a preview is public, including unreleased UI.

Recommendation

Move the MCP server into its own repo and its own Vercel project.

An OAuth-protected MCP server is designed to be publicly reachable and to reject unauthenticated calls. That is its production posture, so Vercel Authentication in front of it is redundant, and it is exactly that redundancy which breaks discovery. Splitting it lets us:

  • Leave protection off on the MCP project only, where it is safe.
  • Re-enable protection on console previews, where it actually matters.

Supporting changes worth doing at the same time:

  • Host the authorization server on a stable production domain and have preview metadata point authorization_servers at it. This takes /authorize, /token, and DCR out of the protected zone and stops the issuer changing on every push, so clients do not re-register per deploy.
  • Give previews a stable alias so the resource identifier is constant across pushes.
  • Enforce OAuth properly on the MCP server: 401 on unauthenticated tool calls, and validate the token audience / resource claim so a token minted for another server is rejected.

Alternative if we would rather not split the repo: use the Advanced Deployment Protection add-on or an Enterprise plan and add a Deployment Protection Exception for a stable preview alias. Same effect, one pipeline, but the exception makes that hostname fully public anyway, so the MCP server still needs to enforce its own OAuth.

Acceptance criteria

  • MCP server lives in its own repo and Vercel project
  • MCP project: Deployment Protection disabled, OAuth enforced and audience-validated
  • console: branch preview protection re-enabled
  • MCP OAuth discovery verified end to end against a preview from a hosted client

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions