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

32 KiB
Raw Permalink Blame History

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).

{
  "$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.
{
  "$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).
{
  "$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.

{
  "$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.

{
  "$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

{
  "$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

{
  "$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.

{
  "$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>}.

{
  "$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.

"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:

{
  "$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:

{
  "$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:

{
  "$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:

{
  "$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:

{
  "$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:

{
  "$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:

{
  "$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.