Skip to content

Commit b5ef0ef

Browse files
committed
fix: added dict as a return type in mcp tools to keep up to date with fastmcp
1 parent ca9f644 commit b5ef0ef

File tree

2 files changed

+48
-38
lines changed

2 files changed

+48
-38
lines changed

jadx_mcp_server.py

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
from typing import Dict, List, Any, Optional, Union, Callable
1919
from fastmcp import FastMCP
20+
from fastmcp.server.middleware.logging import StructuredLoggingMiddleware
21+
2022

2123
# Set up logging configuration
2224
logger = logging.getLogger()
@@ -31,6 +33,9 @@
3133
# Initialize the MCP server
3234
mcp = FastMCP("JADX-AI-MCP Plugin Reverse Engineering Server")
3335

36+
# for development only
37+
mcp.add_middleware(StructuredLoggingMiddleware(include_payloads=True))
38+
3439
# Parse the arguments
3540
parser = argparse.ArgumentParser("MCP Server for Jadx")
3641
parser.add_argument("--http", help="Serve MCP Server over HTTP stream.", action="store_true", default=False)
@@ -200,7 +205,10 @@ async def get_from_jadx(endpoint: str, params: dict = {}) -> Union[str, dict]:
200205
async with httpx.AsyncClient() as client:
201206
resp = await client.get(f"{JADX_HTTP_BASE}/{endpoint}", params=params, timeout=60)
202207
resp.raise_for_status()
203-
return resp.text
208+
response = resp.text
209+
if isinstance(response, str):
210+
return json.loads(response)
211+
return response
204212
except httpx.HTTPStatusError as e:
205213
error_message = f"HTTP error {e.response.status_code}: {e.response.text}"
206214
logger.error(error_message)
@@ -226,16 +234,19 @@ async def fetch_current_class() -> dict:
226234
return await get_from_jadx("current-class")
227235

228236
@mcp.tool()
229-
async def get_selected_text() -> str:
237+
async def get_selected_text() -> dict:
230238
"""Returns the currently selected text in the decompiled code view.
231239
232240
Returns:
233241
String containing currently highlighted/selected text in jadx-gui.
234242
"""
235-
return await get_from_jadx("selected-text")
243+
response = await get_from_jadx("selected-text")
244+
if isinstance(response, str):
245+
return json.loads(response)
246+
return response
236247

237248
@mcp.tool()
238-
async def get_method_by_name(class_name: str, method_name: str) -> str:
249+
async def get_method_by_name(class_name: str, method_name: str) -> dict:
239250
"""Fetch the source code of a method from a specific class.
240251
241252
Args:
@@ -245,7 +256,10 @@ async def get_method_by_name(class_name: str, method_name: str) -> str:
245256
Returns:
246257
Code of requested method as String.
247258
"""
248-
return await get_from_jadx("method-by-name", {"class": class_name, "method": method_name})
259+
response = await get_from_jadx("method-by-name", {"class": class_name, "method": method_name})
260+
if isinstance(response, str):
261+
return json.loads(response)
262+
return response
249263

250264
@mcp.tool()
251265
async def get_all_classes(offset: int = 0, count: int = 0) -> dict:
@@ -266,7 +280,7 @@ async def get_all_classes(offset: int = 0, count: int = 0) -> dict:
266280
)
267281

268282
@mcp.tool()
269-
async def get_class_source(class_name: str) -> str:
283+
async def get_class_source(class_name: str) -> dict:
270284
"""Fetch the Java source of a specific class.
271285
272286
Args:
@@ -275,10 +289,13 @@ async def get_class_source(class_name: str) -> str:
275289
Returns:
276290
Code of requested class as String.
277291
"""
278-
return await get_from_jadx("class-source", {"class": class_name})
292+
response = await get_from_jadx("class-source", {"class": class_name})
293+
if isinstance(response, str):
294+
return json.loads(response)
295+
return response
279296

280297
@mcp.tool()
281-
async def search_method_by_name(method_name: str) -> List[str]:
298+
async def search_method_by_name(method_name: str) -> dict:
282299
"""Search for a method name across all classes.
283300
284301
Args:
@@ -287,14 +304,13 @@ async def search_method_by_name(method_name: str) -> List[str]:
287304
Returns:
288305
A list of all classes containing the method.
289306
"""
290-
291307
response = await get_from_jadx("search-method", {"method": method_name})
292-
all_matches = response.splitlines() if response else []
293-
294-
return all_matches
308+
if isinstance(response, str):
309+
return json.loads(response)
310+
return response
295311

296312
@mcp.tool()
297-
async def get_methods_of_class(class_name: str) -> List[str]:
313+
async def get_methods_of_class(class_name: str) -> dict:
298314
"""List all method names in a class.
299315
300316
Args:
@@ -304,13 +320,13 @@ async def get_methods_of_class(class_name: str) -> List[str]:
304320
A list of all methods in the class.
305321
"""
306322

307-
response = await get_from_jadx("methods-of-class", {"class": class_name})
308-
all_methods = response.splitlines() if response else []
309-
310-
return all_methods
323+
response = await get_from_jadx("methods-of-class", {"method": class_name})
324+
if isinstance(response, str):
325+
return json.loads(response)
326+
return response
311327

312328
@mcp.tool()
313-
async def get_fields_of_class(class_name: str) -> List[str]:
329+
async def get_fields_of_class(class_name: str) -> dict:
314330
"""List all field names in a class.
315331
316332
Args:
@@ -320,13 +336,13 @@ async def get_fields_of_class(class_name: str) -> List[str]:
320336
A list of all fields in the class.
321337
"""
322338

323-
response = await get_from_jadx("fields-of-class", {"class": class_name})
324-
all_fields = response.splitlines() if response else []
325-
326-
return all_fields
339+
response = await get_from_jadx("fields-of-class", {"method": class_name})
340+
if isinstance(response, str):
341+
return json.loads(response)
342+
return response
327343

328344
@mcp.tool()
329-
async def get_smali_of_class(class_name: str) -> str:
345+
async def get_smali_of_class(class_name: str) -> dict:
330346
"""Fetch the smali representation of a class.
331347
332348
Args:
@@ -335,7 +351,10 @@ async def get_smali_of_class(class_name: str) -> str:
335351
Returns:
336352
Smali code of the requested class as String.
337353
"""
338-
return await get_from_jadx("smali-of-class", {"class": class_name})
354+
response = await get_from_jadx("smali-of-class", {"class": class_name})
355+
if isinstance(response, str):
356+
return json.loads(response)
357+
return response
339358

340359
@mcp.tool()
341360
async def get_android_manifest() -> dict:
@@ -389,7 +408,7 @@ async def get_resource_file(resource_name: str) -> dict:
389408
return await get_from_jadx("get-resource-file", {"name": resource_name})
390409

391410
@mcp.tool()
392-
async def get_main_application_classes_names() -> List[str]:
411+
async def get_main_application_classes_names() -> dict:
393412
"""Fetch all the main application classes' names based on the package name defined in the AndroidManifest.xml.
394413
395414
Args:
@@ -400,18 +419,9 @@ async def get_main_application_classes_names() -> List[str]:
400419
"""
401420

402421
response = await get_from_jadx("main-application-classes-names")
403-
if isinstance(response, dict):
404-
class_names = response.get("classes", [])
405-
else:
406-
import json
407-
try:
408-
parsed = json.loads(response)
409-
class_info_list = parsed.get("classes", [])
410-
class_names = [cls_info.get("name") for cls_info in class_info_list if "name" in cls_info]
411-
except (json.JSONDecodeError, AttributeError):
412-
class_names = []
413-
414-
return class_names
422+
if isinstance(response, str):
423+
return json.loads(response)
424+
return response
415425

416426
@mcp.tool()
417427
async def get_main_application_classes_code(offset: int = 0, count: int = 0) -> dict:

test.sh

100644100755
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
set -euo pipefail
1313

14-
MCP_URL="${MCP_URL:-http://127.0.0.1:9000/mcp/}"
14+
MCP_URL="${MCP_URL:-http://127.0.0.1:9000/mcp}"
1515
ACCEPT_HDR="application/json, text/event-stream"
1616
CONTENT_HDR="application/json"
1717

0 commit comments

Comments
 (0)