Skip to content

Commit f9a6bc2

Browse files
Matt Davisclaude
andcommitted
fix(binary-ninja-client): use GET + JSON for /decompile
The binja_mcp plugin's HTTP server only routes /decompile in do_GET (http_server.py line 825). do_POST has no /decompile branch, so the previous client code — which sent a form-urlencoded POST and then treated the response body as the raw decompiled text — failed for every function: the server fell through to a 4xx and the client reported "Function not found" even though the function existed in list_functions / search_functions. Switch to GET ?name=<func> with reqwest's query() (consistent with list_functions and search_functions in this same file), parse the JSON {"decompiled": "..."} envelope, and surface the JSON "error" field as either FunctionNotFound or RequestFailed. Keep a plain-text fallback for older / future plugin variants that might return text. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 72771f4 commit f9a6bc2

1 file changed

Lines changed: 29 additions & 5 deletions

File tree

crates/binary-ninja-client/src/client.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,28 +223,52 @@ impl BinaryNinjaClient {
223223
function_name: &str,
224224
) -> Result<String> {
225225
let url = self.get_server_url(binary_id)?;
226+
// binja_mcp serves /decompile via GET with ?name=<func>; the
227+
// response is JSON of the form {"decompiled": "...", "function": {...}}
228+
// matching the convention used by every other endpoint in the
229+
// plugin. Earlier versions of this client sent POST with a form
230+
// body, which the plugin's do_POST never routed — every call
231+
// failed with what looked like "function not found".
226232
let decompile_url = format!("{}/decompile", url);
227233

228234
debug!("Decompiling function: {}", function_name);
229235

230236
let response = self
231237
.client
232-
.post(&decompile_url)
233-
.form(&[("name", function_name)])
238+
.get(&decompile_url)
239+
.query(&[("name", function_name)])
234240
.send()
235241
.await?;
236242

237243
if !response.status().is_success() {
238244
return Err(BinaryNinjaError::FunctionNotFound(function_name.to_string()).into());
239245
}
240246

241-
let code = response.text().await?;
247+
let text = response.text().await?;
248+
249+
// Preferred path: JSON response with "decompiled" field.
250+
if let Ok(json_response) = serde_json::from_str::<serde_json::Value>(&text) {
251+
if let Some(err) = json_response.get("error").and_then(|v| v.as_str()) {
252+
if err.contains("not found") {
253+
return Err(BinaryNinjaError::FunctionNotFound(
254+
function_name.to_string(),
255+
)
256+
.into());
257+
}
258+
return Err(BinaryNinjaError::RequestFailed(err.to_string()).into());
259+
}
260+
if let Some(code) = json_response.get("decompiled").and_then(|v| v.as_str()) {
261+
return Ok(code.to_string());
262+
}
263+
}
242264

243-
if code.starts_with("Error") || code.starts_with("Function not found") {
265+
// Fallback: treat the body as raw decompiled text (older binja_mcp
266+
// builds, or any future variant that returns plain text).
267+
if text.starts_with("Error") || text.starts_with("Function not found") {
244268
return Err(BinaryNinjaError::FunctionNotFound(function_name.to_string()).into());
245269
}
246270

247-
Ok(code)
271+
Ok(text)
248272
}
249273

250274
/// Get detailed information about a function including decompiled code

0 commit comments

Comments
 (0)