BREAKING CHANGE: Removed circuits, tenancy, VPN, wireless modules entirely. Stripped DCIM, IPAM, virtualization, extras to only essential tools. Deleted NETBOX_ENABLED_MODULES filtering — no longer needed. - Delete circuits.py, tenancy.py, vpn.py, wireless.py - Strip dcim.py to sites, devices, interfaces only (11 tools) - Strip ipam.py to IPs, prefixes, services only (10 tools) - Strip virtualization.py to clusters, VMs, VM interfaces only (10 tools) - Strip extras.py to tags, journal entries only (6 tools) - Remove all module filtering code from config.py and server.py - Rewrite README.md with accurate 37-tool documentation - Update CHANGELOG.md with breaking change entry - Token reduction: ~19,810 → ~3,700 (~81%) Remaining work: Update cmdb-assistant plugin skills (10 files) in follow-up PR Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
88 lines
2.8 KiB
Python
88 lines
2.8 KiB
Python
"""
|
|
Extras tools for NetBox MCP Server.
|
|
|
|
Covers: Tags and Journal Entries only.
|
|
"""
|
|
import logging
|
|
from typing import List, Dict, Optional, Any
|
|
from ..netbox_client import NetBoxClient
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ExtrasTools:
|
|
"""Tools for Extras operations in NetBox"""
|
|
|
|
def __init__(self, client: NetBoxClient):
|
|
self.client = client
|
|
self.base_endpoint = 'extras'
|
|
|
|
# ==================== Tags ====================
|
|
|
|
async def list_tags(
|
|
self,
|
|
name: Optional[str] = None,
|
|
slug: Optional[str] = None,
|
|
color: Optional[str] = None,
|
|
**kwargs
|
|
) -> List[Dict]:
|
|
"""List all tags with optional filtering."""
|
|
params = {k: v for k, v in {
|
|
'name': name, 'slug': slug, 'color': color, **kwargs
|
|
}.items() if v is not None}
|
|
return self.client.list(f'{self.base_endpoint}/tags', params=params)
|
|
|
|
async def get_tag(self, id: int) -> Dict:
|
|
"""Get a specific tag by ID."""
|
|
return self.client.get(f'{self.base_endpoint}/tags', id)
|
|
|
|
async def create_tag(
|
|
self,
|
|
name: str,
|
|
slug: str,
|
|
color: str = '9e9e9e',
|
|
description: Optional[str] = None,
|
|
**kwargs
|
|
) -> Dict:
|
|
"""Create a new tag."""
|
|
data = {'name': name, 'slug': slug, 'color': color, **kwargs}
|
|
if description:
|
|
data['description'] = description
|
|
return self.client.create(f'{self.base_endpoint}/tags', data)
|
|
|
|
# ==================== Journal Entries ====================
|
|
|
|
async def list_journal_entries(
|
|
self,
|
|
assigned_object_type: Optional[str] = None,
|
|
assigned_object_id: Optional[int] = None,
|
|
kind: Optional[str] = None,
|
|
**kwargs
|
|
) -> List[Dict]:
|
|
"""List all journal entries."""
|
|
params = {k: v for k, v in {
|
|
'assigned_object_type': assigned_object_type,
|
|
'assigned_object_id': assigned_object_id, 'kind': kind, **kwargs
|
|
}.items() if v is not None}
|
|
return self.client.list(f'{self.base_endpoint}/journal-entries', params=params)
|
|
|
|
async def get_journal_entry(self, id: int) -> Dict:
|
|
"""Get a specific journal entry by ID."""
|
|
return self.client.get(f'{self.base_endpoint}/journal-entries', id)
|
|
|
|
async def create_journal_entry(
|
|
self,
|
|
assigned_object_type: str,
|
|
assigned_object_id: int,
|
|
comments: str,
|
|
kind: str = 'info',
|
|
**kwargs
|
|
) -> Dict:
|
|
"""Create a new journal entry."""
|
|
data = {
|
|
'assigned_object_type': assigned_object_type,
|
|
'assigned_object_id': assigned_object_id,
|
|
'comments': comments, 'kind': kind, **kwargs
|
|
}
|
|
return self.client.create(f'{self.base_endpoint}/journal-entries', data)
|