Merge remote-tracking branch 'origin2/main' into meta_properies_in_tools

This commit is contained in:
lorenzo.neumann 2025-10-02 09:26:18 +02:00
commit 7624eefc1e

View file

@ -22,6 +22,8 @@ import { SESSION_KEYS, getServerSpecificKey } from "./lib/constants";
import { AuthDebuggerState, EMPTY_DEBUGGER_STATE } from "./lib/auth-types";
import { OAuthStateMachine } from "./lib/oauth-state-machine";
import { cacheToolOutputSchemas } from "./utils/schemaUtils";
import { cleanParams } from "./utils/paramUtils";
import type { JsonSchemaType } from "./utils/jsonUtils";
import React, {
Suspense,
useCallback,
@ -74,6 +76,10 @@ import ElicitationTab, {
PendingElicitationRequest,
ElicitationResponse,
} from "./components/ElicitationTab";
import {
CustomHeaders,
migrateFromLegacyAuth,
} from "./lib/types/customHeaders";
const CONFIG_LOCAL_STORAGE_KEY = "inspectorConfig_v1";
@ -103,6 +109,14 @@ const App = () => {
const [transportType, setTransportType] = useState<
"stdio" | "sse" | "streamable-http"
>(getInitialTransportType);
const [connectionType, setConnectionType] = useState<"direct" | "proxy">(
() => {
return (
(localStorage.getItem("lastConnectionType") as "direct" | "proxy") ||
"proxy"
);
},
);
const [logLevel, setLogLevel] = useState<LoggingLevel>("debug");
const [notifications, setNotifications] = useState<ServerNotification[]>([]);
const [roots, setRoots] = useState<Root[]>([]);
@ -127,6 +141,39 @@ const App = () => {
return localStorage.getItem("lastOauthScope") || "";
});
// Custom headers state with migration from legacy auth
const [customHeaders, setCustomHeaders] = useState<CustomHeaders>(() => {
const savedHeaders = localStorage.getItem("lastCustomHeaders");
if (savedHeaders) {
try {
return JSON.parse(savedHeaders);
} catch (error) {
console.warn(
`Failed to parse custom headers: "${savedHeaders}", will try legacy migration`,
error,
);
// Fall back to migration if JSON parsing fails
}
}
// Migrate from legacy auth if available
const legacyToken = localStorage.getItem("lastBearerToken") || "";
const legacyHeaderName = localStorage.getItem("lastHeaderName") || "";
if (legacyToken) {
return migrateFromLegacyAuth(legacyToken, legacyHeaderName);
}
// Default to empty array
return [
{
name: "Authorization",
value: "Bearer ",
enabled: false,
},
];
});
const [pendingSampleRequests, setPendingSampleRequests] = useState<
Array<
PendingRequest & {
@ -200,6 +247,7 @@ const App = () => {
serverCapabilities,
mcpClient,
requestHistory,
clearRequestHistory,
makeRequest,
sendNotification,
handleCompletion,
@ -212,11 +260,11 @@ const App = () => {
args,
sseUrl,
env,
bearerToken,
headerName,
customHeaders,
oauthClientId,
oauthScope,
config,
connectionType,
onNotification: (notification) => {
setNotifications((prev) => [...prev, notification as ServerNotification]);
},
@ -302,13 +350,42 @@ const App = () => {
}, [transportType]);
useEffect(() => {
localStorage.setItem("lastBearerToken", bearerToken);
localStorage.setItem("lastConnectionType", connectionType);
}, [connectionType]);
useEffect(() => {
if (bearerToken) {
localStorage.setItem("lastBearerToken", bearerToken);
} else {
localStorage.removeItem("lastBearerToken");
}
}, [bearerToken]);
useEffect(() => {
localStorage.setItem("lastHeaderName", headerName);
if (headerName) {
localStorage.setItem("lastHeaderName", headerName);
} else {
localStorage.removeItem("lastHeaderName");
}
}, [headerName]);
useEffect(() => {
localStorage.setItem("lastCustomHeaders", JSON.stringify(customHeaders));
}, [customHeaders]);
// Auto-migrate from legacy auth when custom headers are empty but legacy auth exists
useEffect(() => {
if (customHeaders.length === 0 && (bearerToken || headerName)) {
const migratedHeaders = migrateFromLegacyAuth(bearerToken, headerName);
if (migratedHeaders.length > 0) {
setCustomHeaders(migratedHeaders);
// Clear legacy auth after migration
setBearerToken("");
setHeaderName("");
}
}
}, [bearerToken, headerName, customHeaders, setCustomHeaders]);
useEffect(() => {
localStorage.setItem("lastOauthClientId", oauthClientId);
}, [oauthClientId]);
@ -706,12 +783,18 @@ const App = () => {
lastToolCallOriginTabRef.current = currentTabRef.current;
try {
// Find the tool schema to clean parameters properly
const tool = tools.find((t) => t.name === name);
const cleanedParams = tool?.inputSchema
? cleanParams(params, tool.inputSchema as JsonSchemaType)
: params;
const response = await sendMCPRequest(
{
method: "tools/call" as const,
params: {
name,
arguments: params,
arguments: cleanedParams,
_meta: {
progressToken: progressTokenRef.current++,
...(meta ?? {}),
@ -723,6 +806,8 @@ const App = () => {
);
setToolResult(response);
// Clear any validation errors since tool execution completed
setErrors((prev) => ({ ...prev, tools: null }));
} catch (e) {
const toolResult: CompatibilityCallToolResult = {
content: [
@ -734,6 +819,8 @@ const App = () => {
isError: true,
};
setToolResult(toolResult);
// Clear validation errors - tool execution errors are shown in ToolResults
setErrors((prev) => ({ ...prev, tools: null }));
}
};
@ -741,6 +828,10 @@ const App = () => {
await sendNotification({ method: "notifications/roots/list_changed" });
};
const handleClearNotifications = () => {
setNotifications([]);
};
const sendLogLevelRequest = async (level: LoggingLevel) => {
await sendMCPRequest(
{
@ -810,10 +901,8 @@ const App = () => {
setEnv={setEnv}
config={config}
setConfig={setConfig}
bearerToken={bearerToken}
setBearerToken={setBearerToken}
headerName={headerName}
setHeaderName={setHeaderName}
customHeaders={customHeaders}
setCustomHeaders={setCustomHeaders}
oauthClientId={oauthClientId}
setOauthClientId={setOauthClientId}
oauthScope={oauthScope}
@ -823,6 +912,8 @@ const App = () => {
logLevel={logLevel}
sendLogLevelRequest={sendLogLevelRequest}
loggingSupported={!!serverCapabilities?.logging || false}
connectionType={connectionType}
setConnectionType={setConnectionType}
/>
<div
onMouseDown={handleSidebarDragStart}
@ -1111,6 +1202,8 @@ const App = () => {
<HistoryAndNotifications
requestHistory={requestHistory}
serverNotifications={notifications}
onClearHistory={clearRequestHistory}
onClearNotifications={handleClearNotifications}
/>
</div>
</div>