Some checks are pending
Build Ghidra Plugin / build (push) Waiting to run
Allows AI clients to submit feedback about tool quality, report issues, and track statistics. Persists to ~/.ghydramcp/feedback.db (SQLite). - Add fastmcp-feedback dependency - Add feedback_enabled / feedback_db_path config fields - Wire add_feedback_tools() into create_server() with graceful fallback - Show feedback path in startup banner Disable with GHYDRA_FEEDBACK=false
130 lines
3.7 KiB
Python
130 lines
3.7 KiB
Python
"""Configuration management for GhydraMCP.
|
|
|
|
Handles environment variables, default settings, and runtime configuration.
|
|
"""
|
|
|
|
import os
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
|
|
@dataclass
|
|
class DockerConfig:
|
|
"""Docker-specific configuration."""
|
|
|
|
# Docker image settings
|
|
image_name: str = "ghydramcp"
|
|
image_tag: str = field(default_factory=lambda: os.environ.get("GHYDRAMCP_VERSION", "latest"))
|
|
|
|
# Default container settings
|
|
default_port: int = field(default_factory=lambda: int(os.environ.get("GHYDRA_PORT", "8192")))
|
|
default_memory: str = field(default_factory=lambda: os.environ.get("GHYDRA_MAXMEM", "2G"))
|
|
|
|
# Project directory (for building)
|
|
project_dir: Optional[Path] = None
|
|
|
|
# Auto-start settings
|
|
auto_start_enabled: bool = field(default_factory=lambda: os.environ.get("GHYDRA_DOCKER_AUTO", "false").lower() == "true")
|
|
auto_start_wait: bool = True
|
|
auto_start_timeout: float = 300.0
|
|
|
|
|
|
# Docker configuration instance
|
|
_docker_config: Optional[DockerConfig] = None
|
|
|
|
|
|
def get_docker_config() -> DockerConfig:
|
|
"""Get the Docker configuration instance."""
|
|
global _docker_config
|
|
if _docker_config is None:
|
|
_docker_config = DockerConfig()
|
|
return _docker_config
|
|
|
|
|
|
def set_docker_config(config: DockerConfig) -> None:
|
|
"""Set the Docker configuration instance."""
|
|
global _docker_config
|
|
_docker_config = config
|
|
|
|
|
|
@dataclass
|
|
class GhydraConfig:
|
|
"""Configuration for GhydraMCP server."""
|
|
|
|
# Ghidra connection settings
|
|
ghidra_host: str = field(default_factory=lambda: os.environ.get("GHIDRA_HOST", "localhost"))
|
|
default_port: Optional[int] = None
|
|
|
|
# Port scanning ranges for instance discovery
|
|
quick_discovery_range: range = field(default_factory=lambda: range(18489, 18499))
|
|
full_discovery_range: range = field(default_factory=lambda: range(18400, 18600))
|
|
|
|
# HTTP client settings
|
|
request_timeout: float = 30.0
|
|
discovery_timeout: float = 0.5
|
|
|
|
# Pagination defaults
|
|
default_page_size: int = 50
|
|
max_page_size: int = 500
|
|
|
|
# Cursor management
|
|
cursor_ttl_seconds: int = 300 # 5 minutes
|
|
max_cursors_per_session: int = 100
|
|
|
|
# Response size limits (for return_all guard)
|
|
max_response_tokens: int = 8000 # Hard budget — guard triggers above this
|
|
large_response_threshold: int = 4000 # Warn above this in normal pagination
|
|
|
|
# Expected API version
|
|
expected_api_version: int = 2
|
|
|
|
# Feedback collection
|
|
feedback_enabled: bool = field(
|
|
default_factory=lambda: os.environ.get("GHYDRA_FEEDBACK", "true").lower() == "true"
|
|
)
|
|
feedback_db_path: str = field(
|
|
default_factory=lambda: os.environ.get(
|
|
"GHYDRA_FEEDBACK_DB",
|
|
str(Path.home() / ".ghydramcp" / "feedback.db"),
|
|
)
|
|
)
|
|
|
|
# Resource caps for enumeration endpoints
|
|
resource_caps: dict = field(default_factory=lambda: {
|
|
"functions": 1000,
|
|
"strings": 500,
|
|
"data": 1000,
|
|
"structs": 500,
|
|
"xrefs": 500,
|
|
})
|
|
|
|
def __post_init__(self):
|
|
"""Validate configuration after initialization."""
|
|
if self.default_page_size > self.max_page_size:
|
|
self.default_page_size = self.max_page_size
|
|
|
|
|
|
# Global configuration instance (can be replaced for testing)
|
|
_config: Optional[GhydraConfig] = None
|
|
|
|
|
|
def get_config() -> GhydraConfig:
|
|
"""Get the global configuration instance."""
|
|
global _config
|
|
if _config is None:
|
|
_config = GhydraConfig()
|
|
return _config
|
|
|
|
|
|
def set_config(config: GhydraConfig) -> None:
|
|
"""Set the global configuration instance."""
|
|
global _config
|
|
_config = config
|
|
|
|
|
|
def reset_config() -> None:
|
|
"""Reset to default configuration."""
|
|
global _config
|
|
_config = None
|