Skip to main content
Glama

@digitable-lol/tools-core

Headless TypeScript port of the it-tools utility collection: pure functions, a machine-readable catalog, and an MCP stdio server that exposes all of it through three tools instead of ninety-four.

No Vue. No DOM. No browser APIs. Runs under Node, Bun, and as a single compiled binary.

  • 95 tools ported from 75 of the 86 upstream utilities

  • 14 categories, 114 worked examples (all executed by the test suite)

  • 220 tests passing, tsc --noEmit clean

Origin and licence. This is a derivative work of it-tools by Corentin Thomasset, which is published under the GNU General Public License version 3. Because this package copies and adapts that source, it inherits the same licence and is redistributed under GPL-3.0-only — see LICENSE and NOTICE. Not affiliated with or endorsed by the upstream author.


Why three MCP tools

Registering 95 MCP tools would push every input schema into the model's context on every turn. This package routes in two cheap steps instead:

MCP tool

Purpose

Payload

tools_categories

Index of the 14 categories, one line each

~536 tokens

tools_list

Full schemas + examples for one category

1-17 tools

tools_execute

Run a tool by tool_id with args

Results are returned both as structuredContent and as a pretty-printed JSON text block, so clients that read either channel work unchanged.

The design target is a 0.6B router model, which is why descriptions are short and single-purpose, schemas are flat (never deeper than two levels of properties), and oneOf / anyOf never appear in an input schema. A test enforces all three properties.


Related MCP server: aleostudio MCP Server

Quick start

npm install
npm test                 # 220 tests
npm run typecheck
npm run build:binary     # -> dist/digit-tools-mcp
npm run smoke:binary     # replays all 114 catalog examples through the binary

Run the server straight from source:

npm run mcp              # node --experimental-strip-types src/mcp.ts

Use it as a library:

import { executeTool, getCategoryIndex, getToolsInCategory } from '@digitable-lol/tools-core';

getCategoryIndex();                    // step 1: which category?
getToolsInCategory('crypto');          // step 2: which tool, and what arguments?

await executeTool('hash_text', { text: 'hello', algorithm: 'SHA256' });
// { ok: true, result: { algorithm: 'SHA256', encoding: 'Hex', hash: '2cf24dba…' } }

await executeTool('hash_text', { algorithm: 'SHA256' });
// { ok: false, code: 'invalid_args', error: "must have required property 'text'" }

executeTool never throws. Every outcome is {ok: true, result} or {ok: false, error, code}, where code is one of unknown_tool, invalid_args, execution_error, timeout. Arguments are validated against the tool's input_schema with ajv (coerceTypes and useDefaults on, since small models routinely emit "2024" where a number is wanted).


Connecting an MCP client

Compiled binary — no runtime required on the host:

{
  "mcpServers": {
    "digit-tools": {
      "command": "/absolute/path/to/tools-core/dist/digit-tools-mcp"
    }
  }
}

From source, using Node's built-in TypeScript stripping:

{
  "mcpServers": {
    "digit-tools": {
      "command": "node",
      "args": [
        "--experimental-strip-types",
        "/absolute/path/to/tools-core/src/mcp.ts"
      ]
    }
  }
}

Binary size and startup

Metric

Value

Binary size

111,683,904 bytes (107 MiB)

Startup (spawn → initialize response), median

542 ms

Startup, min / p90

390 ms / 641 ms

Same, running from source under Node

~3,900 ms median

Measured with npm run bench:startup (12 runs, first two discarded) on Linux 6.8, Node v24.18, Bun 1.3.12, at a 1-minute load average of ~10 on 8 cores.

Caveat on these numbers: this machine carries other workloads. A re-run at load average ~48 gave min 3,711 ms / median 7,794 ms for the same binary — roughly 14x worse purely from CPU contention. Re-measure on an idle host before treating 542 ms as the figure; the shape of the result (binary ≈ 7x faster than Node-from-source) held at both load levels.

Startup was 1,214 ms before the heaviest payloads — mathjs, the OUI table, markdown-it, libphonenumber, sql-formatter, node-forge, qrcode, figlet, the emoji dataset — were moved behind dynamic import() calls inside the tools that need them. The first call to those tools pays a one-time load cost; everything else starts fast.


The catalog

src/catalog.ts is the key artifact. Each entry:

{
  id: 'hash_text',
  name_ru: 'Хеш текста',
  name_en: 'Hash text',
  category: 'crypto',
  description_ru: 'Считает криптографический хеш строки выбранным алгоритмом.',
  deterministic: true,
  input_schema:  { /* flat JSON Schema */ },
  output_schema: { /* flat JSON Schema */ },
  examples: [{ input: {...}, output: {...} }],
  run: hashText,
}

API:

  • getCategoryIndex() — the 14 categories with one-line descriptions and tool counts (1,875 chars ≈ 536 tokens)

  • getToolsInCategory(cat) — full schemas for one category, implementations stripped

  • getToolSchema(id), getFullCatalog(), getToolIds()

  • npm run catalog:dump writes the whole thing as JSON (~184 KB)

Entries live in src/catalog/*.ts grouped by family; src/catalog.ts aggregates them and exposes the routing API.

Categories

id

Russian name

Tools

crypto

Криптография

9

generators

Генераторы

9

auth

Аутентификация

7

encoding

Кодирование

12

converter

Конвертеры форматов

17

text

Работа с текстом

8

web

Веб

10

network

Сети

5

development

Разработка

5

data

Разбор данных

4

datetime

Дата и время

1

math

Математика

3

measurement

Измерения

2

images

Изображения

3

Ten come straight from the upstream registry; generators, auth, encoding and datetime were split out of it-tools' oversized Converter and Web buckets so no category is large enough to blow the router's context.

Examples are the eval set

Every tool has at least one example, and test/catalog.test.ts executes all 114 through executeTool. For the 80 deterministic tools the output is compared with a deep equality check. For the 15 non-deterministic ones the example carries match: 'shape' and only the key set and value kinds are verified — the values genuinely change between runs.

npm run smoke:binary replays the same 114 examples through the compiled binary over real stdio. This is what caught the figlet font-loading bug described below; a passing unit suite would not have.


What was ported

Group A — pure deterministic functions (62 upstream utilities). Hashes, HMAC, codecs, format converters, parsers, formatters, calculators. Logic carried over from the upstream .service.ts / .models.ts where one existed, or lifted out of the <script setup> block where it did not. Cryptographic code (bcrypt, BIP39, JWT, crypto-js ciphers, OTP) was moved verbatim and still leans on the same npm packages at the same major versions as it-tools.

Group C — random or clock-dependent (13 upstream utilities, 15 tool ids). Ported and flagged deterministic: false:

bcrypt_hash, encrypt_text, rsa_keypair_generate, token_generate, uuid_generate, ulid_generate, bip39_generate, lorem_ipsum_generate, random_port_generate, mac_address_generate, otp_generate_totp, otp_verify_totp, otp_secret_generate, ipv6_ula_generate, eta_calculate

Where it was cheap, randomness was made reproducible. src/utils.ts provides a seeded mulberry32 PRNG, and an optional integer seed makes token_generate, uuid_generate, ulid_generate, lorem_ipsum_generate, random_port_generate, mac_address_generate and otp_secret_generate repeatable. Clock-driven tools take an explicit time instead: otp_generate_totp/otp_verify_totp accept now, ipv6_ula_generate accepts timestamp, eta_calculate accepts startedAtMs.

Not seedable, deliberately: bcrypt_hash and encrypt_text draw their salt from the underlying library, and rsa_keypair_generate from node-forge. Forcing a seed there would mean reimplementing cryptographic primitives, which is exactly the kind of change that introduces silent security bugs.

Three tools are deterministic despite living next to random ones, and are marked as such: bcrypt_compare, decrypt_text, bip39_from_entropy / bip39_to_entropy.

What was NOT ported (Group B — 11 utilities)

Utility

Reason

base64-file-converter

Needs a browser File/FileReader upload

camera-recorder

MediaRecorder + camera permissions

chronometer

A live wall-clock stopwatch; no meaning in a request/response tool

device-information

Reads navigator, screen, window

keycode-info

Reads live DOM keyboard events

pdf-signature-checker

Needs an uploaded PDF binary

html-wysiwyg-editor

TipTap editor UI; no headless logic

text-diff

Pure Monaco diff-editor widget; the .vue file contains no logic at all

git-memo

Static cheatsheet, not a computation

regex-memo

Static cheatsheet, not a computation

meta-tag-generator

Skipped: depends on @it-tools/oggen, and the value is a form UI rather than a function

The last three are honest omissions rather than technical impossibilities — a cheatsheet lookup tool could be built, it just would not be a port of anything.


Deliberate deviations from upstream

Ten behaviour changes, made on purpose, each because the browser original was wrong for a headless caller. Everything not on this list is a straight port — see NOTICE for the structural changes (Vue removed, catalog / execution / MCP layers added) that sit on top of these.

  1. xml_format emits LF, not CRLF. xml-formatter defaults to \r\n.

  2. temperature_convert rounds to 10 decimals. The scale factors are irrational in binary, so 100 °C → °F produced 211.99999999999994. Rounding removes the noise without touching any precision a temperature reading carries.

  3. benchmark_stats takes one series, not many. Upstream compared several suites at once, which forces an array-of-objects-holding-arrays input — three levels of schema nesting. One series per call keeps the schema flat.

  4. chmod_calculate takes a flat string. Upstream drove nine checkboxes; here "755" or "rwxr-xr-x" works, in either direction.

  5. json_diff returns a flat change list. Upstream produced a nested tree for its tree widget; a list of {path, status, oldValue, value} is far easier for a small model.

  6. Errors are thrown, not swallowed. Upstream wrapped most tools in withDefaultOnError(..., '') and rendered an empty string. Here the throw propagates to executeTool, which turns it into {ok: false, code: 'execution_error'}. Affects xml_format, token_generate (empty alphabet) and the format converters.

  7. eta_calculate formats durations without date-fns locales, so output is stable.

  8. jwt_parse renders exp/iat/nbf as ISO 8601, not a machine-local locale string.

  9. QR codes render as SVG, not a canvas — same qrcode package, headless renderer.

  10. emoji-picker became emoji_search. Upstream is a scrollable grid with a fuse.js search box; headless, only the search means anything. Fuzzy matching is replaced by exact / prefix / substring tiers over name, slug, keywords, group, code points and the emoji itself, so a query always returns the same list in the same order. With no query the tool returns the first limit emoji of the dataset (optionally of one group).

Everything else is a straight port. Where upstream had a .service.ts, the algorithm is unchanged.


Tests

test/catalog.test.ts   124 tests   catalog integrity + all 114 examples via executeTool
test/ported.test.ts     60 tests   upstream unit tests, imports adapted
test/tools.test.ts      18 tests   mac_address_lookup and emoji_search, incl. embedded data
test/execute.test.ts    11 tests   validation, defaults, coercion, timeouts, error mapping
test/mcp.test.ts         7 tests   the three MCP tools over an in-memory transport
──────────────────────────────────
                       220 passing

test/ported.test.ts carries over every upstream *.service.test.ts / *.models.test.ts that survived the port: chmod, hash-text, integer-base-converter, ipv4-address-converter, ipv4-range-expander, json-diff, json-to-csv, json-viewer, list-converter, mac-address-generator, numeronym, OTP, password-strength, regex-tester, roman-numerals, safelink, string-obfuscator, text-statistics, text-to-binary, text-to-unicode, token-generator, xml-formatter, color-converter, date-time-converter. The two assertions that changed are commented in place, both consequences of deviation #6 above.

Upstream tests that were dropped: chronometer.service.test.ts (tool not ported).


Known limitations

  • The execution timeout is not preemptive. JavaScript cannot interrupt synchronous CPU-bound work. withTimeout fires at the next await boundary, which in practice covers the lazily-loaded tools; a runaway regex inside regex_test would still block the process. Fixing this properly needs a worker thread. Documented on withTimeout in src/execute.ts and pinned by a test.

  • ascii_art_generate ships 15 fonts, not figlet's 328. figlet reads .flf files from disk at runtime, which bun build --compile does not bundle — the binary crashed with ENOENT: /$bunfs/fonts/Standard.flf. A curated set is embedded as data by scripts/generate-figlet-fonts.mjs (~300 KB) and loaded lazily. Add names to that script and to src/tools/figlet-font-names.ts to include more.

  • The OUI vendor table is embedded, not read from oui-data. Same trap as the fonts: the package is a bare index.json that require resolved from node_modules, so the compiled binary failed with Cannot find package 'oui-data' from '/$bunfs/root/…' whenever it ran outside this checkout. All 39,227 entries are inlined by scripts/generate-oui-data.mjs into src/tools/oui-data.ts (~4 MB) and loaded through a lazy import(). Run npm run generate:oui after bumping the dev dependency to refresh it.

  • emoji_search ships a snapshot of the CLDR emoji data. 1,870 emoji with names, slugs, groups and keywords are inlined in src/tools/emoji-data.ts (~260 KB), taken from unicode-emoji-json 0.4.0 and emojilib 3.0.10 — the datasets upstream loads in the browser. Refreshing them means regenerating that file.

  • json_to_toml / yaml_to_toml write 8_080, not 8080. @iarna/toml uses underscore digit separators for larger integers. This is the library's canonical output, left as-is.

  • iban_validate reports a generic checksum error. ibantools v4 exposes its detailed error-code list through an overload that extractIBAN does not surface, so the errors array is coarser than upstream's.

  • The binary is 107 MiB. Bun's compiled output embeds the whole runtime. The embedded OUI table (~4 MB), the figlet fonts (~300 KB) and the emoji dataset (~260 KB) add to it, but the runtime dominates.

  • iarna-toml-esm (used upstream) is unloadable under plain Node ESM, so this package uses @iarna/toml v3 — the same code, same major version, packaged for CommonJS.

Layout

src/
  catalog.ts            aggregation + getCategoryIndex / getToolsInCategory
  catalog/*.ts          catalog entries grouped by family
  execute.ts            executeTool: ajv validation, timeout, structured results
  mcp.ts                binary entry point
  mcp-server.ts         the three MCP tools
  tools/*.ts            the ported implementations
  utils.ts              base64, seeded PRNG, shared helpers
  interop.ts            dual-build (CJS/ESM) default-export unwrapping
  node-require-shim.ts  global `require` for js-sha256's eval probe
scripts/                font/OUI generation, catalog dump, binary smoke test, benchmark
test/                   the four suites above

src/interop.ts and src/node-require-shim.ts exist because several dependencies ship both a CommonJS and an ESM build. Node resolves one, Bun's bundler resolves the other, and a plain default import works in exactly one of them. The namespace-import-plus-unwrap pattern is what lets identical source run under node --experimental-strip-types, vitest, and bun build --compile.

License

GPL-3.0-only, inherited — not chosen — from it-tools by Corentin Thomasset. This package copies and adapts it-tools source code, which makes it a derivative work, so the GNU General Public License version 3 covers it as a whole. Upstream states "GNU GPLv3" and ships the plain GPL-3.0 text with no "or any later version" grant, so version 3 is the version that applies here too.

The full licence text is in LICENSE. Provenance, the list of changes, and the third-party data notices are in NOTICE.

Practical consequence: if you hand someone the compiled binary (dist/digit-tools-mcp), GPL-3.0 sections 4-6 require you to give them the corresponding source under the same licence. That obligation is why this repository is published rather than kept private — the binary is distributable precisely because its source is here.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    A
    quality
    D
    maintenance
    A lightweight MCP server providing utility tools for math, text processing, data conversion, and URL fetching. It supports both STDIO and SSE communication modes for seamless integration with Claude Desktop and remote AI agents.
    Last updated
    5
    1
  • F
    license
    -
    quality
    D
    maintenance
    Swiss-army-knife utility MCP server for AI agents. 18 tools for JSON validation/formatting, base64 encode/decode, hash generation, UUID generation, URL parsing, regex testing, markdown↔HTML conversion, text stats, slug generation, datetime conversion, cron parsing, text diffing, CSV↔JSON conversion, and JWT decoding. Zero API Key required
    Last updated
    5
  • A
    license
    -
    quality
    C
    maintenance
    MCP server providing 35 utility tools for AI agents including text analysis, encoding, hashing, password generation, JSON/CSV/XML parsing, regex, color, date, finance, URL metadata, SEO tags, DNS lookup, SSL inspection, and JWT decoding. Free, zero-dependency, and works with any MCP client.
    Last updated
    MIT

View all related MCP servers

Related MCP Connectors

  • Remote MCP server: 10 developer utilities (base64, JWT, DNS, UUID, URL, JSON, UA, IP lookup).

  • 500+ deterministic tools for AI agents: math, conversion, validation, hashing, encoding, date/time.

  • Markdown-first MCP server for Notion API with 8 composite tools and 39 actions.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/digitable-lol/tools-core'

If you have feedback or need assistance with the MCP directory API, please join our Discord server