Skip to main content
Glama
kc-ml2

scv-triage

by kc-ml2
README.md
# 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

B3.3/5.0

Scored across 3 tools

Disambiguation2/5

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.

Naming Consistency2/5

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.

Tool Count4/5

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.

Completeness2/5

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.

Maintenance

ActivitySlowing
ResponsivenessNo issues