Skip to content

contract(ams): migrate the AMS miner MCP server to @loopover/contract — structured output for all 11 tools #9536

Description

@JSONbored

Context

Part of #9515; the AMS third of what #9518 originally covered, split out so each migration is a single reviewable PR.

packages/loopover-miner/bin/loopover-miner-mcp.ts registers 11 tools, and not one of them declares an outputSchema or returns structuredContent — every handler returns { content: [{ type: "text", text: JSON.stringify(...) }] }. It is the only one of LoopOver's three MCP servers with no structured output at all, so a client has to parse a JSON string out of a text block and hope the shape holds. Input schemas are raw zod shapes with no shared source.

Two more defects in the same file, both to fix here:

  1. Inconsistent error envelopes. Only loopover_miner_get_audit_feed catches store failures and returns isError: true; the other ten throw, so a missing or corrupt store surfaces as a protocol error from ten tools and a clean structured result from one.
  2. A hardcoded tool-name list in the tests. test/unit/miner-mcp-scaffold.test.ts pins the 11 names inline, so adding a tool needs a manual edit there and in the README — the exact hand-maintenance this epic exists to remove.

Because these tools read the miner box's own SQLite stores, every one of them is locality: "miner" — the property that makes explicit why they cannot be served from a Worker.

Requirements

  1. Contract entries for all 11 tools in @loopover/contract, each with a real input schema and a real output schema modelled from what the handler actually returns (collectPortfolioDashboard, collectManageStatus, collectEventLedgerAuditFeed, buildCalibrationReport, collectStatus/runDoctorChecks, and the plan/claim/governor/run-state store readers). Metadata: locality: "miner", category, auth, availability, read-only annotations.
  2. Every tool emits structuredContent alongside its existing text block. Keep the text payload byte-identical so no current consumer breaks — this is additive, and a before/after snapshot test proves it.
  3. Unified error envelope across all 11. Extend test/unit/miner-mcp-contract.test.ts's existing parity suite (which already runs a per-tool table for secret-shaped values and excluded raw columns) to assert the same schema-described error result for a missing store, a corrupt store, and an unreadable one — for every tool, not just the audit feed.
  4. Delete the hardcoded name list in miner-mcp-scaffold.test.ts; assert against the contract registry filtered to locality: "miner" instead, so a new tool cannot miss the test surface.
  5. Registration reads from the registry, matching the pattern contract(remote): migrate the remote MCP server's tools to @loopover/contract — typed handlers, real output schemas #9518 establishes on the remote server, so the miner server keeps no tool list of its own.
  6. No wire regression: every currently-advertised tool name, description, and input shape is preserved or widened, never tightened. Assert with a tools/list snapshot.
  7. Full branch-counted patch coverage per the house bar. Defects found in a migrated handler get fixed in this PR.

Non-goals

Deliverables

  • 11 contract entries with real input + output schemas
  • structuredContent on every tool, text payload unchanged
  • Unified error envelope + parity-suite coverage for all 11
  • Hardcoded name list replaced by a registry assertion
  • tools/list snapshot proving no wire regression

Expected outcome

The AMS server stops being the odd one out: every tool advertises a validated output schema, emits structured content, and fails the same way, all from the same registry the other two servers read — and the contract validator (#9520) can then hold it to that contract mechanically.

References

Part of #9515. Blocked by #9517 (the contract package). Split from #9518, alongside the stdio server migration. Unblocks #9523 (AMS management tools).

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