-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathfastmcp_adapter.py
More file actions
166 lines (133 loc) · 4.7 KB
/
fastmcp_adapter.py
File metadata and controls
166 lines (133 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
from mcp.server.fastmcp import FastMCP
import requests
from requests.exceptions import ConnectionError
import sys
# Create the MCP adapter with a human-readable name
mcp = FastMCP("Jadx MCP Server")
# Address of the Jadx MCP plugin's HTTP server
DEFAULT_MCP_SERVER = "http://localhost:8085"
mcp_server = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_MCP_SERVER
def invoke_jadx(tool: str, parameters: dict = {}) -> dict:
"""
Internal helper to send a tool request to the Jadx MCP HTTP server.
"""
try:
resp = requests.post(f"{mcp_server}/invoke", json={"tool": tool, "parameters": parameters})
resp.raise_for_status()
data = resp.json()
if "error" in data:
raise ValueError(data["error"])
return data.get("result", data)
except ConnectionError:
raise ConnectionError("Jadx MCP server is not running. Please start Jadx and try again.")
except Exception as e:
raise RuntimeError(f"Unexpected error: {str(e)}")
@mcp.tool()
def list_all_classes(limit: int = 250, offset: int = 0) -> dict:
"""
Returns a paginated list of class names.
Params:
- limit: Max number of classes to return (default 250)
- offset: Starting index of class list
"""
return invoke_jadx("list_all_classes", {"limit": limit, "offset": offset})
@mcp.tool()
def search_class_by_name(query: str) -> dict:
"""Search for class names that contain the given query string (case-insensitive)."""
return invoke_jadx("search_class_by_name", {"query": query})
@mcp.tool()
def get_class_source(class_name: str) -> str:
"""
Returns the full decompiled source code of a given class.
"""
return invoke_jadx("get_class_source", {"class_name": class_name})
@mcp.tool()
def search_method_by_name(method_name: str) -> str:
"""
Searches for all methods matching the provided name.
Returns class and method pairs as string.
"""
return invoke_jadx("search_method_by_name", {"method_name": method_name})
@mcp.tool()
def get_methods_of_class(class_name: str) -> list:
"""
Returns all method names declared in the specified class.
"""
return invoke_jadx("get_methods_of_class", {"class_name": class_name})
@mcp.tool()
def get_fields_of_class(class_name: str) -> list:
"""
Returns all field names declared in the specified class.
"""
return invoke_jadx("get_fields_of_class", {"class_name": class_name})
@mcp.tool()
def get_method_code(class_name: str, method_name: str) -> str:
"""
Returns only the source code block of a specific method within a class.
"""
return invoke_jadx("get_method_code", {
"class_name": class_name,
"method_name": method_name
})
@mcp.tool()
def get_android_manifest() -> str:
"""
Returns the content of AndroidManifest.xml
"""
return invoke_jadx("get_android_manifest")
@mcp.tool()
def get_all_resource_file_names(limit: int = 250, offset: int = 0) -> dict:
"""
Returns a list of all resource file names in the APK.
Params:
- limit: Max number of resources to return (default 250)
- offset: Starting index of resource list
"""
return invoke_jadx("get_all_resource_file_names", {"limit": limit, "offset": offset})
@mcp.tool()
def get_resource_file(resource_name: str) -> dict:
"""
Returns the content of a specific resource file.
"""
return invoke_jadx("get_resource_file", {"resource_name": resource_name})
@mcp.tool()
def get_class_xrefs(class_name: str) -> dict:
"""
Returns all references to a class.
"""
return invoke_jadx("get_class_xrefs", {"class_name": class_name})
@mcp.tool()
def get_method_xrefs(class_name: str, method_name: str) -> dict:
"""
Returns all references to a method.
"""
return invoke_jadx("get_method_xrefs", {
"class_name": class_name,
"method_name": method_name
})
@mcp.tool()
def get_field_xrefs(class_name: str, field_name: str) -> dict:
"""
Returns all references to a field.
"""
return invoke_jadx("get_field_xrefs", {
"class_name": class_name,
"field_name": field_name
})
@mcp.resource("jadx://tools")
def get_tools_resource() -> dict:
"""
Fetches the list of all available tools and their descriptions from the Jadx plugin.
Used for dynamic tool discovery.
"""
try:
resp = requests.get(f"{mcp_server}/tools")
resp.raise_for_status()
return resp.json()
except ConnectionError:
raise ConnectionError("Jadx MCP server is not running. Please start Jadx and try again.")
except Exception as e:
raise RuntimeError(f"Unexpected error: {str(e)}")
if __name__ == "__main__":
mcp.run(transport="stdio")
print("Adapter started", file=sys.stderr)