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
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,
apps/local/src/server/migrate-connections.ts+0−7
@@ -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,
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
304const OAuth2ProviderState = Schema.Struct({304const 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();