# Directory Structure
```
├── ida-mcp-server.py
├── LICENSE
├── README.md
└── requirements.txt
```
# Files
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | [](https://mseep.ai/app/taida957789-ida-mcp-server-plugin)
2 |
3 | # IDA Pro MCP Server
4 |
5 | IDA Pro MCP Server is a plugin that allows remote querying and control of IDA Pro through the Model Context Protocol (MCP) interface. This plugin enables AI assistants (such as Claude) to interact directly with IDA Pro for binary analysis tasks.
6 |
7 | ## Overview
8 |
9 | This server provides a series of tools that allow AI assistants to perform the following operations:
10 | - Get byte data from specific addresses
11 | - Get disassembly code
12 | - Get decompiled pseudocode
13 | - Query function names
14 | - Get segment information
15 | - List all functions
16 | - Find cross-references
17 | - Get import/export tables
18 | - Get entry points
19 | - Define/undefine functions
20 | - Get various data types (dword, word, byte, qword, float, double, string)
21 | - Get all strings in the binary file
22 | - Get the length of the instruction at the specified address
23 |
24 | ## Installation
25 |
26 | > **Note:** This plugin is designed for and tested with IDA Pro version 9.0+.
27 |
28 | 1. Ensure Python and related dependencies are installed:
29 |
30 | ```bash
31 | pip install -r requirements.txt
32 | ```
33 |
34 | 2. Copy the `ida-mcp-server.py` file to the IDA Pro plugins directory:
35 | - Windows: `%Programfiles%\IDA Pro 9.0\plugins\`
36 | - Linux: `~/.idapro/plugins/`
37 | - macOS: `~/Library/Application Support/IDA Pro/plugins/`
38 |
39 | ## Configure Claude / VSCode
40 |
41 | Add the following configuration to the `mcp.json` file in Claude or VSCode:
42 |
43 | ```json
44 | {
45 | "mcpServers": {
46 | "IDAPro": {
47 | "url": "http://127.0.0.1:3000/sse",
48 | "type": "sse"
49 | }
50 | }
51 | }
52 | ```
53 |
54 | ## Usage
55 |
56 | 1. Open a binary file in IDA Pro
57 | 2. The plugin will automatically load and start the MCP server locally (port 3000)
58 | 3. Connect your AI assistant (e.g., Claude) to this server
59 | 4. Use the AI assistant to perform binary analysis tasks
60 |
61 | ## Available Analysis Tools
62 |
63 | IDA Pro MCP Server provides the following tools:
64 |
65 | - `get_bytes`: Get bytes at a specified address
66 | - `get_disasm`: Get disassembly at a specified address
67 | - `get_decompiled_func`: Get pseudocode of the function containing the specified address
68 | - `get_function_name`: Get function name at a specified address
69 | - `get_segments`: Get all segment information
70 | - `get_functions`: Get all functions in the binary
71 | - `get_xrefs_to`: Get all cross-references to a specified address
72 | - `get_imports`: Get all imported functions
73 | - `get_exports`: Get all exported functions
74 | - `get_entry_point`: Get the entry point of the binary
75 | - `make_function`: Create a function at a specified address
76 | - `undefine_function`: Undefine a function at a specified address
77 | - `get_dword_at`: Get the dword at a specified address
78 | - `get_word_at`: Get the word at a specified address
79 | - `get_byte_at`: Get the byte at a specified address
80 | - `get_qword_at`: Get the qword at a specified address
81 | - `get_float_at`: Get the float at a specified address
82 | - `get_double_at`: Get the double at a specified address
83 | - `get_string_at`: Get the string at a specified address
84 | - `get_string_list`: Get all strings in the binary
85 | - `get_strings`: Get all strings in the binary (with addresses)
86 |
87 | ## Best Practices
88 |
89 | When analyzing binary files, it's recommended to follow these steps:
90 |
91 | 1. Examine the entry point
92 | 2. Analyze the import table
93 | 3. Review strings
94 | 4. Track key API calls
95 | 5. Identify main functional blocks
96 | 6. Analyze control flow
97 | 7. Identify malicious behaviors
98 | 8. Analyze algorithms and encryption routines
99 | 9. Document analysis results
100 | 10. Use advanced techniques
101 |
102 | ## License
103 |
104 | MIT License
105 |
106 | Copyright (c) 2023
107 |
108 | Permission is hereby granted, free of charge, to any person obtaining a copy
109 | of this software and associated documentation files (the "Software"), to deal
110 | in the Software without restriction, including without limitation the rights
111 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
112 | copies of the Software, and to permit persons to whom the Software is
113 | furnished to do so, subject to the following conditions:
114 |
115 | The above copyright notice and this permission notice shall be included in all
116 | copies or substantial portions of the Software.
117 |
118 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
119 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
120 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
121 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
122 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
123 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
124 | SOFTWARE.
125 |
```
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
```
1 | mcp==1.5.0
2 | starlette==0.46.1
3 | sse-starlette==2.2.1
4 | uvicorn==0.34.0
5 | typing_extensions==4.12.2
6 | pydantic==2.10.6
7 | pydantic_core==2.27.2
8 | anyio==4.9.0
9 | idna==3.10
10 | sniffio==1.3.1
11 | httpcore==1.0.7
12 | httpx==0.28.1
13 | httpx-sse==0.4.0
14 | h11==0.14.0
15 | click==8.1.8
16 |
```
--------------------------------------------------------------------------------
/ida-mcp-server.py:
--------------------------------------------------------------------------------
```python
1 | import glob
2 | import json
3 | import os
4 | import ida_bytes
5 | import ida_ua
6 | import ida_funcs
7 | import ida_hexrays
8 | import ida_name
9 | import ida_segment
10 | import idautils
11 | import idc
12 | import ida_idaapi
13 | import ida_kernwin
14 | import idaapi
15 | import threading
16 |
17 | from typing import Dict, List, Optional, Any, Tuple
18 | from datetime import datetime
19 | from functools import wraps
20 | from starlette.middleware import Middleware
21 | from starlette.middleware.cors import CORSMiddleware
22 | from starlette.applications import Starlette
23 | from starlette.requests import Request
24 | from starlette.routing import Mount, Route
25 | from starlette.responses import Response
26 | from mcp.server import Server
27 | from mcp.server.sse import SseServerTransport
28 | from mcp.server import FastMCP
29 | import uvicorn
30 |
31 |
32 | # Initialize FastMCP server for IDA tools
33 | mcp = FastMCP("IDA MCP Server", port=3000)
34 |
35 | # 封裝函數執行在主線程的裝飾器
36 | def execute_on_main_thread(f):
37 | @wraps(f)
38 | def wrapper(*args, **kwargs):
39 | result = []
40 | exception = []
41 |
42 | def run_function():
43 | try:
44 | result.append(f(*args, **kwargs))
45 | except Exception as e:
46 | exception.append(e)
47 | return 0
48 |
49 | ida_kernwin.execute_sync(run_function, ida_kernwin.MFF_FAST)
50 |
51 | if exception:
52 | raise exception[0]
53 | return result[0]
54 | return wrapper
55 |
56 |
57 | @mcp.tool()
58 | @execute_on_main_thread
59 | def get_bytes(ea: int, size: int) -> List[int]:
60 | """Get bytes at specified address.
61 |
62 | Args:
63 | ea: Effective address to read from
64 | size: Number of bytes to read
65 | """
66 | try:
67 | result = [ida_bytes.get_byte(ea + i) for i in range(size)]
68 | return result
69 | except Exception as e:
70 | print(f"Error in get_bytes: {str(e)}")
71 | return {"error": str(e)}
72 |
73 |
74 | @mcp.tool()
75 | @execute_on_main_thread
76 | def get_disasm(ea: int) -> str:
77 | """Get disassembly at specified address.
78 |
79 | Args:
80 | ea: Effective address to disassemble
81 | """
82 | return idc.generate_disasm_line(ea, 0)
83 |
84 |
85 | @mcp.tool()
86 | @execute_on_main_thread
87 | def get_decompiled_func(ea: int) -> Dict[str, Any]:
88 | """Get decompiled pseudocode of function containing address.
89 |
90 | Args:
91 | ea: Effective address within the function
92 | """
93 | try:
94 | func = ida_funcs.get_func(ea)
95 | if not func:
96 | return {"error": "No function found at address"}
97 |
98 | decompiler = ida_hexrays.decompile(func.start_ea)
99 | if not decompiler:
100 | return {"error": "Failed to decompile function"}
101 |
102 | return {"code": str(decompiler)}
103 | except Exception as e:
104 | return {"error": str(e)}
105 |
106 |
107 | @mcp.tool()
108 | @execute_on_main_thread
109 | def get_function_name(ea: int) -> str:
110 | """Get function name at specified address.
111 |
112 | Args:
113 | ea: Effective address of the function
114 | """
115 | return ida_name.get_name(ea)
116 |
117 |
118 | @mcp.tool()
119 | @execute_on_main_thread
120 | def get_segments() -> List[Dict[str, Any]]:
121 | """Get all segments information.
122 |
123 | @return: List of segments (start, end, name, class, perm, bitness, align, comb, type, sel, flags)
124 | """
125 | segments = []
126 | n = 0
127 | seg = ida_segment.getnseg(n)
128 | while seg:
129 | segments.append(
130 | {
131 | "start": seg.start_ea,
132 | "end": seg.end_ea,
133 | "name": ida_segment.get_segm_name(seg),
134 | "class": ida_segment.get_segm_class(seg),
135 | "perm": seg.perm,
136 | "bitness": seg.bitness,
137 | "align": seg.align,
138 | "comb": seg.comb,
139 | "type": seg.type,
140 | "sel": seg.sel,
141 | "flags": seg.flags,
142 | }
143 | )
144 | n += 1
145 | seg = ida_segment.getnseg(n)
146 | return segments
147 |
148 |
149 | @mcp.tool()
150 | @execute_on_main_thread
151 | def get_functions() -> List[Dict[str, Any]]:
152 | """Get all functions in the binary."""
153 | functions = []
154 | for func_ea in idautils.Functions():
155 | func_name = ida_name.get_name(func_ea)
156 | functions.append({"address": func_ea, "name": func_name})
157 | return functions
158 |
159 |
160 | @mcp.tool()
161 | @execute_on_main_thread
162 | def get_xrefs_to(ea: int) -> List[Dict[str, Any]]:
163 | """Get all cross references to specified address.
164 |
165 | Args:
166 | ea: Effective address to find references to
167 | """
168 | xrefs = []
169 | for xref in idautils.XrefsTo(ea, 0):
170 | xrefs.append({"from": xref.frm, "type": xref.type})
171 | return xrefs
172 |
173 |
174 | @mcp.tool()
175 | @execute_on_main_thread
176 | def get_imports() -> dict[str, list[tuple[int, str, int]]]:
177 | """Get all imports in the binary.
178 |
179 | Args:
180 | None
181 |
182 | Returns:
183 | A dictionary where the keys are module names and the values are lists of tuples.
184 | Each tuple contains the address of the imported function, the name of the function,
185 | and the ordinal value of the function.
186 | """
187 | tree = {}
188 | nimps = idaapi.get_import_module_qty()
189 |
190 | for i in range(0, nimps):
191 | name = idaapi.get_import_module_name(i)
192 | if not name:
193 | continue
194 | # Create a list for imported names
195 | items = []
196 |
197 | def imports_names_cb(ea, name, ord):
198 | items.append((ea, "" if not name else name, ord))
199 | # True -> Continue enumeration
200 | return True
201 |
202 | # Enum imported entries in this module
203 | idaapi.enum_import_names(i, imports_names_cb)
204 |
205 | if name not in tree:
206 | tree[name] = []
207 | tree[name].extend(items)
208 |
209 | return tree
210 |
211 |
212 | @mcp.tool()
213 | @execute_on_main_thread
214 | def get_exports() -> List[Tuple[int, int, int, str]]:
215 | """Get all exports in the binary.
216 |
217 | @return: List of tuples (index, ordinal, ea, name)
218 | """
219 | return list(idautils.Entries())
220 |
221 |
222 | @mcp.tool()
223 | @execute_on_main_thread
224 | def get_entry_point() -> int:
225 | """Get the entry point of the binary."""
226 | try:
227 | import ida_ida
228 | return ida_ida.inf_get_start_ea()
229 | except (ImportError, AttributeError):
230 | try:
231 | # Alternative method: idc.get_inf_attr to get
232 | import idc
233 | return idc.get_inf_attr(idc.INF_START_EA)
234 | except (ImportError, AttributeError):
235 | # Last alternative method: use cvar.inf
236 | return idaapi.cvar.inf.start_ea
237 |
238 |
239 | @mcp.tool()
240 | @execute_on_main_thread
241 | def make_function(ea: int) -> None:
242 | """Make a function at specified address."""
243 | ida_funcs.add_func(ea)
244 |
245 |
246 | @mcp.tool()
247 | @execute_on_main_thread
248 | def undefine_function(ea: int) -> None:
249 | """Undefine a function at specified address."""
250 | ida_funcs.del_func(ea)
251 |
252 |
253 | @mcp.tool()
254 | @execute_on_main_thread
255 | def get_dword_at(ea: int) -> int:
256 | """Get the dword at specified address."""
257 | return idc.get_dword(ea)
258 |
259 |
260 | @mcp.tool()
261 | @execute_on_main_thread
262 | def get_word_at(ea: int) -> int:
263 | """Get the word at specified address."""
264 | return idc.get_word(ea)
265 |
266 |
267 | @mcp.tool()
268 | @execute_on_main_thread
269 | def get_byte_at(ea: int) -> int:
270 | """Get the byte at specified address."""
271 | return idc.get_byte(ea)
272 |
273 |
274 | @mcp.tool()
275 | @execute_on_main_thread
276 | def get_qword_at(ea: int) -> int:
277 | """Get the qword at specified address."""
278 | return idc.get_qword(ea)
279 |
280 |
281 | @mcp.tool()
282 | @execute_on_main_thread
283 | def get_float_at(ea: int) -> float:
284 | """Get the float at specified address."""
285 | return idc.get_float(ea)
286 |
287 |
288 | @mcp.tool()
289 | @execute_on_main_thread
290 | def get_double_at(ea: int) -> float:
291 | """Get the double at specified address."""
292 | return idc.get_double(ea)
293 |
294 |
295 | @mcp.tool()
296 | @execute_on_main_thread
297 | def get_string_at(ea: int) -> str:
298 | """Get the string at specified address."""
299 | return idc.get_strlit_contents(ea)
300 |
301 |
302 | @mcp.tool()
303 | @execute_on_main_thread
304 | def get_strings():
305 | strings = []
306 | for s in idautils.Strings():
307 | strings.append({"address": s.ea, "string": str(s)})
308 | return strings
309 |
310 | @mcp.tool()
311 | @execute_on_main_thread
312 | def get_current_file_path():
313 | return idc.get_input_file_path()
314 |
315 | @mcp.tool()
316 | @execute_on_main_thread
317 | def list_files_with_relative_path(relative_path: str = ""):
318 | base_dir = os.path.dirname(idc.get_input_file_path())
319 | if ':' in relative_path or '..' in relative_path or '//' in relative_path:
320 | return json.dumps({"error": "Invalid relative path"})
321 | if relative_path is None or relative_path == "":
322 | return glob.glob(os.path.join(base_dir, "*"))
323 | else:
324 | return glob.glob(os.path.join(base_dir, relative_path, "*"))
325 |
326 | @mcp.tool()
327 | @execute_on_main_thread
328 | def read_file(relative_path: str):
329 | base_dir = os.path.dirname(idc.get_input_file_path())
330 | if ':' in relative_path or '..' in relative_path or '//' in relative_path:
331 | return json.dumps({"error": "Invalid relative path"})
332 | if relative_path is "":
333 | return json.dumps({"error": "Relative path is required"})
334 | with open(os.path.join(base_dir, relative_path), "r") as f:
335 | return f.read()
336 |
337 | @mcp.tool()
338 | @execute_on_main_thread
339 | def write_file(relative_path: str, content: str):
340 | base_dir = os.path.dirname(idc.get_input_file_path())
341 | if ':' in relative_path or '..' in relative_path or '//' in relative_path:
342 | return json.dumps({"error": "Invalid relative path"})
343 | if relative_path is "":
344 | return json.dumps({"error": "Relative path is required"})
345 | with open(os.path.join(base_dir, relative_path), "w") as f:
346 | f.write(content)
347 |
348 | @mcp.tool()
349 | @execute_on_main_thread
350 | def read_binary(relative_path: str):
351 | base_dir = os.path.dirname(idc.get_input_file_path())
352 | if ':' in relative_path or '..' in relative_path or '//' in relative_path:
353 | return json.dumps({"error": "Invalid relative path"})
354 | if relative_path is "":
355 | return json.dumps({"error": "Relative path is required"})
356 | with open(os.path.join(base_dir, relative_path), "rb") as f:
357 | return f.read()
358 |
359 | @mcp.tool()
360 | @execute_on_main_thread
361 | def write_binary(relative_path: str , content: bytes):
362 | base_dir = os.path.dirname(idc.get_input_file_path())
363 | if ':' in relative_path or '..' in relative_path or '//' in relative_path:
364 | return json.dumps({"error": "Invalid relative path"})
365 | if relative_path is "":
366 | return json.dumps({"error": "Relative path is required"})
367 | with open(os.path.join(base_dir, relative_path), "wb") as f:
368 | f.write(content)
369 |
370 | @mcp.tool()
371 | @execute_on_main_thread
372 | def eval_pythoni(script: str):
373 | return eval(script)
374 |
375 | @mcp.tool()
376 | @execute_on_main_thread
377 | def get_instruction_length(address: int) -> int:
378 | """
379 | Retrieves the length (in bytes) of the instruction at the specified address.
380 |
381 | Args:
382 | address: The address of the instruction.
383 |
384 | Returns:
385 | The length (in bytes) of the instruction. Returns 0 if the instruction cannot be decoded.
386 | """
387 | try:
388 | # Create an insn_t object to store instruction information.
389 | insn = ida_ua.insn_t()
390 |
391 | # Decode the instruction.
392 | length = ida_ua.decode_insn(insn, address)
393 | if length == 0:
394 | print(f"Failed to decode instruction at address {hex(address)}")
395 | return 0
396 |
397 | return length
398 | except Exception as e:
399 | print(f"Error getting instruction length: {str(e)}")
400 | return 0
401 |
402 | @mcp.prompt()
403 | def binary_analysis_strategy() -> str:
404 | """
405 | Guild for analyzing the binary
406 | """
407 | return (
408 | "IDA Pro MCP Server Tools and Best Practices:\n\n."
409 | "Tools: \n"
410 | "- get_bytes: Get bytes at specified address.\n"
411 | "- get_disasm: Get disassembly at specified address.\n"
412 | "- get_decompiled_func: Get decompiled pseudocode of function containing address.\n"
413 | "- get_function_name: Get function name at specified address.\n"
414 | "- get_segments: Get all segments information.\n"
415 | "- get_functions: Get all functions in the binary.\n"
416 | "- get_xrefs_to: Get all cross references to a specified address.\n"
417 | "- get_imports: Get all imports in the binary.\n"
418 | "- get_exports: Get all exports in the binary.\n"
419 | "- get_entry_point: Get the entry point of the binary.\n"
420 | "- make_function: Make a function at specified address.\n"
421 | "- undefine_function: Undefine a function at specified address.\n"
422 | "- get_dword_at: Get the dword at specified address.\n"
423 | "- get_word_at: Get the word at specified address.\n"
424 | "- get_byte_at: Get the byte at specified address.\n"
425 | "- get_qword_at: Get the qword at specified address.\n"
426 | "- get_float_at: Get the float at specified address.\n"
427 | "- get_double_at: Get the double at specified address.\n"
428 | "- get_string_at: Get the string at specified address.\n"
429 | "- get_strings: Get all strings in the binary.\n"
430 | "- get_current_file_path: Get the current path of the binary.\n"
431 | "- list_files_with_relative_path: List all files in the specified relative path in the current directory.\n"
432 | "- read_file: Read the content of a file.\n"
433 | "- write_file: Write content to a file.\n"
434 | "- read_binary: Read the content of a binary file.\n"
435 | "- write_binary: Write content to a binary file.\n"
436 | "- eval_python: Evaluate a Python script in IDA Pro.\n"
437 | "- get_instruction_length: Get the length of the instruction at the specified address.\n"
438 | "Best Practices: \n"
439 | "- Initial Analysis Phase\n"
440 | " 1. Examine the Entry Point\n"
441 | " - Use the get_entry_point() tool to locate the program's entry point\n"
442 | " - Analyze the code at the entry point to understand the program's startup flow\n"
443 | " - Look for unusual instructions or jumps\n"
444 | " 2. Analyze Import Table\n"
445 | " - Use the get_imports() tool to view all imported functions\n"
446 | " - Look for suspicious API functions, such as:\n"
447 | " - File operations: CreateFile, WriteFile\n"
448 | " - Network communication: socket, connect, InternetOpen\n"
449 | " - Process manipulation: CreateProcess, VirtualAlloc\n"
450 | " - Registry operations: RegOpenKey, RegSetValue\n"
451 | " - Cryptography related: CryptEncrypt, CryptDecrypt\n"
452 | " 3. Review Strings\n"
453 | " - Use the get_strings() tool to obtain all strings\n"
454 | " - Pay attention to IP addresses, URLs, domain names, file paths\n"
455 | " - Look for encrypted or obfuscated string patterns\n"
456 | " - Analyze command line parameters and error messages\n"
457 | " 4. In-Depth Analysis Phase\n"
458 | " - Track Key API Calls\n"
459 | " - Use get_xrefs_to() to find cross-references to suspicious imported functions\n"
460 | " - Use get_decompiled_func() to analyze functions that call these APIs\n"
461 | " - Analyze how parameters and return values are handled\n"
462 | " 5. Identify Main Functional Blocks\n"
463 | " - Use get_functions() to get a list of all functions\n"
464 | " - Sort functions by size and complexity\n"
465 | " - Decompile and analyze large, complex functions\n"
466 | " - Look for suspicious function names or unnamed functions\n"
467 | " 6. Analyze Control Flow\n"
468 | " - Observe conditional branches and loop structures\n"
469 | " - Analyze function call graphs and execution paths\n"
470 | " - Look for anti-debugging and anti-VM detection techniques\n"
471 | " - Pay attention to unusual jumps and callback mechanisms\n"
472 | " 7. Identifying Malicious Behaviors\n"
473 | " - Identify Common Malicious Functionality\n"
474 | " - Persistence mechanisms: Registry modifications, startup items, service creation\n"
475 | " - Data theft: File searching, keylogging, screen capturing\n"
476 | " - Communication features: C&C communication, data exfiltration channels\n"
477 | " - Evasion techniques: Obfuscation, packing, anti-analysis checks\n"
478 | " - Destructive behaviors: File encryption, system damage\n"
479 | " 8. Analyze Algorithms and Encryption Routines\n"
480 | " - Identify encryption and decryption functions\n"
481 | " - Look for hardcoded keys and cryptographic constants\n"
482 | " - Analyze how data is processed in memory\n"
483 | " 9. Analyze Network Communication\n"
484 | " - Identify network communication functions\n"
485 | " - Look for IP addresses, URLs, and domain names\n"
486 | " - Analyze how data is sent and received over the network\n"
487 | " 10. Dump payloads if there is any decryption or encoding\n"
488 | " - Generate decryption or decodeing script in python and ida python\n"
489 | " - Use eval_python to execute the script in IDA Pro\n"
490 | " - Dump payloads in the current directory\n"
491 | " 11. Document Analysis Results\n"
492 | " - Add comments to key functions\n"
493 | " - Rename functions to reflect their actual functionality\n"
494 | " - Create a logical structure diagram of the code\n"
495 | " 12. Using Advanced Techniques\n"
496 | " - Use IDA Pro's advanced features like IDAPython scripting, IDA SDK, and IDA API\n"
497 | " - Implement custom analysis scripts to automate repetitive tasks\n"
498 | " - Explore IDA Pro's plugin ecosystem for additional analysis capabilities\n"
499 | )
500 |
501 |
502 | def create_starlette_app(mcp_server: Server, *, debug: bool = False) -> Starlette:
503 | """Create a Starlette application that can serve the provided mcp server with SSE."""
504 |
505 | middleware = [
506 | Middleware(
507 | CORSMiddleware,
508 | allow_origins=["*"],
509 | allow_methods=["*"],
510 | allow_headers=["*"],
511 | )
512 | ]
513 | return Starlette(
514 | debug=debug,
515 | middleware=middleware,
516 | routes=[
517 | Mount("/", app=mcp.sse_app()),
518 | ],
519 | )
520 |
521 |
522 | class ModelContextProtocolPlugin(ida_idaapi.plugin_t):
523 | flags = ida_idaapi.PLUGIN_FIX | ida_idaapi.PLUGIN_HIDE
524 | comment = "IDA Model Context Protocol Server"
525 | help = "Provides REST API and SSE for IDA Pro analysis"
526 | wanted_name = "IDA MCP Server"
527 | wanted_hotkey = ""
528 |
529 | def init(self):
530 | try:
531 | print("Initializing IDA Model Context Protocol Server...")
532 | # app = create_starlette_app(mcp, debug=True)
533 |
534 | def run_server():
535 | try:
536 | # 設置將異常轉換為 JSON 響應
537 | mcp.run(transport="sse")
538 | # uvicorn.run(app, host="localhost", port=3000, log_level="debug")
539 | except Exception as e:
540 | print(f"Server error: {str(e)}")
541 |
542 | server_thread = threading.Thread(target=run_server)
543 | server_thread.daemon = True
544 | server_thread.start()
545 | print("Server started successfully!")
546 | return ida_idaapi.PLUGIN_KEEP
547 | except Exception as e:
548 | print(f"Failed to start server: {str(e)}")
549 | return ida_idaapi.PLUGIN_SKIP
550 |
551 | def run(self, arg):
552 | pass
553 |
554 | def term(self):
555 | print("Terminating IDA Model Context Protocol Server...")
556 |
557 |
558 | def PLUGIN_ENTRY():
559 | return ModelContextProtocolPlugin()
560 |
561 |
562 | if __name__ == "__main__":
563 | PLUGIN_ENTRY()
564 |
```