@@ -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).
|
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
|
### From this repository
|
||||||
|
|
||||||
If you're working on the inspector itself:
|
If you're working on the inspector itself:
|
||||||
|
|||||||
@@ -97,6 +97,9 @@ const App = () => {
|
|||||||
>([]);
|
>([]);
|
||||||
const [roots, setRoots] = useState<Root[]>([]);
|
const [roots, setRoots] = useState<Root[]>([]);
|
||||||
const [env, setEnv] = useState<Record<string, string>>({});
|
const [env, setEnv] = useState<Record<string, string>>({});
|
||||||
|
const [bearerToken, setBearerToken] = useState<string>(() => {
|
||||||
|
return localStorage.getItem("lastBearerToken") || "";
|
||||||
|
});
|
||||||
|
|
||||||
const [pendingSampleRequests, setPendingSampleRequests] = useState<
|
const [pendingSampleRequests, setPendingSampleRequests] = useState<
|
||||||
Array<
|
Array<
|
||||||
@@ -164,6 +167,7 @@ const App = () => {
|
|||||||
args,
|
args,
|
||||||
sseUrl,
|
sseUrl,
|
||||||
env,
|
env,
|
||||||
|
bearerToken,
|
||||||
proxyServerUrl: PROXY_SERVER_URL,
|
proxyServerUrl: PROXY_SERVER_URL,
|
||||||
onNotification: (notification) => {
|
onNotification: (notification) => {
|
||||||
setNotifications((prev) => [...prev, notification as ServerNotification]);
|
setNotifications((prev) => [...prev, notification as ServerNotification]);
|
||||||
@@ -199,6 +203,10 @@ const App = () => {
|
|||||||
localStorage.setItem("lastTransportType", transportType);
|
localStorage.setItem("lastTransportType", transportType);
|
||||||
}, [transportType]);
|
}, [transportType]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem("lastBearerToken", bearerToken);
|
||||||
|
}, [bearerToken]);
|
||||||
|
|
||||||
// Auto-connect if serverUrl is provided in URL params (e.g. after OAuth callback)
|
// Auto-connect if serverUrl is provided in URL params (e.g. after OAuth callback)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const serverUrl = params.get("serverUrl");
|
const serverUrl = params.get("serverUrl");
|
||||||
@@ -418,6 +426,8 @@ const App = () => {
|
|||||||
setSseUrl={setSseUrl}
|
setSseUrl={setSseUrl}
|
||||||
env={env}
|
env={env}
|
||||||
setEnv={setEnv}
|
setEnv={setEnv}
|
||||||
|
bearerToken={bearerToken}
|
||||||
|
setBearerToken={setBearerToken}
|
||||||
onConnect={connectMcpServer}
|
onConnect={connectMcpServer}
|
||||||
stdErrNotifications={stdErrNotifications}
|
stdErrNotifications={stdErrNotifications}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ interface SidebarProps {
|
|||||||
setSseUrl: (url: string) => void;
|
setSseUrl: (url: string) => void;
|
||||||
env: Record<string, string>;
|
env: Record<string, string>;
|
||||||
setEnv: (env: Record<string, string>) => void;
|
setEnv: (env: Record<string, string>) => void;
|
||||||
|
bearerToken: string;
|
||||||
|
setBearerToken: (token: string) => void;
|
||||||
onConnect: () => void;
|
onConnect: () => void;
|
||||||
stdErrNotifications: StdErrNotification[];
|
stdErrNotifications: StdErrNotification[];
|
||||||
}
|
}
|
||||||
@@ -51,11 +53,14 @@ const Sidebar = ({
|
|||||||
setSseUrl,
|
setSseUrl,
|
||||||
env,
|
env,
|
||||||
setEnv,
|
setEnv,
|
||||||
|
bearerToken,
|
||||||
|
setBearerToken,
|
||||||
onConnect,
|
onConnect,
|
||||||
stdErrNotifications,
|
stdErrNotifications,
|
||||||
}: SidebarProps) => {
|
}: SidebarProps) => {
|
||||||
const [theme, setTheme] = useTheme();
|
const [theme, setTheme] = useTheme();
|
||||||
const [showEnvVars, setShowEnvVars] = useState(false);
|
const [showEnvVars, setShowEnvVars] = useState(false);
|
||||||
|
const [showBearerToken, setShowBearerToken] = useState(false);
|
||||||
const [shownEnvVars, setShownEnvVars] = useState<Set<string>>(new Set());
|
const [shownEnvVars, setShownEnvVars] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -110,6 +115,7 @@ const Sidebar = ({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
<>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">URL</label>
|
<label className="text-sm font-medium">URL</label>
|
||||||
<Input
|
<Input
|
||||||
@@ -119,6 +125,33 @@ const Sidebar = ({
|
|||||||
className="font-mono"
|
className="font-mono"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setShowBearerToken(!showBearerToken)}
|
||||||
|
className="flex items-center w-full"
|
||||||
|
>
|
||||||
|
{showBearerToken ? (
|
||||||
|
<ChevronDown className="w-4 h-4 mr-2" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="w-4 h-4 mr-2" />
|
||||||
|
)}
|
||||||
|
Authentication
|
||||||
|
</Button>
|
||||||
|
{showBearerToken && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Bearer Token</label>
|
||||||
|
<Input
|
||||||
|
placeholder="Bearer Token"
|
||||||
|
value={bearerToken}
|
||||||
|
onChange={(e) => setBearerToken(e.target.value)}
|
||||||
|
className="font-mono"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{transportType === "stdio" && (
|
{transportType === "stdio" && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ interface UseConnectionOptions {
|
|||||||
sseUrl: string;
|
sseUrl: string;
|
||||||
env: Record<string, string>;
|
env: Record<string, string>;
|
||||||
proxyServerUrl: string;
|
proxyServerUrl: string;
|
||||||
|
bearerToken?: string;
|
||||||
requestTimeout?: number;
|
requestTimeout?: number;
|
||||||
onNotification?: (notification: Notification) => void;
|
onNotification?: (notification: Notification) => void;
|
||||||
onStdErrNotification?: (notification: Notification) => void;
|
onStdErrNotification?: (notification: Notification) => void;
|
||||||
@@ -58,6 +59,7 @@ export function useConnection({
|
|||||||
sseUrl,
|
sseUrl,
|
||||||
env,
|
env,
|
||||||
proxyServerUrl,
|
proxyServerUrl,
|
||||||
|
bearerToken,
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT_MSEC,
|
requestTimeout = DEFAULT_REQUEST_TIMEOUT_MSEC,
|
||||||
onNotification,
|
onNotification,
|
||||||
onStdErrNotification,
|
onStdErrNotification,
|
||||||
@@ -229,9 +231,11 @@ export function useConnection({
|
|||||||
// Inject auth manually instead of using SSEClientTransport, because we're
|
// Inject auth manually instead of using SSEClientTransport, because we're
|
||||||
// proxying through the inspector server first.
|
// proxying through the inspector server first.
|
||||||
const headers: HeadersInit = {};
|
const headers: HeadersInit = {};
|
||||||
const tokens = await authProvider.tokens();
|
|
||||||
if (tokens) {
|
// Use manually provided bearer token if available, otherwise use OAuth tokens
|
||||||
headers["Authorization"] = `Bearer ${tokens.access_token}`;
|
const token = bearerToken || (await authProvider.tokens())?.access_token;
|
||||||
|
if (token) {
|
||||||
|
headers["Authorization"] = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientTransport = new SSEClientTransport(backendUrl, {
|
const clientTransport = new SSEClientTransport(backendUrl, {
|
||||||
|
|||||||
Reference in New Issue
Block a user