Extract every quantity with its unit, normalized, and refuse the ambiguous ones by name
- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Topics declared
- ✓Documented (README)
claude mcp add units-le -- npx -y units-le-mcp{
"mcpServers": {
"units-le": {
"command": "npx",
"args": ["-y", "units-le-mcp"]
}
}
}MCP Servers overview
<p align="center">
<img src="src/assets/images/icon.png" alt="Units-LE Logo" width="96" height="96"/>
</p>
<h1 align="center">Units-LE: One Quantity, One Unit</h1>
<p align="center">
<b>Find every quantity in a document — durations, sizes, percentages, frequencies — with its value in one base unit, and refuse the ambiguous ones by name</b><br/>
<i>YAML · TOML · JSON · INI · dotenv · CSV · and anything else, scanned as text</i>
</p>
<p align="center">
<a href="https://marketplace.visualstudio.com/items?itemName=nolindnaidoo.units-le">
<img src="https://img.shields.io/badge/Install%20from-VS%20Code-blue?style=for-the-badge&logo=visualstudiocode" alt="Install from VS Code Marketplace" />
</a>
<a href="https://open-vsx.org/extension/nolindnaidoo/units-le">
<img src="https://img.shields.io/open-vsx/dt/nolindnaidoo/units-le?style=for-the-badge&label=Open%20VSX&color=blue" alt="Open VSX downloads" />
</a>
<a href="https://www.npmjs.com/package/units-le-mcp">
<img src="https://img.shields.io/npm/v/units-le-mcp?style=for-the-badge&label=MCP%20server&color=blue&logo=npm" alt="units-le-mcp on npm" />
</a>
<a href="https://crates.io/crates/units-le">
<img src="https://img.shields.io/crates/v/units-le?style=for-the-badge&label=Rust%20CLI&color=blue&logo=rust" alt="units-le on crates.io" />
</a>
<a href="https://letools.dev/tools/units-le">
<img src="https://img.shields.io/badge/LE%20Tools-letools.dev-blue?style=for-the-badge" alt="LE Tools" />
</a>
</p>
---
> **Useful?** A star or rating is how other developers find it —
> [★ GitHub](https://github.com/nolindnaidoo/units-le) ·
> [★ Open VSX](https://open-vsx.org/extension/nolindnaidoo/units-le/reviews) ·
> [★ Marketplace](https://marketplace.visualstudio.com/items?itemName=nolindnaidoo.units-le&ssr=false#review-details)
## What it does
`timeout: 30s` in one service and `timeout: 30000` in another are the same number of milliseconds, and nothing about the text says so. `memory: 1GB` and `memory: 1GiB` differ by 7% and look identical at a glance.
Run `Units-LE: Extract Quantities` and every quantity in the active document — a number welded to a unit — opens in a report beside the editor: as the document wrote it, in one base unit so two of them can be compared, with its line, column and key path. A quantity it cannot read unambiguously keeps its row and gets a reason instead of a guess. Works in VS Code and in VS Code–based editors like Cursor and VSCodium (installable from Open VSX).
- **Before a migration** — every timeout and memory limit in a values file, in milliseconds and bytes
- **Reviewing a config change** — the `500m` that means minutes to one reader and millicores to another
- **Against a runbook or a retention policy** — the limit the document states, checked against the one the config sets
- **Across a project** — every file that holds a quantity, and how many, in one table
**It never rewrites a document, and never guesses.**
## Install
| Where | What you get | Install |
|---|---|---|
| **VS Code** | The report, in your editor, on a keystroke | [Marketplace](https://marketplace.visualstudio.com/items?itemName=nolindnaidoo.units-le) |
| **Cursor, VSCodium, Windsurf** | The same extension | [Open VSX](https://open-vsx.org/extension/nolindnaidoo/units-le) |
| **A terminal or a CI step** | A whole tree, with exit codes | `cargo install units-le` · [crates.io](https://crates.io/crates/units-le) |
| **Any MCP agent, via Node** | `extract_units` over stdio | `npx units-le-mcp` · [npm](https://www.npmjs.com/package/units-le-mcp) |
## A refusal is a finding
This is the whole product. Any tool can multiply `30s` by a thousand;
what makes this one usable in a review is that it will not pretend.
| It sees | Reason | What comes back |
|---|---|---|
| `500m`, `10M`, `5k`, `1y`, `100Mb` | `ambiguous_unit` | no base value — `m` is minutes, milliseconds or Kubernetes millicores; `M` is mega- or minutes; `k`/`K`/`G`/`T`/`P` are prefixes with no unit after them; a calendar year has no fixed length; a lowercase `b` is bits by the standard and bytes in most software that writes it |
| `1.5KB`, `1.5KiB` | `fractional_bytes` | no base value — a byte count has no fractional part. `1.5KiB` is exactly 1536 bytes and is **still** refused: the refusal is about the category, not the arithmetic |
| `1,5s`, `1.000s`, `1.2.3s` | `locale_separator` | no base value — one and a half, or fifteen hundred? This tool does not infer a locale. Narrowed so `0.825s` and `2.5s` read normally |
| `1h + 30m`, `30s*2` | `compound_arithmetic` | no base value — an expression, not a quantity, reported as **one** finding rather than two |
| `1MB`, `256KB` | `si_iec_hazard` | **the base value, plus the reason** — see below |
| `99999…PiB` | `out_of_range` | no base value — it does not fit in 128 bits, and is refused rather than wrapped |
Every one of them keeps its row, its source text, its position and a
sentence a person can act on. A refusal is never a dropped row and
never a guess.
### `si_iec_hazard` annotates; it does not withhold
It is the one reason that still answers. `MB` is 10^6 by the standard,
so `1MB` comes back as **1000000 bytes** *and* carries
`reason: "si_iec_hazard"` — because a great deal of software writes
`MB` and means 2^20. Reporting nothing would be less useful than
reporting the standard answer with the flag attached, and picking 2^20
would be the guess this tool exists not to make.
So it is **not counted as refused**. A row with `si_iec_hazard` is the
only row that carries both a `base` and a `reason`; every other reason
means the base is `null`.
**A bare number with no unit is not a finding at all.** `timeout: 30`
yields nothing here — that is
[numbers-le](https://github.com/nolindnaidoo/numbers-le)'s question, and
the boundary is what keeps the two tools distinct.
## Four dimensions
| Dimension | Base unit | Units read |
|---|---|---|
| `duration` | milliseconds | `ns` `us` `µs` `μs` `ms` `s` `sec` `secs` `min` `mins` `h` `hr` `hrs` `d` `day` `days` `w` · compounds `1h30m` · ISO-8601 `PT1H30M` `P1DT2H` `P2W` `PT0.5S` |
| `bytes` | bytes | `B` · SI `kB` `KB` `MB` `GB` `TB` `PB` · IEC `KiB` `MiB` `GiB` `TiB` `PiB` · Kubernetes `Ki` `Mi` `Gi` `Ti` `Pi` |
| `percent` | ratio | `%` — `15%` is `0.15` |
| `frequency` | hertz | `Hz` `kHz` `KHz` `MHz` `GHz` `THz` |
**Case is part of the symbol.** `MB` is a megabyte and `Mb` is a
megabit; folding them would silently multiply by eight, so `Mb` is
refused rather than treated as a synonym.
**`1h30m` is a grammar and `1h + 30m` is arithmetic.** The first is one
quantity written in two parts. Inside a compound the parts disambiguate
each other — `m` alone is refused, and `m` between `h` and `s` can only
be minutes, because a compound is written largest-first. That ordering
is required rather than assumed, so `30s1h` is not a quantity at all.
**Physical units are a non-goal, not a gap.** Length, mass, temperature
belong to a different question, and reading them would mean unit algebra
(`m/s²`), which is what `uom` is for. See
[`crate/SPEC.md`](crate/SPEC.md).
## Formats
JSON, YAML, CSV, TOML, INI and dotenv are parsed. **Everything else is
scanned as text** — a Kubernetes manifest, a Terraform file, a Markdown
table of limits, a log — so the files where quantities actually live
yield them rather than nothing.
Unlike numbers-le's text scan, this one has a shape to look for, so
`v1.2.3` yields nothing rather than two numbers. Every format with a
shape carries a key path — `cache.ttl`, `limits[0]`, `server.timeout`,
`TIMEOUT` — and every finding carries a 1-based line and column, the
column in UTF-16 units, which is the number your editor shows.
**Its false-positive class is measured rather than imagined.** A run
inside an opaque blob still reads as a quantity: `001d` in a UUID is one
day, and a base64 hash ending `/2w==` is two weeks. The boundary
characters that let those through are the same ones that let `-30s` and
`ttl=30s` through. Over `crate/fixtures/documents/opaque.txt` — 280
lines of lockfile hashes, container digests, git object names, UUIDs and
signing material, none of which is a quantity — the scan reports **5
false findings, 1.8%**, and a test prints that number on every run.
Each one carries its line and column, which is what makes it a row you
discard rather than a number you trust.
## It has no opinions
No "this timeout is too low". No defaults database. No conversion flag,
no rewriting, no arithmetic. It reports what a document says and what
that means in one base unit; which limits are right is the reviewer's
call.
## Across a folder or a workspace
Extract reads the document you have open. A scan reads many files from disk and gives one report.
- **The whole workspace**: run `Units-LE: Scan Workspace for Quantities` from the command palette.
- **One folder**: right-click it in the Explorer and choose `Scan Folder for Quantities`, or run `Units-LE: Scan Folder for Quantities` and pick one.
The report opens with a table of every file that holds something, then has a section per file:
```markdown
# Units-LE workspace report
`my-project` · 113 file(s) read · 3 quantit(ies), 1 could not be resolved
| File | Quantities | Could not be resolved |
|---|---|---|
| `deploy/values.yaml` | 3 | 1 |
## `deploy/values.yaml` · yaml (3)
- **2:8** · `1h30m` · duration · → `5400000` milliseconds · key `cache.ttl`
- **3:9** · `512MiB` · bytes · → `536870912` bytes · key `memory`
- **4:7** · `2GB` · bytes · → `2000000000` bytes · key `disk`
> 2 file(s) larger than the safety limit were not read.
```
**What a scan reads.** Files come from disk, so an unsaved edit is not seen. A file over the safety size, or one that is not UTF-8 text, is left unread. It stops at 5,000 files or 10,000 listed quantities. The report ends with a line for each thing it left out, so a short report is never mistaken for a clean project.
**What it skips, and how to change that.** Three switchWhat people ask about units-le
What is nolindnaidoo/units-le?
+
nolindnaidoo/units-le is mcp servers for the Claude AI ecosystem. Extract every quantity with its unit, normalized, and refuse the ambiguous ones by name It has 1 GitHub stars and its last recorded update is dated 2026-10-06.
How do I install units-le?
+
You can install units-le by cloning the repository (https://github.com/nolindnaidoo/units-le) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is nolindnaidoo/units-le safe to use?
+
Our security agent has analyzed nolindnaidoo/units-le and assigned a Trust Score of 95/100 (tier: Verified). See the full breakdown of passed checks and flags on this page.
Who maintains nolindnaidoo/units-le?
+
nolindnaidoo/units-le is maintained by nolindnaidoo. The last recorded GitHub activity is dated 2026-10-06, with 4 open issues.
Are there alternatives to units-le?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy units-le to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/nolindnaidoo-units-le)<a href="https://claudewave.com/repo/nolindnaidoo-units-le"><img src="https://claudewave.com/api/badge/nolindnaidoo-units-le" alt="Featured on ClaudeWave: nolindnaidoo/units-le" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
The fastest path to AI-powered full stack observability, even for lean teams.