Repository navigation
feat(OMN-12841): surface generated COMPUTE nodes as MCP tools via an orchestrator wrapper surface - #2113
Conversation
…orchestrator wrapper surface Contract-native Option B: emit, alongside a generated COMPUTE node, a thin declarative ORCHESTRATOR wrapper (CONTRACT + handler_routing only) that is the MCP-exposed entity. The wrapper declares mcp.expose=true + a tool_name and routes the inbound MCP invocation envelope to the generated COMPUTE handler. It satisfies the existing orchestrator-only MCP gate WITHOUT relaxing it. - ServiceMCPWrapperContractEmitter (pure, deterministic): given a ModelGeneratedComputeNodeSpec, emits ModelMCPWrapperEmission carrying BOTH the generated COMPUTE contract (verbatim) and the ORCHESTRATOR wrapper contract. - The two orchestrator-only gates (service_mcp_tool_sync.py / service_mcp_tool_discovery.py:197-212) are NOT modified. Option A (relaxing the gate for node-type:compute) was rejected by the ticket as a "flip the literal" smell that forks the MCP gate logic permanently. - Topic suffixes for the per-tool invoke/result are composed + validated by the canonical topics module (build_generated_tool_invoke_suffix / build_generated_tool_result_suffix) so no hardcoded topic literals live in the emitter (no-hardcoded-topics gate preserved, no allowlist). TDD: discovery-layer test (wrapper-shaped orchestrator projection converts to a tool; bare compute projection returns None) + generation-layer test (emitter emits BOTH compute + orchestrator wrapper contracts) written first. Also vendors the omnimarket node_projection_event_chain migration (OMN-13620, merged to omnimarket dev after the last infra PR) into the infra forward tree via scripts/sync-node-migrations.sh, required to keep the node-migration-sync CI gate green for this and all subsequent infra PRs. Evidence-Source: 725d2967b031d7065efd41d0751f903638202cc1 Evidence-Ticket: OMN-12841
|
Warning Review limit reached
More reviews will be available in 50 minutes and 35 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (13)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
OMN-12841 — Surface generated COMPUTE nodes as MCP tools via an orchestrator wrapper surface
Implements Option B (contract-native) from the ticket: a generated COMPUTE node never surfaces as an MCP tool because MCP exposure is gated orchestrator-only in two source-verified places (
services/mcp/service_mcp_tool_discovery.py:197-212discovery filter;services/mcp/service_mcp_tool_sync.pyexposure rule). Rather than relaxing the gate (Option A — rejected by the ticket as a "flip the literal" smell that forks the MCP gate logic), this emits, alongside the generated COMPUTE node, a thin declarative ORCHESTRATOR wrapper (CONTRACT +handler_routingonly, no custom node logic) whose contract declaresmcp.expose=true+ atool_nameand routes the inbound MCP invocation to the generated COMPUTE handler. The wrapper is an ORCHESTRATOR, so it satisfies the existing gate without modifying it.What landed
ServiceMCPWrapperContractEmitter(pure, deterministic, no I/O): given aModelGeneratedComputeNodeSpec, returns aModelMCPWrapperEmissioncarrying both the generated COMPUTE contract (verbatim) and the ORCHESTRATOR wrapper contract YAML.ModelGeneratedComputeNodeSpec/ModelMCPWrapperEmission— strongly-typed, frozen,extra="forbid", fail-fast on blanks.topics/platform_topic_suffixes.py(build_generated_tool_invoke_suffix/build_generated_tool_result_suffix), validated viavalidate_topic_suffix, so no hardcoded topic literals live in the emitter (no-hardcoded-topics gate preserved; no allowlist).contracts/OMN-12841.yaml.Gates explicitly NOT modified (DoD #3)
service_mcp_tool_sync.pyexposure rule — unchanged.service_mcp_tool_discovery.py:197-212orchestrator-only filter — unchanged.TDD (failing-first)
tests/unit/services/mcp/test_service_mcp_wrapper_contract_emitter.py— written failing first (ModuleNotFoundError), drives the emitter; asserts the pipeline emits BOTH a*_computecontract AND a*_orchestratorwrapper contract (mcp.expose=true+tool_name,payload_type_matchroute to the COMPUTE handler). 7 cases.tests/integration/services/mcp/test_service_mcp_tool_discovery.py::TestMCPWrapperOrchestratorSatisfiesGate— wrapper-shaped orchestrator projection → non-NoneModelMCPToolDefinition; barecompute_genericprojection →None(rule preserved, not relaxed).tests/unit/topics/test_platform_topic_suffixes.py::TestGeneratedToolTopicBuilders.Verification
uv run pytest tests/unit/services/mcp/ tests/unit/topics/ tests/integration/services/mcp/test_service_mcp_tool_discovery.py -q→ 322 passed.mypy --strictclean on new sources;ruff format/checkclean;pre-commiton changed files green (patterns gate exemptions added for literal name identifiers — same precedent asmodel_skill_mapping.py; no-hardcoded-topics PASS).uv run pytest tests/ -v(no-k) runs in CI as the authoritative gate.CI-unblocking dependency (documented)
This PR also vendors
docker/migrations/forward/nodes/node_projection_event_chain/0001_create_event_chain.sqlvia the canonicalscripts/sync-node-migrations.sh. OMN-13620 merged this node migration to omnimarket dev (11:52 UTC) after the last infra PR #2112 merged (10:16 UTC), so thenode-migration-syncCI gate is now RED on infra@dev for this and every subsequent infra PR until the migration is vendored. The vendored file matches omnimarketorigin/devbyte-for-byte. This is the root-fix path the gate itself mandates ("Run scripts/sync-node-migrations.sh then commit the diff").Scope notes (honest)
node_generate_node_effect), not omnibase_infra. The wrapper-emission logic is correctly placed in omnibase_infra (the repo that owns MCP-exposure semantics and the orchestrator-only gate); the generation pipeline calls this emitter. Out of scope (per ticket): true hot-load / runtime dynamic dispatch of generated nodes; the June-11 demo loop (sandbox-invoke path); changing the sandbox-invoke path.Evidence-Source: OCC#3183
Evidence-Ticket: OMN-12841
Refs OMN-12841. Paired OCC receipt PR: OCC#3183.