mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-10-12 04:55:18 +00:00
After 30 minutes without a message the idle monitor closed stdin and expected the read loop to end. On a real, blocking stdin a read already in progress is not woken by closing the file, so the server lived on, the client's next message completed that read and the one after failed with 'read /dev/stdin: file already closed', which ended the connection mid-call. The idle shutdown is now off by default (a stdio server lives as long as its client; orphans are still caught by the parent check). CLAUDE_MNEMONIC_MCP_IDLE_TIMEOUT (a Go duration) turns it on, and then it cancels the loop's context so Run returns nil instead of closing stdin. Co-authored-by: Claude Sonnet 5.5 <[email protected]>
39 lines
1.0 KiB
Go
39 lines
1.0 KiB
Go
package mcp
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestIdleTimeoutFromEnv(t *testing.T) {
|
|
// The idle shutdown is off unless asked for; anything that is not a positive duration leaves it off.
|
|
for value, want := range map[string]time.Duration{
|
|
"": 0,
|
|
"0": 0,
|
|
"abc": 0,
|
|
"-5m": 0,
|
|
"45": 0, // a bare number is not a duration
|
|
"90m": 90 * time.Minute,
|
|
"2h": 2 * time.Hour,
|
|
"30s": 30 * time.Second,
|
|
" 2h ": 0, // not trimmed: the operator wrote it, so it is wrong rather than guessed
|
|
} {
|
|
t.Run(value, func(t *testing.T) {
|
|
t.Setenv(IdleTimeoutEnv, value)
|
|
assert.Equal(t, want, idleTimeoutFromEnv())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewServer_HasNoIdleTimeoutByDefault(t *testing.T) {
|
|
t.Setenv(IdleTimeoutEnv, "")
|
|
assert.Zero(t, NewServer(nil, "http://localhost:1", "p", "v").idleTimeout)
|
|
}
|
|
|
|
func TestNewServer_TakesTheIdleTimeoutFromTheEnvironment(t *testing.T) {
|
|
t.Setenv(IdleTimeoutEnv, "3h")
|
|
assert.Equal(t, 3*time.Hour, NewServer(nil, "http://localhost:1", "p", "v").idleTimeout)
|
|
}
|