Skip to content

Commit 73fde03

Browse files
committed
working metadata fetch
1 parent b2fa996 commit 73fde03

File tree

7 files changed

+63
-112
lines changed

7 files changed

+63
-112
lines changed

client/src/App.tsx

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
} from "@modelcontextprotocol/sdk/types.js";
2020
import { OAuthTokensSchema } from "@modelcontextprotocol/sdk/shared/auth.js";
2121
import { SESSION_KEYS, getServerSpecificKey } from "./lib/constants";
22-
import { AuthDebuggerState } from "./lib/auth-types";
22+
import { AuthDebuggerState, EMPTY_DEBUGGER_STATE } from "./lib/auth-types";
2323
import { cacheToolOutputSchemas } from "./utils/schemaUtils";
2424
import React, {
2525
Suspense,
@@ -121,20 +121,7 @@ const App = () => {
121121
const [isAuthDebuggerVisible, setIsAuthDebuggerVisible] = useState(false);
122122

123123
// Auth debugger state
124-
const [authState, setAuthState] = useState<AuthDebuggerState>({
125-
isInitiatingAuth: false,
126-
oauthTokens: null,
127-
loading: true,
128-
oauthStep: "metadata_discovery",
129-
oauthMetadata: null,
130-
resourceMetadata: null,
131-
oauthClientInfo: null,
132-
authorizationUrl: null,
133-
authorizationCode: "",
134-
latestError: null,
135-
statusMessage: null,
136-
validationError: null,
137-
});
124+
const [authState, setAuthState] = useState<AuthDebuggerState>(EMPTY_DEBUGGER_STATE);
138125

139126
// Helper function to update specific auth state properties
140127
const updateAuthState = (updates: Partial<AuthDebuggerState>) => {

client/src/components/AuthDebugger.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useCallback, useMemo } from "react";
22
import { Button } from "@/components/ui/button";
33
import { DebugInspectorOAuthClientProvider } from "../lib/auth";
44
import { AlertCircle } from "lucide-react";
5-
import { AuthDebuggerState } from "../lib/auth-types";
5+
import { AuthDebuggerState, EMPTY_DEBUGGER_STATE } from "../lib/auth-types";
66
import { OAuthFlowProgress } from "./OAuthFlowProgress";
77
import { OAuthStateMachine } from "../lib/oauth-state-machine";
88

@@ -178,14 +178,7 @@ const AuthDebugger = ({
178178
);
179179
serverAuthProvider.clear();
180180
updateAuthState({
181-
oauthTokens: null,
182-
oauthStep: "metadata_discovery",
183-
latestError: null,
184-
oauthClientInfo: null,
185-
authorizationCode: "",
186-
authorizationUrl: "",
187-
validationError: null,
188-
oauthMetadata: null,
181+
...EMPTY_DEBUGGER_STATE,
189182
statusMessage: {
190183
type: "success",
191184
message: "OAuth tokens cleared successfully",

client/src/components/OAuthFlowProgress.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ interface OAuthFlowProgressProps {
5757
}
5858

5959
const steps: Array<OAuthStep> = [
60-
"resource_metadata_discovery",
6160
"metadata_discovery",
6261
"client_registration",
6362
"authorization_redirect",
@@ -139,6 +138,19 @@ export const OAuthFlowProgress = ({
139138
</pre>
140139
</details>
141140
)}
141+
{authState.resourceMetadataError && (
142+
<div className="mt-2 p-3 border border-yellow-300 bg-yellow-50 rounded-md">
143+
<p className="text-sm font-small text-yellow-700">
144+
Failed to retrieve resource metadata, falling back to /.well-known/oauth-authorization-server:
145+
</p>
146+
<p className="text-xs text-yellow-600 mt-1">
147+
{authState.resourceMetadataError.message}
148+
{authState.resourceMetadataError instanceof TypeError
149+
? " (This could indicate the endpoint doesn't exist or does not have CORS configured)"
150+
: authState.resourceMetadataError['status'] && ` (${authState.resourceMetadataError['status']})`}
151+
</p>
152+
</div>
153+
)}
142154
{provider.getServerMetadata() && (
143155
<details className="text-xs mt-2">
144156
<summary className="cursor-pointer text-muted-foreground font-medium">

client/src/components/__tests__/AuthDebugger.test.tsx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
auth,
5252
} from "@modelcontextprotocol/sdk/client/auth.js";
5353
import { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
54+
import { EMPTY_DEBUGGER_STATE } from "@/lib/auth-types";
5455

5556
// Type the mocked functions properly
5657
const mockDiscoverOAuthMetadata = discoverOAuthMetadata as jest.MockedFunction<
@@ -84,19 +85,7 @@ Object.defineProperty(window, "location", {
8485
});
8586

8687
describe("AuthDebugger", () => {
87-
const defaultAuthState = {
88-
isInitiatingAuth: false,
89-
oauthTokens: null,
90-
loading: false,
91-
oauthStep: "metadata_discovery" as const,
92-
oauthMetadata: null,
93-
oauthClientInfo: null,
94-
authorizationUrl: null,
95-
authorizationCode: "",
96-
latestError: null,
97-
statusMessage: null,
98-
validationError: null,
99-
};
88+
const defaultAuthState = EMPTY_DEBUGGER_STATE;
10089

10190
const defaultProps = {
10291
serverUrl: "https://example.com",

client/src/lib/auth-types.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
OAuthClientInformationFull,
44
OAuthClientInformation,
55
OAuthTokens,
6+
OAuthProtectedResourceMetadata,
67
} from "@modelcontextprotocol/sdk/shared/auth.js";
78

89
// OAuth flow steps
@@ -28,8 +29,8 @@ export interface AuthDebuggerState {
2829
oauthTokens: OAuthTokens | null;
2930
loading: boolean;
3031
oauthStep: OAuthStep;
31-
// TODO: use sdk type
32-
resourceMetadata: object | null;
32+
resourceMetadata: OAuthProtectedResourceMetadata | null;
33+
resourceMetadataError: Error | { status: number; statusText: string; message: string } | null;
3334
oauthMetadata: OAuthMetadata | null;
3435
oauthClientInfo: OAuthClientInformationFull | OAuthClientInformation | null;
3536
authorizationUrl: string | null;
@@ -38,3 +39,19 @@ export interface AuthDebuggerState {
3839
statusMessage: StatusMessage | null;
3940
validationError: string | null;
4041
}
42+
43+
export const EMPTY_DEBUGGER_STATE: AuthDebuggerState = {
44+
isInitiatingAuth: false,
45+
oauthTokens: null,
46+
loading: true,
47+
oauthStep: "metadata_discovery",
48+
oauthMetadata: null,
49+
resourceMetadata: null,
50+
resourceMetadataError: null,
51+
oauthClientInfo: null,
52+
authorizationUrl: null,
53+
authorizationCode: "",
54+
latestError: null,
55+
statusMessage: null,
56+
validationError: null,
57+
}

client/src/lib/oauth-state-machine.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import {
55
registerClient,
66
startAuthorization,
77
exchangeAuthorization,
8+
discoverOAuthProtectedResourceMetadata,
89
} from "@modelcontextprotocol/sdk/client/auth.js";
9-
import { OAuthMetadataSchema } from "@modelcontextprotocol/sdk/shared/auth.js";
10+
import { OAuthMetadataSchema, OAuthProtectedResourceMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
1011

1112
export interface StateMachineContext {
1213
state: AuthDebuggerState;
@@ -20,29 +21,28 @@ export interface StateTransition {
2021
execute: (context: StateMachineContext) => Promise<void>;
2122
}
2223

23-
const fetchProtectedResourceMetadata = async (serverUrl: string): Promise<object> => {
24-
// TODO: use sdk
25-
const url = new URL("/.well-known/oauth-protected-resource", serverUrl);
26-
const response = await fetch(url);
27-
const resourceMetadata = await response.json();
28-
29-
return resourceMetadata;
30-
}
31-
3224
// State machine transitions
3325
export const oauthTransitions: Record<OAuthStep, StateTransition> = {
3426
metadata_discovery: {
3527
canTransition: async () => true,
3628
execute: async (context) => {
37-
const authServerUrl = context.serverUrl;
38-
// try {
39-
// const resourceMetadata = await fetchProtectedResourceMetadata(context.serverUrl);
40-
// if (resourceMetadata && resourceMetadata) {
41-
// authServerUrl = resourceMetadata
42-
// }
43-
// } catch (_error) {
44-
// // pass
45-
// }
29+
let authServerUrl = context.serverUrl;
30+
let resourceMetadata: OAuthProtectedResourceMetadata | null = null;
31+
let resourceMetadataError: Error | null = null;
32+
try {
33+
resourceMetadata = await discoverOAuthProtectedResourceMetadata(context.serverUrl);
34+
if (resourceMetadata && resourceMetadata.authorization_servers?.length) {
35+
authServerUrl = resourceMetadata.authorization_servers[0];
36+
}
37+
} catch (e) {
38+
console.info(`Failed to find protected resource metadata: ${e}`);
39+
console.log(e);
40+
if (e instanceof Error) {
41+
resourceMetadataError = e;
42+
} else {
43+
resourceMetadataError = new Error(String(e));
44+
}
45+
}
4646

4747
const metadata = await discoverOAuthMetadata(authServerUrl);
4848
if (!metadata) {
@@ -52,6 +52,7 @@ export const oauthTransitions: Record<OAuthStep, StateTransition> = {
5252
context.provider.saveServerMetadata(parsedMetadata);
5353
context.updateState({
5454
resourceMetadata,
55+
resourceMetadataError,
5556
oauthMetadata: parsedMetadata,
5657
oauthStep: "client_registration",
5758
});

package-lock.json

Lines changed: 5 additions & 53 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)