From b677d373b80c111cde4439171a84374a7d89bef6 Mon Sep 17 00:00:00 2001
From: Kelsey Myers <52179263+kelsey-myers@users.noreply.github.com>
Date: Mon, 15 Jun 2026 03:42:08 -0700
Subject: [PATCH 1/3] Add repo-scoped support to list_issue_types tool

---
 README.md                                     |  7 +--
 pkg/errors/error_test.go                      |  7 ++-
 .../__toolsnaps__/list_issue_types.snap       |  8 ++-
 pkg/github/issues.go                          | 49 +++++++++++++++++--
 pkg/github/issues_test.go                     | 24 +++++++++
 5 files changed, 82 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index dc063f22c..857fa20bc 100644
--- a/README.md
+++ b/README.md
@@ -875,9 +875,10 @@ The following sets of tools are available:
   - `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
 
 - **list_issue_types** - List available issue types
-  - **Required OAuth Scopes**: `read:org`
-  - **Accepted OAuth Scopes**: `admin:org`, `read:org`, `write:org`
-  - `owner`: The organization owner of the repository (string, required)
+  - **Required OAuth Scopes**: `repo`, `read:org`
+  - **Accepted OAuth Scopes**: `admin:org`, `read:org`, `repo`, `write:org`
+  - `owner`: The account owner of the repository or organization. (string, required)
+  - `repo`: The name of the repository. When provided, returns issue types for this specific repository. When omitted, returns org-level issue types directly. (string, optional)
 
 - **list_issues** - List issues
   - **Required OAuth Scopes**: `repo`
diff --git a/pkg/errors/error_test.go b/pkg/errors/error_test.go
index 77ceb2137..3c899b6b5 100644
--- a/pkg/errors/error_test.go
+++ b/pkg/errors/error_test.go
@@ -3,13 +3,13 @@ package errors
 import (
 	"context"
 	"fmt"
-	"net/http"
-	"testing"
-	"time"
 	"github.com/google/go-github/v87/github"
 	"github.com/modelcontextprotocol/go-sdk/mcp"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+	"net/http"
+	"testing"
+	"time"
 )
 
 func TestGitHubErrorContext(t *testing.T) {
@@ -687,4 +687,3 @@ func TestNewGitHubAPIErrorResponse_RateLimits(t *testing.T) {
 		assert.Contains(t, text, "validation failed")
 	})
 }
-
diff --git a/pkg/github/__toolsnaps__/list_issue_types.snap b/pkg/github/__toolsnaps__/list_issue_types.snap
index f1f1377a8..283cb5a8d 100644
--- a/pkg/github/__toolsnaps__/list_issue_types.snap
+++ b/pkg/github/__toolsnaps__/list_issue_types.snap
@@ -3,11 +3,15 @@
     "readOnlyHint": true,
     "title": "List available issue types"
   },
-  "description": "List supported issue types for repository owner (organization).",
+  "description": "List supported issue types for a repository or its owner organization. When repo is omitted, returns org-level issue types directly.",
   "inputSchema": {
     "properties": {
       "owner": {
-        "description": "The organization owner of the repository",
+        "description": "The account owner of the repository or organization.",
+        "type": "string"
+      },
+      "repo": {
+        "description": "The name of the repository. When provided, returns issue types for this specific repository. When omitted, returns org-level issue types directly.",
         "type": "string"
       }
     },
diff --git a/pkg/github/issues.go b/pkg/github/issues.go
index 27fc0a4ab..b0f6617e6 100644
--- a/pkg/github/issues.go
+++ b/pkg/github/issues.go
@@ -1067,13 +1067,14 @@ func GetIssueLabels(ctx context.Context, client *githubv4.Client, owner string,
 	return utils.NewToolResultText(string(out)), nil
 }
 
-// ListIssueTypes creates a tool to list defined issue types for an organization. This can be used to understand supported issue type values for creating or updating issues.
+// ListIssueTypes creates a tool to list defined issue types for an organization or repository.
+// This can be used to understand supported issue type values for creating or updating issues.
 func ListIssueTypes(t translations.TranslationHelperFunc) inventory.ServerTool {
 	return NewTool(
 		ToolsetMetadataIssues,
 		mcp.Tool{
 			Name:        "list_issue_types",
-			Description: t("TOOL_LIST_ISSUE_TYPES_FOR_ORG", "List supported issue types for repository owner (organization)."),
+			Description: t("TOOL_LIST_ISSUE_TYPES_FOR_ORG", "List supported issue types for a repository or its owner organization. When repo is omitted, returns org-level issue types directly."),
 			Annotations: &mcp.ToolAnnotations{
 				Title:        t("TOOL_LIST_ISSUE_TYPES_USER_TITLE", "List available issue types"),
 				ReadOnlyHint: true,
@@ -1083,23 +1084,63 @@ func ListIssueTypes(t translations.TranslationHelperFunc) inventory.ServerTool {
 				Properties: map[string]*jsonschema.Schema{
 					"owner": {
 						Type:        "string",
-						Description: "The organization owner of the repository",
+						Description: "The account owner of the repository or organization.",
+					},
+					"repo": {
+						Type:        "string",
+						Description: "The name of the repository. When provided, returns issue types for this specific repository. When omitted, returns org-level issue types directly.",
 					},
 				},
 				Required: []string{"owner"},
 			},
 		},
-		[]scopes.Scope{scopes.ReadOrg},
+		[]scopes.Scope{scopes.Repo, scopes.ReadOrg},
 		func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) {
 			owner, err := RequiredParam[string](args, "owner")
 			if err != nil {
 				return utils.NewToolResultError(err.Error()), nil, nil
 			}
+			repo, err := OptionalParam[string](args, "repo")
+			if err != nil {
+				return utils.NewToolResultError(err.Error()), nil, nil
+			}
 
 			client, err := deps.GetClient(ctx)
 			if err != nil {
 				return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil
 			}
+
+			if repo != "" {
+				apiURL := fmt.Sprintf("repos/%s/%s/issue-types", owner, repo)
+				req, err := client.NewRequest(ctx, "GET", apiURL, nil)
+				if err != nil {
+					return utils.NewToolResultErrorFromErr("failed to create request", err), nil, nil
+				}
+				var issueTypes []*github.IssueType
+				resp, err := client.Do(req, &issueTypes)
+				if err != nil {
+					return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to list issue types", resp, err), nil, nil
+				}
+				defer func() { _ = resp.Body.Close() }()
+
+				if resp.StatusCode != http.StatusOK {
+					body, err := io.ReadAll(resp.Body)
+					if err != nil {
+						return utils.NewToolResultErrorFromErr("failed to read response body", err), nil, nil
+					}
+					return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to list issue types", resp, body), nil, nil
+				}
+
+				r, err := json.Marshal(issueTypes)
+				if err != nil {
+					return utils.NewToolResultErrorFromErr("failed to marshal issue types", err), nil, nil
+				}
+
+				result := utils.NewToolResultText(string(r))
+				result = attachRepoVisibilityIFCLabelLazy(ctx, deps, owner, repo, result, ifc.LabelRepoMetadata)
+				return result, nil, nil
+			}
+
 			issueTypes, resp, err := client.Organizations.ListIssueTypes(ctx, owner)
 			if err != nil {
 				return utils.NewToolResultErrorFromErr("failed to list issue types", err), nil, nil
diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go
index 7e47cdb52..324d741c2 100644
--- a/pkg/github/issues_test.go
+++ b/pkg/github/issues_test.go
@@ -4802,6 +4802,30 @@ func Test_ListIssueTypes(t *testing.T) {
 			expectError:    false, // This should be handled by parameter validation, error returned in result
 			expectedErrMsg: "missing required parameter: owner",
 		},
+		{
+			name: "successful repo issue types retrieval",
+			mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
+				"GET /repos/testorg/testrepo/issue-types": mockResponse(t, http.StatusOK, mockIssueTypes),
+			}),
+			requestArgs: map[string]any{
+				"owner": "testorg",
+				"repo":  "testrepo",
+			},
+			expectError:        false,
+			expectedIssueTypes: mockIssueTypes,
+		},
+		{
+			name: "repo not found",
+			mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
+				"GET /repos/testorg/nonexistent/issue-types": mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`),
+			}),
+			requestArgs: map[string]any{
+				"owner": "testorg",
+				"repo":  "nonexistent",
+			},
+			expectError:    true,
+			expectedErrMsg: "failed to list issue types",
+		},
 	}
 
 	for _, tc := range tests {

From 096cdb51282573561ef9e51679ab5c1b33890c16 Mon Sep 17 00:00:00 2001
From: Kelsey Myers <52179263+kelsey-myers@users.noreply.github.com>
Date: Mon, 15 Jun 2026 07:59:51 -0700
Subject: [PATCH 2/3] Render multi-scope tools as "any of" in generated docs

---
 README.md                              |  2 +-
 cmd/github-mcp-server/generate_docs.go | 10 +++++++++-
 docs/feature-flags.md                  |  2 +-
 docs/insiders-features.md              |  2 +-
 4 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 857fa20bc..c5e97ae77 100644
--- a/README.md
+++ b/README.md
@@ -875,7 +875,7 @@ The following sets of tools are available:
   - `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
 
 - **list_issue_types** - List available issue types
-  - **Required OAuth Scopes**: `repo`, `read:org`
+  - **Required OAuth Scopes (any of)**: `repo`, `read:org`
   - **Accepted OAuth Scopes**: `admin:org`, `read:org`, `repo`, `write:org`
   - `owner`: The account owner of the repository or organization. (string, required)
   - `repo`: The name of the repository. When provided, returns issue types for this specific repository. When omitted, returns org-level issue types directly. (string, optional)
diff --git a/cmd/github-mcp-server/generate_docs.go b/cmd/github-mcp-server/generate_docs.go
index 78ed8361a..212851c50 100644
--- a/cmd/github-mcp-server/generate_docs.go
+++ b/cmd/github-mcp-server/generate_docs.go
@@ -221,7 +221,15 @@ func writeToolDoc(buf *strings.Builder, tool inventory.ServerTool) {
 
 	// OAuth scopes if present
 	if len(tool.RequiredScopes) > 0 {
-		fmt.Fprintf(buf, "  - **Required OAuth Scopes**: `%s`\n", strings.Join(tool.RequiredScopes, "`, `"))
+		// Scope filtering uses "any of" semantics (see scopes.HasRequiredScopes),
+		// so when multiple required scopes are listed, render them as alternatives
+		// rather than implying all are required.
+		scopeList := "`" + strings.Join(tool.RequiredScopes, "`, `") + "`"
+		if len(tool.RequiredScopes) > 1 {
+			fmt.Fprintf(buf, "  - **Required OAuth Scopes (any of)**: %s\n", scopeList)
+		} else {
+			fmt.Fprintf(buf, "  - **Required OAuth Scopes**: %s\n", scopeList)
+		}
 
 		// Only show accepted scopes if they differ from required scopes
 		if len(tool.AcceptedScopes) > 0 && !scopesEqual(tool.RequiredScopes, tool.AcceptedScopes) {
diff --git a/docs/feature-flags.md b/docs/feature-flags.md
index cb02463a1..6906b8d91 100644
--- a/docs/feature-flags.md
+++ b/docs/feature-flags.md
@@ -95,7 +95,7 @@ runtime behavior (such as output formatting) won't appear here.
   - `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
 
 - **list_issue_fields** - List issue fields
-  - **Required OAuth Scopes**: `repo`, `read:org`
+  - **Required OAuth Scopes (any of)**: `repo`, `read:org`
   - **Accepted OAuth Scopes**: `admin:org`, `read:org`, `repo`, `write:org`
   - `owner`: The account owner of the repository or organization. The name is not case sensitive. (string, required)
   - `repo`: The name of the repository. When provided, returns fields for this specific repository (inherited from its organization). When omitted, returns org-level fields directly. (string, optional)
diff --git a/docs/insiders-features.md b/docs/insiders-features.md
index 2277f0c8e..677e52208 100644
--- a/docs/insiders-features.md
+++ b/docs/insiders-features.md
@@ -89,7 +89,7 @@ The list below is generated from the Go source. It covers tool **inventory and s
   - `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
 
 - **list_issue_fields** - List issue fields
-  - **Required OAuth Scopes**: `repo`, `read:org`
+  - **Required OAuth Scopes (any of)**: `repo`, `read:org`
   - **Accepted OAuth Scopes**: `admin:org`, `read:org`, `repo`, `write:org`
   - `owner`: The account owner of the repository or organization. The name is not case sensitive. (string, required)
   - `repo`: The name of the repository. When provided, returns fields for this specific repository (inherited from its organization). When omitted, returns org-level fields directly. (string, optional)

From 9fa8dce3a733bd8a037a1eca2a6e0f13e6835751 Mon Sep 17 00:00:00 2001
From: Kelsey Myers <52179263+kelsey-myers@users.noreply.github.com>
Date: Wed, 17 Jun 2026 08:39:56 -0700
Subject: [PATCH 3/3] Clarify issue type field description for repo-scoped
 list_issue_types

---
 README.md                                                     | 2 +-
 docs/feature-flags.md                                         | 4 ++--
 docs/insiders-features.md                                     | 4 ++--
 pkg/github/__toolsnaps__/issue_write.snap                     | 2 +-
 .../__toolsnaps__/issue_write_ff_remote_mcp_issue_fields.snap | 2 +-
 pkg/github/issues.go                                          | 4 ++--
 6 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/README.md b/README.md
index c5e97ae77..10ec472c1 100644
--- a/README.md
+++ b/README.md
@@ -872,7 +872,7 @@ The following sets of tools are available:
   - `state`: New state (string, optional)
   - `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional)
   - `title`: Issue title (string, optional)
-  - `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
+  - `type`: Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
 
 - **list_issue_types** - List available issue types
   - **Required OAuth Scopes (any of)**: `repo`, `read:org`
diff --git a/docs/feature-flags.md b/docs/feature-flags.md
index 6906b8d91..65f716bcc 100644
--- a/docs/feature-flags.md
+++ b/docs/feature-flags.md
@@ -69,7 +69,7 @@ runtime behavior (such as output formatting) won't appear here.
   - `state`: New state (string, optional)
   - `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional)
   - `title`: Issue title (string, optional)
-  - `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
+  - `type`: Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
 
 ### `remote_mcp_issue_fields`
 
@@ -92,7 +92,7 @@ runtime behavior (such as output formatting) won't appear here.
   - `state`: New state (string, optional)
   - `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional)
   - `title`: Issue title (string, optional)
-  - `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
+  - `type`: Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
 
 - **list_issue_fields** - List issue fields
   - **Required OAuth Scopes (any of)**: `repo`, `read:org`
diff --git a/docs/insiders-features.md b/docs/insiders-features.md
index 677e52208..dc14a2b36 100644
--- a/docs/insiders-features.md
+++ b/docs/insiders-features.md
@@ -63,7 +63,7 @@ The list below is generated from the Go source. It covers tool **inventory and s
   - `state`: New state (string, optional)
   - `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional)
   - `title`: Issue title (string, optional)
-  - `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
+  - `type`: Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
 
 ### `remote_mcp_issue_fields`
 
@@ -86,7 +86,7 @@ The list below is generated from the Go source. It covers tool **inventory and s
   - `state`: New state (string, optional)
   - `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional)
   - `title`: Issue title (string, optional)
-  - `type`: Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
+  - `type`: Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string, optional)
 
 - **list_issue_fields** - List issue fields
   - **Required OAuth Scopes (any of)**: `repo`, `read:org`
diff --git a/pkg/github/__toolsnaps__/issue_write.snap b/pkg/github/__toolsnaps__/issue_write.snap
index 88b01f08f..20529e0ce 100644
--- a/pkg/github/__toolsnaps__/issue_write.snap
+++ b/pkg/github/__toolsnaps__/issue_write.snap
@@ -82,7 +82,7 @@
         "type": "string"
       },
       "type": {
-        "description": "Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter.",
+        "description": "Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter.",
         "type": "string"
       }
     },
diff --git a/pkg/github/__toolsnaps__/issue_write_ff_remote_mcp_issue_fields.snap b/pkg/github/__toolsnaps__/issue_write_ff_remote_mcp_issue_fields.snap
index 332a4de3e..da6a28688 100644
--- a/pkg/github/__toolsnaps__/issue_write_ff_remote_mcp_issue_fields.snap
+++ b/pkg/github/__toolsnaps__/issue_write_ff_remote_mcp_issue_fields.snap
@@ -118,7 +118,7 @@
         "type": "string"
       },
       "type": {
-        "description": "Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter.",
+        "description": "Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter.",
         "type": "string"
       }
     },
diff --git a/pkg/github/issues.go b/pkg/github/issues.go
index b0f6617e6..6d72fc3de 100644
--- a/pkg/github/issues.go
+++ b/pkg/github/issues.go
@@ -1897,7 +1897,7 @@ Options are:
 					},
 					"type": {
 						Type:        "string",
-						Description: "Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter.",
+						Description: "Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter.",
 					},
 					"state": {
 						Type:        "string",
@@ -2171,7 +2171,7 @@ Options are:
 					},
 					"type": {
 						Type:        "string",
-						Description: "Type of this issue. Only use if the repository has issue types configured. Use list_issue_types tool to get valid type values for the organization. If the repository doesn't support issue types, omit this parameter.",
+						Description: "Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter.",
 					},
 					"state": {
 						Type:        "string",
