diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index 4918cdd3..4e7221c3 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -30,7 +30,6 @@ import { Progress, LoggingLevel, ElicitRequestSchema, - isJSONRPCRequest, } from "@modelcontextprotocol/sdk/types.js"; import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js"; import { useEffect, useState } from "react"; @@ -58,8 +57,7 @@ import { getMCPServerRequestTimeout } from "@/utils/configUtils"; import { InspectorConfig } from "../configurationTypes"; import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import { CustomHeaders } from "../types/customHeaders"; -import { JsonSchemaType } from "@/utils/jsonUtils"; -import { resolveRef } from "@/utils/schemaUtils"; +import { resolveRefsInMessage } from "@/utils/schemaUtils"; interface UseConnectionOptions { transportType: "stdio" | "sse" | "streamable-http"; @@ -687,29 +685,8 @@ export function useConnection({ const protocolOnMessage = transport.onmessage; if (protocolOnMessage) { transport.onmessage = (message) => { - // Resolve $ref references in requests before validation - if (isJSONRPCRequest(message) && message.params?.requestedSchema) { - const requestedSchema = message.params - .requestedSchema as JsonSchemaType; - if (requestedSchema?.properties) { - const resolvedProperties = Object.fromEntries( - Object.entries(requestedSchema.properties).map( - ([key, propSchema]) => [ - key, - resolveRef(propSchema, requestedSchema), - ], - ), - ); - message.params = { - ...message.params, - requestedSchema: { - ...requestedSchema, - properties: resolvedProperties, - }, - }; - } - } - protocolOnMessage(message); + const resolvedMessage = resolveRefsInMessage(message); + protocolOnMessage(resolvedMessage); }; } diff --git a/client/src/utils/schemaUtils.ts b/client/src/utils/schemaUtils.ts index 36587021..e7fe2c8f 100644 --- a/client/src/utils/schemaUtils.ts +++ b/client/src/utils/schemaUtils.ts @@ -1,7 +1,8 @@ import type { JsonValue, JsonSchemaType, JsonObject } from "./jsonUtils"; import Ajv from "ajv"; import type { ValidateFunction } from "ajv"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool, JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; +import { isJSONRPCRequest } from "@modelcontextprotocol/sdk/types.js"; const ajv = new Ajv(); @@ -299,3 +300,40 @@ export function formatFieldLabel(key: string): string { .replace(/_/g, " ") // Replace underscores with spaces .replace(/^\w/, (c) => c.toUpperCase()); // Capitalize first letter } + +/** + * Resolves $ref references in a JSON-RPC message's requestedSchema + * @param message The JSON-RPC message that may contain $ref references + * @returns A new message with resolved $ref references, or the original message if no resolution is needed + */ +export function resolveRefsInMessage(message: JSONRPCMessage): JSONRPCMessage { + if (!isJSONRPCRequest(message) || !message.params?.requestedSchema) { + return message; + } + + const requestedSchema = message.params.requestedSchema as JsonSchemaType; + + if (!requestedSchema?.properties) { + return message; + } + + const resolvedMessage = { + ...message, + params: { + ...message.params, + requestedSchema: { + ...requestedSchema, + properties: Object.fromEntries( + Object.entries(requestedSchema.properties).map( + ([key, propSchema]) => [ + key, + resolveRef(propSchema, requestedSchema), + ], + ), + ), + }, + }, + }; + + return resolvedMessage; +}