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 | reason: "config.endpoint missing", | 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 | return { kind: "legacy-migratable", row, legacy, endpoint }; | 100 | return { kind: "legacy-migratable", row, legacy, endpoint }; |
| 109 | } | 101 | } |
| 110 | 102 | ||
| @@ -230,7 +222,7 @@ const main = async () => { · 119 unmodified lines | |||
| 230 | const providerState = { | 222 | const providerState = { |
| 231 | endpoint: b.endpoint, | 223 | endpoint: b.endpoint, |
| 232 | tokenType: l.tokenType, | 224 | tokenType: l.tokenType, |
| 233 | clientInformation: l.clientInformation!, | 225 | clientInformation: l.clientInformation, |
| 234 | authorizationServerUrl: l.authorizationServerUrl, | 226 | authorizationServerUrl: l.authorizationServerUrl, |
| 235 | authorizationServerMetadata: null, | 227 | authorizationServerMetadata: null, |
| 236 | resourceMetadataUrl: l.resourceMetadataUrl, | 228 | resourceMetadataUrl: l.resourceMetadataUrl, |
| @@ -405,13 +405,6 @@ const migrateMcp = (sqlite: Database): void => { · 404 unmodified lines | |||
| 405 | ); | 405 | ); |
| 406 | continue; | 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 | const connectionId = `mcp-oauth2-${row.id}`; | 408 | const connectionId = `mcp-oauth2-${row.id}`; |
| 416 | const providerState = { | 409 | const providerState = { |
| 417 | endpoint, | 410 | endpoint, |
| @@ -304,7 +304,11 @@ const MCP_OAUTH2_PROVIDER_KEY = "mcp:oauth2" as const; · 303 unmodified lines | |||
| 304 | const OAuth2ProviderState = Schema.Struct({ | 304 | const OAuth2ProviderState = Schema.Struct({ |
| 305 | endpoint: Schema.String, | 305 | endpoint: Schema.String, |
| 306 | tokenType: Schema.String, | 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 | authorizationServerUrl: Schema.NullOr(Schema.String), | 312 | authorizationServerUrl: Schema.NullOr(Schema.String), |
| 309 | authorizationServerMetadata: Schema.NullOr(McpJsonObject), | 313 | authorizationServerMetadata: Schema.NullOr(McpJsonObject), |
| 310 | resourceMetadataUrl: Schema.NullOr(Schema.String), | 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 | const authServerUrl = | 1305 | const authServerUrl = |
| 1294 | state.authorizationServerUrl ?? | 1306 | state.authorizationServerUrl ?? |
| 1295 | new URL("/", state.endpoint).toString(); | 1307 | new URL("/", state.endpoint).toString(); |