@@ -378,12 +378,16 @@ export default function AddMcpSource(props: {
378378 const remoteHeadersComplete = remoteHeaders . every (
379379 ( header ) => header . name . trim ( ) && header . value . trim ( ) ,
380380 ) ;
381+ // OAuth is "ready to save" even without tokens — the source is stored
382+ // with a stable connectionId pointer, and each user completes their
383+ // own sign-in via McpSignInButton on the source detail page (per-user
384+ // scope shadowing means each user's tokens land at their own scope).
381385 const authReady =
382386 remoteAuthMode === "none"
383387 ? canUseNone
384388 : remoteAuthMode === "header"
385389 ? headerAuthComplete
386- : tokens !== null ;
390+ : true ;
387391 const canAdd = Boolean ( probe ) && authReady && remoteHeadersComplete && ! isAdding && ! isOAuthBusy ;
388392 // Probe failures are shown inline on the URL field; other failures
389393 // (OAuth start, add source) render in the bottom error block.
@@ -493,6 +497,17 @@ export default function AddMcpSource(props: {
493497 if ( ! probe ) return ;
494498 dispatch ( { type : "add-start" } ) ;
495499 const headerAuth = remoteAuthHeaders [ 0 ] ;
500+ // For oauth2 sources saved without completing the flow, use the
501+ // same stable connectionId the handleOAuth path would have used.
502+ // This pins the source's auth pointer, so when a per-user sign-in
503+ // runs later (via McpSignInButton) it mints the connection at the
504+ // user scope against the same id — innermost-wins shadowing then
505+ // resolves tokens per-user at invoke time.
506+ const deferredOAuthConnectionId = mcpOAuthConnectionId (
507+ slugifyNamespace ( remoteIdentity . namespace ) ||
508+ slugifyNamespace ( probe . namespace ?? "" ) ||
509+ "mcp" ,
510+ ) ;
496511 const auth =
497512 remoteAuthMode === "header" && headerAuth ?. secretId
498513 ? {
@@ -501,10 +516,10 @@ export default function AddMcpSource(props: {
501516 secretId : headerAuth . secretId ,
502517 ...( headerAuth . prefix ? { prefix : headerAuth . prefix } : { } ) ,
503518 }
504- : remoteAuthMode === "oauth2" && tokens
519+ : remoteAuthMode === "oauth2"
505520 ? {
506521 kind : "oauth2" as const ,
507- connectionId : tokens . connectionId ,
522+ connectionId : tokens ? .connectionId ?? deferredOAuthConnectionId ,
508523 }
509524 : { kind : "none" as const } ;
510525 const headers = Object . fromEntries (
@@ -788,9 +803,15 @@ export default function AddMcpSource(props: {
788803 { remoteAuthMode === "oauth2" && (
789804 < >
790805 { ! tokens && state . step === "probed" && (
791- < Button onClick = { handleOAuth } variant = "outline" >
792- Sign in
793- </ Button >
806+ < div className = "flex flex-col gap-2" >
807+ < Button onClick = { handleOAuth } variant = "outline" >
808+ Sign in
809+ </ Button >
810+ < p className = "text-[11px] text-muted-foreground" >
811+ Optional — you can save the source now and each user can sign
812+ in from the source detail page later.
813+ </ p >
814+ </ div >
794815 ) }
795816
796817 { ! tokens && state . step === "oauth-starting" && (
0 commit comments