Early-onboarded MCP sources stored a legacy oauth2 auth shape where `clientInformation` was null — DCR completed at sign-in time but was never persisted back onto the source. The refactor made `clientInformation` required on `OAuth2ProviderState`, which meant those rows couldn't be migrated and couldn't load in the UI. Make it nullable + fail refresh cleanly with a re-sign-in message when absent. The migration now passes null through so the source loads with the pointer shape; the user can hit Reconnect to run fresh DCR.
Unified · Split
| @@ -97,14 +97,6 @@ const classifyRow = (row: Row): Bucket => { · 96 unmodified lines | ||
| 97 | 97 | reason: "config.endpoint missing", |
| 98 | 98 | }; |
| 99 | 99 | } |
| 100 | − if (legacy.clientInformation === null) { | |
| 101 | − return { | |
| 102 | − kind: "legacy-blocked", | |
| 103 | − row, | |
| 104 | − legacy, | |
| 105 | − reason: "clientInformation missing — DCR never completed", | |
| 106 | − }; | |
| 107 | − } | |
| 108 | 100 | return { kind: "legacy-migratable", row, legacy, endpoint }; |
| 109 | 101 | } |
| 110 | 102 | |
| @@ -230,7 +222,7 @@ const main = async () => { · 119 unmodified lines | ||
| 230 | 222 | const providerState = { |
| 231 | 223 | endpoint: b.endpoint, |
| 232 | 224 | tokenType: l.tokenType, |
| 233 | − clientInformation: l.clientInformation!, | |
| 225 | + clientInformation: l.clientInformation, | |
| 234 | 226 | authorizationServerUrl: l.authorizationServerUrl, |
| 235 | 227 | authorizationServerMetadata: null, |
| 236 | 228 | resourceMetadataUrl: l.resourceMetadataUrl, |
| @@ -405,13 +405,6 @@ const migrateMcp = (sqlite: Database): void => { · 404 unmodified lines | ||
| 405 | 405 | ); |
| 406 | 406 | continue; |
| 407 | 407 | } |
| 408 | − if (legacy.clientInformation === null) { | |
| 409 | − console.warn( | |
| 410 | − `[migrate-connections] skip mcp ${row.scope_id}/${row.id}: clientInformation missing (DCR never completed)`, | |
| 411 | − ); | |
| 412 | − continue; | |
| 413 | − } | |
| 414 | − | |
| 415 | 408 | const connectionId = `mcp-oauth2-${row.id}`; |
| 416 | 409 | const providerState = { |
| 417 | 410 | endpoint, |
| @@ -304,7 +304,11 @@ const MCP_OAUTH2_PROVIDER_KEY = "mcp:oauth2" as const; · 303 unmodified lines | ||
| 304 | 304 | const OAuth2ProviderState = Schema.Struct({ |
| 305 | 305 | endpoint: Schema.String, |
| 306 | 306 | tokenType: Schema.String, |
| 307 | − clientInformation: McpJsonObject, | |
| 307 | + // Nullable to accommodate early-onboarded sources where DCR client | |
| 308 | + // registration was never persisted back onto the source. Refresh fails | |
| 309 | + // fast on null and surfaces a "re-sign-in required" error, which the UI | |
| 310 | + // turns into a Reconnect prompt. | |
| 311 | + clientInformation: Schema.NullOr(McpJsonObject), | |
| 308 | 312 | authorizationServerUrl: Schema.NullOr(Schema.String), |
| 309 | 313 | authorizationServerMetadata: Schema.NullOr(McpJsonObject), |
| 310 | 314 | resourceMetadataUrl: Schema.NullOr(Schema.String), |
| @@ -1290,6 +1294,14 @@ export const mcpPlugin = definePlugin( · 979 unmodified lines | ||
| 1290 | 1294 | }), |
| 1291 | 1295 | }); |
| 1292 | 1296 | |
| 1297 | + if (state.clientInformation === null) { | |
| 1298 | + return yield* new ConnectionRefreshError({ | |
| 1299 | + connectionId: input.connectionId, | |
| 1300 | + message: | |
| 1301 | + "mcp:oauth2 connection has no clientInformation — re-sign-in required (legacy row; DCR was never persisted)", | |
| 1302 | + }); | |
| 1303 | + } | |
| 1304 | + | |
| 1293 | 1305 | const authServerUrl = |
| 1294 | 1306 | state.authorizationServerUrl ?? |
| 1295 | 1307 | new URL("/", state.endpoint).toString(); |