Skip to main content
Glama

wsltop

wsltop is a unified top-like resource monitor for Windows, WSL2, Docker, and WSL Containers (WSLC). It runs natively on both Windows and WSL, combining host and guest observations on one host-wide CPU scale.

Windows Task Manager says VmmemWSL is busy. wsltop shows which Windows, WSL, WSLC, or Docker workload is responsible.

wsltop provides a one-shot CLI and interactive terminal UI on both Windows and WSL. A graphical UI is outside the current scope.

AI agents can inspect the same workloads through the read-only MCP server.

Version 1.0 establishes a compatibility policy for the existing CLI, JSON output and read-only MCP tools.

The default TUI keeps a compact two-line CPU/RAM summary, history graphs and Win / WSL / WSLC / Docker columns. Win CPU includes Windows system work, interrupts and short-lived tasks. Verified container cgroup CPU is separated from WSL over a common sampling window. WSL* means overlap could not be resolved. Guest accounting and sampling differences can still cause a gap from host CPU. Process and container CPU rows remain available below the header. Use --header classic for the traditional one-line header and --color never for monochrome output. See release notes.

wsltop v0.5.0 showing CPU and RAM history, Windows and WSL workloads, and running WSLC and Docker containers

Live v0.5.0 session with workloads in another WSL distribution and running WSLC and Docker containers, including their process details. The two demo containers are each limited to 0.25 CPU cores. Capture details.

Quick start

Requires Windows 11 with a usable WSL2 distribution; the Linux binary runs inside WSL2. Docker and WSLC are optional.

With cargo-binstall installed, you can fetch the x64 GitHub Releases binaries without a source build, which is faster than cargo install.

Windows

Download the Windows x86_64 MSVC ZIP from the latest GitHub Release. Extract it and open PowerShell in the versioned directory:

.\wsltop.exe --interactive

You can instead install with cargo binstall wsltop, then run wsltop --interactive.

WSL / Linux

Fast prebuilt install:

cargo binstall wsltop
wsltop --interactive

Or build from crates.io:

cargo install --locked wsltop

Without Cargo, download the Linux x86_64 tar.gz from the latest GitHub Release, extract it and run ./wsltop --interactive from the versioned directory. See checksum verification for downloaded archives.

Related MCP server: mcp-vpsobs

Why wsltop?

Task Manager may show only a VM aggregate such as VmmemWSL 35%. That identifies the host process, but not the guest workload. wsltop --tree makes the hierarchy visible:

WSL VM
|- process    verilator
`- infra      plan9

Docker
`- container  build
   |- process compiler
   `- unattributed

Parent and child CPU values are attribution views, not values to add together.

Features

  • Windows native process monitoring

  • Current WSL distribution process monitoring through /proc

  • Multiple running WSL distribution monitoring

  • WSL Containers monitoring

  • Docker and WSLC container monitoring with in-container process attribution

  • WSL and WSLC host attribution trees with an unattributed remainder

  • Host-wide CPU normalization: all environments use Windows host logical CPUs = 100%

  • Interactive terminal UI with flat/tree views, scrolling, and display toggles

  • Shared CPU/memory/name sorting with ascending/descending order in TUI, CLI, and JSON

  • Flat and tree JSON output

  • Resource classification as process, container, infra, or internal host

  • Best-effort degradation when optional WSLC, Docker, or additional-distro collectors are unavailable

What the TUI shows

The flat view is a host-wide activity ranking. Windows and WSL processes appear alongside Docker and WSLC containers. A container is ranked once by its total CPU; optional process rows are an indented explanation of that total, not extra CPU to add to it.

This example uses 16 host logical CPUs: summary CPU percentages are host-wide, while table rows use the core scale (Docker's 11.99% becomes 0.7% in the summary).

CPU 12.0%      ▁▁▂▁▁▁▂▂▁▁▁▁▂▁▁▁▂▂▁▁▁▁▁ | Win   5.7%   WSL   0.3%   WSLC   0.4%   Docker   0.7%
RAM 15.7/31.9G ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ | Win 16.79G   WSL  1.50G   WSLC   348M   Docker   520M
─────────────────────────────────────────────────────────────────────────────────────────────────
ENV     TYPE              ID/PID    CPU%       MEM      TIME+ COMMAND
-------------------------------------------------------------------------------------------------
Docker  container   68dae66282ff  11.99%      520M          -  act-CI-simulate...
        process            34692  11.75%      157M   62:03.46    |- simx
        residual               -   0.24%         -          -    `- unattributed
WSLC    container   5e0c144e6a3c   5.94%      348M          -  mighty_flinders
        process              806   5.71%       31M    4:12.08    |- cc1plus
        process              470   0.19%        6M    0:03.20    |- ninja
        residual               -   0.04%         -          -    `- unattributed
Windows application       3 PIDs   4.82%     1.24G  103:27.51  Teams
Windows application      12 PIDs   2.37%     1.68G  248:10.03  Chrome
Windows application        31460   1.20%      198M  178:27.95  Taskmgr
WSL     infra                  5   0.05%        4M    0:14.82  plan9

[flat cpu↓ core 3.0s]  q quit  ? help  t tree  i infra:on  h hosts:off  0 zero:on

In this example, simx 11.75% is included in its Docker container's 11.99%; the values must not be added. Containers keep their position according to total container CPU, while their processes are sorted within the container. Windows rows are ranked by application, so multi-process applications such as Teams and Chrome appear once. By default, at most five processes are shown per container and additional processes are summarized.

For a Windows application row backed by one observed process, ID/PID shows that process's real PID. Multi-process applications show N PIDs without extra punctuation. If a single member's PID is unavailable, wsltop falls back to 1 PID. Tree view exposes individual process IDs, while JSON retains pid: null for application totals.

Press t to switch to the attribution tree and answer a different question: how much of each VM or container total can wsltop explain?

Host logical CPUs: 16

WSL VM                                    3.20%
|- infra      plan9                       0.05%
|- process    codex                       0.18%
`- unattributed                           2.97%

Docker
`- container  act-CI-simulate...         11.99%
   |- process  simx                       11.75%
   `- unattributed                         0.24%

The default text/TUI scale treats one fully occupied logical CPU as 100%; use --cpu-scale host for the Windows host-wide scale.

Installation

Choose a prebuilt or Cargo install in Quick start. WSL execution requires Windows interoperability and powershell.exe on PATH. Docker collection needs a reachable Docker daemon; WSLC collection needs wslc.exe.

Build from source

A Rust toolchain is required; Windows builds also need the Visual Studio C++ build tools. From PowerShell or a WSL shell:

git clone https://github.com/adachi6k/wsltop.git
cd wsltop
cargo build --release --locked

Run .\target\release\wsltop.exe --interactive on Windows or ./target/release/wsltop --interactive in WSL.

Verify downloads

Each archive in the latest GitHub Release has a .sha256 sidecar. Download both files into the same directory. Names follow wsltop-v<version>-<target>; replace v1.0.0 below with your downloaded version.

Windows PowerShell:

$archive = 'wsltop-v1.0.0-x86_64-pc-windows-msvc.zip'
$expected = ((Get-Content "$archive.sha256") -split '\s+')[0]
if ((Get-FileHash $archive -Algorithm SHA256).Hash -ne $expected) { throw 'Checksum mismatch' }

WSL:

sha256sum --check wsltop-v1.0.0-x86_64-unknown-linux-gnu.tar.gz.sha256

Usage

The default and --once modes take two cumulative CPU snapshots separated by --interval-ms and print one table.

wsltop [OPTIONS]

--once                 Take one sampled measurement (default)
-i, --interactive      Run the continuously updating terminal UI
--json                 Emit JSON; incompatible with --interactive
--tree                 Show CPU attribution; selects the initial TUI view
--limit N              Limit flat resources (default: 30)
--interval-ms N        Sampling/refresh interval (default: 3000, minimum: 100)
--show-wsl-host        Show raw vmmem/vmmemWSL/vmmemwslc-* rows in flat views
--wsl-only             Skip Windows, additional-distro, and WSLC collectors
--distro NAME          Select the primary WSL distro (Windows executable only)
--no-wslc              Disable WSLC collection
--no-docker            Disable Docker collection
--show-container-processes Include Docker/WSLC processes (default for text/TUI)
--hide-container-processes Hide Docker/WSLC processes from flat output
--container-process-limit N Show at most N processes per container (default: 5)
--cpu-scale core|host CPU display scale for text/TUI output (default: core)
--sort cpu|memory|name Sort resources and their children (default: cpu)
--sort-order asc|desc  Sort direction (default: desc)
--hide-infra           Hide infrastructure rows

Options that affect collection or the initial view also apply to interactive mode. --once remains an explicit alias for the default one-shot behavior.

For a memory ranking, use wsltop --sort memory; for alphabetical JSON, use wsltop --json --sort name --sort-order asc. TUI keys c, m, and n select CPU, memory, and name; r reverses the current direction. Changing the key keeps the current direction. The header displays both, and a key change immediately requeries the latest observation, including candidates outside the previous limit.

Flat sorting ranks parents before applying --limit; their process children stay attached and use the same sort before --container-process-limit is applied. Tree sections keep their hierarchy and sort peers within each section, including Windows applications and their member processes. Tree output keeps all collected children; the flat limits do not truncate the attribution tree or its residuals. Memory/name tree views include idle Windows applications and processes; the default CPU tree retains its existing activity-focused Windows view.

CPU ties prefer memory; memory ties prefer CPU. Names use case-sensitive string ordering. Remaining ties use name and resource identity in ascending order, independently of direction. CPU and memory sorting use numeric observations, not formatted strings or the selected CPU display scale. Windows working set, Linux RSS, and container memory have different meanings: sorting does not make their memory values additive or enable parent-minus-child memory accounting.

Interactive TUI

Start the terminal UI with wsltop --interactive in WSL or wsltop.exe --interactive in Windows. It draws immediately and accepts partial collector updates instead of waiting for every source. The primary WSL collector reads local /proc in WSL or samples remotely through wsl.exe on Windows. After its first successful baseline it waits a fixed 150 ms warmup, then uses the configured interval. While Windows host discovery is pending, non-Windows collectors use the executing platform's visible CPU count and are marked provisional; WSL category CPU remains unavailable until the host count is confirmed. If the Windows-reported count differs, provisional rows are discarded and repopulated on the host-wide scale; delayed results carrying the old normalization count are ignored. --wsl-only uses WSL-visible logical CPUs in WSL-native execution and the Windows host logical CPU count in Windows-native execution. Windows collection runs independently; additional WSL distributions, WSLC, and Docker refresh on a slower cadence (at least two seconds), so a slow optional collector cannot serialize primary sampling. Windows primary selection may still require synchronous default/fallback discovery.

Additional distro discovery runs in its own worker and discovers newly started distributions during the session. Its initial baseline remains loading until a CPU delta is available, no additional distro is running, or an error is reported. Transient failures retain last-good rows; confirmed stopped distributions lose their rows and baseline. Distribution-name matching ignores ASCII casing.

The TUI retains each collector's last successful result. While collectors start, the footer reports loading; after a collector error its previous rows remain visible and the footer reports the error. Docker/WSLC aggregate rows are collected separately from internal process details. Details are enabled by default for text/TUI output and use a separate bounded queue and five-second command timeout, so slow process inspection does not block aggregate refreshes. Use --hide-container-processes to skip process rows in flat output.

Windows processes are ranked as application totals in human-readable flat/TUI output. Multi-process applications such as Teams, Chrome, and ChatGPT therefore occupy one top-level row. Tree output expands each application into its contributing PIDs. WebView2 ownership uses current parent-PID evidence plus CIM command-line/package metadata; ambiguous helpers remain under a conservative WebView2 application instead of being assigned by guess. Metadata discovery runs independently in interactive mode and retains its last successful result across transient failures.

Controls:

Key

Action

q, Esc

Quit

Up/Down, Page Up/Page Down

Scroll

s

Enter/leave selection mode in flat view; arrows/Page keys select rows

k

Request normal termination of the selected primary WSL process (Windows-native TUI)

t

Toggle flat/tree view

c, m, n

Sort by CPU, memory, or name

r

Reverse sort direction

i

Toggle infrastructure rows

h

Toggle raw WSL host rows in flat view

0

Toggle zero-CPU rows

?

Open/close summary help (arrows/Pg scroll; Esc closes help)

Terminal raw mode, alternate-screen state, and cursor visibility are restored on normal exit and propagated errors.

Terminating a selected WSL process

In the Windows-native TUI, press s, select a row with the arrows, then press k. The confirmation shows the resolved WSL distribution, PID and process name: y sends SIGTERM; n or Esc cancels. The entire confirmation must fit on screen before it can be accepted. Selection follows the process across sorting and refreshes, and is cleared if the row disappears, is filtered out, or its identity changes. Selection mode uses the flat view; t clears it.

This first action supports only ordinary processes in the primary WSL distribution already used by the collector. It requires Python 3.9+ with pidfd support in that distribution. Nothing is installed automatically. Windows processes, other distributions, containers, aggregate rows, init and unverifiable identities are unsupported. WSL-native execution remains read-only. MCP remains read-only on both platforms.

The helper checks the boot, PID/mount namespaces, user and process start time, and sends SIGTERM through a pidfd so PID reuse after validation cannot redirect the signal. There is no sudo, PID-only fallback, automatic retry or force-kill. One request can run at a time, without blocking the TUI. The footer shows the result; ? shows it in full. Accepted means the signal was accepted, not that the process exited. A timeout has an unknown outcome: inspect the target before retrying. Quitting after confirmation does not undo the request.

Compact resource summary

The TUI defaults to a two-line CPU/RAM summary. Windows, WSL, WSLC and Docker labels use the same colors in the summary and resource list: blue, green, magenta and cyan respectively. A neutral separator divides the summary from the resource table; its length follows the wider of the summary and table headings/rule, capped by terminal width. Long commands do not stretch it. The existing rule below column headings remains. A single footer groups view, sort, CPU scale and interval as [flat cpu↓ core 3.0s], followed by option states and key hints. Press ? for metric definitions, detailed controls and collector status.

Host CPU and RAM have short history graphs alongside their totals when all four environment labels fit. Left is older, right is now. Both use a fixed 0–100% scale (RAM is physical memory in use / total), with 23 time slots at 120 columns or wider and 15 slots at 80–119 columns. Each slot spans the configured refresh interval: at the default 3 seconds these cover 69 and 45 seconds respectively. Both graphs share a fixed clock and shift left together once per interval. Until a new result arrives, the previous value is held; these held values are not new measurements. Before the first result, slots are blank. An explicitly failed or unavailable reading shows ! until a successful reading arrives; ▁ represents low/zero usage. Redraws and other collectors do not advance the clock or append observations. The graphs are not rescaled to exaggerate small changes. TERM=dumb uses ASCII levels (_ through #) with the same ! failure marker.

The Windows collector includes query time in its refresh interval: a 0.8-second query with a 3-second interval waits another 2.2 seconds. If collection takes longer than the interval, the next query starts when it finishes, without overlapping queries. The history holds the last value during that wait. Below 80 columns, only CPU/RAM totals remain in the summary; history continues to be recorded while hidden. Classic and WSL-only views do not show host graphs. Totals are left-aligned in fixed-width fields, so both values and graphs start at matching columns. Environment observations also have fixed columns, with three spaces between blocks at 120+ columns and one on medium terminals, including while values are unavailable or change digit count. Large memory values use fewer decimals or larger binary units to keep columns stable. Very short terminals reduce the summary to one line. The footer progressively omits hints, preserving flat/tree, sort key/order and q quit whenever physically possible.

CPU 23.4%      ▄▃▂▂▂▂▄▃▂▂▂▂▄▃▂ | Win   5.0% WSL  10.0% WSLC   2.0% Docker   4.0%
RAM 12.3/32.0G ▃▃▃▄▄▄▃▃▃▄▄▄▃▃▃ | Win  3.20G WSL  2.10G WSLC   300M Docker   800M

These example values are not an exact additive breakdown of host CPU:

  • Win CPU measures Windows root partition execution, including interrupts and short-lived tasks. It excludes guest execution and does not absorb the residual. Without a hypervisor it equals total host CPU. Win RAM still sums observed process working sets, excluding VM host rows.

  • WSL CPU uses the shared WSL kernel's /proc/stat counters, sampled once through the primary distribution. It includes short-lived processes and kernel work, including other distributions even with --wsl-only. Verified Docker/WSLC cgroup CPU is subtracted from this total and displayed in its own column.

  • WSLC and Docker use common-window cgroup rates when overlap is resolved; otherwise they retain CLI container statistics. Child process rows are excluded.

  • WSL* marks inclusive fallback values: container membership, counter history or collection could not be verified. The warning explains why.

  • Even without container overlap, Win root time and WSL guest-kernel time are not an additive physical-CPU breakdown; see CPU accounting.

  • Environment RAM values are Windows working sets, WSL RSS, and container CLI memory statistics respectively. Shared pages and overlapping observations mean these values must not be summed or subtracted from host physical RAM.

RAM is physical total minus available memory, collected using GlobalMemoryStatusEx. K, M, G, T, P, and E use powers of 1024. Missing, disabled, warming-up or incomplete collectors display N/A; a successful empty collection displays zero. Host RAM can appear before the first CPU interval. With --wsl-only, host totals are unavailable and CPU observations use the WSL CPU scale, as explained in help. Summary observations are independent of row filters, limits, sorting and --cpu-scale.

Overlap detection runs a short read-only sh probe in existing containers using docker exec / wslc.exe exec. It reads kernel boot identity, uptime and leaf cgroup v2 cpu.stat; it does not start containers, install tools or elevate privileges. Up to 16 containers per backend are probed, four at a time, with a two-second timeout per probe. Missing shell/tools/permissions, non-leaf or cgroup v1 configurations, foreign kernels and excessive container counts retain WSL*. The probes are independent of process-detail visibility. Exact cgroup aliases exposed by both backends count once under Docker. Rates use interpolation within observed cumulative-counter intervals, so they remain estimates. See CPU overlap accounting.

Use --header classic for the existing one-line header, or --header compact for the new default. --color auto|always|never controls TUI colors; auto honors nonempty NO_COLOR and disables colors with TERM=dumb, while always explicitly overrides them. --color never retains all labels and numbers. These display options do not change text or JSON output.

CPU display and accounting

The TUI header shows CPU for the entire Windows host (all logical CPUs together = 100%), including WSL/container activity. It uses Hyper-V physical execution counters when a hypervisor is present, otherwise Windows system counters, independently of row limits, filters, sorting, and --cpu-scale. It displays N/A during warmup, with --wsl-only, or when the counter is unavailable. This is busy CPU time, which can differ from Task Manager's frequency-adjusted utilization.

Text and TUI output default to the familiar Linux top convention where one fully busy logical CPU is 100%; multi-threaded workloads can exceed 100%. Use --cpu-scale host for the Task Manager-style whole-host display where all Windows host logical CPUs together equal 100%.

Internally, every CPU percentage remains on the common host-wide denominator. Display scaling is applied only while rendering, so sorting, attribution, residual accounting, and JSON values do not change.

Windows and WSL process percentages come from deltas of cumulative processor time. WSLC and Docker percentages are normalized from their collector-specific values onto the same host scale. See CPU accounting for formulas and caveats.

TIME+ is cumulative CPU time consumed, formatted as unbounded minutes, seconds, and hundredths (MM:SS.hh), as in Linux top; it is not wall-clock process age. Windows application TIME+ is the sum of its currently observed member processes. Process rows report TIME+ where their collector exposes it. Container totals and residual rows show - because wsltop does not infer cumulative time from point-in-time container percentages.

Attribution tree

Use --tree to treat vmmem, vmmemWSL, and vmmemwslc-* as parent resources:

Host logical CPUs: 16 | CPU scale: 1 core = 100%

WSL VM                                  131.20%
|- infra      plan9                      43.20%
|- process    codex                       3.20%
`- unattributed                          84.80%

The remainder is clamped at zero:

unattributed = max(host CPU - known child CPU, 0)

Children are never proportionally scaled to fit a parent. If independently timed samples make children exceed the host value, the internal tree records sampling skew. Memory is displayed as resource data only; host-minus-child memory attribution is not performed.

Raw WSL host processes stay hidden in default flat output to avoid accidental double-counting. --show-wsl-host exposes them for diagnostics; tree mode always collects them for use as parents.

Docker / WSLC behavior

WSLC collection uses the current/default CLI session. A single available vmmemwslc-* host can be associated with its containers. If multiple hosts make the mapping ambiguous, wsltop reports the mapping as unresolved and does not guess; flat WSLC rows remain available.

Docker collection is optional. Container CPU and memory come from Docker statistics. For each container, docker top <id> -eo pid,ppid,pcpu,rss,time,comm,args independently discovers processes in the Docker daemon's PID namespace. Process %CPU is divided by the Windows host logical CPU count and processes are nested under their container. unattributed and over_attributed residuals are calculated without scaling process values to fit the container. If the process backend does not support time, wsltop retries the older column set and leaves TIME+ unavailable instead of dropping the container detail.

Docker Desktop's WSL 2 backend shares the WSL kernel, so its CPU is already included in the WSL category total. A separate Hyper-V backend does not share that kernel. Kernel sharing does not establish a shared PID namespace or a verified attribution parent: Docker stays a top-level group unless host/PID mapping is proven. See Docker's WSL backend documentation.

Text/TUI output includes Docker and WSLC process rows by default while preserving each container row; use --hide-container-processes to suppress them (--show-docker-processes remains a compatibility alias). Flat ranking and --limit treat each container as the top-level resource; its processes and residual are displayed directly beneath it and are not independently ranked or counted toward the limit. Each container shows its top five processes by default; --container-process-limit changes that cap and omitted processes are summarized by count and combined CPU (--docker-process-limit remains an alias).

A missing wslc.exe, missing Docker CLI, or recognized unavailable Docker daemon is treated as an expected absence: its rows are silently omitted and monitoring continues. Unexpected command, output, parse, or per-container attribution failures are reported through the common warning path. Use --no-wslc or --no-docker to disable a collector intentionally.

Windows application grouping

Application CPU is exactly the sum of observed member-process CPU; child PIDs explain the application total and must not be added to it. Ordinary processes fall back to conservative executable-name grouping. A WebView2 process joins another application only when command-line, package, or a currently matching parent process provides unambiguous evidence.

Multiple WSL distributions

When wsltop runs inside WSL, the current distribution is sampled directly from /proc. Other running distributions are discovered with wsl.exe --list --running --quiet, sampled through wsl.exe -d, and labelled with their distribution name. These additional remote samples are best-effort and introduce more timing skew than direct /proc access.

When wsltop.exe runs on Windows, primary selection uses --distro NAME, then the WSL default, then the first running distribution. The selected primary may be started by wsl.exe; additional distributions are collected only while running. All are sampled remotely through wsl.exe. Primary failure aborts a one-shot sample; the TUI reports sampling errors and retries while retaining last-good data. Failure to select a primary prevents streaming startup. Additional distributions remain best-effort. JSON omits source for the primary and includes the distro name for additional sources.

--wsl-only limits WSL distribution collection to the primary distribution and disables Windows and WSLC collection. Optional Docker collection remains enabled unless --no-docker is also passed. In WSL-native execution it uses the WSL-visible logical CPU count and warns that exact Windows-host normalization is unavailable. In Windows-native execution it uses the Windows logical CPU count but still disables Windows host-process attribution.

JSON output

--json preserves the PID-level flat resource-array schema and host-wide CPU values. Windows application rows are a human-readable view and do not replace existing Windows PID objects in flat JSON. Each resource includes fields such as environment, kind, identity, CPU percentage, and memory bytes. When known, cumulative CPU time is added as cpu_time_seconds; the field is omitted when unavailable. --cpu-scale core is rejected with JSON because display scaling does not alter machine-readable values; omitted scale or --cpu-scale host is accepted.

wsltop --once --json

--tree --json emits a structured object containing host_logical_cpu_count, attribution groups, additive Windows application groups, Docker subgroups, unmapped children, unattributed_cpu_percent, and sampling-skew information.

JSON is a one-shot interface; --interactive --json is rejected explicitly.

Use wsltop from AI agents

With v0.5.1 or later, your MCP client can launch wsltop mcp as a local stdio server. It is read-only and observability-only: no shell or arbitrary command execution tools, process kill/termination, container stop/control, or network listener. The four tools are get_system_summary, list_resources, inspect_resource, and list_children.

For a client running inside WSL2, add this stdio server configuration (replace <user> with your username). Run command -v wsltop to check the absolute path:

{
  "mcpServers": {
    "wsltop": {
      "command": "/home/<user>/.cargo/bin/wsltop",
      "args": ["mcp"]
    }
  }
}

For a Windows client, use the absolute path to wsltop.exe as command, with the same ["mcp"] args. Configuration formats vary by client; see MCP quick start and snapshot semantics.

Once connected, ask your agent:

  • “What is using the most CPU on my machine?”

  • “Which Windows, WSL, Docker, or WSLC workload uses the most memory?”

  • “Inspect the busiest resource and explain its immediate children.”

  • “Compare current Windows, WSL, Docker, and WSLC usage.”

Agent example: a slow build

An actual Codex session used this prompt:

My build is running slowly. Use wsltop MCP to identify the likely bottleneck and explain which environment, container, and process are responsible.

The agent called get_system_summary, list_resources, inspect_resource, and list_children, plus a container-filtered listing, keeping the same snapshot while drilling down. wsltop MCP distinguishes Windows, WSL, Docker, and WSLC observations. The result, summarized for readability:

The current load is mainly from WSL. The busiest process, gw_sh in Ubuntu, is using about one CPU core. No Docker or WSLC container is observed as responsible in this snapshot. Overall host CPU usage is moderate, so if this process is the build, limited parallelism is a likely bottleneck; host CPU is not saturated.

Parent/child and environment values may overlap and must not be summed. This is a likely diagnosis: agents should not infer paging, disk-I/O stalls, memory-pressure causality, container membership, or the exact build task without supporting observations; see the observation limits. See the agent workflow and manual agent evaluation guide.

Limitations

  • WSL1 is unsupported and untested; use WSL2.

  • Additional-distro startup/stop discovery is best effort. Docker/WSLC process detail may lag aggregate rows because collection runs at independent cadences.

  • Sampling is best effort. Linux, PowerShell, WSLC, Docker, and remote-distro snapshots are not captured atomically.

  • PowerShell process collection adds latency, but runs independently of other interactive collectors; the Windows logical CPU count is cached after its first successful query.

  • WSLC session attribution is deliberately conservative when multiple host mappings are possible.

  • Docker process %CPU from docker top is a ps-style lifetime/decay average and may not align precisely with interval-sampled container or /proc CPU.

  • Memory values from Windows, WSLC, and Docker have different meanings and are not attributed by subtraction.

  • Both executables require access to a WSL2 distribution; Windows-only monitoring without WSL is not supported.

  • Remote WSL discovery and snapshots can wait on wsl.exe. Optional TUI discovery runs separately from primary sampling, but primary selection/collection can still be delayed.

Documentation

Development

cargo fmt --all -- --check
cargo test --locked --all-targets
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo build --release --locked
cargo package --locked

CI runs build, lint, test, and help/version checks on Ubuntu, a Windows GNU cross-target check, and native Windows tests and release-build checks. Tagged releases package Linux x86_64 archives and Windows x86_64 ZIPs with SHA-256 checksums. Windows/WSL interoperability, WSLC, Docker, attribution accuracy, and terminal recovery require real-host validation; see the test plan.

License

Licensed under the MIT License.

Available Tools

4 tools
get_system_summaryA
Read-only

Start CPU or memory diagnosis here: get observed host and environment CPU/memory state plus snapshot metadata. Usage can overlap; do not sum it. Null does not imply zero usage. Do not infer paging, disk I/O, or memory-pressure causality from memory usage alone.

ParametersJSON Schema
NameRequiredDescriptionDefault
max_age_msNoLatest snapshot age bound; default 3000, zero forces collection. Mutually exclusive with snapshot_id.
snapshot_idNoOpaque retained snapshot ID; never parse it.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only and non-destructive safety. The description adds non-obvious return semantics: 'Usage can overlap; do not sum it' and 'Null does not imply zero usage', plus a warning against over-interpreting memory usage. This is meaningful behavioral context beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: the purpose appears first, and each caveat is a single scannable sentence. There is no filler or repetition of what the schema already provides.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description tells the agent what it will get (host/environment CPU/memory state and snapshot metadata) and gives the key interpretation caveats. It is complete enough for a no-required-parameter read tool, though it leaves the exact return shape to discovery since there is no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents max_age_ms and snapshot_id. The description does not add detail about either parameter, so it stays at the baseline 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'get observed host and environment CPU/memory state plus snapshot metadata'. It also frames the tool as the entry point for CPU/memory diagnosis, clearly distinguishing it from the sibling resource-listing/inspection tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Start CPU or memory diagnosis here' is an explicit when-to-use signal, and the cautions against inferring paging/disk I/O or memory-pressure causality provide when-not guidance. However, it does not name sibling alternatives or state when to prefer them over this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

inspect_resourceA
Read-only

Inspect details of a resource found by list_resources or list_children. Pass its returned resource_id together with the same snapshot_id.

ParametersJSON Schema
NameRequiredDescriptionDefault
resource_idYes
snapshot_idYesOpaque retained snapshot ID; never parse it.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish readOnlyHint=true and destructiveHint=false. The description adds useful behavioral context beyond that by clarifying that resource_id must be paired with the same snapshot_id used to find it, which implies snapshot-bound consistency. It does not contradict the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One sentence with no filler, front-loading the purpose and then stating the parameter provenance. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter read-only tool, the combination of annotations, schema, and description is complete. There is no output schema, and the description's 'Inspect details' adequately signals what the tool returns without needing to enumerate return fields.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 50% and resource_id has no schema description, but the description compensates by explaining that resource_id is the one returned by list_resources/list_children. It also reinforces the snapshot_id relationship, though snapshot_id itself is already described in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Inspect') and resource ('details of a resource') and ties it to the sibling tools list_resources/list_children, so an agent can distinguish it from get_system_summary and the listing tools. The relationship to returned resource_id is explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear precondition: the resource must come from list_resources or list_children. It also instructs exactly what to pass (the returned resource_id together with the same snapshot_id), removing ambiguity about how the parameters relate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_childrenA
Read-only

Drill into a container, application, or attribution hierarchy's immediate children. Pass the parent's resource_id and the same snapshot_id; child usage overlaps parent usage.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
sort_byNo
sort_orderNo
environmentNo
resource_idYes
snapshot_idYesOpaque retained snapshot ID; never parse it.
name_containsNoCase-sensitive name substring.
resource_kindNo

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, covering the safety profile. The description adds a behavioral nuance that 'child usage overlaps parent usage,' which hints at data relationships. However, it does not describe return format, pagination, or other operational details beyond that.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no redundant wording. The purpose and key usage hint are front-loaded, making it efficient and scannable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With 8 parameters, 4 enums, and no output schema, the description is too brief. It omits the purpose of optional parameters such as limit, sort_by, environment, and resource_kind, and does not describe the return structure. While annotations cover safety, the description leaves important calling context unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 25% (snapshot_id and name_contains have descriptions). The description adds meaning for the two required parameters by clarifying that resource_id is the parent's identifier and snapshot_id must be the same as the parent's. This is valuable, but it does not explain optional parameters like limit, sort_by, environment, or resource_kind, so it only partially compensates for the low schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a clear action ('Drill into') and a specific resource ('immediate children of a container, application, or attribution hierarchy'). It distinguishes itself from siblings like list_resources (likely top-level enumeration) and inspect_resource (single resource inspection) by focusing on hierarchical navigation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear guidance on what to pass ('the parent's resource_id and the same snapshot_id') but does not explicitly say when to use this tool versus alternatives like list_resources or inspect_resource. No exclusions or alternative conditions are mentioned, leaving the choice somewhat implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_resourcesA
Read-only

Find processes, applications, containers, or infrastructure by CPU/memory, filters, and limit. Reuse snapshot_id for follow-ups; resource IDs belong to that observation. Use returned hierarchy for attribution; do not infer container membership from process names or source labels alone.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
parentNoImmediate children of this resource; requires snapshot_id.
sort_byNo
max_age_msNoLatest snapshot age bound; default 3000, zero forces collection. Mutually exclusive with snapshot_id.
sort_orderNo
environmentNo
snapshot_idNoOpaque retained snapshot ID; never parse it.
name_containsNoCase-sensitive name substring.
resource_kindNo

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the tool as read-only and non-destructive, so the description does not need to restate that. It adds meaningful behavioral context beyond annotations: snapshot_id ties results to a specific observation, resource IDs are not globally durable, and hierarchy should be trusted over process-name inference. This is useful boundary-setting that the structured annotations do not convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three compact sentences with no filler. It front-loads the core purpose, then adds essential snapshot and hierarchy caveats. Every sentence contributes unique information that is not already present in the input schema or annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only listing tool with nine optional parameters and no output schema, the description covers the key behaviors: what is found, how to scope results to a snapshot, and how to interpret the returned hierarchy. It does not detail output format or pagination, but those are less critical given the tool's simplicity and the schema's parameter descriptions for the more nuanced fields.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 44%, and the description partially compensates by referencing CPU/memory as sorting dimensions, generic filters, a limit, and snapshot_id reuse. It does not clarify important parameters like environment, resource_kind, parent, or sort_order beyond their schema names and enums. The description adds some semantic context but leaves a meaningful gap for the undocumented or under-documented parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool finds processes, applications, containers, or infrastructure using CPU/memory, filters, and a limit. It names the key resource types and the filtering dimensions, so the agent can infer the general capability. However, it does not explicitly contrast with sibling tools like list_children or inspect_resource.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives valuable operational guidance: reuse snapshot_id for follow-ups, treat resource IDs as scoped to that observation, and rely on the returned hierarchy rather than inferring container membership from names. It does not, however, state when to choose list_resources over list_children or get_system_summary, so the when-to-use guidance is implied rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.1.0
    • First observedget_system_summary
    • First observedinspect_resource
    • First observedlist_children
    • First observedlist_resources

TDQS

A4.2/5.0

Scored across 4 tools

Disambiguation5/5

Each tool targets a distinct action: summary overview, resource search, resource details, and hierarchy drill-down. Even the two 'list' tools are clearly separated by noun (resources vs children), so no ambiguity exists.

Naming Consistency5/5

All tool names follow a consistent verb_noun snake_case pattern (get_, list_, inspect_, list_). The two list_ tools are differentiated by their nouns, preserving a predictable naming scheme.

Tool Count5/5

With 4 tools, the set is tightly scoped for a read-only system monitoring workflow. Each tool fills a necessary role in the exploration process without redundancy or bloat.

Completeness5/5

The tools cover the full exploratory lifecycle: overview (get_system_summary), discovery (list_resources), detail (inspect_resource), and hierarchical navigation (list_children). No obvious operations are missing for the stated purpose.

Maintenance

ActivityActive
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Read-only MCP server for diagnosing Windows crashes, stability, and gaming performance by reading event logs, crash dumps, hardware inventory, performance counters, and registry settings.
    35
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables read-only observability of a Linux host via MCP, exposing allowlisted systemd, docker, nginx, logs, disk, and cert info without shell access.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Read-only MCP server to inspect allowlisted Docker containers, systemd services, JSONL logs, and HTTP health endpoints without arbitrary shell access.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables read-only observability of Windows systems, exposing CPU, memory, storage, service, and health information through Model Context Protocol tools.
    MIT