test: restore end-to-end coverage of resolveSdkRegistrationKind
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) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lmj6X2epWpPE7ScdSKQmtA Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
6273790077
commit
be08f6d37d
1 changed files with 97 additions and 0 deletions
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue