You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replaces the heavyweight tools/list liveness probe with the MCP-standard
lightweight `ping`, and makes both background cadences configurable globally
and per-server.
Ping liveness (US1, FR-001/002):
- Add Client.Ping wrapper to internal/upstream/core/client.go (mcp-go Ping).
- Rewrite managed performHealthCheck to probe via a narrow livenessProber
interface (the coreClient in prod; a fake in tests) instead of ListTools,
preserving the existing connection-error classification and flap resistance.
An idle proxy no longer emits recurring tools/list to upstreams (#608).
Configurable intervals (US2/US3, FR-004..009):
- *Duration tri-state (nil=inherit, "0s"=disabled, positive=interval) on both
Config (global) and ServerConfig (per-server): health_check_interval (30s
default) and tool_discovery_interval (5m default). Defaults live only in the
resolvers so unset keys behave exactly as before (SC-005).
- ResolveHealthCheckInterval / ResolveToolDiscoveryInterval: per-server ->
global -> default; resolved <=0 disables the loop.
- Validate() enforces health-check {0} U [5s,1h] and tool-discovery {0} U
[30s,24h] for every global and per-server pointer, with clear messages.
- backgroundHealthCheck and backgroundToolIndexing now use resettable timers
that re-resolve each cycle (hot-reload aware) and skip when disabled.
- Per-server overrides round-trip through UpstreamRecord (storage canary).
UI + docs:
- Web settings "Tool discovery & health checks" accordion (fields.ts) and the
mirrored macOS ConfigSection (SettingsCatalog.swift), duration controls with
0s=disabled help text.
- docs/configuration.md documents both keys, defaults, ranges, 0s semantics,
and the per-server override; swagger regenerated.
Related #608
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Copy file name to clipboardExpand all lines: docs/configuration.md
+50Lines changed: 50 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -96,6 +96,54 @@ MCPProxy looks for configuration in these locations (in order):
96
96
|`tool_response_limit`| integer |`20000`| Maximum characters in tool responses (0 = unlimited) |
97
97
|`call_tool_timeout`| string |`"2m"`| Timeout for tool calls (e.g., `"30s"`, `"2m"`, `"5m"`). **Note**: When using agents like Codex or Claude as MCP servers, you may need to increase this timeout significantly, even up to 10 minutes (`"10m"`), as these agents may require longer processing times for complex operations |
98
98
99
+
### Discovery & Health Checks
100
+
101
+
mcpproxy keeps each upstream connection alive and its tool index fresh with two
102
+
background loops. Both intervals are tunable globally and per server, so you can
103
+
quiet a chatty upstream that returns a large tool catalog.
104
+
105
+
```json
106
+
{
107
+
"health_check_interval": "30s",
108
+
"tool_discovery_interval": "5m"
109
+
}
110
+
```
111
+
112
+
| Field | Type | Default | Description |
113
+
|-------|------|---------|-------------|
114
+
|`health_check_interval`| duration |`"30s"`| How often to probe each connected server for liveness with a lightweight MCP `ping`. `"0s"` disables the periodic probe. Range: `5s`–`1h`. |
115
+
|`tool_discovery_interval`| duration |`"5m"`| How often to re-list every server's tools to rebuild the search index. `"0s"` disables the periodic sweep. Range: `30s`–`24h`. |
116
+
117
+
**Liveness uses `ping`, not `tools/list`.** The health-check loop issues the
118
+
MCP-standard `ping` request rather than re-listing every tool, so an idle proxy
119
+
no longer generates large recurring `tools/list` traffic to upstream servers
changes are still picked up reactively whenever a server pushes
122
+
`notifications/tools/list_changed`.
123
+
124
+
**Disabling a loop (`"0s"`).** Set either key to `"0s"` to turn the
125
+
corresponding loop off:
126
+
127
+
-`health_check_interval: "0s"` — no periodic liveness probe. A dead transport
128
+
is then detected lazily, on the next real tool call or discovery sweep, rather
129
+
than proactively.
130
+
-`tool_discovery_interval: "0s"` — no periodic index rebuild. Tools are still
131
+
discovered at connect time and whenever a server pushes
132
+
`notifications/tools/list_changed`. **Trade-off:** a server that does *not*
133
+
support `list_changed` will not have new/removed tools reflected until it
134
+
reconnects or you trigger a manual refresh.
135
+
136
+
An unset key behaves exactly as before this feature (the built-in default), and
137
+
a change to either interval takes effect on the next cycle without restarting
138
+
the proxy.
139
+
140
+
> **Per-server override.** Both keys can also be set on an individual server
141
+
> entry under `mcpServers[]` (see [Server Fields](#server-fields)) to override
142
+
> the global value for just that server; the per-server value wins, and `"0s"`
143
+
> disables the loop for that server only. A dedicated per-server form control in
144
+
> the Web UI / macOS app is planned; for now set per-server overrides via the
145
+
> Raw JSON editor or the REST API.
146
+
99
147
### Debug & Development
100
148
101
149
```json
@@ -150,6 +198,8 @@ MCPProxy looks for configuration in these locations (in order):
150
198
|`working_dir`| string | No | Working directory for stdio servers, or for the locally-launched child of an HTTP/SSE server (default: current directory) |
151
199
|`env`| object | No | Environment variables for stdio servers, or for the locally-launched child of an HTTP/SSE server |
152
200
|`launcher_wait_timeout`| duration | No | When `command` is set together with an HTTP/SSE `url`, how long mcpproxy waits for that URL to become reachable after spawning the child (e.g. `"15s"`, default `"30s"`) |
201
+
|`health_check_interval`| duration | No | Per-server override for the global [`health_check_interval`](#discovery--health-checks). `"0s"` disables the liveness probe for this server only. Range: `5s`–`1h`. Omit to inherit the global value. |
202
+
|`tool_discovery_interval`| duration | No | Per-server override for the global [`tool_discovery_interval`](#discovery--health-checks). Accepted and persisted for forward-compatibility; the periodic index rebuild currently runs on the global cadence. Range: `30s`–`24h`. Omit to inherit the global value. |
153
203
|`oauth`| object | No | OAuth configuration (see [OAuth Configuration](#oauth-configuration)) |
154
204
|`isolation`| object | No | Per-server Docker isolation settings (see [Docker Isolation](#docker-isolation)) |
155
205
|`enabled`| boolean | No | Enable/disable server (default: `true`) |
{key: 'activity_cleanup_interval_min',label: 'Cleanup runs every (minutes)',control: 'number',min: 1},
332
332
],
333
333
},
334
+
{
335
+
id: 'discovery',
336
+
title: 'Tool discovery & health checks',
337
+
description: 'How often mcpproxy probes upstream servers for liveness and re-discovers their tools. Lower these to reduce background traffic to chatty servers.',
338
+
fields: [
339
+
{key: 'health_check_interval',label: 'Health-check interval',help: 'How often to send a lightweight liveness ping to each connected server. "0s" disables the periodic probe (a dead server is then detected lazily on the next tool call). Range: 5s–1h. Default 30s.',control: 'duration',placeholder: '30s',optional: true},
340
+
{key: 'tool_discovery_interval',label: 'Tool-discovery interval',help: 'How often to re-list every server’s tools to rebuild the search index. "0s" disables the periodic sweep — tool changes are then picked up only at connect time and via tools/list_changed push notifications. Range: 30s–24h. Default 5m.',control: 'duration',placeholder: '5m',optional: true},
MaxResultSizeCharsint`json:"max_result_size_chars,omitempty" mapstructure:"max-result-size-chars"`// Advertised on every tool as `_meta.anthropic/maxResultSizeChars`; raises Claude Code's inline-response ceiling from 50k to up to 500k chars. Set to 0 to disable.
71
116
117
+
// Discovery & health-check cadence (spec 074, #608). Both are *Duration
118
+
// tri-state pointers: nil = inherit the built-in default; a pointer to 0s =
119
+
// the loop is disabled; a positive value = that interval. Defaults live only
120
+
// in the resolvers (ResolveHealthCheckInterval / ResolveToolDiscoveryInterval)
121
+
// so an unset key behaves exactly as before this feature (SC-005). Validated
EnabledTools []string`json:"enabled_tools,omitempty" mapstructure:"enabled_tools"`// Allowlist: only these tools are exposed; mutually exclusive with disabled_tools
261
-
DisabledTools []string`json:"disabled_tools,omitempty" mapstructure:"disabled_tools"`// Denylist: these tools are hidden; mutually exclusive with enabled_tools
314
+
315
+
// Per-server discovery & health-check overrides (spec 074). Same *Duration
316
+
// tri-state as the global keys: nil = inherit the global value (or default),
317
+
// pointer to 0s = disabled for this server, positive = that interval.
318
+
// HealthCheckInterval is fully wired into the per-server health loop;
319
+
// ToolDiscoveryInterval is accepted/validated and round-trips for
320
+
// forward-compat, but the periodic index sweep is governed by the global
321
+
// cadence in this iteration (see spec 074 plan §C).
EnabledTools []string`json:"enabled_tools,omitempty" mapstructure:"enabled_tools"`// Allowlist: only these tools are exposed; mutually exclusive with disabled_tools
326
+
DisabledTools []string`json:"disabled_tools,omitempty" mapstructure:"disabled_tools"`// Denylist: these tools are hidden; mutually exclusive with enabled_tools
262
327
263
328
// SourceRegistryID records which registry this server was added from (empty
264
329
// for manually-configured servers). MCP-866: surfaced in the approval /
0 commit comments