forked from netboxlabs/netbox-mcp-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.py
More file actions
131 lines (107 loc) · 4.38 KB
/
Copy pathconfig.py
File metadata and controls
131 lines (107 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""Configuration management for NetBox MCP Server."""
import logging
import logging.config
from typing import Any, Literal
from pydantic import AnyUrl, SecretStr, field_validator, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""
Centralized configuration for NetBox MCP Server.
Configuration precedence: CLI > Environment > .env file > Defaults
Environment variables should match field names (e.g., NETBOX_URL, TRANSPORT).
"""
# ===== Core NetBox Settings =====
netbox_url: AnyUrl
"""Base URL of the NetBox instance (e.g., https://netbox.example.com/)"""
netbox_token: SecretStr
"""API token for NetBox authentication (treated as secret)"""
# ===== Transport Settings =====
transport: Literal["stdio", "http", "sse"] = "stdio"
"""MCP transport protocol to use (stdio for Claude Desktop, http/sse for servers)"""
host: str = "127.0.0.1"
"""Bind host (used when transport is 'http' or 'sse')"""
port: int = 8000
"""Port to bind server (used when transport is 'http' or 'sse')"""
# ===== Security Settings =====
verify_ssl: bool = True
"""Whether to verify SSL certificates when connecting to NetBox"""
# ===== Observability Settings =====
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO"
"""Logging verbosity level"""
# ===== Pydantic Configuration =====
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
env_prefix="", # No prefix, use field names directly
extra="ignore", # Ignore unknown environment variables
case_sensitive=False, # Environment variables are case-insensitive
)
# ===== Validators =====
@field_validator("port")
@classmethod
def validate_port(cls, v: int) -> int:
"""Ensure port is in valid range."""
if not (0 < v < 65536):
raise ValueError(f"Port must be between 1 and 65535, got {v}")
return v
@field_validator("netbox_url")
@classmethod
def validate_netbox_url(cls, v: AnyUrl) -> AnyUrl:
"""Ensure NetBox URL has a scheme and host."""
if not v.scheme or not v.host:
raise ValueError(
"NETBOX_URL must include scheme and host (e.g., https://netbox.example.com/)"
)
return v
@model_validator(mode="after")
def validate_http_transport_requirements(self) -> "Settings":
"""No additional validation needed for HTTP/SSE transport; defaults are appropriate."""
return self
def get_effective_config_summary(self) -> dict:
"""
Return a non-secret summary of effective configuration for logging.
Returns:
Dictionary with configuration values (secrets masked)
"""
return {
"netbox_url": str(self.netbox_url),
"netbox_token": "***REDACTED***",
"transport": self.transport,
"host": self.host if self.transport in ("http", "sse") else "N/A",
"port": self.port if self.transport in ("http", "sse") else "N/A",
"verify_ssl": self.verify_ssl,
"log_level": self.log_level,
}
def configure_logging(
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
) -> None:
"""
Configure structured logging using dictConfig.
Args:
log_level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
"""
config: dict[str, Any] = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"console": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "console",
"stream": "ext://sys.stderr",
},
},
"loggers": {
# Suppress noisy HTTP client logs unless DEBUG
"urllib3": {"level": "WARNING" if log_level != "DEBUG" else "DEBUG"},
"httpx": {"level": "WARNING" if log_level != "DEBUG" else "DEBUG"},
"requests": {"level": "WARNING" if log_level != "DEBUG" else "DEBUG"},
},
"root": {"level": log_level, "handlers": ["console"]},
}
logging.config.dictConfig(config)