From dbd616905c0faedfe737e2d19bcbe2b23c59dfc1 Mon Sep 17 00:00:00 2001 From: Avi Mizrahi Date: Mon, 10 Mar 2025 17:29:06 +0200 Subject: [PATCH] Support bearer token --- README.md | 4 +++ client/src/App.tsx | 10 ++++++ client/src/components/Sidebar.tsx | 51 ++++++++++++++++++++++----- client/src/lib/hooks/useConnection.ts | 10 ++++-- 4 files changed, 63 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a6da9f4..a6ab6d4 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,10 @@ CLIENT_PORT=8080 SERVER_PORT=9000 npx @modelcontextprotocol/inspector node build For more details on ways to use the inspector, see the [Inspector section of the MCP docs site](https://modelcontextprotocol.io/docs/tools/inspector). For help with debugging, see the [Debugging guide](https://modelcontextprotocol.io/docs/tools/debugging). +### Authentication + +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. + ### From this repository If you're working on the inspector itself: diff --git a/client/src/App.tsx b/client/src/App.tsx index a9adea5..011b5bd 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -97,6 +97,9 @@ const App = () => { >([]); const [roots, setRoots] = useState([]); const [env, setEnv] = useState>({}); + const [bearerToken, setBearerToken] = useState(() => { + return localStorage.getItem("lastBearerToken") || ""; + }); const [pendingSampleRequests, setPendingSampleRequests] = useState< Array< @@ -160,6 +163,7 @@ const App = () => { args, sseUrl, env, + bearerToken, proxyServerUrl: PROXY_SERVER_URL, onNotification: (notification) => { setNotifications((prev) => [...prev, notification as ServerNotification]); @@ -195,6 +199,10 @@ const App = () => { localStorage.setItem("lastTransportType", transportType); }, [transportType]); + useEffect(() => { + localStorage.setItem("lastBearerToken", bearerToken); + }, [bearerToken]); + // Auto-connect if serverUrl is provided in URL params (e.g. after OAuth callback) useEffect(() => { const serverUrl = params.get("serverUrl"); @@ -382,6 +390,8 @@ const App = () => { setSseUrl={setSseUrl} env={env} setEnv={setEnv} + bearerToken={bearerToken} + setBearerToken={setBearerToken} onConnect={connectMcpServer} stdErrNotifications={stdErrNotifications} /> diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index c95f621..48c6ff2 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -35,6 +35,8 @@ interface SidebarProps { setSseUrl: (url: string) => void; env: Record; setEnv: (env: Record) => void; + bearerToken: string; + setBearerToken: (token: string) => void; onConnect: () => void; stdErrNotifications: StdErrNotification[]; } @@ -51,11 +53,14 @@ const Sidebar = ({ setSseUrl, env, setEnv, + bearerToken, + setBearerToken, onConnect, stdErrNotifications, }: SidebarProps) => { const [theme, setTheme] = useTheme(); const [showEnvVars, setShowEnvVars] = useState(false); + const [showBearerToken, setShowBearerToken] = useState(false); const [shownEnvVars, setShownEnvVars] = useState>(new Set()); return ( @@ -110,15 +115,43 @@ const Sidebar = ({ ) : ( -
- - setSseUrl(e.target.value)} - className="font-mono" - /> -
+ <> +
+ + setSseUrl(e.target.value)} + className="font-mono" + /> +
+
+ + {showBearerToken && ( +
+ + setBearerToken(e.target.value)} + className="font-mono" + type="password" + /> +
+ )} +
+ )} {transportType === "stdio" && (
diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index 75b5467..468e9ad 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -37,6 +37,7 @@ interface UseConnectionOptions { sseUrl: string; env: Record; proxyServerUrl: string; + bearerToken?: string; requestTimeout?: number; onNotification?: (notification: Notification) => void; onStdErrNotification?: (notification: Notification) => void; @@ -57,6 +58,7 @@ export function useConnection({ sseUrl, env, proxyServerUrl, + bearerToken, requestTimeout = DEFAULT_REQUEST_TIMEOUT_MSEC, onNotification, onStdErrNotification, @@ -228,9 +230,11 @@ export function useConnection({ // Inject auth manually instead of using SSEClientTransport, because we're // proxying through the inspector server first. const headers: HeadersInit = {}; - const tokens = await authProvider.tokens(); - if (tokens) { - headers["Authorization"] = `Bearer ${tokens.access_token}`; + + // Use manually provided bearer token if available, otherwise use OAuth tokens + const token = bearerToken || (await authProvider.tokens())?.access_token; + if (token) { + headers["Authorization"] = `Bearer ${token}`; } const clientTransport = new SSEClientTransport(backendUrl, {