qualys-mcp 2.1.2__py3-none-any.whl → 2.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qualys-mcp
3
- Version: 2.1.2
3
+ Version: 2.1.4
4
4
  Summary: MCP server for Qualys security APIs - natural language interaction with vulnerability, asset, and cloud security data
5
5
  Project-URL: Homepage, https://github.com/nelssec/qualys-mcp
6
6
  Project-URL: Repository, https://github.com/nelssec/qualys-mcp
@@ -0,0 +1,6 @@
1
+ qualys_mcp.py,sha256=0Z1r61au-1YlvPFAleQcROwVpx7ZwmENPls9pcpMWpM,31643
2
+ qualys_mcp-2.1.4.dist-info/METADATA,sha256=UYXH-NRxdvn0DmbiCDeUPjm_pfRrE-Ik7CF5Es_PNMQ,3295
3
+ qualys_mcp-2.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
4
+ qualys_mcp-2.1.4.dist-info/entry_points.txt,sha256=Dc8X0AhJDjGaZOJ0SNpWDWjEX4sYzrYa9FZEbggX0Rs,47
5
+ qualys_mcp-2.1.4.dist-info/licenses/LICENSE,sha256=dW3nC4AX_VbxPAgneSDR-miZPiHgAYw5JhPtdbUEt_E,1091
6
+ qualys_mcp-2.1.4.dist-info/RECORD,,
qualys_mcp.py CHANGED
@@ -132,14 +132,17 @@ def get_eol_assets(stage_filter="EOL,EOL/EOS", limit=500):
132
132
  data = json.loads(resp.read())
133
133
  assets = []
134
134
  for a in data.get('assetListData', {}).get('asset', []):
135
- os_info = a.get('operatingSystem', {})
136
- lifecycle = os_info.get('lifecycle', {})
135
+ os_info = a.get('operatingSystem', {}) or {}
136
+ lifecycle = os_info.get('lifecycle', {}) or {}
137
+ os_name = os_info.get('osName', '') or os_info.get('fullName', '') or ''
138
+ if os_info.get('version'):
139
+ os_name = f"{os_name} {os_info.get('version', '')}".strip()
137
140
  assets.append({
138
141
  'assetId': a.get('assetId'),
139
142
  'address': a.get('address', ''),
140
143
  'dnsName': a.get('dnsHostName', ''),
141
144
  'operatingSystem': {
142
- 'osName': f"{os_info.get('name', '')} {os_info.get('version', '')}".strip(),
145
+ 'osName': os_name,
143
146
  'lifecycle': {
144
147
  'stage': lifecycle.get('stage', ''),
145
148
  'eolDate': lifecycle.get('eolDate', ''),
@@ -792,6 +795,57 @@ def get_webapp_vulns(severity: int = 4, limit: int = 50) -> dict:
792
795
  return result
793
796
 
794
797
 
798
+ @mcp.tool()
799
+ def debug_api(endpoint: str = "eol") -> dict:
800
+ """Debug API connectivity. Use endpoint='eol' to test EOL query, 'assets' for basic assets, 'auth' for auth test."""
801
+ result = {'endpoint': endpoint, 'gateway_url': GATEWAY_URL, 'base_url': BASE_URL}
802
+
803
+ if endpoint == 'auth':
804
+ token = get_bearer_token()
805
+ result['token_obtained'] = bool(token)
806
+ result['token_preview'] = token[:20] + '...' if token else None
807
+ return result
808
+
809
+ if endpoint == 'assets':
810
+ assets = get_assets(5)
811
+ result['count'] = len(assets)
812
+ result['sample'] = assets[:2] if assets else []
813
+ return result
814
+
815
+ if endpoint == 'eol':
816
+ url = f"{GATEWAY_URL}/rest/2.0/search/am/asset?pageSize=5"
817
+ token = get_bearer_token()
818
+ result['token_obtained'] = bool(token)
819
+
820
+ filter_body = json.dumps({
821
+ "filters": [
822
+ {"field": "operatingSystem.lifecycle.stage", "operator": "IN", "value": "EOL,EOL/EOS,EOS"}
823
+ ]
824
+ })
825
+ result['request_url'] = url
826
+ result['request_body'] = filter_body
827
+
828
+ req = Request(url, data=filter_body.encode(), method='POST')
829
+ req.add_header('Authorization', f'Bearer {token}' if token else f'Basic {BASIC_AUTH}')
830
+ req.add_header('Content-Type', 'application/json')
831
+ req.add_header('X-Requested-With', 'qualys-mcp')
832
+
833
+ try:
834
+ with urlopen(req, timeout=60) as resp:
835
+ raw = resp.read()
836
+ result['response_code'] = resp.status
837
+ result['response_length'] = len(raw)
838
+ data = json.loads(raw)
839
+ result['has_assetListData'] = 'assetListData' in data
840
+ result['asset_count'] = len(data.get('assetListData', {}).get('asset', []))
841
+ if result['asset_count'] > 0:
842
+ result['sample_asset'] = data['assetListData']['asset'][0]
843
+ except Exception as e:
844
+ result['error'] = str(e)
845
+
846
+ return result
847
+
848
+
795
849
  def main():
796
850
  mcp.run()
797
851
 
@@ -1,6 +0,0 @@
1
- qualys_mcp.py,sha256=DYP5SOqd9_E9XhHwwTsvIJxNSDec0NSJOV4fSYkw710,29479
2
- qualys_mcp-2.1.2.dist-info/METADATA,sha256=Ii3I5JnPtgCKWiBFWtLej4sjq--SWuWNrGepJUTRAU4,3295
3
- qualys_mcp-2.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
4
- qualys_mcp-2.1.2.dist-info/entry_points.txt,sha256=Dc8X0AhJDjGaZOJ0SNpWDWjEX4sYzrYa9FZEbggX0Rs,47
5
- qualys_mcp-2.1.2.dist-info/licenses/LICENSE,sha256=dW3nC4AX_VbxPAgneSDR-miZPiHgAYw5JhPtdbUEt_E,1091
6
- qualys_mcp-2.1.2.dist-info/RECORD,,