.cursorrules · git:20251102.72b8e60 · 2025-11-02 · sha256 4ccebf748bf8eacd
.cursorrules git:20251102.72b8e60A
Immutable. This exact content never changes and is served at /api/v1/blob/4ccebf748bf8eacd.
# Cursor Rules for UniFi MCP Server
You are an expert Python developer working on a production-grade MCP (Model Context Protocol) server for UniFi Network Controller integration. This server enables AI agents to manage UniFi network infrastructure through 40+ standardized tools.
## Project Context
**Technology Stack:**
- Python 3.10+ with type hints and async/await patterns
- FastMCP framework for MCP server implementation
- Pydantic for data validation and schema generation
- Redis for optional caching layer
- pytest for testing (target 80%+ coverage)
- Pre-commit hooks: black, isort, ruff, mypy, bandit
**Architecture:**
- Async-first design for high concurrency
- Tool-based API (40+ MCP tools across devices, networks, clients, firewall, WiFi, DPI)
- Resource endpoints for read-only data access
- Optional Redis caching with automatic invalidation
- Webhook support for real-time event handling
- Multi-site UniFi controller support
**Project Structure:**
```
unifi-mcp-server/
├── src/
│ ├── main.py # MCP server registration and entry point
│ ├── api/ # UniFi API client with rate limiting
│ ├── tools/ # MCP tool implementations (grouped by domain)
│ ├── resources/ # MCP resource definitions
│ ├── webhooks/ # Event handlers
│ ├── config/ # Configuration management
│ ├── utils/ # Validators, formatters, helpers
│ └── cache.py # Redis caching implementation
├── tests/
│ ├── unit/ # Fast, isolated tests with mocks
│ └── integration/ # Tests requiring UniFi controller
├── docs/
│ └── AI-Coding/ # AI coding guidelines
├── .env.example # Environment variable template
└── pyproject.toml # Dependencies and project config
```
## Core Coding Principles
### 1. Async-First Development
- **ALWAYS** use `async def` for functions that perform I/O operations
- Use `await` for API calls, database queries, file operations, HTTP requests
- Leverage `asyncio.gather()` for concurrent operations
- Use `async with` for context managers (UniFi client, Redis connections)
- **NEVER** use blocking synchronous calls in async contexts
- Consider using `anyio.to_thread.run_sync()` for CPU-intensive operations
### 2. Type Safety and Validation
- **ALWAYS** provide type hints for all function parameters and return values
- Use Pydantic models for complex input/output structures
- Prefer explicit types over `Any` or untyped dicts
- Use `Optional[T]` or `T | None` for nullable values
- Leverage Pydantic's validation features (Field, validator decorators)
- Run mypy before committing changes
### 3. Security-First Design
- **CRITICAL**: All mutating operations MUST require `confirm=True` parameter
- Implement `dry_run=True` mode for preview-before-apply
- Log all operations to `audit.log` with context (user, timestamp, parameters)
- Use password masking utilities for sensitive data in logs
- Validate and sanitize all user inputs
- Follow least-privilege principle for API access
- Never commit API keys, passwords, or secrets to version control
### 4. Comprehensive Testing
- Write tests FIRST for new features (TDD approach)
- Target 80%+ code coverage for all new code
- Use pytest fixtures for common setup
- Mock external dependencies (UniFi API, Redis) in unit tests
- Mark integration tests with `@pytest.mark.integration`
- Test edge cases, error conditions, and validation failures
- **NEVER** commit code that breaks existing tests
### 5. Documentation Standards
- Write clear, comprehensive docstrings for all public functions
- Use Google-style docstring format
- Include Args, Returns, Raises sections
- Provide usage examples in docstrings for complex tools
- Update API.md when adding new tools or changing signatures
- Keep README.md current with project capabilities
## MCP Tool Implementation Pattern
Every MCP tool should follow this structure:
```python
from typing import Optional, Literal
from pydantic import BaseModel, Field
from fastmcp import FastMCP
mcp = FastMCP("UniFi MCP Server")
@mcp.tool()
async def tool_name(
# Required parameters first
site_id: str,
resource_id: str,
# Optional parameters with defaults
optional_param: Optional[str] = None,
# Safety parameters for mutating operations
confirm: bool = False,
dry_run: bool = False
) -> dict:
"""
Clear, concise description of what the tool does.
Args:
site_id: Always include site_id for multi-site support
resource_id: Specific resource identifier
optional_param: Description of optional parameter
confirm: REQUIRED True for mutating operations (safety)
dry_run: Preview changes without applying them
Returns:
Dictionary with operation results. Specify structure.
Raises:
MCPError: For all MCP-specific errors
ValueError: For invalid inputs
UniFiConnectionError: For API connection issues
"""
# 1. Input validation
if not site_id:
raise ValueError("site_id is required")
# 2. Safety checks for mutating operations
if not confirm and not dry_run:
raise MCPError("This operation requires confirm=True")
# 3. Audit logging
logger.info(
f"tool_name: site={site_id}, id={resource_id}, dry_run={dry_run}",
extra={"operation": "tool_name", "site_id": site_id}
)
# 4. Dry-run mode
if dry_run:
preview = await generate_preview(site_id, resource_id)
return {"status": "preview", "changes": preview}
# 5. Execute operation
try:
result = await execute_operation(site_id, resource_id)
except UniFiAPIError as e:
logger.error(f"Operation failed: {e}")
raise MCPError(f"Failed to execute operation: {str(e)}")
# 6. Cache invalidation (if applicable)
if result.get("success"):
await cache.invalidate(f"resource:{site_id}")
# 7. Return structured result
return {
"status": "success",
"resource_id": resource_id,
"data": result
}
```
## Development Workflow
### Before Starting Work
1. Pull latest changes: `git pull origin main`
2. Create feature branch: `git checkout -b feature/your-feature`
3. Review existing patterns in similar files
### During Development
1. Write tests first (TDD approach)
2. Implement feature following patterns above
3. Run tests frequently: `pytest -v`
4. Check types: `mypy src/`
5. Format code: `black src/ tests/` and `isort src/ tests/`
### Before Committing
1. Run full test suite: `pytest --cov=src --cov-report=term-missing`
2. Ensure coverage ≥80%: Check coverage report
3. Run linter: `ruff check src/ tests/ --fix`
4. Type check: `mypy src/`
5. Run pre-commit hooks: `pre-commit run --all-files`
6. Update documentation if needed
### Commit Message Format
Follow Conventional Commits:
```
<type>: <short summary>
<optional body>
```
Types: `feat`, `fix`, `docs`, `test`, `refactor`, `style`, `chore`
Examples:
```
feat: add DPI statistics tool for bandwidth analysis
fix: correct device restart timeout handling
docs: update API.md with WiFi management tools
test: add integration tests for port forwarding
refactor: simplify client blocking logic
```
## Code Quality Checklist
Before submitting a PR, verify:
- [ ] All tests pass (`pytest`)
- [ ] Code coverage ≥80% (`pytest --cov`)
- [ ] Type checking passes (`mypy src/`)
- [ ] Linting passes (`ruff check src/ tests/`)
- [ ] Code formatted (`black`, `isort`)
- [ ] Security checks pass (`bandit -r src/`)
- [ ] Pre-commit hooks pass
- [ ] Documentation updated (docstrings, API.md)
- [ ] CHANGELOG.md updated for user-facing changes
- [ ] No hardcoded secrets or API keys
- [ ] Audit logging added for mutating operations
## Common Mistakes to Avoid
❌ **DON'T:**
- Use synchronous blocking calls in async functions
- Omit type hints or use `Any` unnecessarily
- Skip tests for new features
- Forget `confirm=True` safety checks on mutating operations
- Expose sensitive data in logs
- Commit without running pre-commit hooks
- Mix sync and async code improperly
- Return untyped dicts when Pydantic models are appropriate
✅ **DO:**
- Use async/await consistently
- Provide comprehensive type hints
- Write tests first (TDD)
- Implement dry-run mode for dangerous operations
- Mask passwords and keys in logs
- Run full test suite before committing
- Use `async with` for resource management
- Define Pydantic models for complex data structures
## Environment Setup
Required environment variables (see `.env.example`):
```bash
# Required
UNIFI_API_KEY=your-api-key-here
UNIFI_API_TYPE=cloud # or "local"
UNIFI_HOST=api.ui.com # or local gateway IP
UNIFI_SITE=default
# Optional
REDIS_HOST=localhost # Enable caching
REDIS_PORT=6379
WEBHOOK_SECRET=your-secret # Enable webhook HMAC verification
```
## Quick Commands
```bash
# Development
uv run mcp dev src/main.py # Run with MCP Inspector
# Testing
pytest # Run all tests
pytest -m unit # Unit tests only
pytest -m integration # Integration tests only
pytest --cov=src --cov-report=html # With coverage report
# Code Quality
black src/ tests/ # Format
isort src/ tests/ # Sort imports
ruff check src/ tests/ --fix # Lint and fix
mypy src/ # Type check
bandit -r src/ # Security scan
# Pre-commit
pre-commit run --all-files # Run all hooks
```
## Key Resources
- **Project Documentation**: README.md, API.md, CONTRIBUTING.md, SECURITY.md
- **MCP Specification**: https://modelcontextprotocol.io/
- **FastMCP Docs**: https://gofastmcp.com/
- **UniFi API**: Official UniFi Controller API documentation
- **Pydantic**: https://docs.pydantic.dev/
## Remember
- **Safety First**: Require `confirm=True` for all network modifications
- **Test Everything**: 80%+ coverage is not optional
- **Type Everything**: Type hints improve code quality and catch bugs
- **Document Well**: Future you (and other developers) will thank you
- **Async Always**: This is an async-first project - respect the pattern
- **Security Matters**: Never expose credentials or sensitive data
---
Generated for Cursor AI IDE
UniFi MCP Server v0.1.2
Python 3.10+ | FastMCP | Async-first Architecture