ha-mcp-dev 7.6.0.dev619__tar.gz → 7.6.0.dev621__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 (124) hide show
  1. {ha_mcp_dev-7.6.0.dev619/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.6.0.dev621}/PKG-INFO +2 -2
  2. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/README.md +1 -1
  3. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/pyproject.toml +1 -1
  4. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_automations.py +5 -8
  5. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_helpers.py +7 -15
  6. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_scenes.py +5 -9
  7. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_scripts.py +4 -7
  8. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_entities.py +11 -52
  9. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_filesystem.py +12 -28
  10. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_groups.py +4 -9
  11. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_hacs.py +15 -32
  12. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_history.py +15 -97
  13. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_integrations.py +43 -82
  14. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_registry.py +7 -7
  15. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_search.py +43 -76
  16. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_service.py +8 -17
  17. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_services.py +7 -7
  18. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_system.py +14 -37
  19. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_updates.py +5 -14
  20. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_utility.py +38 -82
  21. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_yaml_config.py +3 -6
  22. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/util_helpers.py +1 -138
  23. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621/src/ha_mcp_dev.egg-info}/PKG-INFO +2 -2
  24. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/LICENSE +0 -0
  25. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/MANIFEST.in +0 -0
  26. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/setup.cfg +0 -0
  27. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/__init__.py +0 -0
  28. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/__main__.py +0 -0
  29. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/_pypi_marker +0 -0
  30. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/_version.py +0 -0
  31. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/auth/__init__.py +0 -0
  32. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/auth/consent_form.py +0 -0
  33. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/auth/provider.py +0 -0
  34. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/backup_manager.py +0 -0
  35. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/client/__init__.py +0 -0
  36. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/client/rest_client.py +0 -0
  37. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/client/supervisor_client.py +0 -0
  38. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/client/websocket_client.py +0 -0
  39. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/client/websocket_listener.py +0 -0
  40. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/config.py +0 -0
  41. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/errors.py +0 -0
  42. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/__init__.py +0 -0
  43. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/approval_queue.py +0 -0
  44. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/evaluator.py +0 -0
  45. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/handlers.py +0 -0
  46. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/middleware.py +0 -0
  47. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/model.py +0 -0
  48. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/persistence.py +0 -0
  49. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/value_sources.py +0 -0
  50. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/py.typed +0 -0
  51. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  52. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  53. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  54. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  55. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  56. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  57. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  58. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  59. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  60. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  61. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  62. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  63. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  64. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  65. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  66. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  67. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  68. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  69. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  70. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  71. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  72. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  73. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/server.py +0 -0
  74. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/settings_ui.py +0 -0
  75. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/smoke_test.py +0 -0
  76. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  77. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/__init__.py +0 -0
  78. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/auto_backup.py +0 -0
  79. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/backup.py +0 -0
  80. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  81. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/device_control.py +0 -0
  82. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/enhanced.py +0 -0
  83. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/helpers.py +0 -0
  84. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/reference_validator.py +0 -0
  85. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/registry.py +0 -0
  86. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/smart_search.py +0 -0
  87. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_addons.py +0 -0
  88. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_areas.py +0 -0
  89. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  90. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  91. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_calendar.py +0 -0
  92. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_camera.py +0 -0
  93. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_categories.py +0 -0
  94. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_code.py +0 -0
  95. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  96. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  97. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_energy.py +0 -0
  98. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_labels.py +0 -0
  99. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  100. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_resources.py +0 -0
  101. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_todo.py +0 -0
  102. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_traces.py +0 -0
  103. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  104. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_zones.py +0 -0
  105. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/transforms/__init__.py +0 -0
  106. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/transforms/categorized_search.py +0 -0
  107. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  108. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/__init__.py +0 -0
  109. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/config_hash.py +0 -0
  110. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/data_paths.py +0 -0
  111. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/domain_handlers.py +0 -0
  112. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  113. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  114. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/operation_manager.py +0 -0
  115. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/python_sandbox.py +0 -0
  116. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/usage_logger.py +0 -0
  117. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  118. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  119. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  120. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  121. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  122. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/tests/__init__.py +0 -0
  123. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/tests/test_constants.py +0 -0
  124. {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/tests/test_env_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.6.0.dev619
3
+ Version: 7.6.0.dev621
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT
@@ -259,7 +259,7 @@ An MCP server can create automations, helpers, and dashboards, but it has no opi
259
259
 
260
260
  Skills from `homeassistant-ai/skills` are bundled and served as [MCP resources](https://modelcontextprotocol.io/docs/concepts/resources) via `skill://` URIs. Any MCP client that supports resources can discover them automatically — no manual installation needed. For tool-only clients (claude.ai, etc.), the same skills are reachable through the polymorphic `ha_get_skill_guide` tool — call it with no args to list bundled skills, with a `skill` arg to list its files, or with `skill` + `file` to read content. Resources are not auto-injected into context — clients must explicitly request them, so idle context cost is just the metadata listing.
261
261
 
262
- `ha_get_skill_guide` is mandatory-pinned: the catalog always exposes it so tool-only clients never see a silently missing skill surface.
262
+ `ha_get_skill_guide` is a mandatory tool: the catalog always exposes it (it can't be disabled) so tool-only clients never see a silently missing skill surface.
263
263
 
264
264
  Skills can still be installed manually for clients that prefer local skill files — see the [skills repo](https://github.com/homeassistant-ai/skills) for instructions.
265
265
 
@@ -229,7 +229,7 @@ An MCP server can create automations, helpers, and dashboards, but it has no opi
229
229
 
230
230
  Skills from `homeassistant-ai/skills` are bundled and served as [MCP resources](https://modelcontextprotocol.io/docs/concepts/resources) via `skill://` URIs. Any MCP client that supports resources can discover them automatically — no manual installation needed. For tool-only clients (claude.ai, etc.), the same skills are reachable through the polymorphic `ha_get_skill_guide` tool — call it with no args to list bundled skills, with a `skill` arg to list its files, or with `skill` + `file` to read content. Resources are not auto-injected into context — clients must explicitly request them, so idle context cost is just the metadata listing.
231
231
 
232
- `ha_get_skill_guide` is mandatory-pinned: the catalog always exposes it so tool-only clients never see a silently missing skill surface.
232
+ `ha_get_skill_guide` is a mandatory tool: the catalog always exposes it (it can't be disabled) so tool-only clients never see a silently missing skill surface.
233
233
 
234
234
  Skills can still be installed manually for clients that prefer local skill files — see the [skills repo](https://github.com/homeassistant-ai/skills) for instructions.
235
235
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.6.0.dev619"
7
+ version = "7.6.0.dev621"
8
8
  description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13,<3.14"
@@ -44,7 +44,6 @@ from .helpers import (
44
44
  from .reference_validator import validate_config_references
45
45
  from .util_helpers import (
46
46
  apply_entity_category,
47
- coerce_bool_param,
48
47
  coerce_to_list,
49
48
  fetch_entity_category,
50
49
  merge_validation_meta,
@@ -447,7 +446,7 @@ class AutomationConfigTools:
447
446
  ),
448
447
  ] = None,
449
448
  wait: Annotated[
450
- bool | str,
449
+ bool,
451
450
  Field(
452
451
  description="Wait for automation to be queryable before returning. Default: True. Set to False for bulk operations.",
453
452
  default=True,
@@ -829,7 +828,7 @@ class AutomationConfigTools:
829
828
  config_dict: dict[str, Any],
830
829
  identifier: str | None,
831
830
  effective_category: str | None,
832
- wait: bool | str,
831
+ wait: bool,
833
832
  bp_warnings: list[str],
834
833
  validation_meta: dict[str, Any],
835
834
  ) -> dict[str, Any]:
@@ -846,11 +845,10 @@ class AutomationConfigTools:
846
845
  )
847
846
  result.pop("entity_not_verified", None)
848
847
 
849
- wait_bool = coerce_bool_param(wait, "wait", default=True)
850
848
  entity_id = result.get("entity_id")
851
849
  if not entity_id and identifier and identifier.startswith("automation."):
852
850
  entity_id = identifier
853
- if wait_bool and entity_id:
851
+ if wait and entity_id:
854
852
  action_word = "created" if identifier is None else "updated"
855
853
  try:
856
854
  registered = await wait_for_entity_registered(self._client, entity_id)
@@ -1167,7 +1165,7 @@ class AutomationConfigTools:
1167
1165
  ),
1168
1166
  ],
1169
1167
  wait: Annotated[
1170
- bool | str,
1168
+ bool,
1171
1169
  Field(
1172
1170
  description="Wait for automation to be fully removed before returning. Default: True.",
1173
1171
  default=True,
@@ -1208,8 +1206,7 @@ class AutomationConfigTools:
1208
1206
  result = await self._client.delete_automation_config(identifier)
1209
1207
 
1210
1208
  # Wait for entity to be removed
1211
- wait_bool = coerce_bool_param(wait, "wait", default=True)
1212
- if wait_bool and entity_id_for_wait:
1209
+ if wait and entity_id_for_wait:
1213
1210
  try:
1214
1211
  removed = await wait_for_entity_removed(
1215
1212
  self._client, entity_id_for_wait
@@ -33,7 +33,6 @@ from .tools_config_entry_flow import (
33
33
  )
34
34
  from .util_helpers import (
35
35
  apply_entity_category,
36
- coerce_bool_param,
37
36
  parse_json_param,
38
37
  parse_string_list_param,
39
38
  wait_for_entity_registered,
@@ -1553,7 +1552,7 @@ async def _handle_flow_helper(
1553
1552
  area_id: str | None,
1554
1553
  labels: str | list[str] | None,
1555
1554
  category: str | None,
1556
- wait: bool | str,
1555
+ wait: bool,
1557
1556
  action: str | None = None,
1558
1557
  ) -> dict[str, Any]:
1559
1558
  """Create or update a flow-based helper and apply registry updates to all entities.
@@ -1737,10 +1736,9 @@ async def _handle_flow_helper(
1737
1736
  # instances quickly; steady 500ms matches typical entity_registry/list latency
1738
1737
  # on larger remote setups without missing entities near the deadline.
1739
1738
  warnings: list[str] = list(pre_warnings)
1740
- wait_bool = coerce_bool_param(wait, "wait", default=True)
1741
1739
  entities: list[dict[str, Any]] = []
1742
1740
  if entry_id:
1743
- if action == "create" and wait_bool:
1741
+ if action == "create" and wait:
1744
1742
  deadline = 5.0
1745
1743
  intervals = [0.2, 0.3] # first two retries faster
1746
1744
  steady_interval = 0.5
@@ -2070,7 +2068,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
2070
2068
  ),
2071
2069
  ] = None,
2072
2070
  show_advanced_options: Annotated[
2073
- bool | str,
2071
+ bool,
2074
2072
  Field(
2075
2073
  description=(
2076
2074
  "When helper_type='config_subentry', ask Home Assistant "
@@ -2312,7 +2310,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
2312
2310
  ),
2313
2311
  ] = None,
2314
2312
  wait: Annotated[
2315
- bool | str,
2313
+ bool,
2316
2314
  Field(
2317
2315
  description="Wait for helper entity to be queryable before returning. Default: True. Set to False for bulk operations.",
2318
2316
  default=True,
@@ -2484,11 +2482,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
2484
2482
  subentry_type,
2485
2483
  config_dict,
2486
2484
  subentry_id=subentry_id,
2487
- show_advanced_options=coerce_bool_param(
2488
- show_advanced_options,
2489
- "show_advanced_options",
2490
- default=False,
2491
- ),
2485
+ show_advanced_options=show_advanced_options,
2492
2486
  )
2493
2487
 
2494
2488
  # Determine if this is a create or update — set early so the
@@ -2975,8 +2969,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
2975
2969
  # branch under the update action below already skips the
2976
2970
  # wait for this reason; mirror it here so create doesn't
2977
2971
  # burn 10s per tag on every CI run.
2978
- wait_bool = coerce_bool_param(wait, "wait", default=True)
2979
- if wait_bool and entity_id and helper_type != "tag":
2972
+ if wait and entity_id and helper_type != "tag":
2980
2973
  try:
2981
2974
  registered = await wait_for_entity_registered(
2982
2975
  client, entity_id
@@ -3725,8 +3718,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
3725
3718
  warnings.extend(cat_result["warnings"])
3726
3719
 
3727
3720
  # Wait for entity to reflect the update
3728
- wait_bool = coerce_bool_param(wait, "wait", default=True)
3729
- if wait_bool:
3721
+ if wait:
3730
3722
  try:
3731
3723
  registered = await wait_for_entity_registered(client, entity_id)
3732
3724
  if not registered:
@@ -39,7 +39,6 @@ from .helpers import (
39
39
  from .reference_validator import validate_config_references
40
40
  from .util_helpers import (
41
41
  apply_entity_category,
42
- coerce_bool_param,
43
42
  fetch_entity_category,
44
43
  merge_validation_meta,
45
44
  parse_json_param,
@@ -472,7 +471,7 @@ class ConfigSceneTools:
472
471
  ),
473
472
  ] = None,
474
473
  wait: Annotated[
475
- bool | str,
474
+ bool,
476
475
  Field(
477
476
  description=(
478
477
  "Wait for scene to be queryable before returning. Default: True. "
@@ -723,9 +722,8 @@ class ConfigSceneTools:
723
722
  # post-upsert finalisation the full-config branch runs. Without
724
723
  # these, ``wait`` and ``category`` are silently dropped on
725
724
  # python_transform calls.
726
- wait_bool = coerce_bool_param(wait, "wait", default=True)
727
725
  entity_id = await self._resolve_scene_entity_id(resolved_id)
728
- if wait_bool:
726
+ if wait:
729
727
  try:
730
728
  registered = await wait_for_entity_registered(
731
729
  self._client, entity_id
@@ -841,8 +839,7 @@ class ConfigSceneTools:
841
839
  entity_id = await self._resolve_scene_entity_id(resolved_id)
842
840
 
843
841
  # Wait for scene to be queryable
844
- wait_bool = coerce_bool_param(wait, "wait", default=True)
845
- if wait_bool:
842
+ if wait:
846
843
  try:
847
844
  registered = await wait_for_entity_registered(
848
845
  self._client, entity_id
@@ -914,7 +911,7 @@ class ConfigSceneTools:
914
911
  str, Field(description="Scene identifier to delete (e.g., 'old_scene')")
915
912
  ],
916
913
  wait: Annotated[
917
- bool | str,
914
+ bool,
918
915
  Field(
919
916
  description="Wait for scene to be fully removed before returning. Default: True.",
920
917
  default=True,
@@ -970,8 +967,7 @@ class ConfigSceneTools:
970
967
 
971
968
  result = await self._client.delete_scene_config(resolved_id)
972
969
 
973
- wait_bool = coerce_bool_param(wait, "wait", default=True)
974
- if wait_bool:
970
+ if wait:
975
971
  try:
976
972
  removed = await wait_for_entity_removed(self._client, entity_id)
977
973
  if not removed:
@@ -39,7 +39,6 @@ from .helpers import (
39
39
  from .reference_validator import validate_config_references
40
40
  from .util_helpers import (
41
41
  apply_entity_category,
42
- coerce_bool_param,
43
42
  fetch_entity_category,
44
43
  merge_validation_meta,
45
44
  parse_json_param,
@@ -421,7 +420,7 @@ class ConfigScriptTools:
421
420
  ),
422
421
  ] = None,
423
422
  wait: Annotated[
424
- bool | str,
423
+ bool,
425
424
  Field(
426
425
  description="Wait for script to be queryable before returning. Default: True. Set to False for bulk operations.",
427
426
  default=True,
@@ -721,9 +720,8 @@ class ConfigScriptTools:
721
720
  result = await self._client.upsert_script_config(config_dict, script_id)
722
721
 
723
722
  # Wait for script to be queryable
724
- wait_bool = coerce_bool_param(wait, "wait", default=True)
725
723
  entity_id = f"script.{script_id}"
726
- if wait_bool:
724
+ if wait:
727
725
  try:
728
726
  registered = await wait_for_entity_registered(
729
727
  self._client, entity_id
@@ -806,7 +804,7 @@ class ConfigScriptTools:
806
804
  ),
807
805
  ],
808
806
  wait: Annotated[
809
- bool | str,
807
+ bool,
810
808
  Field(
811
809
  description="Wait for script to be fully removed before returning. Default: True.",
812
810
  default=True,
@@ -850,9 +848,8 @@ class ConfigScriptTools:
850
848
  result = await self._client.delete_script_config(script_id)
851
849
 
852
850
  # Wait for script to be removed
853
- wait_bool = coerce_bool_param(wait, "wait", default=True)
854
851
  entity_id = f"script.{script_id}"
855
- if wait_bool:
852
+ if wait:
856
853
  try:
857
854
  removed = await wait_for_entity_removed(self._client, entity_id)
858
855
  if not removed:
@@ -22,7 +22,7 @@ from .helpers import (
22
22
  validate_identifier_not_empty,
23
23
  )
24
24
  from .tools_voice_assistant import KNOWN_ASSISTANTS
25
- from .util_helpers import coerce_bool_param, parse_json_param, parse_string_list_param
25
+ from .util_helpers import parse_json_param, parse_string_list_param
26
26
 
27
27
  logger = logging.getLogger(__name__)
28
28
 
@@ -83,8 +83,8 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
83
83
  area_id: str | None,
84
84
  name: str | None,
85
85
  icon: str | None,
86
- enabled: bool | str | None,
87
- hidden: bool | str | None,
86
+ enabled: bool | None,
87
+ hidden: bool | None,
88
88
  parsed_aliases: list[str] | None,
89
89
  parsed_categories: dict[str, str | None] | None,
90
90
  parsed_labels: list[str] | None,
@@ -151,30 +151,12 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
151
151
  )
152
152
 
153
153
  if enabled is not None:
154
- try:
155
- enabled_bool = coerce_bool_param(enabled, "enabled")
156
- except ValueError as e:
157
- raise_tool_error(
158
- create_error_response(
159
- ErrorCode.VALIDATION_INVALID_PARAMETER,
160
- str(e),
161
- )
162
- )
163
- message["disabled_by"] = None if enabled_bool else "user"
164
- updates_made.append("enabled" if enabled_bool else "disabled")
154
+ message["disabled_by"] = None if enabled else "user"
155
+ updates_made.append("enabled" if enabled else "disabled")
165
156
 
166
157
  if hidden is not None:
167
- try:
168
- hidden_bool = coerce_bool_param(hidden, "hidden")
169
- except ValueError as e:
170
- raise_tool_error(
171
- create_error_response(
172
- ErrorCode.VALIDATION_INVALID_PARAMETER,
173
- str(e),
174
- )
175
- )
176
- message["hidden_by"] = "user" if hidden_bool else None
177
- updates_made.append("hidden" if hidden_bool else "visible")
158
+ message["hidden_by"] = "user" if hidden else None
159
+ updates_made.append("hidden" if hidden else "visible")
178
160
 
179
161
  if parsed_aliases is not None:
180
162
  message["aliases"] = parsed_aliases
@@ -630,7 +612,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
630
612
  ),
631
613
  ] = None,
632
614
  enabled: Annotated[
633
- bool | str | None,
615
+ bool | None,
634
616
  Field(
635
617
  description=(
636
618
  "True to enable the entity, False to disable it. Single entity only. "
@@ -644,7 +626,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
644
626
  ),
645
627
  ] = None,
646
628
  hidden: Annotated[
647
- bool | str | None,
629
+ bool | None,
648
630
  Field(
649
631
  description="True to hide the entity from UI, False to show it. Single entity only.",
650
632
  default=None,
@@ -872,12 +854,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
872
854
  # script.turn_off) which simply prevent them from running while
873
855
  # keeping them visible and manageable.
874
856
  if enabled is not None:
875
- try:
876
- _enabled_check = coerce_bool_param(enabled, "enabled")
877
- except ValueError:
878
- _enabled_check = None # will be caught by _update_single_entity
879
-
880
- if _enabled_check is False:
857
+ if enabled is False:
881
858
  blocked = [
882
859
  eid
883
860
  for eid in entity_ids
@@ -1030,25 +1007,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
1030
1007
  )
1031
1008
  )
1032
1009
 
1033
- # Coerce values to bool
1034
- for asst, val in parsed_expose_to.items():
1035
- try:
1036
- coerced = coerce_bool_param(val, f"expose_to[{asst}]")
1037
- except ValueError as e:
1038
- raise_tool_error(
1039
- create_error_response(
1040
- ErrorCode.VALIDATION_INVALID_PARAMETER,
1041
- str(e),
1042
- )
1043
- )
1044
- if coerced is None:
1045
- raise_tool_error(
1046
- create_error_response(
1047
- ErrorCode.VALIDATION_INVALID_PARAMETER,
1048
- f"expose_to[{asst}] must be a boolean value",
1049
- )
1050
- )
1051
- parsed_expose_to[asst] = coerced
1010
+ # Values are already bool (enforced by the dict[str, bool] annotation)
1052
1011
 
1053
1012
  # Single entity case - use existing logic
1054
1013
  if not is_bulk:
@@ -30,7 +30,7 @@ from .helpers import (
30
30
  raise_tool_error,
31
31
  register_tool_methods,
32
32
  )
33
- from .util_helpers import coerce_bool_param, coerce_int_param, unwrap_service_response
33
+ from .util_helpers import unwrap_service_response
34
34
 
35
35
  logger = logging.getLogger(__name__)
36
36
 
@@ -453,9 +453,11 @@ class FilesystemTools:
453
453
  ),
454
454
  ],
455
455
  tail_lines: Annotated[
456
- int | str | None,
456
+ int | None,
457
457
  Field(
458
458
  default=None,
459
+ ge=1,
460
+ le=10000,
459
461
  description=(
460
462
  "For log files, return only the last N lines. "
461
463
  "Recommended for home-assistant.log to avoid large responses. "
@@ -501,22 +503,13 @@ class FilesystemTools:
501
503
  ```
502
504
  """
503
505
  try:
504
- # Coerce tail_lines parameter
505
- tail_lines_int = coerce_int_param(
506
- tail_lines,
507
- "tail_lines",
508
- default=None,
509
- min_value=1,
510
- max_value=10000,
511
- )
512
-
513
506
  # Check if custom component is available
514
507
  await _assert_mcp_tools_available(self._client)
515
508
 
516
509
  # Build service data
517
510
  service_data: dict[str, Any] = {"path": path}
518
- if tail_lines_int is not None:
519
- service_data["tail_lines"] = tail_lines_int
511
+ if tail_lines is not None:
512
+ service_data["tail_lines"] = tail_lines
520
513
 
521
514
  # Call the custom component service
522
515
  result = await call_mcp_tools_service(
@@ -575,7 +568,7 @@ class FilesystemTools:
575
568
  ),
576
569
  ],
577
570
  overwrite: Annotated[
578
- bool | str,
571
+ bool,
579
572
  Field(
580
573
  default=False,
581
574
  description=(
@@ -585,7 +578,7 @@ class FilesystemTools:
585
578
  ),
586
579
  ] = False,
587
580
  create_dirs: Annotated[
588
- bool | str,
581
+ bool,
589
582
  Field(
590
583
  default=True,
591
584
  description=(
@@ -637,12 +630,6 @@ class FilesystemTools:
637
630
  ```
638
631
  """
639
632
  try:
640
- # Coerce boolean parameters
641
- overwrite_bool = coerce_bool_param(overwrite, "overwrite", default=False)
642
- create_dirs_bool = coerce_bool_param(
643
- create_dirs, "create_dirs", default=True
644
- )
645
-
646
633
  # Check if custom component is available
647
634
  await _assert_mcp_tools_available(self._client)
648
635
 
@@ -650,8 +637,8 @@ class FilesystemTools:
650
637
  service_data: dict[str, Any] = {
651
638
  "path": path,
652
639
  "content": content,
653
- "overwrite": overwrite_bool,
654
- "create_dirs": create_dirs_bool,
640
+ "overwrite": overwrite,
641
+ "create_dirs": create_dirs,
655
642
  }
656
643
 
657
644
  # Call the custom component service
@@ -705,7 +692,7 @@ class FilesystemTools:
705
692
  ),
706
693
  ],
707
694
  confirm: Annotated[
708
- bool | str,
695
+ bool,
709
696
  Field(
710
697
  default=False,
711
698
  description=(
@@ -747,10 +734,7 @@ class FilesystemTools:
747
734
  ```
748
735
  """
749
736
  try:
750
- # Coerce boolean parameter
751
- confirm_bool = coerce_bool_param(confirm, "confirm", default=False)
752
-
753
- if not confirm_bool:
737
+ if not confirm:
754
738
  raise_tool_error(
755
739
  create_error_response(
756
740
  ErrorCode.VALIDATION_INVALID_PARAMETER,
@@ -26,7 +26,6 @@ from .helpers import (
26
26
  validate_identifier_not_empty,
27
27
  )
28
28
  from .util_helpers import (
29
- coerce_bool_param,
30
29
  wait_for_entity_registered,
31
30
  wait_for_entity_removed,
32
31
  )
@@ -264,7 +263,7 @@ class GroupTools:
264
263
  ),
265
264
  ] = None,
266
265
  wait: Annotated[
267
- bool | str,
266
+ bool,
268
267
  Field(
269
268
  description="Wait for group to be queryable before returning. Default: True. Set to False for bulk operations.",
270
269
  default=True,
@@ -340,10 +339,8 @@ class GroupTools:
340
339
  and remove_entities is None
341
340
  )
342
341
 
343
- # Verify entity is queryable after creation/update
344
- wait_bool = coerce_bool_param(wait, "wait", default=True)
345
342
  result: dict[str, Any] = {}
346
- if wait_bool:
343
+ if wait:
347
344
  action_word = "created" if is_create else "updated"
348
345
  try:
349
346
  registered = await wait_for_entity_registered(
@@ -403,7 +400,7 @@ class GroupTools:
403
400
  ),
404
401
  ],
405
402
  wait: Annotated[
406
- bool | str,
403
+ bool,
407
404
  Field(
408
405
  description="Wait for group to be fully removed before returning. Default: True.",
409
406
  default=True,
@@ -462,10 +459,8 @@ class GroupTools:
462
459
 
463
460
  entity_id = f"group.{object_id}"
464
461
 
465
- # Verify entity is removed
466
- wait_bool = coerce_bool_param(wait, "wait", default=True)
467
462
  result: dict[str, Any] = {}
468
- if wait_bool:
463
+ if wait:
469
464
  try:
470
465
  removed = await wait_for_entity_removed(self._client, entity_id)
471
466
  if not removed:
@@ -26,7 +26,7 @@ from .helpers import (
26
26
  safe_progress,
27
27
  validate_identifier_not_empty,
28
28
  )
29
- from .util_helpers import add_timezone_metadata, coerce_bool_param, coerce_int_param
29
+ from .util_helpers import add_timezone_metadata
30
30
 
31
31
  logger = logging.getLogger(__name__)
32
32
 
@@ -122,23 +122,26 @@ class HacsTools:
122
122
  ),
123
123
  ] = None,
124
124
  installed_only: Annotated[
125
- bool | str,
125
+ bool,
126
126
  Field(
127
127
  default=False,
128
128
  description="Only return installed repositories (default: False)",
129
129
  ),
130
130
  ] = False,
131
131
  max_results: Annotated[
132
- int | str,
132
+ int,
133
133
  Field(
134
134
  default=10,
135
+ ge=1,
136
+ le=100,
135
137
  description="Maximum number of results to return (default: 10, max: 100)",
136
138
  ),
137
139
  ] = 10,
138
140
  offset: Annotated[
139
- int | str,
141
+ int,
140
142
  Field(
141
143
  default=0,
144
+ ge=0,
142
145
  description="Number of results to skip for pagination (default: 0)",
143
146
  ),
144
147
  ] = 0,
@@ -169,28 +172,10 @@ class HacsTools:
169
172
  offset: Number of results to skip for pagination (default: 0)
170
173
  """
171
174
  try:
172
- # Coerce parameters
173
- installed_only_bool = coerce_bool_param(
174
- installed_only, "installed_only", default=False
175
- )
176
- max_results_int = coerce_int_param(
177
- max_results,
178
- "max_results",
179
- default=10,
180
- min_value=1,
181
- max_value=100,
182
- )
183
- offset_int = coerce_int_param(
184
- offset,
185
- "offset",
186
- default=0,
187
- min_value=0,
188
- )
189
-
190
175
  await safe_info(
191
176
  ctx,
192
177
  f"ha_hacs_search starting: query={query!r} "
193
- f"category={category} installed_only={installed_only_bool}",
178
+ f"category={category} installed_only={installed_only}",
194
179
  )
195
180
  await safe_progress(
196
181
  ctx,
@@ -242,9 +227,7 @@ class HacsTools:
242
227
  total=3,
243
228
  message=f"filtering {len(all_repositories)} repositories",
244
229
  )
245
- matches = _filter_and_score_repos(
246
- all_repositories, query, installed_only_bool
247
- )
230
+ matches = _filter_and_score_repos(all_repositories, query, installed_only)
248
231
  await safe_progress(
249
232
  ctx,
250
233
  progress=3,
@@ -252,8 +235,8 @@ class HacsTools:
252
235
  message=f"matched {len(matches)} repositories",
253
236
  )
254
237
 
255
- limited_matches = matches[offset_int : offset_int + max_results_int]
256
- has_more = (offset_int + len(limited_matches)) < len(matches)
238
+ limited_matches = matches[offset : offset + max_results]
239
+ has_more = (offset + len(limited_matches)) < len(matches)
257
240
 
258
241
  return await add_timezone_metadata(
259
242
  self._client,
@@ -261,13 +244,13 @@ class HacsTools:
261
244
  "success": True,
262
245
  "query": query if query.strip() else None,
263
246
  "category_filter": category,
264
- "installed_only": installed_only_bool,
247
+ "installed_only": installed_only,
265
248
  "total_matches": len(matches),
266
- "offset": offset_int,
267
- "limit": max_results_int,
249
+ "offset": offset,
250
+ "limit": max_results,
268
251
  "count": len(limited_matches),
269
252
  "has_more": has_more,
270
- "next_offset": offset_int + max_results_int if has_more else None,
253
+ "next_offset": offset + max_results if has_more else None,
271
254
  "results": limited_matches,
272
255
  },
273
256
  )