From be08f6d37d0e9fdc4873c9ca2b176db2b9db72ff Mon Sep 17 00:00:00 2001 From: cliffhall Date: Mon, 7 Sep 2026 02:28:07 -0400 Subject: [PATCH] test: restore end-to-end coverage of resolveSdkRegistrationKind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot review (#2287): the CIMD E2E assertion had gone vacuous. Once the pre-registration started writing the issuer-keyed slot itself, the SDK found an already-stamped credential and never called `saveClientInformation` at all — so the assertion held even with the resolver gutted. Verified: reverting the dispatch to `: "dcr"` left all 34 tests passing. Add a case that seeds the legacy *unkeyed* CIMD registration — what every pre-SEP-2352 install has on disk, and the exact shape #2242 was reported against. The SDK back-stamps it with the issuer, which is the save that used to relabel it `Dynamic (DCR)`, so the resolver is genuinely on the path. That case now fails on both SSE and Streamable HTTP when the dispatch is reverted, which is the reported bug reproduced across the integration boundary. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Lmj6X2epWpPE7ScdSKQmtA Signed-off-by: cliffhall --- .../mcp/inspectorClient-oauth-e2e.test.ts | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/clients/web/src/test/integration/mcp/inspectorClient-oauth-e2e.test.ts b/clients/web/src/test/integration/mcp/inspectorClient-oauth-e2e.test.ts index 9e5431b7..dfbedba9 100644 --- a/clients/web/src/test/integration/mcp/inspectorClient-oauth-e2e.test.ts +++ b/clients/web/src/test/integration/mcp/inspectorClient-oauth-e2e.test.ts @@ -374,6 +374,103 @@ describe("InspectorClient OAuth E2E", () => { // #2242: the metadata-document URL is the client_id, and the stored // provenance still says CIMD after the SDK bound the registration to // the issuer — no `POST /register` ever happened. + // + // ⚠️ This assertion alone does not exercise `resolveSdkRegistrationKind`: + // the CIMD pre-registration now writes the issuer-keyed slot itself, so + // the SDK finds an already-stamped credential and never calls + // `saveClientInformation`. The test below covers the resolver across the + // integration boundary (Copilot). + const oauthState = await client.getOAuthState(); + expect(oauthState?.client).toMatchObject({ + clientId: metadataUrl, + registrationKind: "cimd", + }); + }); + + // The reported #2242 shape: an unkeyed CIMD registration — what every + // pre-SEP-2352 install has on disk, and what the pre-registration wrote + // before it knew the issuer. The SDK back-stamps it, which is the save + // that used to relabel it `Dynamic (DCR)`. This is the case that puts + // `resolveSdkRegistrationKind` on the path end to end. + it("keeps CIMD provenance when the SDK issuer-stamps an unkeyed registration", async () => { + const testRedirectUrl = "http://localhost:3001/oauth/callback"; + + const clientMetadata: ClientMetadataDocument = { + redirect_uris: [testRedirectUrl], + token_endpoint_auth_method: "none", + grant_types: ["authorization_code", "refresh_token"], + response_types: ["code"], + client_name: "MCP Inspector Test Client", + client_uri: "https://github.com/modelcontextprotocol/inspector", + scope: "mcp", + }; + + metadataServer = await createClientMetadataServer(clientMetadata); + const metadataUrl = metadataServer.url; + + const serverConfig = { + ...getDefaultServerConfig(), + serverType: transport.serverType, + ...createOAuthTestServerConfig({ + requireAuth: true, + supportCIMD: true, + }), + }; + + server = new TestServerHttp(serverConfig); + const port = await server.start(); + const serverUrl = `http://localhost:${port}`; + await waitForOAuthWellKnown(serverUrl); + + const oauthConfig = createTestOAuthConfig({ + mode: "cimd", + clientMetadataUrl: metadataUrl, + redirectUrl: testRedirectUrl, + }); + + const mcpUrl = `${serverUrl}${transport.endpoint}`; + // Seed the legacy unkeyed slot: a CIMD registration with no issuer. + await oauthConfig.storage.saveClientInformation( + mcpUrl, + { client_id: metadataUrl }, + { registrationKind: "cimd" }, + ); + + const clientConfig: InspectorClientOptions = { + environment: { + transport: createTransportNode, + oauth: { + storage: oauthConfig.storage, + navigation: oauthConfig.navigation, + redirectUrlProvider: oauthConfig.redirectUrlProvider, + }, + }, + oauth: { + clientId: oauthConfig.clientId, + clientSecret: oauthConfig.clientSecret, + clientMetadataUrl: oauthConfig.clientMetadataUrl, + scope: oauthConfig.scope, + }, + }; + + client = new InspectorClient( + { + type: transport.clientType, + url: mcpUrl, + } as MCPServerConfig, + clientConfig, + ); + + const authUrl = await client.authenticate(); + if (!authUrl) throw new Error("Expected authorization URL"); + + const { code: authCode, iss: authCodeIss } = + await completeOAuthAuthorization(authUrl); + await client.completeOAuthFlow(authCode, authCodeIss); + await client.connect(); + + expect(client.getStatus()).toBe("connected"); + const oauthState = await client.getOAuthState(); expect(oauthState?.client).toMatchObject({ clientId: metadataUrl,