Skip to content

Add a shared multi-tenant MCP connector for preview sandboxes #112

Description

@chenaski

Goal

Let someone evaluating a preview sandbox point their own AI tool (Claude, ChatGPT) at that sandbox's CMS by pasting a single URL, with no sign-in, and without standing up a throwaway deployment for every sandbox.

Why a preview deployment cannot serve this itself

Chat applications add a custom connector from their own cloud, and the first thing they do is probe the origin for auth-discovery documents with no query string attached:

  • /.well-known/oauth-protected-resource
  • /.well-known/oauth-authorization-server
  • /register

On a deployment-protected preview all three answer 302 to the SSO login instead of 404. The client concludes the server has a sign-in service, its dynamic client registration then fails, and the user sees an error along the lines of "couldn't register with the sign-in service".

Two consequences worth stating explicitly, because both cost time to rediscover:

  • a protection-bypass query parameter does not help. It works under curl because curl carries the parameter; the discovery probes do not
  • the connector host must not be behind deployment protection at all

Since production (main) is not protection-protected, it can host the connector, and the connector forwards to the protected preview using the bypass secret server-side.

Proposal

One connector served from the production deployment, multi-tenant rather than one per sandbox:

  • endpoint at /<unguessable-path-segment>, where the segment identifies the tenant
  • the segment maps to that tenant's upstream sandbox URL and API key, held server-side
  • the connector forwards MCP traffic upstream, adding the protection-bypass header itself
  • forward only the MCP transport headers (content-type, accept, mcp-session-id, mcp-protocol-version), never the caller's full header set
  • an unknown or wrong path segment returns 404, same as any other unknown path

Onboarding a sandbox becomes adding a config entry; retiring one becomes removing it. Nothing is deployed or torn down per sandbox.

Security constraints, please do not skip these

Satisfying dynamic client registration requires answering the OAuth endpoints, but that OAuth layer is protocol compliance, not access control. A working implementation of this pattern auto-approves every authorization request: the tokens it issues carry no identity and grant nothing beyond what the URL already grants. Real cryptography is still worth having (HMAC-signed stateless tokens, PKCE S256 verified, expiry enforced, malformed tokens rejected with a 4xx rather than throwing), but none of it authenticates anybody.

So the actual security boundary is only these two things, and both must hold:

  1. the unguessable path segment - anyone holding the URL is authorized, so it must be long, random, and kept out of query strings and logs
  2. a capability-limited tool surface - the exposed tools must not be able to publish or delete. Writes should land as drafts for a human to review

Which leads to the hard requirement:

This connector must only ever target an isolated preview sandbox with its own database. It must never be pointed at production. In front of production it is an open door for anyone who has the link.

A guard that refuses to start, or refuses a tenant entry, when the configured upstream is the production deployment would be worth having rather than relying on configuration discipline.

Note this interacts with the shared media store: until preview media is isolated, a connector that can write media can affect production. Keeping media write and image upload out of the tool surface avoids that until then.

Acceptance criteria

  • one deployment serves connectors for many sandboxes, distinguished by path segment
  • adding or removing a sandbox is a configuration change, with nothing to deploy or delete
  • all auth-discovery paths return 404 when probed without a valid path segment
  • the exposed tool list contains no publish and no delete operation, verified by a test
  • pointing a tenant at the production deployment is rejected

Activity

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

Metadata

Metadata

Assignees

No one assigned

    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