From c46d66607c7574a39f14a0cf76fee0c54e0d086d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Leblanc?= Date: Tue, 27 May 2025 16:59:40 -0400 Subject: [PATCH 1/3] [OAuth] Request all supported scopes when not using the OAuth debugger When using the OAuth debugger, it correctly adds the scope search parameter with the ones provided in the `scopes_supported` key. However, the main "connect" button does not pass them to the MCP SDK and they are missing from the call. Some OAuth providers and setups will require you to have them and the OAuth flow fails in these cases. This change fixes that. --- .../src/lib/hooks/__tests__/useConnection.test.tsx | 9 +++++++++ client/src/lib/hooks/useConnection.ts | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/client/src/lib/hooks/__tests__/useConnection.test.tsx b/client/src/lib/hooks/__tests__/useConnection.test.tsx index c6700dcfd..3e99270db 100644 --- a/client/src/lib/hooks/__tests__/useConnection.test.tsx +++ b/client/src/lib/hooks/__tests__/useConnection.test.tsx @@ -34,6 +34,15 @@ jest.mock("@modelcontextprotocol/sdk/client/sse.js", () => ({ jest.mock("@modelcontextprotocol/sdk/client/auth.js", () => ({ auth: jest.fn().mockResolvedValue("AUTHORIZED"), + discoverOAuthMetadata: jest.fn().mockResolvedValue({ + issuer: "https://oauth.example.com", + authorization_endpoint: "https://oauth.example.com/authorize", + token_endpoint: "https://oauth.example.com/token", + response_types_supported: ["code"], + grant_types_supported: ["authorization_code", "refresh_token"], + code_challenge_methods_supported: ["S256"], + scopes_supported: ["read", "write"], + }), })); // Mock the toast hook diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index 9c8253189..252e7a1f5 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -30,12 +30,13 @@ import { Progress, } from "@modelcontextprotocol/sdk/types.js"; import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js"; +import { OAuthMetadataSchema } from "@modelcontextprotocol/sdk/shared/auth.js"; import { useState } from "react"; import { useToast } from "@/lib/hooks/useToast"; import { z } from "zod"; import { ConnectionStatus } from "../constants"; import { Notification, StdErrNotificationSchema } from "../notificationTypes"; -import { auth } from "@modelcontextprotocol/sdk/client/auth.js"; +import { auth, discoverOAuthMetadata } from "@modelcontextprotocol/sdk/client/auth.js"; import { InspectorOAuthClientProvider } from "../auth"; import packageJson from "../../../package.json"; import { @@ -261,7 +262,14 @@ export function useConnection({ if (is401Error(error)) { const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl); - const result = await auth(serverAuthProvider, { serverUrl: sseUrl }); + // Use all supported scopes if available + const metadata = await discoverOAuthMetadata(sseUrl); + if (!metadata) { + throw new Error("Failed to discover OAuth metadata"); + } + const parsedMetadata = await OAuthMetadataSchema.parseAsync(metadata); + const scope = parsedMetadata.scopes_supported?.join(" "); + const result = await auth(serverAuthProvider, { serverUrl: sseUrl, scope }); return result === "AUTHORIZED"; } From 40c3c3fd3715840f0d643d920b0d63e4563cd3fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Leblanc?= Date: Tue, 27 May 2025 17:11:39 -0400 Subject: [PATCH 2/3] Add paragraph talking about OAuth --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d7c34b971..390a9d046 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,8 @@ You can paste the Server Entry into your existing `mcp.json` file under your cho The inspector supports bearer token authentication for SSE connections. Enter your token in the UI when connecting to an MCP server, and it will be sent in the Authorization header. You can override the header name using the input field in the sidebar. +It also follows [the MCP specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization#2-authorization-flow) for OAuth for remote transports. It uses all the scopes specified in the provider's `scopes_supported` field to help with debugging. + ### Security Considerations The MCP Inspector includes a proxy server that can run and communicate with local MCP processes. The proxy server should not be exposed to untrusted networks as it has permissions to spawn local processes and can connect to any specified MCP server. From 5ab39c75ae8280b9e8b6548d5060dbdd0fe91d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Leblanc?= Date: Mon, 9 Jun 2025 21:33:42 -0400 Subject: [PATCH 3/3] Fix linting --- client/src/lib/hooks/__tests__/useConnection.test.tsx | 2 +- client/src/lib/hooks/useConnection.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/client/src/lib/hooks/__tests__/useConnection.test.tsx b/client/src/lib/hooks/__tests__/useConnection.test.tsx index a2bce53d6..f1780b505 100644 --- a/client/src/lib/hooks/__tests__/useConnection.test.tsx +++ b/client/src/lib/hooks/__tests__/useConnection.test.tsx @@ -44,7 +44,7 @@ jest.mock("@modelcontextprotocol/sdk/client/auth.js", () => ({ auth: jest.fn().mockResolvedValue("AUTHORIZED"), discoverOAuthMetadata: jest.fn().mockResolvedValue({ issuer: "https://oauth.example.com", - authorization_endpoint: "https://oauth.example.com/authorize", + authorization_endpoint: "https://oauth.example.com/authorize", token_endpoint: "https://oauth.example.com/token", response_types_supported: ["code"], grant_types_supported: ["authorization_code", "refresh_token"], diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index 9202971a8..17871ebd8 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -36,7 +36,10 @@ import { useToast } from "@/lib/hooks/useToast"; import { z } from "zod"; import { ConnectionStatus } from "../constants"; import { Notification, StdErrNotificationSchema } from "../notificationTypes"; -import { auth, discoverOAuthMetadata } from "@modelcontextprotocol/sdk/client/auth.js"; +import { + auth, + discoverOAuthMetadata, +} from "@modelcontextprotocol/sdk/client/auth.js"; import { InspectorOAuthClientProvider } from "../auth"; import packageJson from "../../../package.json"; import { @@ -273,7 +276,10 @@ export function useConnection({ } const parsedMetadata = await OAuthMetadataSchema.parseAsync(metadata); const scope = parsedMetadata.scopes_supported?.join(" "); - const result = await auth(serverAuthProvider, { serverUrl: sseUrl, scope }); + const result = await auth(serverAuthProvider, { + serverUrl: sseUrl, + scope, + }); return result === "AUTHORIZED"; }