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 e902da9..5650954 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< @@ -164,6 +167,7 @@ const App = () => { args, sseUrl, env, + bearerToken, proxyServerUrl: PROXY_SERVER_URL, onNotification: (notification) => { setNotifications((prev) => [...prev, notification as ServerNotification]); @@ -199,6 +203,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"); @@ -418,6 +426,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 9e7bb11..4abd5c6 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -38,6 +38,7 @@ interface UseConnectionOptions { sseUrl: string; env: Record; proxyServerUrl: string; + bearerToken?: string; requestTimeout?: number; onNotification?: (notification: Notification) => void; onStdErrNotification?: (notification: Notification) => void; @@ -58,6 +59,7 @@ export function useConnection({ sseUrl, env, proxyServerUrl, + bearerToken, requestTimeout = DEFAULT_REQUEST_TIMEOUT_MSEC, onNotification, onStdErrNotification, @@ -229,9 +231,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, {