Schema docs (docs/schema) cover tool mapping, input/output contracts and endpoint coverage for all 31 MCP tools; megaplan M1 phases 01-03 applied (namespace rename to read_*/write_*, input schema rectification). Go module provides config loading, Ombi client, and tool registry skeleton for implementation.
7.3 KiB
Phase 05 — Output Schema Modularisation
Objective
Dismantle the monolithic 1,200-line union in 04-results.md. Publish per-tool outputSchema on each Tool object (MCP 2025-11-25), each referencing a centralised $defs library.
Prerequisites
- Phase 01 complete (
operationenum values already carry the new tool names; this phase replaces the enum with per-toolconst). - Phase 04 complete (string-label enums used by the replacement
issue/retrydefs). - Locked decision D4: each of the 31 tools declares a distinct
outputSchemacomposed from a centralised$defslibrary of the 15 result families; output schemas are never embedded indescriptionstrings. - Source document to rewrite:
docs/schema/04-results.md.
Specification
Structure of the rewritten 04-results.md
- Keep prose sections
Registration and results,Domain projections,Errors and partial effects,Resources(update wording: per-tooloutputSchema, not "the common outputSchema"; operation enum → new names). - § Shared
$defslibrary — one JSON block containing every definition (target,identifier,reference,credit,provider,episode,season,media,request,issue,comment,group,vote,user,calendar,artwork,retry,paging,media_page,request_page,issue_page,comment_page,group_page,vote_page,user_page,calendar_page,artwork_page,retry_page,reference_page,metric,metrics,change,settings,mutation,log_file,logs,error). All carried verbatim except the two replacements below. - § Envelope template — printed in full below.
- § Tool→family matrix + composition rule — printed in full below.
- Remove the giant
allOfif/then cascade and the globaloperationenum (replaced by per-toolconst).
Envelope template (per-tool outputSchema, printed in full)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"ok": { "type": "boolean" },
"operation": { "const": "<tool_name>" },
"data": { "oneOf": [ "<$ref to each allowed family>" ] },
"error": { "$ref": "#/$defs/error" },
"warnings": {
"type": "array",
"items": { "type": "string" },
"maxItems": 100
},
"truncated": { "type": "boolean" },
"correlation_id": { "type": "string" }
},
"required": ["ok", "operation", "warnings", "truncated", "correlation_id"],
"additionalProperties": false,
"allOf": [
{
"if": { "properties": { "ok": { "const": true } } },
"then": { "required": ["data"], "not": { "required": ["error"] } },
"else": { "required": ["error"] }
}
],
"$defs": { "<transitive closure of the shared library for this tool>" }
}
Composition rule (deterministic)
For each tool: set operation.const to the tool name; set data.oneOf to its allowed families per the matrix; compute $defs = {error} ∪ the allowed families ∪ every def reachable by following $refs inside them (e.g. media_page → media → identifier, target, credit, provider, reference, season, episode; request_page → request → target, identifier, season, episode, paging). Registration text must state: "tools/list advertises outputSchema on each Tool object per MCP 2025-11-25; output contracts never appear inside description."
Tool→family matrix (identical allowances as before, new names)
| Tool | Allowed data families |
|---|---|
read_search |
media_page |
read_discover |
media_page |
read_media |
media_page |
read_reference |
reference_page |
read_requests |
request_page, retry_page |
read_request_stats |
metrics |
read_issues |
issue_page, comment_page, group_page, metrics |
read_votes |
vote_page |
read_users |
user_page, reference_page |
read_library |
media_page, calendar_page, artwork_page |
read_server |
metrics, reference_page |
read_integration |
reference_page, user_page |
read_settings |
settings |
read_logs |
logs |
write_request_create … all 17 write_* |
mutation |
Replacement defs (printed in full — enum abstraction per Phase 04)
issue (labels + code twins):
"issue": {
"type": "object",
"properties": {
"id": { "type": "integer", "minimum": 1 },
"title": { "type": "string" },
"subject": { "type": "string" },
"description": { "type": "string" },
"category_id": { "type": "integer", "minimum": 1 },
"status": {
"type": "string",
"enum": ["pending", "in_progress", "resolved", "closed"]
},
"status_code": { "type": "integer", "enum": [0, 1, 2, 3] },
"request_type": {
"type": "string",
"enum": ["movie", "tv", "album"]
},
"request_type_code": { "type": "integer", "enum": [0, 1, 2] },
"request_id": { "type": "integer", "minimum": 1 },
"provider_id": { "type": "string" },
"created_date": { "type": "string" },
"resolved_date": { "type": "string" },
"reported_by_user_id": { "type": "string" }
},
"required": ["id"],
"additionalProperties": false
}
retry (label + code twin):
"retry": {
"type": "object",
"properties": {
"queue_id": { "type": "integer", "minimum": 1 },
"request_id": { "type": "integer", "minimum": 1 },
"request_type": {
"type": "string",
"enum": ["movie", "tv", "album"]
},
"request_type_code": { "type": "integer", "enum": [0, 1, 2] },
"title": { "type": "string" },
"reason": { "type": "string" }
},
"required": ["queue_id"],
"additionalProperties": false
}
vote stays numeric (vote_code enum:[0,1]) — VoteType labels remain Verify. settings def gains optional leaf naming note (no schema change needed — change.value already allows string|number; agent/notification_type leaves emit label strings when mapped, raw ints + warning when unmapped).
All other defs carry over byte-identical from the current 04-results.md (media, request, comment, group, user, calendar, artwork, paging, all *_page shells, metric, metrics, change, settings, mutation, log_file, logs, error).
Target artefacts
docs/schema/04-results.md— restructured per the five steps above: prose updated, shared$defslibrary block (withissue/retryreplaced), envelope template, tool→family matrix, monolithic union removed. Per-tooloutputSchemacomposition documented;operationbecomesconst.
Verification gates
operationis aconstper tool; no globaloneOf-of-15-families union remains at envelope level.- Every tool's
data.oneOfmatches the matrix exactly. $defsin each tool schema is exactly the transitive closure (no unused defs — context budget).issue/retryemitstatus/request_typelabel enums plus*_codetwins.- Docs state
outputSchemais a Tool field; nothing about embedding contracts indescription.
Evidence labels
- Documented —
outputSchemaon theToolobject per MCP 2025-11-25 (D4); result-family allowances carried over from the existing spec. - Design — per-tool
outputSchemacomposition, transitive-closure$defsrule,operationasconst,issue/retrylabel+code twins per Phase 04. - Verify —
votestays numeric pendingVoteTypelabel verification; unmapped upstream ints emit*_code+ warning per the Phase 04 fallback contract.