omnibase_infra 0.2.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (833) hide show
  1. omnibase_infra/__init__.py +101 -0
  2. omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
  3. omnibase_infra/capabilities/__init__.py +15 -0
  4. omnibase_infra/capabilities/capability_inference_rules.py +211 -0
  5. omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
  6. omnibase_infra/capabilities/intent_type_extractor.py +160 -0
  7. omnibase_infra/cli/__init__.py +1 -0
  8. omnibase_infra/cli/commands.py +216 -0
  9. omnibase_infra/clients/__init__.py +0 -0
  10. omnibase_infra/configs/widget_mapping.yaml +176 -0
  11. omnibase_infra/constants_topic_patterns.py +26 -0
  12. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +264 -0
  13. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +141 -0
  14. omnibase_infra/decorators/__init__.py +29 -0
  15. omnibase_infra/decorators/allow_any.py +109 -0
  16. omnibase_infra/dlq/__init__.py +90 -0
  17. omnibase_infra/dlq/constants_dlq.py +57 -0
  18. omnibase_infra/dlq/models/__init__.py +26 -0
  19. omnibase_infra/dlq/models/enum_replay_status.py +37 -0
  20. omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
  21. omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
  22. omnibase_infra/dlq/service_dlq_tracking.py +611 -0
  23. omnibase_infra/enums/__init__.py +132 -0
  24. omnibase_infra/enums/enum_any_type_violation.py +104 -0
  25. omnibase_infra/enums/enum_backend_type.py +27 -0
  26. omnibase_infra/enums/enum_capture_outcome.py +42 -0
  27. omnibase_infra/enums/enum_capture_state.py +88 -0
  28. omnibase_infra/enums/enum_chain_violation_type.py +119 -0
  29. omnibase_infra/enums/enum_circuit_state.py +51 -0
  30. omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
  31. omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
  32. omnibase_infra/enums/enum_contract_type.py +84 -0
  33. omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
  34. omnibase_infra/enums/enum_dispatch_status.py +191 -0
  35. omnibase_infra/enums/enum_environment.py +46 -0
  36. omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
  37. omnibase_infra/enums/enum_handler_error_type.py +111 -0
  38. omnibase_infra/enums/enum_handler_loader_error.py +178 -0
  39. omnibase_infra/enums/enum_handler_source_mode.py +86 -0
  40. omnibase_infra/enums/enum_handler_source_type.py +87 -0
  41. omnibase_infra/enums/enum_handler_type.py +77 -0
  42. omnibase_infra/enums/enum_handler_type_category.py +61 -0
  43. omnibase_infra/enums/enum_infra_transport_type.py +73 -0
  44. omnibase_infra/enums/enum_introspection_reason.py +154 -0
  45. omnibase_infra/enums/enum_kafka_acks.py +99 -0
  46. omnibase_infra/enums/enum_message_category.py +213 -0
  47. omnibase_infra/enums/enum_node_archetype.py +74 -0
  48. omnibase_infra/enums/enum_node_output_type.py +185 -0
  49. omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
  50. omnibase_infra/enums/enum_policy_type.py +32 -0
  51. omnibase_infra/enums/enum_registration_state.py +261 -0
  52. omnibase_infra/enums/enum_registration_status.py +33 -0
  53. omnibase_infra/enums/enum_registry_response_status.py +28 -0
  54. omnibase_infra/enums/enum_response_status.py +26 -0
  55. omnibase_infra/enums/enum_retry_error_category.py +98 -0
  56. omnibase_infra/enums/enum_security_rule_id.py +103 -0
  57. omnibase_infra/enums/enum_selection_strategy.py +91 -0
  58. omnibase_infra/enums/enum_topic_standard.py +42 -0
  59. omnibase_infra/enums/enum_validation_severity.py +78 -0
  60. omnibase_infra/errors/__init__.py +160 -0
  61. omnibase_infra/errors/error_architecture_violation.py +152 -0
  62. omnibase_infra/errors/error_binding_resolution.py +128 -0
  63. omnibase_infra/errors/error_chain_propagation.py +188 -0
  64. omnibase_infra/errors/error_compute_registry.py +95 -0
  65. omnibase_infra/errors/error_consul.py +132 -0
  66. omnibase_infra/errors/error_container_wiring.py +243 -0
  67. omnibase_infra/errors/error_event_bus_registry.py +105 -0
  68. omnibase_infra/errors/error_infra.py +610 -0
  69. omnibase_infra/errors/error_message_type_registry.py +101 -0
  70. omnibase_infra/errors/error_policy_registry.py +115 -0
  71. omnibase_infra/errors/error_vault.py +123 -0
  72. omnibase_infra/event_bus/__init__.py +72 -0
  73. omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +84 -0
  74. omnibase_infra/event_bus/event_bus_inmemory.py +797 -0
  75. omnibase_infra/event_bus/event_bus_kafka.py +1716 -0
  76. omnibase_infra/event_bus/mixin_kafka_broadcast.py +180 -0
  77. omnibase_infra/event_bus/mixin_kafka_dlq.py +771 -0
  78. omnibase_infra/event_bus/models/__init__.py +29 -0
  79. omnibase_infra/event_bus/models/config/__init__.py +20 -0
  80. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +693 -0
  81. omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
  82. omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
  83. omnibase_infra/event_bus/models/model_event_headers.py +115 -0
  84. omnibase_infra/event_bus/models/model_event_message.py +60 -0
  85. omnibase_infra/event_bus/testing/__init__.py +26 -0
  86. omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
  87. omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
  88. omnibase_infra/event_bus/topic_constants.py +376 -0
  89. omnibase_infra/handlers/__init__.py +82 -0
  90. omnibase_infra/handlers/filesystem/__init__.py +48 -0
  91. omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
  92. omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
  93. omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
  94. omnibase_infra/handlers/handler_consul.py +795 -0
  95. omnibase_infra/handlers/handler_db.py +1046 -0
  96. omnibase_infra/handlers/handler_filesystem.py +1478 -0
  97. omnibase_infra/handlers/handler_graph.py +2015 -0
  98. omnibase_infra/handlers/handler_http.py +926 -0
  99. omnibase_infra/handlers/handler_intent.py +387 -0
  100. omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
  101. omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
  102. omnibase_infra/handlers/handler_mcp.py +1430 -0
  103. omnibase_infra/handlers/handler_qdrant.py +1076 -0
  104. omnibase_infra/handlers/handler_vault.py +428 -0
  105. omnibase_infra/handlers/mcp/__init__.py +19 -0
  106. omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
  107. omnibase_infra/handlers/mcp/protocols.py +178 -0
  108. omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
  109. omnibase_infra/handlers/mixins/__init__.py +47 -0
  110. omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
  111. omnibase_infra/handlers/mixins/mixin_consul_kv.py +338 -0
  112. omnibase_infra/handlers/mixins/mixin_consul_service.py +542 -0
  113. omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
  114. omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
  115. omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
  116. omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
  117. omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
  118. omnibase_infra/handlers/models/__init__.py +286 -0
  119. omnibase_infra/handlers/models/consul/__init__.py +81 -0
  120. omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
  121. omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
  122. omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
  123. omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
  124. omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
  125. omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
  126. omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
  127. omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
  128. omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
  129. omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
  130. omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
  131. omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
  132. omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
  133. omnibase_infra/handlers/models/graph/__init__.py +35 -0
  134. omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
  135. omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
  136. omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
  137. omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
  138. omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
  139. omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
  140. omnibase_infra/handlers/models/http/__init__.py +50 -0
  141. omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
  142. omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
  143. omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
  144. omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
  145. omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
  146. omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
  147. omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
  148. omnibase_infra/handlers/models/mcp/__init__.py +23 -0
  149. omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
  150. omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
  151. omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
  152. omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
  153. omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
  154. omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
  155. omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
  156. omnibase_infra/handlers/models/model_db_query_response.py +60 -0
  157. omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
  158. omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
  159. omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
  160. omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
  161. omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
  162. omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
  163. omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
  164. omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
  165. omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
  166. omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
  167. omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
  168. omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
  169. omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
  170. omnibase_infra/handlers/models/model_handler_response.py +103 -0
  171. omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
  172. omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
  173. omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
  174. omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
  175. omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
  176. omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
  177. omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
  178. omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
  179. omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
  180. omnibase_infra/handlers/models/model_operation_context.py +187 -0
  181. omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
  182. omnibase_infra/handlers/models/model_retry_state.py +162 -0
  183. omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
  184. omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
  185. omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
  186. omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
  187. omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
  188. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
  189. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
  190. omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
  191. omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
  192. omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
  193. omnibase_infra/handlers/models/vault/__init__.py +69 -0
  194. omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
  195. omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
  196. omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
  197. omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
  198. omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
  199. omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
  200. omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
  201. omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
  202. omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
  203. omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
  204. omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
  205. omnibase_infra/handlers/registration_storage/__init__.py +43 -0
  206. omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
  207. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +922 -0
  208. omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
  209. omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
  210. omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
  211. omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
  212. omnibase_infra/handlers/service_discovery/__init__.py +43 -0
  213. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +1051 -0
  214. omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
  215. omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
  216. omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
  217. omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
  218. omnibase_infra/handlers/service_discovery/models/model_service_info.py +109 -0
  219. omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
  220. omnibase_infra/idempotency/__init__.py +94 -0
  221. omnibase_infra/idempotency/models/__init__.py +43 -0
  222. omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
  223. omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
  224. omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
  225. omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
  226. omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
  227. omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
  228. omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
  229. omnibase_infra/idempotency/store_inmemory.py +265 -0
  230. omnibase_infra/idempotency/store_postgres.py +923 -0
  231. omnibase_infra/infrastructure/__init__.py +0 -0
  232. omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
  233. omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
  234. omnibase_infra/mixins/__init__.py +71 -0
  235. omnibase_infra/mixins/mixin_async_circuit_breaker.py +656 -0
  236. omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
  237. omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
  238. omnibase_infra/mixins/mixin_node_introspection.py +2670 -0
  239. omnibase_infra/mixins/mixin_retry_execution.py +386 -0
  240. omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
  241. omnibase_infra/models/__init__.py +144 -0
  242. omnibase_infra/models/bindings/__init__.py +59 -0
  243. omnibase_infra/models/bindings/constants.py +144 -0
  244. omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
  245. omnibase_infra/models/bindings/model_operation_binding.py +44 -0
  246. omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
  247. omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
  248. omnibase_infra/models/corpus/__init__.py +17 -0
  249. omnibase_infra/models/corpus/model_capture_config.py +133 -0
  250. omnibase_infra/models/corpus/model_capture_result.py +86 -0
  251. omnibase_infra/models/discovery/__init__.py +42 -0
  252. omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
  253. omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
  254. omnibase_infra/models/discovery/model_introspection_config.py +330 -0
  255. omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
  256. omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
  257. omnibase_infra/models/dispatch/__init__.py +155 -0
  258. omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
  259. omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
  260. omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
  261. omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
  262. omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
  263. omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
  264. omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
  265. omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
  266. omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
  267. omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
  268. omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
  269. omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
  270. omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
  271. omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
  272. omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
  273. omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
  274. omnibase_infra/models/errors/__init__.py +45 -0
  275. omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
  276. omnibase_infra/models/errors/model_infra_error_context.py +99 -0
  277. omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
  278. omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
  279. omnibase_infra/models/handlers/__init__.py +80 -0
  280. omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
  281. omnibase_infra/models/handlers/model_contract_discovery_result.py +82 -0
  282. omnibase_infra/models/handlers/model_handler_descriptor.py +200 -0
  283. omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
  284. omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
  285. omnibase_infra/models/health/__init__.py +9 -0
  286. omnibase_infra/models/health/model_health_check_result.py +40 -0
  287. omnibase_infra/models/lifecycle/__init__.py +39 -0
  288. omnibase_infra/models/logging/__init__.py +51 -0
  289. omnibase_infra/models/logging/model_log_context.py +756 -0
  290. omnibase_infra/models/mcp/__init__.py +15 -0
  291. omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
  292. omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
  293. omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
  294. omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
  295. omnibase_infra/models/model_node_identity.py +126 -0
  296. omnibase_infra/models/model_retry_error_classification.py +78 -0
  297. omnibase_infra/models/projection/__init__.py +43 -0
  298. omnibase_infra/models/projection/model_capability_fields.py +112 -0
  299. omnibase_infra/models/projection/model_registration_projection.py +434 -0
  300. omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
  301. omnibase_infra/models/projection/model_sequence_info.py +182 -0
  302. omnibase_infra/models/projection/model_snapshot_topic_config.py +591 -0
  303. omnibase_infra/models/projectors/__init__.py +41 -0
  304. omnibase_infra/models/projectors/model_projector_column.py +289 -0
  305. omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
  306. omnibase_infra/models/projectors/model_projector_index.py +270 -0
  307. omnibase_infra/models/projectors/model_projector_schema.py +415 -0
  308. omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
  309. omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
  310. omnibase_infra/models/registration/__init__.py +68 -0
  311. omnibase_infra/models/registration/commands/__init__.py +15 -0
  312. omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
  313. omnibase_infra/models/registration/events/__init__.py +56 -0
  314. omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
  315. omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
  316. omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
  317. omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
  318. omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
  319. omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
  320. omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
  321. omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
  322. omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
  323. omnibase_infra/models/registration/model_node_capabilities.py +190 -0
  324. omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
  325. omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
  326. omnibase_infra/models/registration/model_node_introspection_event.py +195 -0
  327. omnibase_infra/models/registration/model_node_metadata.py +79 -0
  328. omnibase_infra/models/registration/model_node_registration.py +162 -0
  329. omnibase_infra/models/registration/model_node_registration_record.py +162 -0
  330. omnibase_infra/models/registry/__init__.py +29 -0
  331. omnibase_infra/models/registry/model_domain_constraint.py +202 -0
  332. omnibase_infra/models/registry/model_message_type_entry.py +271 -0
  333. omnibase_infra/models/resilience/__init__.py +9 -0
  334. omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
  335. omnibase_infra/models/routing/__init__.py +25 -0
  336. omnibase_infra/models/routing/model_routing_entry.py +52 -0
  337. omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
  338. omnibase_infra/models/runtime/__init__.py +49 -0
  339. omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
  340. omnibase_infra/models/runtime/model_discovery_error.py +81 -0
  341. omnibase_infra/models/runtime/model_discovery_result.py +162 -0
  342. omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
  343. omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
  344. omnibase_infra/models/runtime/model_handler_contract.py +296 -0
  345. omnibase_infra/models/runtime/model_loaded_handler.py +129 -0
  346. omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
  347. omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
  348. omnibase_infra/models/security/__init__.py +50 -0
  349. omnibase_infra/models/security/classification_levels.py +99 -0
  350. omnibase_infra/models/security/model_environment_policy.py +145 -0
  351. omnibase_infra/models/security/model_handler_security_policy.py +107 -0
  352. omnibase_infra/models/security/model_security_error.py +81 -0
  353. omnibase_infra/models/security/model_security_validation_result.py +328 -0
  354. omnibase_infra/models/security/model_security_warning.py +67 -0
  355. omnibase_infra/models/snapshot/__init__.py +27 -0
  356. omnibase_infra/models/snapshot/model_field_change.py +65 -0
  357. omnibase_infra/models/snapshot/model_snapshot.py +270 -0
  358. omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
  359. omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
  360. omnibase_infra/models/types/__init__.py +71 -0
  361. omnibase_infra/models/validation/__init__.py +89 -0
  362. omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
  363. omnibase_infra/models/validation/model_any_type_violation.py +141 -0
  364. omnibase_infra/models/validation/model_category_match_result.py +345 -0
  365. omnibase_infra/models/validation/model_chain_violation.py +166 -0
  366. omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
  367. omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
  368. omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
  369. omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
  370. omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
  371. omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
  372. omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
  373. omnibase_infra/models/validation/model_output_validation_params.py +74 -0
  374. omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
  375. omnibase_infra/models/validation/model_validation_error_params.py +84 -0
  376. omnibase_infra/models/validation/model_validation_outcome.py +287 -0
  377. omnibase_infra/nodes/__init__.py +57 -0
  378. omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
  379. omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
  380. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +203 -0
  381. omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
  382. omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
  383. omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
  384. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
  385. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
  386. omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
  387. omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
  388. omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
  389. omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
  390. omnibase_infra/nodes/architecture_validator/node.py +262 -0
  391. omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
  392. omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
  393. omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
  394. omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
  395. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +106 -0
  396. omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
  397. omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
  398. omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
  399. omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
  400. omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
  401. omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
  402. omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
  403. omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
  404. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
  405. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
  406. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
  407. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
  408. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
  409. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
  410. omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
  411. omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
  412. omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
  413. omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
  414. omnibase_infra/nodes/effects/README.md +358 -0
  415. omnibase_infra/nodes/effects/__init__.py +26 -0
  416. omnibase_infra/nodes/effects/contract.yaml +167 -0
  417. omnibase_infra/nodes/effects/models/__init__.py +32 -0
  418. omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
  419. omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
  420. omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
  421. omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
  422. omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
  423. omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
  424. omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
  425. omnibase_infra/nodes/effects/registry_effect.py +525 -0
  426. omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
  427. omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
  428. omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
  429. omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
  430. omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
  431. omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
  432. omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
  433. omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
  434. omnibase_infra/nodes/node_intent_storage_effect/__init__.py +50 -0
  435. omnibase_infra/nodes/node_intent_storage_effect/contract.yaml +194 -0
  436. omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py +24 -0
  437. omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py +141 -0
  438. omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py +130 -0
  439. omnibase_infra/nodes/node_intent_storage_effect/node.py +94 -0
  440. omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py +35 -0
  441. omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py +294 -0
  442. omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
  443. omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
  444. omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
  445. omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
  446. omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
  447. omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
  448. omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
  449. omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
  450. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
  451. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
  452. omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
  453. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
  454. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
  455. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
  456. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
  457. omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
  458. omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
  459. omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
  460. omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
  461. omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
  462. omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
  463. omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
  464. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +482 -0
  465. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
  466. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
  467. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
  468. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
  469. omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
  470. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
  471. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +694 -0
  472. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
  473. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
  474. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
  475. omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
  476. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
  477. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
  478. omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
  479. omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
  480. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
  481. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
  482. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
  483. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
  484. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
  485. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
  486. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
  487. omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
  488. omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
  489. omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
  490. omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
  491. omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
  492. omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
  493. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +528 -0
  494. omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +393 -0
  495. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +743 -0
  496. omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
  497. omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
  498. omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
  499. omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
  500. omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
  501. omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
  502. omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
  503. omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
  504. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +220 -0
  505. omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
  506. omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
  507. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
  508. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
  509. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
  510. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
  511. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
  512. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
  513. omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
  514. omnibase_infra/nodes/node_registration_storage_effect/node.py +112 -0
  515. omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
  516. omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
  517. omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
  518. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +215 -0
  519. omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
  520. omnibase_infra/nodes/node_registry_effect/contract.yaml +677 -0
  521. omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
  522. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
  523. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
  524. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +417 -0
  525. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
  526. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
  527. omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
  528. omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
  529. omnibase_infra/nodes/node_registry_effect/node.py +165 -0
  530. omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
  531. omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
  532. omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
  533. omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
  534. omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
  535. omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
  536. omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
  537. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
  538. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
  539. omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
  540. omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
  541. omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
  542. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
  543. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
  544. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
  545. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
  546. omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
  547. omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
  548. omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
  549. omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
  550. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +222 -0
  551. omnibase_infra/nodes/reducers/__init__.py +30 -0
  552. omnibase_infra/nodes/reducers/models/__init__.py +37 -0
  553. omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +87 -0
  554. omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
  555. omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
  556. omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
  557. omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
  558. omnibase_infra/nodes/reducers/registration_reducer.py +1138 -0
  559. omnibase_infra/observability/__init__.py +143 -0
  560. omnibase_infra/observability/constants_metrics.py +91 -0
  561. omnibase_infra/observability/factory_observability_sink.py +525 -0
  562. omnibase_infra/observability/handlers/__init__.py +118 -0
  563. omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
  564. omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
  565. omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
  566. omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
  567. omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
  568. omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
  569. omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
  570. omnibase_infra/observability/hooks/__init__.py +74 -0
  571. omnibase_infra/observability/hooks/hook_observability.py +1223 -0
  572. omnibase_infra/observability/models/__init__.py +30 -0
  573. omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
  574. omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
  575. omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
  576. omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
  577. omnibase_infra/observability/sinks/__init__.py +69 -0
  578. omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
  579. omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
  580. omnibase_infra/plugins/__init__.py +27 -0
  581. omnibase_infra/plugins/examples/__init__.py +28 -0
  582. omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
  583. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
  584. omnibase_infra/plugins/models/__init__.py +21 -0
  585. omnibase_infra/plugins/models/model_plugin_context.py +76 -0
  586. omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
  587. omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
  588. omnibase_infra/plugins/plugin_compute_base.py +449 -0
  589. omnibase_infra/projectors/__init__.py +30 -0
  590. omnibase_infra/projectors/contracts/__init__.py +63 -0
  591. omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
  592. omnibase_infra/projectors/projection_reader_registration.py +1559 -0
  593. omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
  594. omnibase_infra/protocols/__init__.py +104 -0
  595. omnibase_infra/protocols/protocol_capability_projection.py +253 -0
  596. omnibase_infra/protocols/protocol_capability_query.py +251 -0
  597. omnibase_infra/protocols/protocol_container_aware.py +200 -0
  598. omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
  599. omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
  600. omnibase_infra/protocols/protocol_event_projector.py +96 -0
  601. omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
  602. omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
  603. omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
  604. omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
  605. omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
  606. omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
  607. omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
  608. omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
  609. omnibase_infra/runtime/__init__.py +445 -0
  610. omnibase_infra/runtime/binding_config_resolver.py +2771 -0
  611. omnibase_infra/runtime/binding_resolver.py +753 -0
  612. omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
  613. omnibase_infra/runtime/constants_notification.py +75 -0
  614. omnibase_infra/runtime/constants_security.py +70 -0
  615. omnibase_infra/runtime/contract_handler_discovery.py +587 -0
  616. omnibase_infra/runtime/contract_loaders/__init__.py +51 -0
  617. omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
  618. omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
  619. omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
  620. omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
  621. omnibase_infra/runtime/emit_daemon/cli.py +844 -0
  622. omnibase_infra/runtime/emit_daemon/client.py +811 -0
  623. omnibase_infra/runtime/emit_daemon/config.py +535 -0
  624. omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
  625. omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
  626. omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
  627. omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
  628. omnibase_infra/runtime/emit_daemon/queue.py +618 -0
  629. omnibase_infra/runtime/enums/__init__.py +18 -0
  630. omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
  631. omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
  632. omnibase_infra/runtime/envelope_validator.py +179 -0
  633. omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
  634. omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
  635. omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
  636. omnibase_infra/runtime/handler_contract_source.py +750 -0
  637. omnibase_infra/runtime/handler_identity.py +81 -0
  638. omnibase_infra/runtime/handler_plugin_loader.py +2046 -0
  639. omnibase_infra/runtime/handler_registry.py +329 -0
  640. omnibase_infra/runtime/handler_source_resolver.py +367 -0
  641. omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
  642. omnibase_infra/runtime/kafka_contract_source.py +984 -0
  643. omnibase_infra/runtime/kernel.py +40 -0
  644. omnibase_infra/runtime/mixin_policy_validation.py +522 -0
  645. omnibase_infra/runtime/mixin_semver_cache.py +402 -0
  646. omnibase_infra/runtime/mixins/__init__.py +24 -0
  647. omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
  648. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +778 -0
  649. omnibase_infra/runtime/models/__init__.py +229 -0
  650. omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
  651. omnibase_infra/runtime/models/model_binding_config.py +168 -0
  652. omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
  653. omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
  654. omnibase_infra/runtime/models/model_cached_secret.py +138 -0
  655. omnibase_infra/runtime/models/model_compute_key.py +138 -0
  656. omnibase_infra/runtime/models/model_compute_registration.py +97 -0
  657. omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
  658. omnibase_infra/runtime/models/model_config_ref.py +331 -0
  659. omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
  660. omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
  661. omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
  662. omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
  663. omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
  664. omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
  665. omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
  666. omnibase_infra/runtime/models/model_failed_component.py +55 -0
  667. omnibase_infra/runtime/models/model_health_check_response.py +168 -0
  668. omnibase_infra/runtime/models/model_health_check_result.py +229 -0
  669. omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
  670. omnibase_infra/runtime/models/model_logging_config.py +42 -0
  671. omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
  672. omnibase_infra/runtime/models/model_optional_string.py +94 -0
  673. omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
  674. omnibase_infra/runtime/models/model_policy_context.py +100 -0
  675. omnibase_infra/runtime/models/model_policy_key.py +138 -0
  676. omnibase_infra/runtime/models/model_policy_registration.py +139 -0
  677. omnibase_infra/runtime/models/model_policy_result.py +103 -0
  678. omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
  679. omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
  680. omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
  681. omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
  682. omnibase_infra/runtime/models/model_retry_policy.py +105 -0
  683. omnibase_infra/runtime/models/model_runtime_config.py +150 -0
  684. omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
  685. omnibase_infra/runtime/models/model_runtime_scheduler_config.py +625 -0
  686. omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
  687. omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
  688. omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
  689. omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
  690. omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
  691. omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
  692. omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
  693. omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
  694. omnibase_infra/runtime/models/model_security_config.py +109 -0
  695. omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
  696. omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
  697. omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
  698. omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
  699. omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
  700. omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
  701. omnibase_infra/runtime/projector_schema_manager.py +565 -0
  702. omnibase_infra/runtime/projector_shell.py +1330 -0
  703. omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
  704. omnibase_infra/runtime/protocol_contract_source.py +92 -0
  705. omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
  706. omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
  707. omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
  708. omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
  709. omnibase_infra/runtime/protocol_policy.py +366 -0
  710. omnibase_infra/runtime/protocols/__init__.py +37 -0
  711. omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
  712. omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
  713. omnibase_infra/runtime/registry/__init__.py +93 -0
  714. omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
  715. omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
  716. omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
  717. omnibase_infra/runtime/registry/registry_message_type.py +542 -0
  718. omnibase_infra/runtime/registry/registry_protocol_binding.py +445 -0
  719. omnibase_infra/runtime/registry_compute.py +1143 -0
  720. omnibase_infra/runtime/registry_contract_source.py +693 -0
  721. omnibase_infra/runtime/registry_dispatcher.py +678 -0
  722. omnibase_infra/runtime/registry_policy.py +1185 -0
  723. omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
  724. omnibase_infra/runtime/runtime_scheduler.py +1070 -0
  725. omnibase_infra/runtime/secret_resolver.py +2112 -0
  726. omnibase_infra/runtime/security_metadata_validator.py +776 -0
  727. omnibase_infra/runtime/service_kernel.py +1651 -0
  728. omnibase_infra/runtime/service_message_dispatch_engine.py +2350 -0
  729. omnibase_infra/runtime/service_runtime_host_process.py +3493 -0
  730. omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
  731. omnibase_infra/runtime/transition_notification_publisher.py +765 -0
  732. omnibase_infra/runtime/util_container_wiring.py +1124 -0
  733. omnibase_infra/runtime/util_validation.py +314 -0
  734. omnibase_infra/runtime/util_version.py +98 -0
  735. omnibase_infra/runtime/util_wiring.py +723 -0
  736. omnibase_infra/schemas/schema_registration_projection.sql +320 -0
  737. omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
  738. omnibase_infra/services/__init__.py +89 -0
  739. omnibase_infra/services/corpus_capture.py +684 -0
  740. omnibase_infra/services/mcp/__init__.py +31 -0
  741. omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
  742. omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
  743. omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
  744. omnibase_infra/services/mcp/service_mcp_tool_sync.py +565 -0
  745. omnibase_infra/services/registry_api/__init__.py +40 -0
  746. omnibase_infra/services/registry_api/main.py +261 -0
  747. omnibase_infra/services/registry_api/models/__init__.py +66 -0
  748. omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
  749. omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
  750. omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
  751. omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
  752. omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
  753. omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
  754. omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
  755. omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
  756. omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
  757. omnibase_infra/services/registry_api/models/model_warning.py +49 -0
  758. omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
  759. omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
  760. omnibase_infra/services/registry_api/routes.py +371 -0
  761. omnibase_infra/services/registry_api/service.py +837 -0
  762. omnibase_infra/services/service_capability_query.py +945 -0
  763. omnibase_infra/services/service_health.py +898 -0
  764. omnibase_infra/services/service_node_selector.py +530 -0
  765. omnibase_infra/services/service_timeout_emitter.py +699 -0
  766. omnibase_infra/services/service_timeout_scanner.py +394 -0
  767. omnibase_infra/services/session/__init__.py +56 -0
  768. omnibase_infra/services/session/config_consumer.py +137 -0
  769. omnibase_infra/services/session/config_store.py +139 -0
  770. omnibase_infra/services/session/consumer.py +1007 -0
  771. omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
  772. omnibase_infra/services/session/store.py +997 -0
  773. omnibase_infra/services/snapshot/__init__.py +31 -0
  774. omnibase_infra/services/snapshot/service_snapshot.py +647 -0
  775. omnibase_infra/services/snapshot/store_inmemory.py +637 -0
  776. omnibase_infra/services/snapshot/store_postgres.py +1279 -0
  777. omnibase_infra/shared/__init__.py +8 -0
  778. omnibase_infra/testing/__init__.py +10 -0
  779. omnibase_infra/testing/utils.py +23 -0
  780. omnibase_infra/topics/__init__.py +45 -0
  781. omnibase_infra/topics/platform_topic_suffixes.py +140 -0
  782. omnibase_infra/topics/util_topic_composition.py +95 -0
  783. omnibase_infra/types/__init__.py +48 -0
  784. omnibase_infra/types/type_cache_info.py +49 -0
  785. omnibase_infra/types/type_dsn.py +173 -0
  786. omnibase_infra/types/type_infra_aliases.py +60 -0
  787. omnibase_infra/types/typed_dict/__init__.py +29 -0
  788. omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
  789. omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
  790. omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
  791. omnibase_infra/types/typed_dict_capabilities.py +64 -0
  792. omnibase_infra/utils/__init__.py +117 -0
  793. omnibase_infra/utils/correlation.py +208 -0
  794. omnibase_infra/utils/util_atomic_file.py +261 -0
  795. omnibase_infra/utils/util_consumer_group.py +232 -0
  796. omnibase_infra/utils/util_datetime.py +372 -0
  797. omnibase_infra/utils/util_db_transaction.py +239 -0
  798. omnibase_infra/utils/util_dsn_validation.py +333 -0
  799. omnibase_infra/utils/util_env_parsing.py +264 -0
  800. omnibase_infra/utils/util_error_sanitization.py +457 -0
  801. omnibase_infra/utils/util_pydantic_validators.py +477 -0
  802. omnibase_infra/utils/util_retry_optimistic.py +281 -0
  803. omnibase_infra/utils/util_semver.py +233 -0
  804. omnibase_infra/validation/__init__.py +307 -0
  805. omnibase_infra/validation/contracts/security.validation.yaml +114 -0
  806. omnibase_infra/validation/enums/__init__.py +11 -0
  807. omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
  808. omnibase_infra/validation/infra_validators.py +1514 -0
  809. omnibase_infra/validation/linter_contract.py +907 -0
  810. omnibase_infra/validation/mixin_any_type_classification.py +120 -0
  811. omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
  812. omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
  813. omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
  814. omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
  815. omnibase_infra/validation/models/__init__.py +15 -0
  816. omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
  817. omnibase_infra/validation/models/model_contract_violation.py +41 -0
  818. omnibase_infra/validation/service_validation_aggregator.py +395 -0
  819. omnibase_infra/validation/validation_exemptions.yaml +2033 -0
  820. omnibase_infra/validation/validator_any_type.py +715 -0
  821. omnibase_infra/validation/validator_chain_propagation.py +839 -0
  822. omnibase_infra/validation/validator_execution_shape.py +465 -0
  823. omnibase_infra/validation/validator_localhandler.py +261 -0
  824. omnibase_infra/validation/validator_registration_security.py +410 -0
  825. omnibase_infra/validation/validator_routing_coverage.py +1020 -0
  826. omnibase_infra/validation/validator_runtime_shape.py +915 -0
  827. omnibase_infra/validation/validator_security.py +513 -0
  828. omnibase_infra/validation/validator_topic_category.py +1152 -0
  829. omnibase_infra-0.2.6.dist-info/METADATA +197 -0
  830. omnibase_infra-0.2.6.dist-info/RECORD +833 -0
  831. omnibase_infra-0.2.6.dist-info/WHEEL +4 -0
  832. omnibase_infra-0.2.6.dist-info/entry_points.txt +5 -0
  833. omnibase_infra-0.2.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1430 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """MCP Handler - Model Context Protocol integration for ONEX nodes.
4
+
5
+ Exposes ONEX nodes as MCP tools for AI agent integration via streamable HTTP transport.
6
+ This handler enables AI agents (Claude, etc.) to discover and invoke ONEX nodes as tools.
7
+
8
+ The handler implements the MCP protocol specification using the official MCP Python SDK,
9
+ providing a bridge between the ONEX node ecosystem and AI agent tool interfaces.
10
+
11
+ Key Features:
12
+ - Streamable HTTP transport for production scalability
13
+ - Dynamic tool discovery from ONEX node registry
14
+ - Contract-to-MCP schema generation
15
+ - Request/response correlation for observability
16
+ - Internal uvicorn server lifecycle management (OMN-1282)
17
+
18
+ Note:
19
+ This handler requires the `mcp` package (anthropic-ai/mcp-python-sdk).
20
+ Install via: poetry add mcp
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import asyncio
26
+ import logging
27
+ import time
28
+ from typing import TYPE_CHECKING
29
+ from uuid import UUID, uuid4
30
+
31
+ import uvicorn
32
+ from pydantic import ValidationError
33
+ from starlette.applications import Starlette
34
+ from starlette.requests import Request
35
+ from starlette.responses import JSONResponse
36
+ from starlette.routing import Route
37
+
38
+ from omnibase_core.models.dispatch import ModelHandlerOutput
39
+ from omnibase_infra.enums import (
40
+ EnumHandlerType,
41
+ EnumHandlerTypeCategory,
42
+ EnumInfraTransportType,
43
+ )
44
+ from omnibase_infra.errors import (
45
+ InfraUnavailableError,
46
+ ModelInfraErrorContext,
47
+ ProtocolConfigurationError,
48
+ RuntimeHostError,
49
+ )
50
+ from omnibase_infra.handlers.models.mcp import (
51
+ EnumMcpOperationType,
52
+ ModelMcpHandlerConfig,
53
+ ModelMcpToolResult,
54
+ )
55
+ from omnibase_infra.mixins import MixinAsyncCircuitBreaker, MixinEnvelopeExtraction
56
+ from omnibase_infra.services.mcp import MCPServerLifecycle, ModelMCPServerConfig
57
+
58
+ if TYPE_CHECKING:
59
+ from collections.abc import Callable, Coroutine, Sequence
60
+
61
+ from omnibase_core.models.container.model_onex_container import ModelONEXContainer
62
+ from omnibase_infra.adapters.adapter_onex_tool_execution import (
63
+ AdapterONEXToolExecution,
64
+ )
65
+ from omnibase_infra.services.mcp.service_mcp_tool_registry import (
66
+ ServiceMCPToolRegistry,
67
+ )
68
+ from omnibase_spi.protocols.types.protocol_mcp_tool_types import (
69
+ ProtocolMCPToolDefinition,
70
+ )
71
+
72
+ logger = logging.getLogger(__name__)
73
+
74
+ # Handler ID for ModelHandlerOutput
75
+ HANDLER_ID_MCP: str = "mcp-handler"
76
+
77
+ # Shutdown timeout constants (can be overridden via class attributes)
78
+ _DEFAULT_SHUTDOWN_TIMEOUT: float = 5.0
79
+ _DEFAULT_CANCEL_TIMEOUT: float = 1.0
80
+ _DEFAULT_STARTUP_TIMEOUT: float = 2.0
81
+
82
+ # Error message truncation limit for health check responses
83
+ _ERROR_MESSAGE_MAX_LENGTH: int = 200
84
+
85
+
86
+ def _require_config_value[T](
87
+ config: dict[str, object],
88
+ key: str,
89
+ expected_type: type[T],
90
+ correlation_id: UUID,
91
+ ) -> T:
92
+ """Extract required config value or raise ProtocolConfigurationError.
93
+
94
+ Per CLAUDE.md configuration rules, the `.env` file is the SINGLE SOURCE OF TRUTH.
95
+ There should be ZERO hardcoded fallbacks - all configuration must be explicitly
96
+ provided. If missing, this function raises an error rather than using defaults.
97
+
98
+ Args:
99
+ config: Configuration dictionary to extract value from.
100
+ key: Configuration key to look up.
101
+ expected_type: Expected Python type for the value.
102
+ correlation_id: Correlation ID for error context.
103
+
104
+ Returns:
105
+ The validated configuration value.
106
+
107
+ Raises:
108
+ ProtocolConfigurationError: If value is missing or has wrong type.
109
+ """
110
+ value = config.get(key)
111
+ if value is None:
112
+ raise ProtocolConfigurationError(
113
+ f"Missing required config: '{key}'. Must be set in .env or runtime config.",
114
+ context=ModelInfraErrorContext.with_correlation(
115
+ correlation_id=correlation_id,
116
+ transport_type=EnumInfraTransportType.MCP,
117
+ operation="initialize",
118
+ target_name="handler_mcp",
119
+ ),
120
+ )
121
+ if not isinstance(value, expected_type):
122
+ raise ProtocolConfigurationError(
123
+ f"Invalid config type for '{key}': expected {expected_type.__name__}, "
124
+ f"got {type(value).__name__}",
125
+ context=ModelInfraErrorContext.with_correlation(
126
+ correlation_id=correlation_id,
127
+ transport_type=EnumInfraTransportType.MCP,
128
+ operation="initialize",
129
+ target_name="handler_mcp",
130
+ ),
131
+ )
132
+ return value
133
+
134
+
135
+ # Supported operations
136
+ _SUPPORTED_OPERATIONS: frozenset[str] = frozenset(
137
+ {op.value for op in EnumMcpOperationType}
138
+ )
139
+
140
+
141
+ class HandlerMCP(MixinEnvelopeExtraction, MixinAsyncCircuitBreaker):
142
+ """MCP protocol handler for exposing ONEX nodes as AI agent tools.
143
+
144
+ This handler creates an MCP server using streamable HTTP transport,
145
+ enabling AI agents to discover and invoke ONEX nodes as tools.
146
+
147
+ The handler integrates with the ONEX registry to dynamically expose
148
+ registered nodes as MCP tools, translating ONEX contracts into
149
+ MCP tool definitions.
150
+
151
+ Architecture:
152
+ - Uses official MCP Python SDK for protocol compliance
153
+ - Streamable HTTP transport for production deployments
154
+ - Stateless mode for horizontal scaling
155
+ - JSON response mode for compatibility
156
+
157
+ Security Features:
158
+ - Tool execution timeout enforcement (via config.timeout_seconds)
159
+ - Request size limits inherited from ONEX nodes
160
+ - Correlation ID propagation for tracing
161
+ - Circuit breaker protection against cascading failures
162
+
163
+ Authentication:
164
+ Authentication is NOT yet implemented in this MVP version. The MCP
165
+ endpoint is currently open/unauthenticated. Authentication will be
166
+ added in a future release via:
167
+ - Bearer token validation in the transport layer
168
+ - Integration with ONEX identity service for token verification
169
+ - Optional API key support for service-to-service communication
170
+ See: TODO(OMN-1288) for authentication implementation tracking
171
+
172
+ For production deployments before authentication is implemented,
173
+ deploy behind an API gateway with authentication or restrict
174
+ network access to trusted clients.
175
+
176
+ Dispatcher Integration:
177
+ This MVP version uses placeholder tool execution. Full ONEX dispatcher
178
+ integration is planned to enable:
179
+ - Routing tool calls to the appropriate ONEX node
180
+ - Timeout enforcement via asyncio.wait_for()
181
+ - Full observability through the ONEX runtime
182
+ See: TODO(OMN-1288) for dispatcher integration tracking
183
+
184
+ Class Attributes:
185
+ shutdown_timeout: Timeout for graceful server shutdown (default: 5.0s).
186
+ cancel_timeout: Timeout for forced cancellation after graceful fails (default: 1.0s).
187
+ startup_timeout: Timeout for server readiness check during startup (default: 2.0s).
188
+ """
189
+
190
+ # Configurable timeout attributes (can be overridden on subclasses or instances)
191
+ shutdown_timeout: float = _DEFAULT_SHUTDOWN_TIMEOUT
192
+ cancel_timeout: float = _DEFAULT_CANCEL_TIMEOUT
193
+ startup_timeout: float = _DEFAULT_STARTUP_TIMEOUT
194
+
195
+ def __init__(
196
+ self,
197
+ container: ModelONEXContainer | None = None,
198
+ registry: ServiceMCPToolRegistry | None = None,
199
+ executor: AdapterONEXToolExecution | None = None,
200
+ ) -> None:
201
+ """Initialize HandlerMCP with optional ONEX container for dependency injection.
202
+
203
+ Args:
204
+ container: Optional ONEX container providing dependency injection for
205
+ services, configuration, and runtime context. When None, the handler
206
+ operates in standalone mode without container-based DI.
207
+ registry: Optional MCP tool registry for dynamic tool discovery.
208
+ If provided, tools are looked up from this registry. If not
209
+ provided, the handler uses its local _tool_registry dict.
210
+ executor: Optional tool execution adapter for dispatching to
211
+ ONEX orchestrators. If provided, tool calls are routed through
212
+ this adapter. If not provided, placeholder execution is used.
213
+
214
+ Note:
215
+ The container parameter is optional to support two instantiation paths:
216
+ 1. Registry-based: RuntimeHostProcess creates handlers via registry lookup
217
+ with no-argument constructor calls. Container is None in this case.
218
+ 2. DI-based: Explicit container injection for full ONEX integration.
219
+
220
+ When container is provided, it enables future DI-based service resolution
221
+ (e.g., dispatcher routing, metrics integration).
222
+
223
+ MCP Integration (OMN-1281):
224
+ When registry and executor are provided, the handler operates in
225
+ "integrated mode" with full MCP tool discovery and execution:
226
+ - Tools are discovered from Consul via ServiceMCPToolDiscovery
227
+ - Tool list is cached in ServiceMCPToolRegistry
228
+ - Tool execution routes through AdapterONEXToolExecution
229
+ - Hot reload updates are received via ServiceMCPToolSync
230
+
231
+ Server Lifecycle (OMN-1282):
232
+ The handler owns its uvicorn server lifecycle. When initialize() is
233
+ called, the handler starts a uvicorn server in a background task.
234
+ When shutdown() is called, the server is gracefully stopped.
235
+ """
236
+ self._container = container
237
+ self._config: ModelMcpHandlerConfig | None = None
238
+ self._initialized: bool = False
239
+ self._tool_registry: dict[str, ProtocolMCPToolDefinition] = {}
240
+
241
+ # MCP integration components (OMN-1281)
242
+ self._mcp_registry: ServiceMCPToolRegistry | None = registry
243
+ self._mcp_executor: AdapterONEXToolExecution | None = executor
244
+
245
+ # Server lifecycle components (OMN-1282)
246
+ self._server: uvicorn.Server | None = None
247
+ self._server_task: asyncio.Task[None] | None = None
248
+ self._lifecycle: MCPServerLifecycle | None = None
249
+ self._skip_server: bool = False # Track if server was intentionally skipped
250
+ self._server_started_at: float | None = None # Timestamp for uptime tracking
251
+
252
+ @property
253
+ def handler_type(self) -> EnumHandlerType:
254
+ """Return the architectural role of this handler.
255
+
256
+ Returns:
257
+ EnumHandlerType.INFRA_HANDLER - This handler is an infrastructure
258
+ protocol/transport handler that exposes ONEX nodes via MCP.
259
+ """
260
+ return EnumHandlerType.INFRA_HANDLER
261
+
262
+ @property
263
+ def handler_category(self) -> EnumHandlerTypeCategory:
264
+ """Return the behavioral classification of this handler.
265
+
266
+ Returns:
267
+ EnumHandlerTypeCategory.EFFECT - This handler performs side-effecting
268
+ I/O operations (tool execution via MCP protocol).
269
+ """
270
+ return EnumHandlerTypeCategory.EFFECT
271
+
272
+ @property
273
+ def transport_type(self) -> EnumInfraTransportType:
274
+ """Return the transport protocol identifier.
275
+
276
+ Returns:
277
+ EnumInfraTransportType.MCP - Model Context Protocol transport.
278
+ """
279
+ return EnumInfraTransportType.MCP
280
+
281
+ def _create_json_endpoint(
282
+ self,
283
+ response_factory: Callable[[], Coroutine[object, object, dict[str, object]]],
284
+ ) -> Callable[[Request], Coroutine[object, object, JSONResponse]]:
285
+ """Create a JSON endpoint that wraps an async response factory.
286
+
287
+ This method creates a Starlette-compatible async route handler that:
288
+ 1. Calls the provided response_factory to generate response data
289
+ 2. Wraps the data in a JSONResponse
290
+
291
+ Args:
292
+ response_factory: Async callable that returns the response data dict.
293
+ The factory is called on each request to generate fresh data.
294
+
295
+ Returns:
296
+ Async function suitable for Starlette Route.
297
+ """
298
+
299
+ async def endpoint(_request: Request) -> JSONResponse:
300
+ data = await response_factory()
301
+ return JSONResponse(data)
302
+
303
+ return endpoint
304
+
305
+ def _create_health_endpoint(
306
+ self,
307
+ ) -> Callable[[Request], Coroutine[object, object, JSONResponse]]:
308
+ """Create health endpoint with explicit handler binding.
309
+
310
+ Returns a coroutine function that closes over `self` explicitly,
311
+ avoiding fragile closure patterns with intermediate variables.
312
+
313
+ Returns:
314
+ Async function suitable for Starlette Route.
315
+ """
316
+ # Capture reference explicitly in closure scope
317
+ handler = self
318
+
319
+ async def get_health_data() -> dict[str, object]:
320
+ """Return health status data for the MCP server."""
321
+ tool_count = 0
322
+ if handler._lifecycle and handler._lifecycle.registry:
323
+ tool_count = handler._lifecycle.registry.tool_count
324
+ return {
325
+ "status": "healthy",
326
+ "tool_count": tool_count,
327
+ "initialized": handler._initialized,
328
+ }
329
+
330
+ return self._create_json_endpoint(get_health_data)
331
+
332
+ def _create_tools_list_endpoint(
333
+ self,
334
+ ) -> Callable[[Request], Coroutine[object, object, JSONResponse]]:
335
+ """Create tools list endpoint with explicit handler binding.
336
+
337
+ Returns a coroutine function that closes over `self` explicitly,
338
+ avoiding fragile closure patterns with intermediate variables.
339
+
340
+ Returns:
341
+ Async function suitable for Starlette Route.
342
+ """
343
+ # Capture reference explicitly in closure scope
344
+ handler = self
345
+
346
+ async def get_tools_data() -> dict[str, object]:
347
+ """Return list of available MCP tools."""
348
+ if handler._lifecycle and handler._lifecycle.registry:
349
+ tools = await handler._lifecycle.registry.list_tools()
350
+ return {
351
+ "tools": [
352
+ {
353
+ "name": t.name,
354
+ "description": t.description,
355
+ "endpoint": t.endpoint,
356
+ }
357
+ for t in tools
358
+ ]
359
+ }
360
+ return {"tools": []}
361
+
362
+ return self._create_json_endpoint(get_tools_data)
363
+
364
+ async def _wait_for_server_ready(
365
+ self,
366
+ host: str,
367
+ port: int,
368
+ timeout: float = 2.0,
369
+ poll_interval: float = 0.05,
370
+ ) -> None:
371
+ """Wait for server to be ready by polling TCP connect.
372
+
373
+ Args:
374
+ host: Server host
375
+ port: Server port
376
+ timeout: Maximum time to wait
377
+ poll_interval: Time between connection attempts
378
+
379
+ Raises:
380
+ ProtocolConfigurationError: If server doesn't start within timeout
381
+
382
+ Note:
383
+ Circuit Breaker Failures Are NOT Recorded Here
384
+
385
+ This method is for startup verification, not runtime health checking.
386
+ TCP connect failures during startup are expected and transient - the
387
+ server is still spinning up and will become available shortly.
388
+
389
+ Circuit breaker tracking is intentionally omitted because:
390
+
391
+ 1. Startup retries are bounded and transient - the method either succeeds
392
+ within the timeout or raises ProtocolConfigurationError, ending startup.
393
+
394
+ 2. Recording startup failures would pollute circuit breaker metrics with
395
+ expected transient failures, potentially triggering an open circuit
396
+ before the server even starts.
397
+
398
+ 3. Circuit breakers are designed for runtime fault tolerance - detecting
399
+ when a previously-healthy service becomes unhealthy. Startup behavior
400
+ is fundamentally different: we expect failures until success.
401
+
402
+ 4. If the server fails to start within timeout, we fail fast with
403
+ ProtocolConfigurationError rather than entering a degraded state.
404
+
405
+ Circuit breaker tracking should occur during runtime operations (e.g.,
406
+ tool execution, health checks) where failures indicate actual service
407
+ degradation rather than expected startup latency.
408
+ """
409
+ import socket
410
+
411
+ start_time = time.perf_counter()
412
+ last_error: Exception | None = None
413
+
414
+ while time.perf_counter() - start_time < timeout:
415
+ # Check if server task has failed
416
+ if self._server_task is not None and self._server_task.done():
417
+ exc = self._server_task.exception()
418
+ if exc:
419
+ ctx = ModelInfraErrorContext.with_correlation(
420
+ transport_type=EnumInfraTransportType.MCP,
421
+ operation="server_startup",
422
+ target_name="mcp_handler",
423
+ )
424
+ raise ProtocolConfigurationError(
425
+ f"Server failed to start: {exc}",
426
+ context=ctx,
427
+ ) from exc
428
+
429
+ # Try TCP connect
430
+ try:
431
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
432
+ sock.settimeout(poll_interval)
433
+ # TCP PROTOCOL REQUIREMENT (NOT a config fallback):
434
+ # When a server binds to 0.0.0.0 (INADDR_ANY), it listens on all
435
+ # interfaces but you cannot connect() to 0.0.0.0 - it's not a
436
+ # routable address. TCP requires connecting to a specific interface.
437
+ # Using 127.0.0.1 (loopback) is the correct way to reach a local
438
+ # server that bound to 0.0.0.0. This is standard TCP/IP behavior,
439
+ # not an environment configuration fallback.
440
+ effective_host = "127.0.0.1" if host == "0.0.0.0" else host # noqa: S104
441
+ result = sock.connect_ex((effective_host, port))
442
+ sock.close()
443
+ if result == 0:
444
+ return # Server is ready
445
+ except Exception as e:
446
+ last_error = e
447
+
448
+ await asyncio.sleep(poll_interval)
449
+
450
+ # Timeout reached
451
+ ctx = ModelInfraErrorContext.with_correlation(
452
+ transport_type=EnumInfraTransportType.MCP,
453
+ operation="server_startup",
454
+ target_name="mcp_handler",
455
+ )
456
+ raise ProtocolConfigurationError(
457
+ f"Server failed to start within {timeout}s. Last error: {last_error}",
458
+ context=ctx,
459
+ )
460
+
461
+ async def initialize(self, config: dict[str, object]) -> None:
462
+ """Initialize MCP handler with configuration and optionally start uvicorn server.
463
+
464
+ This method performs the following steps:
465
+ 1. Parse and validate handler configuration
466
+ 2. Initialize MCPServerLifecycle for tool discovery (unless skip_server=True)
467
+ 3. Create Starlette app with /health and /mcp/tools endpoints
468
+ 4. Start uvicorn server in a background task (unless skip_server=True)
469
+
470
+ Args:
471
+ config: Configuration dict containing:
472
+ - host: Host to bind MCP server to (default: 0.0.0.0)
473
+ - port: Port for MCP endpoint (default: 8090)
474
+ - path: URL path for MCP endpoint (default: /mcp)
475
+ - stateless: Enable stateless mode (default: True)
476
+ - json_response: Return JSON responses (default: True)
477
+ - timeout_seconds: Tool execution timeout (default: 30.0)
478
+ - max_tools: Maximum tools to expose (default: 100)
479
+ - consul_host: Consul server hostname (REQUIRED - no default)
480
+ - consul_port: Consul server port (REQUIRED - no default)
481
+ - kafka_enabled: Whether to enable Kafka hot reload (REQUIRED - no default)
482
+ - dev_mode: Whether to run in development mode (REQUIRED - no default)
483
+ - contracts_dir: Directory for contract scanning in dev mode (optional)
484
+ - skip_server: Skip starting uvicorn server (default: False).
485
+ Use for unit testing to avoid port binding.
486
+
487
+ Raises:
488
+ ProtocolConfigurationError: If configuration is invalid or required
489
+ config values (consul_host, consul_port, kafka_enabled, dev_mode)
490
+ are missing. Per CLAUDE.md, .env is the single source of truth -
491
+ no hardcoded fallbacks are used.
492
+ """
493
+ init_correlation_id = uuid4()
494
+
495
+ logger.info(
496
+ "Initializing %s",
497
+ self.__class__.__name__,
498
+ extra={
499
+ "handler": self.__class__.__name__,
500
+ "correlation_id": str(init_correlation_id),
501
+ },
502
+ )
503
+
504
+ try:
505
+ # Use Pydantic validation for type-safe configuration parsing.
506
+ # Pydantic handles type coercion (e.g., str "8090" -> int 8090) automatically.
507
+ # ValidationError will be raised for truly invalid types that cannot be coerced.
508
+ self._config = ModelMcpHandlerConfig(**config)
509
+
510
+ # Initialize tool registry (empty until tools are registered)
511
+ self._tool_registry = {}
512
+
513
+ # Initialize circuit breaker for tool execution resilience
514
+ # Configuration from contract.yaml: threshold=5, reset_timeout=60.0
515
+ self._init_circuit_breaker(
516
+ threshold=5,
517
+ reset_timeout=60.0,
518
+ service_name="mcp-handler",
519
+ transport_type=EnumInfraTransportType.MCP,
520
+ )
521
+
522
+ # Check if server startup should be skipped (for unit testing)
523
+ skip_server_val = config.get("skip_server")
524
+ skip_server: bool = (
525
+ skip_server_val if isinstance(skip_server_val, bool) else False
526
+ )
527
+ self._skip_server = skip_server
528
+
529
+ if not skip_server:
530
+ # Build MCPServerConfig from handler config (OMN-1282)
531
+ # Map handler config fields to lifecycle config fields
532
+ #
533
+ # Per CLAUDE.md: .env is the SINGLE SOURCE OF TRUTH.
534
+ # No hardcoded fallbacks - all required config must be explicit.
535
+ # The _require_config_value helper validates type, cast() is for mypy.
536
+ consul_host = _require_config_value(
537
+ config, "consul_host", str, init_correlation_id
538
+ )
539
+ consul_port = _require_config_value(
540
+ config, "consul_port", int, init_correlation_id
541
+ )
542
+ kafka_enabled = _require_config_value(
543
+ config, "kafka_enabled", bool, init_correlation_id
544
+ )
545
+ dev_mode = _require_config_value(
546
+ config, "dev_mode", bool, init_correlation_id
547
+ )
548
+ # contracts_dir is optional - only used when dev_mode=True
549
+ contracts_dir_val = config.get("contracts_dir")
550
+ contracts_dir: str | None = (
551
+ contracts_dir_val if isinstance(contracts_dir_val, str) else None
552
+ )
553
+
554
+ server_config = ModelMCPServerConfig(
555
+ consul_host=consul_host,
556
+ consul_port=consul_port,
557
+ kafka_enabled=kafka_enabled,
558
+ http_host=self._config.host,
559
+ http_port=self._config.port,
560
+ default_timeout=self._config.timeout_seconds,
561
+ dev_mode=dev_mode,
562
+ contracts_dir=contracts_dir,
563
+ )
564
+
565
+ # Wrap entire server startup in try/except to ensure cleanup
566
+ # if ANY step fails after lifecycle starts. This prevents:
567
+ # - Orphan lifecycle resources (registry, executor, sync)
568
+ # - Orphan server tasks
569
+ # - Resource leaks from partial initialization
570
+ try:
571
+ # Create and start MCPServerLifecycle for tool discovery
572
+ # Container is required for lifecycle initialization
573
+ if self._container is None:
574
+ raise ValueError(
575
+ "Container required for MCPServerLifecycle initialization"
576
+ )
577
+ self._lifecycle = MCPServerLifecycle(
578
+ container=self._container,
579
+ config=server_config,
580
+ bus=None,
581
+ )
582
+ await self._lifecycle.start()
583
+
584
+ # Update MCP registry and executor references from lifecycle
585
+ if self._lifecycle.registry is not None:
586
+ self._mcp_registry = self._lifecycle.registry
587
+ if self._lifecycle.executor is not None:
588
+ self._mcp_executor = self._lifecycle.executor
589
+
590
+ # Create Starlette app with HTTP endpoints (OMN-1282)
591
+ # Use factory methods for explicit handler reference binding
592
+ health_endpoint = self._create_health_endpoint()
593
+ tools_list_endpoint = self._create_tools_list_endpoint()
594
+
595
+ app = Starlette(
596
+ routes=[
597
+ Route("/health", health_endpoint, methods=["GET"]),
598
+ Route("/mcp/tools", tools_list_endpoint, methods=["GET"]),
599
+ ],
600
+ )
601
+
602
+ # Create uvicorn server config and server
603
+ uvicorn_config = uvicorn.Config(
604
+ app=app,
605
+ host=self._config.host,
606
+ port=self._config.port,
607
+ log_level="info",
608
+ )
609
+ self._server = uvicorn.Server(uvicorn_config)
610
+
611
+ # Start server in background task
612
+ self._server_task = asyncio.create_task(self._server.serve())
613
+
614
+ # Wait for server to be ready before marking as initialized
615
+ await self._wait_for_server_ready(
616
+ self._config.host,
617
+ self._config.port,
618
+ timeout=self.startup_timeout,
619
+ )
620
+ self._server_started_at = time.time()
621
+
622
+ except Exception as startup_error:
623
+ # Any failure during server startup - clean up all resources
624
+ # This handles failures in:
625
+ # - lifecycle.start() (Consul/contract discovery)
626
+ # - Starlette app creation
627
+ # - uvicorn config/server creation
628
+ # - server task creation
629
+ # - server readiness check
630
+ logger.exception(
631
+ "MCP server startup failed, cleaning up resources",
632
+ extra={
633
+ "host": self._config.host,
634
+ "port": self._config.port,
635
+ "lifecycle_created": self._lifecycle is not None,
636
+ "server_created": self._server is not None,
637
+ "server_task_created": self._server_task is not None,
638
+ "correlation_id": str(init_correlation_id),
639
+ },
640
+ )
641
+ # shutdown() safely handles partially initialized state:
642
+ # - Checks each component before cleanup
643
+ # - Safe to call even if components weren't created
644
+ await self.shutdown()
645
+ ctx = ModelInfraErrorContext(
646
+ transport_type=EnumInfraTransportType.MCP,
647
+ operation="initialize",
648
+ target_name="mcp_handler",
649
+ correlation_id=init_correlation_id,
650
+ )
651
+ raise ProtocolConfigurationError(
652
+ f"MCP server startup failed: {startup_error}",
653
+ context=ctx,
654
+ ) from startup_error
655
+
656
+ self._initialized = True
657
+
658
+ tool_count = 0
659
+ if self._lifecycle and self._lifecycle.registry:
660
+ tool_count = self._lifecycle.registry.tool_count
661
+
662
+ if skip_server:
663
+ logger.info(
664
+ "%s initialized successfully (server skipped)",
665
+ self.__class__.__name__,
666
+ extra={
667
+ "handler": self.__class__.__name__,
668
+ "host": self._config.host,
669
+ "port": self._config.port,
670
+ "path": self._config.path,
671
+ "stateless": self._config.stateless,
672
+ "skip_server": True,
673
+ "correlation_id": str(init_correlation_id),
674
+ },
675
+ )
676
+ else:
677
+ logger.info(
678
+ "%s initialized successfully - uvicorn server running",
679
+ self.__class__.__name__,
680
+ extra={
681
+ "handler": self.__class__.__name__,
682
+ "host": self._config.host,
683
+ "port": self._config.port,
684
+ "path": self._config.path,
685
+ "stateless": self._config.stateless,
686
+ "tool_count": tool_count,
687
+ "url": f"http://{self._config.host}:{self._config.port}",
688
+ "correlation_id": str(init_correlation_id),
689
+ },
690
+ )
691
+
692
+ except ValidationError as e:
693
+ ctx = ModelInfraErrorContext(
694
+ transport_type=EnumInfraTransportType.MCP,
695
+ operation="initialize",
696
+ target_name="mcp_handler",
697
+ correlation_id=init_correlation_id,
698
+ )
699
+ raise ProtocolConfigurationError(
700
+ f"Invalid MCP handler configuration: {e}", context=ctx
701
+ ) from e
702
+ except (TypeError, ValueError) as e:
703
+ ctx = ModelInfraErrorContext(
704
+ transport_type=EnumInfraTransportType.MCP,
705
+ operation="initialize",
706
+ target_name="mcp_handler",
707
+ correlation_id=init_correlation_id,
708
+ )
709
+ raise ProtocolConfigurationError(
710
+ f"Invalid MCP handler configuration: {e}", context=ctx
711
+ ) from e
712
+
713
+ async def shutdown(self) -> None:
714
+ """Shutdown MCP handler with timeout protection.
715
+
716
+ This method performs graceful shutdown with timeout protection:
717
+ 1. Signal uvicorn server to stop
718
+ 2. Wait for server task with timeout (max 5s graceful, 1s forced)
719
+ 3. Shutdown MCPServerLifecycle (registry, discovery, sync)
720
+ 4. Clear tool registry and reset state
721
+
722
+ Safe to call multiple times. Never hangs indefinitely (max ~6s with defaults).
723
+
724
+ Note:
725
+ Timeouts are configurable via class attributes:
726
+ - shutdown_timeout: Graceful shutdown timeout (default: 5.0s)
727
+ - cancel_timeout: Forced cancellation timeout (default: 1.0s)
728
+ """
729
+ shutdown_correlation_id = uuid4()
730
+
731
+ logger.info(
732
+ "Shutting down %s",
733
+ self.__class__.__name__,
734
+ extra={
735
+ "handler": self.__class__.__name__,
736
+ "correlation_id": str(shutdown_correlation_id),
737
+ },
738
+ )
739
+
740
+ # Stop uvicorn server with timeout protection (OMN-1282)
741
+ if (
742
+ self._server is not None
743
+ and self._server_task is not None
744
+ and not self._skip_server
745
+ ):
746
+ # Signal server to stop
747
+ self._server.should_exit = True
748
+
749
+ try:
750
+ # Wait for graceful shutdown with timeout
751
+ logger.debug(
752
+ "Waiting for server task to complete",
753
+ extra={
754
+ "timeout_seconds": self.shutdown_timeout,
755
+ "correlation_id": str(shutdown_correlation_id),
756
+ },
757
+ )
758
+ await asyncio.wait_for(self._server_task, timeout=self.shutdown_timeout)
759
+ except TimeoutError:
760
+ logger.warning(
761
+ "Server shutdown timed out, forcing cancellation",
762
+ extra={
763
+ "timeout_seconds": self.shutdown_timeout,
764
+ "correlation_id": str(shutdown_correlation_id),
765
+ },
766
+ )
767
+ self._server_task.cancel()
768
+ try:
769
+ await asyncio.wait_for(
770
+ self._server_task, timeout=self.cancel_timeout
771
+ )
772
+ except (TimeoutError, asyncio.CancelledError):
773
+ pass # Best effort
774
+ except asyncio.CancelledError:
775
+ logger.debug(
776
+ "Server task was cancelled",
777
+ extra={"correlation_id": str(shutdown_correlation_id)},
778
+ )
779
+
780
+ # Shutdown lifecycle (registry, discovery, sync)
781
+ if self._lifecycle is not None:
782
+ logger.debug(
783
+ "Shutting down MCPServerLifecycle",
784
+ extra={"correlation_id": str(shutdown_correlation_id)},
785
+ )
786
+ await self._lifecycle.shutdown()
787
+ self._lifecycle = None
788
+
789
+ # Clear registry and executor references
790
+ self._mcp_registry = None
791
+ self._mcp_executor = None
792
+
793
+ # Clear all state
794
+ self._tool_registry.clear()
795
+ self._config = None
796
+ self._initialized = False
797
+ self._server = None
798
+ self._server_task = None
799
+ self._skip_server = False
800
+ self._server_started_at = None
801
+
802
+ logger.info(
803
+ "%s shutdown complete",
804
+ self.__class__.__name__,
805
+ extra={
806
+ "handler": self.__class__.__name__,
807
+ "correlation_id": str(shutdown_correlation_id),
808
+ },
809
+ )
810
+
811
+ async def execute(
812
+ self, envelope: dict[str, object]
813
+ ) -> ModelHandlerOutput[dict[str, object]]:
814
+ """Execute MCP operation from envelope.
815
+
816
+ Supported operations:
817
+ - mcp.list_tools: List all available MCP tools
818
+ - mcp.call_tool: Invoke a specific tool
819
+ - mcp.describe: Return handler metadata
820
+
821
+ Args:
822
+ envelope: Request envelope containing:
823
+ - operation: One of the supported MCP operations
824
+ - payload: Operation-specific payload
825
+ - correlation_id: Optional correlation ID
826
+ - envelope_id: Optional envelope ID
827
+
828
+ Returns:
829
+ ModelHandlerOutput containing operation result.
830
+
831
+ Raises:
832
+ RuntimeHostError: If handler not initialized.
833
+ ProtocolConfigurationError: If operation invalid.
834
+ """
835
+ correlation_id = self._extract_correlation_id(envelope)
836
+ input_envelope_id = self._extract_envelope_id(envelope)
837
+
838
+ if not self._initialized:
839
+ ctx = ModelInfraErrorContext(
840
+ transport_type=EnumInfraTransportType.MCP,
841
+ operation="execute",
842
+ target_name="mcp_handler",
843
+ correlation_id=correlation_id,
844
+ )
845
+ raise RuntimeHostError(
846
+ "HandlerMCP not initialized. Call initialize() first.", context=ctx
847
+ )
848
+
849
+ operation = envelope.get("operation")
850
+ if not isinstance(operation, str):
851
+ ctx = ModelInfraErrorContext(
852
+ transport_type=EnumInfraTransportType.MCP,
853
+ operation="execute",
854
+ target_name="mcp_handler",
855
+ correlation_id=correlation_id,
856
+ )
857
+ raise ProtocolConfigurationError(
858
+ "Missing or invalid 'operation' in envelope", context=ctx
859
+ )
860
+
861
+ if operation not in _SUPPORTED_OPERATIONS:
862
+ ctx = ModelInfraErrorContext(
863
+ transport_type=EnumInfraTransportType.MCP,
864
+ operation=operation,
865
+ target_name="mcp_handler",
866
+ correlation_id=correlation_id,
867
+ )
868
+ raise ProtocolConfigurationError(
869
+ f"Operation '{operation}' not supported. "
870
+ f"Available: {', '.join(sorted(_SUPPORTED_OPERATIONS))}",
871
+ context=ctx,
872
+ )
873
+
874
+ payload = envelope.get("payload", {})
875
+ if not isinstance(payload, dict):
876
+ payload = {}
877
+
878
+ # Route to operation handler
879
+ if operation == EnumMcpOperationType.LIST_TOOLS.value:
880
+ return await self._handle_list_tools(
881
+ payload, correlation_id, input_envelope_id
882
+ )
883
+ elif operation == EnumMcpOperationType.CALL_TOOL.value:
884
+ return await self._handle_call_tool(
885
+ payload, correlation_id, input_envelope_id
886
+ )
887
+ else: # mcp.describe
888
+ return await self._handle_describe(correlation_id, input_envelope_id)
889
+
890
+ async def _handle_list_tools(
891
+ self,
892
+ payload: dict[str, object],
893
+ correlation_id: UUID,
894
+ input_envelope_id: UUID,
895
+ ) -> ModelHandlerOutput[dict[str, object]]:
896
+ """Handle mcp.list_tools operation.
897
+
898
+ Returns a list of all registered MCP tools with their schemas.
899
+ """
900
+ tools = self._get_tool_definitions()
901
+
902
+ # Convert to MCP-compatible format
903
+ tool_list = [
904
+ {
905
+ "name": tool.name,
906
+ "description": tool.description,
907
+ "inputSchema": self._build_input_schema(tool),
908
+ }
909
+ for tool in tools
910
+ ]
911
+
912
+ return ModelHandlerOutput.for_compute(
913
+ input_envelope_id=input_envelope_id,
914
+ correlation_id=correlation_id,
915
+ handler_id=HANDLER_ID_MCP,
916
+ result={
917
+ "status": "success",
918
+ "payload": {"tools": tool_list},
919
+ "correlation_id": str(correlation_id),
920
+ },
921
+ )
922
+
923
+ async def _handle_call_tool(
924
+ self,
925
+ payload: dict[str, object],
926
+ correlation_id: UUID,
927
+ input_envelope_id: UUID,
928
+ ) -> ModelHandlerOutput[dict[str, object]]:
929
+ """Handle mcp.call_tool operation.
930
+
931
+ Invokes the specified tool with provided arguments.
932
+ """
933
+ # Parse tool call request
934
+ tool_name = payload.get("tool_name")
935
+ if not isinstance(tool_name, str):
936
+ ctx = ModelInfraErrorContext(
937
+ transport_type=EnumInfraTransportType.MCP,
938
+ operation="mcp.call_tool",
939
+ target_name="mcp_handler",
940
+ correlation_id=correlation_id,
941
+ )
942
+ raise ProtocolConfigurationError(
943
+ "Missing or invalid 'tool_name' in payload", context=ctx
944
+ )
945
+
946
+ arguments = payload.get("arguments", {})
947
+ if not isinstance(arguments, dict):
948
+ arguments = {}
949
+
950
+ # Check if tool exists
951
+ if tool_name not in self._tool_registry:
952
+ ctx = ModelInfraErrorContext(
953
+ transport_type=EnumInfraTransportType.MCP,
954
+ operation="mcp.call_tool",
955
+ target_name=tool_name,
956
+ correlation_id=correlation_id,
957
+ )
958
+ raise InfraUnavailableError(
959
+ f"Tool '{tool_name}' not found in registry", context=ctx
960
+ )
961
+
962
+ # Execute tool (placeholder - actual execution delegates to ONEX node)
963
+ start_time = time.perf_counter()
964
+
965
+ try:
966
+ result = await self._execute_tool(tool_name, arguments, correlation_id)
967
+ execution_time_ms = (time.perf_counter() - start_time) * 1000
968
+
969
+ tool_result = ModelMcpToolResult(
970
+ success=True,
971
+ content=result,
972
+ is_error=False,
973
+ correlation_id=correlation_id,
974
+ execution_time_ms=execution_time_ms,
975
+ )
976
+
977
+ except InfraUnavailableError as e:
978
+ # Circuit breaker open or tool unavailable
979
+ execution_time_ms = (time.perf_counter() - start_time) * 1000
980
+ logger.warning(
981
+ "Tool execution failed: infrastructure unavailable",
982
+ extra={
983
+ "tool_name": tool_name,
984
+ "error": str(e),
985
+ "error_type": "InfraUnavailableError",
986
+ "correlation_id": str(correlation_id),
987
+ "execution_time_ms": execution_time_ms,
988
+ },
989
+ )
990
+ tool_result = ModelMcpToolResult(
991
+ success=False,
992
+ content=str(e),
993
+ is_error=True,
994
+ error_message=str(e),
995
+ correlation_id=correlation_id,
996
+ execution_time_ms=execution_time_ms,
997
+ )
998
+
999
+ except (RuntimeHostError, ProtocolConfigurationError) as e:
1000
+ # Handler or configuration errors
1001
+ execution_time_ms = (time.perf_counter() - start_time) * 1000
1002
+ logger.warning(
1003
+ "Tool execution failed: runtime or configuration error",
1004
+ extra={
1005
+ "tool_name": tool_name,
1006
+ "error": str(e),
1007
+ "error_type": type(e).__name__,
1008
+ "correlation_id": str(correlation_id),
1009
+ "execution_time_ms": execution_time_ms,
1010
+ },
1011
+ )
1012
+ tool_result = ModelMcpToolResult(
1013
+ success=False,
1014
+ content=str(e),
1015
+ is_error=True,
1016
+ error_message=str(e),
1017
+ correlation_id=correlation_id,
1018
+ execution_time_ms=execution_time_ms,
1019
+ )
1020
+
1021
+ except (TimeoutError, OSError) as e:
1022
+ # Network/IO errors during tool execution
1023
+ execution_time_ms = (time.perf_counter() - start_time) * 1000
1024
+ logger.exception(
1025
+ "Tool execution failed: network or timeout error",
1026
+ extra={
1027
+ "tool_name": tool_name,
1028
+ "error": str(e),
1029
+ "error_type": type(e).__name__,
1030
+ "correlation_id": str(correlation_id),
1031
+ "execution_time_ms": execution_time_ms,
1032
+ },
1033
+ )
1034
+ tool_result = ModelMcpToolResult(
1035
+ success=False,
1036
+ content=str(e),
1037
+ is_error=True,
1038
+ error_message=str(e),
1039
+ correlation_id=correlation_id,
1040
+ execution_time_ms=execution_time_ms,
1041
+ )
1042
+
1043
+ except (ValueError, TypeError, KeyError) as e:
1044
+ # Data validation or type errors
1045
+ execution_time_ms = (time.perf_counter() - start_time) * 1000
1046
+ logger.warning(
1047
+ "Tool execution failed: data validation error",
1048
+ extra={
1049
+ "tool_name": tool_name,
1050
+ "error": str(e),
1051
+ "error_type": type(e).__name__,
1052
+ "correlation_id": str(correlation_id),
1053
+ "execution_time_ms": execution_time_ms,
1054
+ },
1055
+ )
1056
+ tool_result = ModelMcpToolResult(
1057
+ success=False,
1058
+ content=str(e),
1059
+ is_error=True,
1060
+ error_message=str(e),
1061
+ correlation_id=correlation_id,
1062
+ execution_time_ms=execution_time_ms,
1063
+ )
1064
+
1065
+ return ModelHandlerOutput.for_compute(
1066
+ input_envelope_id=input_envelope_id,
1067
+ correlation_id=correlation_id,
1068
+ handler_id=HANDLER_ID_MCP,
1069
+ result={
1070
+ "status": "success" if tool_result.success else "error",
1071
+ "payload": tool_result.model_dump(),
1072
+ "correlation_id": str(correlation_id),
1073
+ },
1074
+ )
1075
+
1076
+ async def _handle_describe(
1077
+ self,
1078
+ correlation_id: UUID,
1079
+ input_envelope_id: UUID,
1080
+ ) -> ModelHandlerOutput[dict[str, object]]:
1081
+ """Handle mcp.describe operation."""
1082
+ return ModelHandlerOutput.for_compute(
1083
+ input_envelope_id=input_envelope_id,
1084
+ correlation_id=correlation_id,
1085
+ handler_id=HANDLER_ID_MCP,
1086
+ result={
1087
+ "status": "success",
1088
+ "payload": self.describe(),
1089
+ "correlation_id": str(correlation_id),
1090
+ },
1091
+ )
1092
+
1093
+ def _get_tool_definitions(self) -> Sequence[ProtocolMCPToolDefinition]:
1094
+ """Get all registered tool definitions."""
1095
+ return list(self._tool_registry.values())
1096
+
1097
+ def _build_input_schema(self, tool: ProtocolMCPToolDefinition) -> dict[str, object]:
1098
+ """Build JSON Schema for tool input from MCP tool definition."""
1099
+ properties: dict[str, object] = {}
1100
+ required: list[str] = []
1101
+
1102
+ for param in tool.parameters:
1103
+ param_schema: dict[str, object] = {
1104
+ "type": param.parameter_type,
1105
+ "description": param.description,
1106
+ }
1107
+ if param.schema:
1108
+ param_schema.update(param.schema)
1109
+
1110
+ properties[param.name] = param_schema
1111
+
1112
+ if param.required:
1113
+ required.append(param.name)
1114
+
1115
+ return {
1116
+ "type": "object",
1117
+ "properties": properties,
1118
+ "required": required,
1119
+ }
1120
+
1121
+ async def _execute_tool(
1122
+ self,
1123
+ tool_name: str,
1124
+ arguments: dict[str, object],
1125
+ correlation_id: UUID,
1126
+ ) -> dict[str, object]:
1127
+ """Execute a registered tool.
1128
+
1129
+ This method delegates to the ONEX orchestrator that provides this tool.
1130
+ When operating in integrated mode (with registry and executor), the tool
1131
+ is looked up from the MCP registry and executed via the execution adapter.
1132
+
1133
+ Circuit breaker protection is applied to prevent cascading failures
1134
+ when tool execution repeatedly fails.
1135
+
1136
+ Integration Mode (OMN-1281):
1137
+ When _mcp_registry and _mcp_executor are configured:
1138
+ 1. Look up the tool definition from the MCP registry
1139
+ 2. Delegate execution to AdapterONEXToolExecution
1140
+ 3. The adapter dispatches to the orchestrator endpoint
1141
+ 4. Timeout is enforced by the adapter using the tool's timeout_seconds
1142
+
1143
+ Legacy Mode:
1144
+ When registry/executor are not configured, returns placeholder response
1145
+ for backward compatibility.
1146
+
1147
+ Args:
1148
+ tool_name: Name of the tool to execute.
1149
+ arguments: Tool arguments.
1150
+ correlation_id: Correlation ID for tracing.
1151
+
1152
+ Returns:
1153
+ Tool execution result.
1154
+
1155
+ Raises:
1156
+ InfraUnavailableError: If tool not found or execution fails.
1157
+ """
1158
+ # Check circuit breaker before tool execution
1159
+ async with self._circuit_breaker_lock:
1160
+ await self._check_circuit_breaker("execute_tool", correlation_id)
1161
+
1162
+ try:
1163
+ # Integrated mode: use MCP registry and executor (OMN-1281)
1164
+ if self._mcp_registry is not None and self._mcp_executor is not None:
1165
+ # Look up tool from registry
1166
+ tool = await self._mcp_registry.get_tool(tool_name)
1167
+ if tool is None:
1168
+ ctx = ModelInfraErrorContext.with_correlation(
1169
+ correlation_id=correlation_id,
1170
+ transport_type=self.transport_type,
1171
+ operation="execute_tool",
1172
+ target_name=tool_name,
1173
+ )
1174
+ raise InfraUnavailableError(
1175
+ f"Tool not found: {tool_name}",
1176
+ context=ctx,
1177
+ )
1178
+
1179
+ logger.info(
1180
+ "Executing MCP tool via adapter",
1181
+ extra={
1182
+ "tool_name": tool_name,
1183
+ "argument_count": len(arguments),
1184
+ "correlation_id": str(correlation_id),
1185
+ },
1186
+ )
1187
+
1188
+ # Execute via adapter
1189
+ result = await self._mcp_executor.execute(
1190
+ tool=tool,
1191
+ arguments=arguments,
1192
+ correlation_id=correlation_id,
1193
+ )
1194
+
1195
+ # Reset circuit breaker on success
1196
+ async with self._circuit_breaker_lock:
1197
+ await self._reset_circuit_breaker()
1198
+
1199
+ return result
1200
+
1201
+ # Legacy mode: placeholder response for backward compatibility
1202
+ logger.info(
1203
+ "Tool execution requested (placeholder mode)",
1204
+ extra={
1205
+ "tool_name": tool_name,
1206
+ "argument_count": len(arguments),
1207
+ "correlation_id": str(correlation_id),
1208
+ },
1209
+ )
1210
+
1211
+ placeholder_result: dict[str, object] = {
1212
+ "message": f"Tool '{tool_name}' executed successfully",
1213
+ "arguments_received": list(arguments.keys()),
1214
+ }
1215
+
1216
+ # Reset circuit breaker on success
1217
+ async with self._circuit_breaker_lock:
1218
+ await self._reset_circuit_breaker()
1219
+
1220
+ return placeholder_result
1221
+
1222
+ except InfraUnavailableError:
1223
+ # Record failure in circuit breaker and re-raise
1224
+ async with self._circuit_breaker_lock:
1225
+ await self._record_circuit_failure("execute_tool", correlation_id)
1226
+ raise
1227
+
1228
+ except Exception:
1229
+ # Record failure in circuit breaker
1230
+ async with self._circuit_breaker_lock:
1231
+ await self._record_circuit_failure("execute_tool", correlation_id)
1232
+ raise
1233
+
1234
+ def register_tool(self, tool: ProtocolMCPToolDefinition) -> bool:
1235
+ """Register an MCP tool definition.
1236
+
1237
+ Args:
1238
+ tool: Tool definition to register.
1239
+
1240
+ Returns:
1241
+ True if tool was registered successfully, False if max tool limit exceeded.
1242
+
1243
+ Note:
1244
+ Callers MUST check the return value. If False, the tool was NOT registered
1245
+ due to the max_tools limit being reached. Silently ignoring a False return
1246
+ will lead to tools being unavailable without any error being raised.
1247
+
1248
+ The tool registry is a simple dict and is NOT thread-safe. If concurrent
1249
+ registration is required, external synchronization must be provided by
1250
+ the caller.
1251
+
1252
+ Example:
1253
+ if not handler.register_tool(my_tool):
1254
+ ctx = ModelInfraErrorContext(
1255
+ transport_type=EnumInfraTransportType.MCP,
1256
+ operation="register_tool",
1257
+ target_name=my_tool.name,
1258
+ correlation_id=uuid4(),
1259
+ )
1260
+ raise ProtocolConfigurationError(
1261
+ f"Failed to register tool: {my_tool.name}",
1262
+ context=ctx,
1263
+ )
1264
+ """
1265
+ if self._config and len(self._tool_registry) >= self._config.max_tools:
1266
+ logger.warning(
1267
+ "Maximum tool limit reached, tool not registered",
1268
+ extra={"tool_name": tool.name, "max_tools": self._config.max_tools},
1269
+ )
1270
+ return False
1271
+
1272
+ self._tool_registry[tool.name] = tool
1273
+ logger.info(
1274
+ "Tool registered",
1275
+ extra={
1276
+ "tool_name": tool.name,
1277
+ "tool_type": tool.tool_type,
1278
+ "parameter_count": len(tool.parameters),
1279
+ },
1280
+ )
1281
+ return True
1282
+
1283
+ def unregister_tool(self, tool_name: str) -> bool:
1284
+ """Unregister an MCP tool.
1285
+
1286
+ Args:
1287
+ tool_name: Name of the tool to unregister.
1288
+
1289
+ Returns:
1290
+ True if tool was unregistered, False if not found.
1291
+ """
1292
+ if tool_name in self._tool_registry:
1293
+ del self._tool_registry[tool_name]
1294
+ logger.info("Tool unregistered", extra={"tool_name": tool_name})
1295
+ return True
1296
+ return False
1297
+
1298
+ def describe(self) -> dict[str, object]:
1299
+ """Return handler metadata and capabilities.
1300
+
1301
+ Returns:
1302
+ dict containing handler type, category, transport type,
1303
+ supported operations, configuration, tool count, and server state.
1304
+ """
1305
+ config_dict: dict[str, object] = {}
1306
+ if self._config:
1307
+ config_dict = {
1308
+ "host": self._config.host,
1309
+ "port": self._config.port,
1310
+ "path": self._config.path,
1311
+ "stateless": self._config.stateless,
1312
+ "json_response": self._config.json_response,
1313
+ "timeout_seconds": self._config.timeout_seconds,
1314
+ "max_tools": self._config.max_tools,
1315
+ }
1316
+
1317
+ # Include lifecycle tool count if available (OMN-1282)
1318
+ tool_count = len(self._tool_registry)
1319
+ if self._lifecycle and self._lifecycle.registry:
1320
+ tool_count = self._lifecycle.registry.tool_count
1321
+
1322
+ return {
1323
+ "handler_type": self.handler_type.value,
1324
+ "handler_category": self.handler_category.value,
1325
+ "transport_type": self.transport_type.value,
1326
+ "supported_operations": sorted(_SUPPORTED_OPERATIONS),
1327
+ "tool_count": tool_count,
1328
+ "config": config_dict,
1329
+ "initialized": self._initialized,
1330
+ "server_running": self._server is not None
1331
+ and self._server_task is not None,
1332
+ "lifecycle_running": self._lifecycle is not None
1333
+ and self._lifecycle.is_running,
1334
+ "version": "0.1.0-mvp",
1335
+ }
1336
+
1337
+ async def health_check(self) -> dict[str, object]:
1338
+ """Check handler health and server status.
1339
+
1340
+ Returns unhealthy if:
1341
+ - Not initialized
1342
+ - Server task has crashed/completed unexpectedly
1343
+ - Server task was cancelled
1344
+
1345
+ Note:
1346
+ When skip_server=True was used during initialization, the handler is
1347
+ considered healthy if initialized, even without a running server.
1348
+ This enables unit testing without actual port binding.
1349
+ """
1350
+ if not self._initialized:
1351
+ return {
1352
+ "healthy": False,
1353
+ "reason": "not_initialized",
1354
+ "transport_type": self.transport_type.value,
1355
+ }
1356
+
1357
+ if self._skip_server:
1358
+ return {
1359
+ "healthy": True,
1360
+ "skip_server": True,
1361
+ "transport_type": self.transport_type.value,
1362
+ "initialized": True,
1363
+ }
1364
+
1365
+ # Capture server task reference once to avoid TOCTOU race conditions.
1366
+ # If _server_task is reassigned (e.g., by concurrent shutdown()),
1367
+ # we work with the captured reference consistently.
1368
+ server_task = self._server_task
1369
+
1370
+ # Check server task state
1371
+ if server_task is None:
1372
+ return {
1373
+ "healthy": False,
1374
+ "reason": "server_task_missing",
1375
+ "transport_type": self.transport_type.value,
1376
+ "initialized": True,
1377
+ }
1378
+
1379
+ if server_task.done():
1380
+ # Task completed - check why
1381
+ if server_task.cancelled():
1382
+ return {
1383
+ "healthy": False,
1384
+ "reason": "server_cancelled",
1385
+ "transport_type": self.transport_type.value,
1386
+ "initialized": True,
1387
+ }
1388
+
1389
+ exc = server_task.exception()
1390
+ if exc is not None:
1391
+ return {
1392
+ "healthy": False,
1393
+ "reason": "server_crashed",
1394
+ "error": str(exc)[:_ERROR_MESSAGE_MAX_LENGTH],
1395
+ "transport_type": self.transport_type.value,
1396
+ "initialized": True,
1397
+ }
1398
+
1399
+ # Exited cleanly but unexpectedly
1400
+ return {
1401
+ "healthy": False,
1402
+ "reason": "server_exited",
1403
+ "transport_type": self.transport_type.value,
1404
+ "initialized": True,
1405
+ }
1406
+
1407
+ # Task is still running - healthy
1408
+ # Include lifecycle tool count if available (OMN-1282)
1409
+ tool_count = len(self._tool_registry)
1410
+ if self._lifecycle and self._lifecycle.registry:
1411
+ tool_count = self._lifecycle.registry.tool_count
1412
+
1413
+ lifecycle_running = self._lifecycle is not None and self._lifecycle.is_running
1414
+
1415
+ return {
1416
+ "healthy": True,
1417
+ "initialized": True,
1418
+ "server_running": True,
1419
+ "tool_count": tool_count,
1420
+ "transport_type": self.transport_type.value,
1421
+ "lifecycle_running": lifecycle_running,
1422
+ "uptime_seconds": (
1423
+ time.time() - self._server_started_at
1424
+ if self._server_started_at is not None
1425
+ else None
1426
+ ),
1427
+ }
1428
+
1429
+
1430
+ __all__: list[str] = ["HandlerMCP", "HANDLER_ID_MCP"]