Merge branch 'main' into update-dependencies
This commit is contained in:
commit
5598cff694
16 changed files with 1262 additions and 65 deletions
2
.husky/pre-commit
Normal file
2
.husky/pre-commit
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
npx lint-staged
|
||||
git update-index --again
|
||||
10
README.md
10
README.md
|
|
@ -166,6 +166,16 @@ If you need to disable authentication (NOT RECOMMENDED), you can set the `DANGER
|
|||
DANGEROUSLY_OMIT_AUTH=true npm start
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**🚨 WARNING 🚨**
|
||||
|
||||
Disabling authentication with `DANGEROUSLY_OMIT_AUTH` is incredibly dangerous! Disabling auth leaves your machine open to attack not just when exposed to the public internet, but also **via your web browser**. Meaning, visiting a malicious website OR viewing a malicious advertizement could allow an attacker to remotely compromise your computer. Do not disable this feature unless you truly understand the risks.
|
||||
|
||||
Read more about the risks of this vulnerability on Oligo's blog: [Critical RCE Vulnerability in Anthropic MCP Inspector - CVE-2025-49596](https://www.oligo.security/blog/critical-rce-vulnerability-in-anthropic-mcp-inspector-cve-2025-49596)
|
||||
|
||||
---
|
||||
|
||||
You can also set the token via the `MCP_PROXY_AUTH_TOKEN` environment variable when starting the server:
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@modelcontextprotocol/inspector-cli",
|
||||
"version": "0.16.3",
|
||||
"version": "0.16.5",
|
||||
"description": "CLI for the Model Context Protocol inspector",
|
||||
"license": "MIT",
|
||||
"author": "Anthropic, PBC (https://anthropic.com)",
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
},
|
||||
"devDependencies": {},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.17.0",
|
||||
"@modelcontextprotocol/sdk": "^1.17.3",
|
||||
"commander": "^13.1.0",
|
||||
"spawn-rx": "^5.1.2"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ function createStdioTransport(options: TransportOptions): Transport {
|
|||
const defaultEnv = getDefaultEnvironment();
|
||||
|
||||
const env: Record<string, string> = {
|
||||
...processEnv,
|
||||
...defaultEnv,
|
||||
...processEnv,
|
||||
};
|
||||
|
||||
const { cmd: actualCommand, args: actualArgs } = findActualExecutable(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@modelcontextprotocol/inspector-client",
|
||||
"version": "0.16.3",
|
||||
"version": "0.16.5",
|
||||
"description": "Client-side application for the Model Context Protocol inspector",
|
||||
"license": "MIT",
|
||||
"author": "Anthropic, PBC (https://anthropic.com)",
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
"cleanup:e2e": "node e2e/global-teardown.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.17.0",
|
||||
"@modelcontextprotocol/sdk": "^1.17.3",
|
||||
"@radix-ui/react-checkbox": "^1.1.4",
|
||||
"@radix-ui/react-dialog": "^1.1.3",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
|
|
|
|||
|
|
@ -46,6 +46,17 @@ const OAuthDebugCallback = ({ onConnect }: OAuthCallbackProps) => {
|
|||
if (storedState) {
|
||||
try {
|
||||
restoredState = JSON.parse(storedState);
|
||||
if (restoredState && typeof restoredState.resource === "string") {
|
||||
restoredState.resource = new URL(restoredState.resource);
|
||||
}
|
||||
if (
|
||||
restoredState &&
|
||||
typeof restoredState.authorizationUrl === "string"
|
||||
) {
|
||||
restoredState.authorizationUrl = new URL(
|
||||
restoredState.authorizationUrl,
|
||||
);
|
||||
}
|
||||
// Clean up the stored state
|
||||
sessionStorage.removeItem(SESSION_KEYS.AUTH_DEBUGGER_STATE);
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ const mockOAuthMetadata = {
|
|||
token_endpoint: "https://oauth.example.com/token",
|
||||
response_types_supported: ["code"],
|
||||
grant_types_supported: ["authorization_code"],
|
||||
scopes_supported: ["read", "write"],
|
||||
};
|
||||
|
||||
const mockOAuthClientInfo = {
|
||||
|
|
@ -56,6 +57,57 @@ import {
|
|||
import { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
|
||||
import { EMPTY_DEBUGGER_STATE } from "@/lib/auth-types";
|
||||
|
||||
// Mock local auth module
|
||||
jest.mock("@/lib/auth", () => ({
|
||||
DebugInspectorOAuthClientProvider: jest.fn().mockImplementation(() => ({
|
||||
tokens: jest.fn().mockImplementation(() => Promise.resolve(undefined)),
|
||||
clear: jest.fn().mockImplementation(() => {
|
||||
// Mock the real clear() behavior which removes items from sessionStorage
|
||||
sessionStorage.removeItem("[https://example.com/mcp] mcp_tokens");
|
||||
sessionStorage.removeItem("[https://example.com/mcp] mcp_client_info");
|
||||
sessionStorage.removeItem(
|
||||
"[https://example.com/mcp] mcp_server_metadata",
|
||||
);
|
||||
}),
|
||||
redirectUrl: "http://localhost:3000/oauth/callback/debug",
|
||||
clientMetadata: {
|
||||
redirect_uris: ["http://localhost:3000/oauth/callback/debug"],
|
||||
token_endpoint_auth_method: "none",
|
||||
grant_types: ["authorization_code", "refresh_token"],
|
||||
response_types: ["code"],
|
||||
client_name: "MCP Inspector",
|
||||
},
|
||||
clientInformation: jest.fn().mockImplementation(async () => {
|
||||
const serverUrl = "https://example.com/mcp";
|
||||
const preregisteredKey = `[${serverUrl}] ${SESSION_KEYS.PREREGISTERED_CLIENT_INFORMATION}`;
|
||||
const preregisteredData = sessionStorage.getItem(preregisteredKey);
|
||||
if (preregisteredData) {
|
||||
return JSON.parse(preregisteredData);
|
||||
}
|
||||
const dynamicKey = `[${serverUrl}] ${SESSION_KEYS.CLIENT_INFORMATION}`;
|
||||
const dynamicData = sessionStorage.getItem(dynamicKey);
|
||||
if (dynamicData) {
|
||||
return JSON.parse(dynamicData);
|
||||
}
|
||||
return undefined;
|
||||
}),
|
||||
saveClientInformation: jest.fn().mockImplementation((clientInfo) => {
|
||||
const serverUrl = "https://example.com/mcp";
|
||||
const key = `[${serverUrl}] ${SESSION_KEYS.CLIENT_INFORMATION}`;
|
||||
sessionStorage.setItem(key, JSON.stringify(clientInfo));
|
||||
}),
|
||||
saveTokens: jest.fn(),
|
||||
redirectToAuthorization: jest.fn(),
|
||||
saveCodeVerifier: jest.fn(),
|
||||
codeVerifier: jest.fn(),
|
||||
saveServerMetadata: jest.fn(),
|
||||
getServerMetadata: jest.fn(),
|
||||
})),
|
||||
discoverScopes: jest.fn().mockResolvedValue("read write" as never),
|
||||
}));
|
||||
|
||||
import { discoverScopes } from "@/lib/auth";
|
||||
|
||||
// Type the mocked functions properly
|
||||
const mockDiscoverAuthorizationServerMetadata =
|
||||
discoverAuthorizationServerMetadata as jest.MockedFunction<
|
||||
|
|
@ -75,6 +127,9 @@ const mockDiscoverOAuthProtectedResourceMetadata =
|
|||
discoverOAuthProtectedResourceMetadata as jest.MockedFunction<
|
||||
typeof discoverOAuthProtectedResourceMetadata
|
||||
>;
|
||||
const mockDiscoverScopes = discoverScopes as jest.MockedFunction<
|
||||
typeof discoverScopes
|
||||
>;
|
||||
|
||||
const sessionStorageMock = {
|
||||
getItem: jest.fn(),
|
||||
|
|
@ -103,9 +158,15 @@ describe("AuthDebugger", () => {
|
|||
// Suppress console errors in tests to avoid JSDOM navigation noise
|
||||
jest.spyOn(console, "error").mockImplementation(() => {});
|
||||
|
||||
mockDiscoverAuthorizationServerMetadata.mockResolvedValue(
|
||||
mockOAuthMetadata,
|
||||
);
|
||||
// Set default mock behaviors with complete OAuth metadata
|
||||
mockDiscoverAuthorizationServerMetadata.mockResolvedValue({
|
||||
issuer: "https://oauth.example.com",
|
||||
authorization_endpoint: "https://oauth.example.com/authorize",
|
||||
token_endpoint: "https://oauth.example.com/token",
|
||||
response_types_supported: ["code"],
|
||||
grant_types_supported: ["authorization_code"],
|
||||
scopes_supported: ["read", "write"],
|
||||
});
|
||||
mockRegisterClient.mockResolvedValue(mockOAuthClientInfo);
|
||||
mockDiscoverOAuthProtectedResourceMetadata.mockRejectedValue(
|
||||
new Error("No protected resource metadata found"),
|
||||
|
|
@ -427,7 +488,24 @@ describe("AuthDebugger", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("should not include scope in authorization URL when scopes_supported is not present", async () => {
|
||||
it("should include scope in authorization URL when scopes_supported is not present", async () => {
|
||||
const updateAuthState =
|
||||
await setupAuthorizationUrlTest(mockOAuthMetadata);
|
||||
|
||||
// Wait for the updateAuthState to be called
|
||||
await waitFor(() => {
|
||||
expect(updateAuthState).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
authorizationUrl: expect.stringContaining("scope="),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should omit scope from authorization URL when discoverScopes returns undefined", async () => {
|
||||
// Mock discoverScopes to return undefined (no scopes available)
|
||||
mockDiscoverScopes.mockResolvedValueOnce(undefined);
|
||||
|
||||
const updateAuthState =
|
||||
await setupAuthorizationUrlTest(mockOAuthMetadata);
|
||||
|
||||
|
|
@ -442,6 +520,103 @@ describe("AuthDebugger", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("Client Registration behavior", () => {
|
||||
it("uses preregistered (static) client information without calling DCR", async () => {
|
||||
const preregClientInfo = {
|
||||
client_id: "static_client_id",
|
||||
client_secret: "static_client_secret",
|
||||
redirect_uris: ["http://localhost:3000/oauth/callback/debug"],
|
||||
};
|
||||
|
||||
// Return preregistered client info for the server-specific key
|
||||
sessionStorageMock.getItem.mockImplementation((key) => {
|
||||
if (
|
||||
key ===
|
||||
`[${defaultProps.serverUrl}] ${SESSION_KEYS.PREREGISTERED_CLIENT_INFORMATION}`
|
||||
) {
|
||||
return JSON.stringify(preregClientInfo);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
const updateAuthState = jest.fn();
|
||||
|
||||
await act(async () => {
|
||||
renderAuthDebugger({
|
||||
updateAuthState,
|
||||
authState: {
|
||||
...defaultAuthState,
|
||||
isInitiatingAuth: false,
|
||||
oauthStep: "client_registration",
|
||||
oauthMetadata: mockOAuthMetadata as unknown as OAuthMetadata,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Proceed from client_registration → authorization_redirect
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText("Continue"));
|
||||
});
|
||||
|
||||
// Should NOT attempt dynamic client registration
|
||||
expect(mockRegisterClient).not.toHaveBeenCalled();
|
||||
|
||||
// Should advance with the preregistered client info
|
||||
expect(updateAuthState).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
oauthClientInfo: expect.objectContaining({
|
||||
client_id: "static_client_id",
|
||||
}),
|
||||
oauthStep: "authorization_redirect",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to DCR when no static client information is available", async () => {
|
||||
// No preregistered or dynamic client info present in session storage
|
||||
sessionStorageMock.getItem.mockImplementation(() => null);
|
||||
|
||||
// DCR returns a new client
|
||||
mockRegisterClient.mockResolvedValueOnce(mockOAuthClientInfo);
|
||||
|
||||
const updateAuthState = jest.fn();
|
||||
|
||||
await act(async () => {
|
||||
renderAuthDebugger({
|
||||
updateAuthState,
|
||||
authState: {
|
||||
...defaultAuthState,
|
||||
isInitiatingAuth: false,
|
||||
oauthStep: "client_registration",
|
||||
oauthMetadata: mockOAuthMetadata as unknown as OAuthMetadata,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText("Continue"));
|
||||
});
|
||||
|
||||
expect(mockRegisterClient).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Should save and advance with the DCR client info
|
||||
expect(updateAuthState).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
oauthClientInfo: expect.objectContaining({
|
||||
client_id: "test_client_id",
|
||||
}),
|
||||
oauthStep: "authorization_redirect",
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify the dynamically registered client info was persisted
|
||||
expect(sessionStorage.setItem).toHaveBeenCalledWith(
|
||||
`[${defaultProps.serverUrl}] ${SESSION_KEYS.CLIENT_INFORMATION}`,
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("OAuth State Persistence", () => {
|
||||
it("should store auth state to sessionStorage before redirect in Quick OAuth Flow", async () => {
|
||||
const updateAuthState = jest.fn();
|
||||
|
|
|
|||
155
client/src/lib/__tests__/auth.test.ts
Normal file
155
client/src/lib/__tests__/auth.test.ts
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
import { discoverScopes } from "../auth";
|
||||
import { discoverAuthorizationServerMetadata } from "@modelcontextprotocol/sdk/client/auth.js";
|
||||
|
||||
jest.mock("@modelcontextprotocol/sdk/client/auth.js", () => ({
|
||||
discoverAuthorizationServerMetadata: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockDiscoverAuth =
|
||||
discoverAuthorizationServerMetadata as jest.MockedFunction<
|
||||
typeof discoverAuthorizationServerMetadata
|
||||
>;
|
||||
|
||||
const baseMetadata = {
|
||||
issuer: "https://test.com",
|
||||
authorization_endpoint: "https://test.com/authorize",
|
||||
token_endpoint: "https://test.com/token",
|
||||
response_types_supported: ["code"],
|
||||
grant_types_supported: ["authorization_code"],
|
||||
scopes_supported: ["read", "write"],
|
||||
};
|
||||
|
||||
describe("discoverScopes", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: "returns joined scopes from OAuth metadata",
|
||||
mockResolves: baseMetadata,
|
||||
serverUrl: "https://example.com",
|
||||
expected: "read write",
|
||||
expectedCallUrl: "https://example.com/",
|
||||
},
|
||||
{
|
||||
name: "prefers resource metadata over OAuth metadata",
|
||||
mockResolves: baseMetadata,
|
||||
serverUrl: "https://example.com",
|
||||
resourceMetadata: {
|
||||
resource: "https://example.com",
|
||||
scopes_supported: ["admin", "full"],
|
||||
},
|
||||
expected: "admin full",
|
||||
},
|
||||
{
|
||||
name: "falls back to OAuth when resource has empty scopes",
|
||||
mockResolves: baseMetadata,
|
||||
serverUrl: "https://example.com",
|
||||
resourceMetadata: {
|
||||
resource: "https://example.com",
|
||||
scopes_supported: [],
|
||||
},
|
||||
expected: "read write",
|
||||
},
|
||||
{
|
||||
name: "normalizes URL with port and path",
|
||||
mockResolves: baseMetadata,
|
||||
serverUrl: "https://example.com:8080/some/path",
|
||||
expected: "read write",
|
||||
expectedCallUrl: "https://example.com:8080/",
|
||||
},
|
||||
{
|
||||
name: "normalizes URL with trailing slash",
|
||||
mockResolves: baseMetadata,
|
||||
serverUrl: "https://example.com/",
|
||||
expected: "read write",
|
||||
expectedCallUrl: "https://example.com/",
|
||||
},
|
||||
{
|
||||
name: "handles single scope",
|
||||
mockResolves: { ...baseMetadata, scopes_supported: ["admin"] },
|
||||
serverUrl: "https://example.com",
|
||||
expected: "admin",
|
||||
},
|
||||
{
|
||||
name: "prefers resource metadata even with fewer scopes",
|
||||
mockResolves: {
|
||||
...baseMetadata,
|
||||
scopes_supported: ["read", "write", "admin", "full"],
|
||||
},
|
||||
serverUrl: "https://example.com",
|
||||
resourceMetadata: {
|
||||
resource: "https://example.com",
|
||||
scopes_supported: ["read"],
|
||||
},
|
||||
expected: "read",
|
||||
},
|
||||
];
|
||||
|
||||
const undefinedCases = [
|
||||
{
|
||||
name: "returns undefined when OAuth discovery fails",
|
||||
mockRejects: new Error("Discovery failed"),
|
||||
serverUrl: "https://example.com",
|
||||
},
|
||||
{
|
||||
name: "returns undefined when OAuth has no scopes",
|
||||
mockResolves: { ...baseMetadata, scopes_supported: [] },
|
||||
serverUrl: "https://example.com",
|
||||
},
|
||||
{
|
||||
name: "returns undefined when scopes_supported missing",
|
||||
mockResolves: (() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { scopes_supported, ...rest } = baseMetadata;
|
||||
return rest;
|
||||
})(),
|
||||
serverUrl: "https://example.com",
|
||||
},
|
||||
{
|
||||
name: "returns undefined with resource metadata but OAuth fails",
|
||||
mockRejects: new Error("No OAuth metadata"),
|
||||
serverUrl: "https://example.com",
|
||||
resourceMetadata: {
|
||||
resource: "https://example.com",
|
||||
scopes_supported: ["read", "write"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
test.each(testCases)(
|
||||
"$name",
|
||||
async ({
|
||||
mockResolves,
|
||||
serverUrl,
|
||||
resourceMetadata,
|
||||
expected,
|
||||
expectedCallUrl,
|
||||
}) => {
|
||||
mockDiscoverAuth.mockResolvedValue(mockResolves);
|
||||
|
||||
const result = await discoverScopes(serverUrl, resourceMetadata);
|
||||
|
||||
expect(result).toBe(expected);
|
||||
if (expectedCallUrl) {
|
||||
expect(mockDiscoverAuth).toHaveBeenCalledWith(new URL(expectedCallUrl));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test.each(undefinedCases)(
|
||||
"$name",
|
||||
async ({ mockResolves, mockRejects, serverUrl, resourceMetadata }) => {
|
||||
if (mockRejects) {
|
||||
mockDiscoverAuth.mockRejectedValue(mockRejects);
|
||||
} else {
|
||||
mockDiscoverAuth.mockResolvedValue(mockResolves);
|
||||
}
|
||||
|
||||
const result = await discoverScopes(serverUrl, resourceMetadata);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
@ -6,10 +6,45 @@ import {
|
|||
OAuthTokensSchema,
|
||||
OAuthClientMetadata,
|
||||
OAuthMetadata,
|
||||
OAuthProtectedResourceMetadata,
|
||||
} from "@modelcontextprotocol/sdk/shared/auth.js";
|
||||
import { discoverAuthorizationServerMetadata } from "@modelcontextprotocol/sdk/client/auth.js";
|
||||
import { SESSION_KEYS, getServerSpecificKey } from "./constants";
|
||||
import { generateOAuthState } from "@/utils/oauthUtils";
|
||||
|
||||
/**
|
||||
* Discovers OAuth scopes from server metadata, with preference for resource metadata scopes
|
||||
* @param serverUrl - The MCP server URL
|
||||
* @param resourceMetadata - Optional resource metadata containing preferred scopes
|
||||
* @returns Promise resolving to space-separated scope string or undefined
|
||||
*/
|
||||
export const discoverScopes = async (
|
||||
serverUrl: string,
|
||||
resourceMetadata?: OAuthProtectedResourceMetadata,
|
||||
): Promise<string | undefined> => {
|
||||
try {
|
||||
const metadata = await discoverAuthorizationServerMetadata(
|
||||
new URL("/", serverUrl),
|
||||
);
|
||||
|
||||
// Prefer resource metadata scopes, but fall back to OAuth metadata if empty
|
||||
const resourceScopes = resourceMetadata?.scopes_supported;
|
||||
const oauthScopes = metadata?.scopes_supported;
|
||||
|
||||
const scopesSupported =
|
||||
resourceScopes && resourceScopes.length > 0
|
||||
? resourceScopes
|
||||
: oauthScopes;
|
||||
|
||||
return scopesSupported && scopesSupported.length > 0
|
||||
? scopesSupported.join(" ")
|
||||
: undefined;
|
||||
} catch (error) {
|
||||
console.debug("OAuth scope discovery failed:", error);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const getClientInformationFromSessionStorage = async ({
|
||||
serverUrl,
|
||||
isPreregistered,
|
||||
|
|
@ -67,10 +102,15 @@ export const clearClientInformationFromSessionStorage = ({
|
|||
};
|
||||
|
||||
export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
||||
constructor(protected serverUrl: string) {
|
||||
constructor(
|
||||
protected serverUrl: string,
|
||||
scope?: string,
|
||||
) {
|
||||
this.scope = scope;
|
||||
// Save the server URL to session storage
|
||||
sessionStorage.setItem(SESSION_KEYS.SERVER_URL, serverUrl);
|
||||
}
|
||||
scope: string | undefined;
|
||||
|
||||
get redirectUrl() {
|
||||
return window.location.origin + "/oauth/callback";
|
||||
|
|
@ -84,6 +124,7 @@ export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
|||
response_types: ["code"],
|
||||
client_name: "MCP Inspector",
|
||||
client_uri: "https://github.com/modelcontextprotocol/inspector",
|
||||
scope: this.scope ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -110,10 +151,17 @@ export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
|||
}
|
||||
|
||||
saveClientInformation(clientInformation: OAuthClientInformation) {
|
||||
// Remove client_secret before storing (not needed after initial OAuth flow)
|
||||
const safeInfo = Object.fromEntries(
|
||||
Object.entries(clientInformation).filter(
|
||||
([key]) => key !== "client_secret",
|
||||
),
|
||||
) as OAuthClientInformation;
|
||||
|
||||
// Save the dynamically registered client information to session storage
|
||||
saveClientInformationToSessionStorage({
|
||||
serverUrl: this.serverUrl,
|
||||
clientInformation,
|
||||
clientInformation: safeInfo,
|
||||
isPreregistered: false,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,16 @@ import { useConnection } from "../useConnection";
|
|||
import { z } from "zod";
|
||||
import { ClientRequest } from "@modelcontextprotocol/sdk/types.js";
|
||||
import { DEFAULT_INSPECTOR_CONFIG } from "../../constants";
|
||||
import { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
|
||||
import {
|
||||
SSEClientTransportOptions,
|
||||
SseError,
|
||||
} from "@modelcontextprotocol/sdk/client/sse.js";
|
||||
import {
|
||||
ElicitResult,
|
||||
ElicitRequest,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
|
||||
import { discoverScopes } from "../../auth";
|
||||
|
||||
// Mock fetch
|
||||
global.fetch = jest.fn().mockResolvedValue({
|
||||
|
|
@ -53,14 +58,27 @@ jest.mock("@modelcontextprotocol/sdk/client/index.js", () => ({
|
|||
Client: jest.fn().mockImplementation(() => mockClient),
|
||||
}));
|
||||
|
||||
jest.mock("@modelcontextprotocol/sdk/client/sse.js", () => ({
|
||||
SSEClientTransport: jest.fn((url, options) => {
|
||||
mockSSETransport.url = url;
|
||||
mockSSETransport.options = options;
|
||||
return mockSSETransport;
|
||||
}),
|
||||
SseError: jest.fn(),
|
||||
}));
|
||||
jest.mock("@modelcontextprotocol/sdk/client/sse.js", () => {
|
||||
// Minimal mock class that supports instanceof checks
|
||||
class SseError extends Error {
|
||||
code: number;
|
||||
event: ErrorEvent;
|
||||
constructor(code: number, message: string, event: ErrorEvent) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.event = event;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
SSEClientTransport: jest.fn((url, options) => {
|
||||
mockSSETransport.url = url;
|
||||
mockSSETransport.options = options;
|
||||
return mockSSETransport;
|
||||
}),
|
||||
SseError,
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock("@modelcontextprotocol/sdk/client/streamableHttp.js", () => ({
|
||||
StreamableHTTPClientTransport: jest.fn((url, options) => {
|
||||
|
|
@ -85,11 +103,18 @@ jest.mock("@/lib/hooks/useToast", () => ({
|
|||
jest.mock("../../auth", () => ({
|
||||
InspectorOAuthClientProvider: jest.fn().mockImplementation(() => ({
|
||||
tokens: jest.fn().mockResolvedValue({ access_token: "mock-token" }),
|
||||
redirectUrl: "http://localhost:3000/oauth/callback",
|
||||
})),
|
||||
clearClientInformationFromSessionStorage: jest.fn(),
|
||||
saveClientInformationToSessionStorage: jest.fn(),
|
||||
discoverScopes: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockAuth = auth as jest.MockedFunction<typeof auth>;
|
||||
const mockDiscoverScopes = discoverScopes as jest.MockedFunction<
|
||||
typeof discoverScopes
|
||||
>;
|
||||
|
||||
describe("useConnection", () => {
|
||||
const defaultProps = {
|
||||
transportType: "sse" as const,
|
||||
|
|
@ -714,4 +739,142 @@ describe("useConnection", () => {
|
|||
).toHaveProperty("X-MCP-Proxy-Auth", "Bearer test-proxy-token");
|
||||
});
|
||||
});
|
||||
|
||||
describe("OAuth Error Handling with Scope Discovery", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockAuth.mockResolvedValue("AUTHORIZED");
|
||||
mockDiscoverScopes.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
const setup401Error = () => {
|
||||
const mockErrorEvent = new ErrorEvent("error", {
|
||||
message: "Mock error event",
|
||||
});
|
||||
mockClient.connect.mockRejectedValueOnce(
|
||||
new SseError(401, "Unauthorized", mockErrorEvent),
|
||||
);
|
||||
};
|
||||
|
||||
const attemptConnection = async (props = defaultProps) => {
|
||||
const { result } = renderHook(() => useConnection(props));
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.connect();
|
||||
} catch {
|
||||
// Expected error from auth handling
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const testCases = [
|
||||
[
|
||||
"discovers and includes scopes in auth call",
|
||||
{
|
||||
discoveredScope: "read write admin",
|
||||
oauthScope: undefined,
|
||||
expectScopeCall: true,
|
||||
expectedAuthScope: "read write admin",
|
||||
authResult: "AUTHORIZED",
|
||||
},
|
||||
],
|
||||
[
|
||||
"handles scope discovery failure gracefully",
|
||||
{
|
||||
discoveredScope: undefined,
|
||||
oauthScope: undefined,
|
||||
expectScopeCall: true,
|
||||
expectedAuthScope: undefined,
|
||||
authResult: "AUTHORIZED",
|
||||
},
|
||||
],
|
||||
[
|
||||
"uses manual oauthScope override instead of discovered scopes",
|
||||
{
|
||||
discoveredScope: "discovered:scope",
|
||||
oauthScope: "manual:scope",
|
||||
expectScopeCall: false,
|
||||
expectedAuthScope: "manual:scope",
|
||||
authResult: "AUTHORIZED",
|
||||
},
|
||||
],
|
||||
[
|
||||
"triggers scope discovery when oauthScope is whitespace",
|
||||
{
|
||||
discoveredScope: "discovered:scope",
|
||||
oauthScope: " ",
|
||||
expectScopeCall: true,
|
||||
expectedAuthScope: "discovered:scope",
|
||||
authResult: "AUTHORIZED",
|
||||
},
|
||||
],
|
||||
[
|
||||
"handles auth failure after scope discovery",
|
||||
{
|
||||
discoveredScope: "read write",
|
||||
oauthScope: undefined,
|
||||
expectScopeCall: true,
|
||||
expectedAuthScope: "read write",
|
||||
authResult: "UNAUTHORIZED",
|
||||
},
|
||||
],
|
||||
] as const;
|
||||
|
||||
test.each(testCases)(
|
||||
"should %s",
|
||||
async (
|
||||
_,
|
||||
{
|
||||
discoveredScope,
|
||||
oauthScope,
|
||||
expectScopeCall,
|
||||
expectedAuthScope,
|
||||
authResult = "AUTHORIZED",
|
||||
},
|
||||
) => {
|
||||
mockDiscoverScopes.mockResolvedValue(discoveredScope);
|
||||
mockAuth.mockResolvedValue(authResult as never);
|
||||
setup401Error();
|
||||
|
||||
const props =
|
||||
oauthScope !== undefined
|
||||
? { ...defaultProps, oauthScope }
|
||||
: defaultProps;
|
||||
await attemptConnection(props);
|
||||
|
||||
if (expectScopeCall) {
|
||||
expect(mockDiscoverScopes).toHaveBeenCalledWith(
|
||||
defaultProps.sseUrl,
|
||||
undefined,
|
||||
);
|
||||
} else {
|
||||
expect(mockDiscoverScopes).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
expect(mockAuth).toHaveBeenCalledWith(expect.any(Object), {
|
||||
serverUrl: defaultProps.sseUrl,
|
||||
scope: expectedAuthScope,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("should handle slow scope discovery gracefully", async () => {
|
||||
mockDiscoverScopes.mockImplementation(
|
||||
() =>
|
||||
new Promise((resolve) => setTimeout(() => resolve(undefined), 100)),
|
||||
);
|
||||
|
||||
setup401Error();
|
||||
await attemptConnection();
|
||||
|
||||
expect(mockDiscoverScopes).toHaveBeenCalledWith(
|
||||
defaultProps.sseUrl,
|
||||
undefined,
|
||||
);
|
||||
expect(mockAuth).toHaveBeenCalledWith(expect.any(Object), {
|
||||
serverUrl: defaultProps.sseUrl,
|
||||
scope: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -37,11 +37,15 @@ import { useToast } from "@/lib/hooks/useToast";
|
|||
import { z } from "zod";
|
||||
import { ConnectionStatus } from "../constants";
|
||||
import { Notification, StdErrNotificationSchema } from "../notificationTypes";
|
||||
import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
|
||||
import {
|
||||
auth,
|
||||
discoverOAuthProtectedResourceMetadata,
|
||||
} from "@modelcontextprotocol/sdk/client/auth.js";
|
||||
import {
|
||||
clearClientInformationFromSessionStorage,
|
||||
InspectorOAuthClientProvider,
|
||||
saveClientInformationToSessionStorage,
|
||||
discoverScopes,
|
||||
} from "../auth";
|
||||
import packageJson from "../../../package.json";
|
||||
import {
|
||||
|
|
@ -315,11 +319,27 @@ export function useConnection({
|
|||
|
||||
const handleAuthError = async (error: unknown) => {
|
||||
if (is401Error(error)) {
|
||||
const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl);
|
||||
let scope = oauthScope?.trim();
|
||||
if (!scope) {
|
||||
// Only discover resource metadata when we need to discover scopes
|
||||
let resourceMetadata;
|
||||
try {
|
||||
resourceMetadata = await discoverOAuthProtectedResourceMetadata(
|
||||
new URL("/", sseUrl),
|
||||
);
|
||||
} catch {
|
||||
// Resource metadata is optional, continue without it
|
||||
}
|
||||
scope = await discoverScopes(sseUrl, resourceMetadata);
|
||||
}
|
||||
const serverAuthProvider = new InspectorOAuthClientProvider(
|
||||
sseUrl,
|
||||
scope,
|
||||
);
|
||||
|
||||
const result = await auth(serverAuthProvider, {
|
||||
serverUrl: sseUrl,
|
||||
scope: oauthScope,
|
||||
scope,
|
||||
});
|
||||
return result === "AUTHORIZED";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { OAuthStep, AuthDebuggerState } from "./auth-types";
|
||||
import { DebugInspectorOAuthClientProvider } from "./auth";
|
||||
import { DebugInspectorOAuthClientProvider, discoverScopes } from "./auth";
|
||||
import {
|
||||
discoverAuthorizationServerMetadata,
|
||||
registerClient,
|
||||
|
|
@ -89,12 +89,16 @@ export const oauthTransitions: Record<OAuthStep, StateTransition> = {
|
|||
clientMetadata.scope = scopesSupported.join(" ");
|
||||
}
|
||||
|
||||
const fullInformation = await registerClient(context.serverUrl, {
|
||||
metadata,
|
||||
clientMetadata,
|
||||
});
|
||||
// Try Static client first, with DCR as fallback
|
||||
let fullInformation = await context.provider.clientInformation();
|
||||
if (!fullInformation) {
|
||||
fullInformation = await registerClient(context.serverUrl, {
|
||||
metadata,
|
||||
clientMetadata,
|
||||
});
|
||||
context.provider.saveClientInformation(fullInformation);
|
||||
}
|
||||
|
||||
context.provider.saveClientInformation(fullInformation);
|
||||
context.updateState({
|
||||
oauthClientInfo: fullInformation,
|
||||
oauthStep: "authorization_redirect",
|
||||
|
|
@ -109,10 +113,10 @@ export const oauthTransitions: Record<OAuthStep, StateTransition> = {
|
|||
const metadata = context.state.oauthMetadata!;
|
||||
const clientInformation = context.state.oauthClientInfo!;
|
||||
|
||||
let scope: string | undefined = undefined;
|
||||
if (metadata.scopes_supported) {
|
||||
scope = metadata.scopes_supported.join(" ");
|
||||
}
|
||||
const scope = await discoverScopes(
|
||||
context.serverUrl,
|
||||
context.state.resourceMetadata ?? undefined,
|
||||
);
|
||||
|
||||
const { authorizationUrl, codeVerifier } = await startAuthorization(
|
||||
context.serverUrl,
|
||||
|
|
@ -173,7 +177,11 @@ export const oauthTransitions: Record<OAuthStep, StateTransition> = {
|
|||
authorizationCode: context.state.authorizationCode,
|
||||
codeVerifier,
|
||||
redirectUri: context.provider.redirectUrl,
|
||||
resource: context.state.resource ?? undefined,
|
||||
resource: context.state.resource
|
||||
? context.state.resource instanceof URL
|
||||
? context.state.resource
|
||||
: new URL(context.state.resource)
|
||||
: undefined,
|
||||
});
|
||||
|
||||
context.provider.saveTokens(tokens);
|
||||
|
|
|
|||
642
package-lock.json
generated
642
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@modelcontextprotocol/inspector",
|
||||
"version": "0.16.3",
|
||||
"version": "0.16.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@modelcontextprotocol/inspector",
|
||||
"version": "0.16.3",
|
||||
"version": "0.16.5",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"client",
|
||||
|
|
@ -14,10 +14,10 @@
|
|||
"cli"
|
||||
],
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/inspector-cli": "^0.16.3",
|
||||
"@modelcontextprotocol/inspector-client": "^0.16.3",
|
||||
"@modelcontextprotocol/inspector-server": "^0.16.3",
|
||||
"@modelcontextprotocol/sdk": "^1.17.2",
|
||||
"@modelcontextprotocol/inspector-cli": "^0.16.5",
|
||||
"@modelcontextprotocol/inspector-client": "^0.16.5",
|
||||
"@modelcontextprotocol/inspector-server": "^0.16.5",
|
||||
"@modelcontextprotocol/sdk": "^1.17.3",
|
||||
"concurrently": "^9.2.0",
|
||||
"open": "^10.2.0",
|
||||
"shell-quote": "^1.8.3",
|
||||
|
|
@ -33,7 +33,9 @@
|
|||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.17.0",
|
||||
"@types/shell-quote": "^1.7.5",
|
||||
"husky": "^9.1.7",
|
||||
"jest-fixed-jsdom": "^0.0.9",
|
||||
"lint-staged": "^16.1.5",
|
||||
"prettier": "^3.6.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"typescript": "^5.4.2"
|
||||
|
|
@ -44,10 +46,10 @@
|
|||
},
|
||||
"cli": {
|
||||
"name": "@modelcontextprotocol/inspector-cli",
|
||||
"version": "0.16.3",
|
||||
"version": "0.16.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.17.0",
|
||||
"@modelcontextprotocol/sdk": "^1.17.3",
|
||||
"commander": "^13.1.0",
|
||||
"spawn-rx": "^5.1.2"
|
||||
},
|
||||
|
|
@ -67,10 +69,10 @@
|
|||
},
|
||||
"client": {
|
||||
"name": "@modelcontextprotocol/inspector-client",
|
||||
"version": "0.16.3",
|
||||
"version": "0.16.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.17.0",
|
||||
"@modelcontextprotocol/sdk": "^1.17.3",
|
||||
"@radix-ui/react-checkbox": "^1.1.4",
|
||||
"@radix-ui/react-dialog": "^1.1.3",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
|
|
@ -2009,9 +2011,9 @@
|
|||
"link": true
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.2.tgz",
|
||||
"integrity": "sha512-EFLRNXR/ixpXQWu6/3Cu30ndDFIFNaqUXcTqsGebujeMan9FzhAaFFswLRiFj61rgygDRr8WO1N+UijjgRxX9g==",
|
||||
"version": "1.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz",
|
||||
"integrity": "sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.6",
|
||||
|
|
@ -4671,6 +4673,93 @@
|
|||
"url": "https://polar.sh/cva"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-cursor": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
|
||||
"integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"restore-cursor": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-truncate": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
|
||||
"integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"slice-ansi": "^5.0.0",
|
||||
"string-width": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-truncate/node_modules/ansi-regex": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
|
||||
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-truncate/node_modules/emoji-regex": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cli-truncate/node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-truncate/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
|
|
@ -4746,6 +4835,13 @@
|
|||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
|
|
@ -4986,9 +5082,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
|
|
@ -5272,6 +5368,19 @@
|
|||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/environment": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
|
||||
"integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
|
|
@ -5620,6 +5729,13 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eventsource": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.6.tgz",
|
||||
|
|
@ -6083,6 +6199,19 @@
|
|||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-east-asian-width": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
|
||||
"integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
|
|
@ -6363,6 +6492,22 @@
|
|||
"node": ">=10.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/husky": {
|
||||
"version": "9.1.7",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
|
||||
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"husky": "bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/typicode"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
|
|
@ -7900,6 +8045,160 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lint-staged": {
|
||||
"version": "16.1.5",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.1.5.tgz",
|
||||
"integrity": "sha512-uAeQQwByI6dfV7wpt/gVqg+jAPaSp8WwOA8kKC/dv1qw14oGpnpAisY65ibGHUGDUv0rYaZ8CAJZ/1U8hUvC2A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^5.5.0",
|
||||
"commander": "^14.0.0",
|
||||
"debug": "^4.4.1",
|
||||
"lilconfig": "^3.1.3",
|
||||
"listr2": "^9.0.1",
|
||||
"micromatch": "^4.0.8",
|
||||
"nano-spawn": "^1.0.2",
|
||||
"pidtree": "^0.6.0",
|
||||
"string-argv": "^0.3.2",
|
||||
"yaml": "^2.8.1"
|
||||
},
|
||||
"bin": {
|
||||
"lint-staged": "bin/lint-staged.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/lint-staged"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/chalk": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz",
|
||||
"integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/commander": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz",
|
||||
"integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.1.tgz",
|
||||
"integrity": "sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cli-truncate": "^4.0.0",
|
||||
"colorette": "^2.0.20",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"log-update": "^6.1.0",
|
||||
"rfdc": "^1.4.1",
|
||||
"wrap-ansi": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2/node_modules/ansi-regex": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
|
||||
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2/node_modules/emoji-regex": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/listr2/node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2/node_modules/wrap-ansi": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
|
||||
"integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"string-width": "^7.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
|
|
@ -7936,6 +8235,160 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/log-update": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
|
||||
"integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-escapes": "^7.0.0",
|
||||
"cli-cursor": "^5.0.0",
|
||||
"slice-ansi": "^7.1.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"wrap-ansi": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/ansi-escapes": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
|
||||
"integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"environment": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/ansi-regex": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
|
||||
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/emoji-regex": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/log-update/node_modules/is-fullwidth-code-point": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
|
||||
"integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"get-east-asian-width": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/slice-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"is-fullwidth-code-point": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/wrap-ansi": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
|
||||
"integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"string-width": "^7.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
|
|
@ -8115,6 +8568,19 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-function": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
|
||||
"integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/min-indent": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
|
||||
|
|
@ -8165,6 +8631,19 @@
|
|||
"thenify-all": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nano-spawn": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.2.tgz",
|
||||
"integrity": "sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/nano-spawn?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
|
|
@ -8550,6 +9029,19 @@
|
|||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pidtree": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
|
||||
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"pidtree": "bin/pidtree.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
|
|
@ -9292,6 +9784,52 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/restore-cursor": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
|
||||
"integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"onetime": "^7.0.0",
|
||||
"signal-exit": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/restore-cursor/node_modules/onetime": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
|
||||
"integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mimic-function": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/restore-cursor/node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||
|
|
@ -9303,6 +9841,13 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rfdc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz",
|
||||
|
|
@ -9780,6 +10325,49 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
|
||||
"integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.0.0",
|
||||
"is-fullwidth-code-point": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
|
||||
"integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
|
|
@ -9860,6 +10448,16 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-argv": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
|
||||
"integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6.19"
|
||||
}
|
||||
},
|
||||
"node_modules/string-length": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
|
||||
|
|
@ -11067,16 +11665,16 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
|
||||
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
|
||||
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
"node": ">= 14.6"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
|
|
@ -11148,10 +11746,10 @@
|
|||
},
|
||||
"server": {
|
||||
"name": "@modelcontextprotocol/inspector-server",
|
||||
"version": "0.16.3",
|
||||
"version": "0.16.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.17.0",
|
||||
"@modelcontextprotocol/sdk": "^1.17.3",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^5.1.0",
|
||||
"ws": "^8.18.0",
|
||||
|
|
|
|||
19
package.json
19
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@modelcontextprotocol/inspector",
|
||||
"version": "0.16.3",
|
||||
"version": "0.16.5",
|
||||
"description": "Model Context Protocol inspector",
|
||||
"license": "MIT",
|
||||
"author": "Anthropic, PBC (https://anthropic.com)",
|
||||
|
|
@ -41,16 +41,16 @@
|
|||
"prettier-fix": "prettier --write .",
|
||||
"prettier-check": "prettier --check .",
|
||||
"lint": "prettier --check . && cd client && npm run lint",
|
||||
"prepare": "npm run build",
|
||||
"prepare": "husky && npm run build",
|
||||
"publish-all": "npm publish --workspaces --access public && npm publish --access public",
|
||||
"update-version": "node scripts/update-version.js",
|
||||
"check-version": "node scripts/check-version-consistency.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/inspector-cli": "^0.16.3",
|
||||
"@modelcontextprotocol/inspector-client": "^0.16.3",
|
||||
"@modelcontextprotocol/inspector-server": "^0.16.3",
|
||||
"@modelcontextprotocol/sdk": "^1.17.2",
|
||||
"@modelcontextprotocol/inspector-cli": "^0.16.5",
|
||||
"@modelcontextprotocol/inspector-client": "^0.16.5",
|
||||
"@modelcontextprotocol/inspector-server": "^0.16.5",
|
||||
"@modelcontextprotocol/sdk": "^1.17.3",
|
||||
"concurrently": "^9.2.0",
|
||||
"open": "^10.2.0",
|
||||
"shell-quote": "^1.8.3",
|
||||
|
|
@ -63,12 +63,19 @@
|
|||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.17.0",
|
||||
"@types/shell-quote": "^1.7.5",
|
||||
"husky": "^9.1.7",
|
||||
"jest-fixed-jsdom": "^0.0.9",
|
||||
"lint-staged": "^16.1.5",
|
||||
"prettier": "^3.6.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.7.5"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.{js,ts,jsx,tsx,json,md}": [
|
||||
"prettier --write"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@modelcontextprotocol/inspector-server",
|
||||
"version": "0.16.3",
|
||||
"version": "0.16.5",
|
||||
"description": "Server-side application for the Model Context Protocol inspector",
|
||||
"license": "MIT",
|
||||
"author": "Anthropic, PBC (https://anthropic.com)",
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
"typescript": "^5.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.17.0",
|
||||
"@modelcontextprotocol/sdk": "^1.17.3",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^5.1.0",
|
||||
"ws": "^8.18.0",
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ const createTransport = async (req: express.Request): Promise<Transport> => {
|
|||
const command = query.command as string;
|
||||
const origArgs = shellParseArgs(query.args as string) as string[];
|
||||
const queryEnv = query.env ? JSON.parse(query.env as string) : {};
|
||||
const env = { ...process.env, ...defaultEnvironment, ...queryEnv };
|
||||
const env = { ...defaultEnvironment, ...process.env, ...queryEnv };
|
||||
|
||||
const { cmd, args } = findActualExecutable(command, origArgs);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue