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
@@ -0,0 +1,162 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Bootstrap Handler Descriptor Model with Required handler_class.
4
+
5
+ This module provides ModelBootstrapHandlerDescriptor, a specialized handler
6
+ descriptor for bootstrap handlers that REQUIRES the handler_class field to be set.
7
+
8
+ Bootstrap handlers are hardcoded handlers that must always specify their
9
+ implementation class for dynamic import. Unlike contract-discovered handlers
10
+ where handler_class may be optional (inferred from convention), bootstrap
11
+ handlers have no contract file to derive the class from.
12
+
13
+ Part of OMN-1087: Implement HandlerBootstrapSource descriptor-based validation.
14
+
15
+ See Also:
16
+ - ModelHandlerDescriptor: Base descriptor with optional handler_class
17
+ - HandlerBootstrapSource: Source that uses this specialized descriptor
18
+ - BootstrapEffectDefinition: TypedDict for bootstrap handler definitions
19
+
20
+ .. versionadded:: 0.6.4
21
+ Created as part of OMN-1087 bootstrap handler validation.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from pydantic import ConfigDict, Field
27
+
28
+ from omnibase_infra.models.handlers.model_handler_descriptor import (
29
+ ModelHandlerDescriptor,
30
+ )
31
+
32
+
33
+ class ModelBootstrapHandlerDescriptor(ModelHandlerDescriptor):
34
+ """Handler descriptor for bootstrap handlers with required handler_class.
35
+
36
+ This specialized descriptor extends ModelHandlerDescriptor to enforce that
37
+ handler_class is always set. Bootstrap handlers are hardcoded and must
38
+ specify their implementation class since there is no contract file to
39
+ derive the class from.
40
+
41
+ The key difference from ModelHandlerDescriptor:
42
+ - handler_class: Required (str) instead of optional (str | None)
43
+
44
+ All other fields maintain the same constraints as the parent class.
45
+
46
+ Attributes:
47
+ handler_id: Unique identifier for the handler (e.g., "bootstrap.consul").
48
+ name: Human-readable name for the handler.
49
+ version: Semantic version (ModelSemVer). Accepts string, dict, or ModelSemVer.
50
+ handler_kind: Handler kind (compute, effect, reducer, orchestrator).
51
+ input_model: Fully qualified input model class path.
52
+ output_model: Fully qualified output model class path.
53
+ description: Optional description of the handler.
54
+ handler_class: REQUIRED fully qualified Python class path for dynamic import.
55
+ contract_path: Path to the source contract file (typically None for bootstrap).
56
+
57
+ Example:
58
+ Create a bootstrap handler descriptor:
59
+
60
+ >>> descriptor = ModelBootstrapHandlerDescriptor(
61
+ ... handler_id="bootstrap.consul",
62
+ ... name="Consul Handler",
63
+ ... version="1.0.0",
64
+ ... handler_kind="effect",
65
+ ... input_model="omnibase_infra.models.types.JsonDict",
66
+ ... output_model="omnibase_core.models.dispatch.ModelHandlerOutput",
67
+ ... handler_class="omnibase_infra.handlers.handler_consul.HandlerConsul",
68
+ ... )
69
+ >>> descriptor.handler_class
70
+ 'omnibase_infra.handlers.handler_consul.HandlerConsul'
71
+
72
+ Missing handler_class raises ValidationError:
73
+
74
+ >>> from pydantic import ValidationError
75
+ >>> try:
76
+ ... ModelBootstrapHandlerDescriptor(
77
+ ... handler_id="bootstrap.consul",
78
+ ... name="Consul Handler",
79
+ ... version="1.0.0",
80
+ ... handler_kind="effect",
81
+ ... input_model="omnibase_infra.models.types.JsonDict",
82
+ ... output_model="omnibase_core.models.dispatch.ModelHandlerOutput",
83
+ ... # handler_class omitted - will fail
84
+ ... )
85
+ ... except ValidationError as e:
86
+ ... print("Validation failed as expected")
87
+ Validation failed as expected
88
+
89
+ Raises:
90
+ ValidationError: If handler_class is not provided or is None.
91
+
92
+ .. versionadded:: 0.6.4
93
+ Created as part of OMN-1087 bootstrap handler validation.
94
+ """
95
+
96
+ model_config = ConfigDict(
97
+ frozen=True,
98
+ extra="forbid",
99
+ strict=True,
100
+ )
101
+
102
+ # Override handler_class to be required (no default, not optional)
103
+ # The Field() definition must include the pattern constraint from parent
104
+ handler_class: str = Field(
105
+ ...,
106
+ min_length=3,
107
+ pattern=r"^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)+$",
108
+ description=(
109
+ "REQUIRED: Fully qualified Python class path for dynamic handler import. "
110
+ "Bootstrap handlers must always specify this field since they have no "
111
+ "contract file to derive the class from. "
112
+ "Example: 'omnibase_infra.handlers.handler_consul.HandlerConsul'"
113
+ ),
114
+ )
115
+
116
+ def to_base_descriptor(self) -> ModelHandlerDescriptor:
117
+ """Convert to base ModelHandlerDescriptor for API compatibility.
118
+
119
+ This method allows bootstrap descriptors to be used where the base
120
+ ModelHandlerDescriptor type is expected, while maintaining the
121
+ validation benefits of the bootstrap-specific model.
122
+
123
+ Implementation Notes:
124
+ Uses ``model_dump()`` without ``exclude_unset=True`` because:
125
+
126
+ 1. **Field parity**: This child class has NO extra fields beyond the
127
+ parent. The only difference is ``handler_class`` type constraint
128
+ (required ``str`` vs optional ``str | None``).
129
+
130
+ 2. **Type compatibility**: A ``str`` value from child is valid where
131
+ parent expects ``str | None``.
132
+
133
+ 3. **Complete copy**: ``model_dump()`` ensures all fields are copied,
134
+ including those set to their default values.
135
+
136
+ Using ``exclude_unset=True`` would risk excluding fields that have
137
+ defaults but were explicitly set to those defaults during construction.
138
+
139
+ If future versions add child-specific fields not in parent, this
140
+ method MUST be updated to use ``exclude={'new_field'}`` or refactored.
141
+
142
+ Returns:
143
+ ModelHandlerDescriptor instance with all fields copied.
144
+
145
+ Example:
146
+ >>> bootstrap_desc = ModelBootstrapHandlerDescriptor(
147
+ ... handler_id="bootstrap.consul",
148
+ ... name="Consul Handler",
149
+ ... version="1.0.0",
150
+ ... handler_kind="effect",
151
+ ... input_model="omnibase_infra.models.types.JsonDict",
152
+ ... output_model="omnibase_core.models.dispatch.ModelHandlerOutput",
153
+ ... handler_class="omnibase_infra.handlers.handler_consul.HandlerConsul",
154
+ ... )
155
+ >>> base_desc = bootstrap_desc.to_base_descriptor()
156
+ >>> isinstance(base_desc, ModelHandlerDescriptor)
157
+ True
158
+ """
159
+ return ModelHandlerDescriptor(**self.model_dump())
160
+
161
+
162
+ __all__ = ["ModelBootstrapHandlerDescriptor"]
@@ -99,6 +99,7 @@ class ModelHandlerDescriptor(BaseModel):
99
99
  input_model: Fully qualified input model class path.
100
100
  output_model: Fully qualified output model class path.
101
101
  description: Optional description of the handler.
102
+ handler_class: Fully qualified Python class path for dynamic handler import.
102
103
  contract_path: Path to the source contract file.
103
104
 
104
105
  Example:
@@ -176,10 +177,24 @@ class ModelHandlerDescriptor(BaseModel):
176
177
  default=None,
177
178
  description="Optional description of the handler",
178
179
  )
180
+ handler_class: str | None = Field(
181
+ default=None,
182
+ min_length=3,
183
+ pattern=r"^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)+$",
184
+ description="Fully qualified Python class path for dynamic handler import (e.g., 'omnibase_infra.handlers.handler_consul.HandlerConsul')",
185
+ )
179
186
  contract_path: str | None = Field(
180
187
  default=None,
181
188
  description="Path to the source contract file",
182
189
  )
190
+ contract_config: dict[str, JsonType] | None = Field(
191
+ default=None,
192
+ description=(
193
+ "Parsed configuration from the handler contract file. "
194
+ "Contains extracted values like security settings, tags, and handler metadata. "
195
+ "Populated during handler discovery when contract_path is set."
196
+ ),
197
+ )
183
198
 
184
199
 
185
200
  __all__ = ["LiteralHandlerKind", "ModelHandlerDescriptor"]
@@ -0,0 +1,15 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """MCP models for Model Context Protocol integration."""
4
+
5
+ from omnibase_infra.models.mcp.model_mcp_contract_config import ModelMCPContractConfig
6
+ from omnibase_infra.models.mcp.model_mcp_server_config import ModelMCPServerConfig
7
+ from omnibase_infra.models.mcp.model_mcp_tool_definition import ModelMCPToolDefinition
8
+ from omnibase_infra.models.mcp.model_mcp_tool_parameter import ModelMCPToolParameter
9
+
10
+ __all__ = [
11
+ "ModelMCPContractConfig",
12
+ "ModelMCPServerConfig",
13
+ "ModelMCPToolDefinition",
14
+ "ModelMCPToolParameter",
15
+ ]
@@ -0,0 +1,80 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """MCP contract configuration model for enabling nodes as MCP tools.
4
+
5
+ This model defines the `mcp` field that can be added to ONEX contracts to expose
6
+ orchestrator nodes as MCP tools for AI agent integration.
7
+
8
+ Example contract.yaml:
9
+ node_type: ORCHESTRATOR_GENERIC
10
+ mcp:
11
+ expose: true
12
+ tool_name: "workflow_execute"
13
+ description: "Execute a workflow"
14
+ timeout_seconds: 30
15
+
16
+ Enforcement Rule:
17
+ The `mcp.expose` field is ONLY valid for ORCHESTRATOR_GENERIC nodes.
18
+ Non-orchestrator nodes with `mcp.expose: true` will be ignored during
19
+ registration - the MCP tags will not be added to Consul.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from pydantic import BaseModel, Field
25
+
26
+
27
+ class ModelMCPContractConfig(BaseModel):
28
+ """MCP configuration for exposing a node as an MCP tool.
29
+
30
+ This configuration is embedded in a node's contract.yaml to enable
31
+ AI agents to discover and invoke the node as an MCP tool.
32
+
33
+ Attributes:
34
+ expose: Whether to expose this node as an MCP tool. When True and
35
+ the node is an orchestrator, it will be registered with Consul
36
+ using MCP-specific tags for tool discovery.
37
+ tool_name: Optional stable name for the MCP tool. If not provided,
38
+ defaults to the node's name. Use this to provide a consistent
39
+ tool name across node version changes.
40
+ description: Optional AI-friendly description of what this tool does.
41
+ If not provided, the node's description from the contract is used.
42
+ timeout_seconds: Optional execution timeout in seconds. Defaults to 30.
43
+ This is enforced when AI agents invoke the tool.
44
+
45
+ Example:
46
+ >>> config = ModelMCPContractConfig(
47
+ ... expose=True,
48
+ ... tool_name="my_workflow",
49
+ ... description="Execute my custom workflow",
50
+ ... timeout_seconds=60,
51
+ ... )
52
+ >>> config.expose
53
+ True
54
+ """
55
+
56
+ expose: bool = Field(
57
+ default=False,
58
+ description="Whether to expose this node as an MCP tool. "
59
+ "Only valid for ORCHESTRATOR_GENERIC nodes.",
60
+ )
61
+ tool_name: str | None = Field(
62
+ default=None,
63
+ description="Optional stable name for the MCP tool. "
64
+ "Defaults to the node's name if not specified.",
65
+ )
66
+ description: str | None = Field(
67
+ default=None,
68
+ description="Optional AI-friendly description of what this tool does. "
69
+ "If not provided, the node's description from the contract is used.",
70
+ )
71
+ timeout_seconds: int = Field(
72
+ default=30,
73
+ ge=1,
74
+ le=300,
75
+ description="Execution timeout in seconds for tool invocations. "
76
+ "Minimum: 1, Maximum: 300 (5 minutes).",
77
+ )
78
+
79
+
80
+ __all__ = ["ModelMCPContractConfig"]
@@ -0,0 +1,67 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """MCP server configuration model.
4
+
5
+ This model defines the configuration for the MCP server lifecycle,
6
+ including Consul discovery, Kafka hot reload, and HTTP server settings.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pydantic import BaseModel, Field
12
+
13
+
14
+ class ModelMCPServerConfig(BaseModel):
15
+ """Configuration for the MCP server lifecycle.
16
+
17
+ This model captures all configuration needed for the MCP server:
18
+ - Consul connection settings for service discovery
19
+ - Kafka settings for hot reload
20
+ - HTTP server binding
21
+ - Execution defaults
22
+
23
+ Attributes:
24
+ consul_host: Consul server hostname for service discovery.
25
+ consul_port: Consul server port.
26
+ consul_scheme: HTTP scheme for Consul (http/https).
27
+ consul_token: Optional ACL token for Consul authentication.
28
+ kafka_enabled: Whether to enable Kafka for hot reload.
29
+ http_host: Host to bind the MCP HTTP server.
30
+ http_port: Port for the MCP HTTP server.
31
+ default_timeout: Default execution timeout for tools.
32
+ dev_mode: Whether to run in development mode (local contracts).
33
+ contracts_dir: Directory for contract scanning in dev mode.
34
+ """
35
+
36
+ consul_host: str = Field(default="localhost", description="Consul server hostname")
37
+ consul_port: int = Field(
38
+ default=8500, ge=1, le=65535, description="Consul server port"
39
+ )
40
+ consul_scheme: str = Field(
41
+ default="http", pattern="^https?$", description="HTTP scheme for Consul"
42
+ )
43
+ consul_token: str | None = Field(
44
+ default=None, description="Optional ACL token for Consul authentication"
45
+ )
46
+ kafka_enabled: bool = Field(
47
+ default=True, description="Whether to enable Kafka for hot reload"
48
+ )
49
+ http_host: str = Field(
50
+ default="0.0.0.0", # noqa: S104 - Intentional bind-all for server
51
+ description="Host to bind the MCP HTTP server",
52
+ )
53
+ http_port: int = Field(
54
+ default=8090, ge=1, le=65535, description="Port for the MCP HTTP server"
55
+ )
56
+ default_timeout: float = Field(
57
+ default=30.0, gt=0, le=300, description="Default execution timeout for tools"
58
+ )
59
+ dev_mode: bool = Field(
60
+ default=False, description="Whether to run in development mode"
61
+ )
62
+ contracts_dir: str | None = Field(
63
+ default=None, description="Directory for contract scanning in dev mode"
64
+ )
65
+
66
+
67
+ __all__ = ["ModelMCPServerConfig"]
@@ -0,0 +1,73 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """MCP tool definition model for representing ONEX nodes as MCP tools.
4
+
5
+ This model is used by the MCP adapter layer to:
6
+ 1. Cache discovered tools in the registry
7
+ 2. Generate MCP tool schemas for AI agents
8
+ 3. Route tool invocations to ONEX orchestrators
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from pydantic import BaseModel, Field
14
+
15
+ from omnibase_infra.models.mcp.model_mcp_tool_parameter import ModelMCPToolParameter
16
+
17
+
18
+ class ModelMCPToolDefinition(BaseModel):
19
+ """Complete MCP tool definition derived from an ONEX orchestrator node.
20
+
21
+ This model captures all information needed to:
22
+ - Expose the tool to AI agents via MCP protocol
23
+ - Route invocations to the correct ONEX orchestrator
24
+ - Enforce execution constraints (timeout, etc.)
25
+
26
+ Attributes:
27
+ name: Stable tool name for AI agent invocation. This is derived from
28
+ contract.mcp.tool_name or falls back to the node name.
29
+ description: AI-friendly description of tool functionality.
30
+ version: Tool version from the node contract.
31
+ parameters: List of input parameters with type information.
32
+ input_schema: JSON Schema for input validation.
33
+ orchestrator_node_id: UUID of the ONEX orchestrator node.
34
+ orchestrator_service_id: Consul service ID for routing.
35
+ endpoint: HTTP endpoint for direct invocation (if available).
36
+ timeout_seconds: Execution timeout for tool invocations.
37
+ metadata: Additional metadata for routing and observability.
38
+ """
39
+
40
+ name: str = Field(description="Stable tool name for MCP invocation")
41
+ description: str = Field(description="AI-friendly tool description")
42
+ version: str = Field(default="1.0.0", description="Tool version")
43
+ parameters: list[ModelMCPToolParameter] = Field(
44
+ default_factory=list, description="Input parameters"
45
+ )
46
+ input_schema: dict[str, object] = Field(
47
+ default_factory=lambda: dict[str, object]({"type": "object", "properties": {}}),
48
+ description="JSON Schema for input validation",
49
+ )
50
+ orchestrator_node_id: str | None = Field(
51
+ default=None, description="UUID of the source orchestrator node"
52
+ )
53
+ orchestrator_service_id: str | None = Field(
54
+ default=None, description="Consul service ID for routing"
55
+ )
56
+ endpoint: str | None = Field(
57
+ default=None, description="HTTP endpoint for direct invocation"
58
+ )
59
+ timeout_seconds: int = Field(
60
+ default=30, ge=1, le=300, description="Execution timeout in seconds"
61
+ )
62
+ metadata: dict[str, object] = Field(
63
+ default_factory=dict,
64
+ description="Additional metadata (tags, input_model module, etc.)",
65
+ )
66
+
67
+ @property
68
+ def tool_type(self) -> str:
69
+ """Return the MCP tool type. Always 'function' for ONEX nodes."""
70
+ return "function"
71
+
72
+
73
+ __all__ = ["ModelMCPToolDefinition"]
@@ -0,0 +1,35 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """MCP tool parameter model for representing tool input parameters."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class ModelMCPToolParameter(BaseModel):
11
+ """Parameter definition for an MCP tool.
12
+
13
+ Represents a single parameter that can be passed to an MCP tool,
14
+ including its type, validation constraints, and documentation.
15
+ """
16
+
17
+ name: str = Field(description="Parameter name")
18
+ parameter_type: str = Field(
19
+ default="string",
20
+ description="JSON Schema type: string, number, boolean, array, object",
21
+ )
22
+ description: str = Field(default="", description="Human-readable description")
23
+ required: bool = Field(
24
+ default=True, description="Whether this parameter is required"
25
+ )
26
+ default_value: object | None = Field(
27
+ default=None, description="Default value if not provided"
28
+ )
29
+ json_schema: dict[str, object] | None = Field(
30
+ default=None,
31
+ description="Additional JSON Schema constraints (enum, format, etc.)",
32
+ )
33
+
34
+
35
+ __all__ = ["ModelMCPToolParameter"]
@@ -12,9 +12,12 @@ Note:
12
12
  would not correctly handle this distinction.
13
13
  """
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  from pydantic import BaseModel, ConfigDict, Field
16
18
 
17
19
  from omnibase_core.types import JsonType
20
+ from omnibase_infra.models.mcp.model_mcp_contract_config import ModelMCPContractConfig
18
21
 
19
22
 
20
23
  class ModelNodeCapabilities(BaseModel):
@@ -94,6 +97,14 @@ class ModelNodeCapabilities(BaseModel):
94
97
  description="Nested configuration (JSON-serializable values)",
95
98
  )
96
99
 
100
+ # MCP configuration for exposing node as AI agent tool
101
+ # Only valid for ORCHESTRATOR nodes - ignored for other node types
102
+ mcp: ModelMCPContractConfig | None = Field(
103
+ default=None,
104
+ description="MCP configuration for exposing node as AI agent tool. "
105
+ "Only valid for ORCHESTRATOR_GENERIC nodes.",
106
+ )
107
+
97
108
  def __getitem__(self, key: str) -> object:
98
109
  """Enable dict-like access to capabilities.
99
110
 
@@ -11,11 +11,6 @@
11
11
  name: "architecture_validator"
12
12
  contract_name: "architecture_validator"
13
13
  node_name: "architecture_validator"
14
- # Version (semantic versioning)
15
- version:
16
- major: 1
17
- minor: 0
18
- patch: 0
19
14
  contract_version:
20
15
  major: 1
21
16
  minor: 0
@@ -39,7 +39,7 @@ class RegistryInfraArchitectureValidator:
39
39
 
40
40
  This registry provides a static method to register the NodeArchitectureValidator
41
41
  with the ONEX dependency injection container. The validator is registered as
42
- a factory, allowing lazy instantiation when first resolved.
42
+ an instance after being created with the container reference.
43
43
 
44
44
  Thread Safety:
45
45
  Registration is typically done at startup before the container is frozen.
@@ -53,17 +53,17 @@ class RegistryInfraArchitectureValidator:
53
53
  )
54
54
 
55
55
  container = ModelONEXContainer()
56
- RegistryInfraArchitectureValidator.register(container)
56
+ await RegistryInfraArchitectureValidator.register(container)
57
57
  ```
58
58
  """
59
59
 
60
60
  @staticmethod
61
- def register(container: ModelONEXContainer) -> None:
61
+ async def register(container: ModelONEXContainer) -> None:
62
62
  """Register architecture validator with container.
63
63
 
64
64
  Registers the NodeArchitectureValidator as a service in the ONEX
65
- dependency injection container. The validator is registered with
66
- a factory function that creates a new instance on demand.
65
+ dependency injection container. The validator is created immediately
66
+ and registered as a singleton instance.
67
67
 
68
68
  Args:
69
69
  container: The DI container to register with.
@@ -77,22 +77,29 @@ class RegistryInfraArchitectureValidator:
77
77
  Example:
78
78
  ```python
79
79
  container = ModelONEXContainer()
80
- RegistryInfraArchitectureValidator.register(container)
80
+ await RegistryInfraArchitectureValidator.register(container)
81
81
 
82
82
  # Resolve when needed
83
- validator = container.get_service(NodeArchitectureValidator)
83
+ validator = await container.service_registry.resolve_service(
84
+ NodeArchitectureValidator
85
+ )
84
86
  result = await validator.compute(request)
85
87
  ```
86
88
  """
89
+ from omnibase_core.enums import EnumInjectionScope
90
+
87
91
  # Check if service_registry is available
88
92
  if container.service_registry is None:
89
93
  # Container doesn't have full DI support - skip registration
90
94
  # This allows the code to work with minimal container configurations
91
95
  return
92
96
 
93
- container.service_registry.register(
94
- NodeArchitectureValidator,
95
- lambda c: NodeArchitectureValidator(c),
97
+ # Create instance and register (factory registration not supported in v1.0)
98
+ validator_instance = NodeArchitectureValidator(container)
99
+ await container.service_registry.register_instance(
100
+ interface=NodeArchitectureValidator,
101
+ instance=validator_instance,
102
+ scope=EnumInjectionScope.GLOBAL,
96
103
  )
97
104
 
98
105
 
@@ -10,11 +10,6 @@
10
10
  name: "registry_effect"
11
11
  contract_name: "registry_effect"
12
12
  node_name: "registry_effect"
13
- # Version (semantic versioning)
14
- version:
15
- major: 1
16
- minor: 0
17
- patch: 0
18
13
  contract_version:
19
14
  major: 1
20
15
  minor: 0
@@ -36,6 +36,13 @@ node_version: "1.0.0"
36
36
  name: "node_registration_orchestrator"
37
37
  node_type: "ORCHESTRATOR_GENERIC"
38
38
  description: "Registration workflow orchestrator that coordinates node lifecycle by calling reducer for intents and effect for execution."
39
+ # MCP (Model Context Protocol) Configuration
40
+ # Exposes this orchestrator as an AI-invocable tool via MCP server
41
+ mcp:
42
+ expose: true
43
+ tool_name: "register_node"
44
+ description: "Register a new ONEX node with the cluster. Handles Consul service registration and PostgreSQL metadata storage. Returns registration status and assigned service ID."
45
+ timeout_seconds: 30
39
46
  input_model:
40
47
  name: "ModelOrchestratorInput"
41
48
  module: "omnibase_infra.nodes.node_registration_orchestrator.models"