Fix: keep single-select/iteration option IDs as strings in update_project_item_field
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 theupdate_project_item_fieldtool and pass the value through as a string (the now-unusedAnyimport is removed).github_client.py— the number-field branch (PVTNU_) now coerces a numeric string tofloat. 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: Donewith all-numeric option ID98236657→ set successfully ✅Importance: Lowwith leading-zero option ID07173273→ 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
dataTypemetadata. This PR is narrower and complementary: it fixes theserver.pyeager-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; passfield_valuethrough as a string.github_client.py: forPVTNU_number fields, accept numeric strings and coerce to float.