qualys-mcp 2.1.3__tar.gz → 2.1.4__tar.gz

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.3
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "qualys-mcp"
7
- version = "2.1.3"
7
+ version = "2.1.4"
8
8
  description = "MCP server for Qualys security APIs - natural language interaction with vulnerability, asset, and cloud security data"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -795,6 +795,57 @@ def get_webapp_vulns(severity: int = 4, limit: int = 50) -> dict:
795
795
  return result
796
796
 
797
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
+
798
849
  def main():
799
850
  mcp.run()
800
851
 
File without changes
File without changes
File without changes