Skip to content

Conversation

Nick-PDS
Copy link

Came across an issue where schema wouldn't translate property in a custom MCP I built using unions and discriminator patterns using the example schema below. Introduces some run-time validation based on the union schema.

{
  "anyOf": [
    {
      "type": "object",
      "properties": {
        "operation": {
          "type": "string",
          "const": "getById",
          "description": "Get a page by its ID"
        },
        "useCache": {
          "type": "boolean",
          "default": true,
          "description": "Whether to prioritize cached data"
        },
        "id": {
          "type": "string",
          "description": "Page ID to retrieve"
        },
        "select": {
          "anyOf": [
            {
              "type": "string",
              "description": "Comma-separated list of field names to include in the response"
            },
            {
              "type": "array",
              "items": {
                "type": "string",
                "description": "Field name to include in the response"
              },
              "description": "Array of field names to include in the response"
            }
          ],
          "description": "Fields to include in the response"
        },
        "depth": {
          "type": "number",
          "minimum": 0,
          "maximum": 10,
          "description": "Number (0-10) for relationship population depth"
        }
      },
      "required": [
        "operation",
        "id"
      ],
      "additionalProperties": false,
      "description": "Get a single page by its ID"
    },
    {
      "type": "object",
      "properties": {
        "operation": {
          "type": "string",
          "const": "list",
          "description": "List pages with optional filtering and pagination"
        },
        "useCache": {
          "type": "boolean",
          "default": true,
          "description": "Whether to prioritize cached data"
        },
        "filters": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "where": {
                  "description": "Object of filter conditions with comparison operators"
                },
                "sort": {
                  "anyOf": [
                    {
                      "type": "string",
                      "description": "Field to sort by (prefix with - for descending)"
                    },
                    {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "description": "Field to sort by (prefix with - for descending)"
                      },
                      "description": "Array of fields to sort by"
                    }
                  ],
                  "description": "String or Array for sorting (prefix with - for descending)"
                },
                "limit": {
                  "type": "number",
                  "description": "Number of items per page"
                },
                "page": {
                  "type": "number",
                  "description": "Page number for pagination"
                },
                "depth": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 10,
                  "description": "Number (0-10) for relationship population depth"
                },
                "select": {
                  "$ref": "#/anyOf/0/properties/select",
                  "description": "Fields to include in the response"
                },
                "published": {
                  "type": "boolean",
                  "description": "Filter to only published pages (shorthand for where._status=published)"
                },
                "withBlockType": {
                  "type": "string",
                  "description": "Filter to pages containing a specific block type"
                }
              },
              "additionalProperties": false,
              "description": "Advanced filters for list/count operations (object format)"
            },
            {
              "type": "string",
              "description": "JSON string of filters - WARNING: Always prefer to send an object directly rather than a string"
            }
          ],
          "description": "Optional filters for the list operation"
        }
      },
      "required": [
        "operation"
      ],
      "additionalProperties": false,
      "description": "List pages with optional filtering and pagination"
    },
    {
      "type": "object",
      "properties": {
        "operation": {
          "type": "string",
          "const": "count",
          "description": "Count pages matching criteria"
        },
        "useCache": {
          "type": "boolean",
          "default": true,
          "description": "Whether to prioritize cached data"
        },
        "filters": {
          "$ref": "#/anyOf/1/properties/filters",
          "description": "Optional filters for the count operation"
        }
      },
      "required": [
        "operation"
      ],
      "additionalProperties": false,
      "description": "Count pages matching criteria"
    },
    {
      "type": "object",
      "properties": {
        "operation": {
          "type": "string",
          "const": "getFields",
          "description": "\ud83d\udd11 REQUIRED FIRST STEP: Get field definitions AND verification key for pages collection. You MUST call this before any create/update operations."
        },
        "useCache": {
          "type": "boolean",
          "default": true,
          "description": "Whether to prioritize cached data"
        }
      },
      "required": [
        "operation"
      ],
      "additionalProperties": false,
      "description": "\ud83d\udd11 REQUIRED FIRST STEP: Get field definitions AND verification key needed for page operations"
    },
    {
      "type": "object",
      "properties": {
        "operation": {
          "type": "string",
          "const": "getBlockFields",
          "description": "\ud83d\udd11 REQUIRED STEP: Get field definitions AND verification key for a specific block type. You MUST call this for EACH block type you plan to use."
        },
        "useCache": {
          "type": "boolean",
          "default": true,
          "description": "Whether to prioritize cached data"
        },
        "blockType": {
          "type": "string",
          "description": "Block type to get fields and verification key for (e.g., 'content', 'hero', etc.)"
        }
      },
      "required": [
        "operation",
        "blockType"
      ],
      "additionalProperties": false,
      "description": "\ud83d\udd11 REQUIRED STEP: Get field definitions AND verification key needed for block operations"
    },
    {
      "type": "object",
      "properties": {
        "operation": {
          "type": "string",
          "const": "getBlocksByType",
          "description": "Get blocks of a specific type from a page"
        },
        "useCache": {
          "type": "boolean",
          "default": true,
          "description": "Whether to prioritize cached data"
        },
        "id": {
          "type": "string",
          "description": "Page ID to search blocks in"
        },
        "blockType": {
          "type": "string",
          "description": "Block type to find"
        }
      },
      "required": [
        "operation",
        "id",
        "blockType"
      ],
      "additionalProperties": false,
      "description": "Get blocks of a specific type from a page"
    }
  ],
  "description": "Parameters",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "schema#",
  "type": "object",
  "properties": {}
}

@tjbck
Copy link
Collaborator

tjbck commented Apr 1, 2025

I'll do some refacs here but is this PR tested?

@Nick-PDS
Copy link
Author

Nick-PDS commented Apr 1, 2025

I'll do some refacs here but is this PR tested?

It’s tested, confirmed that this supports union schemas and standard schemas. There aren’t a lot of MCPs utilizing unions but the custom MCP I am using does use union schemas and the output of that schema I provided in the PR message.

Zod’s union methods: https://zod.dev/?id=unions

@tjbck tjbck changed the base branch from main to dev April 2, 2025 23:47
@tremlin tremlin mentioned this pull request Apr 17, 2025
7 tasks
@cutekibry cutekibry mentioned this pull request Apr 24, 2025
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants