Add testing and update README for JSON & testing
This commit is contained in:
parent
cbe5dcc1f3
commit
1e737ed44b
51
README.md
51
README.md
@ -254,6 +254,57 @@ View result from get_function from ghydra (local){
|
|||||||
Based on this analysis, I can see these binaries communicate using a simple protocol where...
|
Based on this analysis, I can see these binaries communicate using a simple protocol where...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# JSON Communication
|
||||||
|
|
||||||
|
GhydraMCP uses structured JSON for all communication between the Python bridge and Java plugin. This ensures consistent and reliable data exchange.
|
||||||
|
|
||||||
|
## Response Format
|
||||||
|
|
||||||
|
All responses follow a standard format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": "...",
|
||||||
|
"timestamp": 1712159482123,
|
||||||
|
"port": 8192,
|
||||||
|
"instanceType": "base"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Error responses include additional information:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "Error message",
|
||||||
|
"status_code": 404,
|
||||||
|
"timestamp": 1712159482123
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This structured approach makes the communication more reliable and easier to debug.
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
|
||||||
|
GhydraMCP includes comprehensive test suites for both the HTTP API and MCP bridge. See [TESTING.md](TESTING.md) for details on running the tests.
|
||||||
|
|
||||||
|
## HTTP API Tests
|
||||||
|
|
||||||
|
Tests the HTTP endpoints exposed by the Java plugin:
|
||||||
|
- Response format and structure
|
||||||
|
- JSON structure consistency
|
||||||
|
- Required fields in responses
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
## MCP Bridge Tests
|
||||||
|
|
||||||
|
Tests the MCP bridge functionality:
|
||||||
|
- MCP protocol communication
|
||||||
|
- Tool availability and structure
|
||||||
|
- Response format and structure
|
||||||
|
- JSON structure consistency
|
||||||
|
|
||||||
# Building from Source
|
# Building from Source
|
||||||
|
|
||||||
You can build different artifacts with Maven:
|
You can build different artifacts with Maven:
|
||||||
|
|||||||
154
TESTING.md
Normal file
154
TESTING.md
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
# Testing GhydraMCP
|
||||||
|
|
||||||
|
This document describes how to test the GhydraMCP plugin and bridge.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Python 3.11 or higher
|
||||||
|
- Ghidra with the GhydraMCP plugin installed and running
|
||||||
|
- The `requests` Python package (`pip install requests`)
|
||||||
|
|
||||||
|
## Running All Tests
|
||||||
|
|
||||||
|
The easiest way to run all tests is to use the test runner script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run_tests.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This will run both the HTTP API tests and the MCP bridge tests and provide a summary of the results.
|
||||||
|
|
||||||
|
You can also run specific test suites:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run only the HTTP API tests
|
||||||
|
python run_tests.py --http
|
||||||
|
|
||||||
|
# Run only the MCP bridge tests
|
||||||
|
python run_tests.py --mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
## HTTP API Tests
|
||||||
|
|
||||||
|
The `test_http_api.py` script tests the HTTP API exposed by the Java plugin. It verifies that the endpoints return the expected JSON structure and that the response format is consistent.
|
||||||
|
|
||||||
|
### Running the HTTP API Tests
|
||||||
|
|
||||||
|
1. Make sure Ghidra is running with the GhydraMCP plugin loaded
|
||||||
|
2. Run the tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python test_http_api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
The tests will automatically skip if Ghidra is not running or if the plugin is not responding.
|
||||||
|
|
||||||
|
### What's Being Tested
|
||||||
|
|
||||||
|
- Basic connectivity to the plugin
|
||||||
|
- Response format and structure
|
||||||
|
- JSON structure consistency
|
||||||
|
- Required fields in responses
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
## MCP Bridge Tests
|
||||||
|
|
||||||
|
The `test_mcp_client.py` script tests the MCP bridge functionality using the MCP client library. It verifies that the bridge responds correctly to MCP requests and that the response format is consistent.
|
||||||
|
|
||||||
|
### Running the MCP Bridge Tests
|
||||||
|
|
||||||
|
1. Make sure Ghidra is running with the GhydraMCP plugin loaded
|
||||||
|
2. Run the tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python test_mcp_client.py
|
||||||
|
```
|
||||||
|
|
||||||
|
The test script will:
|
||||||
|
1. Connect to the bridge using the MCP client
|
||||||
|
2. Initialize the session
|
||||||
|
3. List the available tools
|
||||||
|
4. Call the list_instances tool
|
||||||
|
5. Call the discover_instances tool
|
||||||
|
6. Call the list_functions tool
|
||||||
|
|
||||||
|
### What's Being Tested
|
||||||
|
|
||||||
|
- MCP protocol communication
|
||||||
|
- Tool availability and structure
|
||||||
|
- Response format and structure
|
||||||
|
- JSON structure consistency
|
||||||
|
- Required fields in responses
|
||||||
|
- Proper initialization of the MCP session
|
||||||
|
- Ability to call tools and receive responses
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### HTTP API Tests
|
||||||
|
|
||||||
|
- If tests are skipped with "Ghidra server not running or not accessible", make sure Ghidra is running and the GhydraMCP plugin is loaded.
|
||||||
|
- If tests fail with connection errors, check that the plugin is listening on the expected port (default: 8192).
|
||||||
|
|
||||||
|
### MCP Bridge Tests
|
||||||
|
|
||||||
|
- If tests are skipped with "Failed to start MCP bridge process", check that the bridge script is executable and that all dependencies are installed.
|
||||||
|
- If tests fail with JSON parsing errors, check that the bridge is responding with valid JSON.
|
||||||
|
|
||||||
|
## Adding New Tests
|
||||||
|
|
||||||
|
### HTTP API Tests
|
||||||
|
|
||||||
|
To add a new test for an HTTP endpoint:
|
||||||
|
|
||||||
|
1. Add a new test method to the `GhydraMCPHttpApiTests` class
|
||||||
|
2. Use the `requests` library to make HTTP requests to the endpoint
|
||||||
|
3. Verify the response using assertions
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def test_new_endpoint(self):
|
||||||
|
"""Test the /new_endpoint endpoint"""
|
||||||
|
response = requests.get(f"{BASE_URL}/new_endpoint")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Verify response is valid JSON
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Check required fields in the standard response format
|
||||||
|
self.assertIn("success", data)
|
||||||
|
self.assertTrue(data["success"])
|
||||||
|
self.assertIn("timestamp", data)
|
||||||
|
self.assertIn("port", data)
|
||||||
|
```
|
||||||
|
|
||||||
|
### MCP Bridge Tests
|
||||||
|
|
||||||
|
To add a new test for an MCP tool:
|
||||||
|
|
||||||
|
1. Add a new test method to the `MCPBridgeTests` class
|
||||||
|
2. Use the `send_mcp_request` method to send an MCP request to the bridge
|
||||||
|
3. Verify the response using assertions
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def test_new_tool(self):
|
||||||
|
"""Test the new_tool tool"""
|
||||||
|
response = self.send_mcp_request("call_tool", {
|
||||||
|
"name": "new_tool",
|
||||||
|
"arguments": {
|
||||||
|
"param1": "value1",
|
||||||
|
"param2": "value2"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Check basic response structure
|
||||||
|
self.assertIn("result", response)
|
||||||
|
self.assertIn("content", response["result"])
|
||||||
|
|
||||||
|
# Parse the content
|
||||||
|
content = response["result"]["content"]
|
||||||
|
self.assertIsInstance(content, list)
|
||||||
|
self.assertGreaterEqual(len(content), 1)
|
||||||
|
```
|
||||||
116
run_tests.py
Normal file
116
run_tests.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test runner for GhydraMCP tests.
|
||||||
|
This script runs both the HTTP API tests and the MCP bridge tests.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
import time
|
||||||
|
|
||||||
|
def print_header(text):
|
||||||
|
"""Print a header with the given text"""
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print(f" {text} ".center(80, "="))
|
||||||
|
print("=" * 80 + "\n")
|
||||||
|
|
||||||
|
def run_http_api_tests():
|
||||||
|
"""Run the HTTP API tests"""
|
||||||
|
print_header("Running HTTP API Tests")
|
||||||
|
|
||||||
|
# Import and run the tests
|
||||||
|
try:
|
||||||
|
from test_http_api import GhydraMCPHttpApiTests
|
||||||
|
|
||||||
|
# Create a test suite with all tests from GhydraMCPHttpApiTests
|
||||||
|
suite = unittest.TestLoader().loadTestsFromTestCase(GhydraMCPHttpApiTests)
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
result = unittest.TextTestRunner(verbosity=2).run(suite)
|
||||||
|
|
||||||
|
return result.wasSuccessful()
|
||||||
|
except ImportError:
|
||||||
|
print("Error: Could not import test_http_api.py")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error running HTTP API tests: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run_mcp_bridge_tests():
|
||||||
|
"""Run the MCP bridge tests using the MCP client"""
|
||||||
|
print_header("Running MCP Bridge Tests")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Run the MCP client test script
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
print("Running MCP client test script...")
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, "test_mcp_client.py"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Print the output
|
||||||
|
if result.stdout:
|
||||||
|
print("STDOUT:")
|
||||||
|
print(result.stdout)
|
||||||
|
|
||||||
|
if result.stderr:
|
||||||
|
print("STDERR:")
|
||||||
|
print(result.stderr)
|
||||||
|
|
||||||
|
# Return True if the process exited with code 0
|
||||||
|
return result.returncode == 0
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error running MCP bridge tests: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run_all_tests():
|
||||||
|
"""Run all tests"""
|
||||||
|
print_header("GhydraMCP Test Suite")
|
||||||
|
|
||||||
|
# Run the HTTP API tests
|
||||||
|
http_api_success = run_http_api_tests()
|
||||||
|
|
||||||
|
# Run the MCP bridge tests
|
||||||
|
mcp_bridge_success = run_mcp_bridge_tests()
|
||||||
|
|
||||||
|
# Print a summary
|
||||||
|
print_header("Test Summary")
|
||||||
|
print(f"HTTP API Tests: {'PASSED' if http_api_success else 'FAILED'}")
|
||||||
|
print(f"MCP Bridge Tests: {'PASSED' if mcp_bridge_success else 'FAILED'}")
|
||||||
|
print(f"Overall: {'PASSED' if http_api_success and mcp_bridge_success else 'FAILED'}")
|
||||||
|
|
||||||
|
# Return True if all tests passed, False otherwise
|
||||||
|
return http_api_success and mcp_bridge_success
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Check if we have the required dependencies
|
||||||
|
try:
|
||||||
|
import requests
|
||||||
|
except ImportError:
|
||||||
|
print("Error: The 'requests' package is required to run the tests.")
|
||||||
|
print("Please install it with 'pip install requests'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
if sys.argv[1] == "--http":
|
||||||
|
# Run only the HTTP API tests
|
||||||
|
success = run_http_api_tests()
|
||||||
|
elif sys.argv[1] == "--mcp":
|
||||||
|
# Run only the MCP bridge tests
|
||||||
|
success = run_mcp_bridge_tests()
|
||||||
|
else:
|
||||||
|
print(f"Unknown argument: {sys.argv[1]}")
|
||||||
|
print("Usage: python run_tests.py [--http|--mcp]")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
# Run all tests
|
||||||
|
success = run_all_tests()
|
||||||
|
|
||||||
|
# Exit with the appropriate status code
|
||||||
|
sys.exit(0 if success else 1)
|
||||||
153
test_http_api.py
Normal file
153
test_http_api.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test script for the GhydraMCP HTTP API.
|
||||||
|
This script tests the HTTP endpoints of the Java plugin.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
# Default Ghidra server port
|
||||||
|
DEFAULT_PORT = 8192
|
||||||
|
BASE_URL = f"http://localhost:{DEFAULT_PORT}"
|
||||||
|
|
||||||
|
class GhydraMCPHttpApiTests(unittest.TestCase):
|
||||||
|
"""Test cases for the GhydraMCP HTTP API"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Setup before each test"""
|
||||||
|
# Check if the server is running
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{BASE_URL}/info", timeout=2)
|
||||||
|
if response.status_code != 200:
|
||||||
|
self.skipTest("Ghidra server not running or not responding")
|
||||||
|
except requests.exceptions.RequestException:
|
||||||
|
self.skipTest("Ghidra server not running or not accessible")
|
||||||
|
|
||||||
|
def test_info_endpoint(self):
|
||||||
|
"""Test the /info endpoint"""
|
||||||
|
response = requests.get(f"{BASE_URL}/info")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Verify response is valid JSON
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Check required fields
|
||||||
|
self.assertIn("port", data)
|
||||||
|
self.assertIn("isBaseInstance", data)
|
||||||
|
self.assertIn("project", data)
|
||||||
|
self.assertIn("file", data)
|
||||||
|
|
||||||
|
def test_root_endpoint(self):
|
||||||
|
"""Test the / endpoint"""
|
||||||
|
response = requests.get(BASE_URL)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Verify response is valid JSON
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Check required fields
|
||||||
|
self.assertIn("port", data)
|
||||||
|
self.assertIn("isBaseInstance", data)
|
||||||
|
self.assertIn("project", data)
|
||||||
|
self.assertIn("file", data)
|
||||||
|
|
||||||
|
def test_instances_endpoint(self):
|
||||||
|
"""Test the /instances endpoint"""
|
||||||
|
response = requests.get(f"{BASE_URL}/instances")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Verify response is valid JSON
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Check required fields in the standard response format
|
||||||
|
self.assertIn("success", data)
|
||||||
|
self.assertTrue(data["success"])
|
||||||
|
self.assertIn("timestamp", data)
|
||||||
|
self.assertIn("port", data)
|
||||||
|
|
||||||
|
# Check that we have either result or data
|
||||||
|
self.assertTrue("result" in data or "data" in data)
|
||||||
|
|
||||||
|
def test_functions_endpoint(self):
|
||||||
|
"""Test the /functions endpoint"""
|
||||||
|
response = requests.get(f"{BASE_URL}/functions")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Verify response is valid JSON
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Check required fields in the standard response format
|
||||||
|
self.assertIn("success", data)
|
||||||
|
self.assertTrue(data["success"])
|
||||||
|
self.assertIn("timestamp", data)
|
||||||
|
self.assertIn("port", data)
|
||||||
|
|
||||||
|
# Check that we have either result or data
|
||||||
|
self.assertTrue("result" in data or "data" in data)
|
||||||
|
|
||||||
|
def test_functions_with_pagination(self):
|
||||||
|
"""Test the /functions endpoint with pagination"""
|
||||||
|
response = requests.get(f"{BASE_URL}/functions?offset=0&limit=5")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Verify response is valid JSON
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Check required fields in the standard response format
|
||||||
|
self.assertIn("success", data)
|
||||||
|
self.assertTrue(data["success"])
|
||||||
|
self.assertIn("timestamp", data)
|
||||||
|
self.assertIn("port", data)
|
||||||
|
|
||||||
|
def test_classes_endpoint(self):
|
||||||
|
"""Test the /classes endpoint"""
|
||||||
|
response = requests.get(f"{BASE_URL}/classes")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Verify response is valid JSON
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Check required fields in the standard response format
|
||||||
|
self.assertIn("success", data)
|
||||||
|
self.assertTrue(data["success"])
|
||||||
|
self.assertIn("timestamp", data)
|
||||||
|
self.assertIn("port", data)
|
||||||
|
|
||||||
|
def test_segments_endpoint(self):
|
||||||
|
"""Test the /segments endpoint"""
|
||||||
|
response = requests.get(f"{BASE_URL}/segments")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Verify response is valid JSON
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Check required fields in the standard response format
|
||||||
|
self.assertIn("success", data)
|
||||||
|
self.assertTrue(data["success"])
|
||||||
|
self.assertIn("timestamp", data)
|
||||||
|
self.assertIn("port", data)
|
||||||
|
|
||||||
|
def test_variables_endpoint(self):
|
||||||
|
"""Test the /variables endpoint"""
|
||||||
|
response = requests.get(f"{BASE_URL}/variables")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# Verify response is valid JSON
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Check required fields in the standard response format
|
||||||
|
self.assertIn("success", data)
|
||||||
|
self.assertTrue(data["success"])
|
||||||
|
self.assertIn("timestamp", data)
|
||||||
|
self.assertIn("port", data)
|
||||||
|
|
||||||
|
def test_error_handling(self):
|
||||||
|
"""Test error handling for non-existent endpoints"""
|
||||||
|
response = requests.get(f"{BASE_URL}/nonexistent_endpoint")
|
||||||
|
# This should return 404, but some servers might return other codes
|
||||||
|
self.assertNotEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
71
test_mcp_client.py
Normal file
71
test_mcp_client.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#!/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}")
|
||||||
|
|
||||||
|
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()
|
||||||
Loading…
x
Reference in New Issue
Block a user