fix(mcp): address SDK migration review

Co-Authored-By: OpenAI Codex (GPT-5) <noreply@openai.com>
This commit is contained in:
Bo-Yi Wu
2026-08-03 12:02:21 +08:00
parent 0dc9868e2e
commit cc0cb109a8
4 changed files with 66 additions and 11 deletions
+11 -2
View File
@@ -41,6 +41,9 @@ const maxRequestBodyBytes = 32 << 20
// session, and initialize takes no token. Clients re-initialize on the 404.
const sessionTimeout = 30 * time.Minute
// httpReadHeaderTimeout bounds slow header reads without limiting SSE writes.
const httpReadHeaderTimeout = 10 * time.Second
var (
mcpServer *mcp.Server
@@ -111,11 +114,15 @@ func newHTTPServer(addr string, s *mcp.Server) *http.Server {
&mcp.StreamableHTTPOptions{
Logger: log.Slog(),
MaxRequestBodyBytes: maxRequestBodyBytes,
Stateless: false, // PR 2 switches this on
Stateless: false, // SessionTimeout requires stateful sessions.
SessionTimeout: sessionTimeout,
},
))
return &http.Server{Addr: addr, Handler: mux}
return &http.Server{
Addr: addr,
Handler: mux,
ReadHeaderTimeout: httpReadHeaderTimeout,
}
}
func Run() error {
@@ -157,6 +164,8 @@ func Run() error {
}
func newMCPServer(version string) *mcp.Server {
// SDK keepalives send MCP ping requests and disconnect clients without a
// server-to-client channel, so KeepAlive stays disabled.
s := mcp.NewServer(
&mcp.Implementation{
Name: "Gitea MCP Server",
+16
View File
@@ -2,6 +2,22 @@ package operation
import "testing"
func TestNewHTTPServerConfig(t *testing.T) {
server := newHTTPServer(":12345", newMCPServer("test"))
if server.Addr != ":12345" {
t.Errorf("Addr = %q, want %q", server.Addr, ":12345")
}
if server.Handler == nil {
t.Error("Handler is nil")
}
if server.ReadHeaderTimeout != httpReadHeaderTimeout {
t.Errorf("ReadHeaderTimeout = %v, want %v", server.ReadHeaderTimeout, httpReadHeaderTimeout)
}
if server.WriteTimeout != 0 {
t.Errorf("WriteTimeout = %v, want zero for SSE", server.WriteTimeout)
}
}
func TestParseAuthToken(t *testing.T) {
tests := []struct {
name string
+38 -9
View File
@@ -21,7 +21,12 @@ import (
"github.com/modelcontextprotocol/go-sdk/mcp"
)
const testServerVersion = "test-version"
// Pin negotiated versions so SDK upgrades require compatibility review.
const (
testServerVersion = "test-version"
expectedProtocolVersion = "2026-07-28"
expectedStatefulHTTPProtocolVersion = "2025-11-25"
)
func exposeAllTools(t *testing.T) {
t.Helper()
@@ -51,6 +56,21 @@ func registeredToolCount() int {
return count
}
// stdioCommandEnvironment removes variables that override subprocess flags.
func stdioCommandEnvironment() []string {
environment := os.Environ()
filtered := make([]string, 0, len(environment))
for _, entry := range environment {
name, _, _ := strings.Cut(entry, "=")
switch name {
case "GITEA_READONLY", "GITEA_SCOPES", "GITEA_TOOLS", "MCP_MODE":
continue
}
filtered = append(filtered, entry)
}
return filtered
}
func textContent(t *testing.T, result *mcp.CallToolResult) string {
t.Helper()
if len(result.Content) != 1 {
@@ -104,8 +124,8 @@ func TestOfficialSDKInMemory(t *testing.T) {
if err != nil {
t.Fatalf("Connect() error = %v", err)
}
if got := session.InitializeResult().ProtocolVersion; got != "2026-07-28" {
t.Errorf("protocol version = %q, want %q", got, "2026-07-28")
if got := session.InitializeResult().ProtocolVersion; got != expectedProtocolVersion {
t.Errorf("protocol version = %q, want %q", got, expectedProtocolVersion)
}
listAndCallVersion(ctx, t, session, testServerVersion)
if err := session.Close(); err != nil {
@@ -142,8 +162,9 @@ func TestStreamableHTTPStateful(t *testing.T) {
t.Fatalf("Connect() error = %v", err)
}
defer session.Close()
if got := session.InitializeResult().ProtocolVersion; got != "2025-11-25" {
t.Errorf("protocol version = %q, want %q", got, "2025-11-25")
// Stateful Streamable HTTP cannot negotiate the sessionless 2026 protocol.
if got := session.InitializeResult().ProtocolVersion; got != expectedStatefulHTTPProtocolVersion {
t.Errorf("protocol version = %q, want %q", got, expectedStatefulHTTPProtocolVersion)
}
listAndCallVersion(ctx, t, session, testServerVersion)
@@ -347,19 +368,27 @@ func TestStdioCommandTransport(t *testing.T) {
if testing.Short() {
t.Skip("skipping subprocess build in short mode")
}
exposeAllTools(t) // the subprocess runs with default flags, so match them here
for name, value := range map[string]string{
"GITEA_READONLY": "true",
"GITEA_SCOPES": "user",
"GITEA_TOOLS": "get_me",
"MCP_MODE": "http",
} {
t.Setenv(name, value)
}
exposeAllTools(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
binary := filepath.Join(t.TempDir(), "gitea-mcp")
build := exec.CommandContext(ctx, "go", "build", "-o", binary, "..")
build.Env = os.Environ()
if output, err := build.CombinedOutput(); err != nil {
t.Fatalf("build stdio test binary: %v\n%s", err, output)
}
client := mcp.NewClient(&mcp.Implementation{Name: "gitea-mcp-stdio-test", Version: "1"}, nil)
command := exec.CommandContext(ctx, binary, "--transport", "stdio")
command.Env = stdioCommandEnvironment()
session, err := client.Connect(ctx, &mcp.CommandTransport{
Command: command,
TerminateDuration: 2 * time.Second,
@@ -368,8 +397,8 @@ func TestStdioCommandTransport(t *testing.T) {
t.Fatalf("Connect() error = %v", err)
}
defer session.Close()
if got := session.InitializeResult().ProtocolVersion; got != "2026-07-28" {
t.Errorf("protocol version = %q, want %q", got, "2026-07-28")
if got := session.InitializeResult().ProtocolVersion; got != expectedProtocolVersion {
t.Errorf("protocol version = %q, want %q", got, expectedProtocolVersion)
}
listAndCallVersion(ctx, t, session, "Gitea MCP Server version:")
}