Skip to content
Merged
10 changes: 9 additions & 1 deletion cmd/mcpproxy-tray/internal/api/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"runtime"
"strings"

internalRuntime "github.com/smart-mcp-proxy/mcpproxy-go/internal/runtime"
"github.com/smart-mcp-proxy/mcpproxy-go/internal/tray"
Expand Down Expand Up @@ -307,8 +308,15 @@ func (a *ServerAdapter) ReloadConfiguration() error {
return fmt.Errorf("ReloadConfiguration not yet supported via API")
}

// GetConfigPath returns the configuration file path
// GetConfigPath returns the configuration file path core is running with.
// The tray passes MCPPROXY_TRAY_CONFIG_PATH to core as --config (see
// buildCoreArgs in main.go), so tray-side config consumers — e.g. the Spec 079
// update_check gate — must resolve to that same override, not a hardcoded
// default. Falls back to the default ~/.mcpproxy path when unset.
func (a *ServerAdapter) GetConfigPath() string {
if cfg := strings.TrimSpace(os.Getenv("MCPPROXY_TRAY_CONFIG_PATH")); cfg != "" {
return cfg
}
homeDir, err := os.UserHomeDir()
if err != nil {
return "~/.mcpproxy/mcp_config.json" // fallback
Expand Down
27 changes: 27 additions & 0 deletions cmd/mcpproxy-tray/internal/api/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -645,3 +645,30 @@ func TestHealthDataFlow_EndToEnd(t *testing.T) {
status := adapter.GetStatus().(map[string]interface{})
assert.Equal(t, 1, status["connected_servers"], "Status should use health.level for connected count")
}

// =============================================================================
// ServerAdapter.GetConfigPath Tests
// =============================================================================

// The tray launches core with --config <MCPPROXY_TRAY_CONFIG_PATH> (see
// buildCoreArgs in main.go). GetConfigPath must resolve to that SAME path so
// tray-side config consumers (e.g. the Spec 079 update_check gate) read the
// config core is actually using — not a hardcoded default.
func TestServerAdapter_GetConfigPath_HonorsTrayConfigPathEnv(t *testing.T) {
const custom = "/tmp/custom-tray-config/mcp_config.json"
t.Setenv("MCPPROXY_TRAY_CONFIG_PATH", custom)

adapter := NewServerAdapter(NewMockClient())

assert.Equal(t, custom, adapter.GetConfigPath())
}

func TestServerAdapter_GetConfigPath_DefaultWhenEnvUnset(t *testing.T) {
t.Setenv("MCPPROXY_TRAY_CONFIG_PATH", "")

adapter := NewServerAdapter(NewMockClient())

got := adapter.GetConfigPath()
assert.Contains(t, got, "mcp_config.json")
assert.NotEqual(t, "", got)
}
4 changes: 2 additions & 2 deletions docs/api/rest-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ Get application info, version, and update availability.
| `listen_addr` | string | Server listen address |
| `endpoints.http` | string | HTTP API endpoint address |
| `endpoints.socket` | string | Unix socket path (empty if disabled) |
| `update` | object | Update information (may be null if not checked yet) |
| `update` | object | Update information (may be null if not checked yet; omitted entirely when update checking is disabled via `update_check.enabled: false` or `MCPPROXY_DISABLE_AUTO_UPDATE=true`) |
| `update.available` | boolean | Whether a newer version is available |
| `update.latest_version` | string | Latest version available on GitHub |
| `update.release_url` | string | URL to the GitHub release page |
Expand All @@ -802,7 +802,7 @@ Get application info, version, and update availability.
| `update.check_error` | string | Error message if update check failed |

:::tip Update Checking
MCPProxy automatically checks for updates every 4 hours. The update information is exposed via this endpoint and used by the tray application and web UI to show update notifications.
MCPProxy automatically checks for updates every 4 hours. The update information is exposed via this endpoint and used by the tray application and web UI to show update notifications. Use `?refresh=true` to force an immediate re-check. Checking is controlled by the `update_check` config block (`enabled`, `channel`) — see [Version Updates](/features/version-updates); when disabled, `?refresh=true` performs no check and the `update` object is omitted.
:::

### Docker
Expand Down
1 change: 1 addition & 0 deletions docs/cli/status-command.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ When the daemon is running, `status` surfaces the result of the background updat
- **Update available**: `Version: v1.2.0 (update available: v1.3.0 — <release URL>)`
- **Up to date**: `Version: v1.3.0 (latest)`
- **Check failed or not yet completed** (offline, rate-limited): the version is shown without any annotation. In JSON output the `update.check_error` field retains the failure reason for diagnostics.
- **Update checking disabled** (`update_check.enabled: false` in the config, or `MCPPROXY_DISABLE_AUTO_UPDATE=true`): the daemon performs no check and omits the `update` object entirely, so the version is shown without any annotation.

In machine-readable output (`-o json`/`-o yaml`) the `update` object also carries `checked_at` (when the last successful check ran, so consumers can judge staleness) and `is_prerelease` (whether the offered version is a prerelease), matching the `/api/v1/info` contract.

Expand Down
46 changes: 45 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ Complete reference for MCPProxy configuration file (`mcp_config.json`). This doc
11. [Code Execution](#code-execution)
12. [Feature Flags](#feature-flags)
13. [Registries](#registries)
14. [Complete Example](#complete-example)
14. [Update Check](#update-check)
15. [Complete Example](#complete-example)

---

Expand Down Expand Up @@ -1033,6 +1034,49 @@ and are hot-reloadable. Non-positive values fall back to the defaults.

---

## Update Check

Controls the background upgrade-awareness checker (Spec 079). MCPProxy
periodically queries GitHub Releases and surfaces "update available" on
`mcpproxy status` / `doctor`, a startup log line, the Web UI (sidebar badge +
dismissible banner), and the trays. Checks never block and fail silently when
offline.

```json
{
"update_check": {
"enabled": true,
"channel": "stable"
}
}
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `enabled` | boolean | `true` | Master switch for update checking. When `false`, no network check is performed (background poll *and* the manual `/api/v1/info?refresh=true` re-check) and no upgrade nudge appears on any surface — the `update` object is omitted from `/api/v1/info`. |
| `channel` | string | `"stable"` | Release channel: `"stable"` (GitHub `releases/latest`; prereleases never offered) or `"rc"` (prerelease tags such as `v0.47.0-rc.1` included). |

Both keys are optional and hot-reloadable: editing them (config file or
`POST /api/v1/config/apply`) takes effect without a restart, and re-enabling
triggers a prompt re-check.

**Environment-variable precedence** — the existing switches keep working and
**win over** the config keys (operator override):

| Variable | Effect |
|----------|--------|
| `MCPPROXY_DISABLE_AUTO_UPDATE=true` | Force-disables update checking even when `update_check.enabled` is `true`. |
| `MCPPROXY_ALLOW_PRERELEASE_UPDATES=true` | Force-selects the prerelease (`rc`) channel even when `update_check.channel` is `stable`. |

The env vars only widen in one direction (disable checks / enable
prereleases); they cannot force-enable checking that config disabled — with
`update_check.enabled: false`, checks stay off regardless of environment.

See [Version Updates](features/version-updates.md) for where updates are
surfaced.

---

## Complete Example

Here's a complete configuration example with all major sections:
Expand Down
21 changes: 21 additions & 0 deletions docs/configuration/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ MCPProxy uses a JSON configuration file located at `~/.mcpproxy/mcp_config.json`
"features": {
"enable_web_ui": true
},
"update_check": {
"enabled": true,
"channel": "stable"
},
"mcpServers": []
}
```
Expand Down Expand Up @@ -112,6 +116,23 @@ Both cadences are configurable globally, and can be overridden per server (see [
| `code_execution_max_tool_calls` | integer | `0` | Maximum tool calls (0 = unlimited) |
| `code_execution_pool_size` | integer | `10` | VM pool size for code execution |

### Update Check Settings

Controls the background upgrade-awareness checker. Both keys are optional and
hot-reloadable (no restart needed).

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `update_check.enabled` | boolean | `true` | Master switch. When `false`, no network check runs (background poll and manual re-check) and no upgrade nudge appears on any surface — the `update` object is omitted from `/api/v1/info`. |
| `update_check.channel` | string | `"stable"` | Release channel: `"stable"` (prereleases never offered) or `"rc"` (prerelease tags like `v0.47.0-rc.1` included). |

The existing environment switches keep working and **win over** these keys:
`MCPPROXY_DISABLE_AUTO_UPDATE=true` force-disables checking, and
`MCPPROXY_ALLOW_PRERELEASE_UPDATES=true` force-selects the prerelease channel.
They only widen in one direction — they cannot re-enable checking that the
config disabled. See [Version Updates](/features/version-updates) for where
updates are surfaced.

### MCP Servers

See [Upstream Servers](/configuration/upstream-servers) for detailed server configuration.
Expand Down
15 changes: 10 additions & 5 deletions docs/configuration/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,19 @@ The tray application doesn't read the config file directly. It launches the core
| `MCPPROXY_TRAY_ENDPOINT` | Override tray-core communication endpoint (unix:///path/socket.sock or npipe:////./pipe/name) | Auto-detect |
| `MCPPROXY_TRAY_INSPECT_ADDR` | Address for tray instrumentation/debug server | - |

### Auto-Update Settings (Tray)
### Auto-Update Settings

| Variable | Description | Default |
|----------|-------------|---------|
| `MCPPROXY_DISABLE_AUTO_UPDATE` | Disable automatic update checks | `false` |
| `MCPPROXY_UPDATE_NOTIFY_ONLY` | Only notify about updates, don't auto-install | `false` |
| `MCPPROXY_ALLOW_PRERELEASE_UPDATES` | Allow prerelease/beta version updates | `false` |
| `MCPPROXY_UPDATE_APP_BUNDLE` | Enable app bundle updates (macOS) | `false` |
| `MCPPROXY_DISABLE_AUTO_UPDATE` | Disable automatic update checks (core + tray) | `false` |
| `MCPPROXY_UPDATE_NOTIFY_ONLY` | Only notify about updates, don't auto-install (tray) | `false` |
| `MCPPROXY_ALLOW_PRERELEASE_UPDATES` | Allow prerelease/beta version updates (core + tray) | `false` |
| `MCPPROXY_UPDATE_APP_BUNDLE` | Enable app bundle updates (macOS tray) | `false` |

Update checking can also be controlled from the config file via the
`update_check` block (`enabled`, `channel`) — see
[Version Updates](/features/version-updates). When both are set, the
environment variables **win** over the config keys.

### Setting Tray Variables on macOS

Expand Down
70 changes: 66 additions & 4 deletions docs/features/version-updates.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ The sidebar displays the current version at the bottom. When an update is availa
- A small "update available" badge appears next to the version
- Click to view the release notes

The dashboard additionally shows a **dismissible update banner** ("Update
available: vX — you are running vY") with a release-notes link. Dismissal is
**per version**: dismissing the banner for v1.3.0 keeps it hidden for v1.3.0
(persisted in the browser), but the banner reappears when a newer release
becomes available. The banner is non-modal and never blocks the UI.

### CLI Doctor Command

The `mcpproxy doctor` command shows version information:
Expand All @@ -55,20 +61,75 @@ Download: https://github.com/smart-mcp-proxy/mcpproxy-go/releases/tag/v1.3.0

## Configuration

### Config File (`update_check`)

Update checking is controlled from `mcp_config.json` via the `update_check`
block:

```json
{
"update_check": {
"enabled": true,
"channel": "stable"
}
}
```

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `enabled` | boolean | `true` | Master switch. When `false`, no network check is performed (background poll and the manual re-check) and no upgrade nudge appears on any surface — the `update` object is omitted from `/api/v1/info`. |
| `channel` | string | `"stable"` | Release channel: `"stable"` (GitHub `releases/latest`; prereleases never offered) or `"rc"` (prerelease tags such as `v0.47.0-rc.1` included). |

Both keys are **hot-reloadable**: editing the config file or applying it via
`POST /api/v1/config/apply` takes effect without a restart. Re-enabling (or
switching channels) triggers a prompt re-check instead of waiting for the next
4-hour tick.

`enabled: false` also gates the Go tray's built-in daily self-update check, so
no surface performs a network check while disabled. The tray does **not** read
`mcp_config.json` itself (it holds no state); instead it asks the core via
`GET /api/v1/info` before checking — the core omits the `update` object when
update checking is disabled, and the tray then skips its own network check. If
the core is unreachable the tray skips that tick and retries, rather than
falling open to a check the operator may have disabled. The tray's own check
still selects prereleases via `MCPPROXY_ALLOW_PRERELEASE_UPDATES` only —
converging it fully onto the shared checker (including `channel`) is a separate
Spec 079 work item (FR-001a).

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `MCPPROXY_DISABLE_AUTO_UPDATE` | Disable background update checks entirely | `false` |
| `MCPPROXY_ALLOW_PRERELEASE_UPDATES` | Include prerelease/beta versions in update checks | `false` |

### Precedence (env vs config)

The environment switches **win over** the `update_check` config keys — they
are the operator override:

- `MCPPROXY_DISABLE_AUTO_UPDATE=true` disables checking even when
`update_check.enabled` is `true`.
- `MCPPROXY_ALLOW_PRERELEASE_UPDATES=true` selects the prerelease channel even
when `update_check.channel` is `"stable"`.

The env vars only widen in one direction (disable checks / include
prereleases). They cannot re-enable checking that the config disabled: with
`update_check.enabled: false`, no check runs regardless of environment.

### Examples

```bash
# Disable update checking
# Disable update checking (config file — persistent, hot-reloads)
# "update_check": { "enabled": false }

# Disable update checking (environment — wins over config)
MCPPROXY_DISABLE_AUTO_UPDATE=true mcpproxy serve

# Enable prerelease updates (for beta testers)
# Opt in to prerelease (RC) updates via config
# "update_check": { "channel": "rc" }

# Enable prerelease updates via environment (for beta testers)
MCPPROXY_ALLOW_PRERELEASE_UPDATES=true mcpproxy serve
```

Expand Down Expand Up @@ -127,7 +188,7 @@ When running a development build (version shows as "development"), update checki
### Update check not working

1. Ensure you have internet connectivity
2. Check if `MCPPROXY_DISABLE_AUTO_UPDATE` is set
2. Check if `MCPPROXY_DISABLE_AUTO_UPDATE` is set, or `update_check.enabled` is `false` in `mcp_config.json`
3. Run `mcpproxy doctor` to see current version status
4. Check logs for any GitHub API errors:
```bash
Expand All @@ -136,7 +197,8 @@ When running a development build (version shows as "development"), update checki

### Prerelease not showing

By default, prerelease versions are excluded. To enable:
By default, prerelease versions are excluded. To enable, set
`"update_check": { "channel": "rc" }` in `mcp_config.json`, or:

```bash
export MCPPROXY_ALLOW_PRERELEASE_UPDATES=true
Expand Down
2 changes: 1 addition & 1 deletion docs/prerelease-builds.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ The GitHub release is created with `prerelease: true`, so it does **not** become
- Does not deploy docs or trigger marketing automation (`deploy-docs`, `trigger-marketing-update` guarded).
- Not offered as an update on **stable channels**:
- The macOS tray uses GitHub `releases/latest`, which excludes prereleases (`native/macos/MCPProxy/MCPProxy/Services/UpdateService.swift`), plus a semver downgrade guard so an `-rc` is never treated as "newer" than the matching stable.
- The backend/tray update check is stable-only by default (`internal/tray/tray.go` → `releases/latest`). Set `MCPPROXY_ALLOW_PRERELEASE_UPDATES=true` to opt in to RC update offers.
- The backend/tray update check is stable-only by default (`internal/tray/tray.go` → `releases/latest`). Set `MCPPROXY_ALLOW_PRERELEASE_UPDATES=true` to opt in to RC update offers; the core checker can also opt in via `"update_check": { "channel": "rc" }` in `mcp_config.json` (Spec 079 — the Go tray's own self-update check gates on the core's decision by querying `GET /api/v1/info` rather than reading the config file, and selects prereleases via `MCPPROXY_ALLOW_PRERELEASE_UPDATES`; converging it fully onto the shared checker is FR-001a).

### Installing an RC

Expand Down
Loading
Loading