History

mcp: allow null clientInformation on connection provider_state

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.

Rhys Sullivan committed 6 months ago

Commit e78249c1 parent 3c74de23 files · +14 −17Browse files

Unified · Split

3 files
  1. apps/cloud/scripts/migrate-mcp-connections.ts +1 −9
  2. apps/local/src/server/migrate-connections.ts +0 −7
  3. packages/plugins/mcp/src/sdk/plugin.ts +13 −1
apps/cloud/scripts/migrate-mcp-connections.ts+1−9
@@ -97,14 +97,6 @@ const classifyRow = (row: Row): Bucket => { · 96 unmodified lines
9797 reason: "config.endpoint missing",
9898 };
9999 }
100− if (legacy.clientInformation === null) {
101− return {
102− kind: "legacy-blocked",
103− row,
104− legacy,
105− reason: "clientInformation missing — DCR never completed",
106− };
107− }
108100 return { kind: "legacy-migratable", row, legacy, endpoint };
109101 }
110102
@@ -230,7 +222,7 @@ const main = async () => { · 119 unmodified lines
230222 const providerState = {
231223 endpoint: b.endpoint,
232224 tokenType: l.tokenType,
233− clientInformation: l.clientInformation!,
225+ clientInformation: l.clientInformation,
234226 authorizationServerUrl: l.authorizationServerUrl,
235227 authorizationServerMetadata: null,
236228 resourceMetadataUrl: l.resourceMetadataUrl,
apps/local/src/server/migrate-connections.ts+0−7
@@ -405,13 +405,6 @@ const migrateMcp = (sqlite: Database): void => { · 404 unmodified lines
405405 );
406406 continue;
407407 }
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−
415408 const connectionId = `mcp-oauth2-${row.id}`;
416409 const providerState = {
417410 endpoint,
packages/plugins/mcp/src/sdk/plugin.ts+13−1
@@ -304,7 +304,11 @@ const MCP_OAUTH2_PROVIDER_KEY = "mcp:oauth2" as const; · 303 unmodified lines
304304 const OAuth2ProviderState = Schema.Struct({
305305 endpoint: Schema.String,
306306 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),
308312 authorizationServerUrl: Schema.NullOr(Schema.String),
309313 authorizationServerMetadata: Schema.NullOr(McpJsonObject),
310314 resourceMetadataUrl: Schema.NullOr(Schema.String),
@@ -1290,6 +1294,14 @@ export const mcpPlugin = definePlugin( · 979 unmodified lines
12901294 }),
12911295 });
12921296
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+
12931305 const authServerUrl =
12941306 state.authorizationServerUrl ??
12951307 new URL("/", state.endpoint).toString();