From bd91676b5b6c5d9e3a7c2748c8a4c19dba6928da Mon Sep 17 00:00:00 2001 From: Tom Elliott Date: Thu, 23 Oct 2025 18:57:54 -0400 Subject: [PATCH 1/6] update fields and prompt --- pkg/github/minimal_types.go | 40 ------- pkg/github/projects.go | 213 +++++++++++++++++++++++++++++------- 2 files changed, 173 insertions(+), 80 deletions(-) diff --git a/pkg/github/minimal_types.go b/pkg/github/minimal_types.go index 0a02dbcf6..f69fe423a 100644 --- a/pkg/github/minimal_types.go +++ b/pkg/github/minimal_types.go @@ -131,23 +131,6 @@ type MinimalProject struct { DeletedBy *MinimalUser `json:"deleted_by,omitempty"` } -type MinimalProjectItem struct { - ID *int64 `json:"id,omitempty"` - NodeID *string `json:"node_id,omitempty"` - Title *string `json:"title,omitempty"` - Description *string `json:"description,omitempty"` - ProjectNodeID *string `json:"project_node_id,omitempty"` - ContentNodeID *string `json:"content_node_id,omitempty"` - ProjectURL *string `json:"project_url,omitempty"` - ContentType *string `json:"content_type,omitempty"` - Creator *MinimalUser `json:"creator,omitempty"` - CreatedAt *github.Timestamp `json:"created_at,omitempty"` - UpdatedAt *github.Timestamp `json:"updated_at,omitempty"` - ArchivedAt *github.Timestamp `json:"archived_at,omitempty"` - ItemURL *string `json:"item_url,omitempty"` - Fields []*projectV2ItemFieldValue `json:"fields,omitempty"` -} - // Helper functions func convertToMinimalProject(fullProject *github.ProjectV2) *MinimalProject { @@ -186,29 +169,6 @@ func convertToMinimalUser(user *github.User) *MinimalUser { } } -func convertToMinimalProjectItem(item *projectV2Item) *MinimalProjectItem { - if item == nil { - return nil - } - - return &MinimalProjectItem{ - ID: item.ID, - NodeID: item.NodeID, - Title: item.Title, - Description: item.Description, - ProjectNodeID: item.ProjectNodeID, - ContentNodeID: item.ContentNodeID, - ProjectURL: item.ProjectURL, - ContentType: item.ContentType, - Creator: convertToMinimalUser(item.Creator), - CreatedAt: item.CreatedAt, - UpdatedAt: item.UpdatedAt, - ArchivedAt: item.ArchivedAt, - ItemURL: item.ItemURL, - Fields: item.Fields, - } -} - // convertToMinimalCommit converts a GitHub API RepositoryCommit to MinimalCommit func convertToMinimalCommit(commit *github.RepositoryCommit, includeDiffs bool) MinimalCommit { minimalCommit := MinimalCommit{ diff --git a/pkg/github/projects.go b/pkg/github/projects.go index 262288f83..3d3499131 100644 --- a/pkg/github/projects.go +++ b/pkg/github/projects.go @@ -404,7 +404,7 @@ func ListProjectItems(getClient GetClientFn, t translations.TranslationHelperFun mcp.Description("Number of results per page (max 100, default: 30)"), ), mcp.WithArray("fields", - mcp.Description("Specific list of field IDs to include in the response (e.g. [\"102589\", \"985201\", \"169875\"]). If not provided, only the title field is included."), + mcp.Description("Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included."), mcp.WithStringItems(), ), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -479,11 +479,8 @@ func ListProjectItems(getClient GetClientFn, t translations.TranslationHelperFun } return mcp.NewToolResultError(fmt.Sprintf("%s: %s", ProjectListFailedError, string(body))), nil } - minimalProjectItems := []MinimalProjectItem{} - for _, item := range projectItems { - minimalProjectItems = append(minimalProjectItems, *convertToMinimalProjectItem(&item)) - } - r, err := json.Marshal(minimalProjectItems) + + r, err := json.Marshal(projectItems) if err != nil { return nil, fmt.Errorf("failed to marshal response: %w", err) } @@ -517,7 +514,7 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc) mcp.Description("The item's ID."), ), mcp.WithArray("fields", - mcp.Description("Specific list of field IDs to include in the response (e.g. [\"102589\", \"985201\", \"169875\"]). If not provided, only the title field is included."), + mcp.Description("Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included."), mcp.WithStringItems(), ), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -589,7 +586,7 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc) } return mcp.NewToolResultError(fmt.Sprintf("failed to get project item: %s", string(body))), nil } - r, err := json.Marshal(convertToMinimalProjectItem(&projectItem)) + r, err := json.Marshal(projectItem) if err != nil { return nil, fmt.Errorf("failed to marshal response: %w", err) } @@ -691,7 +688,7 @@ func AddProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc) } return mcp.NewToolResultError(fmt.Sprintf("%s: %s", ProjectAddFailedError, string(body))), nil } - r, err := json.Marshal(convertToMinimalProjectItem(&addedItem)) + r, err := json.Marshal(addedItem) if err != nil { return nil, fmt.Errorf("failed to marshal response: %w", err) } @@ -725,7 +722,7 @@ func UpdateProjectItem(getClient GetClientFn, t translations.TranslationHelperFu ), mcp.WithObject("updated_field", mcp.Required(), - mcp.Description("Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set \"value\" to null. Example: {\"id\": 123456, \"value\": \"New Value\"}"), + mcp.Description("Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {id: 123456, value: New Value}"), ), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { owner, err := RequiredParam[string](req, "owner") @@ -796,7 +793,7 @@ func UpdateProjectItem(getClient GetClientFn, t translations.TranslationHelperFu } return mcp.NewToolResultError(fmt.Sprintf("%s: %s", ProjectUpdateFailedError, string(body))), nil } - r, err := json.Marshal(convertToMinimalProjectItem(&updatedItem)) + r, err := json.Marshal(updatedItem) if err != nil { return nil, fmt.Errorf("failed to marshal response: %w", err) } @@ -917,20 +914,33 @@ type projectV2ItemFieldValue struct { } type projectV2Item struct { - ID *int64 `json:"id,omitempty"` - Title *string `json:"title,omitempty"` - Description *string `json:"description,omitempty"` - NodeID *string `json:"node_id,omitempty"` - ProjectNodeID *string `json:"project_node_id,omitempty"` - ContentNodeID *string `json:"content_node_id,omitempty"` - ProjectURL *string `json:"project_url,omitempty"` - ContentType *string `json:"content_type,omitempty"` - Creator *github.User `json:"creator,omitempty"` - CreatedAt *github.Timestamp `json:"created_at,omitempty"` - UpdatedAt *github.Timestamp `json:"updated_at,omitempty"` - ArchivedAt *github.Timestamp `json:"archived_at,omitempty"` - ItemURL *string `json:"item_url,omitempty"` - Fields []*projectV2ItemFieldValue `json:"fields,omitempty"` + ArchivedAt *github.Timestamp `json:"archived_at,omitempty"` + Content *projectV2ItemContent `json:"content,omitempty"` + ContentType *string `json:"content_type,omitempty"` + CreatedAt *github.Timestamp `json:"created_at,omitempty"` + Creator *github.User `json:"creator,omitempty"` + Description *string `json:"description,omitempty"` + Fields []*projectV2ItemFieldValue `json:"fields,omitempty"` + ID *int64 `json:"id,omitempty"` + ItemURL *string `json:"item_url,omitempty"` + NodeID *string `json:"node_id,omitempty"` + ProjectURL *string `json:"project_url,omitempty"` + Title *string `json:"title,omitempty"` + UpdatedAt *github.Timestamp `json:"updated_at,omitempty"` +} + +type projectV2ItemContent struct { + Body *string `json:"body,omitempty"` + ClosedAt *github.Timestamp `json:"closed_at,omitempty"` + CreatedAt *github.Timestamp `json:"created_at,omitempty"` + ID *int64 `json:"id,omitempty"` + Number *int `json:"number,omitempty"` + Repository MinimalRepository `json:"repository,omitempty"` + State *string `json:"state,omitempty"` + StateReason *string `json:"stateReason,omitempty"` + Title *string `json:"title,omitempty"` + UpdatedAt *github.Timestamp `json:"updated_at,omitempty"` + URL *string `json:"url,omitempty"` } type paginationOptions struct { @@ -1017,49 +1027,172 @@ func addOptions(s string, opts any) (string, error) { func ManageProjectItemsPrompt(t translations.TranslationHelperFunc) (tool mcp.Prompt, handler server.PromptHandlerFunc) { return mcp.NewPrompt("ManageProjectItems", - mcp.WithPromptDescription(t("PROMPT_MANAGE_PROJECT_ITEMS_DESCRIPTION", "Guide for working with GitHub Projects, including listing projects, viewing fields, querying items, and updating field values.")), + mcp.WithPromptDescription(t("PROMPT_MANAGE_PROJECT_ITEMS_DESCRIPTION", "Interactive guide for managing GitHub Projects V2, including discovery, field management, querying, and updates.")), mcp.WithArgument("owner", mcp.ArgumentDescription("The owner of the project (user or organization name)"), mcp.RequiredArgument()), mcp.WithArgument("owner_type", mcp.ArgumentDescription("Type of owner: 'user' or 'org'"), mcp.RequiredArgument()), + mcp.WithArgument("task", mcp.ArgumentDescription("Optional: specific task to focus on (e.g., 'discover_projects', 'update_items', 'create_reports')")), ), func(_ context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { owner := request.Params.Arguments["owner"] ownerType := request.Params.Arguments["owner_type"] + task := "" + if t, exists := request.Params.Arguments["task"]; exists { + task = fmt.Sprintf("%v", t) + } + messages := []mcp.PromptMessage{ { - Role: "user", - Content: mcp.NewTextContent("You are an assistant helping users work with GitHub Projects V2. Your role is to help them discover projects, understand project fields, query items, and update field values on project items."), + Role: "system", + Content: mcp.NewTextContent("You are a GitHub Projects V2 management assistant. Your expertise includes:\n\n" + + "**Core Capabilities:**\n" + + "- Project discovery and field analysis\n" + + "- Item querying with advanced filters\n" + + "- Field value updates and management\n" + + "- Progress reporting and insights\n\n" + + "**Key Rules:**\n" + + "- ALWAYS use the 'query' parameter in **list_project_items** to filter results effectively\n" + + "- ALWAYS include 'fields' parameter with specific field IDs to retrieve field values\n" + + "- Use proper field IDs (not names) when updating items\n" + + "- Provide step-by-step workflows with concrete examples\n\n" + + "**Understanding Project Items:**\n" + + "- Project items reference underlying content (issues or pull requests)\n" + + "- Project tools provide: project fields, item metadata, and basic content info\n" + + "- For detailed information about an issue or pull request (comments, events, etc.), use issue/PR specific tools\n" + + "- The 'content' field in project items includes: repository, issue/PR number, title, state\n" + + "- Use this info to fetch full details: **get_issue**, **list_comments**, **list_issue_events**\n\n" + + "**Available Tools:**\n" + + "- **list_projects**: Discover available projects\n" + + "- **get_project**: Get detailed project information\n" + + "- **list_project_fields**: Get field definitions and IDs\n" + + "- **list_project_items**: Query items with filters and field selection\n" + + "- **get_project_item**: Get specific item details\n" + + "- **add_project_item**: Add issues/PRs to projects\n" + + "- **update_project_item**: Update field values\n" + + "- **delete_project_item**: Remove items from projects"), + }, + { + Role: "user", + Content: mcp.NewTextContent(fmt.Sprintf("I want to work with GitHub Projects for %s (owner_type: %s).%s\n\n"+ + "Help me get started with project management tasks.", + owner, + ownerType, + func() string { + if task != "" { + return fmt.Sprintf(" I'm specifically interested in: %s.", task) + } + return "" + }())), + }, + { + Role: "assistant", + Content: mcp.NewTextContent(fmt.Sprintf("Perfect! I'll help you manage GitHub Projects for %s. Let me guide you through the essential workflows.\n\n"+ + "**🔍 Step 1: Project Discovery**\n"+ + "First, let's see what projects are available using **list_projects**.", owner)), }, { Role: "user", - Content: mcp.NewTextContent(fmt.Sprintf("I want to work with projects owned by %s (owner_type: %s). Please help me understand what projects are available.", owner, ownerType)), + Content: mcp.NewTextContent("Great! After seeing the projects, I want to understand how to work with project fields and items."), }, { - Role: "assistant", - Content: mcp.NewTextContent(fmt.Sprintf("I'll help you explore the projects for %s. Let me start by listing the available projects.", owner)), + Role: "assistant", + Content: mcp.NewTextContent("**📋 Step 2: Understanding Project Structure**\n\n" + + "Once you select a project, I'll help you:\n\n" + + "1. **Get field information** using **list_project_fields**\n" + + " - Find field IDs, names, and data types\n" + + " - Understand available options for select fields\n" + + " - Identify required vs. optional fields\n\n" + + "2. **Query project items** using **list_project_items**\n" + + " - Filter by assignees: query=\"assignee:@me\"\n" + + " - Filter by status: query=\"status:In Progress\"\n" + + " - Filter by labels: query=\"label:bug\"\n" + + " - Include specific fields: fields=[\"198354254\", \"198354255\"]\n\n" + + "**💡 Pro Tip:** Always specify the 'fields' parameter to get field values, not just titles!"), }, { Role: "user", - Content: mcp.NewTextContent("Great! Once you show me the projects, I'd like to understand the fields available in a specific project."), + Content: mcp.NewTextContent("How do I update field values? What about the different field types?"), }, { - Role: "assistant", - Content: mcp.NewTextContent("Perfect! After showing you the projects, I can help you:\n\n1. 📋 List all fields in a project (using `list_project_fields`)\n2. 🔍 Get details about specific fields including their IDs, data types, and options\n3. 📊 Query project items with specific field values (using `list_project_items`)\n\nIMPORTANT: When querying project items, you must provide a list of field IDs in the 'fields' parameter to access field values. For example: fields=[\"198354254\", \"198354255\"] to get Status and Assignees. Without this parameter, only the title field is returned."), + Role: "assistant", + Content: mcp.NewTextContent("**✏️ Step 3: Updating Field Values**\n\n" + + "Use **update_project_item** with the updated_field parameter. The format varies by field type:\n\n" + + "**Text fields:**\n" + + "json\n" + + "{\"id\": 123456, \"value\": \"Updated text content\"}\n\n\n" + + "**Single-select fields:**\n" + + "json\n" + + "{\"id\": 198354254, \"value\": 18498754}\n\n" + + "*(Use option ID, not option name)*\n\n" + + "**Date fields:**\n" + + "json\n" + + "{\"id\": 789012, \"value\": \"2024-03-15\"}\n\n\n" + + "**Number fields:**\n" + + "json\n" + + "{\"id\": 345678, \"value\": 5}\n\n\n" + + "**Clear a field:**\n" + + "json\n" + + "{\"id\": 123456, \"value\": null}\n\n\n" + + "**⚠️ Important:** Use the internal project item_id (not issue/PR number) for updates!"), }, { Role: "user", - Content: mcp.NewTextContent("How do I update field values on project items?"), + Content: mcp.NewTextContent("Can you show me a complete workflow example?"), }, { - Role: "assistant", - Content: mcp.NewTextContent("To update field values on project items, you'll use the `update_project_item` tool. Here's what you need to know:\n\n1. **Get the item_id**: Use `list_project_items` to find the internal project item ID (not the issue/PR number)\n2. **Get the field_id**: Use `list_project_fields` to find the ID of the field you want to update\n3. **Update the field**: Call `update_project_item` with:\n - project_number: The project's number\n - item_id: The internal project item ID\n - updated_field: An object with {\"id\": , \"value\": }\n\nFor single_select fields, the value should be the option name (e.g., \"In Progress\").\nFor text fields, provide a string value.\nFor number fields, provide a numeric value.\nTo clear a field, set \"value\" to null."), + Role: "assistant", + Content: mcp.NewTextContent(fmt.Sprintf("**🔄 Complete Workflow Example**\n\n"+ + "Here's how to find and update your assigned items:\n\n"+ + "**Step 1:** Discover projects\n\n"+ + "**list_projects** owner=\"%s\" owner_type=\"%s\"\n\n\n"+ + "**Step 2:** Get project fields (using project #123)\n\n"+ + "**list_project_fields** owner=\"%s\" owner_type=\"%s\" project_number=123\n\n"+ + "*(Note the Status field ID, e.g., 198354254)*\n\n"+ + "**Step 3:** Query your assigned items\n\n"+ + "**list_project_items**\n"+ + " owner=\"%s\"\n"+ + " owner_type=\"%s\"\n"+ + " project_number=123\n"+ + " query=\"assignee:@me\"\n"+ + " fields=[\"198354254\", \"other_field_ids\"]\n\n\n"+ + "**Step 4:** Update item status\n\n"+ + "**update_project_item**\n"+ + " owner=\"%s\"\n"+ + " owner_type=\"%s\"\n"+ + " project_number=123\n"+ + " item_id=789123\n"+ + " updated_field={\"id\": 198354254, \"value\": 18498754}\n\n\n"+ + "Let me start by listing your projects now!", owner, ownerType, owner, ownerType, owner, ownerType, owner, ownerType)), }, { Role: "user", - Content: mcp.NewTextContent("Can you give me an example workflow for finding items and updating their status?"), + Content: mcp.NewTextContent("What if I need more details about the items, like recent comments or linked pull requests?"), }, { - Role: "assistant", - Content: mcp.NewTextContent(fmt.Sprintf("Absolutely! Here's a complete workflow:\n\n**Step 1: Find your project**\nUse `list_projects` with owner=\"%s\" and owner_type=\"%s\"\n\n**Step 2: Get the Status field ID**\nUse `list_project_fields` with the project_number from step 1\nLook for the field with name=\"Status\" and note its ID (e.g., 198354254)\n\n**Step 3: Query items with the Status field**\nUse `list_project_items` with fields=[\"198354254\"] to see current status values\nOptionally add a query parameter to filter items (e.g., query=\"assignee:@me\")\n\n**Step 4: Update an item's status**\nUse `update_project_item` with:\n- item_id: The ID from the item you want to update\n- updated_field: {\"id\": 198354254, \"value\": \"In Progress\"}\n\nLet me start by listing your projects now.", owner, ownerType)), + Role: "assistant", + Content: mcp.NewTextContent("**📝 Accessing Underlying Issue/PR Details**\n\n" + + "Project items contain basic content info, but for detailed information you need to use issue/PR tools:\n\n" + + "**From project items, extract:**\n" + + "- content.repository.name and content.repository.owner.login\n" + + "- content.number (the issue/PR number)\n" + + "- content_type (\"Issue\" or \"PullRequest\")\n\n" + + "**Then use these tools for details:**\n\n" + + "1. **Get full issue/PR details:**\n" + + " - **get_issue** owner=repo_owner repo=repo_name issue_number=123\n" + + " - Returns: full body, labels, assignees, milestone, etc.\n\n" + + "2. **Get recent comments:**\n" + + " - **list_comments** owner=repo_owner repo=repo_name issue_number=123\n" + + " - Add since parameter to filter recent comments\n\n" + + "3. **Get issue events:**\n" + + " - **list_issue_events** owner=repo_owner repo=repo_name issue_number=123\n" + + " - Shows timeline: assignments, label changes, status updates\n\n" + + "4. **For pull requests specifically:**\n" + + " - **get_pull_request** owner=repo_owner repo=repo_name pull_number=123\n" + + " - **list_pull_request_reviews** for review status\n\n" + + "**💡 Example:** To check for blockers in comments:\n" + + "1. Get project items with query=\"assignee:@me is:open\"\n" + + "2. For each item, extract repository and issue number from content\n" + + "3. Use **list_comments** to get recent comments\n" + + "4. Search comments for keywords like \"blocked\", \"blocker\", \"waiting\""), }, } return &mcp.GetPromptResult{ From d026d01925a720720d7f776e2d34534c306ba7dd Mon Sep 17 00:00:00 2001 From: Tom Elliott Date: Thu, 23 Oct 2025 19:10:12 -0400 Subject: [PATCH 2/6] update snapshots --- pkg/github/__toolsnaps__/get_project_item.snap | 2 +- pkg/github/__toolsnaps__/list_project_items.snap | 2 +- pkg/github/__toolsnaps__/update_project_item.snap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/github/__toolsnaps__/get_project_item.snap b/pkg/github/__toolsnaps__/get_project_item.snap index 36eb7bb63..222289c74 100644 --- a/pkg/github/__toolsnaps__/get_project_item.snap +++ b/pkg/github/__toolsnaps__/get_project_item.snap @@ -7,7 +7,7 @@ "inputSchema": { "properties": { "fields": { - "description": "Specific list of field IDs to include in the response (e.g. [\"102589\", \"985201\", \"169875\"]). If not provided, only the title field is included.", + "description": "Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included.", "items": { "type": "string" }, diff --git a/pkg/github/__toolsnaps__/list_project_items.snap b/pkg/github/__toolsnaps__/list_project_items.snap index ebc7d17df..192d17f95 100644 --- a/pkg/github/__toolsnaps__/list_project_items.snap +++ b/pkg/github/__toolsnaps__/list_project_items.snap @@ -7,7 +7,7 @@ "inputSchema": { "properties": { "fields": { - "description": "Specific list of field IDs to include in the response (e.g. [\"102589\", \"985201\", \"169875\"]). If not provided, only the title field is included.", + "description": "Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included.", "items": { "type": "string" }, diff --git a/pkg/github/__toolsnaps__/update_project_item.snap b/pkg/github/__toolsnaps__/update_project_item.snap index 96a8e749a..ba43ba6d0 100644 --- a/pkg/github/__toolsnaps__/update_project_item.snap +++ b/pkg/github/__toolsnaps__/update_project_item.snap @@ -27,7 +27,7 @@ "type": "number" }, "updated_field": { - "description": "Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set \"value\" to null. Example: {\"id\": 123456, \"value\": \"New Value\"}", + "description": "Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {id: 123456, value: New Value}", "properties": {}, "type": "object" } From 53c03b24a3d54ea38cc28bd39121a8b65be47b57 Mon Sep 17 00:00:00 2001 From: Tom Elliott Date: Thu, 23 Oct 2025 19:11:15 -0400 Subject: [PATCH 3/6] update docs --- README.md | 6 +++--- docs/remote-server.md | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ea93b8adb..660b36143 100644 --- a/README.md +++ b/README.md @@ -820,7 +820,7 @@ Options are: - `project_number`: The project's number. (number, required) - **get_project_item** - Get project item - - `fields`: Specific list of field IDs to include in the response (e.g. ["102589", "985201", "169875"]). If not provided, only the title field is included. (string[], optional) + - `fields`: Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included. (string[], optional) - `item_id`: The item's ID. (number, required) - `owner`: 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. (string, required) - `owner_type`: Owner type (string, required) @@ -833,7 +833,7 @@ Options are: - `project_number`: The project's number. (number, required) - **list_project_items** - List project items - - `fields`: Specific list of field IDs to include in the response (e.g. ["102589", "985201", "169875"]). If not provided, only the title field is included. (string[], optional) + - `fields`: Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included. (string[], optional) - `owner`: 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. (string, required) - `owner_type`: Owner type (string, required) - `per_page`: Number of results per page (max 100, default: 30) (number, optional) @@ -851,7 +851,7 @@ Options are: - `owner`: 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. (string, required) - `owner_type`: Owner type (string, required) - `project_number`: The project's number. (number, required) - - `updated_field`: Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set "value" to null. Example: {"id": 123456, "value": "New Value"} (object, required) + - `updated_field`: Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {id: 123456, value: New Value} (object, required) diff --git a/docs/remote-server.md b/docs/remote-server.md index 66c8be388..fa55168e5 100644 --- a/docs/remote-server.md +++ b/docs/remote-server.md @@ -19,8 +19,7 @@ Below is a table of available toolsets for the remote GitHub MCP Server. Each to | Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) | |----------------|--------------------------------------------------|-------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| default | Default GitHub MCP toolset (see [default toolset](../README.md#default-toolset)) | https://api.githubcopilot.com/mcp/ | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=github&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2F%22%7D) | [read-only](https://api.githubcopilot.com/mcp/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=github&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Freadonly%22%7D) | -| all | All available GitHub MCP tools | https://api.githubcopilot.com/mcp/x/all | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=github-all&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fall%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/all/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=github-all&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fall%2Freadonly%22%7D) | +| all | All available GitHub MCP tools | https://api.githubcopilot.com/mcp/ | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=github&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2F%22%7D) | [read-only](https://api.githubcopilot.com/mcp/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=github&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Freadonly%22%7D) | | Actions | GitHub Actions workflows and CI/CD operations | https://api.githubcopilot.com/mcp/x/actions | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-actions&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Factions%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/actions/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-actions&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Factions%2Freadonly%22%7D) | | Code Security | Code security related tools, such as GitHub Code Scanning | https://api.githubcopilot.com/mcp/x/code_security | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-code_security&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcode_security%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/code_security/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-code_security&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcode_security%2Freadonly%22%7D) | | Dependabot | Dependabot tools | https://api.githubcopilot.com/mcp/x/dependabot | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-dependabot&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fdependabot%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/dependabot/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-dependabot&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fdependabot%2Freadonly%22%7D) | From 263144157231cbb184ab66a18f36e49cf31beb79 Mon Sep 17 00:00:00 2001 From: Tom Elliott Date: Thu, 23 Oct 2025 19:20:07 -0400 Subject: [PATCH 4/6] pr feedback --- pkg/github/projects.go | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/pkg/github/projects.go b/pkg/github/projects.go index 3d3499131..57da7de4a 100644 --- a/pkg/github/projects.go +++ b/pkg/github/projects.go @@ -404,7 +404,7 @@ func ListProjectItems(getClient GetClientFn, t translations.TranslationHelperFun mcp.Description("Number of results per page (max 100, default: 30)"), ), mcp.WithArray("fields", - mcp.Description("Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included."), + mcp.Description("Specific list of field IDs to include in the response (e.g. [\"102589\", \"985201\", \"169875\"]). If not provided, only the title field is included."), mcp.WithStringItems(), ), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -514,7 +514,7 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc) mcp.Description("The item's ID."), ), mcp.WithArray("fields", - mcp.Description("Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included."), + mcp.Description("Specific list of field IDs to include in the response (e.g. [\"102589\", \"985201\", \"169875\"]). If not provided, only the title field is included."), mcp.WithStringItems(), ), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -722,7 +722,7 @@ func UpdateProjectItem(getClient GetClientFn, t translations.TranslationHelperFu ), mcp.WithObject("updated_field", mcp.Required(), - mcp.Description("Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {id: 123456, value: New Value}"), + mcp.Description("Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {\"id\": 123456, \"value\": \"New Value\"}"), ), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { owner, err := RequiredParam[string](req, "owner") @@ -1117,21 +1117,26 @@ func ManageProjectItemsPrompt(t translations.TranslationHelperFunc) (tool mcp.Pr Content: mcp.NewTextContent("**✏️ Step 3: Updating Field Values**\n\n" + "Use **update_project_item** with the updated_field parameter. The format varies by field type:\n\n" + "**Text fields:**\n" + - "json\n" + - "{\"id\": 123456, \"value\": \"Updated text content\"}\n\n\n" + + "```json\n" + + "{\"id\": 123456, \"value\": \"Updated text content\"}\n" + + "```\n\n" + "**Single-select fields:**\n" + - "json\n" + - "{\"id\": 198354254, \"value\": 18498754}\n\n" + + "```json\n" + + "{\"id\": 198354254, \"value\": 18498754}\n" + + "```\n" + "*(Use option ID, not option name)*\n\n" + "**Date fields:**\n" + - "json\n" + - "{\"id\": 789012, \"value\": \"2024-03-15\"}\n\n\n" + + "```json\n" + + "{\"id\": 789012, \"value\": \"2024-03-15\"}\n" + + "```\n\n" + "**Number fields:**\n" + - "json\n" + - "{\"id\": 345678, \"value\": 5}\n\n\n" + + "```json\n" + + "{\"id\": 345678, \"value\": 5}\n" + + "```\n\n" + "**Clear a field:**\n" + - "json\n" + - "{\"id\": 123456, \"value\": null}\n\n\n" + + "```json\n" + + "{\"id\": 123456, \"value\": null}\n" + + "```\n\n" + "**⚠️ Important:** Use the internal project item_id (not issue/PR number) for updates!"), }, { From a14c4326f7aa950b380b922372d50a66484ad0d1 Mon Sep 17 00:00:00 2001 From: Tom Elliott Date: Thu, 23 Oct 2025 19:26:01 -0400 Subject: [PATCH 5/6] update snapshots --- pkg/github/__toolsnaps__/get_project_item.snap | 2 +- pkg/github/__toolsnaps__/list_project_items.snap | 2 +- pkg/github/__toolsnaps__/update_project_item.snap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/github/__toolsnaps__/get_project_item.snap b/pkg/github/__toolsnaps__/get_project_item.snap index 222289c74..36eb7bb63 100644 --- a/pkg/github/__toolsnaps__/get_project_item.snap +++ b/pkg/github/__toolsnaps__/get_project_item.snap @@ -7,7 +7,7 @@ "inputSchema": { "properties": { "fields": { - "description": "Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included.", + "description": "Specific list of field IDs to include in the response (e.g. [\"102589\", \"985201\", \"169875\"]). If not provided, only the title field is included.", "items": { "type": "string" }, diff --git a/pkg/github/__toolsnaps__/list_project_items.snap b/pkg/github/__toolsnaps__/list_project_items.snap index 192d17f95..ebc7d17df 100644 --- a/pkg/github/__toolsnaps__/list_project_items.snap +++ b/pkg/github/__toolsnaps__/list_project_items.snap @@ -7,7 +7,7 @@ "inputSchema": { "properties": { "fields": { - "description": "Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included.", + "description": "Specific list of field IDs to include in the response (e.g. [\"102589\", \"985201\", \"169875\"]). If not provided, only the title field is included.", "items": { "type": "string" }, diff --git a/pkg/github/__toolsnaps__/update_project_item.snap b/pkg/github/__toolsnaps__/update_project_item.snap index ba43ba6d0..6c8648503 100644 --- a/pkg/github/__toolsnaps__/update_project_item.snap +++ b/pkg/github/__toolsnaps__/update_project_item.snap @@ -27,7 +27,7 @@ "type": "number" }, "updated_field": { - "description": "Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {id: 123456, value: New Value}", + "description": "Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {\"id\": 123456, \"value\": \"New Value\"}", "properties": {}, "type": "object" } From 071e8c62c7699f2e29187ccac32cea2dce3ef2d4 Mon Sep 17 00:00:00 2001 From: Tom Elliott Date: Thu, 23 Oct 2025 19:27:09 -0400 Subject: [PATCH 6/6] update docs --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 660b36143..1e37bc0e1 100644 --- a/README.md +++ b/README.md @@ -820,7 +820,7 @@ Options are: - `project_number`: The project's number. (number, required) - **get_project_item** - Get project item - - `fields`: Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included. (string[], optional) + - `fields`: Specific list of field IDs to include in the response (e.g. ["102589", "985201", "169875"]). If not provided, only the title field is included. (string[], optional) - `item_id`: The item's ID. (number, required) - `owner`: 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. (string, required) - `owner_type`: Owner type (string, required) @@ -833,7 +833,7 @@ Options are: - `project_number`: The project's number. (number, required) - **list_project_items** - List project items - - `fields`: Specific list of field IDs to include in the response (e.g. [102589, 985201, 169875]). If not provided, only the title field is included. (string[], optional) + - `fields`: Specific list of field IDs to include in the response (e.g. ["102589", "985201", "169875"]). If not provided, only the title field is included. (string[], optional) - `owner`: 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. (string, required) - `owner_type`: Owner type (string, required) - `per_page`: Number of results per page (max 100, default: 30) (number, optional) @@ -851,7 +851,7 @@ Options are: - `owner`: 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. (string, required) - `owner_type`: Owner type (string, required) - `project_number`: The project's number. (number, required) - - `updated_field`: Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {id: 123456, value: New Value} (object, required) + - `updated_field`: Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {"id": 123456, "value": "New Value"} (object, required)