Skip to content
Closed
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
4 changes: 3 additions & 1 deletion internal/mcp/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -837,8 +837,10 @@ func (h *handlers) getMessage(ctx context.Context, req mcp.CallToolRequest) (*mc
}

maxChars := intArg(args, "max_chars", defaultBodyChars)
if maxChars <= 0 || maxChars > maxBodyChars {
if maxChars <= 0 {
maxChars = defaultBodyChars
} else if maxChars > maxBodyChars {
maxChars = maxBodyChars
}

fullBody := msg.BodyText
Expand Down
2 changes: 1 addition & 1 deletion internal/mcp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ func getMessageTool() mcp.Tool {
mcp.Description("Byte offset from the start of body_text to center the window on (e.g. char_offset from search_in_message). Takes precedence over offset."),
),
mcp.WithNumber("max_chars",
mcp.Description("Maximum body_text bytes to return (default 2000, max 4000). Out-of-range values are silently replaced with the default."),
mcp.Description("Maximum body_text bytes to return (default 2000, max 4000). Values above 4000 are clamped to 4000; zero or negative values use the default."),
),
)
}
Expand Down
36 changes: 36 additions & 0 deletions internal/mcp/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,42 @@ func TestGetMessage(t *testing.T) {
assertpkg.Equal(t, 0, msg.Offset, "starts at body start")
})

t.Run("max_chars above cap clamps to 4000", func(t *testing.T) {
assert := assertpkg.New(t)
longBody := strings.Repeat("x", 5000)
eng2 := &querytest.MockEngine{
Messages: map[int64]*query.MessageDetail{
54: testutil.NewMessageDetail(54).WithBodyText(longBody).BuildPtr(),
},
}
h2 := newTestHandlers(eng2)
msg := runTool[getMessageResp](t, "get_message", h2.getMessage, map[string]any{
"id": float64(54),
"max_chars": float64(5000),
})
assert.Equal(4000, msg.BodyReturned, "body_returned")
assert.Len(msg.BodyText, 4000, "clamped body_text")
assert.True(msg.HasMore, "has_more")
})

t.Run("max_chars zero uses default", func(t *testing.T) {
assert := assertpkg.New(t)
longBody := strings.Repeat("x", 5000)
eng2 := &querytest.MockEngine{
Messages: map[int64]*query.MessageDetail{
55: testutil.NewMessageDetail(55).WithBodyText(longBody).BuildPtr(),
},
}
h2 := newTestHandlers(eng2)
msg := runTool[getMessageResp](t, "get_message", h2.getMessage, map[string]any{
"id": float64(55),
"max_chars": float64(0),
})
assert.Equal(2000, msg.BodyReturned, "body_returned")
assert.Len(msg.BodyText, 2000, "default body_text")
assert.True(msg.HasMore, "has_more")
})

errorCases := []struct {
name string
args map[string]any
Expand Down