Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8e5e70d
chore: add .worktrees/ to .gitignore
abubnalitic-nbl Oct 28, 2025
8a4a4f8
chore: create src/netbox_mcp_server directory structure
abubnalitic-nbl Oct 28, 2025
9f89e60
feat: add package __init__.py with version and exports
abubnalitic-nbl Oct 29, 2025
94b0466
feat: add __main__.py for module execution support
abubnalitic-nbl Oct 29, 2025
5026665
refactor: move config.py to src/netbox_mcp_server/
abubnalitic-nbl Oct 29, 2025
234c447
refactor: move netbox_types.py to src/netbox_mcp_server/
abubnalitic-nbl Oct 29, 2025
2393dac
refactor: move netbox_client.py to src/ and update imports
abubnalitic-nbl Oct 29, 2025
27f935e
refactor: move server.py to src/, update imports, add main() function
abubnalitic-nbl Oct 29, 2025
519ff70
feat: add console script entry point netbox-mcp-server
abubnalitic-nbl Oct 29, 2025
4134f01
chore: add python-semantic-release dev dependency
abubnalitic-nbl Oct 29, 2025
d22b5e4
feat: configure python-semantic-release for automated versioning
abubnalitic-nbl Oct 29, 2025
eb4c489
test: update test_config.py imports to use package structure
abubnalitic-nbl Oct 29, 2025
944e9fe
test: update test_filter_validation.py imports to use package structure
abubnalitic-nbl Oct 29, 2025
b252baa
test: update test_ordering.py imports to use package structure
abubnalitic-nbl Oct 29, 2025
2616e8e
test: update test_pagination.py imports to use package structure
abubnalitic-nbl Oct 29, 2025
5d4dc8e
test: update test_search.py imports to use package structure
abubnalitic-nbl Oct 29, 2025
c1b99c7
test: update test_brief.py imports to use package structure
abubnalitic-nbl Oct 29, 2025
877784c
chore: create .github/workflows directory for CI/CD
abubnalitic-nbl Oct 29, 2025
0cbd557
ci: add test workflow for automated testing
abubnalitic-nbl Oct 29, 2025
babad3c
ci: add release workflow for automated semantic releases
abubnalitic-nbl Oct 29, 2025
8aa150d
docs: add breaking change notice to README for v1.0.0
abubnalitic-nbl Oct 29, 2025
736e10c
docs: update README commands to use netbox-mcp-server
abubnalitic-nbl Oct 29, 2025
eceed32
docs: update Claude Desktop/Code config examples in README
abubnalitic-nbl Oct 29, 2025
d431fb7
docs: update project structure in CLAUDE.md
abubnalitic-nbl Oct 29, 2025
8954399
docs: update common commands in CLAUDE.md
abubnalitic-nbl Oct 29, 2025
bedf7a0
docs: add version management section to CLAUDE.md
abubnalitic-nbl Oct 29, 2025
d88af76
chore: update Dockerfile to use netbox-mcp-server command
abubnalitic-nbl Oct 29, 2025
aa25162
docs: minor formatting fixes to CLAUDE.md and add implementation plan
abubnalitic-nbl Oct 29, 2025
456376d
fix: improved specification for changed_object_type_id
abubnalitic-nbl Oct 29, 2025
b16c824
chore: removed spec doc
abubnalitic-nbl Oct 29, 2025
7eb1605
Update .github/workflows/release.yml
abubnalitic-nbl Oct 29, 2025
c399c1b
Update src/netbox_mcp_server/__init__.py
abubnalitic-nbl Oct 29, 2025
7e10b68
fix: updated to manual releases
abubnalitic-nbl Oct 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Release

permissions:
contents: write
issues: write
pull-requests: write

on:
workflow_dispatch:

jobs:
release:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install dependencies
run: uv sync

- name: Python Semantic Release
uses: python-semantic-release/python-semantic-release@v10
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
40 changes: 40 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Test

on:
pull_request:
push:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest

services:
netbox:
image: netboxcommunity/netbox:latest
env:
SKIP_SUPERUSER: true
ports:
- 8000:8080

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install dependencies
run: uv sync

- name: Run tests
run: uv run pytest -v
env:
NETBOX_URL: http://localhost:8000
NETBOX_TOKEN: ${{ secrets.NETBOX_TOKEN }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,8 @@ logs/
*.bak
*.swp
*.swo

# Git worktrees
.worktrees/

.plans/*
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ ENV PATH="/app/.venv/bin:$PATH"

EXPOSE 8000

CMD ["python", "-u", "server.py"]
CMD ["netbox-mcp-server"]
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# NetBox MCP Server

> **⚠️ Breaking Change in v1.0.0**: The project structure has changed.
> If upgrading from v0.1.0, update your configuration:
> - Change `uv run server.py` to `uv run netbox-mcp-server`
> - Update Claude Desktop/Code configs to use `netbox-mcp-server` instead of `server.py`
> - Docker users: rebuild images with updated CMD
> - See [CHANGELOG.md](CHANGELOG.md) for full details

This is a simple read-only [Model Context Protocol](https://modelcontextprotocol.io/) server for NetBox. It enables you to interact with your data in NetBox directly via LLMs that support MCP.

## Tools
Expand All @@ -26,7 +33,7 @@ This is a simple read-only [Model Context Protocol](https://modelcontextprotocol
pip install -e .
```

3. Verify the server can run: `NETBOX_URL=https://netbox.example.com/ NETBOX_TOKEN=<your-api-token> uv run server.py`
3. Verify the server can run: `NETBOX_URL=https://netbox.example.com/ NETBOX_TOKEN=<your-api-token> uv run netbox-mcp-server`

4. Add the MCP server to your LLM client. See below for some examples with Claude.

Expand All @@ -40,7 +47,7 @@ Add the server using the `claude mcp add` command:
claude mcp add --transport stdio netbox \
--env NETBOX_URL=https://netbox.example.com/ \
--env NETBOX_TOKEN=<your-api-token> \
-- uv --directory /path/to/netbox-mcp-server run server.py
-- uv --directory /path/to/netbox-mcp-server run netbox-mcp-server
```

**Important notes:**
Expand All @@ -61,7 +68,7 @@ For HTTP transport, first start the server manually:
NETBOX_URL=https://netbox.example.com/ \
NETBOX_TOKEN=<your-api-token> \
TRANSPORT=http \
uv run server.py
uv run netbox-mcp-server
```

Then add the running server to Claude Code:
Expand Down Expand Up @@ -91,7 +98,7 @@ Add the server configuration to your Claude Desktop config file. On Mac, edit `~
"--directory",
"/path/to/netbox-mcp-server",
"run",
"server.py"
"netbox-mcp-server"
],
"env": {
"NETBOX_URL": "https://netbox.example.com/",
Expand Down Expand Up @@ -176,7 +183,7 @@ For local Claude Desktop or Claude Code usage with stdio transport:
"mcpServers": {
"netbox": {
"command": "uv",
"args": ["--directory", "/path/to/netbox-mcp-server", "run", "server.py"],
"args": ["--directory", "/path/to/netbox-mcp-server", "run", "netbox-mcp-server"],
"env": {
"NETBOX_URL": "https://netbox.example.com/",
"NETBOX_TOKEN": "<your-api-token>"
Expand All @@ -198,10 +205,10 @@ export TRANSPORT=http
export HOST=127.0.0.1
export PORT=8000

uv run server.py
uv run netbox-mcp-server

# Or using CLI arguments
uv run server.py \
uv run netbox-mcp-server \
--netbox-url https://netbox.example.com/ \
--netbox-token <your-api-token> \
--transport http \
Expand Down Expand Up @@ -237,11 +244,11 @@ LOG_LEVEL=INFO
All configuration options can be overridden via CLI arguments:

```bash
uv run server.py --help
uv run netbox-mcp-server --help

# Common examples:
uv run server.py --log-level DEBUG --no-verify-ssl # Development
uv run server.py --transport http --port 9000 # Custom HTTP port
uv run netbox-mcp-server --log-level DEBUG --no-verify-ssl # Development
uv run netbox-mcp-server --transport http --port 9000 # Custom HTTP port
```

## Docker Usage
Expand Down
45 changes: 36 additions & 9 deletions claude.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,20 @@ A read-only [Model Context Protocol](https://modelcontextprotocol.io/) server th

```text
.
├── server.py # Main MCP server with tool definitions
├── netbox_client.py # NetBox REST API client abstraction
├── pyproject.toml # Dependencies and project metadata
├── README.md # User-facing documentation
├── SECURITY.md # Security policy and reporting
└── LICENSE # Apache 2.0 license
├── src/
│ └── netbox_mcp_server/
│ ├── __init__.py # Package initialization with __version__
│ ├── __main__.py # Entry point for module execution
│ ├── server.py # Main MCP server with tool definitions
│ ├── netbox_client.py # NetBox REST API client abstraction
│ ├── netbox_types.py # NetBox object type mappings
│ └── config.py # Settings and logging configuration
├── tests/ # Test suite
├── .github/workflows/ # CI/CD automation
├── pyproject.toml # Dependencies and project metadata
├── README.md # User-facing documentation
├── CHANGELOG.md # Auto-generated release notes
└── LICENSE # Apache 2.0 license
```

**Design Pattern**: Clean separation between MCP server logic (`server.py`) and NetBox API client (`netbox_client.py`) to support future plugin-based implementations.
Expand All @@ -35,13 +43,16 @@ A read-only [Model Context Protocol](https://modelcontextprotocol.io/) server th
uv sync

# Run the server locally (requires env vars)
NETBOX_URL=https://netbox.example.com/ NETBOX_TOKEN=<token> uv run server.py
NETBOX_URL=https://netbox.example.com/ NETBOX_TOKEN=<token> uv run netbox-mcp-server

# Alternative: module execution
uv run -m netbox_mcp_server

# Add to Claude Code (for development/testing)
claude mcp add --transport stdio netbox \
--env NETBOX_URL=https://netbox.example.com/ \
--env NETBOX_TOKEN=<token> \
-- uv --directory /path/to/netbox-mcp-server run server.py
-- uv --directory /path/to/netbox-mcp-server run netbox-mcp-server
```

## Development Philosophy
Expand All @@ -57,6 +68,23 @@ claude mcp add --transport stdio netbox \
- **Functional where clear**: Use functional, stateless approaches when they improve clarity
- **Clean core logic**: Keep business logic clean; push implementation details to the edges

## Version Management

This project uses [python-semantic-release](https://python-semantic-release.readthedocs.io/) for automated version management. Versions are automatically determined from commit messages following [Conventional Commits](https://www.conventionalcommits.org/).

**Release triggers:**

- `feat:` commits trigger minor version bumps (1.0.0 → 1.1.0)
- `fix:` and `perf:` commits trigger patch version bumps (1.0.0 → 1.0.1)
- Commits with `BREAKING CHANGE:` in the body trigger major version bumps (1.0.0 → 2.0.0)
- `docs:`, `test:`, `chore:`, `ci:`, `refactor:` commits are logged but don't trigger releases

**Workflow:**

- Merge to `main` automatically triggers release analysis
- If commits warrant a release, version is bumped and CHANGELOG updated
- GitHub Release is created with auto-generated release notes

## Code Standards

### Python Conventions
Expand Down Expand Up @@ -260,7 +288,6 @@ Currently no automated test suite. When adding tests:
- ❌ **NEVER commit directly to `main`** - Always use feature branches
- ✅ **DO keep commits professional and concise** and focused on the change


## Decision Heuristics

### When to Add a New Tool
Expand Down
24 changes: 24 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "netbox-mcp-server"
version = "0.1.0"
Expand All @@ -12,8 +16,28 @@ dependencies = [
"pydantic-settings>=2.0",
]

[project.scripts]
netbox-mcp-server = "netbox_mcp_server.server:main"

[dependency-groups]
dev = [
"pytest>=8.4.2",
"pytest-cov>=7.0.0",
"python-semantic-release>=10.4.1",
]

[tool.semantic_release]
version_toml = ["pyproject.toml:project.version"]
version_variables = ["src/netbox_mcp_server/__init__.py:__version__"]
branch = "main"
upload_to_vcs_release = true
build_command = "uv build"
tag_format = "v{version}"

[tool.semantic_release.commit_parser_options]
allowed_tags = ["feat", "fix", "docs", "chore", "refactor", "test", "ci", "perf"]
minor_tags = ["feat"]
patch_tags = ["fix", "perf"]

[tool.semantic_release.changelog]
changelog_file = "CHANGELOG.md"
9 changes: 9 additions & 0 deletions src/netbox_mcp_server/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""NetBox MCP Server - Read-only MCP server for NetBox infrastructure data."""

__version__ = "0.1.0" # Auto-managed by semantic-release

__all__ = ["NetBoxRestClient", "NETBOX_OBJECT_TYPES", "Settings"]

from netbox_mcp_server.netbox_client import NetBoxRestClient
from netbox_mcp_server.netbox_types import NETBOX_OBJECT_TYPES
from netbox_mcp_server.config import Settings
6 changes: 6 additions & 0 deletions src/netbox_mcp_server/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Entry point for python -m netbox_mcp_server execution."""

from netbox_mcp_server.server import main

if __name__ == "__main__":
main()
File renamed without changes.
File renamed without changes.
File renamed without changes.
28 changes: 20 additions & 8 deletions server.py → src/netbox_mcp_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from fastmcp import FastMCP
from pydantic import Field

from config import Settings, configure_logging
from netbox_client import NetBoxRestClient
from netbox_types import NETBOX_OBJECT_TYPES
from netbox_mcp_server.config import Settings, configure_logging
from netbox_mcp_server.netbox_client import NetBoxRestClient
from netbox_mcp_server.netbox_types import NETBOX_OBJECT_TYPES


def parse_cli_args() -> dict[str, Any]:
Expand Down Expand Up @@ -368,17 +368,21 @@ def netbox_get_changelogs(filters: dict):
Filtering options include:
- user_id: Filter by user ID who made the change
- user: Filter by username who made the change
- changed_object_type_id: Filter by ContentType ID of the changed object
- changed_object_type_id: Filter by numeric ContentType ID (e.g., 21 for dcim.device)
Note: This expects a numeric ID, not an object type string
- changed_object_id: Filter by ID of the changed object
- object_repr: Filter by object representation (usually contains object name)
- action: Filter by action type (created, updated, deleted)
- time_before: Filter for changes made before a given time (ISO 8601 format)
- time_after: Filter for changes made after a given time (ISO 8601 format)
- q: Search term to filter by object representation

Example:
To find all changes made to a specific device with ID 123:
{"changed_object_type_id": "dcim.device", "changed_object_id": 123}
Examples:
To find all changes made to a specific object by ID:
{"changed_object_id": 123}

To find changes by object name pattern:
{"object_repr": "router-01"}

To find all deletions in the last 24 hours:
{"action": "delete", "time_after": "2023-01-01T00:00:00Z"}
Expand Down Expand Up @@ -505,7 +509,11 @@ def _endpoint_for_type(object_type: str) -> str:
"""
return NETBOX_OBJECT_TYPES[object_type]['endpoint']

if __name__ == "__main__":

def main() -> None:
"""Main entry point for the MCP server."""
global netbox

cli_overlay: dict[str, Any] = parse_cli_args()

try:
Expand Down Expand Up @@ -561,3 +569,7 @@ def _endpoint_for_type(object_type: str) -> str:
except Exception as e:
logger.error(f"Failed to start MCP server: {e}")
sys.exit(1)


if __name__ == "__main__":
main()
Loading