cloudg 0.5.2__tar.gz → 0.6.0__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.
Files changed (381) hide show
  1. {cloudg-0.5.2 → cloudg-0.6.0}/PKG-INFO +315 -20
  2. {cloudg-0.5.2 → cloudg-0.6.0}/README.md +37 -8
  3. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/__init__.py +2 -2
  4. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/api.py +86 -307
  5. cloudg-0.6.0/cloudg/api_scanners.py +255 -0
  6. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/cli.py +97 -63
  7. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/cli_commands.py +31 -30
  8. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/cli_helpers.py +2 -2
  9. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/cli_inventory.py +2 -2
  10. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/cli_run_helpers.py +64 -34
  11. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/__init__.py +1 -1
  12. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/aws.py +137 -66
  13. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/aws_services.py +3 -1
  14. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/aws_services_extended.py +5 -3
  15. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/azure.py +49 -22
  16. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/gcp.py +62 -53
  17. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/multi.py +92 -41
  18. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/config.py +216 -16
  19. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/coverage.py +1 -1
  20. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/credentials.py +83 -35
  21. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/graph/builder.py +128 -13
  22. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/graph/ontology.py +27 -17
  23. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/graph/ontology_rules.py +255 -77
  24. cloudg-0.6.0/cloudg/graph/ports.py +174 -0
  25. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/graph/rag_export.py +135 -80
  26. cloudg-0.6.0/cloudg/graph/reachability.py +527 -0
  27. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/ingest.py +1 -1
  28. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/__init__.py +1 -1
  29. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_deep.py +3 -3
  30. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/_common.py +40 -1
  31. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/alarms.py +2 -2
  32. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_deep.py +1 -1
  33. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/__init__.py +2 -2
  34. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/query.py +50 -8
  35. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/dependencies.py +205 -18
  36. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_deep.py +4 -1
  37. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/context.py +2 -2
  38. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/iam.py +2 -1
  39. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/names.py +21 -4
  40. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/kubernetes.py +1 -1
  41. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/linker.py +9 -185
  42. cloudg-0.6.0/cloudg/inventory/linker_rules.py +252 -0
  43. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/mapper.py +104 -41
  44. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/mapper_result.py +33 -20
  45. cloudg-0.6.0/cloudg/mcp/__init__.py +138 -0
  46. cloudg-0.6.0/cloudg/mcp/__main__.py +10 -0
  47. cloudg-0.6.0/cloudg/mcp/adapters/__init__.py +232 -0
  48. cloudg-0.6.0/cloudg/mcp/adapters/_common.py +276 -0
  49. cloudg-0.6.0/cloudg/mcp/adapters/export.py +259 -0
  50. cloudg-0.6.0/cloudg/mcp/adapters/fastmcp.py +583 -0
  51. cloudg-0.6.0/cloudg/mcp/adapters/lowlevel.py +593 -0
  52. cloudg-0.6.0/cloudg/mcp/adapters/mcp_sdk.py +108 -0
  53. cloudg-0.6.0/cloudg/mcp/catalog/__init__.py +89 -0
  54. cloudg-0.6.0/cloudg/mcp/catalog/_common.py +545 -0
  55. cloudg-0.6.0/cloudg/mcp/catalog/_compliance.py +330 -0
  56. cloudg-0.6.0/cloudg/mcp/catalog/_findings.py +236 -0
  57. cloudg-0.6.0/cloudg/mcp/catalog/_graph.py +480 -0
  58. cloudg-0.6.0/cloudg/mcp/catalog/_graph_impact.py +400 -0
  59. cloudg-0.6.0/cloudg/mcp/catalog/_inventory.py +292 -0
  60. cloudg-0.6.0/cloudg/mcp/catalog/_live.py +445 -0
  61. cloudg-0.6.0/cloudg/mcp/catalog/_resources.py +161 -0
  62. cloudg-0.6.0/cloudg/mcp/catalog/_sparql.py +247 -0
  63. cloudg-0.6.0/cloudg/mcp/catalog/compliance.py +212 -0
  64. cloudg-0.6.0/cloudg/mcp/catalog/export.py +180 -0
  65. cloudg-0.6.0/cloudg/mcp/catalog/findings.py +499 -0
  66. cloudg-0.6.0/cloudg/mcp/catalog/graph.py +384 -0
  67. cloudg-0.6.0/cloudg/mcp/catalog/inventory.py +387 -0
  68. cloudg-0.6.0/cloudg/mcp/catalog/live.py +449 -0
  69. cloudg-0.6.0/cloudg/mcp/catalog/meta.py +293 -0
  70. cloudg-0.6.0/cloudg/mcp/catalog/ontology.py +352 -0
  71. cloudg-0.6.0/cloudg/mcp/catalog/privacy.py +424 -0
  72. cloudg-0.6.0/cloudg/mcp/catalog/prompts.py +587 -0
  73. cloudg-0.6.0/cloudg/mcp/catalog/resources.py +602 -0
  74. cloudg-0.6.0/cloudg/mcp/catalog/workspace.py +240 -0
  75. cloudg-0.6.0/cloudg/mcp/cli.py +471 -0
  76. cloudg-0.6.0/cloudg/mcp/cli_common.py +238 -0
  77. cloudg-0.6.0/cloudg/mcp/cli_config.py +148 -0
  78. cloudg-0.6.0/cloudg/mcp/content.py +275 -0
  79. cloudg-0.6.0/cloudg/mcp/context.py +162 -0
  80. cloudg-0.6.0/cloudg/mcp/core.py +643 -0
  81. cloudg-0.6.0/cloudg/mcp/layer.py +653 -0
  82. cloudg-0.6.0/cloudg/mcp/layer_options.py +96 -0
  83. cloudg-0.6.0/cloudg/mcp/layer_output.py +452 -0
  84. cloudg-0.6.0/cloudg/mcp/middleware.py +619 -0
  85. cloudg-0.6.0/cloudg/mcp/native/__init__.py +61 -0
  86. cloudg-0.6.0/cloudg/mcp/native/_http_io.py +404 -0
  87. cloudg-0.6.0/cloudg/mcp/native/_http_sessions.py +262 -0
  88. cloudg-0.6.0/cloudg/mcp/native/_methods.py +238 -0
  89. cloudg-0.6.0/cloudg/mcp/native/_shared.py +178 -0
  90. cloudg-0.6.0/cloudg/mcp/native/auth.py +229 -0
  91. cloudg-0.6.0/cloudg/mcp/native/http.py +597 -0
  92. cloudg-0.6.0/cloudg/mcp/native/jsonrpc.py +152 -0
  93. cloudg-0.6.0/cloudg/mcp/native/protocol.py +608 -0
  94. cloudg-0.6.0/cloudg/mcp/native/stdio.py +215 -0
  95. cloudg-0.6.0/cloudg/mcp/policies/airgapped.yaml +8 -0
  96. cloudg-0.6.0/cloudg/mcp/policies/audit.yaml +15 -0
  97. cloudg-0.6.0/cloudg/mcp/policies/open.yaml +11 -0
  98. cloudg-0.6.0/cloudg/mcp/policies/read_only.yaml +8 -0
  99. cloudg-0.6.0/cloudg/mcp/policies/soc-analyst.yaml +64 -0
  100. cloudg-0.6.0/cloudg/mcp/policies/standard.yaml +56 -0
  101. cloudg-0.6.0/cloudg/mcp/policies/strict.yaml +62 -0
  102. cloudg-0.6.0/cloudg/mcp/policy/__init__.py +133 -0
  103. cloudg-0.6.0/cloudg/mcp/policy/access.py +260 -0
  104. cloudg-0.6.0/cloudg/mcp/policy/audit.py +184 -0
  105. cloudg-0.6.0/cloudg/mcp/policy/config.py +229 -0
  106. cloudg-0.6.0/cloudg/mcp/policy/describe.py +98 -0
  107. cloudg-0.6.0/cloudg/mcp/policy/engine.py +346 -0
  108. cloudg-0.6.0/cloudg/mcp/policy/pipelines.py +282 -0
  109. cloudg-0.6.0/cloudg/mcp/policy/ratelimit.py +95 -0
  110. cloudg-0.6.0/cloudg/mcp/policy/vaults.py +48 -0
  111. cloudg-0.6.0/cloudg/mcp/schema.py +198 -0
  112. cloudg-0.6.0/cloudg/mcp/server.py +744 -0
  113. cloudg-0.6.0/cloudg/mcp/state/__init__.py +72 -0
  114. cloudg-0.6.0/cloudg/mcp/state/base.py +83 -0
  115. cloudg-0.6.0/cloudg/mcp/state/dataset.py +592 -0
  116. cloudg-0.6.0/cloudg/mcp/state/diff.py +140 -0
  117. cloudg-0.6.0/cloudg/mcp/state/loading.py +358 -0
  118. cloudg-0.6.0/cloudg/mcp/state/workspace.py +347 -0
  119. cloudg-0.6.0/cloudg/mcp/transforms/__init__.py +230 -0
  120. cloudg-0.6.0/cloudg/mcp/transforms/annotation.py +518 -0
  121. cloudg-0.6.0/cloudg/mcp/transforms/base.py +83 -0
  122. cloudg-0.6.0/cloudg/mcp/transforms/builtin_detectors.py +399 -0
  123. cloudg-0.6.0/cloudg/mcp/transforms/builtin_rules.py +221 -0
  124. cloudg-0.6.0/cloudg/mcp/transforms/detectors.py +370 -0
  125. cloudg-0.6.0/cloudg/mcp/transforms/entities.py +366 -0
  126. cloudg-0.6.0/cloudg/mcp/transforms/projection.py +403 -0
  127. cloudg-0.6.0/cloudg/mcp/transforms/redaction.py +475 -0
  128. cloudg-0.6.0/cloudg/mcp/transforms/redaction_run.py +612 -0
  129. cloudg-0.6.0/cloudg/mcp/transforms/strategies.py +222 -0
  130. cloudg-0.6.0/cloudg/mcp/transforms/substitution.py +578 -0
  131. cloudg-0.6.0/cloudg/mcp/transforms/textscan.py +95 -0
  132. cloudg-0.6.0/cloudg/mcp/transforms/vault.py +531 -0
  133. cloudg-0.6.0/cloudg/mcp/transforms/vault_formats.py +561 -0
  134. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/normaliser.py +9 -9
  135. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/policies/custodian-aws-security.yml +1 -1
  136. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/policies/custodian-azure.yml +1 -1
  137. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/policies/custodian-gcp.yml +1 -1
  138. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/registry.py +1 -1
  139. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/html_report.py +1 -1
  140. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/svg.py +3 -3
  141. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/svg_layout.py +46 -27
  142. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/terraform_export.py +6 -6
  143. cloudg-0.6.0/cloudg/resilience/__init__.py +127 -0
  144. cloudg-0.6.0/cloudg/resilience/aws.py +395 -0
  145. cloudg-0.6.0/cloudg/resilience/azure.py +344 -0
  146. cloudg-0.6.0/cloudg/resilience/breaker.py +239 -0
  147. cloudg-0.6.0/cloudg/resilience/errors.py +490 -0
  148. cloudg-0.6.0/cloudg/resilience/gcp.py +169 -0
  149. cloudg-0.6.0/cloudg/resilience/governor.py +441 -0
  150. cloudg-0.6.0/cloudg/resilience/guard.py +494 -0
  151. cloudg-0.6.0/cloudg/resilience/limiter.py +758 -0
  152. cloudg-0.6.0/cloudg/resilience/retry.py +290 -0
  153. cloudg-0.6.0/cloudg/resilience/stats.py +354 -0
  154. cloudg-0.6.0/cloudg/retry.py +180 -0
  155. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/check_equivalence.yaml +1 -1
  156. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/cis_aws_v3.yaml +2 -2
  157. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/cis_azure_v2.yaml +1 -1
  158. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/cis_gcp_v3.yaml +1 -1
  159. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/aws_foundational_security_best_practices_aws.yaml +1 -1
  160. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/cis_5.0_aws.yaml +1 -1
  161. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/cis_5.0_azure.yaml +1 -1
  162. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/cis_5.0_gcp.yaml +1 -1
  163. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/gdpr_aws.yaml +1 -1
  164. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/hipaa_aws.yaml +1 -1
  165. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/hipaa_azure.yaml +1 -1
  166. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/hipaa_gcp.yaml +1 -1
  167. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/iso27001_2022_aws.yaml +1 -1
  168. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/iso27001_2022_azure.yaml +1 -1
  169. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/iso27001_2022_gcp.yaml +1 -1
  170. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/mitre_attack_aws.yaml +1 -1
  171. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/mitre_attack_azure.yaml +1 -1
  172. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/mitre_attack_gcp.yaml +1 -1
  173. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/nist_800_53_revision_5_aws.yaml +1 -1
  174. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/nist_csf_2.0_aws.yaml +1 -1
  175. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/pci_4.0_aws.yaml +1 -1
  176. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/pci_4.0_azure.yaml +1 -1
  177. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/pci_4.0_gcp.yaml +1 -1
  178. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/soc2_aws.yaml +3 -3
  179. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/soc2_azure.yaml +3 -3
  180. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/soc2_gcp.yaml +4 -4
  181. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/gdpr.yaml +1 -1
  182. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/hipaa_security_rule.yaml +6 -6
  183. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/iso_27001_2022.yaml +1 -1
  184. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/nist_800_53.yaml +8 -8
  185. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/pci_dss_v4.yaml +1 -1
  186. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/soc2_tsc.yaml +1 -1
  187. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/checkov.py +1 -1
  188. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/prowler.py +3 -3
  189. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/scoutsuite.py +1 -1
  190. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/templates/report.html.j2 +1 -1
  191. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/ui.py +8 -8
  192. {cloudg-0.5.2 → cloudg-0.6.0}/docs/DOCUMENTATION.md +309 -18
  193. cloudg-0.6.0/docs/INVENTORY_CATALOG.md +2895 -0
  194. cloudg-0.6.0/docs/INVENTORY_INTERNALS.md +1380 -0
  195. cloudg-0.6.0/docs/INVENTORY_REFERENCE.md +1518 -0
  196. cloudg-0.6.0/docs/MCP.md +2902 -0
  197. cloudg-0.6.0/docs/MCP_INTERNALS.md +1553 -0
  198. cloudg-0.6.0/docs/MCP_PRIVACY.md +1913 -0
  199. cloudg-0.6.0/docs/MCP_TOOLS.md +11755 -0
  200. cloudg-0.6.0/docs/RESILIENCE.md +733 -0
  201. {cloudg-0.5.2 → cloudg-0.6.0}/pyproject.toml +16 -5
  202. {cloudg-0.5.2 → cloudg-0.6.0}/tests/conftest.py +39 -0
  203. cloudg-0.6.0/tests/mcp/__init__.py +0 -0
  204. cloudg-0.6.0/tests/mcp/_adapter_testkit.py +157 -0
  205. cloudg-0.6.0/tests/mcp/conftest.py +91 -0
  206. cloudg-0.6.0/tests/mcp/fake_secrets.py +39 -0
  207. cloudg-0.6.0/tests/mcp/fixtures/__init__.py +1 -0
  208. cloudg-0.6.0/tests/mcp/fixtures/sample_estate.py +905 -0
  209. cloudg-0.6.0/tests/mcp/test_adapters_args_model.py +117 -0
  210. cloudg-0.6.0/tests/mcp/test_adapters_export.py +111 -0
  211. cloudg-0.6.0/tests/mcp/test_adapters_fastmcp.py +206 -0
  212. cloudg-0.6.0/tests/mcp/test_adapters_sdk.py +341 -0
  213. cloudg-0.6.0/tests/mcp/test_adapters_serve.py +414 -0
  214. cloudg-0.6.0/tests/mcp/test_catalog_audit_fixes.py +451 -0
  215. cloudg-0.6.0/tests/mcp/test_catalog_compliance.py +91 -0
  216. cloudg-0.6.0/tests/mcp/test_catalog_export_live_meta.py +596 -0
  217. cloudg-0.6.0/tests/mcp/test_catalog_findings.py +222 -0
  218. cloudg-0.6.0/tests/mcp/test_catalog_graph.py +222 -0
  219. cloudg-0.6.0/tests/mcp/test_catalog_inventory.py +204 -0
  220. cloudg-0.6.0/tests/mcp/test_catalog_ontology.py +134 -0
  221. cloudg-0.6.0/tests/mcp/test_catalog_prompts.py +162 -0
  222. cloudg-0.6.0/tests/mcp/test_catalog_registry.py +237 -0
  223. cloudg-0.6.0/tests/mcp/test_catalog_regressions.py +374 -0
  224. cloudg-0.6.0/tests/mcp/test_catalog_resources.py +259 -0
  225. cloudg-0.6.0/tests/mcp/test_catalog_workspace.py +108 -0
  226. cloudg-0.6.0/tests/mcp/test_cli_mcp.py +248 -0
  227. cloudg-0.6.0/tests/mcp/test_middleware.py +318 -0
  228. cloudg-0.6.0/tests/mcp/test_native_auth.py +75 -0
  229. cloudg-0.6.0/tests/mcp/test_native_http.py +650 -0
  230. cloudg-0.6.0/tests/mcp/test_native_privacy.py +200 -0
  231. cloudg-0.6.0/tests/mcp/test_native_protocol.py +580 -0
  232. cloudg-0.6.0/tests/mcp/test_native_stdio.py +247 -0
  233. cloudg-0.6.0/tests/mcp/test_policy.py +753 -0
  234. cloudg-0.6.0/tests/mcp/test_policy_regressions.py +697 -0
  235. cloudg-0.6.0/tests/mcp/test_privacy_tools.py +528 -0
  236. cloudg-0.6.0/tests/mcp/test_state.py +437 -0
  237. cloudg-0.6.0/tests/mcp/test_transforms.py +1401 -0
  238. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_aws_application.py +21 -0
  239. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_aws_network_ext.py +2 -2
  240. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_collectors.py +57 -0
  241. cloudg-0.6.0/tests/test_graph_builder.py +253 -0
  242. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_inventory_deep.py +33 -1
  243. cloudg-0.6.0/tests/test_ontology.py +1091 -0
  244. cloudg-0.6.0/tests/test_ports.py +392 -0
  245. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_rag_export.py +131 -0
  246. cloudg-0.6.0/tests/test_reachability.py +513 -0
  247. cloudg-0.6.0/tests/test_resilience.py +1884 -0
  248. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_scanner_orchestration.py +1 -1
  249. cloudg-0.5.2/cloudg/graph/reachability.py +0 -261
  250. cloudg-0.5.2/cloudg/retry.py +0 -106
  251. cloudg-0.5.2/tests/test_ontology.py +0 -389
  252. {cloudg-0.5.2 → cloudg-0.6.0}/.gitignore +0 -0
  253. {cloudg-0.5.2 → cloudg-0.6.0}/LICENSE +0 -0
  254. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/base.py +0 -0
  255. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/gcp_assets.py +0 -0
  256. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/graph/__init__.py +0 -0
  257. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/_util.py +0 -0
  258. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_deep_tasks.py +0 -0
  259. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/__init__.py +0 -0
  260. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/_base.py +0 -0
  261. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/_policy_grants.py +0 -0
  262. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/__init__.py +0 -0
  263. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/backup.py +0 -0
  264. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/batch.py +0 -0
  265. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/build_deploy.py +0 -0
  266. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/cicd.py +0 -0
  267. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/compute.py +0 -0
  268. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/containers_ext.py +0 -0
  269. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/firehose.py +0 -0
  270. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/integration.py +0 -0
  271. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/logs.py +0 -0
  272. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/operations.py +0 -0
  273. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/serverless_ext.py +0 -0
  274. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/cloudcontrol.py +0 -0
  275. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/containers/__init__.py +0 -0
  276. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/containers/ecr.py +0 -0
  277. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/containers/ecs.py +0 -0
  278. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/containers/eks.py +0 -0
  279. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/__init__.py +0 -0
  280. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/_common.py +0 -0
  281. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/analytics.py +0 -0
  282. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/bedrock.py +0 -0
  283. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/databases.py +0 -0
  284. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/glue.py +0 -0
  285. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/glue_etl.py +0 -0
  286. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/lakeformation.py +0 -0
  287. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/messaging.py +0 -0
  288. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/registry.py +0 -0
  289. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/sagemaker.py +0 -0
  290. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/serverless_data.py +0 -0
  291. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/transfer.py +0 -0
  292. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/__init__.py +0 -0
  293. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/_common.py +0 -0
  294. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/cognito.py +0 -0
  295. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/data_protection.py +0 -0
  296. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/iam_extras.py +0 -0
  297. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/identity_center.py +0 -0
  298. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/ram.py +0 -0
  299. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/s3_access_points.py +0 -0
  300. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/service_catalog.py +0 -0
  301. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/sharing.py +0 -0
  302. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/stack_sets.py +0 -0
  303. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/identity.py +0 -0
  304. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/__init__.py +0 -0
  305. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/_common.py +0 -0
  306. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/apigateway.py +0 -0
  307. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/cloudfront.py +0 -0
  308. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/direct_connect.py +0 -0
  309. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/elb_rules.py +0 -0
  310. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/global_accelerator.py +0 -0
  311. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/lattice.py +0 -0
  312. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/network_manager.py +0 -0
  313. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/privatelink.py +0 -0
  314. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/resolver.py +0 -0
  315. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/tgw.py +0 -0
  316. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/vpn.py +0 -0
  317. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/__init__.py +0 -0
  318. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/compute_fabric.py +0 -0
  319. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/data.py +0 -0
  320. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/dns_iac_ops.py +0 -0
  321. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/load_balancing.py +0 -0
  322. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/storage.py +0 -0
  323. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/security/__init__.py +0 -0
  324. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/security/_common.py +0 -0
  325. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/security/detection.py +0 -0
  326. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/security/network_protection.py +0 -0
  327. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/security/posture.py +0 -0
  328. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/serverless/__init__.py +0 -0
  329. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/serverless/apigateway.py +0 -0
  330. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/serverless/functions.py +0 -0
  331. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/serverless/messaging.py +0 -0
  332. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/arm_types.py +0 -0
  333. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/builder.py +0 -0
  334. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/compute.py +0 -0
  335. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/data_security.py +0 -0
  336. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/edge.py +0 -0
  337. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/helpers.py +0 -0
  338. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/network.py +0 -0
  339. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/registry.py +0 -0
  340. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/web_containers.py +0 -0
  341. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_hierarchy.py +0 -0
  342. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/catalogs/__init__.py +0 -0
  343. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/catalogs/aws_arn_types.yaml +0 -0
  344. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/catalogs/aws_cloudcontrol.yaml +0 -0
  345. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/catalogs/azure_arm_types.yaml +0 -0
  346. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/catalogs/gcp_asset_types.yaml +0 -0
  347. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_hierarchy.py +0 -0
  348. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/__init__.py +0 -0
  349. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/compute.py +0 -0
  350. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/data_logging.py +0 -0
  351. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/gke.py +0 -0
  352. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/load_balancing.py +0 -0
  353. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/messaging.py +0 -0
  354. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/network.py +0 -0
  355. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/pipelines.py +0 -0
  356. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/resource_manager.py +0 -0
  357. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/serverless.py +0 -0
  358. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/linker_index.py +0 -0
  359. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/organization.py +0 -0
  360. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/organization_assets.py +0 -0
  361. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/policies/custodian.yml +0 -0
  362. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/region_discovery.py +0 -0
  363. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/__init__.py +0 -0
  364. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/json_export.py +0 -0
  365. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/__init__.py +0 -0
  366. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/iam_linter.py +0 -0
  367. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/trivy.py +0 -0
  368. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/schema/__init__.py +0 -0
  369. {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/schema/models.py +0 -0
  370. {cloudg-0.5.2 → cloudg-0.6.0}/tests/__init__.py +0 -0
  371. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_api.py +0 -0
  372. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_aws_data_ml.py +0 -0
  373. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_aws_governance.py +0 -0
  374. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_azure_inventory.py +0 -0
  375. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_catalogs_cloudcontrol.py +0 -0
  376. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_gcp_inventory.py +0 -0
  377. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_ingest.py +0 -0
  378. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_inventory.py +0 -0
  379. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_normaliser.py +0 -0
  380. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_region_discovery.py +0 -0
  381. {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_terraform_export.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cloudg
3
- Version: 0.5.2
3
+ Version: 0.6.0
4
4
  Summary: cloudg (cloud graphing) - multi-cloud infrastructure mapping, knowledge graphs and security intelligence for AWS, Azure and GCP
5
5
  Project-URL: Homepage, https://github.com/morpheuslord/cloudg
6
6
  Project-URL: Repository, https://github.com/morpheuslord/cloudg
@@ -9,7 +9,7 @@ Project-URL: Changelog, https://github.com/morpheuslord/cloudg/blob/main/CHANGEL
9
9
  Author-email: morpheuslord <chiranjeevi.naidu@proton.me>
10
10
  License: MIT
11
11
  License-File: LICENSE
12
- Keywords: audit,aws,azure,cloud,gcp,graph,ontology,security,terraform
12
+ Keywords: audit,aws,azure,cloud,gcp,graph,mcp,model-context-protocol,ontology,security,terraform
13
13
  Classifier: Development Status :: 4 - Beta
14
14
  Classifier: Environment :: Console
15
15
  Classifier: Intended Audience :: Information Technology
@@ -44,6 +44,7 @@ Requires-Dist: azure-mgmt-storage>=21.0; extra == 'all'
44
44
  Requires-Dist: boto3>=1.28; extra == 'all'
45
45
  Requires-Dist: google-auth>=2.22; extra == 'all'
46
46
  Requires-Dist: google-cloud-asset>=3.0; extra == 'all'
47
+ Requires-Dist: mcp<3,>=1.30; extra == 'all'
47
48
  Provides-Extra: aws
48
49
  Requires-Dist: aioboto3>=12.0; extra == 'aws'
49
50
  Requires-Dist: boto3>=1.28; extra == 'aws'
@@ -59,6 +60,7 @@ Requires-Dist: azure-mgmt-storage>=21.0; extra == 'azure'
59
60
  Provides-Extra: dev
60
61
  Requires-Dist: aioboto3>=12.0; extra == 'dev'
61
62
  Requires-Dist: boto3>=1.28; extra == 'dev'
63
+ Requires-Dist: mcp<3,>=1.30; extra == 'dev'
62
64
  Requires-Dist: moto[all]>=5.0; extra == 'dev'
63
65
  Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
64
66
  Requires-Dist: pytest>=7.0; extra == 'dev'
@@ -70,6 +72,8 @@ Requires-Dist: weasyprint>=60.0; extra == 'full'
70
72
  Provides-Extra: gcp
71
73
  Requires-Dist: google-auth>=2.22; extra == 'gcp'
72
74
  Requires-Dist: google-cloud-asset>=3.0; extra == 'gcp'
75
+ Provides-Extra: mcp
76
+ Requires-Dist: mcp<3,>=1.30; extra == 'mcp'
73
77
  Description-Content-Type: text/markdown
74
78
 
75
79
  <p align="center">
@@ -84,9 +88,17 @@ This is the complete reference: every CLI command, the exact input and output fo
84
88
 
85
89
  The inventory mapper (`cloudg map`, `cloudg deps`, `cloudg.inventory`) has three deeper companion documents:
86
90
 
87
- - [Inventory reference](INVENTORY_REFERENCE.md): the structure of every return value and exported file, field by field, with annotated examples (`CloudAsset`, `NetworkEdge`, relation objects, `InventoryResult`, `summary`, coverage records, dependency trees, blast radius, security coverage, `inventory-map.json`, `inventory-graph.json`, GraphML, `inventory-organization.json`, `asset-map.json`, `compliance-map.json`, `cloudg deps --json`).
88
- - [Inventory catalog](INVENTORY_CATALOG.md): all 156 asset types with the native resource types mapped to each, the metadata keys and relations each one carries, and the full relationship matrix.
89
- - [Inventory internals](INVENTORY_INTERNALS.md): how a mapping run works, every module, the 133 AWS collector tasks, the linker's resolution rules, the Azure and GCP extractor frameworks, dependency semantics, catalogs, and recipes for adding collectors, extractors and asset types.
91
+ - [Inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md): the structure of every return value and exported file, field by field, with annotated examples (`CloudAsset`, `NetworkEdge`, relation objects, `InventoryResult`, `summary`, coverage records, dependency trees, blast radius, security coverage, `inventory-map.json`, `inventory-graph.json`, GraphML, `inventory-organization.json`, `asset-map.json`, `compliance-map.json`, `cloudg deps --json`).
92
+ - [Inventory catalog](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_CATALOG.md): all 156 asset types with the native resource types mapped to each, the metadata keys and relations each one carries, and the full relationship matrix.
93
+ - [Inventory internals](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_INTERNALS.md): how a mapping run works, every module, the 133 AWS collector tasks, the linker's resolution rules, the Azure and GCP extractor frameworks, dependency semantics, catalogs, and recipes for adding collectors, extractors and asset types.
94
+
95
+ Since 0.6.0 cloudg also runs as an MCP server for AI agents, and paces every cloud API call it makes. Both have their own documents on GitHub:
96
+
97
+ - [MCP guide](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP.md): installing and serving the MCP layer, client configuration, transports and protocol versions, mounting it into an existing MCP server, in-process use, middleware, the live tools.
98
+ - [MCP tool reference](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_TOOLS.md): every tool, resource, resource template and prompt, with arguments and result shapes.
99
+ - [MCP privacy](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_PRIVACY.md): policies, profiles, roles, transforms, detectors and the pseudonym vault.
100
+ - [MCP internals](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_INTERNALS.md): how the layer is built, for contributors.
101
+ - [Rate limits and throttling](https://github.com/morpheuslord/cloudg/blob/main/docs/RESILIENCE.md): the shared rate limiter, circuit breakers and retries, per-provider wiring, every `ratelimit` key, and the guard on live collections.
90
102
 
91
103
  Contents:
92
104
 
@@ -99,6 +111,8 @@ Contents:
99
111
  - [Configuration](#configuration)
100
112
  - [Python API](#python-api)
101
113
  - [Integration recipes](#integration-recipes)
114
+ - [MCP server](#mcp-server)
115
+ - [Rate limits and throttling](#rate-limits-and-throttling)
102
116
  - [Plugin system](#plugin-system)
103
117
  - [Authentication](#authentication)
104
118
  - [Docker](#docker)
@@ -112,7 +126,8 @@ pip install cloudg # core
112
126
  pip install cloudg[aws] # + boto3, aioboto3
113
127
  pip install cloudg[azure] # + Azure management SDKs
114
128
  pip install cloudg[gcp] # + google-cloud-asset, google-auth
115
- pip install cloudg[all] # every provider SDK
129
+ pip install cloudg[mcp] # + the official MCP SDK (mcp>=1.30,<3), see MCP server
130
+ pip install cloudg[all] # every provider SDK and the MCP SDK
116
131
  ```
117
132
 
118
133
  The cloud SDKs are optional extras, pulled in only when you collect from that provider. The core package alone ships the graph engine, the ontology builder, the findings normaliser, the report renderers and `cloudg ingest`. That is enough to aggregate existing scan results on a machine with no cloud access at all.
@@ -287,7 +302,7 @@ cloudg deps --map ./reports # overview
287
302
  cloudg deps my-function --direction up --json # machine-readable
288
303
  ```
289
304
 
290
- Each edge is read as "dependent needs dependency": a function needs its role, image, key and the queue that triggers it; an ALB needs the WAF protecting it; a resource needs the stack managing it. `--direction up` shows what an asset needs, `down` what needs it (what breaks or changes with it), `both` the two trees. Without an asset, `deps` prints the most shared dependencies (one KMS key behind forty resources), the largest blast radius, and every cross-account edge. Assets are matched by ARN or resource ID, internal ID, or a unique name.
305
+ Each edge is read as "dependent needs dependency": a function needs its role, image, key and the queue that triggers it; an ALB needs the WAF protecting it; a resource needs the stack managing it. `--direction up` shows what an asset needs, `down` what needs it (what breaks or changes with it), `both` the two trees. Without an asset, `deps` prints the most shared dependencies (one KMS key behind forty resources), the largest blast radius, and every cross-account edge. Assets are matched by ARN or resource ID, internal ID, a unique name, or a unique ARN tail (the part after the last `/`).
291
306
 
292
307
  ### cloudg scan
293
308
 
@@ -400,7 +415,7 @@ All commands write into the output directory (`./reports` by default).
400
415
  | `findings.json` | the machine-readable result, structure below |
401
416
  | `raw-findings.json` | pre-normalisation findings (from `scan` and `ingest`) |
402
417
  | `topology.svg`, `topology.graphml`, `topology-cytoscape.json` | the graph in three formats |
403
- | `ontology.ttl`, `ontology.jsonld` | RDF ontology, around 62 inferred relation types, SPARQL-queryable |
418
+ | `ontology.ttl`, `ontology.jsonld` | RDF ontology, 64 inferred relation types, SPARQL-queryable |
404
419
  | `rag_chunks.jsonl`, `rag_metadata_index.json` | retrieval-ready chunks, one JSON object per line |
405
420
  | `terraform/*.tf.json`, `terraform/import.sh` | Terraform recreation of live infrastructure, 25+ asset types |
406
421
  | `inventory-map.json`, `inventory-map.graphml`, `inventory-graph.json` | scanner-independent inventory map: assets, interconnections, summary (`cloudg map`) |
@@ -408,7 +423,7 @@ All commands write into the output directory (`./reports` by default).
408
423
  | `inventory-organization.json` | AWS Organization / Control Tower topology (`cloudg map --org`) |
409
424
  | `asset-map.json`, `compliance-map.json` | inventory overlaid with scanner findings (`cloudg map --findings`) |
410
425
 
411
- The inventory files are specified field by field in the [inventory reference](INVENTORY_REFERENCE.md).
426
+ The inventory files are specified field by field in the [inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md).
412
427
 
413
428
  `findings.json` has this shape:
414
429
 
@@ -448,7 +463,7 @@ The unit every scanner and parser produces.
448
463
 
449
464
  | Field | Type | Notes |
450
465
  |---|---|---|
451
- | `id` | `str` | auto-generated UUID |
466
+ | `id` | `str` | auto-generated UUID; reachability findings get a deterministic UUID5 of the rule and the asset's stable key (its ARN, else its asset ID), so the same finding keeps its id from scan to scan |
452
467
  | `resource_id` | `str` | internal asset ID or native identifier |
453
468
  | `resource_arn` | `str \| None` | cloud-native identifier when known |
454
469
  | `severity` | `Severity` | `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, `INFO` |
@@ -471,14 +486,14 @@ The unit every scanner and parser produces.
471
486
  | `id` | `str` | auto UUID |
472
487
  | `arn` | `str \| None` | native identifier |
473
488
  | `name` | `str` | |
474
- | `asset_type` | `AssetType` | 156-value taxonomy covering compute, networking, storage, databases, IAM and identity federation, keys and secrets, logging, containers and Kubernetes, integration, data and ML platforms, DNS and deployment, security services and scanners, organization and governance, hybrid networking, `OTHER`; the full list with what maps to each is in the [inventory catalog](INVENTORY_CATALOG.md#asset-types-by-category) |
489
+ | `asset_type` | `AssetType` | 156-value taxonomy covering compute, networking, storage, databases, IAM and identity federation, keys and secrets, logging, containers and Kubernetes, integration, data and ML platforms, DNS and deployment, security services and scanners, organization and governance, hybrid networking, `OTHER`; the full list with what maps to each is in the [inventory catalog](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_CATALOG.md#asset-types-by-category) |
475
490
  | `provider` | `CloudProvider` | `AWS`, `AZURE`, `GCP` |
476
491
  | `region` | `str` | `"global"` for regionless resources |
477
492
  | `account_id` | `str \| None` | |
478
493
  | `tags` | `dict[str, str]` | |
479
494
  | `metadata` | `dict[str, Any]` | normalised extra attributes; inventory assets also carry declared `relations` and identifier `aliases` |
480
495
  | `collected_at` | `datetime` | when the asset was built |
481
- | `is_internet_exposed` | `bool` | set by the collector or the graph analysis |
496
+ | `is_internet_exposed` | `bool` | set by the collector; the reachability analysis marks the graph nodes it reaches and reports them as findings, but does not change the asset |
482
497
  | `raw_data` | `dict[str, Any]` | raw API payload, never serialised |
483
498
  | `display_id` | computed `str` | `arn` or `id` |
484
499
 
@@ -491,7 +506,7 @@ Directed edge between two asset IDs (`source_id`, `target_id`); direction always
491
506
  | Field | Type | Notes |
492
507
  |---|---|---|
493
508
  | `id` | `str` | auto UUID |
494
- | `source_id`, `target_id` | `str` | asset IDs; network rule edges can use a CIDR or native security group ID as an endpoint |
509
+ | `source_id`, `target_id` | `str` | asset IDs; the far side of a network rule edge can be a CIDR (or an Azure service tag) instead of an asset ID |
495
510
  | `edge_type` | `EdgeType` | coarse class, below |
496
511
  | `relationship` | `str \| None` | fine-grained ontology relation (`TRIGGERED_BY`, `RUNS_ON`, `CROSS_ACCOUNT_TRUST`, `ENCRYPTED_BY_KMS`, ...) |
497
512
  | `properties` | `dict[str, Any]` | relation detail: trust conditions, granted actions, EKS access policies, notification events, `cross_account`, `external_reference`, `hierarchy` |
@@ -508,7 +523,7 @@ Directed edge between two asset IDs (`source_id`, `target_id`); direction always
508
523
  | Workload | `INVOKES`, `USES_IMAGE`, `LOGS_TO` |
509
524
  | Security and governance | `PROTECTS`, `MONITORS`, `MANAGES`, `GOVERNS` |
510
525
 
511
- What each one means, its typical endpoints and how it counts for dependency analysis is in the [inventory reference](INVENTORY_REFERENCE.md#6-edge-types-and-direction); every observed source type → relationship → target type combination is in the [relationship matrix](INVENTORY_CATALOG.md#relationship-matrix).
526
+ What each one means, its typical endpoints and how it counts for dependency analysis is in the [inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md#6-edge-types-and-direction); every observed source type → relationship → target type combination is in the [relationship matrix](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_CATALOG.md#relationship-matrix).
512
527
 
513
528
  ### ComplianceResult
514
529
 
@@ -620,9 +635,38 @@ report:
620
635
  rulesets:
621
636
  rules_dir: null # defaults to the packaged cloudg/rules/
622
637
 
638
+ ratelimit: # cloud API throttling resilience (0.6.0)
639
+ enabled: true
640
+ aws: # azure and gcp take the same keys
641
+ max_rps: null # null = built-in (AWS 20, Azure 15, GCP 10 per service)
642
+ burst: null
643
+ account_max_rps: null # per account / subscription / project (Azure 20)
644
+ account_burst: null # (Azure 200)
645
+ global_max_rps: null # everything this process sends to the provider
646
+ global_burst: null
647
+ max_concurrency: null # requests in flight per account (AWS 128, Azure 16, GCP 16)
648
+ adaptive: true # halve on throttling, recover additively
649
+ min_rps: null
650
+ breaker_threshold: 5
651
+ breaker_cooldown_seconds: 60
652
+ max_retries: 8 # Azure SDK retries, GCP retries per page (AWS: aws.max_retries)
653
+ max_backoff_seconds: 60
654
+ deadline_seconds: 900
655
+ retry_budget: 500 # retries per provider, all SDKs, refilled by successes
656
+ services: {} # {ec2: {max_rps: 10, burst: 50}, "ec2.DescribeImages": {...}}
657
+ live_cooldown_seconds: null
658
+ live_cooldown_seconds: 120 # guard on live collections triggered through MCP
659
+ live_max_concurrent: 1
660
+ live_max_concurrent_total: 2
661
+ live_caller_max_operations: 0
662
+ live_caller_window_seconds: 3600
663
+ live_operation_timeout_seconds: 3600 # cancel a live collection after this (null: no limit)
664
+
623
665
  concurrency_limit: 5
624
666
  ```
625
667
 
668
+ `aws.max_retries` and `aws.retry_mode` configure botocore's own retries. They were accepted but ignored before 0.6.0; now they are applied to every AWS client. botocore counts `max_retries` after the first attempt, so the default of 10 allows up to 11 attempts per call. The `ratelimit` section is explained under [Rate limits and throttling](#rate-limits-and-throttling).
669
+
626
670
  In code, the same structure is `CloudGConfig`, a Pydantic model:
627
671
 
628
672
  ```python
@@ -699,7 +743,7 @@ print(inventory.summary) # services, types, regions, exposur
699
743
  inventory.export("./reports") # inventory-map.json / .graphml / graph
700
744
  ```
701
745
 
702
- `InventoryResult` carries `assets`, `edges`, `coverage`, `providers`, `regions`, `organization` (the discovered topology, when mapped) and `unresolved_references`, plus a computed `summary` (totals, per-service/type/region/account breakdowns, edge types and relationships, cross-account edges, external accounts, security service gaps, internet-exposed and unlinked counts). `export(dir)` writes `inventory-map.json`, `inventory-map.graphml`, `inventory-graph.json`, `inventory-dependencies.json` and, for an organization, `inventory-organization.json`; `InventoryResult.load(dir_or_file)` reads a map back.
746
+ `InventoryResult` carries `assets`, `edges`, `coverage`, `providers`, `regions`, `organization` (the discovered topology, when mapped), `unresolved_references` and `throttling` (the run's throttling telemetry, or `None` when no API pushed back), plus a computed `summary` (totals, per-service/type/region/account breakdowns, edge types and relationships, cross-account edges, external accounts, security service gaps, internet-exposed and unlinked counts). `export(dir)` writes `inventory-map.json`, `inventory-map.graphml`, `inventory-graph.json`, `inventory-dependencies.json` and, for an organization, `inventory-organization.json`; `InventoryResult.load(dir_or_file)` reads a map back.
703
747
 
704
748
  Interdependency questions go through `DependencyGraph`:
705
749
 
@@ -750,7 +794,7 @@ Your own collectors can declare relationships the same way the built-in ones do,
750
794
 
751
795
  The deep collectors are public too, when you want single-region, single-account control: `AWSDeepInventoryCollector(session, region="us-east-1", account_id=None, tagging_sweep=True, **options)` (keyword options `is_primary_region`, `services`, `exclude_services`, `kubernetes`, `kubernetes_timeout`, `iam_resource_edges`, `max_images_per_repository`, `stack_resources`, `cloud_control`, `cloud_control_types`, `cloud_control_exclude`, `cloud_control_concurrency`; an unknown option raises `TypeError`), `AzureDeepInventoryCollector(credential, subscription_id, graph_client_factory=None, use_resource_graph=True)` and `GCPDeepInventoryCollector(project_id, credentials=None, **options)` (keyword options `organization_id`, `scope`, `project_filter`, `skip_asset_types`, `page_size`, `include_iam`, `client`, `coverage`, `timeout`, `link_locally`) all implement the standard `collect()` / `collect_edges()` / `run()` collector interface. Their assets carry declared relations; pass them through `RelationshipLinker` for the typed edges.
752
796
 
753
- Every structure above, from `summary` to the dependency tree, is specified field by field in the [inventory reference](INVENTORY_REFERENCE.md).
797
+ Every structure above, from `summary` to the dependency tree, is specified field by field in the [inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md).
754
798
 
755
799
  ### The deeper toolkit
756
800
 
@@ -758,9 +802,10 @@ Modules the pipeline uses internally that are equally useful standalone:
758
802
 
759
803
  | Module | Class | What it does from code |
760
804
  |---|---|---|
761
- | `cloudg.graph.builder` | `GraphBuilder` | `build(assets, edges)` → NetworkX DiGraph; `find_attack_paths(src, dst)`, `find_lateral_movement_paths()`, `compute_centrality()` for blast-radius scoring; `to_d3_json()`, `to_cytoscape_json()`, `save_graphml()` / `load_graphml()` |
762
- | `cloudg.graph.reachability` | `ReachabilityAnalyzer` | BFS from the internet node over a built graph; `generate_findings()` returns exposure findings |
763
- | `cloudg.graph.ontology` | `CloudOntology` | `build(assets, edges, findings)` infers ~62 typed RDF relations; `save(path, fmt)` writes Turtle/JSON-LD/XML; query the graph with SPARQL via rdflib |
805
+ | `cloudg.graph.builder` | `GraphBuilder` | `build(assets, edges)` → NetworkX DiGraph, with parallel security group, NACL and internet-exposure rules between the same two nodes merged into one edge (`port_range` and `protocol` become comma lists); `find_attack_paths(src, dst)`, `find_lateral_movement_paths()`, `compute_centrality()` for blast-radius scoring; `to_d3_json()`, `to_cytoscape_json()`, `save_graphml()` / `load_graphml()` |
806
+ | `cloudg.graph.reachability` | `ReachabilityAnalyzer`, `network_flow_graph`, `finding_id`, `asset_key` | BFS from the internet over network-flow edges only. Entry points are `0.0.0.0/0`, `::/0` and the Azure `Internet`, `Any` and `*` sources. The walk follows `INTERNET_EXPOSED`, ingress `SECURITY_GROUP_RULE` and `NACL_RULE` edges, `ROUTE`, `PEERING` and `LOAD_BALANCER_TARGET` source to target; `ATTACHED_TO` backwards into a security group, NSG or NACL and forwards from a network interface or Elastic IP; `CONTAINS` only from a VPC, VNet or subnet. It never follows IAM edges, and not `INVOKES` either, so a function behind a public API gateway is not flagged. Security groups, NSGs, NACLs and target groups are hops: they are marked exposed but get no exposure finding of their own. `generate_findings()` returns exposure and sensitive-port findings with deterministic ids; `flow_hops()`, `flow_successors()`, `internet_entry_points()` and `network_flow_graph()` expose the same walk; `compute_blast_radius(node)` follows every edge type |
807
+ | `cloudg.graph.ports` | `parse_port_ranges`, `edge_port_ranges`, `port_in_ranges`, `is_internet_source` | the port and edge rules the builder, the reachability analysis and the ontology share: `port_range` strings parsed into numeric ranges, filter rule and egress checks, internet sources |
808
+ | `cloudg.graph.ontology` | `CloudOntology` | `build(assets, edges, findings)` infers 64 typed RDF relations; `save(path, fmt)` writes Turtle/JSON-LD/XML; query the graph with SPARQL via rdflib |
764
809
  | `cloudg.graph.rag_export` | `RAGExporter` | `export_all(...)` chunks the infrastructure three ways (entity, community, relation group) into JSONL for retrieval pipelines |
765
810
  | `cloudg.renderers.terraform_export` | `TerraformExporter` | `export(assets, edges)` recreates live infrastructure as `.tf.json` plus an `import.sh`; `preview(assets)` reports mappable coverage first |
766
811
  | `cloudg.normaliser` | `FindingsNormaliser` | the full dedupe / cross-scanner merge / CVSS rescore / compliance mapping pass, on any `list[Finding]` |
@@ -773,6 +818,65 @@ Modules the pipeline uses internally that are equally useful standalone:
773
818
  | `cloudg.inventory.catalogs` | `load_catalog`, `asset_type_map` | the YAML reference catalogs with `$CLOUDG_CATALOG_DIR` overlays |
774
819
  | `cloudg.registry` | `PluginRegistry` | entry-point discovery of collector and scanner plugins |
775
820
 
821
+ ### MCP layer API
822
+
823
+ `cloudg.mcp` exposes the inventory, graph, findings, compliance and ontology to AI agents over the Model Context Protocol; the [MCP server](#mcp-server) chapter shows it in use. Importing it needs nothing beyond cloudg's own dependencies. Its exports:
824
+
825
+ | Name | What it is |
826
+ |---|---|
827
+ | `CloudGMCPLayer` | The layer: registry, workspace, policy and middleware. `call_tool`, `read_resource`, `get_prompt`, `complete`, `list_*` and `*_wire` methods, `register_into(server)`, `serve(transport)` |
828
+ | `default_registry()`, `Registry` | The built-in catalog, and the container for your own tools (`@registry.tool`, `@registry.resource`, `@registry.resource_template`, `@registry.prompt`) |
829
+ | `Workspace`, `Dataset` | Loaded datasets, the active one, the allowed directories |
830
+ | `Policy`, `available_profiles()` | Access rules, rate limits and transform pipelines; the seven built-in profile names |
831
+ | `Principal`, `ToolContext`, `CallInfo` | Who is calling, what a tool receives, what middleware sees |
832
+ | `register_into(layer, server)`, `build_server(layer, flavor)`, `export_definitions(layer)` | Mount into an MCP SDK or fastmcp server, build a standalone server object, or get framework-free wire definitions plus async handlers |
833
+ | `Pipeline`, `TokenVault`, `TransformContext`, `build_transform()` | The transform machinery and the pseudonym vault |
834
+ | `ToolSpec`, `ResourceSpec`, `ResourceTemplateSpec`, `PromptSpec`, `PromptArgument`, `ToolAnnotations`, `Capability`, `Sensitivity`, `Icon` | Primitive specifications |
835
+ | `ToolResult`, `TextContent`, `ImageContent`, `EmbeddedResource`, `ResourceLink`, `TextResourceContents`, `ContentAnnotations`, `PromptMessage`, `PromptResult` | Results and content blocks |
836
+ | `MCPLayerError`, `AccessDeniedError`, `InvalidArgumentsError`, `NotFoundError`, `RateLimitedError` | Errors, each with its JSON-RPC code |
837
+
838
+ `CloudGMCPLayer` also takes all of its keyword arguments as one `LayerOptions` object (`from cloudg.mcp.layer import LayerOptions`; `CloudGMCPLayer(options=LayerOptions(policy="strict", prefix="cloudg_"))`), and an unknown keyword raises `TypeError`. A tool handler declares its arguments as parameters after `ctx`, or, when there are many, as one pydantic model in a parameter named `args` (`def my_tool(ctx, args: MyArgs)`); both forms produce the same flat input schema.
839
+
840
+ The server side lives in `cloudg.mcp.server` (`serve`, `serve_async`, `ServeOptions`, `create_layer_from_options`; `serve_async(layer, transport, options=ServeOptions(...))` takes the serve settings as one object), the CLI in `cloudg.mcp.cli` (`mcp_group`, `main`, `client_config`), the middleware in `cloudg.mcp.middleware` (`AuditLogMiddleware`, `MetricsMiddleware`, `CachingMiddleware`, `ConcurrencyLimitMiddleware`, `RetryMiddleware`). Every signature is in the [MCP guide](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP.md).
841
+
842
+ ### Resilience API
843
+
844
+ `cloudg.resilience` is the throttling layer every collector goes through, and you can put your own cloud calls through it too ([Rate limits and throttling](#rate-limits-and-throttling)):
845
+
846
+ | Name | What it does |
847
+ |---|---|
848
+ | `call_with_resilience(fn, *args, scope=..., policy=None, **kwargs)` | Async. Breaker, deadline, shared rate limit, bulkhead, then the call; retries throttling and transient errors with decorrelated jitter, never sooner than Retry-After. Sync functions run in a worker thread |
849
+ | `call_with_resilience_sync(...)` | The blocking variant, for worker threads |
850
+ | `@resilient(scope, policy=None)` | Decorator form; `scope` may be a callable that receives the function's arguments |
851
+ | `Scope(provider, account, region, service, operation)`, `RetryPolicy(...)` | Where a call goes; per-call retry bounds |
852
+ | `classify(exc)`, `ErrorKind`, `retry_after(exc)`, `describe_error(exc)`, `is_throttle(exc)` | Provider-aware error classification (THROTTLED, TRANSIENT, FATAL) and the server's requested delay |
853
+ | `classify_strict(exc)`, `is_provider_answer(exc)` | Classification from error codes, exception classes and HTTP statuses only, never message text (`None` when they say nothing); whether the error is an answer from the provider rather than a local failure |
854
+ | `get_governor()`, `configure(config)`, `reset_governor()` | The process-wide limiter, breakers, budgets and stats |
855
+ | `stats_scope()`, `current_stats()`, `ResilienceStats` | Per-run throttling telemetry; `get_governor().summary(stats)` is what `InventoryResult.throttling` holds |
856
+ | `LiveOperationGuard`, `GuardSettings`, `operation_key()`, `LiveOperationRejected` (`CooldownActive`, `LiveOperationBusy`, `CallerQuotaExceeded`), `LiveOperationTimeout` | Single-flight, concurrency caps, cooldowns, caller quotas and an operation timeout for on-demand collections |
857
+ | `RateLimiter`, `TokenBucket`, `LimitSpec`, `ProviderLimits`, `Bulkhead`, `CircuitBreaker`, `BreakerRegistry`, `DEFAULT_LIMITS` | The building blocks |
858
+ | `CircuitOpenError`, `DeadlineExceededError`, `RetryBudgetExhaustedError` | cloudg's own errors, carrying `retry_after` |
859
+
860
+ ```python
861
+ import asyncio
862
+
863
+ import boto3
864
+ from cloudg.resilience import Scope, call_with_resilience, stats_scope
865
+
866
+
867
+ async def main():
868
+ ec2 = boto3.client("ec2", region_name="eu-west-1")
869
+ scope = Scope("aws", "123456789012", "eu-west-1", "ec2", "DescribeVpcs")
870
+ with stats_scope() as stats:
871
+ vpcs = await call_with_resilience(ec2.describe_vpcs, scope=scope)
872
+ print(len(vpcs["Vpcs"]), stats.messages())
873
+
874
+
875
+ asyncio.run(main())
876
+ ```
877
+
878
+ `cloudg.retry.with_retry` keeps its signature and retries what it always did. It now also retries provider throttling that `classify_strict` recognises from an error code, exception class or HTTP status (Azure 429s, GCP `ResourceExhausted`), and never sleeps less than the server's Retry-After, capped at its `max_delay`. It does not retry cloudg's own resilience errors, a plain HTTP 500, or an error whose message merely mentions rate limiting.
879
+
776
880
  ### PipelineResult
777
881
 
778
882
  | Field | Type |
@@ -945,6 +1049,197 @@ if critical:
945
1049
  sys.exit(1)
946
1050
  ```
947
1051
 
1052
+ ## MCP server
1053
+
1054
+ cloudg 0.6.0 ships a [Model Context Protocol](https://modelcontextprotocol.io) layer, `cloudg.mcp`. An AI agent (Claude Desktop, Claude Code, Cursor, VS Code, or your own) can load an inventory map or a findings report, search and inspect assets, walk the relationship graph, ask for attack paths and blast radius, review findings and compliance gaps, query the ontology, and, when the policy allows it, start live collections. Every request goes through one policy that decides who may call what and transforms the data on its way out: secrets redacted, attacker-controllable text fenced, identifiers pseudonymised under the stricter profiles.
1055
+
1056
+ The full guide is [MCP.md](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP.md); this chapter is the summary.
1057
+
1058
+ ### Installing
1059
+
1060
+ ```bash
1061
+ pip install cloudg # the layer, the CLI and the dependency-free native server
1062
+ pip install "cloudg[mcp]" # adds the official mcp SDK: the sdk flavor and register_into(...)
1063
+ pip install fastmcp # only to serve with fastmcp or mount into a fastmcp server
1064
+ ```
1065
+
1066
+ The `mcp` extra pins `mcp>=1.30,<3`; this release was verified with mcp 1.30.0 and 2.3.0, and with fastmcp 2.14.7, 3.4.8 and 4.0.11. Mounting into an SDK `MCPServer` (mcp 2.x) or `FastMCP` (mcp 1.x) goes through private SDK attributes that a future release can rename, so applications that use that path should pin the minor version they tested (for example `mcp>=2.3,<2.4`). Mounting into the SDK's low-level `Server` and the native server do not depend on those attributes. `cloudg[all]` includes the `mcp` extra.
1067
+
1068
+ ### Serving it
1069
+
1070
+ ```bash
1071
+ # stdio, for a desktop client that launches the server itself
1072
+ cloudg mcp serve --dataset prod=./reports/inventory-map.json
1073
+
1074
+ # print the client configuration to paste (claude-desktop, claude-code, cursor, vscode)
1075
+ cloudg mcp config --client claude-desktop --dataset prod=./reports/inventory-map.json
1076
+
1077
+ # streamable HTTP on 127.0.0.1:8765/mcp, with a bearer token mapped to a role
1078
+ cloudg mcp serve --transport http --auth-token env:CLOUDG_MCP_TOKEN:analyst
1079
+ ```
1080
+
1081
+ The same commands are available as `cloudg-mcp ...` and `python -m cloudg.mcp ...`. The server speaks both MCP protocol eras: the handshake versions 2024-11-05, 2025-03-26, 2025-06-18 and 2025-11-25, and the stateless 2026-07-28. `--flavor` picks the implementation: `native` (standard library only), `sdk` (the official SDK, needs `cloudg[mcp]`), `fastmcp`, or `auto`, which uses the SDK when it is installed and the native server otherwise. HTTP binds to loopback and checks `Origin` and `Host` by default; when binding anything else, pass `--auth-token`, `--allowed-host` and `--allowed-origin`.
1082
+
1083
+ ### Embedding it
1084
+
1085
+ Mount every tool, resource and prompt into an MCP server you already run. Here an mcp 2.x `MCPServer` with one tool of its own; an mcp 1.x `FastMCP`, a low-level `Server` and a `fastmcp.FastMCP` work the same way:
1086
+
1087
+ ```python
1088
+ import asyncio
1089
+
1090
+ from mcp import Client
1091
+ from mcp.server.mcpserver import MCPServer
1092
+
1093
+ from cloudg.mcp import CloudGMCPLayer
1094
+
1095
+ server = MCPServer("platform-tools")
1096
+
1097
+
1098
+ @server.tool()
1099
+ def ticket_count(team: str) -> int:
1100
+ """The host's own tool."""
1101
+ return 7
1102
+
1103
+
1104
+ layer = CloudGMCPLayer(policy="standard")
1105
+ layer.workspace.load("inventory/inventory-map.json", "prod")
1106
+ layer.register_into(server, prefix="cloudg_")
1107
+
1108
+
1109
+ async def main() -> None:
1110
+ async with Client(server) as client:
1111
+ tools = (await client.list_tools()).tools
1112
+ print(len(tools), [t.name for t in tools][:3])
1113
+ res = await client.call_tool("cloudg_count_assets", {"group_by": "provider"})
1114
+ print(res.structured_content["groups"])
1115
+
1116
+
1117
+ asyncio.run(main())
1118
+ ```
1119
+
1120
+ ```text
1121
+ 74 ['ticket_count', 'cloudg_workspace_status', 'cloudg_list_datasets']
1122
+ {'AWS': 36, 'AZURE': 5, 'GCP': 3}
1123
+ ```
1124
+
1125
+ Mount after the host has registered its own handlers: the host's tools stay, and names cloudg does not own still reach the host. Or skip MCP on the wire and call the layer in-process:
1126
+
1127
+ ```python
1128
+ import asyncio
1129
+ from pathlib import Path
1130
+
1131
+ from cloudg.mcp import CloudGMCPLayer, Workspace
1132
+
1133
+
1134
+ async def main() -> None:
1135
+ layer = CloudGMCPLayer(policy="strict", workspace=Workspace(allowed_roots=[Path(".")]))
1136
+ await layer.call_tool("load_dataset", {"path": "inventory/inventory-map.json", "name": "prod"})
1137
+ res = await layer.call_tool("find_assets", {"internet_exposed": True, "limit": 2,
1138
+ "fields": ["name", "type", "arn"]})
1139
+ print(res.is_error, res.structured["total"], res.structured["items"])
1140
+
1141
+
1142
+ asyncio.run(main())
1143
+ ```
1144
+
1145
+ ```text
1146
+ False 6 [{'id': 'res-04ddd3e8be', 'name': 'res-04ddd3e8be', 'type': 'EC2', 'arn': 'arn:aws:ec2:us-east-1:934619913841:instance/res-c6e5dce4cb'}, ...]
1147
+ ```
1148
+
1149
+ Under `strict` the names, account ids and ARNs come back pseudonymised; passing a pseudonym back as an argument works, because the input pipeline reverses it. Set `CLOUDG_MCP_VAULT_KEY` to keep pseudonyms stable across restarts (without it they change with every process, as in this run). A policy with `vault.path` also saves the vault when the server shuts down, so issued pseudonyms survive a restart. Both examples ran against the synthetic estate in `tests/mcp/fixtures/sample_estate.py`.
1150
+
1151
+ ### CLI
1152
+
1153
+ | Command | What it does |
1154
+ |---|---|
1155
+ | `cloudg mcp serve` | Run the server: `--transport stdio/http/sse`, `--host`, `--port`, `--path`, `--flavor`, `--auth-token`, `--allowed-origin`, `--allowed-host`, `--cache-ttl`, `--max-concurrency`, `--log-level` |
1156
+ | `cloudg mcp tools` / `resources` / `prompts` | List what a principal sees under a policy (`--role`, `--principal-id`, `--json` for the exact wire definitions) |
1157
+ | `cloudg mcp call TOOL --args JSON` | Call a tool in-process through the full policy; exit status 1 on a tool error |
1158
+ | `cloudg mcp read URI` | Read a resource in-process |
1159
+ | `cloudg mcp config` | Print a client configuration snippet |
1160
+
1161
+ All of them except `config` accept `--policy`, `--dataset [NAME=]PATH` (repeatable), `--prefix`, `--include-category` / `--exclude-category`, `--include-tool` / `--exclude-tool`, `--read-only` (drops tools that touch the cloud, run processes or write files), `--audit-log FILE`, `--timeout` and `--registry MODULE:ATTR`.
1162
+
1163
+ ### Catalog
1164
+
1165
+ The built-in catalog has 74 tools, 13 resources, 11 resource templates and 11 prompts. The CLI adds `cloudg://metrics`. Tools by category:
1166
+
1167
+ | Category | Tools | What they do |
1168
+ |---|---|---|
1169
+ | `graph` | 17 | neighbours, paths, exposure, attack and lateral paths, dependencies, blast radius, centrality, sub-graphs |
1170
+ | `inventory` | 11 | search, inspect and aggregate assets, accounts, regions, tags, coverage, organization |
1171
+ | `findings` | 10 | browse, summarise, prioritise, suppress and ingest security findings |
1172
+ | `workspace` | 8 | load, select, snapshot, diff and unload datasets |
1173
+ | `ontology` | 6 | RDF ontology, read-only SPARQL, semantic neighbourhoods, RAG chunks |
1174
+ | `compliance` | 5 | framework posture, controls and gaps |
1175
+ | `live` | 5 | the four live tools (`map_inventory`, `collect_assets`, `run_scanners`, `run_pipeline`) and `rate_limit_status` |
1176
+ | `privacy` | 5 | policy, pseudonym vault and data-handling controls |
1177
+ | `meta` | 4 | server capabilities and cloudg's vocabulary |
1178
+ | `export` | 3 | Terraform recreation, reports, ontology files |
1179
+
1180
+ The 11 prompts are packaged workflows such as `security_posture_review`, `attack_surface_report`, `blast_radius_assessment`, `compliance_gap_analysis` and `incident_triage`. Every primitive is documented in [MCP_TOOLS.md](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_TOOLS.md).
1181
+
1182
+ The `cloudg://docs` resources let an agent read this reference and the inventory reference. The wheel ships both files inside the package (`cloudg/_docs/`), so they work from a plain `pip install`; a source checkout reads them from `docs/`, and `CLOUDG_DOCS_DIR` points elsewhere. The sdist includes every `docs/*.md`.
1183
+
1184
+ ### Policies and privacy
1185
+
1186
+ `--policy` takes a built-in profile, a YAML or JSON file, or inline JSON; the default is `standard`.
1187
+
1188
+ | Profile | In short | Tools listed for the local user |
1189
+ |---|---|---|
1190
+ | `open` | no restrictions, no transforms; for trusted local use | 74 |
1191
+ | `standard` | redacts secrets, drops private keys, masks credential ids, fences attacker-controllable text, refuses secrets in arguments; `reveal_token` only for `admin` and `privacy-admin` | 73 |
1192
+ | `strict` | `standard` plus pseudonymised identifiers, IPs, e-mails and tag values; hides the live, export and RESTRICTED tools (among them `sparql_query`, `subgraph_export` and the text graph and ontology exports); rate limits on every tool | 61 |
1193
+ | `read_only` | `standard` without cloud access, processes or file writes | 66 |
1194
+ | `airgapped` | `standard` without cloud access or processes | 69 |
1195
+ | `audit` | `read_only` with every call logged and risk annotations inline | 66 |
1196
+ | `soc-analyst` | a role-based example: analysts get pseudonymised data, a `lead` role sees it in clear | 62; `analyst` 60, `lead` 70 |
1197
+
1198
+ Policies can extend each other, carry per-role rules, sensitivity ceilings and per-principal rate limits, and compose transforms (`redact`, `sanitize`, `project`, `annotate`, `substitute`, `alias`, `regex_replace`, `rename_keys`, `template`, `guard_secrets`, `depseudonymize`). The pseudonym vault (`TokenVault`) derives format-preserving pseudonyms with HMAC-SHA256. Every profile, key and transform is in [MCP_PRIVACY.md](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_PRIVACY.md). `reveal_token` (for the roles allowed to reveal) turns a pseudonym, or an alias a `substitute` or `alias` transform introduced, back into the real value.
1199
+
1200
+ ### Live tools
1201
+
1202
+ `map_inventory`, `collect_assets`, `run_scanners` and `run_pipeline` call cloud APIs with the server's own credentials, whoever the caller is, so restrict them to trusted roles. The `strict`, `read_only`, `airgapped`, `audit` and `soc-analyst` profiles hide them, and `--read-only` drops them. Before any cloud call, a credential preflight (`preflight=true` by default, at most 25 seconds) checks that the configured AWS, Azure and GCP credentials work, so a missing profile fails in seconds instead of after every collector has retried.
1203
+
1204
+ Then the call goes through the live operation guard. Identical concurrent calls share one run; a scope collected in the last two minutes is refused with `retry_after_seconds` and a pointer to a loaded live dataset that covers the same providers; at most one live operation runs per scope and two overall. A refusal is a tool error with code -31029 and `reason` `cooldown`, `busy` or `quota`. Principals with the `admin` or `operator` role can pass `force=true` to skip the cooldown. An operation still running after `ratelimit.live_operation_timeout_seconds` (default 3600) is cancelled and frees its scope. The answer of every live tool includes a `throttling` block when a cloud API pushed back during the run. `rate_limit_status` and the resource `cloudg://ratelimit` show the cooldowns, the operations in flight and the throttling state without touching any cloud API. The guard's settings are the `ratelimit.live_*` keys described in the next chapter.
1205
+
1206
+ ## Rate limits and throttling
1207
+
1208
+ Since 0.6.0 every cloud API call cloudg makes goes through `cloudg.resilience`, one process-wide governor shared by all clients:
1209
+
1210
+ - Token buckets per account x region x service (per API action for EC2; per subscription and resource provider on Azure; per project or organization and RPC on GCP), with defaults at or below each provider's published limits: AWS 20 rps per service and 20 per EC2 action, Azure 20 rps per subscription and 3 per user for Resource Graph, GCP 1.5 rps for Cloud Asset `ListAssets`.
1211
+ - Adaptive rates: a throttled scope's rate is halved for every client using it, paused for the server's Retry-After, and recovered by 5% of the ceiling every 5 seconds without throttling.
1212
+ - Concurrency caps per account (`max_concurrency`: 128 requests in flight on AWS, 16 on Azure and GCP).
1213
+ - Circuit breakers per service, or per API action for EC2: after 5 consecutive calls that still failed after retries, calls are skipped for 60 seconds, then one probe goes through. A throttled `DescribeSubnets` does not block `DescribeVpcs`.
1214
+ - Retries with decorrelated jitter that never undercut Retry-After, bounded by attempts, a per-call deadline and a per-provider retry budget that every SDK draws from. botocore keeps doing the retrying on AWS (`aws.retry_mode`, `aws.max_retries`), azure-core on Azure (an explicit `RetryPolicy` with cloudg's retries, backoff cap and timeout), google-api-core on GCP (a `Retry` capped per page); all of them report back to the governor. The first two retries of every call are free; after that each retry draws on the budget, and when it is spent the call gives up.
1215
+
1216
+ Throttling never fails a map. A service that stayed throttled is recorded in coverage as FAILED or PARTIAL with an error starting `throttled:`, the rest of the map continues, and the run's numbers are attached to the result:
1217
+
1218
+ ```python
1219
+ result = InventoryMapper(config).map_inventory_sync()
1220
+ result.throttling # None, or totals / messages / skipped / scopes / breakers / slowed_buckets
1221
+ result.summary["throttling"] # totals, messages, skipped
1222
+ ```
1223
+
1224
+ `inventory-map.json` carries the full block under `throttling`, and `cloudg map` logs each message, for example `Throttling: aws/123456789012/us-east-1/ec2: throttled 11x, slowed to 5.0 rps, 10 retries, 1 calls gave up`.
1225
+
1226
+ Everything is configured under `ratelimit` (structure under [Configuration](#configuration)). Unset limits fall back to the built-ins, and service overrides merge into them:
1227
+
1228
+ ```yaml
1229
+ ratelimit:
1230
+ aws:
1231
+ account_max_rps: 40 # all services of one account together
1232
+ services:
1233
+ resourcegroupstaggingapi: {max_rps: 1, burst: 2}
1234
+ ec2.DescribeImageAttribute: {max_rps: 5, burst: 10}
1235
+ azure:
1236
+ services:
1237
+ resourcegraph: {max_rps: 1, burst: 5}
1238
+ live_cooldown_seconds: 600 # MCP live collections, per scope
1239
+ ```
1240
+
1241
+ A misspelled key anywhere under `ratelimit` is logged as `Unknown config key ratelimit.aws.max_rpss is ignored` and the rest still loads. `ratelimit.enabled: false` turns it all off (botocore's own retries remain), and `ratelimit.<provider>.enabled: false` does it for one provider. The [rate limits and throttling guide](https://github.com/morpheuslord/cloudg/blob/main/docs/RESILIENCE.md) has the architecture, the per-provider wiring, the built-in limits with their sources, every key with its default and where it takes effect, example output of a throttled run, the Python API and tuning advice.
1242
+
948
1243
  ## Plugin system
949
1244
 
950
1245
  Custom collectors and scanners register through setuptools entry points; no core changes needed. `PluginRegistry` (in `cloudg.registry`) discovers them at startup and falls back to the built-ins for any name not overridden.
@@ -982,7 +1277,7 @@ myscanner = "my_package.scanner:MyScanner"
982
1277
  mycloud = "my_package.collector:MyCollector"
983
1278
  ```
984
1279
 
985
- After `pip install`, the name is available in `scanners.enabled` and `--scanners`. Collectors follow the base interface in `cloudg/collectors/base.py`. A collector plugin's assets join the inventory map like the built-in ones: declare what each asset talks to in `metadata["relations"]` and extra identifiers in `metadata["aliases"]`, and the relationship linker resolves them (see [Inventory internals](INVENTORY_INTERNALS.md#156-declare-relations-from-your-own-collector-or-plugin)). If your scanner emits a stable check ID in `source_finding_id`, you can add it to `rules/check_equivalence.yaml` so its findings merge with equivalent checks from other tools.
1280
+ After `pip install`, the name is available in `scanners.enabled` and `--scanners`. Collectors follow the base interface in `cloudg/collectors/base.py`. A collector plugin's assets join the inventory map like the built-in ones: declare what each asset talks to in `metadata["relations"]` and extra identifiers in `metadata["aliases"]`, and the relationship linker resolves them (see [Inventory internals](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_INTERNALS.md#156-declare-relations-from-your-own-collector-or-plugin)). If your scanner emits a stable check ID in `source_finding_id`, you can add it to `cloudg/rules/check_equivalence.yaml` so its findings merge with equivalent checks from other tools.
986
1281
 
987
1282
  ## Authentication
988
1283
 
@@ -5,7 +5,7 @@
5
5
  <p align="center">
6
6
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License: MIT"></a>
7
7
  <img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+">
8
- <img src="https://img.shields.io/badge/version-0.5.2-4c1.svg" alt="Version 0.5.2">
8
+ <img src="https://img.shields.io/badge/version-0.6.0-4c1.svg" alt="Version 0.6.0">
9
9
  <a href="https://github.com/astral-sh/uv"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="uv"></a>
10
10
  </p>
11
11
 
@@ -20,7 +20,9 @@ One command collects assets from every configured provider in parallel, feeds th
20
20
 
21
21
  Need the map without the security tooling? `cloudg map` is a scanner-independent inventory mapper: it deep-collects everything deployed (or default) in an account, down to the network fabric, sweeps every service for the rest, and links it all into one asset map you can overlay with scanner findings later. See [Inventory mapping](#inventory-mapping).
22
22
 
23
- **Full documentation:** the rendered handbook lives at [morpheuslord.github.io/cloudg](https://morpheuslord.github.io/cloudg/), with the same content as markdown in the [feature reference](https://github.com/morpheuslord/cloudg/blob/main/docs/DOCUMENTATION.md) and release notes in the [changelog](https://github.com/morpheuslord/cloudg/blob/main/CHANGELOG.md). The inventory mapper has its own deep-dive documents: the [inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md) (every return structure and output file, field by field), the [inventory catalog](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_CATALOG.md) (all 156 asset types, their metadata and relationships) and the [inventory internals](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_INTERNALS.md) (how it works and how to extend it).
23
+ Want an AI agent to work with all of it? `cloudg mcp serve` turns cloudg into an MCP server for Claude Desktop, Claude Code, Cursor or anything else that speaks the Model Context Protocol. See [MCP server](#mcp-server).
24
+
25
+ **Full documentation:** the rendered handbook lives at [morpheuslord.github.io/cloudg](https://morpheuslord.github.io/cloudg/), with the same content as markdown in the [feature reference](https://github.com/morpheuslord/cloudg/blob/main/docs/DOCUMENTATION.md) and release notes in the [changelog](https://github.com/morpheuslord/cloudg/blob/main/CHANGELOG.md). The inventory mapper has its own deep-dive documents: the [inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md) (every return structure and output file, field by field), the [inventory catalog](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_CATALOG.md) (all 156 asset types, their metadata and relationships) and the [inventory internals](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_INTERNALS.md) (how it works and how to extend it). The MCP server is covered by the [MCP guide](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP.md), the [tool reference](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_TOOLS.md) and the [privacy reference](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_PRIVACY.md), and throttling by [rate limits and throttling](https://github.com/morpheuslord/cloudg/blob/main/docs/RESILIENCE.md).
24
26
 
25
27
  ---
26
28
 
@@ -47,7 +49,7 @@ cloudg run -p aws --regions us-east-1 --terraform
47
49
 
48
50
  Reports land in `./reports`. Open `report.html` first.
49
51
 
50
- Already ran the scanners yourself? Feed cloudg their native output files instead — no cloud credentials, no scanner binaries:
52
+ Already ran the scanners yourself? Feed cloudg their native output files instead. No cloud credentials, no scanner binaries:
51
53
 
52
54
  ```bash
53
55
  cloudg ingest --prowler ./prowler-output/ --checkov ./results_json.json --trivy ./trivy.json
@@ -115,6 +117,31 @@ print(inventory.summary["assets_by_service"])
115
117
 
116
118
  ---
117
119
 
120
+ ## MCP server
121
+
122
+ cloudg can hand its inventory, graph, findings, compliance mappings and ontology to AI agents over the Model Context Protocol: 74 tools, 13 resources, 11 resource templates and 11 prompts, from `find_assets` and `attack_paths` to `blast_radius`, `compliance_gaps` and a `security_posture_review` prompt.
123
+
124
+ ```bash
125
+ pip install "cloudg[mcp]" # the native server needs no extra; this adds the official SDK (mcp>=1.30,<3)
126
+
127
+ # stdio for a desktop client, with a map you already made
128
+ cloudg mcp serve --dataset prod=./reports/inventory-map.json
129
+ cloudg mcp config --client claude-desktop --dataset prod=./reports/inventory-map.json
130
+
131
+ # streamable HTTP on 127.0.0.1:8765/mcp with a bearer token
132
+ cloudg mcp serve --transport http --auth-token env:CLOUDG_MCP_TOKEN:analyst
133
+ ```
134
+
135
+ Already running an MCP server? Mount cloudg into it (official `mcp` SDK 1.x or 2.x, or fastmcp) with `CloudGMCPLayer(...).register_into(server)`, or skip the wire and `await layer.call_tool(...)` from Python. Every request, whichever way it arrives, goes through one policy. The default `standard` profile redacts secrets and fences attacker-controllable text; `strict` also pseudonymises account ids, ARNs, IPs and names (pseudonyms passed back as arguments are resolved) and hides the tools that touch the cloud. The live tools (`map_inventory`, `collect_assets`, `run_scanners`, `run_pipeline`) check credentials before the first API call and refuse to re-collect a scope that was collected a couple of minutes ago.
136
+
137
+ The [MCP guide](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP.md) covers serving, client setup, transports, mounting and in-process use; the [tool reference](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_TOOLS.md) lists every primitive, and the [privacy reference](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_PRIVACY.md) every profile and transform.
138
+
139
+ ### Rate limits
140
+
141
+ Mapping a whole AWS Organization means thousands of API calls per account, so every call cloudg makes goes through a shared rate limiter with per-service limits at or below what each provider publishes. A throttled API gets slowed down for every client at once, Retry-After is honoured, requests in flight are capped per account, retries beyond the first two of each call draw from one budget per provider, and an API that stays throttled is skipped for a while by a circuit breaker. The map does not fail over it: coverage says `throttled: ...` and the result carries a `throttling` summary. Limits are tunable under `ratelimit` in `config.yaml`; see [rate limits and throttling](https://github.com/morpheuslord/cloudg/blob/main/docs/RESILIENCE.md).
142
+
143
+ ---
144
+
118
145
  ## Authentication
119
146
 
120
147
  Every provider supports several auth methods, resolved in a fixed priority order. The same config works on a laptop, in CI, and on cloud compute. The full set of fields lives in `config.yaml` with comments for each method.
@@ -172,11 +199,11 @@ graph LR
172
199
  E --> F
173
200
  ```
174
201
 
175
- Collection runs all providers concurrently with asyncio, iterating accounts and regions per provider (regions are auto-discovered when you pass `--regions all`). Assets and network edges go into a directed graph, where BFS from the internet node finds exposed resources and blast radius scoring estimates what an attacker could reach from each node.
202
+ Collection runs all providers concurrently with asyncio, iterating accounts and regions per provider (regions are auto-discovered when you pass `--regions all`). Assets and network edges go into a directed graph, where BFS from the internet node over network-flow edges finds exposed resources and blast radius scoring estimates what an attacker could reach from each node.
176
203
 
177
- The same inventory feeds three other exports. The ontology module infers about 62 typed relations (`exposed_to_internet`, `assumes_role`, `encrypted_by`, `hosted_in_vpc` and so on) and writes RDF you can query with SPARQL. The RAG exporter chunks the graph three ways (per asset, per Louvain community, per relation domain) into JSONL for retrieval pipelines. The Terraform exporter maps 25+ asset types to `.tf.json` resources with an `import.sh` to adopt them into state.
204
+ The same inventory feeds three other exports. The ontology module infers 64 typed relations (`INTERNET_REACHABLE`, `ROLE_ASSUMES_ROLE`, `ENCRYPTED_BY_KMS`, `VPC_CONTAINS_SUBNET` and so on) and writes RDF you can query with SPARQL. The RAG exporter chunks the graph three ways (per asset, per Louvain community, per relation domain) into JSONL for retrieval pipelines. The Terraform exporter maps 25+ asset types to `.tf.json` resources with an `import.sh` to adopt them into state.
178
205
 
179
- Scanner findings are deduplicated in two passes — within a scanner by (scanner, check ID, resource), and across scanners only when both the normalised title and the underlying check semantics (`rules/check_equivalence.yaml`) match — then rescored against CVSS and mapped to compliance controls.
206
+ Scanner findings are deduplicated in two passes (within a scanner by scanner, check ID and resource; across scanners only when both the normalised title and the underlying check semantics in `cloudg/rules/check_equivalence.yaml` match), then rescored against CVSS and mapped to compliance controls.
180
207
 
181
208
  ---
182
209
 
@@ -313,11 +340,13 @@ Much more of cloudg is public, importable API than the CLI suggests. The [Python
313
340
  |---|---|
314
341
  | `cloudg.inventory.InventoryMapper` / `RelationshipLinker` | scanner-free inventory maps and metadata-derived relationship edges |
315
342
  | `cloudg.graph.builder.GraphBuilder` | NetworkX graph, attack paths, centrality/blast-radius metrics, D3/Cytoscape/GraphML export |
316
- | `cloudg.graph.ontology.CloudOntology` | RDF ontology (~62 typed relations), SPARQL-queryable, Turtle/JSON-LD |
343
+ | `cloudg.graph.ontology.CloudOntology` | RDF ontology (64 typed relations), SPARQL-queryable, Turtle/JSON-LD |
317
344
  | `cloudg.graph.rag_export.RAGExporter` | retrieval-ready JSONL chunks of the infrastructure for LLM pipelines |
318
345
  | `cloudg.renderers.terraform_export.TerraformExporter` | `.tf.json` recreation of live infrastructure plus `import.sh` |
319
346
  | `cloudg.ingest.parse_report` and the scanner classes | every scanner's parser, usable standalone |
320
347
  | `cloudg.normaliser.FindingsNormaliser` | cross-scanner dedupe, CVSS rescoring, compliance mapping |
348
+ | `cloudg.mcp.CloudGMCPLayer` | the MCP layer, in-process or mounted into your own MCP server |
349
+ | `cloudg.resilience` | shared rate limits, circuit breakers and retries (`call_with_resilience`, `@resilient`) for your own cloud calls |
321
350
 
322
351
  Custom collectors and scanners register through entry points, no core changes needed:
323
352
 
@@ -332,7 +361,7 @@ mycloud = "my_package.collector:MyCollector"
332
361
 
333
362
  ```bash
334
363
  uv pip install -e ".[all,dev]"
335
- pytest # 152 tests, moto-mocked AWS included
364
+ pytest # about 1,650 tests, moto-mocked AWS included
336
365
  ruff check cloudg/ tests/
337
366
  uv build # wheel + sdist for PyPI
338
367
  ```
@@ -1,6 +1,6 @@
1
- """cloudg — cloud graphing: multi-cloud mapping and security intelligence."""
1
+ """cloudg (cloud graphing): multi-cloud mapping and security intelligence."""
2
2
 
3
- __version__ = "0.5.2"
3
+ __version__ = "0.6.0"
4
4
 
5
5
  from cloudg.api import (
6
6
  AnalysisResult,