Files
ombi-mcp/docs/megaplans/m1/05-output-schema-modularisation.md
gronod 722108ebd0 Initial commit: Ombi MCP server design docs and Go skeleton
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.
2026-09-18 15:55:44 +01:00

156 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 (`operation` enum values already carry the new tool names; this phase replaces the enum with per-tool `const`).
- Phase 04 complete (string-label enums used by the replacement `issue`/`retry` defs).
- Locked decision D4: each of the 31 tools declares a distinct `outputSchema` composed from a centralised `$defs` library of the 15 result families; output schemas are **never** embedded in `description` strings.
- Source document to rewrite: `docs/schema/04-results.md`.
## Specification
### Structure of the rewritten `04-results.md`
1. Keep prose sections `Registration and results`, `Domain projections`, `Errors and partial effects`, `Resources` (update wording: per-tool `outputSchema`, not "the common outputSchema"; operation enum → new names).
2. **§ Shared `$defs` library** — 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.
3. **§ Envelope template** — printed in full below.
4. **§ Tool→family matrix + composition rule** — printed in full below.
5. Remove the giant `allOf` if/then cascade and the global `operation` enum (replaced by per-tool `const`).
### Envelope template (per-tool `outputSchema`, printed in full)
```json
{
"$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 `$ref`s 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):
```json
"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):
```json
"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 `$defs` library block (with `issue`/`retry` replaced), envelope template, tool→family matrix, monolithic union removed. Per-tool `outputSchema` composition documented; `operation` becomes `const`.
## Verification gates
- [ ] `operation` is a `const` per tool; no global `oneOf`-of-15-families union remains at envelope level.
- [ ] Every tool's `data.oneOf` matches the matrix exactly.
- [ ] `$defs` in each tool schema is exactly the transitive closure (no unused defs — context budget).
- [ ] `issue`/`retry` emit `status`/`request_type` label enums plus `*_code` twins.
- [ ] Docs state `outputSchema` is a Tool field; nothing about embedding contracts in `description`.
## Evidence labels
- **Documented** — `outputSchema` on the `Tool` object per MCP 2025-11-25 (D4); result-family allowances carried over from the existing spec.
- **Design** — per-tool `outputSchema` composition, transitive-closure `$defs` rule, `operation` as `const`, `issue`/`retry` label+code twins per Phase 04.
- **Verify** — `vote` stays numeric pending `VoteType` label verification; unmapped upstream ints emit `*_code` + warning per the Phase 04 fallback contract.