omnibase_infra 0.2.1__py3-none-any.whl → 0.2.2__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 (116) hide show
  1. omnibase_infra/__init__.py +1 -1
  2. omnibase_infra/adapters/adapter_onex_tool_execution.py +446 -0
  3. omnibase_infra/cli/commands.py +1 -1
  4. omnibase_infra/configs/widget_mapping.yaml +176 -0
  5. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +4 -1
  6. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +4 -1
  7. omnibase_infra/errors/error_compute_registry.py +4 -1
  8. omnibase_infra/errors/error_event_bus_registry.py +4 -1
  9. omnibase_infra/errors/error_infra.py +3 -1
  10. omnibase_infra/errors/error_policy_registry.py +4 -1
  11. omnibase_infra/handlers/handler_db.py +2 -1
  12. omnibase_infra/handlers/handler_graph.py +10 -5
  13. omnibase_infra/handlers/handler_mcp.py +736 -63
  14. omnibase_infra/handlers/mixins/mixin_consul_kv.py +4 -3
  15. omnibase_infra/handlers/mixins/mixin_consul_service.py +2 -1
  16. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +301 -4
  17. omnibase_infra/handlers/service_discovery/models/model_service_info.py +10 -0
  18. omnibase_infra/mixins/mixin_async_circuit_breaker.py +3 -2
  19. omnibase_infra/mixins/mixin_node_introspection.py +24 -7
  20. omnibase_infra/mixins/mixin_retry_execution.py +1 -1
  21. omnibase_infra/models/handlers/__init__.py +10 -0
  22. omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
  23. omnibase_infra/models/handlers/model_handler_descriptor.py +15 -0
  24. omnibase_infra/models/mcp/__init__.py +15 -0
  25. omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
  26. omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
  27. omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
  28. omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
  29. omnibase_infra/models/registration/model_node_capabilities.py +11 -0
  30. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +0 -5
  31. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +17 -10
  32. omnibase_infra/nodes/effects/contract.yaml +0 -5
  33. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +7 -0
  34. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +86 -1
  35. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +3 -3
  36. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +9 -8
  37. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +14 -13
  38. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +0 -5
  39. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +46 -25
  40. omnibase_infra/nodes/node_registry_effect/contract.yaml +0 -5
  41. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +2 -1
  42. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +24 -19
  43. omnibase_infra/plugins/examples/plugin_json_normalizer.py +2 -2
  44. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +2 -2
  45. omnibase_infra/plugins/plugin_compute_base.py +16 -2
  46. omnibase_infra/protocols/protocol_event_projector.py +1 -1
  47. omnibase_infra/runtime/__init__.py +51 -1
  48. omnibase_infra/runtime/binding_config_resolver.py +102 -37
  49. omnibase_infra/runtime/constants_notification.py +75 -0
  50. omnibase_infra/runtime/contract_handler_discovery.py +6 -1
  51. omnibase_infra/runtime/handler_bootstrap_source.py +514 -0
  52. omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
  53. omnibase_infra/runtime/handler_contract_source.py +289 -167
  54. omnibase_infra/runtime/handler_plugin_loader.py +4 -2
  55. omnibase_infra/runtime/mixin_semver_cache.py +25 -1
  56. omnibase_infra/runtime/mixins/__init__.py +7 -0
  57. omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
  58. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +31 -10
  59. omnibase_infra/runtime/models/__init__.py +24 -0
  60. omnibase_infra/runtime/models/model_health_check_result.py +2 -1
  61. omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
  62. omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
  63. omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
  64. omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
  65. omnibase_infra/runtime/projector_plugin_loader.py +1 -1
  66. omnibase_infra/runtime/projector_shell.py +229 -1
  67. omnibase_infra/runtime/protocols/__init__.py +10 -0
  68. omnibase_infra/runtime/registry/registry_protocol_binding.py +3 -2
  69. omnibase_infra/runtime/registry_policy.py +9 -326
  70. omnibase_infra/runtime/secret_resolver.py +4 -2
  71. omnibase_infra/runtime/service_kernel.py +10 -2
  72. omnibase_infra/runtime/service_message_dispatch_engine.py +4 -2
  73. omnibase_infra/runtime/service_runtime_host_process.py +225 -15
  74. omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
  75. omnibase_infra/runtime/transition_notification_publisher.py +764 -0
  76. omnibase_infra/runtime/util_container_wiring.py +6 -5
  77. omnibase_infra/runtime/util_wiring.py +5 -1
  78. omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
  79. omnibase_infra/services/mcp/__init__.py +31 -0
  80. omnibase_infra/services/mcp/mcp_server_lifecycle.py +443 -0
  81. omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
  82. omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
  83. omnibase_infra/services/mcp/service_mcp_tool_sync.py +547 -0
  84. omnibase_infra/services/registry_api/__init__.py +40 -0
  85. omnibase_infra/services/registry_api/main.py +243 -0
  86. omnibase_infra/services/registry_api/models/__init__.py +66 -0
  87. omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
  88. omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
  89. omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
  90. omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
  91. omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
  92. omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
  93. omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
  94. omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
  95. omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
  96. omnibase_infra/services/registry_api/models/model_warning.py +49 -0
  97. omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
  98. omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
  99. omnibase_infra/services/registry_api/routes.py +371 -0
  100. omnibase_infra/services/registry_api/service.py +846 -0
  101. omnibase_infra/services/service_capability_query.py +4 -4
  102. omnibase_infra/services/service_health.py +3 -2
  103. omnibase_infra/services/service_timeout_emitter.py +13 -2
  104. omnibase_infra/utils/util_dsn_validation.py +1 -1
  105. omnibase_infra/validation/__init__.py +3 -19
  106. omnibase_infra/validation/contracts/security.validation.yaml +114 -0
  107. omnibase_infra/validation/infra_validators.py +35 -24
  108. omnibase_infra/validation/validation_exemptions.yaml +113 -9
  109. omnibase_infra/validation/validator_chain_propagation.py +2 -2
  110. omnibase_infra/validation/validator_runtime_shape.py +1 -1
  111. omnibase_infra/validation/validator_security.py +473 -370
  112. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/METADATA +2 -2
  113. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/RECORD +116 -74
  114. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/WHEEL +0 -0
  115. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/entry_points.txt +0 -0
  116. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/licenses/LICENSE +0 -0
@@ -54,6 +54,7 @@ from __future__ import annotations
54
54
  import logging
55
55
  from typing import TYPE_CHECKING
56
56
 
57
+ from omnibase_core.enums import EnumInjectionScope
57
58
  from omnibase_core.models.primitives import ModelSemVer
58
59
  from omnibase_infra.enums import EnumInfraTransportType
59
60
  from omnibase_infra.errors import (
@@ -257,7 +258,7 @@ async def wire_infrastructure_services(
257
258
  await container.service_registry.register_instance(
258
259
  interface=RegistryPolicy,
259
260
  instance=policy_registry,
260
- scope="global",
261
+ scope=EnumInjectionScope.GLOBAL,
261
262
  metadata={
262
263
  "description": "ONEX policy plugin registry",
263
264
  "version": str(SEMVER_DEFAULT),
@@ -274,7 +275,7 @@ async def wire_infrastructure_services(
274
275
  await container.service_registry.register_instance(
275
276
  interface=RegistryProtocolBinding,
276
277
  instance=handler_registry,
277
- scope="global",
278
+ scope=EnumInjectionScope.GLOBAL,
278
279
  metadata={
279
280
  "description": "ONEX protocol handler binding registry",
280
281
  "version": str(SEMVER_DEFAULT),
@@ -291,7 +292,7 @@ async def wire_infrastructure_services(
291
292
  await container.service_registry.register_instance(
292
293
  interface=RegistryCompute,
293
294
  instance=compute_registry,
294
- scope="global",
295
+ scope=EnumInjectionScope.GLOBAL,
295
296
  metadata={
296
297
  "description": "ONEX compute plugin registry",
297
298
  "version": str(SEMVER_DEFAULT),
@@ -530,7 +531,7 @@ async def get_or_create_policy_registry(
530
531
  await container.service_registry.register_instance(
531
532
  interface=RegistryPolicy,
532
533
  instance=policy_registry,
533
- scope="global",
534
+ scope=EnumInjectionScope.GLOBAL,
534
535
  metadata={
535
536
  "description": "ONEX policy plugin registry (auto-registered)",
536
537
  "version": str(SEMVER_DEFAULT),
@@ -804,7 +805,7 @@ async def get_or_create_compute_registry(
804
805
  await container.service_registry.register_instance(
805
806
  interface=RegistryCompute,
806
807
  instance=compute_registry,
807
- scope="global",
808
+ scope=EnumInjectionScope.GLOBAL,
808
809
  metadata={
809
810
  "description": "ONEX compute plugin registry (auto-registered)",
810
811
  "version": str(SEMVER_DEFAULT),
@@ -122,12 +122,14 @@ from omnibase_infra.event_bus.event_bus_inmemory import EventBusInmemory
122
122
  from omnibase_infra.handlers.handler_consul import HandlerConsul
123
123
  from omnibase_infra.handlers.handler_db import HandlerDb
124
124
  from omnibase_infra.handlers.handler_http import HandlerHttpRest
125
+ from omnibase_infra.handlers.handler_mcp import HandlerMCP
125
126
  from omnibase_infra.handlers.handler_vault import HandlerVault
126
127
  from omnibase_infra.runtime.handler_registry import (
127
128
  EVENT_BUS_INMEMORY,
128
129
  HANDLER_TYPE_CONSUL,
129
130
  HANDLER_TYPE_DATABASE,
130
131
  HANDLER_TYPE_HTTP,
132
+ HANDLER_TYPE_MCP,
131
133
  HANDLER_TYPE_VAULT,
132
134
  RegistryEventBusBinding,
133
135
  RegistryProtocolBinding,
@@ -169,6 +171,7 @@ _KNOWN_HANDLERS: dict[str, tuple[type[ProtocolHandler], str]] = {
169
171
  HANDLER_TYPE_CONSUL: (HandlerConsul, "HashiCorp Consul service discovery handler"), # type: ignore[dict-item] # NOTE: structural subtyping
170
172
  HANDLER_TYPE_DATABASE: (HandlerDb, "PostgreSQL database handler"), # type: ignore[dict-item] # NOTE: structural subtyping
171
173
  HANDLER_TYPE_HTTP: (HandlerHttpRest, "HTTP REST protocol handler"), # type: ignore[dict-item] # NOTE: structural subtyping
174
+ HANDLER_TYPE_MCP: (HandlerMCP, "Model Context Protocol handler for AI agents"), # type: ignore[dict-item] # NOTE: structural subtyping
172
175
  HANDLER_TYPE_VAULT: (HandlerVault, "HashiCorp Vault secret management handler"), # type: ignore[dict-item] # NOTE: structural subtyping
173
176
  }
174
177
 
@@ -195,6 +198,7 @@ def wire_default_handlers() -> dict[str, list[str]]:
195
198
  - CONSUL: HandlerConsul for HashiCorp Consul service discovery
196
199
  - DB: HandlerDb for PostgreSQL database operations
197
200
  - HTTP: HandlerHttpRest for HTTP/REST protocol operations
201
+ - MCP: HandlerMCP for Model Context Protocol AI agent integration
198
202
  - VAULT: HandlerVault for HashiCorp Vault secret management
199
203
 
200
204
  Registered Event Buses:
@@ -217,7 +221,7 @@ def wire_default_handlers() -> dict[str, list[str]]:
217
221
  Example:
218
222
  >>> summary = wire_default_handlers()
219
223
  >>> print(summary)
220
- {'handlers': ['consul', 'db', 'http', 'vault'], 'event_buses': ['inmemory']}
224
+ {'handlers': ['consul', 'db', 'http', 'mcp', 'vault'], 'event_buses': ['inmemory']}
221
225
 
222
226
  Note:
223
227
  This function uses the singleton registries returned by
@@ -0,0 +1,245 @@
1
+ -- SPDX-License-Identifier: MIT
2
+ -- Copyright (c) 2025 OmniNode Team
3
+ --
4
+ -- Transition Notification Outbox Schema
5
+ -- Ticket: OMN-1139 (TransitionNotificationOutbox implementation)
6
+ -- Version: 1.0.0
7
+ --
8
+ -- Design Notes:
9
+ -- - Implements the outbox pattern for guaranteed notification delivery
10
+ -- - Stores notifications in same transaction as projections for atomicity
11
+ -- - Supports concurrent processing via SELECT FOR UPDATE SKIP LOCKED
12
+ -- - Includes retry tracking with error recording
13
+ -- - Indexes optimized for processor query patterns:
14
+ -- 1. Pending notification queries (processed_at IS NULL)
15
+ -- 2. Aggregate-specific queries (aggregate_type, aggregate_id)
16
+ -- 3. Retry processing (high retry counts for monitoring)
17
+ -- 4. Cleanup of old processed records
18
+ -- - All timestamps are timezone-aware (TIMESTAMPTZ)
19
+ -- - This schema is idempotent (IF NOT EXISTS used throughout)
20
+ --
21
+ -- Usage:
22
+ -- Execute this SQL file to create or update the outbox schema.
23
+ -- The schema is designed to be re-run safely (idempotent).
24
+ --
25
+ -- Related:
26
+ -- - TransitionNotificationOutbox: src/omnibase_infra/runtime/transition_notification_outbox.py
27
+ -- - ModelStateTransitionNotification: omnibase_core.models.notifications
28
+ -- - ProtocolTransitionNotificationPublisher: omnibase_core.protocols.notifications
29
+
30
+ -- =============================================================================
31
+ -- MAIN TABLE
32
+ -- =============================================================================
33
+
34
+ CREATE TABLE IF NOT EXISTS transition_notification_outbox (
35
+ -- Identity
36
+ id BIGSERIAL PRIMARY KEY,
37
+
38
+ -- Notification Payload (serialized ModelStateTransitionNotification)
39
+ notification_data JSONB NOT NULL,
40
+
41
+ -- Timestamps
42
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
43
+ -- processed_at encodes both "pending" and "processed" states in a single column:
44
+ -- NULL = pending (not yet processed, eligible for processing)
45
+ -- non-NULL = processed (successfully published, timestamp of completion)
46
+ -- Note: No separate "processed_implies_no_pending" constraint is needed because
47
+ -- the single-column semantic makes the states mutually exclusive by design -
48
+ -- a row cannot be both pending (NULL) and processed (non-NULL) simultaneously.
49
+ processed_at TIMESTAMPTZ,
50
+
51
+ -- Retry Tracking
52
+ retry_count INT NOT NULL DEFAULT 0,
53
+ last_error TEXT, -- Most recent error message (sanitized, max 1000 chars)
54
+
55
+ -- Aggregate Information (for queries and debugging)
56
+ aggregate_type TEXT NOT NULL,
57
+ aggregate_id UUID NOT NULL,
58
+
59
+ -- Constraints
60
+ CONSTRAINT valid_retry_count CHECK (retry_count >= 0)
61
+ );
62
+
63
+ -- =============================================================================
64
+ -- INDEXES FOR OUTBOX PROCESSING
65
+ -- =============================================================================
66
+
67
+ -- Index for efficient pending notification queries (PRIMARY query pattern)
68
+ -- The processor uses: SELECT ... WHERE processed_at IS NULL ORDER BY created_at
69
+ -- Partial index: only index rows with NULL processed_at (pending notifications)
70
+ -- Query pattern: SELECT id, notification_data FROM transition_notification_outbox
71
+ -- WHERE processed_at IS NULL ORDER BY created_at LIMIT :batch_size
72
+ -- FOR UPDATE SKIP LOCKED
73
+ CREATE INDEX IF NOT EXISTS idx_outbox_pending
74
+ ON transition_notification_outbox (created_at)
75
+ WHERE processed_at IS NULL;
76
+
77
+ -- Index for aggregate-specific queries (debugging and monitoring)
78
+ -- Enables queries like "show all notifications for entity X"
79
+ -- Query pattern: SELECT * FROM transition_notification_outbox
80
+ -- WHERE aggregate_type = :type AND aggregate_id = :id
81
+ CREATE INDEX IF NOT EXISTS idx_outbox_aggregate
82
+ ON transition_notification_outbox (aggregate_type, aggregate_id);
83
+
84
+ -- Index for retry monitoring (identify stuck/failing notifications)
85
+ -- Partial index: only pending notifications with retries
86
+ -- Query pattern: SELECT * FROM transition_notification_outbox
87
+ -- WHERE processed_at IS NULL AND retry_count > :threshold
88
+ CREATE INDEX IF NOT EXISTS idx_outbox_retry_pending
89
+ ON transition_notification_outbox (retry_count, created_at)
90
+ WHERE processed_at IS NULL AND retry_count > 0;
91
+
92
+ -- Index for cleanup queries (delete old processed records)
93
+ -- Enables efficient deletion of processed records older than a threshold
94
+ -- Query pattern: DELETE FROM transition_notification_outbox
95
+ -- WHERE processed_at IS NOT NULL AND processed_at < :cutoff_time
96
+ CREATE INDEX IF NOT EXISTS idx_outbox_cleanup
97
+ ON transition_notification_outbox (processed_at)
98
+ WHERE processed_at IS NOT NULL;
99
+
100
+ -- Index for aggregate type filtering with pending status
101
+ -- Useful for monitoring specific aggregate types
102
+ -- Query pattern: SELECT COUNT(*) FROM transition_notification_outbox
103
+ -- WHERE aggregate_type = :type AND processed_at IS NULL
104
+ CREATE INDEX IF NOT EXISTS idx_outbox_aggregate_type_pending
105
+ ON transition_notification_outbox (aggregate_type)
106
+ WHERE processed_at IS NULL;
107
+
108
+ -- =============================================================================
109
+ -- TABLE AND COLUMN COMMENTS
110
+ -- =============================================================================
111
+
112
+ COMMENT ON TABLE transition_notification_outbox IS
113
+ 'Outbox pattern table for guaranteed state transition notification delivery (OMN-1139). '
114
+ 'Stores notifications in same transaction as projections for atomicity. '
115
+ 'Background processor publishes pending notifications asynchronously.';
116
+
117
+ COMMENT ON COLUMN transition_notification_outbox.id IS
118
+ 'Auto-incrementing primary key. Used for efficient row locking with FOR UPDATE SKIP LOCKED.';
119
+
120
+ COMMENT ON COLUMN transition_notification_outbox.notification_data IS
121
+ 'Serialized ModelStateTransitionNotification as JSONB. Contains all notification fields: '
122
+ 'aggregate_type, aggregate_id, transition (from_state, to_state, event_type), '
123
+ 'correlation_id, timestamp, and optional metadata.';
124
+
125
+ COMMENT ON COLUMN transition_notification_outbox.created_at IS
126
+ 'Timestamp when notification was stored in outbox. Used for FIFO ordering of pending notifications.';
127
+
128
+ COMMENT ON COLUMN transition_notification_outbox.processed_at IS
129
+ 'Timestamp when notification was successfully published. NULL indicates pending status.';
130
+
131
+ COMMENT ON COLUMN transition_notification_outbox.retry_count IS
132
+ 'Number of failed publish attempts. Incremented on each failure, not reset on success. '
133
+ 'Use for monitoring and alerting on stuck notifications.';
134
+
135
+ COMMENT ON COLUMN transition_notification_outbox.last_error IS
136
+ 'Most recent error message from failed publish attempt (sanitized, max 1000 chars). '
137
+ 'Updated on each failure for debugging. May contain value even when processed_at is set '
138
+ 'if earlier retries failed before final success.';
139
+
140
+ COMMENT ON COLUMN transition_notification_outbox.aggregate_type IS
141
+ 'Type of aggregate this notification is for (e.g., "registration", "workflow"). '
142
+ 'Denormalized from notification_data for efficient indexing and filtering.';
143
+
144
+ COMMENT ON COLUMN transition_notification_outbox.aggregate_id IS
145
+ 'UUID of the aggregate entity this notification is for. '
146
+ 'Denormalized from notification_data for efficient indexing and aggregate-specific queries.';
147
+
148
+ -- =============================================================================
149
+ -- INDEX STRATEGY DOCUMENTATION
150
+ -- =============================================================================
151
+ --
152
+ -- This schema defines multiple indexes optimized for different query patterns
153
+ -- used by the TransitionNotificationOutbox processor and operators.
154
+ --
155
+ -- PENDING NOTIFICATION PROCESSING:
156
+ -- --------------------------------
157
+ -- 1. idx_outbox_pending:
158
+ -- - Single-column index on created_at
159
+ -- - WHERE: processed_at IS NULL
160
+ -- - Use case: Main processor query - fetch pending notifications in FIFO order
161
+ -- - Critical for: process_pending() batch retrieval
162
+ --
163
+ -- The partial WHERE clause keeps this index small and fast by excluding
164
+ -- all processed records.
165
+ --
166
+ -- AGGREGATE QUERIES (Debugging):
167
+ -- ------------------------------
168
+ -- 2. idx_outbox_aggregate:
169
+ -- - Composite index on (aggregate_type, aggregate_id)
170
+ -- - Use case: Query notifications for a specific entity
171
+ -- - Example: "Show all notifications for registration entity X"
172
+ --
173
+ -- RETRY MONITORING:
174
+ -- -----------------
175
+ -- 3. idx_outbox_retry_pending:
176
+ -- - Composite index on (retry_count, created_at)
177
+ -- - WHERE: processed_at IS NULL AND retry_count > 0
178
+ -- - Use case: Identify stuck or failing notifications
179
+ -- - Example: Alert when retry_count > 5 for pending notifications
180
+ --
181
+ -- CLEANUP OPERATIONS:
182
+ -- -------------------
183
+ -- 4. idx_outbox_cleanup:
184
+ -- - Single-column index on processed_at
185
+ -- - WHERE: processed_at IS NOT NULL
186
+ -- - Use case: Efficient deletion of old processed records
187
+ -- - Example: DELETE ... WHERE processed_at < NOW() - INTERVAL '7 days'
188
+ --
189
+ -- TYPE-SPECIFIC MONITORING:
190
+ -- -------------------------
191
+ -- 5. idx_outbox_aggregate_type_pending:
192
+ -- - Single-column index on aggregate_type
193
+ -- - WHERE: processed_at IS NULL
194
+ -- - Use case: Count pending notifications by type for monitoring dashboards
195
+ --
196
+ -- =============================================================================
197
+ -- CONCURRENT PROCESSING PATTERN
198
+ -- =============================================================================
199
+ --
200
+ -- The outbox processor uses SELECT FOR UPDATE SKIP LOCKED to enable safe
201
+ -- concurrent processing by multiple instances:
202
+ --
203
+ -- SELECT id, notification_data
204
+ -- FROM transition_notification_outbox
205
+ -- WHERE processed_at IS NULL
206
+ -- ORDER BY created_at
207
+ -- LIMIT :batch_size
208
+ -- FOR UPDATE SKIP LOCKED;
209
+ --
210
+ -- SKIP LOCKED ensures that if another processor has locked a row, this query
211
+ -- skips it rather than blocking. This prevents:
212
+ -- - Duplicate processing
213
+ -- - Deadlocks between processors
214
+ -- - Head-of-line blocking
215
+ --
216
+ -- The id column (BIGSERIAL PRIMARY KEY) provides an efficient row lock target.
217
+ --
218
+ -- =============================================================================
219
+ -- CLEANUP RECOMMENDATIONS
220
+ -- =============================================================================
221
+ --
222
+ -- Processed records should be periodically deleted to prevent table bloat.
223
+ -- Recommended cleanup query (run periodically via cron or pg_cron):
224
+ --
225
+ -- DELETE FROM transition_notification_outbox
226
+ -- WHERE processed_at IS NOT NULL
227
+ -- AND processed_at < NOW() - INTERVAL '7 days';
228
+ --
229
+ -- Alternatively, implement as a PostgreSQL function:
230
+ --
231
+ -- CREATE OR REPLACE FUNCTION cleanup_outbox(retention_interval INTERVAL)
232
+ -- RETURNS BIGINT AS $$
233
+ -- DECLARE
234
+ -- deleted_count BIGINT;
235
+ -- BEGIN
236
+ -- DELETE FROM transition_notification_outbox
237
+ -- WHERE processed_at IS NOT NULL
238
+ -- AND processed_at < NOW() - retention_interval;
239
+ -- GET DIAGNOSTICS deleted_count = ROW_COUNT;
240
+ -- RETURN deleted_count;
241
+ -- END;
242
+ -- $$ LANGUAGE plpgsql;
243
+ --
244
+ -- Usage: SELECT cleanup_outbox(INTERVAL '7 days');
245
+ --
@@ -0,0 +1,31 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """MCP services for Model Context Protocol integration.
4
+
5
+ This package provides services for exposing ONEX orchestrator nodes
6
+ as MCP tools for AI agent integration.
7
+
8
+ Services:
9
+ ServiceMCPToolRegistry: Event-loop safe in-memory cache of tool definitions
10
+ ServiceMCPToolDiscovery: Consul scanner for MCP-enabled orchestrators
11
+ ServiceMCPToolSync: Kafka listener for hot reload with idempotency
12
+ MCPServerLifecycle: Server lifecycle management
13
+ """
14
+
15
+ from omnibase_infra.services.mcp.mcp_server_lifecycle import (
16
+ MCPServerLifecycle,
17
+ ModelMCPServerConfig,
18
+ )
19
+ from omnibase_infra.services.mcp.service_mcp_tool_discovery import (
20
+ ServiceMCPToolDiscovery,
21
+ )
22
+ from omnibase_infra.services.mcp.service_mcp_tool_registry import ServiceMCPToolRegistry
23
+ from omnibase_infra.services.mcp.service_mcp_tool_sync import ServiceMCPToolSync
24
+
25
+ __all__ = [
26
+ "MCPServerLifecycle",
27
+ "ModelMCPServerConfig",
28
+ "ServiceMCPToolDiscovery",
29
+ "ServiceMCPToolRegistry",
30
+ "ServiceMCPToolSync",
31
+ ]