kmcp 0.1.5.dev0__tar.gz → 0.1.7.dev0__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 (162) hide show
  1. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/.github/workflows/release.yml +0 -3
  2. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/PKG-INFO +1 -1
  3. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/_version.py +3 -3
  4. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/execution.py +2 -2
  5. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/monitor/sse_client.py +22 -21
  6. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/file_read.py +1 -1
  7. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/utils/process.py +1 -1
  8. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kmcp.egg-info/PKG-INFO +1 -1
  9. kmcp-0.1.7.dev0/src/kmcp.egg-info/scm_version.json +8 -0
  10. kmcp-0.1.7.dev0/tests/e2e/test_mcp_prompts_resources.py +142 -0
  11. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_db_manager_extended.py +1 -3
  12. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_vault_extended.py +1 -3
  13. kmcp-0.1.5.dev0/src/kmcp.egg-info/scm_version.json +0 -8
  14. kmcp-0.1.5.dev0/tests/e2e/test_mcp_prompts_resources.py +0 -139
  15. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/.env.example +0 -0
  16. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/.gitignore +0 -0
  17. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/Justfile +0 -0
  18. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/README.md +0 -0
  19. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/docs/FEATURE_ROADMAP.md +0 -0
  20. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/docs/IMPLEMENTATION_PLAN.md +0 -0
  21. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/docs/credential-evidence-vault-plan.md +0 -0
  22. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/docs/interactive-monitor-plan.md +0 -0
  23. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/docs/orchestration-playbooks-plan.md +0 -0
  24. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/docs/parallel-execution-plan.md +0 -0
  25. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/docs/result-normalization-plan.md +0 -0
  26. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/docs/superpowers/specs/2026-10-31-foundation-design.md +0 -0
  27. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/docs/target-scope-management-plan.md +0 -0
  28. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/docs/tool-wrappers-plan.md +0 -0
  29. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/pyproject.toml +0 -0
  30. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/requirements.txt +0 -0
  31. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/setup.cfg +0 -0
  32. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/__init__.py +0 -0
  33. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/__main__.py +0 -0
  34. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/cli.py +0 -0
  35. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/config.py +0 -0
  36. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/db/__init__.py +0 -0
  37. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/db/manager.py +0 -0
  38. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/logging_utils.py +0 -0
  39. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/models.py +0 -0
  40. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/monitor/__init__.py +0 -0
  41. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/monitor/sessions.py +0 -0
  42. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/monitor/tui.py +0 -0
  43. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/notifications.py +0 -0
  44. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/prompts.py +0 -0
  45. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/registry.py +0 -0
  46. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/resources.py +0 -0
  47. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/responses.py +0 -0
  48. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/security.py +0 -0
  49. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/server.py +0 -0
  50. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/__init__.py +0 -0
  51. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/amass.py +0 -0
  52. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/arjun.py +0 -0
  53. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/base.py +0 -0
  54. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/bettercap.py +0 -0
  55. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/bloodhound.py +0 -0
  56. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/commix.py +0 -0
  57. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/crackmapexec.py +0 -0
  58. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/dirb.py +0 -0
  59. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/dursgo.py +0 -0
  60. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/enum4linux.py +0 -0
  61. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/farsight.py +0 -0
  62. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/ffuf.py +0 -0
  63. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/file_download.py +0 -0
  64. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/file_write.py +0 -0
  65. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/flowlyt.py +0 -0
  66. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/generic_command.py +0 -0
  67. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/gobuster.py +0 -0
  68. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/hashcat.py +0 -0
  69. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/httpx.py +0 -0
  70. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/hydra.py +0 -0
  71. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/john.py +0 -0
  72. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/katana.py +0 -0
  73. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/masscan.py +0 -0
  74. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/medusa.py +0 -0
  75. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/msfconsole.py +0 -0
  76. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/naabu.py +0 -0
  77. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/netexec.py +0 -0
  78. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/nmap.py +0 -0
  79. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/normalization.py +0 -0
  80. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/nuclei.py +0 -0
  81. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/parallel_execute.py +0 -0
  82. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/persist.py +0 -0
  83. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/playbook_ad.py +0 -0
  84. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/playbook_engine.py +0 -0
  85. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/playbook_engine_tool.py +0 -0
  86. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/playbook_recon.py +0 -0
  87. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/playbook_tools.py +0 -0
  88. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/playbook_webapp.py +0 -0
  89. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/python_command.py +0 -0
  90. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/responder.py +0 -0
  91. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/searchsploit.py +0 -0
  92. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/spiderfoot.py +0 -0
  93. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/sqlmap.py +0 -0
  94. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/subfinder.py +0 -0
  95. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/theharvester.py +0 -0
  96. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/vault_tool.py +0 -0
  97. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/wfuzz.py +0 -0
  98. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/whatweb.py +0 -0
  99. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/wpscan.py +0 -0
  100. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/zap.py +0 -0
  101. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/zighound.py +0 -0
  102. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/tools/zizmor.py +0 -0
  103. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/utils/__init__.py +0 -0
  104. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/utils/vault.py +0 -0
  105. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kali_mcp/validation.py +0 -0
  106. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kmcp.egg-info/SOURCES.txt +0 -0
  107. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kmcp.egg-info/dependency_links.txt +0 -0
  108. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kmcp.egg-info/entry_points.txt +0 -0
  109. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kmcp.egg-info/requires.txt +0 -0
  110. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kmcp.egg-info/scm_file_list.json +0 -0
  111. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/src/kmcp.egg-info/top_level.txt +0 -0
  112. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/conftest.py +0 -0
  113. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/e2e/mcp_smoke.sh +0 -0
  114. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/e2e/test_mcp_protocol.py +0 -0
  115. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_execution_advanced.py +0 -0
  116. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_execution_engine.py +0 -0
  117. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_file_tools.py +0 -0
  118. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_generic_command.py +0 -0
  119. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_monitor_endpoints.py +0 -0
  120. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_parallel_execute.py +0 -0
  121. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_playbook_engine.py +0 -0
  122. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_playbook_tools.py +0 -0
  123. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_process_and_download.py +0 -0
  124. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_python_command.py +0 -0
  125. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_resources.py +0 -0
  126. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_resources_more.py +0 -0
  127. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_safe_execute.py +0 -0
  128. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/integration/test_vault_tools.py +0 -0
  129. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_cli.py +0 -0
  130. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_config.py +0 -0
  131. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_config_extended.py +0 -0
  132. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_db_edge.py +0 -0
  133. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_db_manager.py +0 -0
  134. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_execution_edge.py +0 -0
  135. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_execution_engine_unit.py +0 -0
  136. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_logging_utils.py +0 -0
  137. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_main_entry.py +0 -0
  138. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_models.py +0 -0
  139. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_monitor_actions.py +0 -0
  140. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_monitor_sessions.py +0 -0
  141. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_monitor_snapshot.py +0 -0
  142. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_monitor_tui.py +0 -0
  143. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_notifications.py +0 -0
  144. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_persist.py +0 -0
  145. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_process_extended.py +0 -0
  146. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_prompts_extended.py +0 -0
  147. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_registry.py +0 -0
  148. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_resources_extended.py +0 -0
  149. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_responses.py +0 -0
  150. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_security.py +0 -0
  151. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_security_edge.py +0 -0
  152. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_security_helpers.py +0 -0
  153. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_server.py +0 -0
  154. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_support_modules.py +0 -0
  155. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_tool_commands_matrix.py +0 -0
  156. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_tool_schemas.py +0 -0
  157. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_validation.py +0 -0
  158. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_validation_edge.py +0 -0
  159. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_vault.py +0 -0
  160. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_vault_edge.py +0 -0
  161. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_vault_sqlite.py +0 -0
  162. {kmcp-0.1.5.dev0 → kmcp-0.1.7.dev0}/tests/unit/test_wrapper_normalize.py +0 -0
@@ -34,9 +34,6 @@ jobs:
34
34
  - name: Run integration tests
35
35
  run: python -m pytest tests/integration
36
36
 
37
- - name: Lint with ruff
38
- run: ruff check src/ tests/
39
-
40
37
  build-and-publish:
41
38
  name: Build and publish to PyPI
42
39
  needs: test
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kmcp
3
- Version: 0.1.5.dev0
3
+ Version: 0.1.7.dev0
4
4
  Summary: MCP server exposing Kali Linux penetration-testing tools to AI agents
5
5
  Author: kali-mcp
6
6
  License: MIT
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.1.5.dev0'
22
- __version_tuple__ = version_tuple = (0, 1, 5, 'dev0')
21
+ __version__ = version = '0.1.7.dev0'
22
+ __version_tuple__ = version_tuple = (0, 1, 7, 'dev0')
23
23
 
24
- __commit_id__ = commit_id = 'gadbac29cb'
24
+ __commit_id__ = commit_id = 'g5916172e7'
@@ -275,7 +275,7 @@ class ExecutionEngine:
275
275
  heartbeat_task.cancel()
276
276
  try:
277
277
  await heartbeat_task
278
- except (asyncio.CancelledError, Exception): # noqa: S110, BLE001
278
+ except (asyncio.CancelledError, Exception): # noqa: S110, BLE001
279
279
  pass
280
280
  if progress_cb is not None:
281
281
  try:
@@ -313,7 +313,7 @@ class ExecutionEngine:
313
313
  heartbeat_task.cancel()
314
314
  try:
315
315
  await heartbeat_task
316
- except (asyncio.CancelledError, Exception): # noqa: S110, BLE001
316
+ except (asyncio.CancelledError, Exception): # noqa: S110, BLE001
317
317
  pass
318
318
  return result
319
319
 
@@ -39,28 +39,29 @@ class SseClient:
39
39
  async def _read_loop(self) -> None:
40
40
  while not self._closed:
41
41
  try:
42
- async with httpx.AsyncClient(timeout=None) as http, http.stream(
43
- "GET", self.url
44
- ) as response:
45
- response.raise_for_status()
46
- async for line in response.aiter_lines():
47
- if self._closed:
48
- return
49
- if line.startswith("data: "):
42
+ async with (
43
+ httpx.AsyncClient(timeout=None) as http,
44
+ http.stream("GET", self.url) as response,
45
+ ):
46
+ response.raise_for_status()
47
+ async for line in response.aiter_lines():
48
+ if self._closed:
49
+ return
50
+ if line.startswith("data: "):
51
+ try:
52
+ payload = json.loads(line[len("data: ") :])
53
+ except json.JSONDecodeError:
54
+ continue
55
+ self._snapshot = payload
56
+ try:
57
+ self._queue.put_nowait(payload)
58
+ except asyncio.QueueFull:
59
+ # Slow consumer — drop this snapshot
50
60
  try:
51
- payload = json.loads(line[len("data: ") :])
52
- except json.JSONDecodeError:
53
- continue
54
- self._snapshot = payload
55
- try:
56
- self._queue.put_nowait(payload)
57
- except asyncio.QueueFull:
58
- # Slow consumer — drop this snapshot
59
- try:
60
- self._queue.get_nowait()
61
- except asyncio.QueueEmpty:
62
- pass
63
- self._queue.put_nowait(payload)
61
+ self._queue.get_nowait()
62
+ except asyncio.QueueEmpty:
63
+ pass
64
+ self._queue.put_nowait(payload)
64
65
  except (httpx.ConnectError, httpx.ReadError, OSError):
65
66
  # Server went down — retry after a short pause
66
67
  await asyncio.sleep(2.0)
@@ -135,7 +135,7 @@ class FileReadTool(BaseTool):
135
135
  )
136
136
 
137
137
  return success_response(result)
138
- except Exception as e: # noqa: BLE001
138
+ except Exception as e: # noqa: BLE001
139
139
  return error_response(
140
140
  ToolError(
141
141
  error="Read failed",
@@ -32,5 +32,5 @@ async def kill_process_tree(proc: asyncio.subprocess.Process) -> None:
32
32
  except TimeoutError:
33
33
  pass
34
34
 
35
- except Exception as e: # noqa: BLE001
35
+ except Exception as e: # noqa: BLE001
36
36
  logger.warning("Error killing process %s: %s", getattr(proc, "pid", "?"), e)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kmcp
3
- Version: 0.1.5.dev0
3
+ Version: 0.1.7.dev0
4
4
  Summary: MCP server exposing Kali Linux penetration-testing tools to AI agents
5
5
  Author: kali-mcp
6
6
  License: MIT
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.1.6",
3
+ "distance": 0,
4
+ "node": "g5916172e78bcfd8484f6e1853bfae1518828a77f",
5
+ "dirty": true,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-11-03"
8
+ }
@@ -0,0 +1,142 @@
1
+ """E2E: MCP prompts and resources over the live protocol (server spawned for real)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import subprocess
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ import httpx
10
+ import pytest
11
+ from mcp.client.session import ClientSession
12
+ from mcp.client.streamable_http import streamable_http_client
13
+
14
+ REPO_ROOT = Path(__file__).resolve().parent.parent.parent
15
+
16
+
17
+ @pytest.fixture(scope="module")
18
+ def server_url():
19
+ import os
20
+ import socket
21
+
22
+ with socket.socket() as s:
23
+ s.bind(("127.0.0.1", 0))
24
+ port = s.getsockname()[1]
25
+ env = {
26
+ **os.environ,
27
+ "MCP_HOST": "127.0.0.1",
28
+ "MCP_PORT": str(port),
29
+ "MCP_TRANSPORT": "streamable-http",
30
+ "MCP_DEBUG": "false",
31
+ "MCP_DB_ENABLED": "false",
32
+ }
33
+ proc = subprocess.Popen(
34
+ [sys.executable, str(REPO_ROOT / "src" / "kali_mcp" / "server.py")],
35
+ cwd=REPO_ROOT,
36
+ env=env,
37
+ stdout=subprocess.DEVNULL,
38
+ stderr=subprocess.DEVNULL,
39
+ )
40
+ base = f"http://127.0.0.1:{port}"
41
+ try:
42
+ last_err = None
43
+ for _ in range(60):
44
+ if proc.poll() is not None:
45
+ raise RuntimeError("server exited early")
46
+ try:
47
+ httpx.get(f"{base}/mcp", timeout=1)
48
+ last_err = None
49
+ break
50
+ except Exception as exc: # noqa: BLE001
51
+ last_err = exc
52
+ import time
53
+
54
+ time.sleep(0.5)
55
+ if last_err is not None:
56
+ raise RuntimeError(f"server not ready: {last_err}")
57
+ yield f"{base}/mcp"
58
+ finally:
59
+ proc.terminate()
60
+ try:
61
+ proc.wait(timeout=10)
62
+ except subprocess.TimeoutExpired:
63
+ proc.kill()
64
+
65
+
66
+ @pytest.mark.e2e
67
+ async def test_prompts_listed_and_rendered(server_url):
68
+ async with (
69
+ streamable_http_client(server_url) as (read, write, _sid),
70
+ ClientSession(read, write) as session,
71
+ ):
72
+ await session.initialize()
73
+
74
+ prompts = await session.list_prompts()
75
+ names = {p.name for p in prompts.prompts}
76
+ assert {
77
+ "recon_workflow",
78
+ "webapp_assessment",
79
+ "ad_assessment",
80
+ "osint_harvest",
81
+ "quick_scan",
82
+ "report_template",
83
+ } <= names
84
+
85
+ result = await session.get_prompt("recon_workflow", {"target": "example.com"})
86
+ assert len(result.messages) == 2
87
+ assert "example.com" in result.messages[0].content.text
88
+
89
+
90
+ @pytest.mark.e2e
91
+ async def test_resources_listed_and_read(server_url):
92
+ async with (
93
+ streamable_http_client(server_url) as (read, write, _sid),
94
+ ClientSession(read, write) as session,
95
+ ):
96
+ await session.initialize()
97
+
98
+ resources = await session.list_resources()
99
+ uris = {str(r.uri) for r in resources.resources}
100
+ assert {
101
+ "kali://config",
102
+ "kali://tools",
103
+ "kali://health",
104
+ "kali://db/stats",
105
+ "kali://vault/index",
106
+ "kali://artifacts",
107
+ } <= uris
108
+
109
+ # Read config + health resources
110
+ config_res = await session.read_resource("kali://config")
111
+ assert config_res.contents[0].mimeType == "application/json"
112
+ import json
113
+
114
+ cfg = json.loads(config_res.contents[0].text)
115
+ # The server runs on a random port — config must reflect reality
116
+ assert cfg["port"] == int(server_url.split(":")[2].split("/")[0])
117
+
118
+ health_res = await session.read_resource("kali://health")
119
+ health = json.loads(health_res.contents[0].text)
120
+ assert len(health) > 0
121
+
122
+
123
+ @pytest.mark.e2e
124
+ async def test_orchestration_tools_callable_via_mcp(server_url):
125
+ """playbook_list / playbook_status / parallel_status must round-trip."""
126
+ import json
127
+
128
+ async with (
129
+ streamable_http_client(server_url) as (read, write, _sid),
130
+ ClientSession(read, write) as session,
131
+ ):
132
+ await session.initialize()
133
+
134
+ result = await session.call_tool("playbook_list", {})
135
+ assert result.isError is False
136
+ payload = json.loads(result.content[0].text)
137
+ assert payload["success"] is False or payload # ExecutionResult or error dict
138
+
139
+ result = await session.call_tool("playbook_status", {"name": "nope"})
140
+ payload = json.loads(result.content[0].text)
141
+ assert payload["success"] is False
142
+ assert "Unknown playbook" in payload["error"]
@@ -92,9 +92,7 @@ class TestDBManagerEdgeCases:
92
92
  mock_config.db_enabled = True
93
93
  import tempfile
94
94
 
95
- mock_config.db_path = (
96
- str(tempfile.mkdtemp()) + "/singleton.sqlite"
97
- )
95
+ mock_config.db_path = str(tempfile.mkdtemp()) + "/singleton.sqlite"
98
96
  db1 = get_db()
99
97
  db2 = get_db()
100
98
  assert db1 is db2
@@ -153,7 +153,5 @@ class TestVaultRowSizeLimit:
153
153
  from kali_mcp import config as cfg
154
154
 
155
155
  small_cfg = replace(cfg.config, vault_max_row_bytes=100)
156
- with patch("kali_mcp.utils.vault.config", small_cfg), pytest.raises(
157
- ValueError
158
- ):
156
+ with patch("kali_mcp.utils.vault.config", small_cfg), pytest.raises(ValueError):
159
157
  vault_write("runs/big", "big", {"data": "x" * 1000})
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "0.1.4",
3
- "distance": 0,
4
- "node": "gadbac29cb12034dc9eb7716640a7156ffd90536f",
5
- "dirty": true,
6
- "branch": "master",
7
- "node_date": "2026-11-03"
8
- }
@@ -1,139 +0,0 @@
1
- """E2E: MCP prompts and resources over the live protocol (server spawned for real)."""
2
-
3
- from __future__ import annotations
4
-
5
- import subprocess
6
- import sys
7
- from pathlib import Path
8
-
9
- import httpx
10
- import pytest
11
- from mcp.client.session import ClientSession
12
- from mcp.client.streamable_http import streamable_http_client
13
-
14
- REPO_ROOT = Path(__file__).resolve().parent.parent.parent
15
-
16
-
17
- @pytest.fixture(scope="module")
18
- def server_url():
19
- import os
20
- import socket
21
-
22
- with socket.socket() as s:
23
- s.bind(("127.0.0.1", 0))
24
- port = s.getsockname()[1]
25
- env = {
26
- **os.environ,
27
- "MCP_HOST": "127.0.0.1",
28
- "MCP_PORT": str(port),
29
- "MCP_TRANSPORT": "streamable-http",
30
- "MCP_DEBUG": "false",
31
- "MCP_DB_ENABLED": "false",
32
- }
33
- proc = subprocess.Popen(
34
- [sys.executable, str(REPO_ROOT / "src" / "kali_mcp" / "server.py")],
35
- cwd=REPO_ROOT,
36
- env=env,
37
- stdout=subprocess.DEVNULL,
38
- stderr=subprocess.DEVNULL,
39
- )
40
- base = f"http://127.0.0.1:{port}"
41
- try:
42
- last_err = None
43
- for _ in range(60):
44
- if proc.poll() is not None:
45
- raise RuntimeError("server exited early")
46
- try:
47
- httpx.get(f"{base}/mcp", timeout=1)
48
- last_err = None
49
- break
50
- except Exception as exc: # noqa: BLE001
51
- last_err = exc
52
- import time
53
-
54
- time.sleep(0.5)
55
- if last_err is not None:
56
- raise RuntimeError(f"server not ready: {last_err}")
57
- yield f"{base}/mcp"
58
- finally:
59
- proc.terminate()
60
- try:
61
- proc.wait(timeout=10)
62
- except subprocess.TimeoutExpired:
63
- proc.kill()
64
-
65
-
66
- @pytest.mark.e2e
67
- async def test_prompts_listed_and_rendered(server_url):
68
- async with streamable_http_client(server_url) as (read, write, _sid), ClientSession(
69
- read, write
70
- ) as session:
71
- await session.initialize()
72
-
73
- prompts = await session.list_prompts()
74
- names = {p.name for p in prompts.prompts}
75
- assert {
76
- "recon_workflow",
77
- "webapp_assessment",
78
- "ad_assessment",
79
- "osint_harvest",
80
- "quick_scan",
81
- "report_template",
82
- } <= names
83
-
84
- result = await session.get_prompt("recon_workflow", {"target": "example.com"})
85
- assert len(result.messages) == 2
86
- assert "example.com" in result.messages[0].content.text
87
-
88
-
89
- @pytest.mark.e2e
90
- async def test_resources_listed_and_read(server_url):
91
- async with streamable_http_client(server_url) as (read, write, _sid), ClientSession(
92
- read, write
93
- ) as session:
94
- await session.initialize()
95
-
96
- resources = await session.list_resources()
97
- uris = {str(r.uri) for r in resources.resources}
98
- assert {
99
- "kali://config",
100
- "kali://tools",
101
- "kali://health",
102
- "kali://db/stats",
103
- "kali://vault/index",
104
- "kali://artifacts",
105
- } <= uris
106
-
107
- # Read config + health resources
108
- config_res = await session.read_resource("kali://config")
109
- assert config_res.contents[0].mimeType == "application/json"
110
- import json
111
-
112
- cfg = json.loads(config_res.contents[0].text)
113
- # The server runs on a random port — config must reflect reality
114
- assert cfg["port"] == int(server_url.split(":")[2].split("/")[0])
115
-
116
- health_res = await session.read_resource("kali://health")
117
- health = json.loads(health_res.contents[0].text)
118
- assert len(health) > 0
119
-
120
-
121
- @pytest.mark.e2e
122
- async def test_orchestration_tools_callable_via_mcp(server_url):
123
- """playbook_list / playbook_status / parallel_status must round-trip."""
124
- import json
125
-
126
- async with streamable_http_client(server_url) as (read, write, _sid), ClientSession(
127
- read, write
128
- ) as session:
129
- await session.initialize()
130
-
131
- result = await session.call_tool("playbook_list", {})
132
- assert result.isError is False
133
- payload = json.loads(result.content[0].text)
134
- assert payload["success"] is False or payload # ExecutionResult or error dict
135
-
136
- result = await session.call_tool("playbook_status", {"name": "nope"})
137
- payload = json.loads(result.content[0].text)
138
- assert payload["success"] is False
139
- assert "Unknown playbook" in payload["error"]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes