Skip to content

contract(remote): migrate the remote MCP server's tools to @loopover/contract — typed handlers, real output schemas #9518

Description

@JSONbored

Context

Part of #9515; the batch phase after #9517's pilot. Today the three MCP servers declare contracts independently:

  • Remote (src/mcp/server.ts): ~116 tools; zod input shapes; ~60 output schemas that are mostly z.unknown().optional() placeholders (e.g. fleetAnalyticsOutputSchema :280, recommendationQualityOutputSchema :293); the rest have none.
  • Stdio (packages/loopover-mcp/bin/loopover-mcp.ts): 102 tools; ~85 shape constants (:371-1104) hand-mirroring the remote; the register helper erases all typing (:1740-1742, three anys), so all 102 handlers take (input: any); exactly one outputSchema (loopover_local_status_structured, :3021) — the comment at :3017-3018 records the abandoned plan to add more. STDIO_TOOL_DESCRIPTORS (:1108-1668) hand-restates name/category/description for all 102.
  • AMS miner (packages/loopover-miner/bin/loopover-miner-mcp.ts): 11 tools, raw zod input shapes, zero output schemas / structuredContent (every handler returns stringified JSON text) — the only server with no structured output at all. test/unit/miner-mcp-scaffold.test.ts:93-105 hardcodes the 11-name list.

Also in scope as duplication that dies here: buildPlanDag/validatePlanDag hand-duplicated from src/services/plan-dag.ts (bin/loopover-mcp.ts:173-215); the fileIncidentReportShape circular-import inline copy (src/mcp/server.ts:262-266) — the schema moves to @loopover/contract and both src/api/routes.ts and src/mcp/server.ts import downward, dissolving the TDZ hazard.

Requirements

  1. Migrate the REMOTE server's (~116) tools to register from @loopover/contract, by category (maintainer, review, branch, discovery, agent, utility, admin). Contract files, registrations switched, handlers typed via z.infer (no any), placeholder output schemas replaced with real ones.

    The stdio and miner servers are split out so each issue stays a single reviewable PR — see the sibling issues. This one owns the remote server plus the shared contract entries the other two consume, so it lands first.

  2. Every tool ends with a real outputSchema and structuredContent. For the miner's 11 tools this adds structured output for the first time — keep the existing text payload for backward compatibility (both content text and structuredContent, matching the other servers' toolResult shape). Output schemas follow the depth policy recorded in contract: create @loopover/contract — the single zod source for tool/API schemas, with a six-tool pilot #9517.

  3. Shared tools declared once. Tools that exist on both remote and stdio (same name, same contract — e.g. loopover_get_repo_context) have exactly one contract entry consumed by both registrations. Locality-specific tools (local-git, miner) carry their locality tag from contract: create @loopover/contract — the single zod source for tool/API schemas, with a six-tool pilot #9517's metadata model.

  4. STDIO_TOOL_DESCRIPTORS is deleted, replaced by the contract registry's name/category/description (single source). The stdio tools CLI command and _meta.category derivation read the registry. Same for the remote's MCP_TOOL_CATEGORIES map — derived, not hand-listed (its sync test converts to a registry meta-test).

  5. Error envelopes unified. The miner's inconsistency (only loopover_miner_get_audit_feed returns isError: true on store failure; the other ten throw) is fixed: every tool on every server returns the same schema-described error envelope. Extend test/unit/miner-mcp-contract.test.ts's parity suite to assert it for all 11.

  6. Hardcoded tool-name lists in tests are replaced by assertions against the contract registry (e.g. miner-mcp-scaffold.test.ts:93-105), so adding a tool cannot silently miss the test surface.

  7. buildPlanDag/validatePlanDag move to a single shared location (contract or engine — whichever src/services/plan-dag.ts and the stdio bin can both import; record the choice in the PR) and the duplicate is deleted.

  8. No wire-visible regressions: tools/list output for existing clients keeps every currently-advertised name, description-meaningful content, and category; input schemas may only widen or stay equal (never tighten an existing accepted input — metagraphed's hard wire-compat constraint). Assert per batch with a before/after tools/list snapshot test.

  9. Full coverage per house bar; defects found in migrated handlers fixed in-batch (program principle 4).

Non-goals

  • New tools (management families are later sub-issues).
  • REST/OpenAPI seam changes.
  • The stdio API client's response validation (generated-clients sub-issue).

Deliverables

  • All ~116 remote + 102 stdio + 11 miner tools registered from @loopover/contract, typed handlers, real output schemas everywhere
  • STDIO_TOOL_DESCRIPTORS and MCP_TOOL_CATEGORIES hand-maps deleted (derived instead)
  • Miner tools emit structuredContent with schemas; unified error envelope + parity-suite coverage
  • Plan-DAG duplication eliminated; incident-report schema single-sourced (TDZ workaround comment gone)
  • Per-batch wire-compat snapshot tests

Expected outcome

grep -rn "mirror of the remote" packages/loopover-mcp returns nothing; no zod shape describing the same contract exists twice anywhere in the repo; every tool on every server advertises a real, Ajv-compilable output schema that its structuredContent actually satisfies (enforced mechanically by the validator sub-issue).

References

Part of #9515. Blocked by #9517. Siblings: the stdio and miner server migrations. The validator (#9515 group 5), docs/clients (group 6), and management families (groups 7–8) build directly on this.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions