Development Guide¶
Guide for developers who want to contribute to or extend the Nautobot MCP Server.
Development Setup¶
Prerequisites¶
- Python 3.11
- Git
- uv (recommended) or pip
Clone and Install¶
# Clone repository
git clone https://github.com/kvncampos/nautobot_mcp.git
cd nautobot_mcp
# Install with dev dependencies
uv sync --group dev
# Or with pip
pip install -e ".[dev]"
Install Pre-commit Hooks¶
Project Structure¶
nautobot_mcp/
├── server.py # Main MCP server
├── helpers/ # Core modules
│ ├── nb_kb_v2.py # Knowledge base
│ ├── endpoint_searcher_chroma.py
│ └── content_processor.py
├── utils/ # Utilities
│ ├── config.py
│ ├── embedding.py
│ └── git_manager.py
├── tests/ # Test suite
├── examples/ # Usage examples
└── docs/ # Documentation
Running Tests¶
# Run all tests
pytest
# Run specific categories
pytest -m "unit"
pytest -m "integration"
pytest -m "offline"
# Run with coverage
pytest --cov=helpers --cov=utils
# Verbose output
pytest -v -s
Code Quality¶
Formatting¶
Linting¶
Type Checking¶
The project uses type hints. Consider adding mypy:
Adding New Features¶
Adding a New MCP Tool¶
- Define the tool schema:
# In server.py
tool_schema = {
"name": "my_new_tool",
"description": "What the tool does",
"inputSchema": {
"type": "object",
"properties": {
"param1": {
"type": "string",
"description": "Parameter description"
}
},
"required": ["param1"]
}
}
- Implement the handler:
@server.call_tool()
async def handle_invoke_tool(name: str, inputs: Dict[str, Any]):
if name == "my_new_tool":
param1 = inputs["param1"]
# Implementation
result = do_something(param1)
return [types.TextContent(type="text", text=json.dumps(result))]
- Add tests:
- Update documentation:
Add to docs/tools.md.
Adding a New Helper Module¶
- Create file in
helpers/ - Implement functionality
- Add tests in
tests/ - Update documentation
Testing Guidelines¶
Unit Tests¶
Test individual functions in isolation:
Integration Tests¶
Test component interactions:
@pytest.mark.integration
def test_endpoint_search():
searcher = EndpointSearcherChroma()
results = searcher.search("device")
assert len(results) > 0
Offline Tests¶
Tests that don't require network:
Documentation¶
Building Docs¶
# Install docs dependencies
uv sync --group docs
# Build documentation
mkdocs build
# Serve locally
mkdocs serve
View at http://localhost:8000
Writing Documentation¶
- Use clear, concise language
- Include code examples
- Add screenshots where helpful
- Cross-reference related pages
Contributing¶
See Contributing Guide for:
- Code style guidelines
- Pull request process
- Issue reporting
- Feature requests
Debugging¶
Debug Mode¶
VS Code Debugging¶
Create .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: MCP Server",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/server.py",
"console": "integratedTerminal",
"env": {
"LOG_LEVEL": "DEBUG"
}
}
]
}
Release Process¶
- Update version in
pyproject.toml - Update
CHANGELOG.md - Create git tag
- Push to GitHub
- GitHub Actions builds and publishes