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
{{ message }}
Repository navigation
Commit 027f66d
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: cmd/mcpproxy/upstream_cmd.go
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -379,7 +379,7 @@ func init() {
379
379
upstreamAddCmd.Flags().StringVar(&upstreamAddTransport, "transport", "", "Transport type: http or stdio (auto-detected if not specified)")
380
380
upstreamAddCmd.Flags().BoolVar(&upstreamAddIfNotExists, "if-not-exists", false, "Don't error if server already exists")
381
381
upstreamAddCmd.Flags().BoolVar(&upstreamAddNoQuarantine, "no-quarantine", false, "Don't quarantine the new server (use with caution)")
382
-
upstreamAddCmd.Flags().StringVar(&upstreamAddTrustMode, "trust-mode", "", "Per-server trust tier governing admission AND tool-change approval: auto (approve without scanning), scan (auto-approve new tools only when the offline TPA scan is green; changed tools are held for review), manual (human reviews every change). Unset inherits the default (manual)")
382
+
upstreamAddCmd.Flags().StringVar(&upstreamAddTrustMode, "trust-mode", "", "Per-server trust tier governing admission AND tool-change approval: auto (approve without scanning), scan (new tools and changed tools are auto-approved only when the offline TPA scan is clean; findings are held for review), manual (human reviews every change). Unset inherits the default (scan)")
383
383
upstreamAddCmd.Flags().StringArrayVar(&upstreamAddSecretEnvs, "secret-env", nil, "Environment variable to store in the OS keyring instead of the config, in KEY=value format (repeatable, FR-065)")
384
384
upstreamAddCmd.Flags().StringArrayVar(&upstreamAddSecretHeaders, "secret-header", nil, "HTTP header to store in the OS keyring instead of the config, in 'Name: value' format (repeatable, FR-065)")
Copy file name to clipboardExpand all lines: docs/cli/review-commands.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -83,7 +83,7 @@ Changed since approval: create_issue
83
83
To let a server you trust update its tools without review, set auto_approve_tool_changes: true on it (https://docs.mcpproxy.app/features/security-quarantine).
84
84
```
85
85
86
-
Scan mode holds every changed contract; the diff and a one-step approval are what keeps that cheap. The Web review screen shows the same changes and an **Approve update (N changed tools)** button that approves exactly the changed tools on screen.
86
+
Manual mode holds every changed contract; scan mode (the default) holds the ones the offline scan flags, cannot cover, or that re-label the tool as safer, and approves the rest. The diff and a one-step approval are what keeps review cheap. The Web review screen shows the same changes and an **Approve update (N changed tools)** button that approves exactly the changed tools on screen.
87
87
88
88
Descriptions and schemas come from the upstream server and are shown as plain text; they are not verified. Without `--full` only the first line of each description is shown and the schemas are left out.
Copy file name to clipboardExpand all lines: docs/configuration.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -415,7 +415,7 @@ it and none can double-report it.
415
415
|`oauth`| object | No | OAuth configuration (see [OAuth Configuration](#oauth-configuration)) |
416
416
|`isolation`| object | No | Per-server Docker isolation settings (see [Docker Isolation](#docker-isolation)) |
417
417
|`enabled`| boolean | No | Enable/disable server (default: `true`) |
418
-
|`quarantined`| boolean | No | Security quarantine status. The default depends on how the server arrives. A server added through the UI, CLI, REST API or an AI agent follows its [trust mode](features/security-quarantine.md#trust-modes-auto--scan--manual) at add time (quarantined under the default `manual` mode). A server you add by editing this file, with no `quarantined` key and no prior `config.db` record, is **held for review** on first load when quarantine is enabled and its trust mode is not `auto` (no `trust_mode` means `manual`, unless a legacy `auto_approve_tool_changes: true` or `skip_quarantine: true` resolves it to `auto`). An explicit `"quarantined": false` admits it, and an explicit `true` holds it. A server that is already recorded in `config.db` keeps its recorded state. See [Servers added by hand-editing `mcp_config.json`](features/security-quarantine.md#servers-added-by-hand-editing-mcp_configjson) for the full admission rules. |
418
+
| `quarantined` | boolean | No | Security quarantine status. The default depends on how the server arrives. A server added through the UI, CLI, REST API or an AI agent follows its [trust mode](features/security-quarantine.md#trust-modes-auto--scan--manual) at add time (quarantined under the default `scan` mode, then released automatically on a clean scan). A server you add by editing this file, with no `quarantined` key and no prior `config.db` record, is **held for review** on first load when quarantine is enabled and its trust mode is not `auto` (no `trust_mode` means `scan`, unless a legacy `auto_approve_tool_changes` resolves it — `true` to `auto`, `false` to `manual` — or `skip_quarantine: true` resolves it to `auto`). An explicit `"quarantined": false` admits it, and an explicit `true` holds it. A server that is already recorded in `config.db` keeps its recorded state. See [Servers added by hand-editing `mcp_config.json`](features/security-quarantine.md#servers-added-by-hand-editing-mcp_configjson) for the full admission rules. |
419
419
|`reconnect_on_use`| boolean | No | When `true`, tool calls to a disconnected server trigger an immediate reconnect attempt (15s timeout) before failing (default: `false`) |
420
420
|`expose_prompts`| boolean | No | Per-server override for whether this server's MCP prompts are aggregated into mcpproxy's `prompts/list`. Only takes effect when the global `aggregate_upstream_prompts` master switch is on. Omit to expose prompts whenever the server advertises `Capabilities.Prompts`; `false` opts this server out even if it does. |
421
421
|`toon_output`| string | No | Per-server override for the global [`toon_output`](#toon-output-adaptive-result-encoding): `off`, `adaptive`, or `always`. Non-empty value wins over the global for this server's tools; omit to inherit. See [TOON Output](features/toon-output.md). |
@@ -965,7 +965,7 @@ block** — off by default, best-effort, and unable to change the baseline verdi
965
965
|`tpa_fetch_url`| string |`""`| Where the fetch step looks. **There is no working default URL**: empty means the fetch makes no request (`disabled`, reason `no_url`), and no URL is compiled into the binary until a release ships the first publisher key. Two shapes are recognised from the URL path: a GitHub Releases `…/repos/<owner>/<repo>/releases/latest` API URL (the release must carry one `scanner-bundle-<sequence>.json` asset with a sibling `.sig`), or any other path, fetched as the bundle with `<url>.sig` as its signature. Must be `https` (plain `http` only for `localhost` or a loopback address) and must not contain credentials, a query string or a fragment; the URL is never logged beyond its host. Rejected on write when `tpa_fetch_enabled` is on and the URL is empty. |
966
966
|`tpa_stale_after_days`| integer | absent (`30`) | Age in days of the active bundle (from its signed `generated_at`) after which it is flagged stale in `GET /api/v1/security/bundle`, `mcpproxy security bundle status` and `mcpproxy doctor`. An explicit `0` disables the stale flag and the doctor warning, and is different from absent. Negative values are rejected on write. |
967
967
| `auto_baseline_scan` | boolean | `true` | Kill switch for the **automatic informational baseline scan**. When on (the default), every newly added server gets one free in-process Pass-1 TPA scan, and once per installation a background sweep scans pre-existing enabled servers that have never been scanned (marker persisted in BBolt, so it runs exactly once and never delays startup). The result only populates the security badge / scan summary: it **never** quarantines, approves, or otherwise gates a server. Disabled servers are skipped, and so are `trust_mode: "scan"` servers — that mode's own admission gate scans them and auto-approves on a clean verdict, so the informational path stays out of it entirely rather than risk feeding that gate; this flag does not affect that separate path. Set to `false` to suppress all automatic scans; manual scans keep working. Hot-reloadable — the flag is read live at each decision point. Env override: `MCPPROXY_AUTO_BASELINE_SCAN` (`true`/`1`/`false`/`0`), which wins over this field on every path. |
968
-
| `auto_rescan_on_change` | boolean | `true` | Finer switch for the **automatic rescan after a tool definition changes** (Spec 118), in every trust mode except `auto`. When on, a changed tool, a tool added after the baseline, or a changed definition that moves again gets the in-process TPA scan of its current definition recorded as hold evidence (`held_reason`/`held_verdict`/`held_signals`, so the review screen shows a per-tool verdict without a click), and a debounced (about 5 s, coalesced per server) informational rescan of the server brings the review screen's scan line back to "current". Evidence and freshness only: a clean verdict **never** approves a changed or new tool, and no approval rule changes. It only takes effect while `auto_baseline_scan` is also on, so set this to `false` to keep the admission scan and turn off per-change rescans. The `trust_mode: "scan"` gate's own admission-time scan of new tools is not affected. Hot-reloadable. Env override: `MCPPROXY_AUTO_RESCAN_ON_CHANGE` (`true`/`1`/`false`/`0`), which wins over this field on every path. |
968
+
| `auto_rescan_on_change` | boolean | `true` | Finer switch for the **automatic rescan after a tool definition changes** (Spec 118), in every trust mode except `auto`. When on, a changed tool, a tool added after the baseline, or a changed definition that moves again gets the in-process TPA scan of its current definition recorded as hold evidence (`held_reason`/`held_verdict`/`held_signals`, so the review screen shows a per-tool verdict without a click), and a debounced (about 5 s, coalesced per server) informational rescan of the server brings the review screen's scan line back to "current". Evidence and freshness only: this switch controls the `manual`-mode evidence scan and the server rescan, not the approval decision, which belongs to the trust-mode gate (under `scan`, the default, the gate's own synchronous scan approves a clean change; under `manual` nothing is approved automatically). It only takes effect while `auto_baseline_scan` is also on, so set this to `false` to keep the admission scan and turn off per-change rescans. The `trust_mode: "scan"` gate's own admission-time scan of new tools is not affected. Hot-reloadable. Env override: `MCPPROXY_AUTO_RESCAN_ON_CHANGE` (`true`/`1`/`false`/`0`), which wins over this field on every path. |
969
969
|`deep_scan.enabled`| boolean |`false`| Master opt-in for the heavy layer. When `false`, no Docker scanner runs and no source extraction is attempted — only the in-process baseline scanner executes. |
970
970
|`deep_scan.fetch_package_source`| boolean |`true` (when deep scan is on) | Whether the scanner fetches (never executes) the published source of `npx`/`uvx` package-runner servers when no local source is available. Set `false` for air-gapped deployments. |
Copy file name to clipboardExpand all lines: docs/development/quarantine-approval-pilot.md
+6-6Lines changed: 6 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -44,7 +44,7 @@ The IDs, titles and sources are in `internal/qapilot/requirements.go`
44
44
| QP-07 | New tool on a quarantined manual/scan server is pending and stays pending across reconnects | S01, S02, S18, S26 |
45
45
| QP-08 | Trusted server's first discovery is the baseline; durable baseline decision recorded | S04 |
46
46
| QP-09 | Post-baseline additions: manual → pending; scan → clean approved / dangerous pending; auto or global off → approved | S05, S11, S12, S15, S16, S17 |
47
-
| QP-10 | Changed definition held in manual **and** scan (whatever the verdict); auto approves | S03, S06, S08, S09, S13, S14, S16 |
47
+
| QP-10 | Changed definition held in manual (whatever the verdict); scan approves it on a clean verdict and holds it on a dangerous one; auto approves | S03, S06, S08, S09, S13, S14, S16 |
| QP-12 | Operator approval approves the *current* definition; reverting afterwards is a change | S09, S10, S25 |
50
50
| QP-13 | Permitted transition reasons for changed→approved and pending→approved | invariant table (99 combinations) |
@@ -60,10 +60,10 @@ The IDs, titles and sources are in `internal/qapilot/requirements.go`
60
60
The sources disagree or are silent in a few places. The model encodes the
61
61
reading below; each is also recorded in the requirement's `Note`.
62
62
63
-
-**Scan mode and a changed tool.** Spec 086 US1 says a clean scan re-approves
64
-
a changed tool; `docs/features/security-quarantine.md` and the RT-02 red-team
65
-
finding say a changed contract is always held. The pilot encodes the docs
66
-
(held). Spec 086 is stale here.
63
+
-**Scan mode and a changed tool.** Spec 119 (2026-10-22) approves a changed
64
+
tool on a clean verdict under `scan` and keeps the RT-02 hold under `manual`;
65
+
the pilot encodes that split. The safer-direction hint flip hold (spec 119
66
+
FR-007) is outside the model: no scenario flips hints.
67
67
-**Baseline marker.** The snapshot compares the durable marker on its own
68
68
(`baseline_decided`) as well as `has_baseline`, so a marker that is never
69
69
written or is lost across a restart cannot hide behind the approved records.
@@ -186,7 +186,7 @@ is by test name, so a designated test must be the one that fails.
186
186
| M04 | QP-14 | Tool-level quarantine gate never applies (pending and changed tools become callable) |`TestQAPilot_ClassifyTable, TestQAPilot_Scenarios`| detected | 13.015s |
187
187
| M05 | QP-16 | A changed (rug-pulled) tool classifies as ready |`TestQAPilot_ClassifyTable, TestQAPilot_DispatchCountingUpstream`| detected | 23.576s |
188
188
| M06 | QP-09 | trust_mode scan accepts a non-clean verdict as scan evidence (dangerous new tools auto-approved) |`TestQAPilot_Scenarios/S12`| detected | 11.262s |
189
-
| M07 | QP-13 | The transition guard lets a clean scan clear a changed record |`TestQAPilot_InvariantTable`| detected | 7.397s |
189
+
| M07 | QP-13 | The transition guard lets the plain auto_approve reason (pending-only) clear a changed record |`TestQAPilot_InvariantTable`| detected | 7.397s |
190
190
| M08 | QP-07 | A quarantined server's first discovery is treated as a trusted baseline (tools auto-approved while quarantined) |`TestQAPilot_Scenarios/S01, TestQAPilot_Scenarios/S02`| detected | 8.639s |
191
191
| M09 | QP-03 | Releasing a server no longer promotes its pending tools (release leaves tools held) |`TestQAPilot_Scenarios/S02`| detected | 12.532s |
192
192
| M10 | QP-15 | A recorded quarantine is not retained across a restart whose config file omits the key (admission gate retain rule removed) |`TestQAPilot_Scenarios/S26, TestQAPilot_Scenarios/S27`| detected | 11.603s |
0 commit comments