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
* feat(telemetry): emit docker isolation flag, CLI source + docker failure codes (schema v5)
Make the #696 (docker CLI not on PATH) and image/interpreter-mismatch failure
modes visible in the telemetry dashboard instead of guessing.
Feature flags (schema bumped 4 -> 5, additive / forward-compatible):
- docker_isolation_enabled (bool) from cfg.DockerIsolation in
BuildFeatureFlagSnapshot — distinguishes "isolation on, 0 matching servers"
from "isolation off".
- docker_cli_source (enum path|bundled|login_shell|absent) — the direct #696
fleet signal. New shellwrap.ResolveDockerSource reports which resolution
branch found docker (shares the docker-path cache); Runtime.GetDockerCLISource
mirrors IsDockerAvailable. Coarse enum only — never the path string.
Diagnostics (flow into error_code_counts_24h, MCPX-prefix gated/PII-safe):
- MCPX_DOCKER_CLI_NOT_FOUND — isolation requested but docker binary unresolved.
- MCPX_DOCKER_EXEC_NOT_FOUND — in-container interpreter missing (e.g. uvx
absent in python:3.11); previously misclassified as MCPX_STDIO_SPAWN_ENOENT.
- MCPX_DOCKER_OCI_RUNTIME — OCI runtime / exec-format failures.
ClassifierHints gains DockerIsolated; the supervisor threads it from a
side-effect-free mirror of IsolationManager.ShouldIsolate so containerized
ENOENT routes to DOCKER codes. Catalog entries added for all three.
Privacy: all new fields are coarse enums/bools; docker_cli_source canary test
asserts only the 4 enum values ever reach the wire and ScanForPII stays green.
Docs/contracts updated (docs/features/telemetry.md + v2/v3 contract notes).
DASHBOARD HAND-OFF (telemetry.mcpproxy.app, separate service): add panels for
feature_flags.docker_isolation_enabled, the 4-way docker_cli_source breakdown,
and the 3 new MCPX_DOCKER_* rows in the error_code_counts_24h panel.
Related #696
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* test(httpapi): add GetDockerCLISource to fakeRuntimeStats for schema v5
The RuntimeStats interface gained GetDockerCLISource() (MCP-2745); the httpapi
telemetry payload test's fake stub must implement it or the package fails to
typecheck under the full-repo CI lint.
Related #696
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(shellwrap): set docker source on stat-probe cache upgrade (Codex P2)
ResolveDockerPath's MCP-2744 stat-probe override (upgrading a live cached
negative to a permanent success when the well-known-path probe finds docker)
updated dockerPath/dockerPathErr but not dockerPathSource. A stale 'absent'
from the prior failed resolution then leaked into docker_cli_source telemetry
(schema v5) even though docker WAS resolved via the bundled path. Set
dockerPathSource = DockerSourceBundled in that branch.
Regression test asserts a cached negative upgraded via the stat probe reports
'bundled', not 'absent' (fails without the one-line fix).
Related #696
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(diagnostics): classify zsh 'command not found: docker' as CLI-not-found (Codex P2 r3)
zsh's login-shell wording is reversed — 'zsh:1: command not found: docker'
(name AFTER the colon), not bash/sh's 'docker: command not found'. The CLI-not-
found case only matched the bash shape, so the common macOS #696 scenario fell
through to the generic 'command not found' branch and recorded
MCPX_DOCKER_EXEC_NOT_FOUND, conflating host-CLI-missing (#696) with in-container
exec ENOENT. Match 'command not found: docker' in both the isolation-spawn
helper and the unhinted classifyDocker fallback.
Added a classifier table case for the zsh wording (verified red→green: routed to
EXEC_NOT_FOUND without the fix, CLI_NOT_FOUND with it).
Related #696
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(shellwrap): unify ResolveDockerSource with ResolveDockerPath cache path (Codex P2 r4)
ResolveDockerSource honored a live cached negative by returning 'absent'
WITHOUT running the well-known-path stat probe that ResolveDockerPath uses
during the negative-TTL window — so docker_cli_source reported 'absent' while
ResolveDockerPath returned the bundled path (schema-v5 telemetry mismatch).
Root-cause the recurring divergence (this is the 2nd source-tracking drift
between the two functions) by extracting a single shared resolveDockerPathLocked
helper that is the only writer of the cache + dockerPathSource. ResolveDockerPath
and ResolveDockerSource now drive the identical cache path, including the
MCP-2744 override, so they cannot disagree on the same cache state.
Regression test asserts ResolveDockerSource reports 'bundled' (not 'absent')
when a well-known docker appears during the negative-TTL window — verified
red->green against the shared-resolver refactor.
Related #696
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(diagnostics): don't label non-docker 'exec format error' as OCI (Codex P2 r5)
The unhinted classifyDocker matched a BARE 'exec format error' as
MCPX_DOCKER_OCI_RUNTIME, but a non-docker wrong-architecture host stdio binary
emits the same string (ENOEXEC) — it would be mislabeled a Docker/OCI failure.
- classifyDocker OCI case now requires real OCI/runc context ('oci runtime' /
'runc'); bare 'exec format error' no longer matches there.
- classifyStdio routes a bare 'exec format error' (and typed syscall.ENOEXEC)
to a new STDIO code MCPX_STDIO_SPAWN_EXEC_FORMAT, guarded against docker OCI
context so a real containerized exec-format still falls through to OCI.
- The docker-isolated path keeps classifying bare 'exec format error' as OCI
via the hinted classifyDockerIsolatedSpawn.
- New catalog entry for MCPX_STDIO_SPAWN_EXEC_FORMAT.
Classifier table cases: bare exec-format WITH hint -> OCI; bare exec-format
WITHOUT hint -> STDIO (verified red MCPX_DOCKER_OCI_RUNTIME -> green
MCPX_STDIO_SPAWN_EXEC_FORMAT); 'oci runtime' context without hint -> OCI.
Related #696
Co-Authored-By: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Copy file name to clipboardExpand all lines: docs/features/telemetry.md
+7-1Lines changed: 7 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ MCPProxy collects anonymous usage statistics to help improve the product. This p
4
4
5
5
## What is collected
6
6
7
-
MCPProxy sends a **daily heartbeat** containing only aggregate, non-identifying information. The current schema is **version 3** (`schema_version: 3` in the JSON payload); the schema is forward-compatible so older consumers simply ignore fields they don't recognize.
7
+
MCPProxy sends a **daily heartbeat** containing only aggregate, non-identifying information. The current schema is **version 5** (`schema_version: 5` in the JSON payload); the schema is forward-compatible so older consumers simply ignore fields they don't recognize.
8
8
9
9
| Field | Example | Purpose |
10
10
|-------|---------|---------|
@@ -22,9 +22,15 @@ MCPProxy sends a **daily heartbeat** containing only aggregate, non-identifying
22
22
|`feature_flags.docker_available`|`true`| Fraction of installs with a reachable Docker daemon (schema v3) |
23
23
|`server_protocol_counts`|`{"stdio":3,"http":2,"sse":0,"streamable_http":1,"auto":0}`| Ratio of remote-HTTP vs local-stdio upstreams (schema v3) |
24
24
|`server_docker_isolated_count`|`2`| How many configured servers the runtime actually wraps in Docker isolation (schema v3) |
25
+
|`feature_flags.docker_isolation_enabled`|`true`| Whether global Docker isolation is turned on (schema v5). Lets us tell "isolation on, 0 matching servers" apart from "isolation off" |
26
+
|`feature_flags.docker_cli_source`|`bundled`| How the `docker` CLI was located — fixed enum `path` / `bundled` / `login_shell` / `absent` (schema v5). The direct signal for "Docker installed but not on the spawn PATH" (issue #696). **Never** the path string itself |
25
27
26
28
The `server_protocol_counts` map uses a **fixed enum of keys** (`stdio`, `http`, `sse`, `streamable_http`, `auto`) — server names and URLs are never included. Unknown or misconfigured protocol values are bucketed into `auto`.
27
29
30
+
The `docker_cli_source` field is likewise a **fixed enum** (`path`, `bundled`, `login_shell`, `absent`); the resolved path is never transmitted.
31
+
32
+
Docker isolation failures surface in `error_code_counts_24h` via three stable diagnostic codes (schema v5): `MCPX_DOCKER_CLI_NOT_FOUND` (isolation requested but the `docker` binary is unresolved — issue #696), `MCPX_DOCKER_EXEC_NOT_FOUND` (the image lacks the interpreter the server needs, e.g. `uvx` missing in `python:3.11`), and `MCPX_DOCKER_OCI_RUNTIME` (OCI runtime / architecture-mismatch failures).
Copy file name to clipboardExpand all lines: internal/diagnostics/registry.go
+38Lines changed: 38 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -51,6 +51,16 @@ func seedSTDIO() {
51
51
},
52
52
DocsURL: docsURL(STDIOSpawnEACCES),
53
53
})
54
+
register(CatalogEntry{
55
+
Code: STDIOSpawnExecFormat,
56
+
Severity: SeverityError,
57
+
UserMessage: "The configured command is the wrong CPU architecture or not an executable (exec format error). Install a build that matches this machine.",
58
+
FixSteps: []FixStep{
59
+
{Type: FixStepCommand, Label: "Check the binary's architecture", Command: "file <command-path>"},
60
+
{Type: FixStepLink, Label: "Install a matching build", URL: docsURL(STDIOSpawnExecFormat)},
61
+
},
62
+
DocsURL: docsURL(STDIOSpawnExecFormat),
63
+
})
54
64
register(CatalogEntry{
55
65
Code: STDIOExitNonzero,
56
66
Severity: SeverityError,
@@ -292,6 +302,34 @@ func seedDOCKER() {
292
302
},
293
303
DocsURL: docsURL(DockerSnapAppArmor),
294
304
})
305
+
register(CatalogEntry{
306
+
Code: DockerCLINotFound,
307
+
Severity: SeverityError,
308
+
UserMessage: "Docker isolation is enabled but the `docker` command could not be found. Install Docker, or add its CLI to your PATH.",
309
+
FixSteps: []FixStep{
310
+
{Type: FixStepCommand, Label: "Check docker is on PATH", Command: "docker --version"},
0 commit comments