Skip to content

[OAuth] Request all supported scopes when not using the OAuth debugger #455

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,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.
Expand Down
9 changes: 9 additions & 0 deletions client/src/lib/hooks/__tests__/useConnection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ jest.mock("@modelcontextprotocol/sdk/client/streamableHttp.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
Expand Down
18 changes: 16 additions & 2 deletions client/src/lib/hooks/useConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ 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 {
Expand Down Expand Up @@ -265,7 +269,17 @@ 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";
}

Expand Down
Loading