Skip to content

Commit 5d2e312

Browse files
authored
feat: add per-server tool_timeout to override global gateway timeout (#5206)
Reusable shared workflows wrapping long-running HTTP MCP servers (e.g. Repo Mind Light) have no way to set the gateway's tool invocation timeout — consumers must know to configure `MCP_GATEWAY_TOOL_TIMEOUT` in `sandbox.mcp.env`, breaking the reusable component contract. ## Changes - **`ServerConfig.ToolTimeout`** — new per-server field (TOML: `tool_timeout`, JSON stdin: `tool_timeout`); minimum 10s; zero/omitted means fall back to `gateway.toolTimeout` - **`callBackendTool`** — per-server `ToolTimeout` takes precedence over `gateway.ToolTimeout` when set - **JSON schema** — `tool_timeout` added to `stdioServerConfig` and `httpServerConfig` definitions so stdin validation accepts the field - **Validation** — `validateStandardServerConfig` enforces minimum of 10 for per-server `tool_timeout` - **Docs** — `CONFIGURATION.md` documents the field and its relationship to the global timeout ## Example A reusable shared workflow can now declare its own timeout directly on the server: ```json { "mcpServers": { "repo-mind": { "type": "http", "url": "http://127.0.0.1:8000/mcp", "tool_timeout": 600 } }, "gateway": { "port": 3000, "domain": "localhost", "apiKey": "${API_KEY}" } } ``` Consumers get a sane 600s timeout without needing to know about `MCP_GATEWAY_TOOL_TIMEOUT`. They can still override at the consumer level if needed. > [!WARNING] > > <details> > <summary>Firewall rules blocked me from connecting to one or more addresses (expand for details)</summary> > > #### I tried to connect to the following addresses, but was blocked by firewall rules: > > - `example.com` > - Triggering command: `/tmp/go-build2847888803/b509/launcher.test /tmp/go-build2847888803/b509/launcher.test -test.testlogfile=/tmp/go-build2847888803/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 997891/b463/_pkg_.a -trimpath ache/go/1.25.9/x64/pkg/tool/linux_amd64/vet -p ny/timefmt-go -lang=go1.25 ache/go/1.25.9/x-buildtags` (dns block) > - Triggering command: `/tmp/go-build1669811436/b513/launcher.test /tmp/go-build1669811436/b513/launcher.test -test.testlogfile=/tmp/go-build1669811436/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s dock�� -stringintconv -tests 64/pkg/tool/linux_amd64/link _.a 997891/b150/ x_amd64/vet 64/pkg/tool/linux_amd64/link --no�� test.test x_amd64/vet x_amd64/vet 1.80.0/channelz/docker 997891/b150/ 64/pkg/tool/linu-nilfunc x_amd64/vet` (dns block) > - `invalid-host-that-does-not-exist-12345.com` > - Triggering command: `/tmp/go-build871958364/b001/config.test /tmp/go-build871958364/b001/config.test -test.paniconexit0 -test.timeout=2m0s -test.count=1 rtcf�� 1024825-9d38bb4040a9/status/status.pb.go 64/src/internal/google.golang.org/grpc/internal/channelz x86_64/as` (dns block) > - Triggering command: `/tmp/go-build2847888803/b491/config.test /tmp/go-build2847888803/b491/config.test -test.testlogfile=/tmp/go-build2847888803/b491/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true msSmPlS8N cfg x_amd64/vet . -imultiarch x86_64-linux-gnu--noprofile x_amd64/vet 9978�� om/spf13/[email protected]/active_he-errorsas cfg x_amd64/vet --gdwarf-5 b/gh-aw-mcpg/intphp8.3 -o x_amd64/vet` (dns block) > - Triggering command: `/tmp/go-build1669811436/b495/config.test /tmp/go-build1669811436/b495/config.test -test.testlogfile=/tmp/go-build1669811436/b495/testlog.txt -test.paniconexit0 -test.timeout=10m0s -ato�� -bool -buildtags x_amd64/vet -errorsas -ifaceassert -nilfunc x_amd64/vet run test -tests ache/go/1.25.9/x64/pkg/tool/linux_amd64/compile _.a aw-mcpg/internal/tmp/go-build1282575160/b425/vet.cfg x_amd64/vet ache/go/1.25.9/x64/pkg/tool/linux_amd64/compile` (dns block) > - `nonexistent.local` > - Triggering command: `/tmp/go-build2847888803/b509/launcher.test /tmp/go-build2847888803/b509/launcher.test -test.testlogfile=/tmp/go-build2847888803/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 997891/b463/_pkg_.a -trimpath ache/go/1.25.9/x64/pkg/tool/linux_amd64/vet -p ny/timefmt-go -lang=go1.25 ache/go/1.25.9/x-buildtags` (dns block) > - Triggering command: `/tmp/go-build1669811436/b513/launcher.test /tmp/go-build1669811436/b513/launcher.test -test.testlogfile=/tmp/go-build1669811436/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s dock�� -stringintconv -tests 64/pkg/tool/linux_amd64/link _.a 997891/b150/ x_amd64/vet 64/pkg/tool/linux_amd64/link --no�� test.test x_amd64/vet x_amd64/vet 1.80.0/channelz/docker 997891/b150/ 64/pkg/tool/linu-nilfunc x_amd64/vet` (dns block) > - `slow.example.com` > - Triggering command: `/tmp/go-build2847888803/b509/launcher.test /tmp/go-build2847888803/b509/launcher.test -test.testlogfile=/tmp/go-build2847888803/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 997891/b463/_pkg_.a -trimpath ache/go/1.25.9/x64/pkg/tool/linux_amd64/vet -p ny/timefmt-go -lang=go1.25 ache/go/1.25.9/x-buildtags` (dns block) > - Triggering command: `/tmp/go-build1669811436/b513/launcher.test /tmp/go-build1669811436/b513/launcher.test -test.testlogfile=/tmp/go-build1669811436/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s dock�� -stringintconv -tests 64/pkg/tool/linux_amd64/link _.a 997891/b150/ x_amd64/vet 64/pkg/tool/linux_amd64/link --no�� test.test x_amd64/vet x_amd64/vet 1.80.0/channelz/docker 997891/b150/ 64/pkg/tool/linu-nilfunc x_amd64/vet` (dns block) > - `this-host-does-not-exist-12345.com` > - Triggering command: `/tmp/go-build2847888803/b518/mcp.test /tmp/go-build2847888803/b518/mcp.test -test.testlogfile=/tmp/go-build2847888803/b518/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true se 958364/b060/vet.cfg ache/go/1.25.9/x64/pkg/tool/linux_amd64/vet . --gdwarf2 --64 ache/go/1.25.9/x-trimpath` (dns block) > - Triggering command: `/tmp/go-build3024656909/b518/mcp.test /tmp/go-build3024656909/b518/mcp.test -test.testlogfile=/tmp/go-build3024656909/b518/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true /tmp/go-build689997891/b448/_pkg_.a -trimpath docker-buildx -p go.opentelemetryinspect -lang=go1.24 docker-buildx -uns�� -unreachable=false /tmp/go-build871958364/b091/vet.-ifaceassert .cfg -c=4 -nolocalimports -importcfg ache/go/1.25.9/x64/pkg/tool/linu{{.Config.OpenStdin}}` (dns block) > - Triggering command: `/tmp/go-build3319390857/b518/mcp.test /tmp/go-build3319390857/b518/mcp.test -test.testlogfile=/tmp/go-build3319390857/b518/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true g_.a /tmp/go-build871958364/b023/vet.journal-or-kmsg x_amd64/vet -c=4 -nolocalimports -importcfg x_amd64/vet -uns�� hI9joKUGJ /tmp/go-build871958364/b152/vet.cfg ache/go/1.25.9/x64/pkg/tool/linux_amd64/vet en.go en.go ache/go/1.25.9/x-o ctHi6v3/br4fXcgGpaHZ4LUv69Ml` (dns block) > > If you need me to access, download, or install something from one of these locations, you can either: > > - Configure [Actions setup steps](https://gh.io/copilot/actions-setup-steps) to set up my environment, which run before the firewall is enabled > - Add the appropriate URLs or hosts to the custom allowlist in this repository's [Copilot coding agent settings](https://github.com/github/gh-aw-mcpg/settings/copilot/coding_agent) (admins only) > > </details>
2 parents d649bbb + 43d605c commit 5d2e312

10 files changed

Lines changed: 424 additions & 5 deletions

docs/CONFIGURATION.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ Run `./awmg --help` for full CLI options. Key flags:
176176

177177
- **`connect_timeout`** (optional, HTTP servers only): Per-transport connection timeout in seconds for connecting to HTTP backends. The gateway tries streamable HTTP, then SSE, then plain JSON-RPC over HTTP POST in sequence; this timeout applies to each attempt. It does **not** set the end-to-end `tools/call` execution timeout. Default: `30`.
178178

179+
- **`tool_timeout`** (optional): Per-server tool invocation timeout in seconds. When set to a positive value, overrides the global tool timeout for `tools/call` requests to this specific server. This allows reusable shared workflow components wrapping long-running HTTP MCP servers to set an appropriate timeout once, without requiring every consumer to configure `MCP_GATEWAY_TOOL_TIMEOUT`. Minimum: `10`. Omit the field (or set to `0`) to fall back to the global timeout.
180+
- Global timeout field name: **`toolTimeout`** in stdin JSON (`gateway.toolTimeout`), **`tool_timeout`** in TOML (`[gateway]` → `tool_timeout`)
181+
- Example (stdin JSON): `"tool_timeout": 600` on an HTTP MCP server that may take up to 10 minutes
182+
- Example (TOML): `tool_timeout = 600` under a `[servers.my-server]` section
183+
179184
- **`rate_limit_threshold`** (optional, TOML/JSON file configs only): Number of consecutive rate-limit errors from this backend that will trip the circuit breaker (transition CLOSED → OPEN). When OPEN, requests are immediately rejected until the breaker is eligible to transition to HALF-OPEN again; this is normally controlled by `rate_limit_cooldown`, but if the gateway knows an upstream rate-limit reset time (for example from response headers or parsed tool error text), that reset time takes precedence. **Not available in JSON stdin format.** Default: `3`.
180185

181186
- **`rate_limit_cooldown`** (optional, TOML/JSON file configs only): Default number of seconds before the circuit breaker allows a single probe request (transition OPEN → HALF-OPEN). If the gateway knows an upstream rate-limit reset time, it uses that reset time instead of this cooldown to decide when to probe again. If the probe succeeds the circuit closes; if rate-limited again it re-opens. **Not available in JSON stdin format.** Default: `60`.

internal/config/config_core.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ type ServerConfig struct {
217217
// slow to initialize. Only applies to HTTP server types. Default: 30 seconds.
218218
ConnectTimeout int `toml:"connect_timeout" json:"connect_timeout,omitempty"`
219219

220+
// ToolTimeout is the per-server maximum time (seconds) to wait for a single tool invocation.
221+
// When set, this overrides the global gateway.tool_timeout for calls to this server only.
222+
// Minimum: 10. When 0 (unset), the global gateway.tool_timeout is used.
223+
ToolTimeout int `toml:"tool_timeout" json:"tool_timeout,omitempty"`
224+
220225
// RateLimitThreshold is the number of consecutive rate-limit errors from this backend
221226
// that will trip the circuit breaker (transition CLOSED → OPEN). When OPEN, requests
222227
// are immediately rejected until the cooldown period elapses. Default: 3.

internal/config/config_stdin.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ type StdinServerConfig struct {
127127
// Only applies to HTTP server types. Default: 30 seconds.
128128
ConnectTimeout *int `json:"connect_timeout,omitempty"`
129129

130+
// ToolTimeout is the per-server maximum time (seconds) to wait for a single tool invocation.
131+
// When set to a positive value, this overrides the global gateway.toolTimeout for calls to
132+
// this server only. Minimum: 10. Omit the field (or set to 0) to fall back to the global
133+
// gateway.toolTimeout (or MCP_GATEWAY_TOOL_TIMEOUT env fallback).
134+
ToolTimeout *int `json:"tool_timeout,omitempty"`
135+
130136
// AdditionalProperties stores any extra fields for custom server types
131137
// This allows custom schemas to define their own fields beyond the standard ones
132138
AdditionalProperties map[string]interface{} `json:"-"`
@@ -170,6 +176,7 @@ func (s *StdinServerConfig) UnmarshalJSON(data []byte) error {
170176
"guard": true,
171177
"auth": true,
172178
"connect_timeout": true,
179+
"tool_timeout": true,
173180
}
174181

175182
// Store additional properties (fields not in the struct)
@@ -429,6 +436,9 @@ func convertStdinServerConfig(name string, server *StdinServerConfig, customSche
429436
if server.ConnectTimeout != nil {
430437
serverCfg.ConnectTimeout = *server.ConnectTimeout
431438
}
439+
if server.ToolTimeout != nil {
440+
serverCfg.ToolTimeout = *server.ToolTimeout
441+
}
432442
if server.Auth != nil {
433443
serverCfg.Auth = &AuthConfig{
434444
Type: server.Auth.Type,

internal/config/config_test.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,3 +1725,122 @@ func TestLoadFromStdin_WithEmptyTrustedBots(t *testing.T) {
17251725
require.Error(t, err)
17261726
assert.ErrorContains(t, err, "trustedBots")
17271727
}
1728+
1729+
// TestLoadFromStdin_HTTPServerWithToolTimeout verifies that tool_timeout is parsed and
1730+
// converted correctly from a stdin JSON HTTP server configuration.
1731+
func TestLoadFromStdin_HTTPServerWithToolTimeout(t *testing.T) {
1732+
jsonConfig := `{
1733+
"mcpServers": {
1734+
"repo-mind": {
1735+
"type": "http",
1736+
"url": "http://127.0.0.1:8000/mcp",
1737+
"tool_timeout": 600
1738+
}
1739+
},
1740+
"gateway": {
1741+
"port": 3000,
1742+
"domain": "localhost",
1743+
"apiKey": "test-key"
1744+
}
1745+
}`
1746+
1747+
oldStdin := os.Stdin
1748+
defer func() { os.Stdin = oldStdin }()
1749+
r, w, err := os.Pipe()
1750+
require.NoError(t, err)
1751+
os.Stdin = r
1752+
go func() {
1753+
defer w.Close()
1754+
_, _ = w.Write([]byte(jsonConfig))
1755+
}()
1756+
1757+
cfg, err := LoadFromStdin()
1758+
require.NoError(t, err, "LoadFromStdin() should succeed with tool_timeout")
1759+
1760+
server, ok := cfg.Servers["repo-mind"]
1761+
require.True(t, ok, "Server 'repo-mind' should be present")
1762+
assert.Equal(t, 600, server.ToolTimeout, "Per-server tool_timeout should be 600")
1763+
}
1764+
1765+
// TestLoadFromStdin_HTTPServerToolTimeoutOverridesGlobal verifies that the per-server
1766+
// tool_timeout in a server config takes precedence over gateway.toolTimeout.
1767+
func TestLoadFromStdin_HTTPServerToolTimeoutOverridesGlobal(t *testing.T) {
1768+
jsonConfig := `{
1769+
"mcpServers": {
1770+
"fast-server": {
1771+
"type": "http",
1772+
"url": "http://127.0.0.1:8001/mcp"
1773+
},
1774+
"slow-server": {
1775+
"type": "http",
1776+
"url": "http://127.0.0.1:8002/mcp",
1777+
"tool_timeout": 300
1778+
}
1779+
},
1780+
"gateway": {
1781+
"port": 3000,
1782+
"domain": "localhost",
1783+
"apiKey": "test-key",
1784+
"toolTimeout": 60
1785+
}
1786+
}`
1787+
1788+
oldStdin := os.Stdin
1789+
defer func() { os.Stdin = oldStdin }()
1790+
r, w, err := os.Pipe()
1791+
require.NoError(t, err)
1792+
os.Stdin = r
1793+
go func() {
1794+
defer w.Close()
1795+
_, _ = w.Write([]byte(jsonConfig))
1796+
}()
1797+
1798+
cfg, err := LoadFromStdin()
1799+
require.NoError(t, err, "LoadFromStdin() should succeed")
1800+
1801+
// Global timeout
1802+
assert.Equal(t, 60, cfg.Gateway.ToolTimeout, "Global toolTimeout should be 60")
1803+
1804+
// fast-server: no per-server timeout, inherits global via callBackendTool
1805+
fastServer, ok := cfg.Servers["fast-server"]
1806+
require.True(t, ok)
1807+
assert.Equal(t, 0, fastServer.ToolTimeout, "fast-server should have no per-server tool_timeout")
1808+
1809+
// slow-server: per-server timeout 300 overrides global 60
1810+
slowServer, ok := cfg.Servers["slow-server"]
1811+
require.True(t, ok)
1812+
assert.Equal(t, 300, slowServer.ToolTimeout, "slow-server should have per-server tool_timeout 300")
1813+
}
1814+
1815+
// TestLoadFromStdin_HTTPServerToolTimeoutBelowMinimum verifies that a per-server
1816+
// tool_timeout below the minimum (10) is rejected.
1817+
func TestLoadFromStdin_HTTPServerToolTimeoutBelowMinimum(t *testing.T) {
1818+
jsonConfig := `{
1819+
"mcpServers": {
1820+
"repo-mind": {
1821+
"type": "http",
1822+
"url": "http://127.0.0.1:8000/mcp",
1823+
"tool_timeout": 5
1824+
}
1825+
},
1826+
"gateway": {
1827+
"port": 3000,
1828+
"domain": "localhost",
1829+
"apiKey": "test-key"
1830+
}
1831+
}`
1832+
1833+
oldStdin := os.Stdin
1834+
defer func() { os.Stdin = oldStdin }()
1835+
r, w, err := os.Pipe()
1836+
require.NoError(t, err)
1837+
os.Stdin = r
1838+
go func() {
1839+
defer w.Close()
1840+
_, _ = w.Write([]byte(jsonConfig))
1841+
}()
1842+
1843+
_, err = LoadFromStdin()
1844+
require.Error(t, err, "LoadFromStdin() should fail with tool_timeout below minimum")
1845+
assert.ErrorContains(t, err, "tool_timeout")
1846+
}

internal/config/schema/mcp-gateway-config.schema.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,21 @@
125125
"default": [
126126
"*"
127127
]
128+
},
129+
"tool_timeout": {
130+
"description": "Per-server tool invocation timeout in seconds. When set to a positive value (minimum 10), overrides the global gateway.toolTimeout for tool calls to this server only. Set to 0 or omit the field to fall back to the global gateway.toolTimeout.",
131+
"oneOf": [
132+
{
133+
"type": "integer",
134+
"const": 0,
135+
"description": "0 means unset: fall back to gateway.toolTimeout."
136+
},
137+
{
138+
"type": "integer",
139+
"minimum": 10,
140+
"description": "Positive timeout value in seconds (minimum 10)."
141+
}
142+
]
128143
}
129144
},
130145
"required": [
@@ -180,6 +195,21 @@
180195
"type": "object",
181196
"description": "Guard policies for access control at the MCP gateway level. The structure of guard policies is server-specific. For GitHub MCP server, see the GitHub guard policy schema. For other servers (Jira, WorkIQ), different policy schemas will apply.",
182197
"additionalProperties": true
198+
},
199+
"tool_timeout": {
200+
"description": "Per-server tool invocation timeout in seconds. When set to a positive value (minimum 10), overrides the global gateway.toolTimeout for tool calls to this server only. Set to 0 or omit the field to fall back to the global gateway.toolTimeout.",
201+
"oneOf": [
202+
{
203+
"type": "integer",
204+
"const": 0,
205+
"description": "0 means unset: fall back to gateway.toolTimeout."
206+
},
207+
{
208+
"type": "integer",
209+
"minimum": 10,
210+
"description": "Positive timeout value in seconds (minimum 10)."
211+
}
212+
]
183213
}
184214
},
185215
"required": [

internal/config/validation.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ func validateStandardServerConfig(name string, server *StdinServerConfig, jsonPa
118118
}
119119
}
120120

121+
// Validate per-server tool_timeout if provided and non-zero.
122+
// A value of 0 means "unset – fall back to the global gateway timeout".
123+
if server.ToolTimeout != nil && *server.ToolTimeout != 0 {
124+
if err := rules.TimeoutMinimum(*server.ToolTimeout, ToolTimeoutMin, "tool_timeout", jsonPath+".tool_timeout"); err != nil {
125+
logValidateServerFailed(name, fmt.Sprintf("tool_timeout %d is below minimum %d", *server.ToolTimeout, ToolTimeoutMin))
126+
return err
127+
}
128+
}
129+
121130
logValidateServerPassed(name)
122131
return nil
123132
}

internal/config/validation_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,3 +1082,101 @@ func TestValidateAuthConfig(t *testing.T) {
10821082
})
10831083
}
10841084
}
1085+
1086+
// TestValidatePerServerToolTimeout tests per-server tool_timeout validation.
1087+
func TestValidatePerServerToolTimeout(t *testing.T) {
1088+
tests := []struct {
1089+
name string
1090+
server *StdinServerConfig
1091+
shouldErr bool
1092+
errMsg string
1093+
}{
1094+
{
1095+
name: "valid tool_timeout on http server",
1096+
server: &StdinServerConfig{
1097+
Type: "http",
1098+
URL: "https://example.com/mcp",
1099+
ToolTimeout: intPtr(600),
1100+
},
1101+
shouldErr: false,
1102+
},
1103+
{
1104+
name: "valid tool_timeout at minimum (10) on http server",
1105+
server: &StdinServerConfig{
1106+
Type: "http",
1107+
URL: "https://example.com/mcp",
1108+
ToolTimeout: intPtr(10),
1109+
},
1110+
shouldErr: false,
1111+
},
1112+
{
1113+
name: "tool_timeout large value (3600 = 1 hour) on http server",
1114+
server: &StdinServerConfig{
1115+
Type: "http",
1116+
URL: "https://example.com/mcp",
1117+
ToolTimeout: intPtr(3600),
1118+
},
1119+
shouldErr: false,
1120+
},
1121+
{
1122+
name: "tool_timeout below minimum (9) on http server",
1123+
server: &StdinServerConfig{
1124+
Type: "http",
1125+
URL: "https://example.com/mcp",
1126+
ToolTimeout: intPtr(9),
1127+
},
1128+
shouldErr: true,
1129+
errMsg: "tool_timeout",
1130+
},
1131+
{
1132+
name: "tool_timeout of 0 on http server (treated as unset, falls back to global)",
1133+
server: &StdinServerConfig{
1134+
Type: "http",
1135+
URL: "https://example.com/mcp",
1136+
ToolTimeout: intPtr(0),
1137+
},
1138+
shouldErr: false,
1139+
},
1140+
{
1141+
name: "valid tool_timeout on stdio server",
1142+
server: &StdinServerConfig{
1143+
Type: "stdio",
1144+
Container: "ghcr.io/owner/image:latest",
1145+
ToolTimeout: intPtr(120),
1146+
},
1147+
shouldErr: false,
1148+
},
1149+
{
1150+
name: "tool_timeout below minimum on stdio server",
1151+
server: &StdinServerConfig{
1152+
Type: "stdio",
1153+
Container: "ghcr.io/owner/image:latest",
1154+
ToolTimeout: intPtr(5),
1155+
},
1156+
shouldErr: true,
1157+
errMsg: "tool_timeout",
1158+
},
1159+
{
1160+
name: "no tool_timeout set (omitted)",
1161+
server: &StdinServerConfig{
1162+
Type: "http",
1163+
URL: "https://example.com/mcp",
1164+
},
1165+
shouldErr: false,
1166+
},
1167+
}
1168+
1169+
for _, tt := range tests {
1170+
t.Run(tt.name, func(t *testing.T) {
1171+
err := validateStandardServerConfig("test-server", tt.server, "mcpServers.test-server")
1172+
if tt.shouldErr {
1173+
require.Error(t, err)
1174+
if tt.errMsg != "" {
1175+
assert.ErrorContains(t, err, tt.errMsg)
1176+
}
1177+
} else {
1178+
assert.NoError(t, err)
1179+
}
1180+
})
1181+
}
1182+
}

0 commit comments

Comments
 (0)