[PR #5] feat: Enhance dynamic endpoint creation with union schema handling #139

Open
opened 2026-10-18 21:12:11 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/mcpo/pull/5
Author: @Nick-PDS
Created: 3/31/2025
Status: 🔄 Open

Base: dev ← Head: main


📝 Commits (1)

  • 6552530 feat: Enhance dynamic endpoint creation with union schema handling

📊 Changes

1 file changed (+199 additions, -59 deletions)

View changed files

📝 src/mcpo/main.py (+199 -59)

📄 Description

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

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/mcpo/pull/5 **Author:** [@Nick-PDS](https://github.com/Nick-PDS) **Created:** 3/31/2025 **Status:** 🔄 Open **Base:** `dev` ← **Head:** `main` --- ### 📝 Commits (1) - [`6552530`](https://github.com/open-webui/mcpo/commit/65525308f8fac353c003e042654b070c4d949048) feat: Enhance dynamic endpoint creation with union schema handling ### 📊 Changes **1 file changed** (+199 additions, -59 deletions) <details> <summary>View changed files</summary> 📝 `src/mcpo/main.py` (+199 -59) </details> ### 📄 Description 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. ```json { "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": {} } ``` --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
open-webui/mcpo#139
No description provided.