Thank you for your interest in contributing to the Loxone MCP Server project. This guide will help you get started.
By participating in this project, you agree to maintain a respectful and inclusive environment for all contributors.
- Rust 1.70 or higher
- Git
- A Loxone Miniserver (for testing)
-
Fork and clone the repository:
git clone https://github.com/yourusername/loxone-mcp-rust cd loxone-mcp-rust -
Install development dependencies:
# Install Rust toolchain components rustup component add rustfmt clippy # Install development tools cargo install cargo-watch cargo-audit
-
Set up pre-commit hooks (optional):
cp .githooks/pre-commit .git/hooks/ chmod +x .git/hooks/pre-commit
git checkout -b feature/your-feature-nameFollow the coding standards and ensure your changes:
- Have appropriate tests
- Pass all existing tests
- Include documentation updates
- Follow Rust idioms
Before committing:
# Format code
cargo fmt
# Run linter
cargo clippy -- -D warnings
# Run tests
cargo test
# Check for security issues
cargo auditWrite clear, descriptive commit messages:
feat: add support for scene activation
- Implement get_light_scenes tool
- Add set_light_scene for scene control
- Include tests for scene validation
Commit message format:
feat:New featuresfix:Bug fixesdocs:Documentation changestest:Test additions/changesrefactor:Code refactoringchore:Maintenance tasks
- Push to your fork
- Create a pull request against
main - Fill out the PR template
- Wait for review
Follow the official Rust style guide:
// Good
pub fn control_device(uuid: &str, command: &str) -> Result<()> {
validate_uuid(uuid)?;
// Implementation
}
// Avoid
pub fn ControlDevice(UUID: &str, cmd: &str) -> Result<()> {
// Non-idiomatic naming
}Use the custom error types:
use crate::error::{LoxoneError, Result};
fn process_command(cmd: &str) -> Result<()> {
if cmd.is_empty() {
return Err(LoxoneError::validation("Command cannot be empty"));
}
// Process command
Ok(())
}Document all public APIs:
/// Controls a Loxone device by UUID.
///
/// # Arguments
/// * `uuid` - The device UUID in Loxone format
/// * `command` - Command to send (e.g., "on", "off", "50")
///
/// # Returns
/// * `Ok(())` on success
/// * `Err(LoxoneError)` on failure
///
/// # Example
/// ```
/// control_device("0f869a3f-0155-8b3f-ffff403fb0c34b9e", "on")?;
/// ```
pub fn control_device(uuid: &str, command: &str) -> Result<()> {
// Implementation
}Write tests for new functionality:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_uuid_validation() {
assert!(validate_uuid("0f869a3f-0155-8b3f-ffff403fb0c34b9e").is_ok());
assert!(validate_uuid("invalid-uuid").is_err());
}
#[tokio::test]
async fn test_device_control() {
let client = MockClient::new();
let result = control_device_with_client(&client, "uuid", "on").await;
assert!(result.is_ok());
}
}-
Add to
src/tools/adapters.rs:pub async fn my_new_tool( context: ToolContext, params: MyToolParams, ) -> ToolResult { // Implementation }
-
Define parameters:
#[derive(Debug, Deserialize, JsonSchema)] pub struct MyToolParams { #[serde(description = "Parameter description")] pub param_name: String, }
-
Register in tool system:
Tool::new("my_new_tool") .description("Tool description") .parameter("param_name", ParameterType::String, "Parameter description") .handler(my_new_tool)
-
Add tests:
#[test] fn test_my_new_tool() { // Test implementation }
-
Update documentation:
- Add to
docs/tools_reference.md - Update README if significant feature
- Add to
For new Loxone device types:
- Check device type in Miniserver structure
- Add filtering logic in relevant tools
- Test with actual device
- Document device-specific behavior
# Run all tests
cargo test
# Run specific test
cargo test test_name
# Run with output
cargo test -- --nocapture# Requires configured Loxone credentials
cargo test --test integration_tests- Build the server:
cargo build - Run with test credentials
- Use MCP Inspector or curl to test tools
- Verify Miniserver state changes
- API Changes: Update
docs/tools_reference.md - Architecture: Update
docs/architecture.md - Security: Update
docs/security.md - Examples: Add to relevant tool documentation
- Use clear, concise language
- Include code examples
- Explain rationale for design decisions
- Keep formatting consistent
For performance-critical changes:
#[bench]
fn bench_cache_lookup(b: &mut Bencher) {
let cache = build_test_cache();
b.iter(|| {
cache.get("test_key")
});
}- Profile before optimizing
- Maintain readability
- Document performance tricks
- Add benchmarks for regressions
All PRs touching security-sensitive areas require:
- Careful review of input validation
- Authentication/authorization checks
- No hardcoded credentials
- Proper error messages (no info leakage)
See security.md for vulnerability reporting.
- Update version in
Cargo.toml - Update CHANGELOG.md
- Create release PR
- After merge, tag release
- GitHub Actions builds releases
- Questions: Open a GitHub issue
- Discussions: GitHub Discussions
- Real-time: Community chat (if available)
Contributors are recognized in:
- GitHub contributors page
- CHANGELOG.md (for significant contributions)
- Release notes
Thank you for contributing to make Loxone home automation more accessible!