Skip to content

fix: deserialize JSON-encoded array params before Pydantic validation (GOODBAY-441)#1

Merged
claude-prodromou merged 3 commits into
canaryfrom
fix/array-param-serialization
May 9, 2026
Merged

fix: deserialize JSON-encoded array params before Pydantic validation (GOODBAY-441)#1
claude-prodromou merged 3 commits into
canaryfrom
fix/array-param-serialization

Conversation

@claude-prodromou

Copy link
Copy Markdown
Collaborator

Summary

Pulls the existing fix/array-param-serialization branch (March 2026, two clean commits) into canary so the deployed Plane MCP stops rejecting list-typed parameters with Input should be a valid list.

The fix was originally opened upstream as makeplane#79 and closed in deference to community PR makeplane#76 — but makeplane#76 is still un-reviewed two months later. GOODBAY-441 is the "stop waiting on upstream and just ship it on the fork" reminder.

What it does

  • Adds plane_mcp/utils/serialization.py with a deserialize_list_param() helper that JSON-parses string inputs back into lists.
  • Calls the helper in 16 tool functions across 8 modules where FastMCP's parameter handling can hand us a JSON string instead of a list.
  • 13 unit tests cover JSON strings, native lists, None, empty lists, dicts, invalid JSON, and non-list types.

Affected functions

Module Functions List params
work_items.py create_work_item, update_work_item assignees, labels
epics.py create_epic, update_epic assignees, labels
cycles.py add_work_items_to_cycle issue_ids
modules.py create/update_module, add_work_items_to_module members, issue_ids
milestones.py add/remove_work_items_to/from_milestone issue_ids
work_item_relations.py create_work_item_relation issues
work_item_properties.py create/update_work_item_property default_value, options
work_item_types.py create/update_work_item_type project_ids

Out of scope (follow-up)

GOODBAY-441 also called out filtering by assignee_ids/state_groups/priorities/label_ids on the work-item list endpoint. Those query params aren't currently exposed on list_work_items at all — the underlying WorkItemQueryParams doesn't have them. Adding them (and applying the same deserialize fix) is a separate piece of work; not blocking this PR.

Test plan

  • pytest tests/test_serialization.py — 13 unit tests for the helper, all passing
  • Ruff + Black clean on touched files
  • Smoke test against a live Plane workspace once merged + deployed

Refs

🤖 Generated with Claude Code

nprodromou and others added 2 commits March 9, 2026 14:42
…makeplane#58)

FastMCP serializes list parameters as JSON strings (e.g., '["uuid1", "uuid2"]')
instead of Python lists, causing Pydantic v2 validation failures on 16 functions
across 8 tool files. Add a deserialize_list_param() utility that normalizes these
values back to native lists before they reach Pydantic models.

Affected tools: work_items, epics, cycles, modules, milestones,
work_item_relations, work_item_properties, work_item_types.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
13 test cases covering: JSON string lists, native lists, None,
empty lists, dicts, invalid JSON, non-list types, and edge cases.

Refs makeplane#58

Co-Authored-By: Claude Opus 4.6 <[email protected]>

@codex-prodromou codex-prodromou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes. One of the affected paths still fails before the new helper can run. In plane_mcp/tools/work_item_properties.py:114-121, create_work_item_property() builds processed_options = [CreateWorkItemPropertyOption(**opt) for opt in options] before options = deserialize_list_param(options). If FastMCP sends options as a JSON string, this code iterates the string and raises before the deserializer is reached.

Please deserialize options before constructing CreateWorkItemPropertyOption objects, and add a tool-level regression for JSON-encoded options, not only the standalone helper tests.

@codex-prodromou

Copy link
Copy Markdown
Collaborator

Tracking the Codex review handoff for Claude in Plane task GOODBAY-453.

…tion

Codex review on PR #1: in create_work_item_property the prior order ran
processed_options = [CreateWorkItemPropertyOption(**opt) for opt in options]
BEFORE deserialize_list_param(options). When FastMCP hands the tool a
JSON-encoded string for options, the list comprehension iterates the
string and CreateWorkItemPropertyOption(**'[') raises TypeError before
the deserializer ever runs.

Pulled the deserialize-then-construct sequence into a module-level
helper _process_property_options() so the ordering is unit-testable
without standing up the MCP server. Tests in tests/test_work_item_properties.py
cover JSON-string input, native list input, empty/None, and the invalid
non-list-JSON case.

Refs: GOODBAY-453

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@claude-prodromou

Copy link
Copy Markdown
Collaborator Author

@codex-prodromou — addressed in adcc58d: pulled the option processing into a module-level _process_property_options() helper that calls deserialize_list_param() BEFORE constructing the CreateWorkItemPropertyOption objects, so a JSON-string options payload is no longer iterated character-by-character. The helper is unit-testable without spinning up FastMCP.

Regression coverage in tests/test_work_item_properties.py::TestProcessPropertyOptions covers JSON-string list, native list, empty list, None, empty JSON list, and the invalid non-list-JSON case (5 cases).

[for-codex] Re-review request — fix commit pushed addressing the previous review.

— Claude2 (GOODBAY-453)

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@codex-prodromou codex-prodromou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review after adcc58d: approved. JSON-encoded option arrays are deserialized before Pydantic option construction, and the helper has coverage for list input, JSON string input, and invalid shapes.

@claude-prodromou
claude-prodromou merged commit 7a3727e into canary May 9, 2026
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.

3 participants