Files
ombi-mcp/docs/megaplans/m1/03-input-schema-rectification.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

964 lines
32 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 03 — Input Schema Defect Rectification
## Objective
Apply all input-schema fixes to `03-input-schemas.md`. Below is the **complete replacement JSON** for every modified tool; unmodified tools carry over verbatim (rename only, Phase 01). Order of `##` sections is unchanged.
## Prerequisites
- Phase 01 complete (all `##` section headers already carry the new `read_*`/`write_*` names).
- Phase 04 consulted for the string↔int enum maps referenced by `request_type`, `status`, `agent`, `notification_type` (the maps themselves are owned by Phase 04; this phase changes the schemas to strings).
- Source document to mutate: `docs/schema/03-input-schemas.md`.
## Specification
### 3.1 `read_media` — fix `by_request` enum
Only change vs. current doc: `by_request.media.enum` `["movie","tv","artist"]` → `["movie","tv","album"]`. Adapter maps `album`→`GET /api/v2/Search/artist/request/{requestId}` (route segment literally `artist`, but the ID namespace is album requests — keep the **Verify** note; response `ArtistInformation` projects into `media_page`).
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"action": { "const": "details" },
"target": { "$ref": "#/$defs/external" }
},
"required": ["action", "target"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "by_request" },
"media": { "type": "string", "enum": ["movie", "tv", "album"] },
"request_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "media", "request_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "movie_localized" },
"tmdb_id": { "type": "integer", "minimum": 1 },
"language": { "type": "string", "minLength": 1, "maxLength": 20 }
},
"required": ["action", "tmdb_id", "language"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "ratings" },
"media": { "type": "string", "enum": ["movie", "tv"] },
"name": { "type": "string", "minLength": 1, "maxLength": 300 },
"year": { "type": "integer", "minimum": 1870, "maximum": 9999 }
},
"required": ["action", "media", "name", "year"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "streaming" },
"media": { "type": "string", "enum": ["movie", "tv"] },
"tmdb_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "media", "tmdb_id"],
"additionalProperties": false
}
],
"$defs": {
"external": {
"oneOf": [
{
"type": "object",
"properties": {
"media": { "const": "movie" },
"provider": { "const": "tmdb" },
"id": { "type": "integer", "minimum": 1 }
},
"required": ["media", "provider", "id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"media": { "const": "movie" },
"provider": { "const": "imdb" },
"id": { "type": "string", "minLength": 1, "maxLength": 32, "pattern": "^tt[0-9]+$" }
},
"required": ["media", "provider", "id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"media": { "const": "tv" },
"provider": { "type": "string", "enum": ["tmdb", "tvdb"] },
"id": { "type": "integer", "minimum": 1 }
},
"required": ["media", "provider", "id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"media": { "type": "string", "enum": ["artist", "album"] },
"provider": { "const": "musicbrainz" },
"id": { "type": "string", "minLength": 1, "maxLength": 100 }
},
"required": ["media", "provider", "id"],
"additionalProperties": false
}
]
}
}
}
```
### 3.2 `read_requests` — optional `status` (default `all`), `sort` → `sort_direction`
- `status` leaves `required`; gains `"default": "all"` (documentation — adapter applies it).
- `sort` object removed; flat optional `sort_direction` enum `["asc","desc"]`, `"default": "desc"`. Wire: `sort=requestDate`, `sortOrder=asc|desc` path segments — `request_date` remains the only published sort field.
- `$defs/sort` deleted; `$defs/page` unchanged.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"action": { "const": "list" },
"media": { "type": "string", "enum": ["movie", "tv"] },
"status": {
"type": "string",
"enum": ["all", "pending", "processing", "available", "denied", "unavailable"],
"default": "all"
},
"sort_direction": { "type": "string", "enum": ["asc", "desc"], "default": "desc" },
"page": { "$ref": "#/$defs/page" }
},
"required": ["action", "media"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "list" },
"media": { "const": "album" },
"status": {
"type": "string",
"enum": ["all", "pending", "processing", "available", "denied"],
"default": "all"
},
"sort_direction": { "type": "string", "enum": ["asc", "desc"], "default": "desc" },
"page": { "$ref": "#/$defs/page" }
},
"required": ["action", "media"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "get" },
"target": {
"type": "object",
"properties": {
"kind": { "type": "string", "enum": ["movie", "tv_parent"] },
"id": { "type": "integer", "minimum": 1 }
},
"required": ["kind", "id"],
"additionalProperties": false
}
},
"required": ["action", "target"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "children" },
"parent_request_id": { "type": "integer", "minimum": 1 },
"page": { "$ref": "#/$defs/page" }
},
"required": ["action", "parent_request_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "search" },
"media": { "type": "string", "enum": ["movie", "tv", "album"] },
"query": { "type": "string", "minLength": 1, "maxLength": 200 },
"page": { "$ref": "#/$defs/page" }
},
"required": ["action", "media", "query"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "recent" },
"page": { "$ref": "#/$defs/page" }
},
"required": ["action"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "retry_queue" },
"page": { "$ref": "#/$defs/page" }
},
"required": ["action"],
"additionalProperties": false
}
],
"$defs": {
"page": {
"type": "object",
"properties": {
"offset": { "type": "integer", "minimum": 0, "default": 0 },
"limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 }
},
"required": [],
"additionalProperties": false
}
}
}
```
### 3.3 `write_request_create` — `is_4k` optional + `season` selection mode
- Movie branch: `is_4k` removed from `required` (`"default": false` documents intent; adapter defaults false → `is4kRequest:false` — note: omitting `is4kRequest` entirely is also acceptable wire-wise since the field is optional upstream; the adapter MUST emit `false` explicitly for deterministic diffs).
- `selection` gains a third oneOf branch: `mode:"season"` + `season_numbers` array.
- Wire construction for `season` mode (**Design**): for each `season_numbers` entry the adapter resolves the show's season/episode list via the same upstream call used by `read_media.details` (TV, same provider namespace), then emits `seasons:[{seasonNumber:n, episodes:[{episodeNumber:e},…]}]` with every listed episode; `requestAll/firstSeason/latestSeason` all `false`. If episode metadata yields zero episodes for a requested season → `INVALID_ARGUMENT` with `field:"season_numbers"`. Sending `episodes:[]` as a whole-season shortcut remains **Verify**-gated and is not the default.
- `overrides` keys already `root_folder_id`/`quality_profile_id` (unchanged — they are the canonical names other tools now align to).
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"action": { "const": "movie" },
"tmdb_id": { "type": "integer", "minimum": 1 },
"is_4k": { "type": "boolean", "default": false },
"language": { "type": "string", "minLength": 1, "maxLength": 20 },
"on_behalf_user_id": { "type": "string", "minLength": 1, "maxLength": 200 },
"overrides": { "$ref": "#/$defs/overrides" }
},
"required": ["action", "tmdb_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "tv" },
"provider": { "const": "tmdb" },
"id": { "type": "integer", "minimum": 1 },
"selection": { "$ref": "#/$defs/selection" },
"language": { "type": "string", "minLength": 1, "maxLength": 20 },
"language_profile_id": { "type": "integer", "minimum": 1 },
"on_behalf_user_id": { "type": "string", "minLength": 1, "maxLength": 200 },
"overrides": { "$ref": "#/$defs/overrides" }
},
"required": ["action", "provider", "id", "selection"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "tv" },
"provider": { "const": "tvdb" },
"id": { "type": "integer", "minimum": 1 },
"selection": { "$ref": "#/$defs/selection" },
"language_profile_id": { "type": "integer", "minimum": 1 },
"on_behalf_user_id": { "type": "string", "minLength": 1, "maxLength": 200 },
"overrides": { "$ref": "#/$defs/overrides" }
},
"required": ["action", "provider", "id", "selection"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "album" },
"musicbrainz_id": { "type": "string", "minLength": 1, "maxLength": 100 },
"requested_by_alias": { "type": "string", "minLength": 1, "maxLength": 200 }
},
"required": ["action", "musicbrainz_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "collection" },
"collection_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "collection_id"],
"additionalProperties": false
}
],
"$defs": {
"overrides": {
"type": "object",
"properties": {
"root_folder_id": { "type": "integer", "minimum": 1 },
"quality_profile_id": { "type": "integer", "minimum": 1 }
},
"required": [],
"additionalProperties": false,
"minProperties": 1
},
"selection": {
"oneOf": [
{
"type": "object",
"properties": {
"mode": { "type": "string", "enum": ["all", "first_season", "latest_season"] }
},
"required": ["mode"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"mode": { "const": "season" },
"season_numbers": {
"type": "array",
"items": { "type": "integer", "minimum": 0 },
"minItems": 1,
"maxItems": 200,
"uniqueItems": true
}
},
"required": ["mode", "season_numbers"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"mode": { "const": "episodes" },
"seasons": {
"type": "array",
"items": {
"type": "object",
"properties": {
"season_number": { "type": "integer", "minimum": 0 },
"episodes": {
"type": "array",
"items": { "type": "integer", "minimum": 1 },
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
}
},
"required": ["season_number", "episodes"],
"additionalProperties": false
},
"minItems": 1,
"maxItems": 200,
"uniqueItems": true
}
},
"required": ["mode", "seasons"],
"additionalProperties": false
}
]
}
}
}
```
### 3.4 `write_request_moderate` — `is_4k` optional in all four movie branches
All four `media:"movie"` branches (`approve`, `deny`, `mark_available`, `mark_unavailable`) drop `is_4k` from `required`; the property stays with `"default": false`. Adapter emits `is4K:false` when omitted. TV/album branches unchanged.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"action": { "const": "approve" },
"media": { "const": "movie" },
"request_id": { "type": "integer", "minimum": 1 },
"is_4k": { "type": "boolean", "default": false }
},
"required": ["action", "media", "request_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "deny" },
"media": { "const": "movie" },
"request_id": { "type": "integer", "minimum": 1 },
"is_4k": { "type": "boolean", "default": false },
"reason": { "type": "string", "minLength": 1, "maxLength": 2000 }
},
"required": ["action", "media", "request_id", "reason"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "mark_available" },
"media": { "const": "movie" },
"request_id": { "type": "integer", "minimum": 1 },
"is_4k": { "type": "boolean", "default": false }
},
"required": ["action", "media", "request_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "mark_unavailable" },
"media": { "const": "movie" },
"request_id": { "type": "integer", "minimum": 1 },
"is_4k": { "type": "boolean", "default": false }
},
"required": ["action", "media", "request_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "approve" },
"media": { "type": "string", "enum": ["tv", "album"] },
"request_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "media", "request_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "deny" },
"media": { "type": "string", "enum": ["tv", "album"] },
"request_id": { "type": "integer", "minimum": 1 },
"reason": { "type": "string", "minLength": 1, "maxLength": 2000 }
},
"required": ["action", "media", "request_id", "reason"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "mark_available" },
"media": { "type": "string", "enum": ["tv", "album"] },
"request_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "media", "request_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "mark_unavailable" },
"media": { "type": "string", "enum": ["tv", "album"] },
"request_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "media", "request_id"],
"additionalProperties": false
}
]
}
```
### 3.5 `write_request_options` — unified override keys
`advanced.options` properties renamed to the canonical `root_folder_id`/`quality_profile_id` (was `root_path_override`/`quality_override`); `language_profile_id` unchanged. Wire mapping: `root_folder_id→rootPathOverride`, `quality_profile_id→qualityOverride`, `language_profile_id→languageProfile`. `tv_root`/`tv_quality` branches already use the canonical names — unchanged.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"action": { "const": "advanced" },
"media": { "type": "string", "enum": ["movie", "tv"] },
"request_id": { "type": "integer", "minimum": 1 },
"options": {
"type": "object",
"properties": {
"root_folder_id": { "type": "integer", "minimum": 1 },
"quality_profile_id": { "type": "integer", "minimum": 1 },
"language_profile_id": { "type": "integer", "minimum": 1 }
},
"required": [],
"additionalProperties": false,
"minProperties": 1
}
},
"required": ["action", "media", "request_id", "options"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "tv_root" },
"parent_request_id": { "type": "integer", "minimum": 1 },
"root_folder_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "parent_request_id", "root_folder_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "tv_quality" },
"parent_request_id": { "type": "integer", "minimum": 1 },
"quality_profile_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "parent_request_id", "quality_profile_id"],
"additionalProperties": false
}
]
}
```
### 3.6 `write_request_reprocess` — string `request_type`, optional `is_4k`, unwrap
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"request_type": { "type": "string", "enum": ["movie", "tv", "album"] },
"request_id": { "type": "integer", "minimum": 1 },
"is_4k": { "type": "boolean", "default": false }
},
"required": ["request_type", "request_id"],
"additionalProperties": false
}
```
Wire: `POST /api/v2/Requests/reprocess/{type}/{requestId}/{is4K}` with `type` = Phase 04 `RequestType` int (`tv`→0, `movie`→1, `album`→2) and `is4K` defaulting false.
### 3.7 `write_issue_create` — `request_type` string enum + unwrap
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"title": { "type": "string", "minLength": 1, "maxLength": 300 },
"subject": { "type": "string", "minLength": 1, "maxLength": 300 },
"description": { "type": "string", "minLength": 1, "maxLength": 10000 },
"category_id": { "type": "integer", "minimum": 1 },
"request_type": { "type": "string", "enum": ["movie", "tv", "album"] },
"request_id": { "type": "integer", "minimum": 1 },
"provider_id": { "type": "string", "minLength": 1, "maxLength": 200 }
},
"required": ["title", "description", "category_id", "request_type"],
"additionalProperties": false,
"anyOf": [
{ "required": ["request_id"] },
{ "required": ["provider_id"] }
]
}
```
### 3.8 `read_issues` — `status` string enum replaces `status_code`
`list` and `summary` branches: `status` stays **required** (upstream path needs a status) as `enum ["pending","in_progress","resolved","closed"]`. `$defs/status_code` deleted; `$defs/page` unchanged.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"action": { "const": "list" },
"status": { "type": "string", "enum": ["pending", "in_progress", "resolved", "closed"] },
"page": { "$ref": "#/$defs/page" }
},
"required": ["action", "status"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "summary" },
"status": { "type": "string", "enum": ["pending", "in_progress", "resolved", "closed"] },
"page": { "$ref": "#/$defs/page" }
},
"required": ["action", "status"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "get" },
"issue_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "issue_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "by_request" },
"request_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "request_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "by_provider" },
"provider_id": { "type": "string", "minLength": 1, "maxLength": 200 }
},
"required": ["action", "provider_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "provider_summary" },
"provider_id": { "type": "string", "minLength": 1, "maxLength": 200 }
},
"required": ["action", "provider_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "comments" },
"issue_id": { "type": "integer", "minimum": 1 },
"page": { "$ref": "#/$defs/page" }
},
"required": ["action", "issue_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "counts" }
},
"required": ["action"],
"additionalProperties": false
}
],
"$defs": {
"page": {
"type": "object",
"properties": {
"offset": { "type": "integer", "minimum": 0, "default": 0 },
"limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 }
},
"required": [],
"additionalProperties": false
}
}
}
```
### 3.9 `write_issue_manage` — `status` string enum
`set_status` branch: `status` required, `enum ["pending","in_progress","resolved","closed"]`; `$defs/status_code` deleted. Wire body `{issueId, status:<int>}`.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"action": { "const": "set_status" },
"issue_id": { "type": "integer", "minimum": 1 },
"status": { "type": "string", "enum": ["pending", "in_progress", "resolved", "closed"] }
},
"required": ["action", "issue_id", "status"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "delete" },
"issue_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "issue_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "delete_comment" },
"comment_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "comment_id"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "create_category" },
"value": { "type": "string", "minLength": 1, "maxLength": 200 }
},
"required": ["action", "value"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"action": { "const": "delete_category" },
"category_id": { "type": "integer", "minimum": 1 }
},
"required": ["action", "category_id"],
"additionalProperties": false
}
]
}
```
### 3.10 `write_settings_patch` — string enums in `NotificationTemplates`
Replace the `Ombi_Store_Entities_NotificationTemplates` `$def` **only**; every section branch and all other `$defs` carry over byte-identical. Patch keys `notification_type`/`agent` translate to wire `notificationType`/`agent` with int values via Phase 04 before merging.
```json
"Ombi_Store_Entities_NotificationTemplates": {
"type": "object",
"properties": {
"notification_type": {
"type": "string",
"enum": [
"new_request",
"issue",
"request_available",
"request_approved",
"admin_note",
"test",
"request_declined",
"item_added_to_fault_queue",
"welcome_email",
"issue_resolved",
"issue_comment",
"newsletter",
"partially_available",
"plex_watchlist_token_expired",
"request_deleted",
"issue_in_progress",
"issue_deleted"
]
},
"agent": {
"type": "string",
"enum": [
"email",
"discord",
"pushbullet",
"pushover",
"telegram",
"slack",
"mattermost",
"mobile",
"gotify",
"webhook",
"whatsapp",
"ntfy"
]
},
"subject": { "type": "string" },
"message": { "type": "string" },
"enabled": { "type": "boolean" }
},
"required": [],
"additionalProperties": false
}
```
### 3.11 Single-element `oneOf` unwraps (9 tools)
Mechanical rule: delete the `oneOf` wrapper; promote the sole branch's `type`/`properties`/`required`/`additionalProperties`/`anyOf` to the schema root; `$defs` unchanged. Complete results:
**`write_issue_comment`**:
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"issue_id": { "type": "integer", "minimum": 1 },
"comment": { "type": "string", "minLength": 1, "maxLength": 10000 }
},
"required": ["issue_id", "comment"],
"additionalProperties": false
}
```
**`write_vote`**:
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"media": { "type": "string", "enum": ["movie", "tv", "album"] },
"request_id": { "type": "integer", "minimum": 1 },
"direction": { "type": "string", "enum": ["up", "down"] }
},
"required": ["media", "request_id", "direction"],
"additionalProperties": false
}
```
**`write_request_delete`**:
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"target": { "$ref": "#/$defs/target" }
},
"required": ["target"],
"additionalProperties": false,
"$defs": {
"target": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "enum": ["movie", "tv_parent", "tv_child", "album"] },
"id": { "type": "integer", "minimum": 1 }
},
"required": ["kind", "id"],
"additionalProperties": false
}
]
}
}
}
```
**`read_settings`**:
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"section": {
"type": "string",
"enum": [
"custom_page", "ombi", "base_url", "plex", "client_id", "emby",
"jellyfin", "landingpage", "customization", "default_language",
"themes", "sonarr", "radarr", "lidarr", "lidarrenabled",
"authentication", "update", "user_management", "couchpotato",
"dognzb", "sickrage", "jobs", "issues", "issuesenabled", "vote",
"voteenabled", "themoviedb", "notifications.email",
"notifications.email.enabled", "notifications.discord",
"notifications.telegram", "notifications.pushbullet",
"notifications.pushover", "notifications.slack",
"notifications.mattermost", "notifications.twilio",
"notifications.mobile", "notifications.gotify",
"notifications.ntfy", "notifications.webhook",
"notifications.newsletter"
]
}
},
"required": ["section"],
"additionalProperties": false
}
```
**`write_integration_test`**:
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"service": {
"type": "string",
"enum": [
"discord", "pushbullet", "pushover", "gotify", "ntfy", "webhook",
"mattermost", "slack", "email", "plex", "emby", "jellyfin",
"radarr", "sonarr", "couchpotato", "telegram", "sickrage",
"newsletter", "mobile", "lidarr", "whatsapp"
]
},
"profile_id": { "type": "string", "minLength": 1, "maxLength": 200 }
},
"required": ["service", "profile_id"],
"additionalProperties": false
}
```
**`write_notification_send`**:
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"subject": { "type": "string", "minLength": 1, "maxLength": 300 },
"body": { "type": "string", "minLength": 1, "maxLength": 20000 },
"bcc": { "type": "boolean" },
"user_ids": {
"type": "array",
"items": { "type": "string", "minLength": 1, "maxLength": 200 },
"minItems": 1,
"maxItems": 100,
"uniqueItems": true
}
},
"required": ["subject", "body", "bcc", "user_ids"],
"additionalProperties": false
}
```
**`write_retry_remove`**:
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"queue_id": { "type": "integer", "minimum": 1 }
},
"required": ["queue_id"],
"additionalProperties": false
}
```
(Phase 3.7 `write_issue_create` above is also an unwrap — already printed.)
### 3.12 Untouched tools — carry over verbatim (rename only)
`read_search`, `read_discover`, `read_reference`, `read_request_stats`, `read_votes`, `read_users`, `read_library`, `read_server`, `read_integration`, `write_request_subscribe`, `write_user_preferences`, `write_user_manage`, `write_job_run`, `read_logs`. These keep byte-identical schema bodies; only the `##` heading renames.
## Target artefacts
- `docs/schema/03-input-schemas.md` — every schema block replaced or unwrapped per §§3.1–3.11; the 14 tools in §3.12 carry over byte-identical (heading already renamed by Phase 01). Section order unchanged.
## Verification gates
- [ ] `is_4k` appears in no `required` array anywhere in the file.
- [ ] `request_type_code`/`status_code`/`sort` object/`root_path_override`/`quality_override` absent.
- [ ] `season_numbers` present with `minItems:1`, `uniqueItems:true`, `minimum:0`.
- [ ] `by_request` enum is exactly `["movie","tv","album"]`.
- [ ] No `oneOf` wrapper has exactly one element.
- [ ] All JSON blocks validate under `Draft202012Validator`; each tool accepts its minimal valid object and rejects `{}` where any field is required.
- [ ] `read_requests` accepts `{"action":"list","media":"movie"}` (status/sort_direction defaults adapter-side).
## Evidence labels
- **Documented** — wire mappings cited for `sort`/`sortOrder`, `rootPathOverride`/`qualityOverride`/`languageProfile`, `is4K`/`is4kRequest`, `reprocess` path ints.
- **Design** — `season_numbers` array semantics and adapter episode expansion (D6); canonical `root_folder_id`/`quality_profile_id` naming; adapter-emitted `is4kRequest:false` for deterministic diffs.
- **Verify** — `album`→`artist/request` route-segment asymmetry note; `episodes:[]` whole-season shortcut remains Verify-gated.