Skip to content
Open
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
7 changes: 3 additions & 4 deletions internal/jsonrpc2/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,9 @@ func (c *Connection) processResult(from any, req *incomingRequest, result any, e
if err == nil {
err = writeErr
}
if writeErr != nil && c.onInternalError != nil {
c.onInternalError(fmt.Errorf("jsonrpc2: failed to write response for %q: %w", req.Method, writeErr))
}
} else {
err = c.internalErrorf("%#v returned a malformed result for %q: %w", from, req.Method, respErr)
}
Expand All @@ -728,10 +731,6 @@ func (c *Connection) processResult(from any, req *incomingRequest, result any, e
err = fmt.Errorf("%w: %q notification failed: %v", ErrInternal, req.Method, err)
}
}
if err != nil {
// TODO: can/should we do anything with this error beyond writing it to the event log?
// (Is this the right label to attach to the log?)
}

// Cancel the request to free any associated resources.
req.cancel()
Expand Down
66 changes: 66 additions & 0 deletions internal/jsonrpc2/conn_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2020 The Go MCP SDK Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package jsonrpc2

import (
"context"
"errors"
"strings"
"testing"
)

type errWriter struct {
err error
}

func (w errWriter) Write(context.Context, Message) error {
return w.err
}

func TestProcessResultWriteFailureReportsInternalError(t *testing.T) {
writeErr := errors.New("write failed")
var internalErrors []error
c := &Connection{
done: make(chan struct{}),
writer: errWriter{err: writeErr},
onInternalError: func(err error) {
internalErrors = append(internalErrors, err)
},
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

call, err := NewCall(StringID("1"), "test", nil)
if err != nil {
t.Fatal(err)
}

req := &incomingRequest{
Request: call,
ctx: ctx,
cancel: cancel,
}

c.updateInFlight(func(s *inFlightState) {
s.incoming = 1
s.incomingByID = map[ID]*incomingRequest{call.ID: req}
})

if err := c.processResult("test", req, "ok", nil); err != nil {
t.Fatalf("processResult() = %v, want nil", err)
}

if len(internalErrors) != 1 {
t.Fatalf("OnInternalError calls = %d, want 1", len(internalErrors))
}
got := internalErrors[0].Error()
if !strings.Contains(got, "failed to write response") {
t.Errorf("OnInternalError = %q, want message about write failure", got)
}
if !errors.Is(internalErrors[0], writeErr) {
t.Errorf("OnInternalError error does not wrap write error: %v", internalErrors[0])
}
}