Skip to content

Commit 4b6e531

Browse files
committed
fix get project items
1 parent f133c95 commit 4b6e531

File tree

1 file changed

+161
-183
lines changed

1 file changed

+161
-183
lines changed

pkg/github/projects.go

Lines changed: 161 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -518,167 +518,131 @@ func GetProjectFields(getClient GetGQLClientFn, t translations.TranslationHelper
518518
}
519519
}
520520

521-
// GetProjectItems lists items for a project.
522-
func GetProjectItems(getClient GetGQLClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
523-
return mcp.NewTool("get_project_items",
524-
mcp.WithDescription(t("TOOL_GET_PROJECT_ITEMS_DESCRIPTION", "Get items for a project")),
525-
mcp.WithToolAnnotation(mcp.ToolAnnotation{Title: t("TOOL_GET_PROJECT_ITEMS_USER_TITLE", "Get project items"), ReadOnlyHint: ToBoolPtr(true)}),
526-
mcp.WithString("owner", mcp.Required(), mcp.Description("Owner login")),
527-
mcp.WithString("owner_type", mcp.Description("Owner type"), mcp.Enum("user", "organization")),
528-
mcp.WithNumber("number", mcp.Required(), mcp.Description("Project number")),
529-
), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
530-
owner, err := RequiredParam[string](req, "owner")
531-
if err != nil {
532-
return mcp.NewToolResultError(err.Error()), nil
533-
}
534-
number, err := RequiredInt(req, "number")
535-
if err != nil {
536-
return mcp.NewToolResultError(err.Error()), nil
537-
}
538-
ownerType, err := OptionalParam[string](req, "owner_type")
539-
if err != nil {
540-
return mcp.NewToolResultError(err.Error()), nil
541-
}
542-
if ownerType == "" {
543-
ownerType = "organization"
544-
}
545-
client, err := getClient(ctx)
546-
if err != nil {
547-
return mcp.NewToolResultError(err.Error()), nil
548-
}
549-
if ownerType == "user" {
550-
var q struct {
551-
User struct {
552-
Project struct {
553-
Items struct {
554-
Nodes []struct {
555-
ID githubv4.ID
556-
FieldValues struct {
557-
Nodes []struct {
558-
ProjectV2ItemFieldTextValue struct {
559-
Text githubv4.String
560-
Field struct {
561-
ProjectV2FieldCommon struct {
562-
Name githubv4.String
563-
} `graphql:"... on ProjectV2FieldCommon"`
564-
}
565-
} `graphql:"... on ProjectV2ItemFieldTextValue"`
566-
ProjectV2ItemFieldDateValue struct {
567-
Date githubv4.Date
568-
Field struct {
569-
ProjectV2FieldCommon struct {
570-
Name githubv4.String
571-
} `graphql:"... on ProjectV2FieldCommon"`
572-
}
573-
} `graphql:"... on ProjectV2ItemFieldDateValue"`
574-
ProjectV2ItemFieldSingleSelectValue struct {
575-
Name githubv4.String
576-
Field struct {
577-
ProjectV2FieldCommon struct {
578-
Name githubv4.String
579-
} `graphql:"... on ProjectV2FieldCommon"`
580-
}
581-
} `graphql:"... on ProjectV2ItemFieldSingleSelectValue"`
582-
}
583-
} `graphql:"fieldValues(first: 8)"`
584-
Content struct {
585-
DraftIssue struct {
586-
Title githubv4.String
587-
Body githubv4.String
588-
} `graphql:"... on DraftIssue"`
589-
Issue struct {
590-
Title githubv4.String
591-
Assignees struct {
592-
Nodes []struct {
593-
Login githubv4.String
594-
}
595-
} `graphql:"assignees(first: 10)"`
596-
} `graphql:"... on Issue"`
597-
PullRequest struct {
598-
Title githubv4.String
599-
Assignees struct {
600-
Nodes []struct {
601-
Login githubv4.String
602-
}
603-
} `graphql:"assignees(first: 10)"`
604-
} `graphql:"... on PullRequest"`
605-
}
606-
}
607-
} `graphql:"items(first: 100)"`
608-
} `graphql:"projectV2(number: $number)"`
609-
} `graphql:"user(login: $login)"`
521+
// FieldNameFragment defines the fields we want from a ProjectV2FieldCommon interface.
522+
type FieldNameFragment struct {
523+
Name githubv4.String
524+
}
525+
526+
// Field represents the 'field' interface on a project item's field value.
527+
// It uses an embedded struct with a graphql tag to act as an inline fragment.
528+
type Field struct {
529+
OnProjectV2FieldCommon FieldNameFragment `graphql:"... on ProjectV2FieldCommon"`
530+
}
531+
532+
// ProjectItem defines the structure of a single item within a project,
533+
// including its field values and content.
534+
type ProjectItem struct {
535+
ID githubv4.ID
536+
FieldValues struct {
537+
Nodes []struct {
538+
TypeName string `graphql:"__typename"`
539+
// Fragment for Text values
540+
OnTextValue struct {
541+
Text githubv4.String
542+
Field Field
543+
} `graphql:"... on ProjectV2ItemFieldTextValue"`
544+
// Fragment for Date values
545+
OnDateValue struct {
546+
Date githubv4.DateTime
547+
Field Field
548+
} `graphql:"... on ProjectV2ItemFieldDateValue"`
549+
// Fragment for Single Select values
550+
OnSingleSelectValue struct {
551+
Name githubv4.String
552+
Field Field
553+
} `graphql:"... on ProjectV2ItemFieldSingleSelectValue"`
554+
}
555+
} `graphql:"fieldValues(first: 8)"`
556+
Content struct {
557+
TypeName string `graphql:"__typename"`
558+
OnDraftIssue struct {
559+
Title githubv4.String
560+
Body githubv4.String
561+
} `graphql:"... on DraftIssue"`
562+
OnIssue struct {
563+
Title githubv4.String
564+
Assignees struct {
565+
Nodes []struct {
566+
Login githubv4.String
610567
}
611-
if err := client.Query(ctx, &q, map[string]any{"login": githubv4.String(owner), "number": githubv4.Int(number)}); err != nil {
612-
return mcp.NewToolResultError(err.Error()), nil
568+
} `graphql:"assignees(first: 10)"`
569+
} `graphql:"... on Issue"`
570+
OnPullRequest struct {
571+
Title githubv4.String
572+
Assignees struct {
573+
Nodes []struct {
574+
Login githubv4.String
613575
}
614-
return MarshalledTextResult(q), nil
615-
}
576+
} `graphql:"assignees(first: 10)"`
577+
} `graphql:"... on PullRequest"`
578+
}
579+
}
580+
581+
func GetProjectItems(getClient GetGQLClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
582+
return mcp.NewTool("get_project_items",
583+
mcp.WithDescription(t("TOOL_GET_PROJECT_ITEMS_DESCRIPTION", "Get items for a project")),
584+
mcp.WithToolAnnotation(mcp.ToolAnnotation{Title: t("TOOL_GET_PROJECT_ITEMS_USER_TITLE", "Get project items"), ReadOnlyHint: ToBoolPtr(true)}),
585+
mcp.WithString("owner", mcp.Required(), mcp.Description("Owner login")),
586+
mcp.WithString("owner_type", mcp.Description("Owner type"), mcp.Enum("user", "organization")),
587+
mcp.WithNumber("number", mcp.Required(), mcp.Description("Project number")),
588+
), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
589+
owner, err := RequiredParam[string](req, "owner")
590+
if err != nil {
591+
return mcp.NewToolResultError(err.Error()), nil
592+
}
593+
number, err := RequiredInt(req, "number")
594+
if err != nil {
595+
return mcp.NewToolResultError(err.Error()), nil
596+
}
597+
ownerType, err := OptionalParam[string](req, "owner_type")
598+
if err != nil {
599+
return mcp.NewToolResultError(err.Error()), nil
600+
}
601+
if ownerType == "" {
602+
ownerType = "organization"
603+
}
604+
605+
client, err := getClient(ctx)
606+
if err != nil {
607+
return mcp.NewToolResultError(err.Error()), nil
608+
}
609+
610+
variables := map[string]any{
611+
"login": githubv4.String(owner),
612+
"number": githubv4.Int(number),
613+
}
614+
615+
if ownerType == "user" {
616616
var q struct {
617-
Organization struct {
617+
User struct {
618618
Project struct {
619619
Items struct {
620-
Nodes []struct {
621-
ID githubv4.ID
622-
FieldValues struct {
623-
Nodes []struct {
624-
ProjectV2ItemFieldTextValue struct {
625-
Text githubv4.String
626-
Field struct {
627-
ProjectV2FieldCommon struct {
628-
Name githubv4.String
629-
} `graphql:"... on ProjectV2FieldCommon"`
630-
}
631-
} `graphql:"... on ProjectV2ItemFieldTextValue"`
632-
ProjectV2ItemFieldDateValue struct {
633-
Date githubv4.Date
634-
Field struct {
635-
ProjectV2FieldCommon struct {
636-
Name githubv4.String
637-
} `graphql:"... on ProjectV2FieldCommon"`
638-
}
639-
} `graphql:"... on ProjectV2ItemFieldDateValue"`
640-
ProjectV2ItemFieldSingleSelectValue struct {
641-
Name githubv4.String
642-
Field struct {
643-
ProjectV2FieldCommon struct {
644-
Name githubv4.String
645-
} `graphql:"... on ProjectV2FieldCommon"`
646-
}
647-
} `graphql:"... on ProjectV2ItemFieldSingleSelectValue"`
648-
}
649-
} `graphql:"fieldValues(first: 8)"`
650-
Content struct {
651-
DraftIssue struct {
652-
Title githubv4.String
653-
Body githubv4.String
654-
} `graphql:"... on DraftIssue"`
655-
Issue struct {
656-
Title githubv4.String
657-
Assignees struct {
658-
Nodes []struct {
659-
Login githubv4.String
660-
}
661-
} `graphql:"assignees(first: 10)"`
662-
} `graphql:"... on Issue"`
663-
PullRequest struct {
664-
Title githubv4.String
665-
Assignees struct {
666-
Nodes []struct {
667-
Login githubv4.String
668-
}
669-
} `graphql:"assignees(first: 10)"`
670-
} `graphql:"... on PullRequest"`
671-
}
672-
}
620+
Nodes []ProjectItem
673621
} `graphql:"items(first: 100)"`
674622
} `graphql:"projectV2(number: $number)"`
675-
} `graphql:"organization(login: $login)"`
623+
} `graphql:"user(login: $login)"`
676624
}
677-
if err := client.Query(ctx, &q, map[string]any{"login": githubv4.String(owner), "number": githubv4.Int(number)}); err != nil {
625+
if err := client.Query(ctx, &q, variables); err != nil {
678626
return mcp.NewToolResultError(err.Error()), nil
679627
}
680-
return MarshalledTextResult(q), nil
628+
return MarshalledTextResult(q.User.Project.Items), nil
629+
}
630+
631+
// This code is now reachable and syntactically correct.
632+
var q struct {
633+
Organization struct {
634+
Project struct {
635+
Items struct {
636+
Nodes []ProjectItem
637+
} `graphql:"items(first: 100)"`
638+
} `graphql:"projectV2(number: $number)"`
639+
} `graphql:"organization(login: $login)"`
681640
}
641+
if err := client.Query(ctx, &q, variables); err != nil {
642+
return mcp.NewToolResultError(err.Error()), nil
643+
}
644+
return MarshalledTextResult(q.Organization.Project.Items), nil
645+
}
682646
}
683647

684648
// CreateIssue creates an issue in a repository.
@@ -800,40 +764,54 @@ func UpdateProjectItemField(getClient GetGQLClientFn, t translations.Translation
800764
// CreateDraftIssue creates a draft issue in a project.
801765
func CreateDraftIssue(getClient GetGQLClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
802766
return mcp.NewTool("create_draft_issue",
803-
mcp.WithDescription(t("TOOL_CREATE_DRAFT_ISSUE_DESCRIPTION", "Create a draft issue in a project")),
804-
mcp.WithToolAnnotation(mcp.ToolAnnotation{Title: t("TOOL_CREATE_DRAFT_ISSUE_USER_TITLE", "Create draft issue"), ReadOnlyHint: ToBoolPtr(false)}),
805-
mcp.WithString("project_id", mcp.Required(), mcp.Description("Project ID")),
806-
mcp.WithString("title", mcp.Required(), mcp.Description("Issue title")),
807-
mcp.WithString("body", mcp.Description("Issue body")),
808-
), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
809-
projectID, err := RequiredParam[string](req, "project_id")
810-
if err != nil {
811-
return mcp.NewToolResultError(err.Error()), nil
812-
}
813-
title, err := RequiredParam[string](req, "title")
814-
if err != nil {
815-
return mcp.NewToolResultError(err.Error()), nil
816-
}
817-
body, err := OptionalParam[string](req, "body")
818-
if err != nil {
819-
return mcp.NewToolResultError(err.Error()), nil
820-
}
821-
client, err := getClient(ctx)
822-
if err != nil {
823-
return mcp.NewToolResultError(err.Error()), nil
824-
}
825-
input := githubv4.AddProjectV2DraftIssueInput{ProjectID: githubv4.ID(projectID), Title: githubv4.String(title)}
826-
if body != "" {
827-
input.Body = githubv4.NewString(githubv4.String(body))
828-
}
829-
var mut struct {
830-
AddProjectV2DraftIssue struct{ Item struct{ ID githubv4.ID } } `graphql:"addProjectV2DraftIssue(input: $input)"`
831-
}
832-
if err := client.Mutate(ctx, &mut, input, nil); err != nil {
833-
return mcp.NewToolResultError(err.Error()), nil
834-
}
835-
return MarshalledTextResult(mut), nil
767+
mcp.WithDescription(t("TOOL_CREATE_DRAFT_ISSUE_DESCRIPTION", "Create a draft issue in a project")),
768+
mcp.WithToolAnnotation(mcp.ToolAnnotation{Title: t("TOOL_CREATE_DRAFT_ISSUE_USER_TITLE", "Create draft issue"), ReadOnlyHint: ToBoolPtr(false)}),
769+
mcp.WithString("project_id", mcp.Required(), mcp.Description("Project ID")),
770+
mcp.WithString("title", mcp.Required(), mcp.Description("Issue title")),
771+
mcp.WithString("body", mcp.Description("Issue body")),
772+
), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
773+
projectID, err := RequiredParam[string](req, "project_id")
774+
if err != nil {
775+
return mcp.NewToolResultError(err.Error()), nil
836776
}
777+
title, err := RequiredParam[string](req, "title")
778+
if err != nil {
779+
return mcp.NewToolResultError(err.Error()), nil
780+
}
781+
body, err := OptionalParam[string](req, "body")
782+
if err != nil {
783+
return mcp.NewToolResultError(err.Error()), nil
784+
}
785+
786+
client, err := getClient(ctx)
787+
if err != nil {
788+
return mcp.NewToolResultError(err.Error()), nil
789+
}
790+
791+
input := githubv4.AddProjectV2DraftIssueInput{
792+
ProjectID: githubv4.ID(projectID),
793+
Title: githubv4.String(title),
794+
}
795+
if body != "" {
796+
input.Body = githubv4.NewString(githubv4.String(body))
797+
}
798+
799+
// CORRECTED: The payload field is 'projectItem', not 'item'.
800+
var mut struct {
801+
AddProjectV2DraftIssue struct {
802+
ProjectItem struct {
803+
ID githubv4.ID
804+
}
805+
} `graphql:"addProjectV2DraftIssue(input: $input)"`
806+
}
807+
808+
// The library requires a pointer to the mutation struct, the input, and variables (nil in this case).
809+
if err := client.Mutate(ctx, &mut, input, nil); err != nil {
810+
return mcp.NewToolResultError(err.Error()), nil
811+
}
812+
813+
return MarshalledTextResult(mut.AddProjectV2DraftIssue.ProjectItem), nil
814+
}
837815
}
838816

839817
// DeleteProjectItem removes an item from a project.

0 commit comments

Comments
 (0)