From 1472d7a107efb9a1fc2837b881f52befea2d30b2 Mon Sep 17 00:00:00 2001 From: thibaud-leclere Date: Fri, 10 Apr 2026 15:38:58 +0200 Subject: [PATCH] fix: support Claude Code MCP protocol version --- internal/mcpserver/server.go | 1 + internal/mcpserver/server_test.go | 54 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/internal/mcpserver/server.go b/internal/mcpserver/server.go index 3ef8f22..8223f59 100644 --- a/internal/mcpserver/server.go +++ b/internal/mcpserver/server.go @@ -34,6 +34,7 @@ const ( var supportedProtocolVersions = []string{ "2025-03-26", + "2024-11-05", } type MailService interface { diff --git a/internal/mcpserver/server_test.go b/internal/mcpserver/server_test.go index 429cfa4..523ed8f 100644 --- a/internal/mcpserver/server_test.go +++ b/internal/mcpserver/server_test.go @@ -287,6 +287,60 @@ func TestRunnerRunWritesToolManifestAndHandlesRequests(t *testing.T) { } } +func TestRunnerRunAcceptsClaudeCodeProtocolVersion(t *testing.T) { + store := &storeStub{ + credential: secretstore.Credential{ + Host: "imap.example.com", + Username: "alice", + Password: "secret", + }, + } + input := bytes.NewBufferString("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"claude-code\",\"version\":\"1.0.0\"}}}\n") + output := &bytes.Buffer{} + runner := NewRunner(New(store, serviceStub{ + listMailboxes: func(context.Context, secretstore.Credential) ([]imapclient.Mailbox, error) { + t.Fatal("ListMailboxes should not be called") + return nil, nil + }, + listMessages: func(context.Context, secretstore.Credential, string, int) ([]imapclient.MessageSummary, error) { + t.Fatal("ListMessages should not be called") + return nil, nil + }, + getMessage: func(context.Context, secretstore.Credential, string, uint32) (imapclient.Message, error) { + t.Fatal("GetMessage should not be called") + return imapclient.Message{}, nil + }, + }), input, output, &bytes.Buffer{}) + + if err := runner.Run(context.Background()); err != nil { + t.Fatalf("Run returned error: %v", err) + } + + var response struct { + JSONRPC string `json:"jsonrpc"` + ID int `json:"id"` + Result struct { + ProtocolVersion string `json:"protocolVersion"` + } `json:"result"` + Error *struct { + Code int `json:"code"` + Message string `json:"message"` + } `json:"error"` + } + if err := json.NewDecoder(output).Decode(&response); err != nil { + t.Fatalf("failed to decode initialize response: %v", err) + } + if response.Error != nil { + t.Fatalf("expected initialize to succeed, got error %#v", response.Error) + } + if response.JSONRPC != "2.0" || response.ID != 1 { + t.Fatalf("unexpected response envelope: %#v", response) + } + if response.Result.ProtocolVersion != "2024-11-05" { + t.Fatalf("expected negotiated protocol version 2024-11-05, got %#v", response.Result) + } +} + func TestRunnerRunReturnsFriendlyMissingCredentialError(t *testing.T) { store := &storeStub{ loadErr: kwallet.ErrCredentialNotFound,