@@ -624,12 +624,44 @@ func DeleteProjectItem(getClient GetClientFn, t translations.TranslationHelperFu
624624func UpdateProjectItem (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
625625 return mcp .NewTool ("update_project_item" ,
626626 mcp .WithDescription (t ("TOOL_UPDATE_PROJECT_ITEM_DESCRIPTION" , "Update a specific Project item for a user or org" )),
627- mcp .WithToolAnnotation (mcp.ToolAnnotation {Title : t ("TOOL_UPDATE_PROJECT_ITEM_USER_TITLE" , "Update project item" ), ReadOnlyHint : ToBoolPtr (false )}),
628- mcp .WithString ("owner_type" , mcp .Required (), mcp .Description ("Owner type" ), mcp .Enum ("user" , "org" )),
629- mcp .WithString ("owner" , mcp .Required (), mcp .Description ("If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive." )),
630- mcp .WithNumber ("project_number" , mcp .Required (), mcp .Description ("The project's number." )),
631- mcp .WithNumber ("item_id" , mcp .Required (), mcp .Description ("The numeric ID of the project item to update (not the issue or pull request ID)." )),
632- mcp .WithArray ("fields" , mcp .Required (), mcp .Description ("A list of field updates to apply." )),
627+ mcp .WithToolAnnotation (mcp.ToolAnnotation {Title : t ("TOOL_UPDATE_PROJECT_ITEM_USER_TITLE" , "Update project item" ),
628+ ReadOnlyHint : ToBoolPtr (false )},
629+ ),
630+ mcp .WithString ("owner_type" ,
631+ mcp .Required (), mcp .Description ("Owner type" ), mcp .Enum ("user" , "org" ),
632+ ),
633+ mcp .WithString ("owner" ,
634+ mcp .Required (),
635+ mcp .Description ("If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive." ),
636+ ),
637+ mcp .WithNumber ("project_number" ,
638+ mcp .Required (), mcp .Description ("The project's number." ),
639+ ),
640+ mcp .WithNumber ("item_id" ,
641+ mcp .Required (),
642+ mcp .Description ("The numeric ID of the project item to update (not the issue or pull request ID)." ),
643+ ),
644+ mcp .WithArray ("fields" ,
645+ mcp .Required (),
646+ mcp .Description ("A list of field updates to apply." ),
647+ mcp .Items (
648+ map [string ]interface {}{
649+ "type" : "object" ,
650+ "additionalProperties" : false ,
651+ "required" : []string {"id" , "value" },
652+ "properties" : map [string ]interface {}{
653+ "id" : map [string ]interface {}{
654+ "type" : "int" ,
655+ "description" : "ID of the project field" ,
656+ },
657+ "value" : map [string ]interface {}{
658+ "type" : "any" ,
659+ // intentionally left without a specific JSON schema type to allow any value
660+ "description" : "value of the project field" ,
661+ },
662+ },
663+ }),
664+ ),
633665 ), func (ctx context.Context , req mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
634666 owner , err := RequiredParam [string ](req , "owner" )
635667 if err != nil {
@@ -651,16 +683,18 @@ func UpdateProjectItem(getClient GetClientFn, t translations.TranslationHelperFu
651683 if err != nil {
652684 return mcp .NewToolResultError (err .Error ()), nil
653685 }
654- fieldsParam , ok := req .GetArguments ()["fields" ]
655- if ! ok {
686+
687+ // Extract and validate fields parameter (required & non-empty)
688+ args := req .GetArguments ()
689+ rawFields , present := args ["fields" ]
690+ if ! present {
656691 return mcp .NewToolResultError ("missing required parameter: fields" ), nil
657692 }
658-
659- rawFields , ok := fieldsParam .([]any )
693+ fields , ok := rawFields .([]interface {})
660694 if ! ok {
661- return mcp .NewToolResultError ("parameter fields must be an array of objects" ), nil
695+ return mcp .NewToolResultError ("fields parameter must be an array of objects with id and value " ), nil
662696 }
663- if len (rawFields ) == 0 {
697+ if len (fields ) == 0 {
664698 return mcp .NewToolResultError ("fields must contain at least one field update" ), nil
665699 }
666700
@@ -671,43 +705,32 @@ func UpdateProjectItem(getClient GetClientFn, t translations.TranslationHelperFu
671705 projectsURL = fmt .Sprintf ("users/%s/projectsV2/%d/items/%d" , owner , projectNumber , itemID )
672706 }
673707
674- updateFields := make ([]* newProjectV2Field , 0 , len (rawFields ))
675- for idx , rawField := range rawFields {
676- fieldMap , ok := rawField .(map [string ]any )
708+ updateFields := make ([]* newProjectV2Field , 0 , len (fields ))
709+ for _ , rawField := range fields {
710+ m , ok := rawField .(map [string ]any )
677711 if ! ok {
678- return mcp .NewToolResultError (fmt . Sprintf ( " fields[%d] must be an object", idx ) ), nil
712+ return mcp .NewToolResultError ("each element of fields must be an object" ), nil
679713 }
680-
681- rawID , ok := fieldMap ["id" ]
714+ idVal , ok := m ["id" ]
682715 if ! ok {
683- return mcp .NewToolResultError (fmt . Sprintf ( "fields[%d] is missing 'id'", idx ) ), nil
716+ return mcp .NewToolResultError ("each field update must include an 'id'" ), nil
684717 }
685-
686- var fieldID int64
687- switch v := rawID .(type ) {
718+ var idInt64 int64
719+ switch v := idVal .(type ) {
688720 case float64 :
689- fieldID = int64 (v )
721+ idInt64 = int64 (v )
722+ case int :
723+ idInt64 = int64 (v )
690724 case int64 :
691- fieldID = v
692- case json.Number :
693- n , convErr := v .Int64 ()
694- if convErr != nil {
695- return mcp .NewToolResultError (fmt .Sprintf ("fields[%d].id must be a numeric value" , idx )), nil
696- }
697- fieldID = n
725+ idInt64 = v
698726 default :
699- return mcp .NewToolResultError (fmt . Sprintf ( "fields[%d].id must be a numeric value" , idx ) ), nil
727+ return mcp .NewToolResultError ("field 'id' must be a number" ), nil
700728 }
701-
702- value , ok := fieldMap ["value" ]
729+ value , ok := m ["value" ]
703730 if ! ok {
704- return mcp .NewToolResultError (fmt . Sprintf ( "fields[%d] is missing 'value'", idx ) ), nil
731+ return mcp .NewToolResultError ("each field update must include a 'value'" ), nil
705732 }
706-
707- updateFields = append (updateFields , & newProjectV2Field {
708- ID : github .Ptr (fieldID ),
709- Value : value ,
710- })
733+ updateFields = append (updateFields , & newProjectV2Field {ID : github .Ptr (idInt64 ), Value : value })
711734 }
712735
713736 updateProjectItemOptions := & updateProjectItemOptions {Fields : updateFields }
@@ -749,8 +772,9 @@ type updateProjectItemOptions struct {
749772}
750773
751774type newProjectV2Field struct {
752- ID * int64 `json:"id,omitempty"`
753- Value any `json:"value,omitempty"`
775+ ID * int64 `json:"id,omitempty"`
776+ // Value should be sent as explicit null if user supplies null, so do NOT use omitempty
777+ Value any `json:"value"`
754778}
755779
756780type newProjectItem struct {
0 commit comments