@@ -6,14 +6,11 @@ import (
66 "fmt"
77 "io"
88 "net/http"
9- "net/url"
10- "reflect"
119 "strings"
1210
1311 ghErrors "github.com/github/github-mcp-server/pkg/errors"
1412 "github.com/github/github-mcp-server/pkg/translations"
1513 "github.com/google/go-github/v77/github"
16- "github.com/google/go-querystring/query"
1714 "github.com/mark3labs/mcp-go/mcp"
1815 "github.com/mark3labs/mcp-go/server"
1916)
@@ -232,27 +229,19 @@ func ListProjectFields(getClient GetClientFn, t translations.TranslationHelperFu
232229 return mcp .NewToolResultError (err .Error ()), nil
233230 }
234231
235- var url string
236- if ownerType == "org" {
237- url = fmt .Sprintf ("orgs/%s/projectsV2/%d/fields" , owner , projectNumber )
238- } else {
239- url = fmt .Sprintf ("users/%s/projectsV2/%d/fields" , owner , projectNumber )
240- }
241- projectFields := []projectV2Field {}
242-
243- opts := paginationOptions {PerPage : perPage }
232+ var resp * github.Response
233+ var projectFields []* github.ProjectV2Field
244234
245- url , err = addOptions (url , opts )
246- if err != nil {
247- return nil , fmt .Errorf ("failed to add options to request: %w" , err )
235+ opts := & github.ListProjectsOptions {
236+ ListProjectsPaginationOptions : github.ListProjectsPaginationOptions {PerPage : & perPage },
248237 }
249238
250- httpRequest , err := client .NewRequest ("GET" , url , nil )
251- if err != nil {
252- return nil , fmt .Errorf ("failed to create request: %w" , err )
239+ if ownerType == "org" {
240+ projectFields , resp , err = client .Projects .ListOrganizationProjectFields (ctx , owner , projectNumber , opts )
241+ } else {
242+ projectFields , resp , err = client .Projects .ListUserProjectFields (ctx , owner , projectNumber , opts )
253243 }
254244
255- resp , err := client .Do (ctx , httpRequest , & projectFields )
256245 if err != nil {
257246 return ghErrors .NewGitHubAPIErrorResponse (ctx ,
258247 "failed to list project fields" ,
@@ -312,7 +301,7 @@ func GetProjectField(getClient GetClientFn, t translations.TranslationHelperFunc
312301 if err != nil {
313302 return mcp .NewToolResultError (err .Error ()), nil
314303 }
315- fieldID , err := RequiredInt (req , "field_id" )
304+ fieldID , err := RequiredBigInt (req , "field_id" )
316305 if err != nil {
317306 return mcp .NewToolResultError (err .Error ()), nil
318307 }
@@ -321,21 +310,15 @@ func GetProjectField(getClient GetClientFn, t translations.TranslationHelperFunc
321310 return mcp .NewToolResultError (err .Error ()), nil
322311 }
323312
324- var url string
313+ var resp * github.Response
314+ var projectField * github.ProjectV2Field
315+
325316 if ownerType == "org" {
326- url = fmt . Sprintf ( "orgs/%s/projectsV2/%d/fields/%d" , owner , projectNumber , fieldID )
317+ projectField , resp , err = client . Projects . GetOrganizationProjectField ( ctx , owner , projectNumber , fieldID )
327318 } else {
328- url = fmt .Sprintf ("users/%s/projectsV2/%d/fields/%d" , owner , projectNumber , fieldID )
329- }
330-
331- projectField := projectV2Field {}
332-
333- httpRequest , err := client .NewRequest ("GET" , url , nil )
334- if err != nil {
335- return nil , fmt .Errorf ("failed to create request: %w" , err )
319+ projectField , resp , err = client .Projects .GetUserProjectField (ctx , owner , projectNumber , fieldID )
336320 }
337321
338- resp , err := client .Do (ctx , httpRequest , & projectField )
339322 if err != nil {
340323 return ghErrors .NewGitHubAPIErrorResponse (ctx ,
341324 "failed to get project field" ,
@@ -411,41 +394,32 @@ func ListProjectItems(getClient GetClientFn, t translations.TranslationHelperFun
411394 if err != nil {
412395 return mcp .NewToolResultError (err .Error ()), nil
413396 }
414- fields , err := OptionalStringArrayParam (req , "fields" )
397+ fields , err := OptionalBigIntArrayParam (req , "fields" )
415398 if err != nil {
416399 return mcp .NewToolResultError (err .Error ()), nil
417400 }
418-
419401 client , err := getClient (ctx )
420402 if err != nil {
421403 return mcp .NewToolResultError (err .Error ()), nil
422404 }
423405
424- var url string
425- if ownerType == "org" {
426- url = fmt .Sprintf ("orgs/%s/projectsV2/%d/items" , owner , projectNumber )
427- } else {
428- url = fmt .Sprintf ("users/%s/projectsV2/%d/items" , owner , projectNumber )
429- }
430- projectItems := []projectV2Item {}
431-
432- opts := listProjectItemsOptions {
433- paginationOptions : paginationOptions {PerPage : perPage },
434- filterQueryOptions : filterQueryOptions {Query : queryStr },
435- fieldSelectionOptions : fieldSelectionOptions {Fields : fields },
436- }
406+ var resp * github.Response
407+ var projectItems []* github.ProjectV2Item
437408
438- url , err = addOptions (url , opts )
439- if err != nil {
440- return nil , fmt .Errorf ("failed to add options to request: %w" , err )
409+ opts := & github.ListProjectItemsOptions {
410+ Fields : fields ,
411+ ListProjectsOptions : github.ListProjectsOptions {
412+ ListProjectsPaginationOptions : github.ListProjectsPaginationOptions {PerPage : & perPage },
413+ Query : & queryStr ,
414+ },
441415 }
442416
443- httpRequest , err := client .NewRequest ("GET" , url , nil )
444- if err != nil {
445- return nil , fmt .Errorf ("failed to create request: %w" , err )
417+ if ownerType == "org" {
418+ projectItems , resp , err = client .Projects .ListOrganizationProjectItems (ctx , owner , projectNumber , opts )
419+ } else {
420+ projectItems , resp , err = client .Projects .ListUserProjectItems (ctx , owner , projectNumber , opts )
446421 }
447422
448- resp , err := client .Do (ctx , httpRequest , & projectItems )
449423 if err != nil {
450424 return ghErrors .NewGitHubAPIErrorResponse (ctx ,
451425 ProjectListFailedError ,
@@ -513,11 +487,11 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
513487 if err != nil {
514488 return mcp .NewToolResultError (err .Error ()), nil
515489 }
516- itemID , err := RequiredInt (req , "item_id" )
490+ itemID , err := RequiredBigInt (req , "item_id" )
517491 if err != nil {
518492 return mcp .NewToolResultError (err .Error ()), nil
519493 }
520- fields , err := OptionalStringArrayParam (req , "fields" )
494+ fields , err := OptionalBigIntArrayParam (req , "fields" )
521495 if err != nil {
522496 return mcp .NewToolResultError (err .Error ()), nil
523497 }
@@ -527,32 +501,21 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
527501 return mcp .NewToolResultError (err .Error ()), nil
528502 }
529503
530- var url string
531- if ownerType == "org" {
532- url = fmt .Sprintf ("orgs/%s/projectsV2/%d/items/%d" , owner , projectNumber , itemID )
533- } else {
534- url = fmt .Sprintf ("users/%s/projectsV2/%d/items/%d" , owner , projectNumber , itemID )
535- }
536-
537- opts := fieldSelectionOptions {}
504+ opts := & github.GetProjectItemOptions {}
538505
539506 if len (fields ) > 0 {
540507 opts .Fields = fields
541508 }
542509
543- url , err = addOptions (url , opts )
544- if err != nil {
545- return mcp .NewToolResultError (err .Error ()), nil
546- }
547-
548- projectItem := projectV2Item {}
510+ var resp * github.Response
511+ var projectItem * github.ProjectV2Item
549512
550- httpRequest , err := client .NewRequest ("GET" , url , nil )
551- if err != nil {
552- return nil , fmt .Errorf ("failed to create request: %w" , err )
513+ if ownerType == "org" {
514+ projectItem , resp , err = client .Projects .GetOrganizationProjectItem (ctx , owner , projectNumber , itemID , opts )
515+ } else {
516+ projectItem , resp , err = client .Projects .GetUserProjectItem (ctx , owner , projectNumber , itemID , opts )
553517 }
554518
555- resp , err := client .Do (ctx , httpRequest , & projectItem )
556519 if err != nil {
557520 return ghErrors .NewGitHubAPIErrorResponse (ctx ,
558521 "failed to get project item" ,
@@ -619,7 +582,7 @@ func AddProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
619582 if err != nil {
620583 return mcp .NewToolResultError (err .Error ()), nil
621584 }
622- itemID , err := RequiredInt (req , "item_id" )
585+ itemID , err := RequiredBigInt (req , "item_id" )
623586 if err != nil {
624587 return mcp .NewToolResultError (err .Error ()), nil
625588 }
@@ -637,24 +600,20 @@ func AddProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
637600 return mcp .NewToolResultError (err .Error ()), nil
638601 }
639602
640- var projectsURL string
641- if ownerType == "org" {
642- projectsURL = fmt .Sprintf ("orgs/%s/projectsV2/%d/items" , owner , projectNumber )
643- } else {
644- projectsURL = fmt .Sprintf ("users/%s/projectsV2/%d/items" , owner , projectNumber )
645- }
646-
647- newItem := & newProjectItem {
648- ID : int64 (itemID ),
603+ newItem := & github.AddProjectItemOptions {
604+ ID : itemID ,
649605 Type : toNewProjectType (itemType ),
650606 }
651- httpRequest , err := client .NewRequest ("POST" , projectsURL , newItem )
652- if err != nil {
653- return nil , fmt .Errorf ("failed to create request: %w" , err )
607+
608+ var resp * github.Response
609+ var addedItem * github.ProjectV2Item
610+
611+ if ownerType == "org" {
612+ addedItem , resp , err = client .Projects .AddOrganizationProjectItem (ctx , owner , projectNumber , newItem )
613+ } else {
614+ addedItem , resp , err = client .Projects .AddUserProjectItem (ctx , owner , projectNumber , newItem )
654615 }
655- addedItem := projectV2Item {}
656616
657- resp , err := client .Do (ctx , httpRequest , & addedItem )
658617 if err != nil {
659618 return ghErrors .NewGitHubAPIErrorResponse (ctx ,
660619 ProjectAddFailedError ,
@@ -864,11 +823,6 @@ func DeleteProjectItem(getClient GetClientFn, t translations.TranslationHelperFu
864823 }
865824}
866825
867- type newProjectItem struct {
868- ID int64 `json:"id,omitempty"`
869- Type string `json:"type,omitempty"`
870- }
871-
872826type updateProjectItemPayload struct {
873827 Fields []updateProjectItem `json:"fields"`
874828}
@@ -878,17 +832,6 @@ type updateProjectItem struct {
878832 Value any `json:"value"`
879833}
880834
881- type projectV2Field struct {
882- ID * int64 `json:"id,omitempty"` // The unique identifier for this field.
883- NodeID string `json:"node_id,omitempty"` // The GraphQL node ID for this field.
884- Name string `json:"name,omitempty"` // The display name of the field.
885- DataType string `json:"data_type,omitempty"` // The data type of the field (e.g., "text", "number", "date", "single_select", "multi_select").
886- URL string `json:"url,omitempty"` // The API URL for this field.
887- Options []* any `json:"options,omitempty"` // Available options for single_select and multi_select fields.
888- CreatedAt * github.Timestamp `json:"created_at,omitempty"` // The time when this field was created.
889- UpdatedAt * github.Timestamp `json:"updated_at,omitempty"` // The time when this field was last updated.
890- }
891-
892835type projectV2ItemFieldValue struct {
893836 ID * int64 `json:"id,omitempty"` // The unique identifier for this field.
894837 Name string `json:"name,omitempty"` // The display name of the field.
@@ -926,26 +869,6 @@ type projectV2ItemContent struct {
926869 URL * string `json:"url,omitempty"`
927870}
928871
929- type paginationOptions struct {
930- PerPage int `url:"per_page,omitempty"`
931- }
932-
933- type filterQueryOptions struct {
934- Query string `url:"q,omitempty"`
935- }
936-
937- type fieldSelectionOptions struct {
938- // Specific list of field IDs to include in the response. If not provided, only the title field is included.
939- // Example: fields=102589,985201,169875 or fields[]=102589&fields[]=985201&fields[]=169875
940- Fields []string `url:"fields,omitempty"`
941- }
942-
943- type listProjectItemsOptions struct {
944- paginationOptions
945- filterQueryOptions
946- fieldSelectionOptions
947- }
948-
949872func toNewProjectType (projType string ) string {
950873 switch strings .ToLower (projType ) {
951874 case "issue" :
@@ -981,28 +904,6 @@ func buildUpdateProjectItem(input map[string]any) (*updateProjectItem, error) {
981904 return payload , nil
982905}
983906
984- // addOptions adds the parameters in opts as URL query parameters to s. opts
985- // must be a struct whose fields may contain "url" tags.
986- func addOptions (s string , opts any ) (string , error ) {
987- v := reflect .ValueOf (opts )
988- if v .Kind () == reflect .Ptr && v .IsNil () {
989- return s , nil
990- }
991-
992- u , err := url .Parse (s )
993- if err != nil {
994- return s , err
995- }
996-
997- qs , err := query .Values (opts )
998- if err != nil {
999- return s , err
1000- }
1001-
1002- u .RawQuery = qs .Encode ()
1003- return u .String (), nil
1004- }
1005-
1006907func ManageProjectItemsPrompt (t translations.TranslationHelperFunc ) (tool mcp.Prompt , handler server.PromptHandlerFunc ) {
1007908 return mcp .NewPrompt ("ManageProjectItems" ,
1008909 mcp .WithPromptDescription (t ("PROMPT_MANAGE_PROJECT_ITEMS_DESCRIPTION" , "Interactive guide for managing GitHub Projects V2, including discovery, field management, querying, and updates." )),
0 commit comments