Fix: keep single-select/iteration option IDs as strings in update_project_item_field

PR
4mo ago
+14-23

Problem

update_project_item_field cannot set a single-select option whose option ID is all-numeric — it raises Invalid value type for single select field … Expected option ID string. On a real board this blocks setting common values such as Status: Done or Priority/Importance: Low whenever GitHub happens to assign that option an all-digit ID (e.g. 98236657). Option IDs with a leading zero (e.g. 07173273) are corrupted on top of that.

Root cause

The MCP tool wrapper in server.py does eager, shape-based type inference on the value before the field type is known:

parsed_value = float(field_value)
if parsed_value.is_integer():
    parsed_value = int(parsed_value)

An all-numeric option ID parses cleanly as a number, so the string becomes an int before it reaches the client. GitHubClient.update_project_item_field then dispatches single-select fields through:

if isinstance(value, str):
    field_value_input = {"singleSelectOptionId": value}
else:
    raise GitHubClientError(... "Expected option ID string.")

The value is now an int, so it is rejected. For an ID like 07173273, the float() → int() round-trip also drops the leading zero, corrupting the ID.

The field's type should decide how the value is interpreted — not the value's textual shape.

Fix

Two small, surgical changes:

  • server.py — drop the shape-based coercion in the update_project_item_field tool and pass the value through as a string (the now-unused Any import is removed).
  • github_client.py — the number-field branch (PVTNU_) now coerces a numeric string to float. The tool layer always passes strings, and at that point the field is already known to be numeric by prefix, so number fields keep working once the eager coercion is gone.

Net effect: single-select / iteration option IDs are preserved verbatim as strings; number / date / text fields are unaffected.

Verification

Tested end-to-end against the live GitHub GraphQL API on a real Projects v2 board (a throwaway draft item, deleted afterwards):

  • Status: Done with all-numeric option ID 98236657 → set successfully ✅
  • Importance: Low with leading-zero option ID 07173273 → set successfully, leading zero preserved ✅
  • text / number fields continue to work.

Notes

  • Related to #10, which independently reworks this same function's type detection for Number/Date fields via dataType metadata. This PR is narrower and complementary: it fixes the server.py eager-coercion path that breaks all-numeric single-select option IDs (not covered by #10). A metadata-based dispatch like #10's would be a fine long-term home for this too — happy to adapt/rebase if you prefer that direction.
  • No behavioral change for non-numeric option IDs.

🤖 Generated with Claude Code


Summary by cubic

Fixes update_project_item_field failing to set single-select/iteration options when the option ID is all numeric. Values are now passed as strings so IDs like "98236657" and those with leading zeros are preserved; number fields continue to work.

  • Bug Fixes
    • server.py: removed shape-based numeric coercion; pass field_value through as a string.
    • github_client.py: for PVTNU_ number fields, accept numeric strings and coerce to float.