scv-triage
# Execution Boundaries for MCP
`scv-triage-runtime` is the Python package that powers this demo. It is a deterministic, trusted-local, non-production, non-live simulation whose three read-only MCP tools return bounded policy verdicts, never live infrastructure state.
## Architecture
```mermaid
graph TB
DB[("Shared demo.sqlite3")]
subgraph boundary["Execution-Boundary MCP Server"]
direction TB
R1["MCP request"] --> RT["Runtime<br/>request validation"]
RT -->|"valid request"| PV["SQLite Provider"]
RT -->|"invalid request"| FC["Bounded Result<br/>INDETERMINATE"]
PV -.->|"read-only query"| DB
PV -->|"raw evidence"| EB["Semantic Evidence Boundary<br/>7 validation layers"]
PV -->|"provider failure"| FC
EB -->|"accepted"| NE["Immutable normalized evidence"]
EB -->|"rejected"| FC
NE --> PL["Policy<br/>deterministic verdict"]
PL --> OUT["Bounded Result<br/>outcome + findings + actions"]
end
subgraph thin["Thin-Wrapper MCP Server"]
direction TB
R2["MCP request"] --> SQL["Source-specific SQL query"]
SQL -.->|"read-only query"| DB
SQL --> RAW["Measurement rows<br/>no verdict, no policy"]
end
```
The semantic evidence boundary is not a pass-through. It independently validates seven layers, then emits immutable normalized evidence for policy evaluation:
```text
SemanticEvidenceBoundary
├── envelope identity schema, tool, profile match the request
├── request identity hostname, at, frm, to match the request
├── coverage the point or window is inside packaged coverage
├── temporal binding evidence timestamps fall within the request window
├── scope host and fleet scope match the requested target
├── row shape and bounds source, fields, types, and numeric ranges are valid
├── ordering / uniqueness / cap no duplicate keys, monotonic order, fixed cap
└── output guarantee immutable normalized evidence
```
Public MCP tools:
- `triage_server(hostname, at?)`
- `fleet_health(at?)`
- `investigate_timeline(hostname, frm, to)`
Every result is a simulation snapshot. Lead a point result with its evaluation timestamp; lead a timeline result with its half-open `[frm, to)` range. Timeline results do not establish an exact state-transition time or root cause.
`severity` is categorical policy metadata, not a calculation of duration, impact, or confidence. Under the policy, `SERVER_OFFLINE` maps to `HIGH`. `HEALTHY` is not a reservation-availability claim. `NO_DATA` is a valid empty result; `INDETERMINATE` means required evidence cannot be reliably evaluated. `actions` require approval and are non-executable.
### Why evidence was rejected
For `INDETERMINATE`, `findings.kind` names the bounded validation class that stopped evaluation:
| Kind | Meaning |
| --- | --- |
| `INVALID_REQUEST` | typed request semantics are invalid |
| `OUT_OF_COVERAGE` | a valid point/window is outside packaged coverage |
| `PROVIDER_UNAVAILABLE` | the packaged provider returned its closed unavailability signal |
| `REQUIRED_EVIDENCE_MISSING` | required evidence is absent, including an empty fleet |
| `REQUIRED_EVIDENCE_DEFECTIVE` | required evidence is stale or otherwise unusable |
| `SCHEMA_REQUEST_MISMATCH` | raw schema or request identity does not match |
| `TEMPORAL_WINDOW_MISMATCH` | evidence time/window does not match the request |
| `HOST_SCOPE_MISMATCH` | evidence escapes the requested or fleet host scope |
| `DUPLICATE_ORDER_CAP_VIOLATION` | evidence violates uniqueness, order, or the fixed cap |
| `INVALID_EVIDENCE_PAYLOAD` | a source, row, type, or bounded value is invalid |
A thin provider pass-through may relay data or structured errors, but it does not independently validate request identity, host scope, time/window, order, uniqueness, caps, and payload bounds. The normalized semantic evidence boundary does, and returns one bounded rejection class without backend disclosure when those invariants do not support a trustworthy verdict.
These fixed categories are rejection classes, not exhaustive root causes. They never expose SQL, paths, database/provider identities, raw exception text, or rejected values. Their severity is always `HIGH` because no trustworthy verdict can be produced—not because incident impact is high. Synthetic failures retain schema-v1 evidence state `defective`; normal states remain `COMPLETE`, `EMPTY`, and `NOT_APPLICABLE`. Required `missing` (and an empty fleet) takes precedence over required `stale`.
## Getting started
### Prerequisites
- Python 3.12 or later
- SQLite 3.37 or later (pre-installed on macOS and most Linux)
- [pipx](https://pipx.pypa.dev/)
If your system Python is older than 3.12, install 3.12 first. On Linux without sudo, use [uv](https://docs.astral.sh/uv/):
```sh
curl -LsSf https://astral.sh/uv/install.sh | sh
uv python install 3.12
```
If pipx is not installed:
```sh
curl -sSf https://pipx.pypa.dev/install.py | python3
```
### Install
If your system Python is 3.12+, install directly:
```sh
pipx install git+https://github.com/kc-ml2/mcp-execution-boundaries.git
```
If pipx defaults to an older Python, specify 3.12 explicitly:
```sh
pipx install --python 3.12 git+https://github.com/kc-ml2/mcp-execution-boundaries.git
```
This puts `scv-triage-stdio` on your PATH. Most MCP clients find it directly. If yours doesn't, run `command -v scv-triage-stdio` and paste the output as the `command` value.
### Configure an MCP client
**Claude Desktop** — edit `claude_desktop_config.json`:
```json
{
"mcpServers": {
"scv-triage": {
"command": "scv-triage-stdio",
"args": [],
"env": {
"SCV_RUNTIME_ENVIRONMENT": "demo",
"SCV_DEMO_PROFILE": "INCIDENT"
}
}
}
}
```
**Cursor** — see `deploy/cursor-mcp.example.json`.
### Restart and verify
Completely quit and reopen the MCP client. The server is ready when these three tools appear:
- `triage_server`
- `fleet_health`
- `investigate_timeline`
### Switch profiles
| Profile | Fleet state | Point verdicts |
| --- | --- | --- |
| `INCIDENT` | CRITICAL — storage critical, GPU violation, one server offline | server-01 CRITICAL, server-02 OFFLINE, server-03 HEALTHY |
| `CLEAR` | HEALTHY — same fleet, no findings | server-01 HEALTHY, server-02 HEALTHY |
At the INCIDENT evaluation point, `triage_server(server-01)` reports both `STORAGE_CRITICAL_ENTRY` (`CRITICAL`) and `VIOLATION_PERIOD` (`HIGH`). Co-present findings do not establish causation.
Change `SCV_DEMO_PROFILE` and restart the client.
### Verify in the MCP client
After restarting, the client should list exactly three tools: `triage_server`, `fleet_health`, and `investigate_timeline`. If the server doesn't appear, run `command -v scv-triage-stdio` and use the printed absolute path as the `command` value instead.
### Update
```sh
pipx upgrade scv-triage-runtime
```
### Uninstall
```sh
pipx uninstall scv-triage-runtime
```
### Install from source
```sh
git clone https://github.com/kc-ml2/mcp-execution-boundaries.git
cd mcp-execution-boundaries
python3.12 -m venv .venv
.venv/bin/pip install .
```
For this method, set `command` to the absolute `<repo>/.venv/bin/scv-triage-stdio` path.
### Runtime gate
The server starts only when `SCV_RUNTIME_ENVIRONMENT=demo` and `SCV_DEMO_PROFILE` is exactly `INCIDENT` or `CLEAR`. Missing, production, or unsupported values abort before MCP protocol startup with no fallback.
## License
MIT — see [LICENSE](LICENSE).
## Presentation
Presented at [MCP Seoul 2026](https://mcpseoul2026.sched.com/event/2PYeW?iframe=no). See [`output/`](output/) for a structured output comparison between the execution-boundary server and a thin API wrapper on the same query.
TDQS
Scored across 3 tools
All tool descriptions are identical, so an agent cannot determine what distinguishes triage_server, fleet_health, and investigate_timeline. The names suggest different purposes, but the identical descriptions provide no disambiguating detail, making misselection likely.
Tool names mix conventions: 'triage_server' and 'fleet_health' are noun-style, while 'investigate_timeline' is verb-object. This inconsistency makes it harder to predict tool naming patterns.
With 3 tools, the count is within the well-scoped 3-15 range, but on the lower end for what appears to be a triage/fleet health domain. Each tool likely covers a distinct area, though the set feels slightly thin.
The identical descriptions obscure what each tool actually does, making it impossible to verify coverage. There appear to be significant gaps, such as no way to list individual fleet members or perform bulk triage, leaving agents without needed operations.