poespas/hypernode-mcp-server
If you are the rightful owner of hypernode-mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.
The Hypernode MCP Server is a comprehensive solution for managing Hypernode environments, offering tools for vhost management, incident handling, attack blocking, and nginx log analysis.
list_vhosts
List all vhosts configured on the Hypernode with their settings
modify_vhost
Modify vhost configurations (create, update, delete, SSL settings, etc.)
list_incidents
List all incidents in the ~/incidents directory
get_incident
Get files from a specific incident directory ~/incidents/[date]/*
list_attacks
List all available attack blocking options from hypernode-systemctl block_attack
block_attack
Block a specific attack using hypernode-systemctl block_attack
analyze_nginx_logs
Analyze nginx logs using pnl with filters, date ranges, and field selection
execute_shell_command
Execute shell commands with safety restrictions (dangerous commands like rm are blocked)
Hypernode MCP Server
A Model Context Protocol (MCP) server for managing Hypernode environments with comprehensive tools for vhost management, incident handling, attack blocking, and nginx log analysis.
Features
- VHost Management: List and modify vhost configurations
- Incident Management: List and retrieve incident files
- Attack Blocking: List and block various types of attacks
- Nginx Log Analysis: Analyze logs with filters and date ranges
- Shell Command Execution: Safe command execution with restrictions
- Multiple Transport Support: stdio, HTTP, and SSE (Server-Sent Events)
Installation
- Install dependencies:
pip install -r requirements.txt
- Run the server:
python server.py
The server will start with:
- stdio transport for MCP clients
- HTTP server on port 8000
- SSE server on port 8001
Available Tools
VHost Management
List VHosts
Lists all configured vhosts with their settings.
{
"name": "list_vhosts",
"description": "List all vhosts configured on the Hypernode with their settings"
}
Response:
{
"success": true,
"vhosts": {
"example.hypernode.io": {
"default_server": false,
"force_https": true,
"https": true,
"ssl_config": "intermediate",
"type": "magento2",
"varnish": false
}
},
"count": 1
}
Modify VHost
Modify vhost configurations (create, update, delete, SSL settings, etc.).
{
"name": "modify_vhost",
"description": "Modify vhost configurations (create, update, delete, SSL settings, etc.)",
"arguments": {
"servername": "example.hypernode.io",
"action": "create",
"vhost_type": "magento2",
"https": true,
"force_https": true,
"varnish": false
}
}
Incident Management
List Incidents
List all incidents in the ~/incidents directory.
{
"name": "list_incidents",
"description": "List all incidents in the ~/incidents directory"
}
Get Incident
Retrieve files from a specific incident directory.
{
"name": "get_incident",
"description": "Get files from a specific incident directory ~/incidents/[date]/*",
"arguments": {
"date": "2024-01-15",
"file_pattern": "*.log"
}
}
Attack Blocking
List Attacks
List all available attack blocking options.
{
"name": "list_attacks",
"description": "List all available attack blocking options from hypernode-systemctl block_attack"
}
Block Attack
Block a specific attack type.
{
"name": "block_attack",
"description": "Block a specific attack using hypernode-systemctl block_attack",
"arguments": {
"attack_name": "BlockChinaBruteForce"
}
}
Nginx Log Analysis
Analyze Nginx Logs
Analyze nginx logs with various filters and options.
{
"name": "analyze_nginx_logs",
"description": "Analyze nginx logs using pnl with filters, date ranges, and field selection",
"arguments": {
"date_range": "today",
"fields": "ip,status,request,user_agent",
"filters": ["status=404", "ip~192.168"],
"php_only": true,
"limit": 100
}
}
Available Fields:
remote_user
,ssl_protocol
,referer
,user_agent
remote_addr
,ssl_cipher
,body_bytes_sent
,country
status
,time
,request_time
,port
,request
server_name
,host
,handler
Filter Examples:
status=404
- Exact matchip~192.168
- Regex matchuser_agent!~bot
- Negative regex match
Shell Command Execution
Execute Shell Command
Execute shell commands with safety restrictions.
{
"name": "execute_shell_command",
"description": "Execute shell commands with safety restrictions (dangerous commands like rm are blocked)",
"arguments": {
"command": "ls -la /var/log",
"timeout": 30,
"working_directory": "/tmp"
}
}
Blocked Commands:
rm
,rmdir
,del
,format
,mkfs
,dd
,shred
kill
,killall
,pkill
,halt
,shutdown
,reboot
Usage Examples
Using with MCP Client
- Start the server:
python server.py
- Connect with an MCP client:
# Example with stdio transport
python server.py | mcp-client
Using with HTTP API
The server exposes an HTTP API on port 8000:
# List vhosts
curl -X POST http://localhost:8000/tools/list_vhosts \
-H "Content-Type: application/json" \
-d '{}'
# Analyze nginx logs
curl -X POST http://localhost:8000/tools/analyze_nginx_logs \
-H "Content-Type: application/json" \
-d '{
"date_range": "today",
"fields": "ip,status,request",
"filters": ["status=404"]
}'
Using with SSE (Server-Sent Events)
The server supports SSE on port 8001 for streaming responses:
const eventSource = new EventSource('http://localhost:8001/stream');
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
Security Features
- Command Validation: Dangerous commands are blocked
- Input Sanitization: All inputs are validated
- Timeout Protection: Commands have configurable timeouts
- Error Handling: Comprehensive error handling and logging
- Audit Logging: All command executions are logged
Configuration
The server can be configured through environment variables:
MCP_HTTP_HOST
: HTTP server host (default: 0.0.0.0)MCP_HTTP_PORT
: HTTP server port (default: 8000)MCP_SSE_HOST
: SSE server host (default: 0.0.0.0)MCP_SSE_PORT
: SSE server port (default: 8001)MCP_LOG_LEVEL
: Logging level (default: INFO)
Development
Project Structure
.
āāā server.py # Main server entry point
āāā requirements.txt # Python dependencies
āāā README.md # This file
āāā .cursorrules # Coding standards
āāā utils/ # Shared utilities
ā āāā __init__.py
ā āāā command_executor.py # Command execution utilities
āāā tools/ # MCP tools
āāā __init__.py
āāā vhosts/ # VHost management tools
ā āāā __init__.py
ā āāā list.py
ā āāā modify.py
āāā incidents/ # Incident management tools
ā āāā __init__.py
ā āāā list.py
ā āāā get.py
āāā block_attack/ # Attack blocking tools
ā āāā __init__.py
ā āāā list.py
ā āāā block.py
āāā nginx_logs/ # Nginx log analysis tools
ā āāā __init__.py
ā āāā analyze.py
āāā shell/ # Shell command tools
āāā __init__.py
āāā execute.py
Creating New Tools
The Hypernode MCP Server uses a custom tool system that automatically discovers and registers tools. Here's how to create new tools:
Tool Structure
- Create a new tool file in the appropriate directory under
tools/
- Inherit from
BaseTool
(fromtools.generic
) - Implement methods with
tool_
prefix - these become the actual MCP tools - Use the tool registry for automatic registration
Example: Hello World Tool
Here's a complete example based on tools/hello_world.py
:
"""
Hello World tool for Hypernode MCP Server.
"""
from typing import Dict, Any
from .generic import BaseTool, tool_registry
class HelloWorldTool(BaseTool):
"""Hello World tool implementation."""
async def tool_hello_world(self) -> Dict[str, Any]:
"""
Simple hello world tool for testing.
Returns:
Dict containing a hello world message
"""
return {
"message": "Hello World from Hypernode MCP Server!",
"status": "success"
}
# Create and register the tool instance automatically
hello_world_tool = HelloWorldTool()
tool_registry.register_tool(hello_world_tool)
Key Components
-
BaseTool Inheritance: All tools inherit from
BaseTool
which provides:- Automatic MCP registration
- Logging setup
- Common functionality
-
Method Naming Convention:
- Methods starting with
tool_
become MCP tools - The tool name is the method name without the
tool_
prefix - Example:
tool_hello_world()
becomes thehello_world
tool
- Methods starting with
-
Return Format: Tools should return
Dict[str, Any]
with:success
: Boolean indicating success/failureerror
: Error message if applicable- Other relevant data
-
Automatic Registration:
- Create a tool instance at module level
- Register it with
tool_registry.register_tool(tool_instance)
- The registry automatically discovers and registers all tools
Advanced Tool Example
Here's a more complex example with command execution:
"""
Example tool that executes shell commands.
"""
from typing import Dict, Any, Optional
from .generic import BaseTool, tool_registry
from utils.command_executor import CommandExecutor
class ExampleTool(BaseTool):
"""Example tool with command execution."""
async def tool_example_command(self, command: str, timeout: int = 30) -> Dict[str, Any]:
"""
Execute a shell command with timeout.
Args:
command: The shell command to execute
timeout: Command timeout in seconds
Returns:
Dict containing command execution results
"""
try:
result = await CommandExecutor.execute_command(command, timeout=timeout)
return {
"success": result.success,
"stdout": result.stdout,
"stderr": result.stderr,
"return_code": result.return_code,
"command": command
}
except Exception as e:
self.logger.error(f"Error executing command: {e}")
return {
"success": False,
"error": str(e),
"command": command
}
async def tool_example_json(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""
Process JSON data.
Args:
data: Input data dictionary
Returns:
Dict containing processed data
"""
return {
"success": True,
"processed_data": data,
"timestamp": "2024-01-01T00:00:00Z"
}
# Create and register the tool instance
example_tool = ExampleTool()
tool_registry.register_tool(example_tool)
Tool Organization and Folder Structure
Tools should be organized in appropriate directories following a clear categorization system. Each category should have its own directory with related tools grouped together.
Current Tool Categories and Structure
Based on the tools currently in server.py
, here's the planned folder structure:
tools/
āāā __init__.py # Auto-registration logic
āāā generic.py # BaseTool and registry
āāā hello_world.py # Simple example tool
āāā vhosts/ # VHost management tools
ā āāā __init__.py
ā āāā list.py # list_vhosts tool
ā āāā modify.py # modify_vhost tool
āāā incidents/ # Incident management tools
ā āāā __init__.py
ā āāā list.py # list_incidents tool
ā āāā get.py # get_incident tool
āāā block_attack/ # Attack blocking tools
ā āāā __init__.py
ā āāā list.py # list_attacks tool
ā āāā block.py # block_attack tool
āāā nginx_logs/ # Nginx log analysis tools
ā āāā __init__.py
ā āāā analyze.py # analyze_nginx_logs tool
ā āāā fields.py # analyze_nginx_logs_fields tool
āāā shell/ # Shell command tools
āāā __init__.py
āāā execute.py # execute_shell_command tool
Tool Migration Plan
The following tools from server.py
will be migrated to their respective directories:
Tool Function | Category | Target File | Tool Method Name |
---|---|---|---|
list_vhosts() | VHosts | tools/vhosts/list.py | tool_list_vhosts() |
modify_vhost() | VHosts | tools/vhosts/modify.py | tool_modify_vhost() |
list_incidents() | Incidents | tools/incidents/list.py | tool_list_incidents() |
get_incident() | Incidents | tools/incidents/get.py | tool_get_incident() |
list_attacks() | Block Attack | tools/block_attack/list.py | tool_list_attacks() |
block_attack() | Block Attack | tools/block_attack/block.py | tool_block_attack() |
analyze_nginx_logs() | Nginx Logs | tools/nginx_logs/analyze.py | tool_analyze_nginx_logs() |
analyze_nginx_logs_fields() | Nginx Logs | tools/nginx_logs/fields.py | tool_analyze_nginx_logs_fields() |
execute_shell_command() | Shell | tools/shell/execute.py | tool_execute_shell_command() |
Naming Conventions
-
Directory Names: Use lowercase with underscores for multi-word directories
block_attack/
(notblock-attack/
orblockAttack/
)
-
File Names: Use descriptive, action-oriented names
list.py
,modify.py
,analyze.py
,execute.py
-
Tool Method Names: Use the
tool_
prefix followed by the original function namelist_vhosts()
becomestool_list_vhosts()
-
Class Names: Use PascalCase with "Tool" suffix
VHostsListTool
,IncidentsListTool
,BlockAttackListTool
Directory Structure Rules
- One Category Per Directory: Each directory should contain tools for a single functional area
- Related Tools Together: Tools that work with the same system or command should be grouped
- Clear Separation: Each tool should be in its own file for maintainability
- Consistent Structure: All directories should follow the same pattern:
category/ āāā __init__.py # Package initialization āāā tool1.py # First tool in category āāā tool2.py # Second tool in category āāā tool3.py # Third tool in category
Benefits of This Structure
- Maintainability: Easy to find and modify specific tools
- Scalability: New tools can be added to appropriate categories
- Organization: Clear separation of concerns and functionality
- Consistency: Standardized naming and structure across all tools
- Discoverability: Developers can quickly understand what each directory contains
Best Practices
- Type Hints: Always use type hints for parameters and return values
- Docstrings: Provide clear docstrings for all tool methods
- Error Handling: Implement proper error handling and logging
- Validation: Validate input parameters when necessary
- Async/Await: Use async/await for I/O operations
- Logging: Use
self.logger
for logging within tools - Return Format: Maintain consistent return format across tools
Testing Tools
Before implementing tools, test the underlying commands:
# Test command execution
hypernode-manage-vhosts --list --format json
# Test error conditions
hypernode-manage-vhosts nonexistent-vhost
# Test with various parameters
pnl --today --limit 10
Security Considerations
- Input Validation: Always validate and sanitize user inputs
- Command Safety: Use
CommandExecutor
which includes safety checks - Timeout Protection: Set appropriate timeouts for long-running operations
- Error Handling: Don't expose sensitive information in error messages
- Logging: Log all operations for audit purposes
Troubleshooting
Common Issues
- Permission Denied: Ensure the user has appropriate permissions for Hypernode commands
- Command Not Found: Verify that Hypernode tools are installed and in PATH
- Timeout Errors: Increase timeout values for long-running operations
- JSON Parse Errors: Check command output format and error handling
Logging
Enable debug logging:
export MCP_LOG_LEVEL=DEBUG
python server.py
Running Tests
This project uses pytest for testing. All tests are located in the tests/
directory, which mirrors the structure of the source code.
Running Tests Locally (Docker)
To run all tests in the official Hypernode Docker image, use the provided script:
./runtests.sh
This will:
- Start the Docker container (
docker.hypernode.com/byteinternet/hypernode-bookworm-docker-php84-mysql80
) - Set up a Python virtual environment
- Install all dependencies
- Run the test suite with pytest
Running Tests Locally (Host Python)
If you have all dependencies installed locally, you can also run:
pytest tests/
Test Structure
- All tests are in the
tests/
directory, mirroring the structure of the source code exactly. - Every test file must be placed in a subdirectory that matches the source code path.
- Test filenames must include the full path to ensure uniqueness across the entire test suite.
- For example:
tools/vhosts/list.py
ātests/tools/vhosts/test_vhosts_list.py
tools/vhosts/modify.py
ātests/tools/vhosts/test_vhosts_modify.py
tools/incidents/list.py
ātests/tools/incidents/test_incidents_list.py
tools/incidents/get.py
ātests/tools/incidents/test_incidents_get.py
- For example:
- Each tool or module should have its own test file with a unique name that includes the category and action.
- Tests are written as synchronous functions (no async/await).
Writing New Tests
- Create a new test file in the appropriate subdirectory under
tests/
. - Use
pytest
conventions: test files start withtest_
, test classes start withTest
, and test functions start withtest_
. - Example:
import pytest
from tools.hello_world import HelloWorldTool
def test_hello_world():
tool = HelloWorldTool()
result = tool.tool_hello_world().__await__().__next__()
assert result["message"] == "Hello World from Hypernode MCP Server!"
Continuous Integration
- All tests are run automatically on each push and pull request to
main
via GitHub Actions using the official Hypernode Docker image.
License
This project is licensed under the MIT License.
Contributing
- Follow the coding standards in
.cursorrules
- Add tests for new functionality
- Update documentation for new tools
- Ensure all commands are tested before implementation