diff --git a/src/application/XrefsAddressResolution.ts b/src/application/XrefsAddressResolution.ts index f28cf62506..8348c12f66 100644 --- a/src/application/XrefsAddressResolution.ts +++ b/src/application/XrefsAddressResolution.ts @@ -21,13 +21,21 @@ export const resolveXrefsAddress = async ( return named.value !== null ? ok(named.value) : err( - new AnalysisInputError("xrefs", undefined, [ + new AnalysisInputError( + "xrefs", { - path: ["address"], - reason: "invalid_value", - message: `Unknown analyzed symbol name ${JSON.stringify(selector)}. Use an exact analyzed name or a hexadecimal address.`, + conformsToInputContract: true, + remediationAction: + "For a function, use rea search --kind procedures to find an analyzed address. For exports, inspect-macho (Mach-O) or inspect-native-exports (PE/ELF) may help; exports can omit local symbols and use different address coordinates. Verify the address in the selected provider, then retry xrefs.", }, - ]), + [ + { + path: ["address"], + reason: "invalid_value", + message: `Unknown analyzed symbol name ${JSON.stringify(selector)}. Use an exact analyzed name or a hexadecimal address.`, + }, + ], + ), ); }; @@ -90,12 +98,20 @@ export const resolveXrefsName = async ( const [address] = addresses; if (addresses.size <= 1) return ok(address ?? null); return err( - new AnalysisInputError(operation, undefined, [ + new AnalysisInputError( + operation, { - path: [operation === "xrefs" ? "address" : "name"], - reason: "invalid_value", - message: `Analyzed symbol name ${JSON.stringify(selector)} is ambiguous at ${[...addresses].sort().join(", ")}. Select an exact address.`, + conformsToInputContract: true, + remediationAction: + "Select an exact address from the candidates and call xrefs with that address.", }, - ]), + [ + { + path: [operation === "xrefs" ? "address" : "name"], + reason: "invalid_value", + message: `Analyzed symbol name ${JSON.stringify(selector)} is ambiguous at ${[...addresses].sort().join(", ")}. Select an exact address.`, + }, + ], + ), ); }; diff --git a/tests/boundary/mcp/enhancedTools.03-returns-typed-graph-failures-and.test.ts b/tests/boundary/mcp/enhancedTools.03-returns-typed-graph-failures-and.test.ts index 0b8c10c1b2..5f2af27fab 100644 --- a/tests/boundary/mcp/enhancedTools.03-returns-typed-graph-failures-and.test.ts +++ b/tests/boundary/mcp/enhancedTools.03-returns-typed-graph-failures-and.test.ts @@ -101,6 +101,9 @@ it("exact-name xrefs preserves ambiguity independently of inventory order and ac }); expect(parseMcpToolError(rejected).error).toMatchObject({ code: "invalid_request", + remediation: { + action: expect.stringContaining("Select an exact address"), + }, details: { issues: [ expect.objectContaining({ @@ -110,6 +113,7 @@ it("exact-name xrefs preserves ambiguity independently of inventory order and ac ], }, }); + expect(parseMcpToolError(rejected).error.recovery).toBeUndefined(); } expect(queried).toEqual([]); rows = [ diff --git a/tests/composition/directAnalysisXrefs.test.ts b/tests/composition/directAnalysisXrefs.test.ts index f32601ffe8..8637c24bc3 100644 --- a/tests/composition/directAnalysisXrefs.test.ts +++ b/tests/composition/directAnalysisXrefs.test.ts @@ -211,8 +211,12 @@ describe("direct CLI cross-reference name resolution", () => { ).then(projectCliAnalysisResult); expect(missing).toMatchObject({ code: "invalid_request", + remediation: { + action: expect.stringContaining("use different address coordinates"), + }, details: { operation: "xrefs", issues: [{ path: ["address"] }] }, }); + expect(missing).not.toHaveProperty("recovery"); expect(JSON.stringify(missing)).toContain("missing_symbol"); expect(provider.closed()).toBe(2); }); @@ -228,7 +232,13 @@ describe("direct CLI cross-reference name resolution", () => { address: "ambiguous", }, ).then(projectCliAnalysisResult); - expect(result).toMatchObject({ code: "invalid_request" }); + expect(result).toMatchObject({ + code: "invalid_request", + remediation: { + action: expect.stringContaining("Select an exact address"), + }, + }); + expect(result).not.toHaveProperty("recovery"); expect(JSON.stringify(result)).toContain("0x1000"); expect(JSON.stringify(result)).toContain("0x2000"); expect(provider.closed()).toBe(1);