@@ -19,6 +19,7 @@ import (
1919 "github.com/github/github-mcp-server/pkg/translations"
2020 "github.com/github/github-mcp-server/pkg/utils"
2121 "github.com/go-chi/chi/v5"
22+ "github.com/modelcontextprotocol/go-sdk/jsonrpc"
2223 "github.com/modelcontextprotocol/go-sdk/mcp"
2324 "github.com/stretchr/testify/assert"
2425 "github.com/stretchr/testify/require"
@@ -1044,6 +1045,80 @@ func TestHTTPToolMinimumProtocolVersion(t *testing.T) {
10441045 }
10451046}
10461047
1048+ func TestSubscriptionsListenIsRejected (t * testing.T ) {
1049+ apiHost , err := utils .NewAPIHost ("https://api.githubcopilot.com" )
1050+ require .NoError (t , err )
1051+
1052+ handler := NewHTTPMcpHandler (
1053+ context .Background (),
1054+ & ServerConfig {Version : "test" },
1055+ nil ,
1056+ translations .NullTranslationHelper ,
1057+ slog .Default (),
1058+ apiHost ,
1059+ WithInventoryFactory (func (_ * http.Request ) (* inventory.Inventory , error ) {
1060+ return inventory .NewBuilder ().Build ()
1061+ }),
1062+ WithGitHubMCPServerFactory (func (_ * http.Request , _ github.ToolDependencies , _ * inventory.Inventory , _ * github.MCPServerConfig ) (* mcp.Server , error ) {
1063+ return mcp .NewServer (& mcp.Implementation {Name : "test" , Version : "0.0.1" }, nil ), nil
1064+ }),
1065+ )
1066+
1067+ body := `{"jsonrpc":"2.0","id":1,"method":"subscriptions/listen","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientInfo":{"name":"test","version":"1.0.0"},"io.modelcontextprotocol/clientCapabilities":{}},"notifications":{"toolsListChanged":true}}}`
1068+ tests := []struct {
1069+ name string
1070+ methodHeader string
1071+ expectedStatus int
1072+ expectedJSONCode int
1073+ }{
1074+ {
1075+ name : "matching method header" ,
1076+ methodHeader : subscriptionsListenMethod ,
1077+ expectedStatus : http .StatusNotFound ,
1078+ expectedJSONCode : jsonrpc .CodeMethodNotFound ,
1079+ },
1080+ {
1081+ name : "missing method header" ,
1082+ expectedStatus : http .StatusBadRequest ,
1083+ expectedJSONCode : mcp .CodeHeaderMismatch ,
1084+ },
1085+ {
1086+ name : "mismatched method header" ,
1087+ methodHeader : "tools/list" ,
1088+ expectedStatus : http .StatusBadRequest ,
1089+ expectedJSONCode : mcp .CodeHeaderMismatch ,
1090+ },
1091+ }
1092+
1093+ for _ , tt := range tests {
1094+ t .Run (tt .name , func (t * testing.T ) {
1095+ req := httptest .NewRequest (http .MethodPost , "/" , strings .NewReader (body ))
1096+ req .Header .Set (headers .ContentTypeHeader , headers .ContentTypeJSON )
1097+ req .Header .Set (headers .AcceptHeader , strings .Join ([]string {headers .ContentTypeJSON , headers .ContentTypeEventStream }, ", " ))
1098+ req .Header .Set ("MCP-Protocol-Version" , "2026-07-28" )
1099+ if tt .methodHeader != "" {
1100+ req .Header .Set (headers .MCPMethodHeader , tt .methodHeader )
1101+ }
1102+
1103+ rr := httptest .NewRecorder ()
1104+ handler .ServeHTTP (rr , req )
1105+
1106+ assert .Equal (t , tt .expectedStatus , rr .Code )
1107+ assert .Equal (t , headers .ContentTypeJSON , rr .Header ().Get (headers .ContentTypeHeader ))
1108+
1109+ var response struct {
1110+ ID int `json:"id"`
1111+ Error struct {
1112+ Code int `json:"code"`
1113+ } `json:"error"`
1114+ }
1115+ require .NoError (t , json .Unmarshal (rr .Body .Bytes (), & response ))
1116+ assert .Equal (t , 1 , response .ID )
1117+ assert .Equal (t , tt .expectedJSONCode , response .Error .Code )
1118+ })
1119+ }
1120+ }
1121+
10471122// TestInsidersRoutePreservesUIMeta is a regression test for the bug where
10481123// _meta.ui was stripped from tools/list responses on the HTTP /insiders route.
10491124//
0 commit comments