Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — you're right, v1.7.0 is not identical to pre.3. There are 4 intervening commits (827f90b..bc72835):

I've assessed them: all are transport-layer bug fixes / a micro-optimization, none breaking, and none require changes on our side (they only touch the streamable HTTP client and SSE writing). Updated the PR description to list them and drop the "tag-only release" framing.

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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
12 changes: 12 additions & 0 deletions pkg/github/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{},
},
Comment on lines +99 to +103

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — added TestNewMCPServer_AdvertisedCapabilities in pkg/github/server_test.go (commit 1709a5d). It connects an in-memory client and asserts the advertised capabilities: tools/prompts/resources present with listChanged == false, resources.subscribe == false, completions preserved, and logging absent. It runs two sub-cases sharing the NewMCPServer entry point — the stdio path (full inventory, items present) and the HTTP path (ForMCPRequest(MCPMethodDiscover, ""), no items registered) — so both users are covered. I verified it fails without the capability declaration (tools would advertise listChanged:true on stdio and be absent entirely on the HTTP/discovery path).

}

// Apply any additional server options
Expand Down
92 changes: 92 additions & 0 deletions pkg/github/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
8 changes: 2 additions & 6 deletions pkg/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
},
Expand Down
4 changes: 2 additions & 2 deletions third-party-licenses.darwin.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
4 changes: 2 additions & 2 deletions third-party-licenses.linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
4 changes: 2 additions & 2 deletions third-party-licenses.windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Loading