diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs index bbd5403662a..cd782debc20 100644 --- a/actions/setup/js/mcp_cli_bridge.cjs +++ b/actions/setup/js/mcp_cli_bridge.cjs @@ -445,6 +445,7 @@ function parseToolArgs(args, schemaProperties = {}) { /** @type {Record} */ const result = {}; let jsonOutput = false; + const hasSchemaProperties = Object.keys(schemaProperties).length > 0; const { normalizedSchemaKeyMap, ambiguousNormalizedSchemaKeys } = buildNormalizedSchemaKeyMap(schemaProperties); for (let i = 0; i < args.length; i++) { @@ -458,13 +459,13 @@ function parseToolArgs(args, schemaProperties = {}) { jsonOutput = true; } else { const canonicalKey = resolveSchemaPropertyKey(key, schemaProperties, normalizedSchemaKeyMap, ambiguousNormalizedSchemaKeys); - result[canonicalKey] = coerceToolArgValue(canonicalKey, raw.slice(eqIdx + 1), schemaProperties[canonicalKey], result[canonicalKey]); + result[canonicalKey] = coerceToolArgValue(canonicalKey, raw.slice(eqIdx + 1), schemaProperties[canonicalKey], result[canonicalKey], !hasSchemaProperties); } } else if (raw === "json") { jsonOutput = true; } else if (i + 1 < args.length && !args[i + 1].startsWith("--")) { const canonicalKey = resolveSchemaPropertyKey(raw, schemaProperties, normalizedSchemaKeyMap, ambiguousNormalizedSchemaKeys); - result[canonicalKey] = coerceToolArgValue(canonicalKey, args[i + 1], schemaProperties[canonicalKey], result[canonicalKey]); + result[canonicalKey] = coerceToolArgValue(canonicalKey, args[i + 1], schemaProperties[canonicalKey], result[canonicalKey], !hasSchemaProperties); i++; } else { const canonicalKey = resolveSchemaPropertyKey(raw, schemaProperties, normalizedSchemaKeyMap, ambiguousNormalizedSchemaKeys); @@ -549,9 +550,10 @@ function resolveSchemaPropertyKey(key, schemaProperties, normalizedSchemaKeyMap, * @param {string} rawValue - Raw CLI value * @param {{type?: string|string[]}|undefined} schemaProperty - JSON schema property * @param {unknown} existingValue - Existing value (for repeated flags) + * @param {boolean} [allowNumericFallback=false] - Allow numeric parsing when schema is unavailable * @returns {unknown} */ -function coerceToolArgValue(key, rawValue, schemaProperty, existingValue) { +function coerceToolArgValue(key, rawValue, schemaProperty, existingValue, allowNumericFallback = false) { /** @type {string[]} */ const types = []; if (schemaProperty && typeof schemaProperty === "object" && "type" in schemaProperty && schemaProperty.type != null) { @@ -620,6 +622,26 @@ function coerceToolArgValue(key, rawValue, schemaProperty, existingValue) { } } + // When schema metadata is unavailable (e.g. empty tools cache), apply + // conservative numeric coercion fallback for CLI ergonomics. + if (allowNumericFallback && types.length === 0) { + const trimmedValue = rawValue.trim(); + + if (/^-?\d+$/.test(trimmedValue)) { + const parsedInt = Number.parseInt(trimmedValue, 10); + if (!Number.isNaN(parsedInt) && Number.isSafeInteger(parsedInt)) { + return parsedInt; + } + } + + if (/^-?(?:(?:\d+\.\d*|\.\d+)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+)$/.test(trimmedValue)) { + const parsedFloat = Number.parseFloat(trimmedValue); + if (!Number.isNaN(parsedFloat) && Number.isFinite(parsedFloat)) { + return parsedFloat; + } + } + } + return rawValue; } diff --git a/actions/setup/js/mcp_cli_bridge.test.cjs b/actions/setup/js/mcp_cli_bridge.test.cjs index c66eb74413f..7668945fdcd 100644 --- a/actions/setup/js/mcp_cli_bridge.test.cjs +++ b/actions/setup/js/mcp_cli_bridge.test.cjs @@ -145,6 +145,33 @@ describe("mcp_cli_bridge.cjs", () => { }); }); + it("falls back to numeric coercion when schema properties are unavailable", () => { + const { args } = parseToolArgs(["--count", "3", "--max_tokens", "3000"], {}); + + expect(args).toEqual({ + count: 3, + max_tokens: 3000, + }); + }); + + it("coerces scientific notation when schema properties are unavailable", () => { + const { args } = parseToolArgs(["--max_tokens", "1e3", "--threshold", "-2E-4"], {}); + + expect(args).toEqual({ + max_tokens: 1000, + threshold: -0.0002, + }); + }); + + it("preserves non-numeric values when schema properties are unavailable", () => { + const { args } = parseToolArgs(["--start_date", "-1d", "--workflow_name", "daily-issues-report"], {}); + + expect(args).toEqual({ + start_date: "-1d", + workflow_name: "daily-issues-report", + }); + }); + it("treats MCP result envelopes with isError=true as errors", () => { formatResponse( {