From a179ed7c92bbf0a05ed95fffd76a9aec3b79c064 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Tue, 28 Jul 2026 17:21:35 +0200 Subject: [PATCH 1/3] build(deps): bump modelcontextprotocol/go-sdk to v1.7.0 Move from the v1.7.0-pre.3 pre-release to the final v1.7.0 release, which consolidates the pre-releases with no further changes. Regenerate the third-party license files to reflect the new version tag. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 95b8432c-f280-472e-a242-d3ca6dc31f19 --- go.mod | 2 +- go.sum | 4 ++-- third-party-licenses.darwin.md | 4 ++-- third-party-licenses.linux.md | 4 ++-- third-party-licenses.windows.md | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 1d8801f5a0..0e7a554ef7 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/josephburnett/jd/v2 v2.5.0 github.com/lithammer/fuzzysearch v1.1.8 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/modelcontextprotocol/go-sdk v1.7.0-pre.3 + github.com/modelcontextprotocol/go-sdk v1.7.0 github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021 github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 diff --git a/go.sum b/go.sum index f6a655d510..0923851dbb 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,8 @@ github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8 github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= -github.com/modelcontextprotocol/go-sdk v1.7.0-pre.3 h1:SEAY9IduDif4iApnZgpFkjFIdo3askSGZVbZIYyTy6I= -github.com/modelcontextprotocol/go-sdk v1.7.0-pre.3/go.mod h1:dL7u98E/zjJTGzEq+j30jQ8K2k1mb6LeAH4inEcSGts= +github.com/modelcontextprotocol/go-sdk v1.7.0 h1:yqjY2dsbKAC0LSuWZVBMrHgiG8ukXv6NRo0JiALay44= +github.com/modelcontextprotocol/go-sdk v1.7.0/go.mod h1:dL7u98E/zjJTGzEq+j30jQ8K2k1mb6LeAH4inEcSGts= github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021 h1:31Y+Yu373ymebRdJN1cWLLooHH8xAr0MhKTEJGV/87g= github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021/go.mod h1:WERUkUryfUWlrHnFSO/BEUZ+7Ns8aZy7iVOGewxKzcc= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= diff --git a/third-party-licenses.darwin.md b/third-party-licenses.darwin.md index 2bf5e86eae..b06ea0832d 100644 --- a/third-party-licenses.darwin.md +++ b/third-party-licenses.darwin.md @@ -24,8 +24,8 @@ The following packages are included for the amd64, arm64 architectures. - [github.com/josephburnett/jd/v2](https://pkg.go.dev/github.com/josephburnett/jd/v2) ([MIT](https://github.com/josephburnett/jd/blob/v2.5.0/v2/LICENSE)) - [github.com/lithammer/fuzzysearch/fuzzy](https://pkg.go.dev/github.com/lithammer/fuzzysearch/fuzzy) ([MIT](https://github.com/lithammer/fuzzysearch/blob/v1.1.8/LICENSE)) - [github.com/microcosm-cc/bluemonday](https://pkg.go.dev/github.com/microcosm-cc/bluemonday) ([BSD-3-Clause](https://github.com/microcosm-cc/bluemonday/blob/v1.0.27/LICENSE.md)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0-pre.3/LICENSE)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0-pre.3/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) - [github.com/muesli/cache2go](https://pkg.go.dev/github.com/muesli/cache2go) ([BSD-3-Clause](https://github.com/muesli/cache2go/blob/518229cd8021/LICENSE.txt)) - [github.com/pelletier/go-toml/v2](https://pkg.go.dev/github.com/pelletier/go-toml/v2) ([MIT](https://github.com/pelletier/go-toml/blob/v2.2.4/LICENSE)) - [github.com/sagikazarmark/locafero](https://pkg.go.dev/github.com/sagikazarmark/locafero) ([MIT](https://github.com/sagikazarmark/locafero/blob/v0.11.0/LICENSE)) diff --git a/third-party-licenses.linux.md b/third-party-licenses.linux.md index 4caa5f58b2..6773ff673e 100644 --- a/third-party-licenses.linux.md +++ b/third-party-licenses.linux.md @@ -24,8 +24,8 @@ The following packages are included for the 386, amd64, arm64 architectures. - [github.com/josephburnett/jd/v2](https://pkg.go.dev/github.com/josephburnett/jd/v2) ([MIT](https://github.com/josephburnett/jd/blob/v2.5.0/v2/LICENSE)) - [github.com/lithammer/fuzzysearch/fuzzy](https://pkg.go.dev/github.com/lithammer/fuzzysearch/fuzzy) ([MIT](https://github.com/lithammer/fuzzysearch/blob/v1.1.8/LICENSE)) - [github.com/microcosm-cc/bluemonday](https://pkg.go.dev/github.com/microcosm-cc/bluemonday) ([BSD-3-Clause](https://github.com/microcosm-cc/bluemonday/blob/v1.0.27/LICENSE.md)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0-pre.3/LICENSE)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0-pre.3/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) - [github.com/muesli/cache2go](https://pkg.go.dev/github.com/muesli/cache2go) ([BSD-3-Clause](https://github.com/muesli/cache2go/blob/518229cd8021/LICENSE.txt)) - [github.com/pelletier/go-toml/v2](https://pkg.go.dev/github.com/pelletier/go-toml/v2) ([MIT](https://github.com/pelletier/go-toml/blob/v2.2.4/LICENSE)) - [github.com/sagikazarmark/locafero](https://pkg.go.dev/github.com/sagikazarmark/locafero) ([MIT](https://github.com/sagikazarmark/locafero/blob/v0.11.0/LICENSE)) diff --git a/third-party-licenses.windows.md b/third-party-licenses.windows.md index a7164a2aad..47fc8f19e8 100644 --- a/third-party-licenses.windows.md +++ b/third-party-licenses.windows.md @@ -25,8 +25,8 @@ The following packages are included for the 386, amd64, arm64 architectures. - [github.com/josephburnett/jd/v2](https://pkg.go.dev/github.com/josephburnett/jd/v2) ([MIT](https://github.com/josephburnett/jd/blob/v2.5.0/v2/LICENSE)) - [github.com/lithammer/fuzzysearch/fuzzy](https://pkg.go.dev/github.com/lithammer/fuzzysearch/fuzzy) ([MIT](https://github.com/lithammer/fuzzysearch/blob/v1.1.8/LICENSE)) - [github.com/microcosm-cc/bluemonday](https://pkg.go.dev/github.com/microcosm-cc/bluemonday) ([BSD-3-Clause](https://github.com/microcosm-cc/bluemonday/blob/v1.0.27/LICENSE.md)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0-pre.3/LICENSE)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0-pre.3/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) - [github.com/muesli/cache2go](https://pkg.go.dev/github.com/muesli/cache2go) ([BSD-3-Clause](https://github.com/muesli/cache2go/blob/518229cd8021/LICENSE.txt)) - [github.com/pelletier/go-toml/v2](https://pkg.go.dev/github.com/pelletier/go-toml/v2) ([MIT](https://github.com/pelletier/go-toml/blob/v2.2.4/LICENSE)) - [github.com/sagikazarmark/locafero](https://pkg.go.dev/github.com/sagikazarmark/locafero) ([MIT](https://github.com/sagikazarmark/locafero/blob/v0.11.0/LICENSE)) From 772df4c932f73adae9b122ce7e4b81b488e14684 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Tue, 28 Jul 2026 22:25:56 +0200 Subject: [PATCH 2/3] fix: don't advertise unsupported list-changed capabilities The server exposes a static set of tools, prompts, and resources and never mutates them at runtime, so it never emits list_changed notifications. When capabilities are left unset, the go-sdk infers listChanged:true from the presence of items and advertises tools/prompts/resources list-change support we don't actually provide - and the 2026-07-28 spec (subscriptions/listen) tightens expectations around this. Declare empty tools/prompts/resources capabilities in NewMCPServer so both the stdio and remote servers advertise honestly. The remote HTTP handler already set these explicitly; that duplication is now removed in favour of the shared default, leaving only the remote-specific schema cache. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 95b8432c-f280-472e-a242-d3ca6dc31f19 --- pkg/github/server.go | 12 ++++++++++++ pkg/http/handler.go | 8 ++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pkg/github/server.go b/pkg/github/server.go index 43e0940017..b8f0197889 100644 --- a/pkg/github/server.go +++ b/pkg/github/server.go @@ -89,6 +89,18 @@ func NewMCPServer(ctx context.Context, cfg *MCPServerConfig, deps ToolDependenci Instructions: inv.Instructions(), Logger: cfg.Logger, CompletionHandler: CompletionsHandler(deps.GetClient), + // Advertise tools, prompts, and resources without list-changed + // notifications. The server has a static set of tools/prompts/resources + // and never mutates them at runtime, so it never emits list_changed + // notifications. Left unset, the SDK would infer listChanged:true from + // the presence of items and advertise a capability we don't support - + // which the 2026-07-28 spec (subscriptions/listen) makes stricter still. + // Explicitly declaring these keeps the advertised capabilities honest. + Capabilities: &mcp.ServerCapabilities{ + Tools: &mcp.ToolCapabilities{}, + Prompts: &mcp.PromptCapabilities{}, + Resources: &mcp.ResourceCapabilities{}, + }, } // Apply any additional server options diff --git a/pkg/http/handler.go b/pkg/http/handler.go index eca628a47b..94ee11db04 100644 --- a/pkg/http/handler.go +++ b/pkg/http/handler.go @@ -205,14 +205,10 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ContentWindowSize: h.config.ContentWindowSize, Logger: h.logger, RepoAccessTTL: h.config.RepoAccessCacheTTL, - // Explicitly set empty capabilities. inv.ForMCPRequest currently returns nothing for Initialize. + // Capabilities (no list-changed advertising) are set by NewMCPServer; + // here we only supply the remote-specific schema cache. ServerOptions: []github.MCPServerOption{ func(so *mcp.ServerOptions) { - so.Capabilities = &mcp.ServerCapabilities{ - Tools: &mcp.ToolCapabilities{}, - Resources: &mcp.ResourceCapabilities{}, - Prompts: &mcp.PromptCapabilities{}, - } so.SchemaCache = h.schemaCache }, }, From 1709a5d818597754caab932c99a38f290e5f745a Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Thu, 30 Jul 2026 13:37:48 +0200 Subject: [PATCH 3/3] test: assert advertised capabilities omit list-changed Add a regression test locking in the capability contract set by NewMCPServer: tools, prompts, and resources are advertised without list-changed notifications, the deprecated logging capability is not advertised, and the inferred completions capability is preserved. Covers both the stdio path (full inventory, items present) and the HTTP path (inventory emptied for the discovery request), which share the same NewMCPServer entry point. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 95b8432c-f280-472e-a242-d3ca6dc31f19 --- pkg/github/server_test.go | 92 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/pkg/github/server_test.go b/pkg/github/server_test.go index bc891fac1f..07cb63c85f 100644 --- a/pkg/github/server_test.go +++ b/pkg/github/server_test.go @@ -11,6 +11,7 @@ import ( "testing" "time" + "github.com/github/github-mcp-server/pkg/inventory" "github.com/github/github-mcp-server/pkg/lockdown" "github.com/github/github-mcp-server/pkg/observability" "github.com/github/github-mcp-server/pkg/observability/metrics" @@ -191,6 +192,97 @@ func TestNewMCPServer_CreatesSuccessfully(t *testing.T) { // is already tested in pkg/github/*_test.go. } +// advertisedServerCapabilities connects an in-memory client to the given server +// and returns the capabilities the server advertised during initialization. +func advertisedServerCapabilities(t *testing.T, server *mcp.Server) *mcp.ServerCapabilities { + t.Helper() + + ctx := context.Background() + clientTransport, serverTransport := mcp.NewInMemoryTransports() + + serverSession, err := server.Connect(ctx, serverTransport, nil) + require.NoError(t, err, "expected server to connect") + t.Cleanup(func() { _ = serverSession.Close() }) + + client := mcp.NewClient(&mcp.Implementation{Name: "test-client", Version: "1.0.0"}, nil) + clientSession, err := client.Connect(ctx, clientTransport, nil) + require.NoError(t, err, "expected client to connect") + t.Cleanup(func() { _ = clientSession.Close() }) + + result := clientSession.InitializeResult() + require.NotNil(t, result, "expected an initialize result") + return result.Capabilities +} + +// TestNewMCPServer_AdvertisedCapabilities locks in the capability contract set by +// NewMCPServer: tools, prompts, and resources are advertised without list-changed +// notifications (the server has a static item set and never emits list_changed), +// the deprecated logging capability is not advertised, and the inferred +// completions capability is preserved. This is asserted for both the stdio path +// (full inventory, items present) and the HTTP path (inventory emptied for the +// discovery/initialize request), which share the same NewMCPServer entry point. +func TestNewMCPServer_AdvertisedCapabilities(t *testing.T) { + t.Parallel() + + cfg := MCPServerConfig{ + Version: "test", + Token: "test-token", + EnabledToolsets: []string{"context"}, + Translator: translations.NullTranslationHelper, + ContentWindowSize: 5000, + } + + deps := stubDeps{obsv: stubExporters()} + + fullInventory, err := NewInventory(cfg.Translator). + WithDeprecatedAliases(DeprecatedToolAliases). + WithToolsets(cfg.EnabledToolsets). + Build() + require.NoError(t, err, "expected inventory build to succeed") + + tests := []struct { + name string + inv *inventory.Inventory + }{ + { + name: "stdio path with registered items", + inv: fullInventory, + }, + { + // The HTTP handler registers only the items relevant to a request; + // for initialize/discover that is nothing, so capabilities must come + // from the explicit declaration rather than being inferred from items. + name: "http path with no registered items for discovery", + inv: fullInventory.ForMCPRequest(inventory.MCPMethodDiscover, ""), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + server, err := NewMCPServer(context.Background(), &cfg, deps, tt.inv) + require.NoError(t, err, "expected server creation to succeed") + + caps := advertisedServerCapabilities(t, server) + + require.NotNil(t, caps.Tools, "tools capability should be advertised") + assert.False(t, caps.Tools.ListChanged, "tools list-changed must not be advertised") + + require.NotNil(t, caps.Prompts, "prompts capability should be advertised") + assert.False(t, caps.Prompts.ListChanged, "prompts list-changed must not be advertised") + + require.NotNil(t, caps.Resources, "resources capability should be advertised") + assert.False(t, caps.Resources.ListChanged, "resources list-changed must not be advertised") + assert.False(t, caps.Resources.Subscribe, "resources subscribe must not be advertised") + + assert.NotNil(t, caps.Completions, "completions capability should be preserved") + // Intentionally asserting the deprecated logging capability is absent. + assert.Nil(t, caps.Logging, "deprecated logging capability should not be advertised") //nolint:staticcheck // SA1019: verifying the deprecated capability is not advertised + }) + } +} + // TestNewServer_NameAndTitleViaTranslation verifies that server name and title // can be overridden via the translation helper (GITHUB_MCP_SERVER_NAME / // GITHUB_MCP_SERVER_TITLE env vars or github-mcp-server-config.json) and