Skip to content

Add Auth debugger tab #355

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

Merged
merged 37 commits into from
May 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
454a609
wip auth debugger
pcarleton Apr 29, 2025
cc77b84
cleanup types and validation
pcarleton May 1, 2025
d11f2db
more cleanup
pcarleton May 1, 2025
be22205
draft test
pcarleton May 1, 2025
19f01e1
wip clean up some
pcarleton May 7, 2025
6277126
rm toasts
pcarleton May 8, 2025
ecefbdd
consolidate state management
pcarleton May 8, 2025
2f8ab16
prettier
pcarleton May 8, 2025
5158c22
hoist state up to App
pcarleton May 8, 2025
5dca3ed
working with quick and guided
pcarleton May 8, 2025
cde4663
sort out displaying debugger
pcarleton May 8, 2025
506d907
prettier
pcarleton May 8, 2025
f4664c7
cleanup types
pcarleton May 8, 2025
c46df44
fix tests
pcarleton May 8, 2025
c3a565f
cleanup comment
pcarleton May 8, 2025
f6053b0
prettier
pcarleton May 8, 2025
779621e
fixup types in tests
pcarleton May 8, 2025
8e8eb41
prettier
pcarleton May 8, 2025
4a67d0c
refactor debug to avoid toasting
pcarleton May 8, 2025
cbe6324
callback shuffling
pcarleton May 8, 2025
f986114
linting
pcarleton May 8, 2025
10cd3e2
types
pcarleton May 8, 2025
50a4895
rm toast in test
pcarleton May 8, 2025
bcfb332
Merge branch 'main' into pcarleton/auth-debugger
pcarleton May 12, 2025
bf4b810
bump typescript sdk version to 0.11.2 for scope parameter passing
pcarleton May 12, 2025
70b965f
use proper scope handling
pcarleton May 12, 2025
415ca4c
test scope parameter passing
pcarleton May 12, 2025
fddcf8f
move functions and s/sseUrl/serverUrl/
pcarleton May 13, 2025
79ef9ab
extract status message into component
pcarleton May 13, 2025
dc875a0
refactor progress and steps into components
pcarleton May 13, 2025
0824277
fix test
pcarleton May 13, 2025
6407465
rename quick handler
pcarleton May 13, 2025
f36c133
one less click
pcarleton May 13, 2025
3ec4069
last step complete
pcarleton May 13, 2025
78c9c21
add state machine
pcarleton May 13, 2025
adaa023
test and types
pcarleton May 13, 2025
78ed3c7
Merge branch 'main' into pcarleton/auth-debugger
pcarleton May 13, 2025
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
133 changes: 131 additions & 2 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import {
Tool,
LoggingLevel,
} from "@modelcontextprotocol/sdk/types.js";
import { OAuthTokensSchema } from "@modelcontextprotocol/sdk/shared/auth.js";
import { SESSION_KEYS, getServerSpecificKey } from "./lib/constants";
import { AuthDebuggerState } from "./lib/auth-types";
import React, {
Suspense,
useCallback,
Expand All @@ -28,18 +31,21 @@ import { useConnection } from "./lib/hooks/useConnection";
import { useDraggablePane } from "./lib/hooks/useDraggablePane";
import { StdErrNotification } from "./lib/notificationTypes";

import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import {
Bell,
Files,
FolderTree,
Hammer,
Hash,
Key,
MessageSquare,
} from "lucide-react";

import { z } from "zod";
import "./App.css";
import AuthDebugger from "./components/AuthDebugger";
import ConsoleTab from "./components/ConsoleTab";
import HistoryAndNotifications from "./components/History";
import PingTab from "./components/PingTab";
Expand Down Expand Up @@ -111,6 +117,27 @@ const App = () => {
}
>
>([]);
const [isAuthDebuggerVisible, setIsAuthDebuggerVisible] = useState(false);

// Auth debugger state
const [authState, setAuthState] = useState<AuthDebuggerState>({
isInitiatingAuth: false,
oauthTokens: null,
loading: true,
oauthStep: "metadata_discovery",
oauthMetadata: null,
oauthClientInfo: null,
authorizationUrl: null,
authorizationCode: "",
latestError: null,
statusMessage: null,
validationError: null,
});

// Helper function to update specific auth state properties
const updateAuthState = (updates: Partial<AuthDebuggerState>) => {
setAuthState((prev) => ({ ...prev, ...updates }));
};
const nextRequestId = useRef(0);
const rootsRef = useRef<Root[]>([]);

Expand Down Expand Up @@ -208,11 +235,64 @@ const App = () => {
(serverUrl: string) => {
setSseUrl(serverUrl);
setTransportType("sse");
setIsAuthDebuggerVisible(false);
void connectMcpServer();
},
[connectMcpServer],
);

// Update OAuth debug state during debug callback
const onOAuthDebugConnect = useCallback(
({
authorizationCode,
errorMsg,
}: {
authorizationCode?: string;
errorMsg?: string;
}) => {
setIsAuthDebuggerVisible(true);
if (authorizationCode) {
updateAuthState({
authorizationCode,
oauthStep: "token_request",
});
}
if (errorMsg) {
updateAuthState({
latestError: new Error(errorMsg),
});
}
},
[],
);

// Load OAuth tokens when sseUrl changes
useEffect(() => {
const loadOAuthTokens = async () => {
try {
if (sseUrl) {
const key = getServerSpecificKey(SESSION_KEYS.TOKENS, sseUrl);
const tokens = sessionStorage.getItem(key);
if (tokens) {
const parsedTokens = await OAuthTokensSchema.parseAsync(
JSON.parse(tokens),
);
updateAuthState({
oauthTokens: parsedTokens,
oauthStep: "complete",
});
}
}
} catch (error) {
console.error("Error loading OAuth tokens:", error);
} finally {
updateAuthState({ loading: false });
}
};

loadOAuthTokens();
}, [sseUrl]);

useEffect(() => {
fetch(`${getMCPProxyAddress(config)}/config`)
.then((response) => response.json())
Expand Down Expand Up @@ -446,6 +526,19 @@ const App = () => {
setStdErrNotifications([]);
};

// Helper component for rendering the AuthDebugger
const AuthDebuggerWrapper = () => (
<TabsContent value="auth">
<AuthDebugger
serverUrl={sseUrl}
onBack={() => setIsAuthDebuggerVisible(false)}
authState={authState}
updateAuthState={updateAuthState}
/>
</TabsContent>
);

// Helper function to render OAuth callback components
if (window.location.pathname === "/oauth/callback") {
const OAuthCallback = React.lazy(
() => import("./components/OAuthCallback"),
Expand All @@ -457,6 +550,17 @@ const App = () => {
);
}

if (window.location.pathname === "/oauth/callback/debug") {
const OAuthDebugCallback = React.lazy(
() => import("./components/OAuthDebugCallback"),
);
return (
<Suspense fallback={<div>Loading...</div>}>
<OAuthDebugCallback onConnect={onOAuthDebugConnect} />
</Suspense>
);
}

return (
<div className="flex h-screen bg-background">
<Sidebar
Expand Down Expand Up @@ -544,6 +648,10 @@ const App = () => {
<FolderTree className="w-4 h-4 mr-2" />
Roots
</TabsTrigger>
<TabsTrigger value="auth">
<Key className="w-4 h-4 mr-2" />
Auth
</TabsTrigger>
</TabsList>

<div className="w-full">
Expand Down Expand Up @@ -689,15 +797,36 @@ const App = () => {
setRoots={setRoots}
onRootsChange={handleRootsChange}
/>
<AuthDebuggerWrapper />
</>
)}
</div>
</Tabs>
) : isAuthDebuggerVisible ? (
<Tabs
defaultValue={"auth"}
className="w-full p-4"
onValueChange={(value) => (window.location.hash = value)}
>
<AuthDebuggerWrapper />
</Tabs>
) : (
<div className="flex items-center justify-center h-full">
<div className="flex flex-col items-center justify-center h-full gap-4">
<p className="text-lg text-gray-500">
Connect to an MCP server to start inspecting
</p>
<div className="flex items-center gap-2">
<p className="text-sm text-muted-foreground">
Need to configure authentication?
</p>
<Button
variant="outline"
size="sm"
onClick={() => setIsAuthDebuggerVisible(true)}
>
Open Auth Settings
</Button>
</div>
</div>
)}
</div>
Expand Down
Loading