186 lines
7.6 KiB
Python
186 lines
7.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Test script for the GhydraMCP bridge using the MCP client.
|
|
This script tests the bridge by sending MCP requests and handling responses.
|
|
"""
|
|
import asyncio
|
|
import logging
|
|
import sys
|
|
from typing import Any
|
|
|
|
import anyio
|
|
|
|
from mcp.client.session import ClientSession
|
|
from mcp.client.stdio import StdioServerParameters, stdio_client
|
|
|
|
# Set up logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger("mcp_client_test")
|
|
|
|
async def test_bridge():
|
|
"""Test the bridge using the MCP client"""
|
|
# Configure the server parameters
|
|
server_parameters = StdioServerParameters(
|
|
command=sys.executable,
|
|
args=["bridge_mcp_hydra.py"],
|
|
)
|
|
|
|
# Connect to the bridge
|
|
logger.info("Connecting to bridge...")
|
|
async with stdio_client(server_parameters) as (read_stream, write_stream):
|
|
# Create a session
|
|
logger.info("Creating session...")
|
|
async with ClientSession(read_stream, write_stream) as session:
|
|
# Initialize the session
|
|
logger.info("Initializing session...")
|
|
init_result = await session.initialize()
|
|
logger.info(f"Initialization result: {init_result}")
|
|
|
|
# List tools
|
|
logger.info("Listing tools...")
|
|
tools_result = await session.list_tools()
|
|
logger.info(f"Tools result: {tools_result}")
|
|
|
|
# Call the list_instances tool
|
|
logger.info("Calling list_instances tool...")
|
|
list_instances_result = await session.call_tool("list_instances")
|
|
logger.info(f"List instances result: {list_instances_result}")
|
|
|
|
# Call the discover_instances tool
|
|
logger.info("Calling discover_instances tool...")
|
|
discover_instances_result = await session.call_tool("discover_instances")
|
|
logger.info(f"Discover instances result: {discover_instances_result}")
|
|
|
|
# Call the list_functions tool
|
|
logger.info("Calling list_functions tool...")
|
|
list_functions_result = await session.call_tool(
|
|
"list_functions",
|
|
arguments={"port": 8192, "offset": 0, "limit": 5}
|
|
)
|
|
logger.info(f"List functions result: {list_functions_result}")
|
|
|
|
# Test mutating operations by changing and reverting
|
|
logger.info("Testing mutating operations...")
|
|
|
|
try:
|
|
# Get a known function to test with from list_functions result
|
|
list_funcs = await session.call_tool(
|
|
"list_functions",
|
|
arguments={"port": 8192, "offset": 0, "limit": 5}
|
|
)
|
|
|
|
if not hasattr(list_funcs, "result") or not hasattr(list_funcs.result, "content") or not list_funcs.result.content:
|
|
logger.warning("No functions found - skipping mutating tests")
|
|
return
|
|
|
|
# The list_functions result contains a JSON string in the text field
|
|
func_json = list_funcs.result.content[0].get("text", "")
|
|
if not func_json:
|
|
logger.warning("No function data found - skipping mutating tests")
|
|
return
|
|
|
|
try:
|
|
# Parse the JSON to get the function list
|
|
func_data = json.loads(func_json)
|
|
func_list = func_data.get("result", "").split("\n")
|
|
if not func_list:
|
|
logger.warning("No functions in result - skipping mutating tests")
|
|
return
|
|
|
|
# Extract first function name (format: "name @ address")
|
|
func_name = func_list[0].split("@")[0].strip()
|
|
except (json.JSONDecodeError, AttributeError) as e:
|
|
logger.warning(f"Error parsing function data: {e} - skipping mutating tests")
|
|
return
|
|
if not func_name:
|
|
logger.warning("Could not parse function name - skipping mutating tests")
|
|
return
|
|
|
|
# Get full function details
|
|
func_details = await session.call_tool(
|
|
"get_function",
|
|
arguments={"port": 8192, "name": func_name}
|
|
)
|
|
|
|
if not hasattr(func_details, "result") or not hasattr(func_details.result, "content") or not func_details.result.content:
|
|
logger.warning("Could not get function details - skipping mutating tests")
|
|
return
|
|
|
|
func_content = func_details.result.content[0]
|
|
func_name = func_content.get("text", "").split("\n")[0]
|
|
func_address = func_content.get("address", "")
|
|
|
|
if not func_name or not func_address:
|
|
logger.warning("Could not get valid function name/address - skipping mutating tests")
|
|
return
|
|
|
|
# Test function renaming
|
|
original_name = func_name
|
|
test_name = f"{func_name}_test"
|
|
|
|
# Rename to test name
|
|
rename_result = await session.call_tool(
|
|
"update_function",
|
|
arguments={"port": 8192, "name": original_name, "new_name": test_name}
|
|
)
|
|
logger.info(f"Rename result: {rename_result}")
|
|
|
|
# Verify rename
|
|
renamed_func = await session.call_tool(
|
|
"get_function",
|
|
arguments={"port": 8192, "name": test_name}
|
|
)
|
|
logger.info(f"Renamed function result: {renamed_func}")
|
|
|
|
# Rename back to original
|
|
revert_result = await session.call_tool(
|
|
"update_function",
|
|
arguments={"port": 8192, "name": test_name, "new_name": original_name}
|
|
)
|
|
logger.info(f"Revert rename result: {revert_result}")
|
|
|
|
# Verify revert
|
|
original_func = await session.call_tool(
|
|
"get_function",
|
|
arguments={"port": 8192, "name": original_name}
|
|
)
|
|
logger.info(f"Original function result: {original_func}")
|
|
|
|
# Test adding/removing comment
|
|
test_comment = "Test comment from MCP client"
|
|
comment_result = await session.call_tool(
|
|
"set_decompiler_comment",
|
|
arguments={
|
|
"port": 8192,
|
|
"address": func_address,
|
|
"comment": test_comment
|
|
}
|
|
)
|
|
logger.info(f"Add comment result: {comment_result}")
|
|
|
|
# Remove comment
|
|
remove_comment_result = await session.call_tool(
|
|
"set_decompiler_comment",
|
|
arguments={
|
|
"port": 8192,
|
|
"address": func_address,
|
|
"comment": ""
|
|
}
|
|
)
|
|
logger.info(f"Remove comment result: {remove_comment_result}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error testing mutating operations: {e}")
|
|
raise
|
|
|
|
def main():
|
|
"""Main entry point"""
|
|
try:
|
|
anyio.run(test_bridge)
|
|
except Exception as e:
|
|
logger.error(f"Error: {e}")
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|