Skip to content

[MCP Plugin]: create/update tools produce { type: 'null' } for nullable relationship fields #16214

@marckrieger

Description

@marckrieger

Describe the Bug

The MCP plugin's simplifyRelationshipFields utility strips $ref options from oneOf unions but does not inject an ID-type replacement. For nullable relationship fields, this causes the field schema to collapse to { type: "null" }, making it impossible to pass a valid relationship ID through MCP create/update tools.

Root cause

In src/utils/schemaConversion/simplifyRelationshipFields.ts, when a nullable relationship field has a JSON schema like:

{
  "oneOf": [
    { "type": "null" },
    { "$ref": "#/definitions/media" }
  ]
}

The function filters out the $ref option (lines 33-35), leaving only [{ "type": "null" }]. Since nonRefOptions.length === 1, it unwraps to just { "type": "null" } (lines 37-39).

The $ref option should be replaced with an ID-accepting type (e.g. { "type": ["string", "number"] }) rather than simply removed, since MCP clients need to pass relationship IDs, not populated objects.

Affected fields

Every nullable (non-required) relationship field on every collection. For example:

  • icon (relationship to media) → becomes { "type": "null" } in the MCP tool schema
  • tenant (relationship added by @payloadcms/plugin-multi-tenant) → becomes { "type": "null" }

Required relationship fields are also affected — they produce an empty schema {} (the oneOf only contains a $ref, so after filtering there are zero nonRefOptions).

Link to the code that reproduces this issue

https://github.com/payloadcms/payload/blob/main/packages/plugin-mcp/src/utils/schemaConversion/simplifyRelationshipFields.ts#L33-L48

Reproduction Steps

  1. Create a collection with a nullable relationship field:
    {
      name: 'icon',
      type: 'relationship',
      relationTo: 'media',
    }
  2. Enable MCP plugin for that collection
  3. Connect an MCP client and inspect the create tool's input schema
  4. The icon field will show { "type": "null" } instead of accepting a string/number ID
  5. Attempting to create a document with icon: 1 (a valid media ID) fails with a validation error

Suggested fix

When stripping $ref options, inject a replacement that accepts relationship IDs:

const nonRefOptions = processed.oneOf
  .filter((option) => !(option && typeof option === 'object' && '$ref' in option))
  .map((option) => simplifyRelationshipFields(option))

// If we removed $ref options, add an ID-accepting type
const hadRef = processed.oneOf.some(
  (option) => option && typeof option === 'object' && '$ref' in option,
)
if (hadRef) {
  nonRefOptions.push({ type: ['string', 'number'] })
}

Which area(s) are affected?

plugin: mcp

Environment Info

Relevant Packages:
  payload: 3.81.0
  @payloadcms/plugin-mcp: 3.81.0
  @payloadcms/plugin-multi-tenant: 3.81.0
  next: 15.3.1
  react: 19.1.0

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions