Skip to content

Commit 3390032

Browse files
Merge branch 'main' into copilot/add-base-branch-specification
2 parents ced0c4c + c44ce2e commit 3390032

File tree

6 files changed

+41
-56
lines changed

6 files changed

+41
-56
lines changed

cmd/github-mcp-server/generate_docs.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -349,14 +349,13 @@ func generateRemoteToolsetsDoc() string {
349349

350350
// Add "all" toolset first (special case)
351351
allIcon := octiconImg("apps", "../")
352-
fmt.Fprintf(&buf, "| %s<br>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) |\n", allIcon)
352+
fmt.Fprintf(&buf, "| %s<br>`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) |\n", allIcon)
353353

354354
// AvailableToolsets() returns toolsets that have tools, sorted by ID
355355
// Exclude context (handled separately) and dynamic (internal only)
356356
for _, ts := range r.AvailableToolsets("context", "dynamic") {
357357
idStr := string(ts.ID)
358358

359-
formattedName := formatToolsetName(idStr)
360359
apiURL := fmt.Sprintf("https://api.githubcopilot.com/mcp/x/%s", idStr)
361360
readonlyURL := fmt.Sprintf("https://api.githubcopilot.com/mcp/x/%s/readonly", idStr)
362361

@@ -372,9 +371,9 @@ func generateRemoteToolsetsDoc() string {
372371
readonlyInstallLink := fmt.Sprintf("[Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-%s&config=%s)", idStr, readonlyConfig)
373372

374373
icon := octiconImg(ts.Icon, "../")
375-
fmt.Fprintf(&buf, "| %s<br>%s | %s | %s | %s | [read-only](%s) | %s |\n",
374+
fmt.Fprintf(&buf, "| %s<br>`%s` | %s | %s | %s | [read-only](%s) | %s |\n",
376375
icon,
377-
formattedName,
376+
idStr,
378377
ts.Description,
379378
apiURL,
380379
installLink,
@@ -397,7 +396,6 @@ func generateRemoteOnlyToolsetsDoc() string {
397396
for _, ts := range github.RemoteOnlyToolsets() {
398397
idStr := string(ts.ID)
399398

400-
formattedName := formatToolsetName(idStr)
401399
apiURL := fmt.Sprintf("https://api.githubcopilot.com/mcp/x/%s", idStr)
402400
readonlyURL := fmt.Sprintf("https://api.githubcopilot.com/mcp/x/%s/readonly", idStr)
403401

@@ -413,9 +411,9 @@ func generateRemoteOnlyToolsetsDoc() string {
413411
readonlyInstallLink := fmt.Sprintf("[Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-%s&config=%s)", idStr, readonlyConfig)
414412

415413
icon := octiconImg(ts.Icon, "../")
416-
fmt.Fprintf(&buf, "| %s<br>%s | %s | %s | %s | [read-only](%s) | %s |\n",
414+
fmt.Fprintf(&buf, "| %s<br>`%s` | %s | %s | %s | [read-only](%s) | %s |\n",
417415
icon,
418-
formattedName,
416+
idStr,
419417
ts.Description,
420418
apiURL,
421419
installLink,

pkg/github/repository_resource.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,16 @@ func GetRepositoryResourcePrContent(t translations.TranslationHelperFunc) invent
102102

103103
// repositoryResourceContentsHandlerFunc returns a ResourceHandlerFunc that creates handlers on-demand.
104104
func repositoryResourceContentsHandlerFunc(resourceURITemplate *uritemplate.Template) inventory.ResourceHandlerFunc {
105-
return func(deps any) mcp.ResourceHandler {
106-
d := deps.(ToolDependencies)
107-
return RepositoryResourceContentsHandler(d, resourceURITemplate)
105+
return func(_ any) mcp.ResourceHandler {
106+
return RepositoryResourceContentsHandler(resourceURITemplate)
108107
}
109108
}
110109

111110
// RepositoryResourceContentsHandler returns a handler function for repository content requests.
112-
func RepositoryResourceContentsHandler(deps ToolDependencies, resourceURITemplate *uritemplate.Template) mcp.ResourceHandler {
111+
// It retrieves ToolDependencies from the context at call time via MustDepsFromContext.
112+
func RepositoryResourceContentsHandler(resourceURITemplate *uritemplate.Template) mcp.ResourceHandler {
113113
return func(ctx context.Context, request *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) {
114+
deps := MustDepsFromContext(ctx)
114115
// Match the URI to extract parameters
115116
uriValues := resourceURITemplate.Match(request.Params.URI)
116117
if uriValues == nil {

pkg/github/repository_resource_test.go

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func Test_repositoryResourceContents(t *testing.T) {
2626
name string
2727
mockedClient *http.Client
2828
uri string
29-
handlerFn func(deps ToolDependencies) mcp.ResourceHandler
29+
handlerFn func() mcp.ResourceHandler
3030
expectedResponseType resourceResponseType
3131
expectError string
3232
expectedResult *mcp.ReadResourceResult
@@ -41,8 +41,8 @@ func Test_repositoryResourceContents(t *testing.T) {
4141
}),
4242
}),
4343
uri: "repo:///repo/contents/README.md",
44-
handlerFn: func(deps ToolDependencies) mcp.ResourceHandler {
45-
return RepositoryResourceContentsHandler(deps, repositoryResourceContentURITemplate)
44+
handlerFn: func() mcp.ResourceHandler {
45+
return RepositoryResourceContentsHandler(repositoryResourceContentURITemplate)
4646
},
4747
expectedResponseType: resourceResponseTypeText, // Ignored as error is expected
4848
expectError: "owner is required",
@@ -57,8 +57,8 @@ func Test_repositoryResourceContents(t *testing.T) {
5757
}),
5858
}),
5959
uri: "repo://owner//refs/heads/main/contents/README.md",
60-
handlerFn: func(deps ToolDependencies) mcp.ResourceHandler {
61-
return RepositoryResourceContentsHandler(deps, repositoryResourceBranchContentURITemplate)
60+
handlerFn: func() mcp.ResourceHandler {
61+
return RepositoryResourceContentsHandler(repositoryResourceBranchContentURITemplate)
6262
},
6363
expectedResponseType: resourceResponseTypeText, // Ignored as error is expected
6464
expectError: "repo is required",
@@ -73,8 +73,8 @@ func Test_repositoryResourceContents(t *testing.T) {
7373
}),
7474
}),
7575
uri: "repo://owner/repo/contents/data.png",
76-
handlerFn: func(deps ToolDependencies) mcp.ResourceHandler {
77-
return RepositoryResourceContentsHandler(deps, repositoryResourceContentURITemplate)
76+
handlerFn: func() mcp.ResourceHandler {
77+
return RepositoryResourceContentsHandler(repositoryResourceContentURITemplate)
7878
},
7979
expectedResponseType: resourceResponseTypeBlob,
8080
expectedResult: &mcp.ReadResourceResult{
@@ -94,8 +94,8 @@ func Test_repositoryResourceContents(t *testing.T) {
9494
}),
9595
}),
9696
uri: "repo://owner/repo/contents/README.md",
97-
handlerFn: func(deps ToolDependencies) mcp.ResourceHandler {
98-
return RepositoryResourceContentsHandler(deps, repositoryResourceContentURITemplate)
97+
handlerFn: func() mcp.ResourceHandler {
98+
return RepositoryResourceContentsHandler(repositoryResourceContentURITemplate)
9999
},
100100
expectedResponseType: resourceResponseTypeText,
101101
expectedResult: &mcp.ReadResourceResult{
@@ -117,8 +117,8 @@ func Test_repositoryResourceContents(t *testing.T) {
117117
}),
118118
}),
119119
uri: "repo://owner/repo/contents/pkg/github/actions.go",
120-
handlerFn: func(deps ToolDependencies) mcp.ResourceHandler {
121-
return RepositoryResourceContentsHandler(deps, repositoryResourceContentURITemplate)
120+
handlerFn: func() mcp.ResourceHandler {
121+
return RepositoryResourceContentsHandler(repositoryResourceContentURITemplate)
122122
},
123123
expectedResponseType: resourceResponseTypeText,
124124
expectedResult: &mcp.ReadResourceResult{
@@ -138,8 +138,8 @@ func Test_repositoryResourceContents(t *testing.T) {
138138
}),
139139
}),
140140
uri: "repo://owner/repo/refs/heads/main/contents/README.md",
141-
handlerFn: func(deps ToolDependencies) mcp.ResourceHandler {
142-
return RepositoryResourceContentsHandler(deps, repositoryResourceBranchContentURITemplate)
141+
handlerFn: func() mcp.ResourceHandler {
142+
return RepositoryResourceContentsHandler(repositoryResourceBranchContentURITemplate)
143143
},
144144
expectedResponseType: resourceResponseTypeText,
145145
expectedResult: &mcp.ReadResourceResult{
@@ -159,8 +159,8 @@ func Test_repositoryResourceContents(t *testing.T) {
159159
}),
160160
}),
161161
uri: "repo://owner/repo/refs/tags/v1.0.0/contents/README.md",
162-
handlerFn: func(deps ToolDependencies) mcp.ResourceHandler {
163-
return RepositoryResourceContentsHandler(deps, repositoryResourceTagContentURITemplate)
162+
handlerFn: func() mcp.ResourceHandler {
163+
return RepositoryResourceContentsHandler(repositoryResourceTagContentURITemplate)
164164
},
165165
expectedResponseType: resourceResponseTypeText,
166166
expectedResult: &mcp.ReadResourceResult{
@@ -180,8 +180,8 @@ func Test_repositoryResourceContents(t *testing.T) {
180180
}),
181181
}),
182182
uri: "repo://owner/repo/sha/abc123/contents/README.md",
183-
handlerFn: func(deps ToolDependencies) mcp.ResourceHandler {
184-
return RepositoryResourceContentsHandler(deps, repositoryResourceCommitContentURITemplate)
183+
handlerFn: func() mcp.ResourceHandler {
184+
return RepositoryResourceContentsHandler(repositoryResourceCommitContentURITemplate)
185185
},
186186
expectedResponseType: resourceResponseTypeText,
187187
expectedResult: &mcp.ReadResourceResult{
@@ -206,8 +206,8 @@ func Test_repositoryResourceContents(t *testing.T) {
206206
}),
207207
}),
208208
uri: "repo://owner/repo/refs/pull/42/head/contents/README.md",
209-
handlerFn: func(deps ToolDependencies) mcp.ResourceHandler {
210-
return RepositoryResourceContentsHandler(deps, repositoryResourcePrContentURITemplate)
209+
handlerFn: func() mcp.ResourceHandler {
210+
return RepositoryResourceContentsHandler(repositoryResourcePrContentURITemplate)
211211
},
212212
expectedResponseType: resourceResponseTypeText,
213213
expectedResult: &mcp.ReadResourceResult{
@@ -226,8 +226,8 @@ func Test_repositoryResourceContents(t *testing.T) {
226226
}),
227227
}),
228228
uri: "repo://owner/repo/contents/nonexistent.md",
229-
handlerFn: func(deps ToolDependencies) mcp.ResourceHandler {
230-
return RepositoryResourceContentsHandler(deps, repositoryResourceContentURITemplate)
229+
handlerFn: func() mcp.ResourceHandler {
230+
return RepositoryResourceContentsHandler(repositoryResourceContentURITemplate)
231231
},
232232
expectedResponseType: resourceResponseTypeText, // Ignored as error is expected
233233
expectError: "404 Not Found",
@@ -242,15 +242,16 @@ func Test_repositoryResourceContents(t *testing.T) {
242242
Client: client,
243243
RawClient: mockRawClient,
244244
}
245-
handler := tc.handlerFn(deps)
245+
ctx := ContextWithDeps(context.Background(), deps)
246+
handler := tc.handlerFn()
246247

247248
request := &mcp.ReadResourceRequest{
248249
Params: &mcp.ReadResourceParams{
249250
URI: tc.uri,
250251
},
251252
}
252253

253-
resp, err := handler(context.TODO(), request)
254+
resp, err := handler(ctx, request)
254255

255256
if tc.expectError != "" {
256257
require.ErrorContains(t, err, tc.expectError)

pkg/inventory/filters.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -203,17 +203,6 @@ func (r *Inventory) filterToolsByName(name string) []ServerTool {
203203
return result
204204
}
205205

206-
// filterResourcesByURI returns resource templates matching the given URI pattern.
207-
// Uses linear scan - optimized for single-lookup per-request scenarios (ForMCPRequest).
208-
func (r *Inventory) filterResourcesByURI(uri string) []ServerResourceTemplate {
209-
for i := range r.resourceTemplates {
210-
if r.resourceTemplates[i].Template.URITemplate == uri {
211-
return []ServerResourceTemplate{r.resourceTemplates[i]}
212-
}
213-
}
214-
return []ServerResourceTemplate{}
215-
}
216-
217206
// filterPromptsByName returns prompts matching the given name.
218207
// Uses linear scan - optimized for single-lookup per-request scenarios (ForMCPRequest).
219208
func (r *Inventory) filterPromptsByName(name string) []ServerPrompt {

pkg/inventory/registry.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ const (
9191
// - MCPMethodToolsList: All available tools (no resources/prompts)
9292
// - MCPMethodToolsCall: Only the named tool
9393
// - MCPMethodResourcesList, MCPMethodResourcesTemplatesList: All available resources (no tools/prompts)
94-
// - MCPMethodResourcesRead: Only the named resource template
94+
// - MCPMethodResourcesRead: All resources (SDK handles URI template matching)
9595
// - MCPMethodPromptsList: All available prompts (no tools/resources)
9696
// - MCPMethodPromptsGet: Only the named prompt
9797
// - Unknown methods: Empty (no items registered)
@@ -134,10 +134,8 @@ func (r *Inventory) ForMCPRequest(method string, itemName string) *Inventory {
134134
case MCPMethodResourcesList, MCPMethodResourcesTemplatesList:
135135
result.tools, result.prompts = nil, nil
136136
case MCPMethodResourcesRead:
137+
// Keep all resources registered - SDK handles URI template matching internally
137138
result.tools, result.prompts = nil, nil
138-
if itemName != "" {
139-
result.resourceTemplates = r.filterResourcesByURI(itemName)
140-
}
141139
case MCPMethodPromptsList:
142140
result.tools, result.resourceTemplates = nil, nil
143141
case MCPMethodPromptsGet:

pkg/inventory/registry_test.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -775,17 +775,15 @@ func TestForMCPRequest_ResourcesRead(t *testing.T) {
775775
}
776776

777777
reg := NewBuilder().SetResources(resources).WithToolsets([]string{"all"}).Build()
778-
filtered := reg.ForMCPRequest(MCPMethodResourcesRead, "repo://{owner}/{repo}")
778+
// Pass a concrete URI - all resources remain registered, SDK handles matching
779+
filtered := reg.ForMCPRequest(MCPMethodResourcesRead, "repo://owner/repo")
779780

781+
// All resources should be available - SDK handles URI template matching internally
780782
available := filtered.AvailableResourceTemplates(context.Background())
781-
if len(available) != 1 {
782-
t.Fatalf("Expected 1 resource for resources/read, got %d", len(available))
783-
}
784-
if available[0].Template.URITemplate != "repo://{owner}/{repo}" {
785-
t.Errorf("Expected URI template 'repo://{owner}/{repo}', got %q", available[0].Template.URITemplate)
783+
if len(available) != 2 {
784+
t.Fatalf("Expected 2 resources for resources/read (SDK handles matching), got %d", len(available))
786785
}
787786
}
788-
789787
func TestForMCPRequest_PromptsList(t *testing.T) {
790788
tools := []ServerTool{
791789
mockTool("tool1", "repos", true),

0 commit comments

Comments
 (0)