ida-pro-mcp-xjoker 1.0.1__py3-none-any.whl → 1.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,7 @@ import ida_idaapi
13
13
  import ida_xref
14
14
  import ida_ua
15
15
  import ida_name
16
+ import ida_idp
16
17
  from .rpc import tool
17
18
  from .sync import idasync, tool_timeout
18
19
  from .cache import decompile_cache, xrefs_cache
@@ -503,7 +504,8 @@ def callees(
503
504
  break
504
505
  current_ea = next_ea
505
506
  continue
506
- if insn.itype in [idaapi.NN_call, idaapi.NN_callfi, idaapi.NN_callni]:
507
+ # Use architecture-independent call instruction detection
508
+ if ida_idp.is_call_insn(insn):
507
509
  op0 = insn.ops[0]
508
510
  if op0.type in (ida_ua.o_mem, ida_ua.o_near, ida_ua.o_far):
509
511
  target = op0.addr
@@ -10,7 +10,7 @@ import ida_nalt
10
10
 
11
11
  from .rpc import tool
12
12
  from .sync import idasync
13
- from .cache import function_cache, string_cache
13
+ from .cache import function_cache
14
14
 
15
15
  # Cached strings list: [(ea, text), ...]
16
16
  _strings_cache: list[tuple[int, str]] | None = None
@@ -3,11 +3,14 @@
3
3
  Provides authentication middleware for MCP server with support for:
4
4
  - Bearer token authentication (Authorization: Bearer <key>)
5
5
  - X-API-Key header authentication
6
+ - Environment variable expansion (${ENV_VAR} syntax)
6
7
  - Timing-attack resistant comparison
7
8
  """
8
9
 
9
10
  import hmac
10
11
  import logging
12
+ import os
13
+ import re
11
14
  from typing import Optional, Callable
12
15
 
13
16
  logger = logging.getLogger(__name__)
@@ -18,6 +21,33 @@ AUTH_EXEMPT_PATHS = frozenset({
18
21
  "/config.html",
19
22
  })
20
23
 
24
+ # Pattern to match ${ENV_VAR} syntax
25
+ _ENV_VAR_PATTERN = re.compile(r"^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$")
26
+
27
+
28
+ def resolve_env_var(value: Optional[str]) -> Optional[str]:
29
+ """Resolve environment variable reference in ${VAR} format.
30
+
31
+ Args:
32
+ value: The value to resolve, may be a literal or ${ENV_VAR} reference
33
+
34
+ Returns:
35
+ The resolved value (from environment) or the original value if not a reference
36
+ """
37
+ if not value:
38
+ return value
39
+
40
+ match = _ENV_VAR_PATTERN.match(value.strip())
41
+ if match:
42
+ env_name = match.group(1)
43
+ env_value = os.environ.get(env_name)
44
+ if env_value:
45
+ return env_value
46
+ else:
47
+ logger.warning(f"Environment variable '{env_name}' not found, using literal value")
48
+ return None # Env var not set, disable auth
49
+ return value
50
+
21
51
 
22
52
  def check_api_key(provided_key: Optional[str], expected_key: Optional[str]) -> bool:
23
53
  """Compare API keys using constant-time comparison to prevent timing attacks.
@@ -100,23 +130,29 @@ class AuthMiddleware:
100
130
 
101
131
  Args:
102
132
  api_key: The expected API key (None = no authentication)
133
+ Supports ${ENV_VAR} syntax to reference environment variables
103
134
  enabled: Whether authentication is enabled
104
135
  """
105
- self._api_key = api_key
136
+ self._api_key_raw = api_key # Store original value (may be ${ENV_VAR})
106
137
  self._enabled = enabled and api_key is not None
107
138
 
108
139
  @property
109
140
  def enabled(self) -> bool:
110
141
  return self._enabled
111
142
 
143
+ @property
144
+ def _api_key(self) -> Optional[str]:
145
+ """Get the resolved API key (expands ${ENV_VAR} references)."""
146
+ return resolve_env_var(self._api_key_raw)
147
+
112
148
  def update_key(self, api_key: Optional[str], enabled: bool = True) -> None:
113
149
  """Update the API key configuration.
114
150
 
115
151
  Args:
116
- api_key: New API key
152
+ api_key: New API key (supports ${ENV_VAR} syntax)
117
153
  enabled: Whether to enable authentication
118
154
  """
119
- self._api_key = api_key
155
+ self._api_key_raw = api_key
120
156
  self._enabled = enabled and api_key is not None
121
157
 
122
158
  def authenticate(self, path: str, headers: dict) -> bool:
@@ -160,6 +196,7 @@ __all__ = [
160
196
  "check_api_key",
161
197
  "extract_api_key_from_headers",
162
198
  "is_path_exempt",
199
+ "resolve_env_var",
163
200
  "AuthMiddleware",
164
201
  "create_auth_check",
165
202
  "AUTH_EXEMPT_PATHS",
@@ -340,6 +340,7 @@ def get_functions_with_calls() -> list[str]:
340
340
  """
341
341
  import idaapi
342
342
  import idautils
343
+ import ida_idp
343
344
 
344
345
  result = []
345
346
  for func_ea in idautils.Functions():
@@ -352,7 +353,8 @@ def get_functions_with_calls() -> list[str]:
352
353
  for head in idautils.Heads(func.start_ea, func.end_ea):
353
354
  insn = idaapi.insn_t()
354
355
  if idaapi.decode_insn(insn, head) > 0:
355
- if insn.itype in [idaapi.NN_call, idaapi.NN_callfi, idaapi.NN_callni]:
356
+ # Use architecture-independent call instruction detection
357
+ if ida_idp.is_call_insn(insn):
356
358
  has_call = True
357
359
  break
358
360
 
@@ -11,7 +11,7 @@ import threading
11
11
  from dataclasses import dataclass, field
12
12
  from typing import Optional, Callable, TYPE_CHECKING
13
13
 
14
- from .config import ServerInstanceConfig, McpConfig, get_config, save_config
14
+ from .config import ServerInstanceConfig, McpConfig, get_config
15
15
  from .auth import AuthMiddleware
16
16
  from .port_utils import try_serve_with_port_retry
17
17
 
@@ -107,11 +107,14 @@ def _sync_wrapper(ff):
107
107
  raise IDASyncError(error_str)
108
108
 
109
109
  call_stack.put((ff.__name__))
110
+ # Enable batch mode for all synchronized operations
111
+ old_batch = idc.batch(1)
110
112
  try:
111
113
  res_container.put(ff())
112
114
  except Exception as x:
113
115
  res_container.put(x)
114
116
  finally:
117
+ idc.batch(old_batch)
115
118
  call_stack.get()
116
119
 
117
120
  idaapi.execute_sync(runned, idaapi.MFF_WRITE)
@@ -132,21 +135,14 @@ def _normalize_timeout(value: object) -> float | None:
132
135
 
133
136
 
134
137
  def sync_wrapper(ff, timeout_override: float | None = None):
135
- """Wrapper to enable batch mode during IDA synchronization."""
138
+ """Wrapper to enable timeout and cancellation during IDA synchronization.
139
+
140
+ Note: Batch mode is handled in _sync_wrapper to ensure it's always
141
+ applied consistently for all synchronized operations.
142
+ """
136
143
  # Capture cancel event from thread-local before execute_sync
137
144
  cancel_event = get_current_cancel_event()
138
145
 
139
- def _run_with_batch(inner_ff):
140
- def _wrapped():
141
- old_batch = idc.batch(1)
142
- try:
143
- return inner_ff()
144
- finally:
145
- idc.batch(old_batch)
146
-
147
- _wrapped.__name__ = inner_ff.__name__
148
- return _wrapped
149
-
150
146
  timeout = timeout_override
151
147
  if timeout is None:
152
148
  timeout = _get_tool_timeout_seconds()
@@ -172,8 +168,8 @@ def sync_wrapper(ff, timeout_override: float | None = None):
172
168
  sys.setprofile(old_profile)
173
169
 
174
170
  timed_ff.__name__ = ff.__name__
175
- return _sync_wrapper(_run_with_batch(timed_ff))
176
- return _sync_wrapper(_run_with_batch(ff))
171
+ return _sync_wrapper(timed_ff)
172
+ return _sync_wrapper(ff)
177
173
 
178
174
 
179
175
  def idasync(f):
@@ -5,10 +5,10 @@ Tests are registered via the @test decorator from the framework module.
5
5
  """
6
6
 
7
7
  # Import all test modules to register tests when the package is imported
8
- from . import test_api_core
9
- from . import test_api_analysis
10
- from . import test_api_memory
11
- from . import test_api_modify
12
- from . import test_api_types
13
- from . import test_api_stack
14
- from . import test_api_resources
8
+ from . import test_api_core as test_api_core
9
+ from . import test_api_analysis as test_api_analysis
10
+ from . import test_api_memory as test_api_memory
11
+ from . import test_api_modify as test_api_modify
12
+ from . import test_api_types as test_api_types
13
+ from . import test_api_stack as test_api_stack
14
+ from . import test_api_resources as test_api_resources
@@ -145,7 +145,7 @@ def test_resource_struct_name():
145
145
  def test_resource_struct_name_not_found():
146
146
  """struct_name_resource handles non-existent structure"""
147
147
  try:
148
- result = struct_name_resource("NonExistentStruct12345")
148
+ struct_name_resource("NonExistentStruct12345")
149
149
  # Should return error or empty
150
150
  except IDAError:
151
151
  pass # Expected for non-existent struct
ida_pro_mcp/ida_mcp/ui.py CHANGED
@@ -10,7 +10,6 @@ from typing import Optional, TYPE_CHECKING
10
10
 
11
11
  from .config import (
12
12
  ServerInstanceConfig,
13
- McpConfig,
14
13
  get_config,
15
14
  save_config,
16
15
  reload_config,
@@ -34,9 +33,9 @@ class ServerConfigForm(idaapi.Form):
34
33
  instance_id = "" if is_new else config.instance_id
35
34
  host = "127.0.0.1" if is_new else config.host
36
35
  port = 13337 if is_new else config.port
37
- auth_enabled = False if is_new else config.auth_enabled
36
+ _ = False if is_new else config.auth_enabled # Reserved for future use
38
37
  api_key = "" if is_new else (config.api_key or "")
39
- auto_start = False if is_new else config.auto_start
38
+ _ = False if is_new else config.auto_start # Reserved for future use
40
39
 
41
40
  form_template = r"""STARTITEM 0
42
41
  BUTTON YES* Save
@@ -22,6 +22,7 @@ from typing import (
22
22
 
23
23
  import ida_funcs
24
24
  import ida_hexrays
25
+ import ida_idp
25
26
  import ida_kernwin
26
27
  import ida_nalt
27
28
  import ida_typeinf
@@ -1022,7 +1023,8 @@ def get_callees(addr: str) -> list[dict]:
1022
1023
  while current_ea < func_end:
1023
1024
  insn = idaapi.insn_t()
1024
1025
  idaapi.decode_insn(insn, current_ea)
1025
- if insn.itype in [idaapi.NN_call, idaapi.NN_callfi, idaapi.NN_callni]:
1026
+ # Use architecture-independent call instruction detection
1027
+ if ida_idp.is_call_insn(insn):
1026
1028
  target = idc.get_operand_value(current_ea, 0)
1027
1029
  target_type = idc.get_operand_type(current_ea, 0)
1028
1030
  if target_type in [idaapi.o_mem, idaapi.o_near, idaapi.o_far]:
@@ -1064,11 +1066,8 @@ def get_callers(addr: str, limit: int = 50) -> list[Function]:
1064
1066
  continue
1065
1067
  insn = idaapi.insn_t()
1066
1068
  idaapi.decode_insn(insn, caller_addr)
1067
- if insn.itype not in [
1068
- idaapi.NN_call,
1069
- idaapi.NN_callfi,
1070
- idaapi.NN_callni,
1071
- ]:
1069
+ # Use architecture-independent call instruction detection
1070
+ if not ida_idp.is_call_insn(insn):
1072
1071
  continue
1073
1072
  callers[func["addr"]] = func
1074
1073
 
ida_pro_mcp/ida_mcp.py CHANGED
@@ -7,18 +7,32 @@ Features:
7
7
  - Web-based configuration at http://host:port/config.html
8
8
  - Bilingual interface (English/中文)
9
9
  - Server restart on config change
10
+ - Auto port increment on conflict
10
11
  """
11
12
 
13
+ import logging
12
14
  import sys
13
15
  import idaapi
14
16
  from typing import TYPE_CHECKING
15
17
 
16
18
  if TYPE_CHECKING:
17
- from . import ida_mcp
19
+ from .ida_mcp.zeromcp.mcp import McpServer
18
20
 
21
+ # Configure logging to stdout (IDA console)
22
+ logging.basicConfig(
23
+ level=logging.INFO,
24
+ format="[MCP] %(message)s",
25
+ handlers=[logging.StreamHandler(sys.stdout)],
26
+ )
27
+ logger = logging.getLogger(__name__)
19
28
 
20
- def unload_package(package_name: str):
21
- """Remove every module that belongs to the package from sys.modules."""
29
+
30
+ def unload_package(package_name: str) -> None:
31
+ """Remove every module that belongs to the package from sys.modules.
32
+
33
+ Args:
34
+ package_name: The package name to unload (e.g., 'ida_mcp')
35
+ """
22
36
  to_remove = [
23
37
  mod_name
24
38
  for mod_name in sys.modules
@@ -29,27 +43,47 @@ def unload_package(package_name: str):
29
43
 
30
44
 
31
45
  class MCP(idaapi.plugin_t):
46
+ """IDA Pro MCP Plugin for LLM-assisted reverse engineering.
47
+
48
+ Provides an MCP (Model Context Protocol) server that allows AI assistants
49
+ to interact with IDA Pro's disassembler and decompiler.
50
+
51
+ Attributes:
52
+ flags: Plugin flags for IDA
53
+ comment: Plugin description
54
+ help: Help text
55
+ wanted_name: Menu item name
56
+ wanted_hotkey: Keyboard shortcut (empty = none)
57
+ """
58
+
32
59
  flags = idaapi.PLUGIN_KEEP
33
60
  comment = "MCP Server for LLM-assisted reverse engineering"
34
61
  help = "Start/Stop MCP Server for AI assistants like Claude"
35
62
  wanted_name = "MCP Server" # Menu name: Edit -> Plugins -> MCP Server
36
63
  wanted_hotkey = "" # No hotkey
37
64
 
38
- def init(self):
39
- print("[MCP] Plugin loaded, use Edit -> Plugins -> MCP Server to toggle")
40
- self.mcp = None
41
- self._current_host = None
42
- self._current_port = None
65
+ def init(self) -> int:
66
+ """Initialize the plugin when IDA loads it.
67
+
68
+ Returns:
69
+ PLUGIN_KEEP to keep the plugin loaded
70
+ """
71
+ logger.info("Plugin loaded, use Edit -> Plugins -> MCP Server to toggle")
72
+ self.mcp: McpServer | None = None
73
+ self._current_host: str | None = None
74
+ self._current_port: int | None = None
43
75
 
44
76
  # Auto-start server on IDA launch
45
77
  self._auto_start()
46
78
 
47
79
  return idaapi.PLUGIN_KEEP
48
80
 
49
- def _auto_start(self):
50
- """Auto-start server when IDA loads a database."""
51
- # Use timer to delay startup until IDA is fully initialized
52
- def delayed_start():
81
+ def _auto_start(self) -> None:
82
+ """Auto-start server when IDA loads a database.
83
+
84
+ Uses a timer to delay startup until IDA is fully initialized.
85
+ """
86
+ def delayed_start() -> int:
53
87
  if self.mcp is None:
54
88
  self._start_server()
55
89
  return -1 # Don't repeat
@@ -57,8 +91,8 @@ class MCP(idaapi.plugin_t):
57
91
  # Delay 1 second to ensure IDA is ready
58
92
  idaapi.register_timer(1000, delayed_start)
59
93
 
60
- def _start_server(self):
61
- """Start the MCP server."""
94
+ def _start_server(self) -> None:
95
+ """Start the MCP server with automatic port retry on conflict."""
62
96
  if self.mcp:
63
97
  return # Already running
64
98
 
@@ -76,7 +110,7 @@ class MCP(idaapi.plugin_t):
76
110
  try:
77
111
  init_caches()
78
112
  except Exception as e:
79
- print(f"[MCP] Cache init failed: {e}")
113
+ logger.warning(f"Cache init failed: {e}")
80
114
 
81
115
  # Set restart callback for web config
82
116
  set_server_restart_callback(self._restart_server)
@@ -91,34 +125,38 @@ class MCP(idaapi.plugin_t):
91
125
  MCP_SERVER, host, port, request_handler=IdaMcpHttpRequestHandler
92
126
  )
93
127
  if failed_ports:
94
- print(f"[MCP] Port {port} was in use, auto-selected port {actual_port}")
128
+ logger.info(f"Port {port} was in use, auto-selected port {actual_port}")
95
129
  self._current_host = host
96
130
  self._current_port = actual_port
97
131
  self.mcp = MCP_SERVER
98
132
  set_download_base_url(f"http://{host}:{actual_port}")
99
- print(f"[MCP] Server started on http://{host}:{actual_port}")
100
- print(f" Config: http://{host}:{actual_port}/config.html")
133
+ logger.info(f"Server started on http://{host}:{actual_port}")
134
+ logger.info(f" Config: http://{host}:{actual_port}/config.html")
101
135
  except OSError as e:
102
136
  if e.errno in (48, 98, 10048):
103
- print(format_port_exhausted_message(host, port, list(range(port, port + 10))))
137
+ logger.error(format_port_exhausted_message(host, port, list(range(port, port + 10))))
104
138
  else:
105
- print(f"[MCP] Error starting server: {e}")
139
+ logger.error(f"Error starting server: {e}")
140
+
141
+ def _restart_server(self, new_host: str, new_port: int) -> None:
142
+ """Restart the server with new configuration.
106
143
 
107
- def _restart_server(self, new_host: str, new_port: int):
108
- """Callback to restart the server with new configuration.
144
+ This is called from a background thread, so we use execute_sync
145
+ to run the actual restart on IDA's main thread.
109
146
 
110
- This is called from a background thread, so we need to use
111
- execute_sync to run the actual restart on IDA's main thread.
147
+ Args:
148
+ new_host: New host address to bind
149
+ new_port: New port number to bind
112
150
  """
113
- def do_restart():
114
- print(f"[MCP] Restarting server on {new_host}:{new_port}...")
151
+ def do_restart() -> int:
152
+ logger.info(f"Restarting server on {new_host}:{new_port}...")
115
153
 
116
154
  # Stop current server
117
155
  if self.mcp:
118
156
  try:
119
157
  self.mcp.stop()
120
158
  except Exception as e:
121
- print(f"[MCP] Error stopping server: {e}")
159
+ logger.error(f"Error stopping server: {e}")
122
160
  self.mcp = None
123
161
 
124
162
  # Reload package and start new server
@@ -136,7 +174,7 @@ class MCP(idaapi.plugin_t):
136
174
  try:
137
175
  init_caches()
138
176
  except Exception as e:
139
- print(f"[MCP] Cache init failed: {e}")
177
+ logger.warning(f"Cache init failed: {e}")
140
178
 
141
179
  # Set restart callback
142
180
  set_server_restart_callback(self._restart_server)
@@ -146,39 +184,49 @@ class MCP(idaapi.plugin_t):
146
184
  MCP_SERVER, new_host, new_port, request_handler=IdaMcpHttpRequestHandler
147
185
  )
148
186
  if failed_ports:
149
- print(f"[MCP] Port {new_port} was in use, auto-selected port {actual_port}")
187
+ logger.info(f"Port {new_port} was in use, auto-selected port {actual_port}")
150
188
  self._current_host = new_host
151
189
  self._current_port = actual_port
152
190
  self.mcp = MCP_SERVER
153
191
  set_download_base_url(f"http://{new_host}:{actual_port}")
154
- print(f"[MCP] Server restarted on http://{new_host}:{actual_port}")
155
- print(f" Config: http://{new_host}:{actual_port}/config.html")
192
+ logger.info(f"Server restarted on http://{new_host}:{actual_port}")
193
+ logger.info(f" Config: http://{new_host}:{actual_port}/config.html")
156
194
  except OSError as e:
157
195
  if e.errno in (48, 98, 10048):
158
- print(format_port_exhausted_message(new_host, new_port, list(range(new_port, new_port + 10))))
196
+ logger.error(format_port_exhausted_message(new_host, new_port, list(range(new_port, new_port + 10))))
159
197
  else:
160
- print(f"[MCP] Error starting server: {e}")
198
+ logger.error(f"Error starting server: {e}")
161
199
 
162
200
  return 1 # Required for execute_sync callback
163
201
 
164
202
  # Execute on IDA's main thread
165
203
  idaapi.execute_sync(do_restart, idaapi.MFF_WRITE)
166
204
 
167
- def run(self, arg):
168
- """Toggle server on/off via menu."""
205
+ def run(self, arg: int) -> None:
206
+ """Toggle server on/off via menu.
207
+
208
+ Args:
209
+ arg: Plugin argument (unused)
210
+ """
169
211
  if self.mcp:
170
212
  self.mcp.stop()
171
213
  self.mcp = None
172
- print("[MCP] Server stopped")
214
+ logger.info("Server stopped")
173
215
  else:
174
216
  self._start_server()
175
217
 
176
- def term(self):
218
+ def term(self) -> None:
219
+ """Cleanup when plugin is unloaded."""
177
220
  if self.mcp:
178
221
  self.mcp.stop()
179
222
 
180
223
 
181
- def PLUGIN_ENTRY():
224
+ def PLUGIN_ENTRY() -> MCP:
225
+ """IDA plugin entry point.
226
+
227
+ Returns:
228
+ Plugin instance
229
+ """
182
230
  return MCP()
183
231
 
184
232
 
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: ida-pro-mcp-xjoker
3
+ Version: 1.0.2
4
+ Summary: Vibe reversing with IDA Pro (enhanced fork)
5
+ Author: mrexodia, can1357, IDA Pro MCP Contributors
6
+ Maintainer: xjoker
7
+ Project-URL: Repository, https://github.com/xjoker/ida-pro-mcp
8
+ Project-URL: Issues, https://github.com/xjoker/ida-pro-mcp/issues
9
+ Keywords: ida,mcp,llm,plugin
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: idapro>=0.0.7
20
+ Requires-Dist: tomli-w>=1.0.0
21
+ Dynamic: license-file
22
+
23
+ # IDA Pro MCP (Enhanced Fork)
24
+
25
+ [中文文档](https://github.com/xjoker/ida-pro-mcp/blob/main/README_zh.md) | English
26
+
27
+ [![PyPI](https://img.shields.io/pypi/v/ida-pro-mcp-xjoker)](https://pypi.org/project/ida-pro-mcp-xjoker/)
28
+ [![Python](https://img.shields.io/pypi/pyversions/ida-pro-mcp-xjoker)](https://pypi.org/project/ida-pro-mcp-xjoker/)
29
+
30
+ An enhanced fork of [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp) - MCP Server for LLM-assisted reverse engineering in IDA Pro.
31
+
32
+ ## What's Different from Original
33
+
34
+ | Feature | Original | This Fork |
35
+ |---------|----------|-----------|
36
+ | **Multi-instance Support** | ❌ Port conflict crashes | ✅ Auto port increment (13337→13346) |
37
+ | **Web Configuration** | ❌ None | ✅ Bilingual UI at `/config.html` |
38
+ | **API Key Auth** | ❌ None | ✅ Bearer token + env var support |
39
+ | **Server Startup** | Manual hotkey | ✅ Auto-start on IDA launch |
40
+ | **Hotkey Conflicts** | Occupies Ctrl+Alt+M | ✅ No hotkey, menu-only |
41
+ | **Config Persistence** | None | ✅ Saved per IDB database |
42
+
43
+ ### Key Enhancements
44
+
45
+ - **Port Conflict Auto-Retry**: Multiple IDA instances automatically use different ports
46
+ - **Web Config UI**: `http://localhost:13337/config.html` with English/中文 interface
47
+ - **API Key Authentication**: Secure remote access with Bearer token
48
+ - **Bug Fixes**: Thread safety, regex handling, type parsing errors fixed
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ pip install ida-pro-mcp-xjoker
54
+ ida-pro-mcp --install
55
+ ```
56
+
57
+ Restart IDA Pro completely after installation.
58
+
59
+ ## Quick Start
60
+
61
+ 1. Open a binary in IDA Pro
62
+ 2. MCP server starts automatically on `http://127.0.0.1:13337`
63
+ 3. Configure your MCP client:
64
+
65
+ ```bash
66
+ # Claude Code
67
+ claude mcp add ida-pro-mcp http://127.0.0.1:13337/mcp
68
+
69
+ # With API Key authentication
70
+ claude mcp add --transport http ida-pro-mcp http://127.0.0.1:13337/mcp \
71
+ --header "Authorization: Bearer your-api-key"
72
+ ```
73
+
74
+ 4. Open web config at `http://127.0.0.1:13337/config.html` to customize settings
75
+
76
+ ## Requirements
77
+
78
+ - Python 3.11+
79
+ - IDA Pro 8.3+ (9.0 recommended), **IDA Free not supported**
80
+ - Any [MCP-compatible client](https://modelcontextprotocol.io/clients)
81
+
82
+ ## API Overview
83
+
84
+ **71 MCP Tools** including:
85
+
86
+ | Category | Tools |
87
+ |----------|-------|
88
+ | Analysis | `decompile`, `disasm`, `xrefs_to`, `callees`, `callers`, `basic_blocks` |
89
+ | Memory | `get_bytes`, `get_string`, `get_int`, `patch` |
90
+ | Types | `declare_type`, `set_type`, `infer_types`, `read_struct` |
91
+ | Modify | `set_comments`, `rename`, `patch_asm` |
92
+ | Search | `find_bytes`, `find_insns`, `find_regex` |
93
+ | Debug | `dbg_*` (20+ debugger tools, enable with `?ext=dbg`) |
94
+ | Python | `py_eval` - execute Python in IDA context |
95
+
96
+ **24 MCP Resources** for read-only access:
97
+ - `ida://idb/metadata`, `ida://cursor`, `ida://structs`, `ida://xrefs/from/{addr}`, etc.
98
+
99
+ ## Headless Mode
100
+
101
+ ```bash
102
+ # SSE transport
103
+ ida-pro-mcp --transport http://127.0.0.1:8744/sse
104
+
105
+ # With idalib (no GUI)
106
+ idalib-mcp --host 127.0.0.1 --port 8745 /path/to/binary
107
+ ```
108
+
109
+ ## Links
110
+
111
+ - [Original Project](https://github.com/mrexodia/ida-pro-mcp) by mrexodia
112
+ - [Changelog](https://github.com/xjoker/ida-pro-mcp/blob/main/CHANGELOG.md)
113
+ - [Issues](https://github.com/xjoker/ida-pro-mcp/issues)
114
+
115
+ ## License
116
+
117
+ MIT - Same as original project
@@ -1,13 +1,13 @@
1
1
  ida_pro_mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  ida_pro_mcp/__main__.py,sha256=OgGb-aPI4F2DJSaU1I-R-OpN_rZfs0hSnMgTo0CPg0o,124
3
- ida_pro_mcp/ida_mcp.py,sha256=-yUE4Ss8jVg1tKZjmXsWLhrQVJsFUpZ5Mc6KxOeOyyM,6982
3
+ ida_pro_mcp/ida_mcp.py,sha256=fyvGl4JTQlnIt0Uwk8luAaud7ym6dd5zoguGwfArf8s,8381
4
4
  ida_pro_mcp/idalib_server.py,sha256=fuWVMjooZegKSl87Ldjg1zsRdDETyGLaSEy1rOoHfhY,11375
5
5
  ida_pro_mcp/idalib_session_manager.py,sha256=UEs0aZODEUOcWDiSRsUx8s9QfdJ-5p8PiTPpBitQUrc,8518
6
6
  ida_pro_mcp/server.py,sha256=AmpktGyT6yEZK03YoUkUHePshWslSoCpTwYRuGLhKoY,35849
7
7
  ida_pro_mcp/test.py,sha256=qqBnhhUkRYrL6cYdlGAMa6oN7wTvVrwzYLxzNez6jno,5023
8
8
  ida_pro_mcp/ida_mcp/__init__.py,sha256=92N8Ksbj7fDp5Z06M_bvdNw2BWaawmv-dyH52HBAxw0,1809
9
- ida_pro_mcp/ida_mcp/api_analysis.py,sha256=vx3hpLIpldALao3fyJYrjYVTR0rdFnG1IXg_XXLkjcw,42328
10
- ida_pro_mcp/ida_mcp/api_core.py,sha256=7j-98-8YTjrmQhtWCAFcR-f5h9g4GJM39DsRY7Il6mI,9291
9
+ ida_pro_mcp/ida_mcp/api_analysis.py,sha256=LpZFbZq1FehuIkIru3TJTINNwOEiEedlYZUKOKIAbpQ,42377
10
+ ida_pro_mcp/ida_mcp/api_core.py,sha256=gOsSI4WWvR0Od-wLAnjV5IVtl26Vcf9L_rkfv8ccQqA,9277
11
11
  ida_pro_mcp/ida_mcp/api_debug.py,sha256=WJKyizs06VQahFM4VWyoa6DXal-9f5JfkMj5Ji46UKw,16027
12
12
  ida_pro_mcp/ida_mcp/api_memory.py,sha256=HJ0Ut4jYQXKJJK9_h4ZL0yN-Mg1fCH9Blhn_gABoe8E,8968
13
13
  ida_pro_mcp/ida_mcp/api_modify.py,sha256=NEokHC_ZLvtYTtx1CSi0ZjSr9O75OlHzSWkEEwqTw_o,14390
@@ -15,31 +15,31 @@ ida_pro_mcp/ida_mcp/api_python.py,sha256=44PEMHNhJMdU4vK6yZ4CPAJbHkR5V-tNlDoTb2r
15
15
  ida_pro_mcp/ida_mcp/api_resources.py,sha256=3vhLHNvv3poT1vneNaAfXco7x8xJnS2a8BMS7F2CfG0,8559
16
16
  ida_pro_mcp/ida_mcp/api_stack.py,sha256=HfspQ9HnrjnWqWWIlwTT-aiti2FyKOUtD6zRCULo6Q0,5050
17
17
  ida_pro_mcp/ida_mcp/api_types.py,sha256=Dz5O0S8uDhrOf0eoOUtbd_BMzPRZqORrWwonWil0saM,16426
18
- ida_pro_mcp/ida_mcp/auth.py,sha256=ghpnbMAMOahvbpu33PDoxlyC_zQlU6iPR78HVGmyHXE,4553
18
+ ida_pro_mcp/ida_mcp/auth.py,sha256=ANjMsCNPLkEW7kog9popq4K3Wo7wfD9B39mpVmvc_pg,5838
19
19
  ida_pro_mcp/ida_mcp/cache.py,sha256=t6DJEydv60dXAHkcCqdfAuD4a7kPrif9KZw3pLIle9Q,6837
20
20
  ida_pro_mcp/ida_mcp/config.py,sha256=WTLcKTo7Wi2Ll6uwzyWq7hPV8Sj9MnaQjtEnltAmmFk,6636
21
- ida_pro_mcp/ida_mcp/framework.py,sha256=k88c366JS4lQ7uIkVXPmlCMIWH7O9sr0Iw43aWJ9P5M,15976
21
+ ida_pro_mcp/ida_mcp/framework.py,sha256=sIYyX9u1FdjOHTQU-fVei0bRUfVFxDctZqDQfJLVFtc,16029
22
22
  ida_pro_mcp/ida_mcp/http.py,sha256=PNFC31TCu19_WWUMJS2h-bY2Oxb1qh-fl5T0Bag0uis,27064
23
23
  ida_pro_mcp/ida_mcp/port_utils.py,sha256=zn3TNSdRsEzmsqOEnC4dOt8ZDUD85D1lXTVKOXzsbqs,3053
24
24
  ida_pro_mcp/ida_mcp/rpc.py,sha256=G0xRdmKehWq5JKlyE0gT7xmzdZ5YyfkEMH5cK9DNhrk,5079
25
- ida_pro_mcp/ida_mcp/server_manager.py,sha256=4hbTu_FmUNl_8oTcvoPUKKxpWaHYfew96rVl9sD2zk0,10835
26
- ida_pro_mcp/ida_mcp/sync.py,sha256=11EHzmALHwFIMo46tMtxpGyE0KIFyAf4WO49KjJPjzE,6823
27
- ida_pro_mcp/ida_mcp/ui.py,sha256=_L0vVPC-yyffBdtWNfndmSGkUNt6_83GhUPFYX-n41U,11204
28
- ida_pro_mcp/ida_mcp/utils.py,sha256=k2fkCtJAn9kQnfAaxcgxqtINHxiGTRDLbO3eN6oD_iA,34413
29
- ida_pro_mcp/ida_mcp/tests/__init__.py,sha256=-it_7Hb45krMiA5wTrylCA5vuOaTFoqY1Z4Y9jXAN7U,447
25
+ ida_pro_mcp/ida_mcp/server_manager.py,sha256=VeW5Rn8I5qx9ukIml8kGkZQQKVHSNoyldTNUmIngfYU,10822
26
+ ida_pro_mcp/ida_mcp/sync.py,sha256=55XKvBXxgZZO-mTUo0-bTvtAqTKjG-G81SdZgELbaEg,6787
27
+ ida_pro_mcp/ida_mcp/ui.py,sha256=k4Mo2ztoPzjhq6LuUvzkFjheCWANeruVLtPKEiqVKCE,11223
28
+ ida_pro_mcp/ida_mcp/utils.py,sha256=3uzcEgBtNMdO-v7b-W7G4JADibpBP3jU7I8FnP3GUXk,34425
29
+ ida_pro_mcp/ida_mcp/tests/__init__.py,sha256=PzCMUTMRjb8xMM0H_WdwRPIlFJw5tXbE4G6Ehr1p7Ac,581
30
30
  ida_pro_mcp/ida_mcp/tests/test_api_analysis.py,sha256=5pe1qO8crvdYYuN7Tw8UEiH_FIc9pcC3rgRvSCqUqpc,8751
31
31
  ida_pro_mcp/ida_mcp/tests/test_api_core.py,sha256=uOh6C-UHIgrf_ZmK-lbmz5V_cXv5LsX0VDe7rgufMZM,7078
32
32
  ida_pro_mcp/ida_mcp/tests/test_api_memory.py,sha256=hNp986HaPISJK5GldRGYYk6KZj01in3SLOJ_YX981z0,5376
33
33
  ida_pro_mcp/ida_mcp/tests/test_api_modify.py,sha256=oV3XPzf_0E5_krDhCL7nyX7uW55ZEhYP3d5Yj4261c8,3403
34
- ida_pro_mcp/ida_mcp/tests/test_api_resources.py,sha256=ePkAuyrIhT6xHHP25lLZ2JlchEzHWMPMmiHFRBCyAt8,5749
34
+ ida_pro_mcp/ida_mcp/tests/test_api_resources.py,sha256=busux61RA1s1bP5JbzGxkWtaj6NBFO7j-irZeIcSsuc,5740
35
35
  ida_pro_mcp/ida_mcp/tests/test_api_stack.py,sha256=8RHMQ0oNoVK5-i0Qo7_8iQ1HGok51g9H9STkRl0jgrs,2081
36
36
  ida_pro_mcp/ida_mcp/tests/test_api_types.py,sha256=LvXwfEc7P-ujWNO7LuNeVvNCbw6YJU-94xbEtpo94l0,7359
37
37
  ida_pro_mcp/ida_mcp/zeromcp/__init__.py,sha256=0wEXEn4fm9TtrM5saxTJ2ZORdvTRCQSXtN4GXBvhwCw,201
38
38
  ida_pro_mcp/ida_mcp/zeromcp/jsonrpc.py,sha256=U4klLVBp_mEh-lnRq5QWPkSxqtKc2J0C-i1hbxQgtIo,14836
39
39
  ida_pro_mcp/ida_mcp/zeromcp/mcp.py,sha256=7l4DT0ONcDOnSK78Mi5mKMF1_FOiVzj2TiOKvTTBy_k,31215
40
- ida_pro_mcp_xjoker-1.0.1.dist-info/licenses/LICENSE,sha256=7n59GIbEpWe6O3oNiKuqrFfnv-7kQCokhy7_p4ora24,1071
41
- ida_pro_mcp_xjoker-1.0.1.dist-info/METADATA,sha256=YDWlDVVax5I6DsrqIdattsvRO5UnPlwbgkRf-5wHTRw,17424
42
- ida_pro_mcp_xjoker-1.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
43
- ida_pro_mcp_xjoker-1.0.1.dist-info/entry_points.txt,sha256=pJ_B_cB3hROec234fBV1ypw1kIz4edLRrk3OLOZbjug,137
44
- ida_pro_mcp_xjoker-1.0.1.dist-info/top_level.txt,sha256=EN_FyE128OksP65oLV_fL3VU618sjUD9yLSMaOES0Ug,12
45
- ida_pro_mcp_xjoker-1.0.1.dist-info/RECORD,,
40
+ ida_pro_mcp_xjoker-1.0.2.dist-info/licenses/LICENSE,sha256=7n59GIbEpWe6O3oNiKuqrFfnv-7kQCokhy7_p4ora24,1071
41
+ ida_pro_mcp_xjoker-1.0.2.dist-info/METADATA,sha256=xgx3nxPmBSpeTGprTUPsf_2NxBO6sJwinUgb9CEtLBM,4041
42
+ ida_pro_mcp_xjoker-1.0.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
43
+ ida_pro_mcp_xjoker-1.0.2.dist-info/entry_points.txt,sha256=pJ_B_cB3hROec234fBV1ypw1kIz4edLRrk3OLOZbjug,137
44
+ ida_pro_mcp_xjoker-1.0.2.dist-info/top_level.txt,sha256=EN_FyE128OksP65oLV_fL3VU618sjUD9yLSMaOES0Ug,12
45
+ ida_pro_mcp_xjoker-1.0.2.dist-info/RECORD,,
@@ -1,405 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: ida-pro-mcp-xjoker
3
- Version: 1.0.1
4
- Summary: Vibe reversing with IDA Pro (enhanced fork)
5
- Author: mrexodia, can1357, IDA Pro MCP Contributors
6
- Project-URL: Repository, https://github.com/xjoker/ida-pro-mcp
7
- Project-URL: Issues, https://github.com/xjoker/ida-pro-mcp/issues
8
- Keywords: ida,mcp,llm,plugin
9
- Classifier: Development Status :: 5 - Production/Stable
10
- Classifier: Intended Audience :: Developers
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.11
13
- Classifier: Operating System :: MacOS
14
- Classifier: Operating System :: Microsoft :: Windows
15
- Requires-Python: >=3.11
16
- Description-Content-Type: text/markdown
17
- License-File: LICENSE
18
- Requires-Dist: idapro>=0.0.7
19
- Requires-Dist: tomli-w>=1.0.0
20
- Dynamic: license-file
21
-
22
- # IDA Pro MCP
23
-
24
- [中文文档](README_zh.md) | English
25
-
26
- Simple [MCP Server](https://modelcontextprotocol.io/introduction) to allow vibe reversing in IDA Pro.
27
-
28
- ## Fork Version Updates
29
-
30
- > This is a fork of [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp) with the following enhancements:
31
-
32
- ### New Features
33
-
34
- - **Web Configuration Interface** - Access `http://localhost:13337/config.html` for bilingual (English/中文) settings
35
- - **Server Configuration** - Configure host, port, and API Key authentication via web UI
36
- - **Auto-start on IDA Launch** - MCP server starts automatically when IDA loads a database
37
- - **No Keyboard Shortcut Occupation** - Removed all hotkey bindings, menu-only activation
38
- - **Menu Name Changed** - Plugin menu renamed from "MCP" to "MCP Server"
39
- - **Server Restart on Config Change** - Server automatically restarts after saving configuration
40
-
41
- ### API Key Authentication
42
-
43
- To enable API Key authentication:
44
-
45
- 1. Open the web configuration at `http://localhost:13337/config.html`
46
- 2. Check "Enable API Key Authentication"
47
- 3. Enter your API Key (or use `${ENV_VAR}` to reference an environment variable)
48
- 4. Save and restart the server
49
-
50
- **Client Configuration Examples:**
51
-
52
- ```bash
53
- # Claude Code - Add MCP server with Bearer token authentication
54
- claude mcp add --transport http ida-pro-mcp http://192.168.1.100:13337/mcp \
55
- --header "Authorization: Bearer your-api-key-here"
56
-
57
- # Or configure in ~/.claude.json manually:
58
- {
59
- "mcpServers": {
60
- "ida-pro-mcp": {
61
- "type": "http",
62
- "url": "http://192.168.1.100:13337/mcp",
63
- "headers": {
64
- "Authorization": "Bearer your-api-key-here"
65
- }
66
- }
67
- }
68
- }
69
- ```
70
-
71
- ### Bug Fixes
72
-
73
- - Fixed IDA main thread crash when restarting server
74
- - Fixed walrus operator logic error in type parsing
75
- - Added regex compilation exception handling
76
- - Fixed bare `except:` statements
77
- - Unified default pagination count values
78
-
79
- ---
80
-
81
- https://github.com/user-attachments/assets/6ebeaa92-a9db-43fa-b756-eececce2aca0
82
-
83
- The binaries and prompt for the video are available in the [mcp-reversing-dataset](https://github.com/mrexodia/mcp-reversing-dataset) repository.
84
-
85
- ## Prerequisites
86
-
87
- - [Python](https://www.python.org/downloads/) (**3.11 or higher**)
88
- - Use `idapyswitch` to switch to the newest Python version
89
- - [IDA Pro](https://hex-rays.com/ida-pro) (8.3 or higher, 9 recommended), **IDA Free is not supported**
90
- - Supported MCP Client (pick one you like)
91
- - [Amazon Q Developer CLI](https://aws.amazon.com/q/developer/)
92
- - [Augment Code](https://www.augmentcode.com/)
93
- - [Claude](https://claude.ai/download)
94
- - [Claude Code](https://www.anthropic.com/code)
95
- - [Cline](https://cline.bot)
96
- - [Codex](https://github.com/openai/codex)
97
- - [Copilot CLI](https://docs.github.com/en/copilot)
98
- - [Crush](https://github.com/charmbracelet/crush)
99
- - [Cursor](https://cursor.com)
100
- - [Gemini CLI](https://google-gemini.github.io/gemini-cli/)
101
- - [Kilo Code](https://www.kilocode.com/)
102
- - [Kiro](https://kiro.dev/)
103
- - [LM Studio](https://lmstudio.ai/)
104
- - [Opencode](https://opencode.ai/)
105
- - [Qodo Gen](https://www.qodo.ai/)
106
- - [Qwen Coder](https://qwenlm.github.io/qwen-code-docs/)
107
- - [Roo Code](https://roocode.com)
108
- - [Trae](https://trae.ai/)
109
- - [VS Code](https://code.visualstudio.com/)
110
- - [VS Code Insiders](https://code.visualstudio.com/insiders)
111
- - [Warp](https://www.warp.dev/)
112
- - [Windsurf](https://windsurf.com)
113
- - [Zed](https://zed.dev/)
114
- - [Other MCP Clients](https://modelcontextprotocol.io/clients#example-clients): Run `ida-pro-mcp --config` to get the JSON config for your client.
115
-
116
- ## Installation
117
-
118
- Install the latest version of the IDA Pro MCP package:
119
-
120
- ```sh
121
- pip uninstall ida-pro-mcp
122
- pip install https://github.com/xjoker/ida-pro-mcp/archive/refs/heads/main.zip
123
- ```
124
-
125
- Configure the MCP servers and install the IDA Plugin:
126
-
127
- ```
128
- ida-pro-mcp --install
129
- ```
130
-
131
- **Important**: Make sure you completely restart IDA and your MCP client for the installation to take effect. Some clients (like Claude) run in the background and need to be quit from the tray icon.
132
-
133
- https://github.com/user-attachments/assets/65ed3373-a187-4dd5-a807-425dca1d8ee9
134
-
135
- _Note_: You need to load a binary in IDA before the plugin menu will show up.
136
-
137
- ## Prompt Engineering
138
-
139
- LLMs are prone to hallucinations and you need to be specific with your prompting. For reverse engineering the conversion between integers and bytes are especially problematic. Below is a minimal example prompt, feel free to start a discussion or open an issue if you have good results with a different prompt:
140
-
141
- ```md
142
- Your task is to analyze a crackme in IDA Pro. You can use the MCP tools to retrieve information. In general use the following strategy:
143
-
144
- - Inspect the decompilation and add comments with your findings
145
- - Rename variables to more sensible names
146
- - Change the variable and argument types if necessary (especially pointer and array types)
147
- - Change function names to be more descriptive
148
- - If more details are necessary, disassemble the function and add comments with your findings
149
- - NEVER convert number bases yourself. Use the `int_convert` MCP tool if needed!
150
- - Do not attempt brute forcing, derive any solutions purely from the disassembly and simple python scripts
151
- - Create a report.md with your findings and steps taken at the end
152
- - When you find a solution, prompt to user for feedback with the password you found
153
- ```
154
-
155
- This prompt was just the first experiment, please share if you found ways to improve the output!
156
-
157
- Another prompt by [@can1357](https://github.com/can1357):
158
-
159
- ```md
160
- Your task is to create a complete and comprehensive reverse engineering analysis. Reference AGENTS.md to understand the project goals and ensure the analysis serves our purposes.
161
-
162
- Use the following systematic methodology:
163
-
164
- 1. **Decompilation Analysis**
165
- - Thoroughly inspect the decompiler output
166
- - Add detailed comments documenting your findings
167
- - Focus on understanding the actual functionality and purpose of each component (do not rely on old, incorrect comments)
168
-
169
- 2. **Improve Readability in the Database**
170
- - Rename variables to sensible, descriptive names
171
- - Correct variable and argument types where necessary (especially pointers and array types)
172
- - Update function names to be descriptive of their actual purpose
173
-
174
- 3. **Deep Dive When Needed**
175
- - If more details are necessary, examine the disassembly and add comments with findings
176
- - Document any low-level behaviors that aren't clear from the decompilation alone
177
- - Use sub-agents to perform detailed analysis
178
-
179
- 4. **Important Constraints**
180
- - NEVER convert number bases yourself - use the int_convert MCP tool if needed
181
- - Use MCP tools to retrieve information as necessary
182
- - Derive all conclusions from actual analysis, not assumptions
183
-
184
- 5. **Documentation**
185
- - Produce comprehensive RE/*.md files with your findings
186
- - Document the steps taken and methodology used
187
- - When asked by the user, ensure accuracy over previous analysis file
188
- - Organize findings in a way that serves the project goals outlined in AGENTS.md or CLAUDE.md
189
- ```
190
-
191
- Live stream discussing prompting and showing some real-world malware analysis:
192
-
193
- [![](https://img.youtube.com/vi/iFxNuk3kxhk/0.jpg)](https://www.youtube.com/watch?v=iFxNuk3kxhk)
194
-
195
- ## Tips for Enhancing LLM Accuracy
196
-
197
- Large Language Models (LLMs) are powerful tools, but they can sometimes struggle with complex mathematical calculations or exhibit "hallucinations" (making up facts). Make sure to tell the LLM to use the `int_convert` MCP tool and you might also need [math-mcp](https://github.com/EthanHenrickson/math-mcp) for certain operations.
198
-
199
- Another thing to keep in mind is that LLMs will not perform well on obfuscated code. Before trying to use an LLM to solve the problem, take a look around the binary and spend some time (automatically) removing the following things:
200
-
201
- - String encryption
202
- - Import hashing
203
- - Control flow flattening
204
- - Code encryption
205
- - Anti-decompilation tricks
206
-
207
- You should also use a tool like Lumina or FLIRT to try and resolve all the open source library code and the C++ STL, this will further improve the accuracy.
208
-
209
- ## SSE Transport & Headless MCP
210
-
211
- You can run an SSE server to connect to the user interface like this:
212
-
213
- ```sh
214
- uv run ida-pro-mcp --transport http://127.0.0.1:8744/sse
215
- ```
216
-
217
- After installing [`idalib`](https://docs.hex-rays.com/user-guide/idalib) you can also run a headless SSE server:
218
-
219
- ```sh
220
- uv run idalib-mcp --host 127.0.0.1 --port 8745 path/to/executable
221
- ```
222
-
223
- _Note_: The `idalib` feature was contributed by [Willi Ballenthin](https://github.com/williballenthin).
224
-
225
-
226
- ## MCP Resources
227
-
228
- **Resources** represent browsable state (read-only data) following MCP's philosophy.
229
-
230
- **Core IDB State:**
231
- - `ida://idb/metadata` - IDB file info (path, arch, base, size, hashes)
232
- - `ida://idb/segments` - Memory segments with permissions
233
- - `ida://idb/entrypoints` - Entry points (main, TLS callbacks, etc.)
234
-
235
- **UI State:**
236
- - `ida://cursor` - Current cursor position and function
237
- - `ida://selection` - Current selection range
238
-
239
- **Type Information:**
240
- - `ida://types` - All local types
241
- - `ida://structs` - All structures/unions
242
- - `ida://struct/{name}` - Structure definition with fields
243
-
244
- **Lookups:**
245
- - `ida://import/{name}` - Import details by name
246
- - `ida://export/{name}` - Export details by name
247
- - `ida://xrefs/from/{addr}` - Cross-references from address
248
-
249
- ## Core Functions
250
-
251
- - `lookup_funcs(queries)`: Get function(s) by address or name (auto-detects, accepts list or comma-separated string).
252
- - `int_convert(inputs)`: Convert numbers to different formats (decimal, hex, bytes, ASCII, binary).
253
- - `list_funcs(queries)`: List functions (paginated, filtered).
254
- - `list_globals(queries)`: List global variables (paginated, filtered).
255
- - `imports(offset, count)`: List all imported symbols with module names (paginated).
256
- - `decompile(addr)`: Decompile function at the given address.
257
- - `disasm(addr)`: Disassemble function with full details (arguments, stack frame, etc).
258
- - `xrefs_to(addrs)`: Get all cross-references to address(es).
259
- - `xrefs_to_field(queries)`: Get cross-references to specific struct field(s).
260
- - `callees(addrs)`: Get functions called by function(s) at address(es).
261
-
262
- ## Modification Operations
263
-
264
- - `set_comments(items)`: Set comments at address(es) in both disassembly and decompiler views.
265
- - `patch_asm(items)`: Patch assembly instructions at address(es).
266
- - `declare_type(decls)`: Declare C type(s) in the local type library.
267
-
268
- ## Memory Reading Operations
269
-
270
- - `get_bytes(addrs)`: Read raw bytes at address(es).
271
- - `get_int(queries)`: Read integer values using ty (i8/u64/i16le/i16be/etc).
272
- - `get_string(addrs)`: Read null-terminated string(s).
273
- - `get_global_value(queries)`: Read global variable value(s) by address or name (auto-detects, compile-time values).
274
-
275
- ## Stack Frame Operations
276
-
277
- - `stack_frame(addrs)`: Get stack frame variables for function(s).
278
- - `declare_stack(items)`: Create stack variable(s) at specified offset(s).
279
- - `delete_stack(items)`: Delete stack variable(s) by name.
280
-
281
- ## Structure Operations
282
-
283
- - `read_struct(queries)`: Read structure field values at specific address(es).
284
- - `search_structs(filter)`: Search structures by name pattern.
285
-
286
- ## Debugger Operations (Extension)
287
-
288
- Debugger tools are hidden by default. Enable with `?ext=dbg` query parameter:
289
-
290
- ```
291
- http://127.0.0.1:13337/mcp?ext=dbg
292
- ```
293
-
294
- **Control:**
295
- - `dbg_start()`: Start debugger process.
296
- - `dbg_exit()`: Exit debugger process.
297
- - `dbg_continue()`: Continue execution.
298
- - `dbg_run_to(addr)`: Run to address.
299
- - `dbg_step_into()`: Step into instruction.
300
- - `dbg_step_over()`: Step over instruction.
301
-
302
- **Breakpoints:**
303
- - `dbg_bps()`: List all breakpoints.
304
- - `dbg_add_bp(addrs)`: Add breakpoint(s).
305
- - `dbg_delete_bp(addrs)`: Delete breakpoint(s).
306
- - `dbg_toggle_bp(items)`: Enable/disable breakpoint(s).
307
-
308
- **Registers:**
309
- - `dbg_regs()`: All registers, current thread.
310
- - `dbg_regs_all()`: All registers, all threads.
311
- - `dbg_regs_remote(tids)`: All registers, specific thread(s).
312
- - `dbg_gpregs()`: GP registers, current thread.
313
- - `dbg_gpregs_remote(tids)`: GP registers, specific thread(s).
314
- - `dbg_regs_named(names)`: Named registers, current thread.
315
- - `dbg_regs_named_remote(tid, names)`: Named registers, specific thread.
316
-
317
- **Stack & Memory:**
318
- - `dbg_stacktrace()`: Call stack with module/symbol info.
319
- - `dbg_read(regions)`: Read memory from debugged process.
320
- - `dbg_write(regions)`: Write memory to debugged process.
321
-
322
- ## Advanced Analysis Operations
323
-
324
- - `py_eval(code)`: Execute arbitrary Python code in IDA context (returns dict with result/stdout/stderr, supports Jupyter-style evaluation).
325
- - `analyze_funcs(addrs)`: Comprehensive function analysis (decompilation, assembly, xrefs, callees, callers, strings, constants, basic blocks).
326
-
327
- ## Pattern Matching & Search
328
-
329
- - `find_regex(queries)`: Search strings with case-insensitive regex (paginated).
330
- - `find_bytes(patterns, limit=1000, offset=0)`: Find byte pattern(s) in binary (e.g., "48 8B ?? ??"). Max limit: 10000.
331
- - `find_insns(sequences, limit=1000, offset=0)`: Find instruction sequence(s) in code. Max limit: 10000.
332
- - `find(type, targets, limit=1000, offset=0)`: Advanced search (immediate values, strings, data/code references). Max limit: 10000.
333
-
334
- ## Control Flow Analysis
335
-
336
- - `basic_blocks(addrs)`: Get basic blocks with successors and predecessors.
337
-
338
- ## Type Operations
339
-
340
- - `set_type(edits)`: Apply type(s) to functions, globals, locals, or stack variables.
341
- - `infer_types(addrs)`: Infer types at address(es) using Hex-Rays or heuristics.
342
-
343
- ## Export Operations
344
-
345
- - `export_funcs(addrs, format)`: Export function(s) in specified format (json, c_header, or prototypes).
346
-
347
- ## Graph Operations
348
-
349
- - `callgraph(roots, max_depth)`: Build call graph from root function(s) with configurable depth.
350
-
351
- ## Batch Operations
352
-
353
- - `rename(batch)`: Unified batch rename operation for functions, globals, locals, and stack variables (accepts dict with optional `func`, `data`, `local`, `stack` keys).
354
- - `patch(patches)`: Patch multiple byte sequences at once.
355
- - `put_int(items)`: Write integer values using ty (i8/u64/i16le/i16be/etc).
356
-
357
- **Key Features:**
358
-
359
- - **Type-safe API**: All functions use strongly-typed parameters with TypedDict schemas for better IDE support and LLM structured outputs
360
- - **Batch-first design**: Most operations accept both single items and lists
361
- - **Consistent error handling**: All batch operations return `[{..., error: null|string}, ...]`
362
- - **Cursor-based pagination**: Search functions return `cursor: {next: offset}` or `{done: true}` (default limit: 1000, enforced max: 10000 to prevent token overflow)
363
- - **Performance**: Strings are cached with MD5-based invalidation to avoid repeated `build_strlist` calls in large projects
364
-
365
- ## Comparison with other MCP servers
366
-
367
- There are a few IDA Pro MCP servers floating around, but I created my own for a few reasons:
368
-
369
- 1. Installation should be fully automated.
370
- 2. The architecture of other plugins make it difficult to add new functionality quickly (too much boilerplate of unnecessary dependencies).
371
- 3. Learning new technologies is fun!
372
-
373
- If you want to check them out, here is a list (in the order I discovered them):
374
-
375
- - https://github.com/taida957789/ida-mcp-server-plugin (SSE protocol only, requires installing dependencies in IDAPython).
376
- - https://github.com/fdrechsler/mcp-server-idapro (MCP Server in TypeScript, excessive boilerplate required to add new functionality).
377
- - https://github.com/MxIris-Reverse-Engineering/ida-mcp-server (custom socket protocol, boilerplate).
378
-
379
- Feel free to open a PR to add your IDA Pro MCP server here.
380
-
381
- ## Development
382
-
383
- Adding new features is a super easy and streamlined process. All you have to do is add a new `@tool` function to the modular API files in `src/ida_pro_mcp/ida_mcp/api_*.py` and your function will be available in the MCP server without any additional boilerplate! Below is a video where I add the `get_metadata` function in less than 2 minutes (including testing):
384
-
385
- https://github.com/user-attachments/assets/951de823-88ea-4235-adcb-9257e316ae64
386
-
387
- To test the MCP server itself:
388
-
389
- ```sh
390
- npx -y @modelcontextprotocol/inspector
391
- ```
392
-
393
- This will open a web interface at http://localhost:5173 and allow you to interact with the MCP tools for testing.
394
-
395
- For testing I create a symbolic link to the IDA plugin and then POST a JSON-RPC request directly to `http://localhost:13337/mcp`. After [enabling symbolic links](https://learn.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development) you can run the following command:
396
-
397
- ```sh
398
- uv run ida-pro-mcp --install
399
- ```
400
-
401
- Generate the changelog of direct commits to `main`:
402
-
403
- ```sh
404
- git log --first-parent --no-merges 1.2.0..main "--pretty=- %s"
405
- ```