Skip to main content
Glama
First-CH

@first-ch/tools-mcp

Official
by First-CH
README.md
# @first-ch/tools-mcp

MCP server exposing [First CH Tools](https://tools.first-ch.com)' free web-tool logic — WCAG contrast, JP character/X-weight counting, WebP conversion, JSON-LD generation, llms.txt generation, encoding/line-ending conversion, Marp Markdown→slide rendering, Japanese/English test-data generation, text/code diffing, cron-expression explanation, Base64/data-URI encoding, URL query-parameter editing, HTML entity escaping/unescaping, JSON⇄YAML conversion, px ⇄ rem/em unit conversion, colour-code conversion with alpha compositing, MD5/SHA-1/SHA-256/SHA-384/SHA-512 hashing, JWT decoding with expiry and signature checks, User-Agent parsing with device/bot detection, bulk UUID v4 / ULID generation, aspect-ratio calculation with responsive sizing, case conversion between camelCase, snake_case and kebab-case, CSV/TSV ⇄ JSON conversion, and IP/CIDR calculation with subnet splitting and IP-range reduction — to AI agents such as Claude Code.

日本語版は [後半セクション](#日本語) を参照してください。

## Install

Three ways to add this server, pick whichever fits your client.

**Requires Node.js `>=18.14.1`** (all three methods below run the server via `npx`, so Node must be installed even when the MCP client itself — e.g. Claude Code's native, no-Node install — doesn't strictly require it).

### 1. npm, via the Claude Code CLI

```bash
claude mcp add firstch-tools -- npx -y @first-ch/tools-mcp
```

### 2. Claude Code plugin (also installs the same MCP server)

```
/plugin marketplace add First-CH/firstch-tools-mcp
/plugin install firstch-tools@first-ch
```

### 3. Any other MCP client (generic JSON config)

Add to your client's server config (e.g. `mcp.json` / `claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "firstch-tools": {
      "command": "npx",
      "args": ["-y", "@first-ch/tools-mcp"]
    }
  }
}
```

This server is also registered in the [MCP Registry](https://registry.modelcontextprotocol.io) as `io.github.First-CH/tools-mcp` (see [`server.json`](./server.json)), so registry-aware clients can discover and install it by that name too.

## Tools

| Tool | What it does | Main input |
| --- | --- | --- |
| `contrast_check` | Computes the WCAG 2.1 contrast ratio between a foreground and background color and returns AA/AAA pass/fail (normal text, large text, UI components) | `fg`, `bg` (hex, e.g. `#333333` / `333` / `fff`) |
| `count_chars` | Counts Japanese text by grapheme, breaks it down into zenkaku/hankaku, counts lines, and computes the X (Twitter) post weight (zenkaku=2, hankaku=1, URL=23 flat, limit 280) | `text` |
| `webp_convert` | Converts PNG/JPEG files (absolute paths) to WebP using the same libwebp WASM encoder (default quality 80) as the browser tool at tools.first-ch.com/webp/. Output defaults to the same directory as each input with a `.webp` extension | `paths[]` (absolute paths), `quality?` (1-100), `outputDir?` |
| `jsonld_generate` | Generates schema.org JSON-LD for `organization` / `faqpage` / `service` / `breadcrumb`. Empty fields are omitted automatically. Returns both a `json` object and a ready-to-embed `<script>` snippet | `type`, plus the matching `organization` / `faq` / `service` / `breadcrumb` object |
| `llmstxt_generate` | Generates an `llms.txt` file (per the llmstxt.org proposed format) summarizing a site for AI crawlers/agents | `siteName`, `summary?`, `notes?`, `sections?` |
| `encoding_convert` | Detects the character encoding (UTF-8 / Shift_JIS), BOM and line endings (CRLF / LF / CR) of a file or text and converts it to UTF-8. Useful for diagnosing garbled Japanese CSVs and for normalising line endings. Output is UTF-8 only — encoding *to* Shift_JIS is not supported (no standard API, and a mapping table would be required). | `base64` or `text`, `mode` (`analyze` \| `convert`), `encoding`, `newline`, `bom` |
| `marp_render` | Renders [Marp](https://marp.app) Markdown to slides. Emits a self-contained HTML file (theme CSS inlined; opens in a browser and prints one-slide-per-page) and optionally PDF. Ships a bundled Japanese theme `firstch` (firstch-design tokens: paper/ink/vermilion, IBM Plex Sans JP) used as the default theme. Marp front-matter in the Markdown (`theme:` / `paginate:` / `size:` / `<!-- _class: lead -->`) is honored. | `markdown` or `inputPath`, `theme?`, `formats?` (`html` \| `pdf`), `outputPath?`, `title?` |
| `testdata_generate` | Generates dummy data for form / CSV-import testing. `mode=records` returns names, kana readings, addresses, postal codes, emails and phone numbers as CSV/TSV/JSON/XLSX with a choice of encoding (UTF-8 / Shift_JIS), BOM and line endings. `format=xlsx` returns a real Excel workbook as base64 (or writes it to `outputPath`) — the ZIP/OOXML parts are assembled directly, with no spreadsheet dependency, and postal codes and phone numbers are written as text cells so leading zeros survive; `mode=text` returns strings of exactly n-1 / n / n+1 characters for `maxlength` boundary tests. All output is fictional (emails use the RFC 2606 `example.com` family). Passing a `seed` makes the output reproducible | `mode?`, `rows?`, `fields?`, `format?`, `locale?`, `encoding?`, `newline?`, `bom?`, `header?`, `seed?`, `preset?`, `length?`, `outputPath?` |
| `diff_check` | Compares two texts (or two files) and returns the added/removed/changed line counts plus a unified diff (`.patch`) — the same logic as the browser tool at tools.first-ch.com/diff/. Lines are matched with patience diff (lines occurring exactly once in both sides become anchors), falling back to Myers only inside anchor-less ranges. With `format=blocks` / `both`, paired changed lines are also compared at token level (runs of letters/digits are one token, CJK characters one each) and returned as `changed_parts`, so you can see *which words* changed. CRLF/CR/LF all count as the same line break; trailing-whitespace-only differences are ignored by default | `a`/`b` (text) or `pathA`/`pathB` (absolute paths), `format?` (`unified` \| `blocks` \| `both`), `context?`, `ignoreWhitespace?`, `ignoreCase?`, `words?` |
| `cron_explain` | Explains a cron expression in plain language and returns the upcoming run times — the same logic as the browser tool at tools.first-ch.com/cron/. Parsing follows Vixie cron (`crontab(5)`): ranges, steps, lists, `JAN-DEC` / `SUN-SAT` names and the `@daily`-style shorthands; six fields means the first one is seconds (node-cron / Spring style). `warnings` call out the traps — day-of-month and day-of-week are OR'd (not AND'd) when both are restricted, `*/n` that does not divide its range evenly is not a uniform interval, and impossible dates such as February 30th never fire. Run times are found on the wall clock of the target time zone and converted back to real instants, so they stay correct across daylight-saving transitions | `expression`, `timeZone?` (IANA, default `UTC`), `count?` (default 5, max 100), `from?` (ISO 8601) |
| `base64_encode` | Encodes text or a file as Base64 and a `data:` URI, and returns ready-to-paste HTML `<img>` / CSS `background-image` snippets — the same logic as the browser tool at tools.first-ch.com/base64/. With `mode="decode"` it turns Base64 or a data URI back into bytes, writing them to `outputPath` when given. For SVG it returns both encodings and defaults to the shorter one (percent-encoding beats Base64, which always inflates by ~33%), always escaping `& " < > # %`, whitespace and non-ASCII so the URI drops straight into an HTML attribute or a CSS `url("…")`. Decoding accepts standard or URL-safe Base64, tolerates whitespace and newlines, does not require padding, and trusts the actual magic numbers (PNG/JPEG/GIF/WebP/ico/PDF/zip/woff/woff2/SVG) over the MIME type a data URI claims | `mode?` (`encode` default / `decode`), `text?` or `path?`, `base64?`, `outputPath?`, `urlSafe?`, `wrap?` (76 for MIME), `dataUri?`, `mimeType?`, `snippets?` |
| `url_params` | Breaks a URL's query string into keys and values, edits it (`set` / `remove` / `utm` / `removeTracking` / `sort`) and rebuilds it — the same logic as the browser tool at tools.first-ch.com/url/. Values come back decoded (`%XX` to characters, `+` to a space), and parameters you did not touch are written back byte-for-byte, so calling it with nothing but a `url` returns exactly what you passed in — signed URLs survive a round trip. `reencode` normalises the whole query with `encodeURIComponent` rules instead. `warnings` flag duplicate keys, unencoded spaces and non-ASCII, broken `%XX`, `+` being read as a space, mixed-case UTM values, a missing utm_source/utm_medium, passwords in the URL, credential-looking keys and URLs over 2,000 characters. Relative paths and broken percent-encoding are parsed as far as they can be read instead of throwing. `mode="encode"` / `"decode"` converts a bare string instead (`scheme`: `component` / `uri` / `form`). No network access — the URL is never fetched | `url?`, `mode?` (`parse` default / `encode` / `decode`), `text?`, `scheme?`, `set?`, `remove?`, `utm?`, `removeTracking?`, `sort?`, `reencode?`, `spaceAsPlus?` |
| `html_escape` | Escapes `< > & " '` into HTML entities, or with `mode="unescape"` turns entities such as `&amp;`, `&#39;` and `&#x3042;` back into characters — the same logic as the browser tool at tools.first-ch.com/html-escape/. Escaping handles `&` first, so a second pass never double-escapes what the first pass produced; pick named or numeric references (`numeric`), decide whether quotes are escaped (`quotes`, mandatory inside an attribute value) and whether `'` is written as `&#39;` or `&apos;` (`apos` — `&apos;` does not exist in HTML 4.01), and encode non-ASCII characters as references (`nonAscii`) when the charset may not survive the pipeline. Unescaping covers all 252 named references from HTML 4.01 plus decimal and hexadecimal ones, maps C1-range references such as `&#128;` to their Windows-1252 characters as the spec requires, and leaves unknown names, out-of-range numbers and semicolon-less references untouched rather than guessing. `notes` reports already-escaped input, bare `&`, missing semicolons, unknown entity names and no-break spaces (U+00A0) | `mode?` (`escape` default / `unescape`), `text?` or `path?`, `outputPath?`, `quotes?`, `apos?`, `numeric?`, `nonAscii?` (`none` / `named` / `decimal` / `hex`) |
| `json_to_yaml` | Converts JSON to formatted YAML, and `yaml_to_json` converts back — the same logic as the browser tool at tools.first-ch.com/json-yaml/, with no YAML dependency (the parser and writer are implemented in this package). Choose the indent width, the quoting style (`quote`), how null is written (`nullStyle`), whether multi-line strings become `|` blocks (`block`), whether keys are sorted (`sortKeys`) and whether the output starts with `---` (`docStart`). Strings another parser could read as a different type — `yes` / `no` / `on` / `off`, `0755`, `12:30`, `2026-08-12`, anything numeric-looking — plus strings with surrounding whitespace or a leading `-` / `*` / `#` are quoted automatically, so the output means the same thing to a YAML 1.1 parser such as PyYAML. JSON containing comments, trailing commas, single quotes or unquoted keys (as in `tsconfig.json`) is read and converted, with `notes` saying it is not valid JSON (`relaxed=false` rejects it strictly instead) | `text?` or `path?`, `outputPath?`, `indent?` (1-8), `quote?` (`auto` / `single` / `double`), `nullStyle?` (`null` / `tilde` / `empty`), `block?`, `sortKeys?`, `docStart?`, `relaxed?` |
| `yaml_to_json` | Converts YAML to JSON — useful for turning docker-compose, GitHub Actions, Kubernetes and CI config into something a program can handle, and for checking that a file parses at all. A syntax error is returned as an error naming the line, the column, the cause and the fix, with the two surrounding lines quoted and a `^` under the column. Multiple documents (`---`) become a single JSON array, and anchors (`&name`), aliases (`*name`) and merge keys (`<<`) are expanded because JSON has no references (`notes` says when that happened). Scalars follow the YAML 1.2 core schema, so `yes` / `no` / `on` / `off` / `NO` stay strings — but `notes` flags them, along with `0755` (decimal 755, not octal), `12:30` (750 in YAML 1.1's base 60), date-like values, duplicate keys and integers beyond 2^53. Supports block mappings and sequences, flow style, quoted scalars (multi-line, with escapes), block scalars (`|` `>` with chomping and an explicit indent), tags (`!!str` `!!int` `!!float` `!!bool` `!!null` `!!binary`) and comments; only the explicit `? key` notation is unsupported | `text?` or `path?`, `outputPath?`, `indent?` (0-8 or `"tab"`; 0 minifies), `sortKeys?`, `ascii?` |
| `px_rem_convert` | Converts CSS lengths between px, rem, em and pt — the same logic as the browser tool at tools.first-ch.com/px-rem/. Pass `value` for a single conversion and you get px/rem/em/pt, a ready-to-paste `font-size` line and the scale of common font sizes (12–64px, one row per size with its typical use); pass `css` or `path` and the whole stylesheet is rewritten (`direction`: `px2rem` default / `px2em` / `rem2px` / `em2px`). The bulk pass never touches comments, strings (`content: "10px"`), the contents of `url()` or digits inside identifiers such as `--size-16px`, and by default keeps hairlines in px (`minPx=2`, because a 1px border in rem varies in thickness between devices) and leaves the conditions of `@media` and other at-rules alone (`skipMedia`), which is where breakpoints live. `ignoreProps` excludes properties by prefix, `zeroUnitless` writes a bare `0`. The root can be given as a percentage (`root="62.5%"` → 10px, read against the 16px browser default), and `notes` reports rounding, skipped values and the accessibility cost of the 62.5% trick | `value?` (`24` / `"1.5rem"`) or `css?` / `path?`, `unit?`, `outputPath?`, `direction?`, `root?`, `parent?`, `precision?` (`auto` or 0-6), `minPx?`, `zeroUnitless?`, `skipMedia?`, `ignoreProps?`, `scale?` |
| `color_convert` | Converts a colour between HEX, RGB, HSL and OKLCH and builds the `rgba()` / `hsla()` / 8-digit HEX code at any alpha — the same logic as the browser tool at tools.first-ch.com/color/. Input can be HEX (3/4/6/8 digits), `rgb()`, `hsl()`, `hwb()`, `oklch()`, `oklab()`, one of the 148 CSS named colours or `transparent`, in either the legacy comma form or the modern slash form, with angles in `deg` / `grad` / `rad` / `turn`. Pass a `background` and `flattened` returns the colour that actually reaches the screen once the transparency is composited over it (`foreground × α + background × (1−α)`) — what you need to turn a semi-transparent layer from a mockup into a solid HEX. It also returns the WCAG 2.1 contrast against white and black, an `alpha_table` stepped by `step`%, and an 11-step lightness `palette` (50–950) that keeps the hue and chroma. An OKLCH value outside sRGB has its chroma lowered by binary search until it fits, keeping the lightness and hue, because clipping the RGB channels would shift the hue; `notes` says when that happened, when the colour matches a named colour exactly, and when it is a neutral grey | `color`, `alpha?` (0-1, 0-100 or `"50%"`), `background?` (default `#ffffff`), `syntax?` (`modern` default / `legacy`), `uppercase?`, `alphaPercent?`, `step?` (1-50, default 10), `alphaTable?`, `palette?` |
| `hash_generate` | Computes the MD5, SHA-1, SHA-256, SHA-384 and SHA-512 digests of a string or a file in one call — the same logic as the browser tool at tools.first-ch.com/hash/. Pass `expected` and the digest is verified against it: the algorithm is inferred from the length, and `verification` says whether it matched. `expected` takes command output as-is — the `<digest>␣␣<filename>` form from `sha256sum`, the output of `shasum -a 256`, the `SHA256 (file) = …` form, a `sha256:` prefix, colon-separated hex, Base64 and base64url are all understood. Output is lower-case hex by default, or `HEX` / `base64` / `base64url`. For `text`, `newline` (`lf` / `crlf`) and `bom` control the exact bytes that get hashed, so a digest produced from a Windows file can be reproduced (the encoding is always UTF-8); neither applies to `path`, which is hashed byte for byte and streamed in a single pass when large. `notes` warns that MD5 and SHA-1 are broken for collision resistance and that a plain digest is not password storage | `text?` or `path?`, `algorithms?` (default `["md5","sha1","sha256","sha512"]`), `format?` (`hex` default / `HEX` / `base64` / `base64url`), `newline?`, `bom?`, `expected?` |
| `jwt_decode` | Decodes a JWT (JSON Web Token) into its header and payload and reports on the expiry — the same logic as the browser tool at tools.first-ch.com/jwt/. `token` can be a whole `Authorization: Bearer <token>` line: the header name, the `Bearer` prefix, surrounding quotes, trailing commas and line breaks inside the token are all stripped. `expiry` gives the status (`valid` / `expired` / `not_yet` / `no_exp`), the ISO timestamps of `exp` / `nbf` / `iat`, the seconds remaining and how far through its lifetime the token is; `clockTolerance` allows for clock skew, and `now` pins the current time so a check is reproducible. Pass `key` and the signature is verified with Web Crypto: HS256/384/512 take the shared secret as a string (`keyEncoding` accepts `base64url` / `hex`), while RS / PS / ES / EdDSA take an SPKI PEM public key or a JWK / JWKS document (a JWKS is matched against the header's `kid`). A private key, a certificate or a PKCS#1 key is refused with the command that converts it. `warnings` cover `alg: none`, a missing signature, an expired or not-yet-valid token, `exp` given in milliseconds (`Date.now()` without dividing by 1000), a lifetime that is too long for an access token, and secrets or personal data left in the payload — which anyone holding the token can read, because base64url is encoding, not encryption. A five-segment JWE returns its header only (it is not decrypted), and nothing is ever fetched: JWKS documents must be passed in | `token`, `key?`, `keyEncoding?` (`utf8` default / `base64url` / `base64` / `hex`), `clockTolerance?` (seconds), `now?` (Unix seconds) |
| `user_agent_parse` | Parses a User-Agent string into browser name and version, rendering engine, OS and version, device type (`desktop` / `mobile` / `tablet` / `tv` / `console` / `wearable` / `bot`), vendor and model, and CPU architecture — the same logic as the browser tool at tools.first-ch.com/user-agent/. `ua` can be a raw access-log line: a leading `User-Agent:`, surrounding quotes and a trailing comma are stripped. Detection tries the more specific token first (`Edg/` → `OPR/` → `Chrome/` → `Safari/`), because Chrome, Edge and Opera all claim both `Chrome/` and `Safari/` and a substring match misclassifies them. `tokens` breaks the string down token by token and explains the fossils (`Mozilla/5.0`, `KHTML, like Gecko`, `Mobile/15E148`). `notes` reports what the UA cannot tell you: Chrome's reduced UA (minor version frozen to `0.0.0`, Android model frozen to `K`), macOS always reporting `10_15_7`, Windows 10 being indistinguishable from 11, an iPad sending the same UA as a Mac, in-app browsers (LINE / Instagram / Facebook / Android WebView), and the fact that a UA is self-declared and must never be used for access control. Crawlers (Googlebot, bingbot, GPTBot, ClaudeBot, PerplexityBot) and HTTP clients (curl, python-requests, axios) are identified as well. Pass `uas` to parse many at once and get a `summary` breakdown by browser, OS and device type | `ua?` or `uas?` (array), `includeTokens?` (default: on for 20 strings or fewer) |
| `uuid_generate` | Generates 1-100 UUID v4 or ULID values at once — the same logic as the browser tool at tools.first-ch.com/uuid/. A UUID v4 is 128 bits of which 122 are random (the other 6 carry the version and variant); a ULID is 26 characters where the first 10 encode the creation time (Unix milliseconds, 48 bits) and the last 16 are random (80 bits), written in Crockford's Base32. Because a ULID leads with the time, sorting the strings lexicographically sorts them chronologically, and `timestamp` (ISO 8601, Unix seconds or Unix milliseconds) pins that time for reproducible output. Values minted inside the same millisecond follow the monotonic rule from the ULID spec — the random part is incremented by one — so a batch of 100 still comes out in creation order. Randomness comes from `node:crypto` `randomBytes` (CSPRNG); `Math.random` is never used. `format` shapes `text` (one per line / JSON array / comma separated / quoted for a SQL `INSERT`), and `uppercase` / `hyphens` / `braces` control the spelling (UUID defaults to lower case per RFC 9562, ULID to upper case; `hyphens: false` gives the 32-character form for MySQL `BINARY(16)`, `braces: true` the Windows GUID form) | `type?` (`uuid` \| `ulid`), `count?` (1-100), `format?`, `uppercase?`, `hyphens?`, `braces?`, `timestamp?` (ULID only) |
| `aspect_ratio_calc` | Calculates between an aspect ratio and a size — the same logic as the browser tool at tools.first-ch.com/aspect-ratio/. Pass `ratio` (`16:9`, `16/9`, `16x9` or a decimal such as `1.85`) with either `width` or `height` and the other side comes back, along with the simplified ratio, the decimal, the `padding-top` percentage and an `aspect-ratio` declaration. Pass `width` and `height` without a `ratio` and it works backwards: the ratio simplified by the greatest common divisor, the orientation, the megapixel count, the closest common ratio (16:9, 4:3, 3:2, 1:1, 4:5, 9:16, 21:9, 1.85:1, 2.39:1, 1.91:1 for OGP, the golden ratio, A-series paper) with the percentage it is off by, and the name of the size where there is one (1920×1080 is FHD). `round` takes the result to the nearest whole pixel, an even number, down or up, and `notes` then reports the real ratio that rounding produced and how far it drifts — H.264 / H.265 store colour at half resolution (YUV 4:2:0), so `round: "even"` is what video needs. `box` fits the size into a frame with `contain` or `cover` and returns the rendered size, the scale, the letterbox / pillarbox bars, how many pixels are cropped and how much stays visible. `widths` (or `table: true`) returns the height at every breakpoint, and `snippet: true` returns the CSS — with an optional `@supports` `padding-top` fallback — plus HTML carrying the `width` / `height` attributes that prevent layout shift | `ratio?` and/or `width?` / `height?`, `round?` (`none` default / `round` / `floor` / `ceil` / `even`), `widths?`, `table?`, `box?` (`"1280x400"`), `fit?` (`cover` default / `contain`), `snippet?`, `selector?`, `target?` (`img` / `video` / `iframe` / `background`), `objectFit?`, `fallback?` |
| `markdown_table` | Turns TSV or CSV copied from a spreadsheet into a Markdown table, and converts a Markdown table back to CSV, TSV, HTML or JSON — the same logic as the browser tool at tools.first-ch.com/markdown-table/. Copying a range in Excel, Google Sheets or Numbers puts **tab-separated** text on the clipboard, not comma-separated, so the delimiter is detected by parsing with tabs, commas and semicolons in turn and keeping whichever gives the most consistent column count — thousands separators (1,200) never split a column. CSV input follows RFC 4180 quoting (commas, newlines and doubled quotes inside a quoted field). When padding cells, full-width characters are counted as two columns following the Unicode East Asian Width property, so the pipes line up in a monospaced editor. Columns whose non-empty cells are all numeric (thousands separators, decimals, currency symbols, percent signs, units) are right-aligned with `---:`, and `aligns` overrides any column. Pipes inside cells become `\|`, line breaks become `<br>` (or a space), short rows are padded with empty cells, and every fix is reported in `notes` rather than applied silently. Reading a Markdown table picks up the alignment colons and turns `<br>` back into a real newline, so a CSV export reopens cleanly in a spreadsheet | `text` or `path`, `outputPath?`, `from?` (`auto` default / `tsv` / `csv` / `ssv` / `markdown`), `to?` (`markdown` default / `csv` / `tsv` / `ssv` / `html` / `json`), `header?` (`first` default / `auto` / `none`), `align?`, `aligns?`, `pad?`, `eastAsian?`, `autoNumber?`, `trim?`, `skipEmpty?`, `transpose?`, `multiline?` (`br` default / `space`), `eol?` |
| `sql_format` | Formats a SQL query that has been squashed onto a single line — uppercase keywords, a line break per clause and real indentation — the same logic as the browser tool at tools.first-ch.com/sql-format/. `SELECT` / `FROM` / `WHERE` / `GROUP BY` / `HAVING` / `ORDER BY` / `LIMIT` / `INSERT INTO` / `VALUES` / `UPDATE` / `SET` / `DELETE FROM` / `WITH` / `UNION` start a line and their contents are indented one level; joins start a line with `ON` indented below them; `AND` / `OR` break per condition, except the `AND` of `BETWEEN a AND b`. A parenthesis becomes an indented subquery only when `SELECT`, `WITH` or `VALUES` follows it, so function calls (`SUM(…)`, `IN (1, 2, 3)`, `OVER (PARTITION BY … ORDER BY …)`) stay on one line, and `CASE` gets `WHEN` / `ELSE` / `END` stacked. Only keywords and type names are re-cased — table, column and alias spellings are left exactly as typed, because identifier folding differs per database. Strings, quoted identifiers (`"…"` / `` `…` `` / `[…]`), comments and placeholders (`?` / `:name` / `$1` / `@var`) pass through verbatim. `notes` reports UPDATE or DELETE without a WHERE, unbalanced parentheses and quotes, `SELECT *`, implicit comma joins and placeholder usage. `compact` collapses everything back onto one line. It is a lexical formatter rather than a parser for one database, so MySQL, PostgreSQL, SQL Server, SQLite and Oracle dialects survive as written (syntax errors are not detected) | `text` or `path`, `outputPath?`, `keywordCase?` (`upper` default / `lower` / `preserve`), `functionCase?`, `indent?` (`2` / `4` default / `8` / `tab`), `commaStyle?` (`trailing` default / `leading`), `logicStyle?` (`leading` default / `trailing`), `breakColumns?`, `breakLogic?`, `breakOn?`, `breakCase?`, `breakSubquery?`, `expandClauses?`, `compact?`, `eol?` |
| `qr_generate` | Turns a URL or any text into a QR code and returns it as SVG (vector), PNG or terminal-friendly text art — the same logic as the browser tool at tools.first-ch.com/qr/. Encoding (numeric, alphanumeric or UTF-8 byte mode, picked automatically from the input), Reed–Solomon error correction, the layout of finder, timing and alignment patterns, version information and the choice between the eight mask patterns are all implemented here (JIS X 0510 / ISO 18004, versions 1–40); there is no external API or image service. `ecLevel` trades capacity for damage tolerance (L about 7%, M about 15%, Q about 25%, H about 30% — use Q or H for print), `size` is the edge length in pixels, and `margin` is the quiet zone in modules (4 is the recommended default; 0 can break scanning over a patterned background). Wi-Fi (`WIFI:T:WPA;S:…;P:…;;`), `mailto:`, `tel:`, `SMSTO:` and `geo:` strings just go in as `text`. `outputPath` writes the file instead of returning the body; `format="png"` without it returns a data URI. Capacity tops out at 7,089 digits, 4,296 alphanumeric characters or 2,953 bytes (all at level L) | `text`, `ecLevel?` (`L` / `M` default / `Q` / `H`), `size?` (64–4096, default 320), `margin?` (0–32, default 4), `format?` (`svg` default / `png` / `text`), `mode?` (`auto` default / `numeric` / `alnum` / `byte`), `mask?` (0–7), `minVersion?`, `outputPath?` |
| `unixtime_convert` | Converts Unix seconds, milliseconds, microseconds and nanoseconds to and from ISO 8601 and ordinary date strings — the same logic as the browser tool at tools.first-ch.com/unixtime/. The unit of a plain number is detected from its digit count (up to 10 digits seconds, up to 13 milliseconds, up to 16 microseconds, longer nanoseconds); `unit` states it outright instead. Digits below the millisecond are dropped and reported in `notes`. On the date side it accepts `2026-08-24T09:30:00Z` (ISO 8601), `2026-08-24 18:30`, `2026/8/24 9:05`, the Japanese `2026年8月24日 18時30分`, `Sun, 24 Aug 2026 03:00:00 GMT` (HTTP-date) and `now`, stripping the quotes, brackets and trailing commas that come with a paste from CSV or JSON. `input` takes one value per line, up to 500 lines at a time, and every row comes back with Unix seconds, milliseconds, ISO 8601 in UTC, local time in `timeZone` (with weekday and UTC offset) and a relative reading ("30分後" / "in 30 minutes"); a line that cannot be read fails on its own without taking the others down. **A date with no offset is read as a wall-clock time in `timeZone`** and always says so in `notes`; an explicit `+09:00` or `Z` in the input wins over `timeZone`. A wall-clock time that daylight saving skips is moved forward to the moment after the change, also noted. An 8-digit number such as `20260824` is Unix seconds here, so it is flagged in case a date was meant. Negative values (before 1970) and fractional seconds work | `input`, `timeZone?` (IANA name, default `UTC`), `unit?` (`auto` default / `s` / `ms` / `us` / `ns`), `now?` (Unix milliseconds or a date string, default the current time), `lang?` (`ja` default / `en`) |
| `robotstxt_generate` | Assembles a robots.txt from crawl rules, sitemap declarations and a preset for AI crawlers — the same logic as the browser tool at tools.first-ch.com/robots-txt/. `ai.preset` decides how 24 known AI crawlers are treated in one move: `training` (the default: refuse the ones that collect training data — GPTBot, ClaudeBot, Google-Extended, Bytespider, CCBot and the rest — while allowing AI search and on-demand fetchers such as OAI-SearchBot, Claude-SearchBot, PerplexityBot and ChatGPT-User), `allow`, `block`, `none` (leave AI crawlers out) or `custom` with `ai.overrides` naming them one by one. **A crawler obeys exactly one matching group**, so writing a group for GPTBot stops the `User-agent: *` rules from ever reaching it; by default the shared disallow paths are copied into the AI groups to close that gap (`ai.inherit: false` turns it off). Paths get a leading slash added and full URLs reduced to their path, and `warnings` reports spaces inside a path, non-ASCII paths that need percent-encoding, wildcards that not every crawler supports, a `Crawl-delay` Googlebot will ignore, a missing sitemap, a User-agent listed twice and a site blocked in full. `listCrawlers: true` returns just the crawler list (name, vendor, purpose) without generating anything | `siteUrl?`, `disallow?`, `allow?`, `userAgents?` (default `*`), `crawlDelay?`, `blockCrawlers?`, `sitemaps?`, `ai?` (`preset?`, `overrides?`, `inherit?`), `groups?`, `comments?` (default true), `allowStyle?` (`disallow-empty` default / `allow-slash`), `listCrawlers?`, `lang?` (`ja` default / `en`), `outputPath?` |
| `case_convert` | Converts identifiers between eleven cases — camelCase, PascalCase, snake_case, CONSTANT_CASE, kebab-case, Train-Case, dot.case, Title Case, Sentence case, lower case and UPPER CASE — the same logic as the browser tool at tools.first-ch.com/case/. Built for the mechanical jobs at the boundary between layers: renaming variables in bulk, moving between `snake_case` database columns and `camelCase` JSON keys, tidying CSV headers. `scope` sets the unit of work: `lines` (the default, one item per line), `items` (every comma or tab separated field of a line, for CSV header rows) or `whole`. **Leading and trailing spaces, indentation, line endings (LF/CRLF) and the separators are preserved**, so only the names change. A run of capitals is broken only before a capital followed by a lower-case letter (`XMLHttpRequest` → `XML` / `Http` / `Request`), and digits stay attached to the word before them (`sha256Hash` → `sha256` / `Hash`; `splitDigits` splits them). Acronyms fold to `parseXmlData` by default and stay capitalised with `keepAcronyms`, except the first word of a camelCase name, which is always lower-cased (`URLParser` → `urlParser`). Characters with no letter case (Japanese, for example) pass through rather than being transliterated. `notes` reports **names that collide after conversion** (`first name` and `first_name` both become `first_name`, which is how a column silently disappears on import), results starting with a digit that cannot be identifiers, and items containing acronyms; `items[].detected` names the case each input was written in. `allFormats` expands every item into all eleven cases, `listFormats` returns just the list of cases with what each is used for | `text?` or `path?`, `outputPath?`, `format?` (`camel` default), `scope?` (`lines` default / `items` / `whole`), `splitDigits?`, `keepAcronyms?`, `allFormats?`, `listFormats?`, `lang?` (`ja` default / `en`) |
| `csv_convert` | Converts CSV/TSV into a JSON array and back — the same logic as the browser tool at tools.first-ch.com/csv-json/. For moving data you already have between a spreadsheet and an API: turning an exported table into JSON a request body can carry, or an API response into something Excel opens. With `direction` omitted it guesses `json2csv` when the input starts with `[` or `{` and `csv2json` otherwise. The delimiter is detected by looking at the first 20 lines for the character that appears the same number of times on every line, and can be pinned with `delimiter`. **Column names such as `stock.qty` are read and written as nested objects, and `tags.0` / `tags.1` (`tags[0]` means the same) become an array** — the reverse direction flattens by the same rule, so a round trip through CSV restores the original structure (`nest: false` writes nested values into a single cell as JSON text). **Type inference is decided by round trip — a value becomes a number only when converting it back to text is character-for-character identical** — so `0123`, `+1`, `1.50` and integers beyond 2^53 (`12345678901234567890`) stay strings and postcodes, phone numbers, invoice numbers and version strings survive. Reading follows RFC 4180: quoted cells, `""` escapes, line breaks inside cells, CRLF and a BOM are all handled, and the output side offers `bom` and `newline` for Excel. On the JSON side it accepts an array, a single object, an object wrapping an array such as `{"data":[…]}`, and JSON Lines. **A JSON syntax error comes back as an `ok: false` result with a line, a column and an excerpt rather than an exception**, so the reply itself says where to fix it. `notes` also reports rows whose column count does not match the header, duplicate header names (kept as `name_2`), empty headers (named `column4`), values that would lose digits, cells starting with `=`, `+`, `-` or `@` that a spreadsheet may execute as a formula (CSV injection), and objects with a different set of keys | `text?` or `path?`, `outputPath?`, `direction?` (`auto` default / `csv2json` / `json2csv`), `delimiter?` (`auto` default / `comma` / `tab` / `semicolon` / `pipe`), `header?`, `nest?`, `types?`, `trim?`, `emptyNull?`, `indent?` (`2` default / 0–8 / `tab`), `newline?` (`lf` default / `crlf`), `quoteAll?`, `bom?`, `lang?` (`ja` default / `en`) |
| `zenkaku_convert` | Converts letters and digits, katakana, symbols and spaces between their full-width and half-width forms **with a direction chosen per kind**, and cleans up the whitespace in the same pass — the same logic as the browser tool at tools.first-ch.com/zenkaku/. For normalising Japanese copy supplied by a client, cleaning a file before an import, and tidying form input. Each of `alnum` / `kana` / `symbol` / `space` takes `keep`, `han` (half-width) or `zen` (full-width); the defaults are the form most Japanese copy is set in — letters, digits, symbols and spaces half-width, katakana full-width (the same as `preset: 'ja'`). `preset` offers `ja`, `csv` (`ja` plus the whitespace and invisible-character cleanup), `han` and `zen`, and any per-kind option overrides it. **Half-width katakana is not one character per glyph**, so converting to full-width composes a following voiced mark (`ガ` → `ガ`, `パ` → `パ`, `ヴ` → `ヴ`) and converting the other way splits it into two. Japanese punctuation, brackets, the middle dot and the prolonged sound mark (`。 、 「 」 ・ ー`) live inside the half-width katakana block (U+FF61–U+FF9F) and are **treated as katakana** (`kana: 'han'` turns `「テスト」。` into `「テスト」。`; use `keep` to leave the punctuation full-width). Spaces cover U+3000 ⇄ U+0020 and fold NBSP and the Latin space family to the half-width side. The cleanup is `collapseSpaces` (a run of spaces becomes its first character), `trimLines`, `blankLines` (`keep` / `collapse` / `remove`), `removeInvisible` (zero-width, BOM, control codes) and `composeMarks` (NFC, for copy that writes `ガ` as `カ` plus a combining U+3099). **Line endings (LF/CRLF), tabs, hiragana and kanji are never rewritten.** `notes` reports what is left — half-width katakana, ideographic spaces, invisible characters, combining marks — plus the wave dash `〜` (U+301C) versus the fullwidth tilde `~` (U+FF5E) and platform-dependent characters (`① ㈱ Ⅲ`), which are **reported and never rewritten** because which way they should go depends on the destination. **This is deliberately not NFKC**: `①` never becomes `1`, and only the kinds you selected are touched. `inspect: true` converts nothing and returns just the audit | `text?` or `path?`, `outputPath?`, `preset?` (`ja` / `csv` / `han` / `zen`), `alnum?`, `kana?`, `symbol?`, `space?` (each `keep` / `han` / `zen`), `collapseSpaces?`, `trimLines?`, `blankLines?` (`keep` default / `collapse` / `remove`), `removeInvisible?`, `composeMarks?`, `inspect?`, `lang?` (`ja` default / `en`) |
| `cidr_calc` | Turns CIDR notation, a subnet mask, an ACL wildcard mask or a bare address into the network address, broadcast address, usable host range, address and host counts, reverse DNS zone and a binary breakdown — the same logic as the browser tool at tools.first-ch.com/cidr/. For subnet maths while writing server configs, firewall rules and Basic-auth allow lists. **IPv4 and IPv6 both work**: `::` compression, a trailing IPv4 form (`::ffff:192.168.0.1`), `[...]` brackets and a `%zone` suffix are parsed, and output follows RFC 5952. `192.168.1.5 255.255.255.0` and `192.168.1.5/0.0.0.255` (the ACL convention) are accepted in the same field. **The exceptions in host counting are built in**: a `/31` has two usable addresses for point-to-point links (RFC 3021), a `/32` is one host, and IPv6 has no broadcast address. When host bits are set in the address you pass, `notes` returns the block it actually belongs to — the form that belongs in a config file. **Octets with a leading zero (`192.168.001.1`) are refused**, because some libraries read them as octal and the same text then means a different address. The address type is matched against the main IANA special-purpose blocks (private, CGNAT, loopback, link-local, documentation, multicast, ULA and more) and a network that extends beyond its reserved range is flagged. `split` lists the equal subnets of a new prefix length, `contains` tests addresses for membership, and `range` reduces ranges such as `10.0.0.5 - 10.0.0.200` (mixed with CIDR blocks and single addresses, one per line or `;`-separated) to **the fewest blocks that cover them exactly**, merging overlaps and adjacencies first. All arithmetic is BigInt | `cidr?` and/or `range?`, `split?` (prefix length), `contains?` (string[]), `limit?` (1-4096, default 256), `lang?` (`ja` default / `en`) |

See [`server.mjs`](./server.mjs) for the exact Zod input schemas.

**PDF output (`marp_render`) needs a local Chrome/Chromium.** marp-core renders the HTML with no browser dependency (so the package stays light for `npx`); PDF is produced by driving a locally-installed Chrome/Chromium in headless mode. It is auto-detected on common paths, or set `MARP_CHROME_PATH` to the executable. If none is found, `marp_render` returns the HTML only and reports `pdf_skipped` — you can still open that HTML and print → PDF yourself (one slide per page).

## Telemetry

Nothing is logged by default. Usage is recorded **only** when you set the `FIRSTCH_TOOLS_USAGE_LOG` environment variable to a file path — each tool call then appends one JSON line (`{ ts, tool, source }`) to that local file. There is no network transmission of any kind; if the variable is unset, no file is written and no data leaves your machine.

## Web version

The same algorithms are also available as a free, no-install browser tool at **[tools.first-ch.com](https://tools.first-ch.com)** — useful when you want a UI instead of an MCP call, or want to hand a link to someone without an MCP client.

## Vendor sync (WebP codec)

`webp_convert` bundles a vendored, unmodified subset of [`@jsquash/webp`](https://github.com/jamsinclair/jSquash) v1.5.0 under [`vendor/jsquash-webp/`](./vendor/jsquash-webp/). The web version at tools.first-ch.com vendors the exact same v1.5.0 subset, so both surfaces produce identical output. Each side's CI independently verifies its vendored files against [`vendor/jsquash-webp/CHECKSUMS.sha256`](./vendor/jsquash-webp/CHECKSUMS.sha256) (see [`.github/workflows/ci.yml`](./.github/workflows/ci.yml)), so a silent, unnoticed drift between the two copies isn't possible.

## License

The package itself is licensed under [MIT](./LICENSE).

It bundles a subset of [jSquash](https://github.com/jamsinclair/jSquash)'s WebP codec under `vendor/jsquash-webp/` to power `webp_convert`, which carries its own licenses:

- The jSquash wrapper code is licensed under **Apache-2.0** — see [`vendor/jsquash-webp/LICENSE`](./vendor/jsquash-webp/LICENSE).
- The underlying libwebp codec (WASM binary and its JS glue) is licensed under **BSD-3-Clause, Copyright (c) 2010 Google Inc.** — see [`vendor/jsquash-webp/codec/LICENSE.codec.md`](./vendor/jsquash-webp/codec/LICENSE.codec.md).

Both license files are included verbatim in the published npm package, as required by their respective terms (BSD-3-Clause in particular requires the copyright notice, condition list, and disclaimer to be reproduced in binary redistributions).

## Development

```bash
npm ci
npm test        # unit tests (lib.mjs / webp.mjs / marp.mjs / testdata.mjs / diff.mjs / cron.mjs), see test.mjs
node e2e.mjs     # stdio smoke test: spawns server.mjs, lists tools, calls a couple of handlers
```

CI runs both across Node 18.14.1 / 20 / 22, plus a vendor checksum check and a published-tarball content check — see [`.github/workflows/ci.yml`](./.github/workflows/ci.yml).

Release rule: a push to `main` automatically publishes a new package version through npm Trusted Publishing (OIDC) after every CI gate passes. No `NPM_TOKEN`, `npm login`, OTP, or manual approval is used. The workflow verifies npm propagation and creates the matching `vX.Y.Z` tag. Package changes must bump every synchronized version field; if a version already exists with different package contents, CI fails instead of overwriting it. CI-only changes with identical package contents safely skip publishing.

---

## 日本語

`@first-ch/tools-mcp` は、[First CH Tools](https://tools.first-ch.com)(無料Webツール集)の計算ロジック — WCAGコントラスト比・日本語文字数/Xウェイト計測・WebP変換・JSON-LD生成・llms.txt生成・文字コード/改行コード変換・Marp Markdown→スライド レンダリング・テストデータ生成・テキスト/コード差分・Cron式の解説・Base64/Data URI変換・URLパラメータの分解/編集/再構築・HTML特殊文字のエスケープ/エンティティのデコード・JSON⇄YAMLの相互変換・px⇄rem/emの単位換算とCSSの一括変換・カラーコードの相互変換とアルファ透過の合成・MD5/SHA-1/SHA-256/SHA-384/SHA-512のハッシュ生成と照合・JWTのデコードと有効期限/署名の検証・User-Agent文字列の解析とデバイス判定・UUID v4 / ULIDの一括生成・アスペクト比の計算とレスポンシブサイズの算出・camelCase / snake_case / kebab-case などの文字列ケース変換・CSV/TSV ⇄ JSON の相互変換 — をAIエージェント(Claude Code等)向けMCPツールとして提供するサーバーです。

### インストール

導入経路は3通りあります。使っているクライアントに合わせて選んでください。

**Node.js `>=18.14.1` が必要です**(以下いずれの方法も `npx` 経由でサーバーを起動するため。Claude Code本体はNode不要のnativeインストールもありますが、その場合でもNodeは別途必要です)。

#### 1. npm(Claude Code CLI)

```bash
claude mcp add firstch-tools -- npx -y @first-ch/tools-mcp
```

#### 2. Claude Code plugin(同じMCPサーバーを導入)

```
/plugin marketplace add First-CH/firstch-tools-mcp
/plugin install firstch-tools@first-ch
```

#### 3. その他のMCPクライアント(汎用JSON設定)

設定ファイル(`mcp.json` / `claude_desktop_config.json` 等)に以下を追加します。

```json
{
  "mcpServers": {
    "firstch-tools": {
      "command": "npx",
      "args": ["-y", "@first-ch/tools-mcp"]
    }
  }
}
```

本サーバーは [MCP Registry](https://registry.modelcontextprotocol.io) にも `io.github.First-CH/tools-mcp` として登録済みです([`server.json`](./server.json) 参照)。レジストリ対応クライアントはこの名前からも発見・導入できます。

### ツール一覧

| ツール | 何をするか | 主な入力 |
| --- | --- | --- |
| `contrast_check` | 文字色と背景色のWCAG 2.1コントラスト比を計算し、AA/AAA基準(通常テキスト・大テキスト・UI部品)の合否を返す | `fg`・`bg`(hex。例: `#333333` / `333` / `fff`) |
| `count_chars` | 日本語テキストを書記素単位で数え、全角/半角内訳・行数・X(Twitter)投稿ウェイト(全角=2・半角=1・URL=一律23・上限280)を返す | `text` |
| `webp_convert` | PNG/JPEG画像(絶対パス)をWebPへ変換する。tools.first-ch.com/webp/ と同一のlibwebp WASMエンコーダ(品質既定80)。出力先省略時は各入力と同じ場所に拡張子`.webp`で保存 | `paths[]`(絶対パス)・`quality?`(1-100)・`outputDir?` |
| `jsonld_generate` | schema.org準拠のJSON-LDを生成する(`organization` / `faqpage` / `service` / `breadcrumb`)。空項目は自動で省略。`json`オブジェクトと埋め込み用`<script>`スニペットの両方を返す | `type` と対応する `organization` / `faq` / `service` / `breadcrumb` オブジェクト |
| `llmstxt_generate` | AI検索・生成AI向けにサイト概要を伝える `llms.txt`(llmstxt.org提案フォーマット準拠)を生成する | `siteName`・`summary?`・`notes?`・`sections?` |
| `encoding_convert` | ファイル/テキストの文字コード(UTF-8 / Shift_JIS)・BOM有無・改行コード(CRLF / LF / CR)を判定し、UTF-8へ変換する。日本語CSVの文字化け調査、改行コードの統一に。**出力はUTF-8のみ**(Shift_JISへのエンコードは標準APIに無く変換表が必要なため非対応) | `base64` または `text`、`mode`(`analyze` \| `convert`)、`encoding`、`newline`、`bom` |
| `marp_render` | [Marp](https://marp.app) Markdown をスライドへレンダリングする。テーマCSSをインラインした自己完結HTML(ブラウザで開けて、印刷すると1スライド=1ページ)と、任意でPDFを書き出す。和文テーマ `firstch`(firstch-design トークン: 紙/墨/朱・IBM Plex Sans JP)を同梱し既定テーマにする。Markdown内の Marp フロントマター(`theme:` / `paginate:` / `size:` / `<!-- _class: lead -->`)はそのまま効く | `markdown` または `inputPath`、`theme?`、`formats?`(`html` \| `pdf`)、`outputPath?`、`title?` |
| `testdata_generate` | フォーム入力・CSV取り込みテスト用のダミーデータを生成する。`mode=records` は氏名・フリガナ・住所・郵便番号・メール・電話番号などをCSV/TSV/JSON/XLSXで返し、文字コード(UTF-8 / Shift_JIS)・BOM・改行コードを指定できる。`format=xlsx` は Excel ファイル本体を base64(または `outputPath` へ書き出し)で返す。ZIP+OOXMLを依存ライブラリなしで直接組み立てており、郵便番号・電話番号は文字列セルにするため先頭の0が消えない。`mode=text` は `maxlength` の境界値テスト用に n-1 / n / n+1 文字ちょうどの文字列を返す。生成データはすべて架空(メールは RFC 2606 の `example.com` 系)。`seed` を渡すと同じデータを再現できる | `mode?`・`rows?`・`fields?`・`format?`・`locale?`・`encoding?`・`newline?`・`bom?`・`header?`・`seed?`・`preset?`・`length?`・`outputPath?` |
| `diff_check` | 2つのテキスト(またはファイル)を比較し、追加/削除/変更の行数と unified diff(`.patch`)を返す。tools.first-ch.com/diff/ と同一ロジック。行の対応づけは patience diff(両方に1回だけ現れる行をアンカーに分割)+アンカーの取れない範囲だけ Myers。`format=blocks` / `both` では、変更行のペアを語単位(英数字はひとかたまり・和文は1文字ずつ)でも比較し `changed_parts` として返すため「どの語が変わったか」まで取れる。CRLF/CR/LF は同じ行区切りとして扱い、行末の空白だけの差は既定で無視する | `a`/`b`(テキスト)または `pathA`/`pathB`(絶対パス)、`format?`(`unified` \| `blocks` \| `both`)、`context?`、`ignoreWhitespace?`、`ignoreCase?`、`words?` |
| `cron_explain` | Cron式を人間向けの文へ読み下し、次回からの発火日時を返す。tools.first-ch.com/cron/ と同一ロジック。解釈は Vixie cron(`crontab(5)`)準拠で、範囲・ステップ・列挙・`JAN-DEC` / `SUN-SAT` の名前・`@daily` 等の省略記法に対応し、6フィールドのときは先頭を秒として扱う(node-cron / Spring 形式)。誤りやすい点は `warnings` で知らせる(「日」と「曜日」の両方指定は AND ではなく OR、範囲を割り切らない `*/n` は等間隔にならない、2月30日のような存在しない日付は発火しない)。発火日時はタイムゾーンの壁時計上で求めてから実時刻へ戻すため、夏時間のある地域でもずれない | `expression`、`timeZone?`(IANA名・既定 `UTC`)、`count?`(既定5・最大100)、`from?`(ISO 8601) |
| `base64_encode` | テキストやファイルを Base64・`data:` URI へ変換し、そのまま貼れる HTML `<img>` / CSS `background-image` のスニペットも返す。tools.first-ch.com/base64/ と同一ロジック。`mode="decode"` では Base64 や data URI を元のバイト列へ戻し、`outputPath` を渡せばファイルとして書き出す。SVGは両方のエンコードを返して短い方を既定にし(base64は必ず約1.33倍になるためパーセントエンコードの方が小さい)、`& " < > # %` と空白・非ASCIIを必ずエスケープするのでHTML属性にもCSSの `url("…")` にもそのまま貼れる。デコードは標準/URLセーフのどちらでも、空白・改行混じりでも、パディングが欠けていても読み取り、data URI が名乗るMIMEタイプより実際のマジックナンバー(PNG/JPEG/GIF/WebP/ico/PDF/zip/woff/woff2/SVG)から判定した種類を優先する | `mode?`(既定 `encode` / `decode`)、`text?` または `path?`、`base64?`、`outputPath?`、`urlSafe?`、`wrap?`(MIMEは76)、`dataUri?`、`mimeType?`、`snippets?` |
| `url_params` | URLのクエリ文字列をキーと値へ分解し、編集(`set` / `remove` / `utm` / `removeTracking` / `sort`)して再構築する。tools.first-ch.com/url/ と同一ロジック。値はデコードして返し(`%XX` を元の文字へ、`+` を半角スペースへ)、触っていないパラメータは生の文字列のまま書き戻すため、`url` だけを渡した場合の出力は入力と1バイトも変わらない(署名付きURLを通しても壊れない)。`reencode` を立てると全体を `encodeURIComponent` の規則へ正規化する。`warnings` では重複キー・未エンコードのスペースや非ASCII・壊れた `%XX`・`+` のスペース解釈・UTM値の大文字混在・utm_source/utm_medium の片落ち・URL内のパスワード・トークンらしきキー・2000文字超を知らせる。相対パスや壊れたパーセントエンコードでも例外にせず読める範囲まで分解する。`mode="encode"` / `"decode"` では文字列単体を変換する(`scheme`: `component` / `uri` / `form`)。URLへのアクセスは行わない | `url?`、`mode?`(既定 `parse` / `encode` / `decode`)、`text?`、`scheme?`、`set?`、`remove?`、`utm?`、`removeTracking?`、`sort?`、`reencode?`、`spaceAsPlus?` |
| `html_escape` | テキスト中の `< > & " '` をHTMLエンティティへ変換し、`mode="unescape"` では `&amp;` `&#39;` `&#x3042;` などの文字参照を元の文字へ戻す。tools.first-ch.com/html-escape/ と同一ロジック。エスケープは `&` を最初に処理するため、1回目の出力をもう一度通しても二重エスケープにならない。名前付きと数値文字参照の切り替え(`numeric`)、引用符を変換するか(`quotes`・属性値へ入れるなら必須)、`'` を `&#39;` と `&apos;` のどちらで書くか(`apos`・`&apos;` はHTML 4.01に無い)、非ASCII文字を参照にするか(`nonAscii`・文字コードが伝わらない経路への保険)を選べる。デコードはHTML 4.01の名前付き文字参照252個すべてと10進/16進に対応し、`&#128;` のようなC1領域の参照は仕様どおり Windows-1252 の文字へ読み替え、知らない名前・範囲外の数値・セミコロン無しの参照は推測で変換せずそのまま残す。`notes` ではすでにエスケープ済みの入力・裸の `&`・セミコロンの閉じ忘れ・知らないエンティティ名・ノーブレークスペース(U+00A0)の混入を知らせる | `mode?`(既定 `escape` / `unescape`)、`text?` または `path?`、`outputPath?`、`quotes?`、`apos?`、`numeric?`、`nonAscii?`(`none` / `named` / `decimal` / `hex`) |
| `json_to_yaml` | JSONをYAMLへ変換して整形する(逆向きは `yaml_to_json`)。tools.first-ch.com/json-yaml/ と同一ロジックで、YAMLの解析・生成もこのパッケージ内に実装しており外部のYAMLライブラリには依存しない。インデント幅・引用符の付け方(`quote`)・nullの書き方(`nullStyle`)・複数行文字列をブロックスカラー `|` で書くか(`block`)・キーを名前順に並べるか(`sortKeys`)・先頭に `---` を付けるか(`docStart`)を選べる。別の型に読まれうる文字列(`yes` / `no` / `on` / `off`・`0755`・`12:30`・`2026-08-12`・数値に見える文字列)、前後に空白がある文字列、`-` / `*` / `#` で始まる文字列は自動で引用符を付けるので、YAML 1.1 のパーサ(PyYAMLなど)に渡しても意味が変わらない。コメント・末尾カンマ・シングルクォート・引用符なしのキーを含むJSON(`tsconfig.json` など)も読み取って変換し、JSONとしては不正であることを `notes` で知らせる(`relaxed=false` で厳密に拒否できる) | `text?` または `path?`、`outputPath?`、`indent?`(1-8)、`quote?`(`auto` / `single` / `double`)、`nullStyle?`(`null` / `tilde` / `empty`)、`block?`、`sortKeys?`、`docStart?`、`relaxed?` |
| `yaml_to_json` | YAMLをJSONへ変換する。docker-compose・GitHub Actions・Kubernetes・CIの設定をプログラムから扱える形へ読み替えるときや、構文が通るかを確かめるときに使う。構文エラーは「何行何桁・原因・直し方」と前後2行の抜き出し(桁を指す `^` つき)をエラーとして返す。複数ドキュメント(`---`)はJSONの配列1つにまとめ、アンカー `&名前`・エイリアス `*名前`・マージキー `<<` はJSONに参照の仕組みが無いため展開する(展開したことは `notes` で知らせる)。スカラーの解釈は YAML 1.2 core schema なので `yes` / `no` / `on` / `off` / `NO` は文字列のままだが、`notes` でそれを知らせる。`0755`(8進数ではなく10進の755になる)・`12:30`(YAML 1.1では60進数の750)・日付に見える値・キーの重複・2の53乗を超える整数も同様。ブロックマップ/シーケンス・フロー表記・引用スカラー(複数行・エスケープ)・ブロックスカラー(`|` `>` と chomping・明示インデント)・タグ(`!!str` `!!int` `!!float` `!!bool` `!!null` `!!binary`)・コメントに対応し、未対応は「`? キー`」の明示キー記法のみ | `text?` または `path?`、`outputPath?`、`indent?`(0-8 または `"tab"`。0で1行)、`sortKeys?`、`ascii?` |
| `px_rem_convert` | CSSの長さの単位を px ⇄ rem / em / pt で換算する。tools.first-ch.com/px-rem/ と同一ロジック。`value` を渡すと1つの値の換算になり、px・rem・em・pt の値、そのまま貼れる `font-size` の1行、よく使うフォントサイズのスケール表(12〜64pxの15段・用途の目安つき)を返す。`css` / `path` を渡すとCSS全体の一括変換になる(`direction`: 既定 `px2rem` / `px2em` / `rem2px` / `em2px`)。一括変換ではコメント・文字列(`content: "10px"`)・`url()` の中身と、`--size-16px` のように識別子の一部になっている数字は書き換えない。既定は `minPx=2` で1pxの罫線を残し(remにすると環境によって太さがばらつくため)、ブレークポイントが書かれる `@media` などアットルールの条件も変換しない(`skipMedia`)。`ignoreProps` は前方一致でプロパティを除外し、`zeroUnitless` は 0 を単位なしで書き出す。ルートは `root="62.5%"` のようにパーセントでも指定でき(ブラウザ既定16pxに対する割合として10pxと読む)、丸めが起きたこと・変換しなかった箇所・62.5%テクニックのアクセシビリティ上の副作用は `notes` で返る | `value?`(`24` / `"1.5rem"`)または `css?` / `path?`、`unit?`、`outputPath?`、`direction?`、`root?`、`parent?`、`precision?`(`auto` または 0-6)、`minPx?`、`zeroUnitless?`、`skipMedia?`、`ignoreProps?`、`scale?` |
| `color_convert` | 色のコードを HEX / RGB / HSL / OKLCH で相互変換し、アルファ付きの `rgba()` / `hsla()` / 8桁HEX を作る。tools.first-ch.com/color/ と同一ロジック。入力は HEX(3/4/6/8桁)・`rgb()`・`hsl()`・`hwb()`・`oklch()`・`oklab()`・CSSの名前付き色(148色)・`transparent` に対応し、旧記法のカンマ区切りと新記法のスラッシュ区切り、角度の単位(`deg` / `grad` / `rad` / `turn`)も読む。`background` を渡すと、透過色をその背景の上に重ねたときに実際に見える色(`前景×α + 背景×(1−α)`)を `flattened` で返すので、デザインカンプの半透明レイヤーを実装で不透明なHEXへ置き換えるときに使える。あわせて白・黒とのWCAG 2.1コントラスト比、`step`%刻みの `alpha_table`、色相と彩度を保ったまま明度だけを50〜950の11段に振った `palette` を返す。sRGBの範囲外のOKLCHは、RGBを切り詰めると色相までずれるため、明度と色相を保ったまま彩度だけを二分探索で下げて収める(収めたこと・名前付き色と完全一致したこと・無彩色であることは `notes` で知らせる) | `color`、`alpha?`(0〜1・0〜100・`"50%"`)、`background?`(既定 `#ffffff`)、`syntax?`(既定 `modern` / `legacy`)、`uppercase?`、`alphaPercent?`、`step?`(1〜50・既定10)、`alphaTable?`、`palette?` |
| `hash_generate` | テキストまたはファイルの MD5・SHA-1・SHA-256・SHA-384・SHA-512 ハッシュ値を一度に算出する。tools.first-ch.com/hash/ と同一の仕様。`expected` に期待値を渡すと桁数から対象のアルゴリズムを判定して照合し、一致したかどうかを `verification` で返す。`expected` はコマンドの出力をそのまま渡せる(`sha256sum` の `<ハッシュ値>␣␣<ファイル名>`・`shasum -a 256` の出力・`SHA256 (file) = …`・`sha256:` のような接頭辞・コロン区切りの16進・Base64 / base64url)。出力は既定が16進の小文字で、`HEX` / `base64` / `base64url` も選べる。`text` では `newline`(`lf` / `crlf`)と `bom` で実際にハッシュへ渡すバイト列を決められるので、Windowsで作られたファイルの値も再現できる(文字コードはUTF-8固定)。`path` にはどちらも影響せず中身をそのまま読み、大きなファイルは1パスのストリームで処理する。MD5とSHA-1が衝突耐性を破られていること・ハッシュ値をそのままパスワード保存に使ってはいけないことは `notes` で知らせる | `text?` または `path?`、`algorithms?`(既定 `["md5","sha1","sha256","sha512"]`)、`format?`(既定 `hex` / `HEX` / `base64` / `base64url`)、`newline?`、`bom?`、`expected?` |
| `jwt_decode` | JWT(JSON Web Token)をデコードしてヘッダーとペイロードを返し、有効期限を判定する。tools.first-ch.com/jwt/ と同一の仕様。`token` は `Authorization: Bearer <token>` の1行のまま渡せる(ヘッダー名・`Bearer`・前後の引用符・末尾のカンマ・途中の改行は取り除く)。`expiry` では状態(`valid` / `expired` / `not_yet` / `no_exp`)・`exp` / `nbf` / `iat` のISO日時・残り秒数・有効期間のどこまで来ているかを返す。`clockTolerance` で時計のズレを許容でき、`now` に判定時刻を渡せば結果が再現できる。`key` を渡すと Web Crypto で署名も検証する: HS256/384/512 は共有鍵の文字列(`keyEncoding` で `base64url` / `hex` も可)、RS・PS・ES・EdDSA は SPKI形式のPEM公開鍵かJWK / JWKSのJSON(JWKSはヘッダーの `kid` で選ぶ)。秘密鍵・証明書・PKCS#1 を渡した場合は検証せず、変換コマンドを案内する。`warnings` は `alg: none`・署名なし・期限切れ・まだ有効前・`exp` がミリ秒(`Date.now()` の1000割り忘れ)・アクセストークンとして長すぎる有効期間・ペイロードに残った秘密情報や個人情報(base64url は暗号化ではないので、トークンを持つ人は誰でも読める)を指摘する。5セグメントのJWEはヘッダーのみ返し(復号はしない)、ネットワークへは一切出ない(JWKSは呼び出し側が渡す) | `token`、`key?`、`keyEncoding?`(既定 `utf8` / `base64url` / `base64` / `hex`)、`clockTolerance?`(秒)、`now?`(UNIX秒) |
| `user_agent_parse` | User-Agent文字列から、ブラウザ名とバージョン・レンダリングエンジン・OSとそのバージョン・デバイス種別(`desktop` / `mobile` / `tablet` / `tv` / `console` / `wearable` / `bot`)・メーカーと機種・CPUアーキテクチャを判定する。tools.first-ch.com/user-agent/ と同一の仕様。`ua` はアクセスログの行のまま渡せる(先頭の `User-Agent:`・前後の引用符・末尾のカンマは取り除く)。判定は「より限定的なトークンから先に試す」順序で行う(`Edg/` → `OPR/` → `Chrome/` → `Safari/`)。Chrome・Edge・Opera はいずれも `Chrome/` と `Safari/` を名乗るため、部分一致では必ず取り違えるからである。`tokens` ではUA文字列をトークン単位に分解し、`Mozilla/5.0`・`KHTML, like Gecko`・`Mobile/15E148` といった化石の意味を返す。`notes` では「UAでは分からないこと」を知らせる: Chrome 110以降の削減済みUA(マイナー版は `0.0.0`、Androidの機種名は `K` に凍結)・macOSが常に `10_15_7` を名乗ること・Windows 10と11が区別できないこと・iPadがMac版Safariと同じUAを送ること・アプリ内ブラウザ(LINE / Instagram / Facebook / Android WebView)・そしてUAは自己申告なのでアクセス制御の根拠にしてはいけないこと。Googlebot・bingbot・GPTBot・ClaudeBot・PerplexityBot などのクローラーや curl・python-requests・axios のHTTPクライアントも判定する。`uas` に配列を渡すと複数件をまとめて解析し、ブラウザ・OS・デバイス種別の内訳を `summary` で返す | `ua?` または `uas?`(配列)、`includeTokens?`(既定: 20件以下なら含める) |
| `uuid_generate` | UUID v4 または ULID を1〜100件まとめて生成する。tools.first-ch.com/uuid/ と同一の仕様。UUID v4 は128bitのうち122bitが乱数(残る6bitがバージョンとバリアント)、ULID は26文字で先頭10文字が生成時刻(UNIX時間のミリ秒・48bit)・後ろ16文字が乱数(80bit)で、表記は Crockford's Base32(紛らわしい I・L・O・U を除く)。ULIDは時刻が先頭にあるため文字列の辞書順が生成順と一致し、`timestamp`(ISO8601 / UNIX秒 / UNIXミリ秒)で時刻を固定すれば出力を再現できる。同一ミリ秒内に複数生成するときは ULID 仕様の単調増加(monotonic)に従って乱数部を+1するので、100件を一度に作っても順序が崩れない。乱数は `node:crypto` の `randomBytes`(CSPRNG)で、`Math.random` は使わない。`format` で `text` の形(1行1件 / JSON配列 / カンマ区切り / SQLの `INSERT` 向けの引用符付き)、`uppercase` / `hyphens` / `braces` で表記(UUIDの既定は RFC 9562 に従い小文字・ULIDの既定は大文字。`hyphens: false` はMySQLの `BINARY(16)` 向けの32文字、`braces: true` はWindowsのGUID表記)を選べる | `type?`(`uuid` \| `ulid`)、`count?`(1〜100)、`format?`、`uppercase?`、`hyphens?`、`braces?`、`timestamp?`(ULIDのみ) |
| `aspect_ratio_calc` | アスペクト比と寸法を相互に計算する。tools.first-ch.com/aspect-ratio/ と同一の仕様。`ratio`(`16:9` / `16/9` / `16x9` / `1.85` のような小数)と `width` か `height` の片方を渡すと、もう一方の寸法に加えて約分した比率・小数・`padding-top` の百分率・`aspect-ratio` の1行を返す。`ratio` を省いて `width` と `height` を渡すと逆向きに働き、最大公約数で約分した比率・向き・画素数・いちばん近い定番比率(16:9・4:3・3:2・1:1・4:5・9:16・21:9・1.85:1・2.39:1・OGPの1.91:1・黄金比・A判用紙)とのずれ(%)を返す。1920×1080 のように名前のある寸法はその名前も返す。`round` で 四捨五入・偶数・切り捨て・切り上げ を選べ、丸めた場合は丸め後の実際の比率とそのずれを `notes` で知らせる(H.264 / H.265 は色情報を縦横半分の解像度で持つ=YUV 4:2:0 ため、動画には `round: "even"` が要る)。`box` を渡すとその枠へ `contain` / `cover` ではめ込んだときの描画サイズ・拡大率・上下左右の余白(レターボックス / ピラーボックス)・切り取られる量・見える割合を返す。`widths`(または `table: true`)でブレークポイントごとの高さの早見表を、`snippet: true` でCSS(`@supports` で囲んだ `padding-top` のフォールバックも任意で付く)と、CLSを防ぐ `width` / `height` 属性入りのHTMLを返す | `ratio?` および `width?` / `height?`、`round?`(既定 `none` / `round` / `floor` / `ceil` / `even`)、`widths?`、`table?`、`box?`(`"1280x400"`)、`fit?`(既定 `cover` / `contain`)、`snippet?`、`selector?`、`target?`(`img` / `video` / `iframe` / `background`)、`objectFit?`、`fallback?` |
| `markdown_table` | ExcelやスプレッドシートからコピーしたTSV/CSVをMarkdownの表へ整形し、逆にMarkdownの表をCSV/TSV/HTML/JSONへ書き出す。tools.first-ch.com/markdown-table/ と同一の仕様。表計算ソフトで範囲をコピーしたときのクリップボードは**カンマ区切りではなくタブ区切り**なので、タブ・カンマ・セミコロンのそれぞれで実際に読んでみて「列数がいちばん揃うもの」を選ぶ方式で自動判定する(金額の桁区切り 1,200 で列がずれない)。CSVはRFC 4180の引用符(セル内のカンマ・改行・二重引用符)に対応。桁揃えでは全角文字をUnicodeのEast Asian Widthに従って2桁として数えるので、等幅フォントで縦線が揃う。空でないセルがすべて数値(桁区切り・小数点・通貨記号・%・単位を含む)の列は自動で右寄せ(`---:`)にし、`aligns` で列ごとに上書きできる。セル内の `|` は `\|` へ、セル内の改行は `<br>`(または半角スペース)へ置き換え、列数が足りない行には空セルを補い、直した内容は黙って処理せず `notes` で返す。Markdownを読むときは区切り行のコロンから配置を読み取り、`<br>` は改行へ戻すので、CSVへ書き出せば表計算ソフトへそのまま取り込める | `text` か `path`、`outputPath?`、`from?`(既定 `auto` / `tsv` / `csv` / `ssv` / `markdown`)、`to?`(既定 `markdown` / `csv` / `tsv` / `ssv` / `html` / `json`)、`header?`(既定 `first` / `auto` / `none`)、`align?`、`aligns?`、`pad?`、`eastAsian?`、`autoNumber?`、`trim?`、`skipEmpty?`、`transpose?`、`multiline?`(既定 `br` / `space`)、`eol?` |
| `sql_format` | 1行に固まったSQLを、予約語の大文字化・句ごとの改行・字下げの付いたクエリへ整形する。tools.first-ch.com/sql-format/ と同一の仕様。`SELECT` / `FROM` / `WHERE` / `GROUP BY` / `HAVING` / `ORDER BY` / `LIMIT` / `INSERT INTO` / `VALUES` / `UPDATE` / `SET` / `DELETE FROM` / `WITH` / `UNION` などの句を行頭へ、その中身を1段下げて並べ直す。JOIN は行頭に置いて `ON` を1段下げ、`AND` / `OR` は条件ごとに改行する(`BETWEEN a AND b` の `AND` は条件の区切りではないので改行しない)。`(` の直後が `SELECT` / `WITH` / `VALUES` のときだけサブクエリとみなして改行+字下げし、関数呼び出し(`SUM(…)` / `IN (1, 2, 3)` / `OVER (PARTITION BY … ORDER BY …)`)は1行のまま保つ。`CASE` 式は `WHEN` / `ELSE` / `END` を縦に並べる。予約語と型名だけを大文字(または小文字)へ揃え、**テーブル名・列名・別名の綴りは変えない**(識別子の折りたたみ方がDBごとに違うため)。文字列・引用符付き識別子(`"…"` / `` `…` `` / `[…]`)・コメント・プレースホルダ(`?` / `:name` / `$1` / `@var`)はそのまま残す。WHEREの無いUPDATE/DELETE・閉じていない括弧や引用符・`SELECT *`・暗黙の結合・プレースホルダの有無は `notes` で返す。`compact` を立てると逆に1行へ畳む。特定DBのパーサーではなく字句ベースの整形器なので、MySQL・PostgreSQL・SQL Server・SQLite・Oracle の方言も壊さずに通す(構文エラーの検出はしない) | `text` か `path`、`outputPath?`、`keywordCase?`(既定 `upper` / `lower` / `preserve`)、`functionCase?`、`indent?`(`2` / 既定 `4` / `8` / `tab`)、`commaStyle?`(既定 `trailing` / `leading`)、`logicStyle?`(既定 `leading` / `trailing`)、`breakColumns?`、`breakLogic?`、`breakOn?`、`breakCase?`、`breakSubquery?`、`expandClauses?`、`compact?`、`eol?` |
| `qr_generate` | URL・テキストからQRコードを作り、SVG(ベクター)・PNG・端末に貼れる文字の図で返す。tools.first-ch.com/qr/ と同一の仕様。符号化(数字 / 英数字 / UTF-8のバイトモードを入力に応じて自動選択)・リード・ソロモン符号による誤り訂正・位置検出/タイミング/位置合わせパターンと型番情報の配置・8種類のマスクからの自動選択まで自前で実装しており(JIS X 0510 / ISO 18004・型番1〜40)、外部のAPIも画像生成サーバーも使わない。`ecLevel` は容量と汚れへの強さの取引(L 約7% / M 約15% / Q 約25% / H 約30%。印刷物はQ以上を推奨)、`size` は出力の一辺(px)、`margin` は余白=クワイエットゾーンのモジュール数(規格の推奨は4。0にすると背景の柄によっては読めなくなる)。Wi-Fi(`WIFI:T:WPA;S:…;P:…;;`)・`mailto:`・`tel:`・`SMSTO:`・`geo:` はその文字列をそのまま `text` に渡す。`outputPath` を渡すとファイルへ書き出す(本文は返さない)。`format="png"` で省いた場合は data URI で返る。容量の上限は数字7089桁・英数字4296文字・バイト2953文字(いずれもレベルL) | `text`、`ecLevel?`(`L` / 既定 `M` / `Q` / `H`)、`size?`(64〜4096・既定 320)、`margin?`(0〜32・既定 4)、`format?`(既定 `svg` / `png` / `text`)、`mode?`(既定 `auto` / `numeric` / `alnum` / `byte`)、`mask?`(0〜7)、`minVersion?`、`outputPath?` |
| `unixtime_convert` | UNIX秒・ミリ秒・マイクロ秒・ナノ秒と、ISO 8601や日時文字列を相互に変換する。tools.first-ch.com/unixtime/ と同一の仕様。数値の単位は桁数から自動判定し(10桁までを秒 / 13桁までをミリ秒 / 16桁までをマイクロ秒 / それ以上をナノ秒)、`unit` を渡せばその単位で読む。ミリ秒より下の桁は切り捨て、その旨を `notes` で返す。日時側は `2026-08-24T09:30:00Z`(ISO 8601)・`2026-08-24 18:30`・`2026/8/24 9:05`・`2026年8月24日 18時30分`・`Sun, 24 Aug 2026 03:00:00 GMT`(HTTP-date)・`now` を受け付け、CSVやJSONから貼ったときの引用符・角括弧・行末カンマは自動で外す。`input` は1行1件で最大500行までまとめて変換し、行ごとにUNIX秒・ミリ秒・ISO 8601(UTC)・`timeZone` の現地時刻(曜日・UTCオフセット付き)・相対表示(○分前 / ○日後)を返す。読めなかった行は他の行を巻き込まずその行だけ `error` になる。**オフセットを持たない日時は `timeZone` の壁時計として解釈**し、必ず `notes` で明示する(入力に `+09:00` や `Z` があればそちらが優先)。夏時間で存在しない時刻は切り替え後へ繰り上げ、これも `notes` に出す。8桁の数字(`20260824`)はUNIX秒として読むため、日付のつもりの入力には注意を返す。負の値(1970年より前)と小数点付きの秒にも対応 | `input`、`timeZone?`(IANA名・既定 `UTC`)、`unit?`(既定 `auto` / `s` / `ms` / `us` / `ns`)、`now?`(UNIXミリ秒か日時文字列・既定は実行時刻)、`lang?`(既定 `ja` / `en`) |
| `robotstxt_generate` | クロールの許可/禁止ルール・サイトマップ宣言・AIクローラーの許可プリセットから robots.txt を組み立てる。tools.first-ch.com/robots-txt/ と同一の仕様。`ai.preset` で24種のAIクローラーの扱いをまとめて決められる: `training`(既定=GPTBot・ClaudeBot・Google-Extended・Bytespider・CCBot など**学習用データを集めるクローラーだけを拒否**し、OAI-SearchBot・Claude-SearchBot・PerplexityBot・ChatGPT-User などAI検索と都度取得は許可)/ `allow` / `block` / `none`(AIクローラーを書かない)/ `custom`(`ai.overrides` で1件ずつ指定)。**クローラーは自分に一致するグループを1つだけ読む**ため、GPTBot 用のグループを作った時点で `User-agent: *` の禁止ルールはそのクローラーへ届かなくなる。既定では共通の禁止パスをAI側のグループへ書き写してこの取りこぼしを防ぐ(`ai.inherit: false` で止められる)。パスは先頭の `/` を補い、絶対URLからはパス部分だけを取り出す。`warnings` では空白を含むパス・パーセントエンコードしていない日本語のパス・対応していないクローラーのあるワイルドカード・Googlebotが読まない `Crawl-delay`・サイトマップの宣言漏れ・同じUser-agentの重複・サイト全体の拒否を指摘する。`listCrawlers: true` を渡すと生成せずAIクローラーの一覧(名前・提供元・目的)だけを返す | `siteUrl?`、`disallow?`、`allow?`、`userAgents?`(既定 `*`)、`crawlDelay?`、`blockCrawlers?`、`sitemaps?`、`ai?`(`preset?` / `overrides?` / `inherit?`)、`groups?`、`comments?`(既定 true)、`allowStyle?`(既定 `disallow-empty` / `allow-slash`)、`listCrawlers?`、`lang?`(既定 `ja` / `en`)、`outputPath?` |
| `case_convert` | 識別子を camelCase / PascalCase / snake_case / CONSTANT_CASE / kebab-case・Train-Case・dot.case・Title Case・Sentence case・lower case・UPPER CASE の11形式へ相互変換する。tools.first-ch.com/case/ と同一ロジック。変数名の一括リネーム、`snake_case` のDB列名と `camelCase` のJSONキーの読み替え、CSVヘッダーの整形といった、層の境目で発生する定型処理のための道具。`scope` で処理の単位を選ぶ: `lines`(既定=1行1件)/ `items`(行内のカンマ・タブ区切りで1件ずつ=CSVヘッダー)/ `whole`(全体で1件)。**前後の空白・インデント・改行コード(LF/CRLF)・区切り記号はそのまま残す**ので、名前だけが置き換わる。大文字の連なりは直後に「大文字+小文字」が続くときだけ手前で切り(`XMLHttpRequest` → `XML` / `Http` / `Request`)、数字は既定で直前の語へ付く(`sha256Hash` → `sha256` / `Hash`。`splitDigits` で分けられる)。頭字語は既定で `parseXmlData` へ畳み、`keepAcronyms` で `parseXMLData` のまま残す(camelCase の先頭語はどちらでも小文字=`URLParser` → `urlParser`)。大文字小文字を持たない文字(日本語など)はそのまま出力し、ローマ字化はしない。`notes` では**変換後に同じ名前へ衝突する項目**(`first name` と `first_name` はどちらも `first_name`=インポート時に片方が黙って消える事故の元)・数字で始まって識別子にできない結果・頭字語を含む項目を指摘し、`items[].detected` では各入力がどの形式で書かれていたかを返す。`allFormats` で各項目を11形式すべてへ展開、`listFormats` で形式の一覧と使いどころだけを返す | `text?` または `path?`、`outputPath?`、`format?`(既定 `camel`)、`scope?`(既定 `lines` / `items` / `whole`)、`splitDigits?`、`keepAcronyms?`、`allFormats?`、`listFormats?`、`lang?`(既定 `ja` / `en`) |
| `csv_convert` | CSV/TSVとJSON配列を相互変換する。tools.first-ch.com/csv-json/ と同一ロジック。表計算ソフトから書き出した表をAPIに渡せるJSONにする、逆にAPIの応答をExcelで開ける表にする、という**手持ちのデータを形式だけ移し替える**ための道具。`direction` を省くと、入力の先頭が `[` か `{` なら `json2csv`、そうでなければ `csv2json` と推定する。区切り文字は先頭20行で「どの記号が各行に同じ個数だけ現れるか」を見て自動判定し、`delimiter` で固定もできる。**`stock.qty` のような列名は入れ子のオブジェクトとして読み書きし、`tags.0` / `tags.1`(`tags[0]` も同義)は配列になる**。逆向きも同じ規則で平らにするので、CSVへ出してJSONへ戻すと元の構造に復元される(`nest: false` なら入れ子はJSONの文字列としてセル1つに入る)。**型の読み替えは「一度数値にして文字列へ戻したとき元の表記と1文字も違わないか」で決める**ため、`0123`・`+1`・`1.50`・2の53乗を超えるID(`12345678901234567890`)は文字列のまま残り、郵便番号・電話番号・伝票番号・バージョン番号が壊れない。読み取りは RFC 4180 準拠で、引用符・`""` エスケープ・セル内改行・CRLF・BOM に対応する(書き出し側は Excel 向けに `bom` と `newline` を選べる)。JSONは配列・単体のオブジェクト・`{"data":[…]}` のように配列を包んだもの・1行1件のJSON Lines を受け取れる。**JSONの構文エラーは例外にせず、行・桁と前後の抜粋を持つ `ok: false` の結果として返す**ので、応答そのものが直しどころになる。`notes` では、列数が見出しと合わない行・重複した列名(`name_2` として残す)・空の見出し(`column4` を割り当てる)・桁落ちする数値・`=` `+` `-` `@` で始まり表計算ソフトが数式として実行しうるセル(CSVインジェクション)・キーの並びが揃わないオブジェクトを指摘する | `text?` または `path?`、`outputPath?`、`direction?`(既定 `auto` / `csv2json` / `json2csv`)、`delimiter?`(既定 `auto` / `comma` / `tab` / `semicolon` / `pipe`)、`header?`、`nest?`、`types?`、`trim?`、`emptyNull?`、`indent?`(既定 `2` / 0〜8 / `tab`)、`newline?`(既定 `lf` / `crlf`)、`quoteAll?`、`bom?`、`lang?`(既定 `ja` / `en`) |
| `zenkaku_convert` | 英数字・カタカナ・記号・スペースを**文字種ごとに向きを選んで**全角⇄半角へ変換し、あわせて空白まわりを掃除する。tools.first-ch.com/zenkaku/ と同一ロジック。顧客支給原稿の表記統一、CSV取り込み前の正規化、フォーム入力値の掃除のための道具。`alnum` / `kana` / `symbol` / `space` にそれぞれ `keep`(そのまま)/ `han`(半角へ)/ `zen`(全角へ)を渡す。既定は日本語の原稿で最も多い「英数字・記号・スペースは半角、カタカナは全角」(`preset: 'ja'` と同じ)。`preset` は `ja` / `csv`(`ja` に空白の掃除と見えない文字の除去を足したもの)/ `han` / `zen` で、個別指定が優先される。**半角カタカナは1文字ではない**ため、半角→全角では次が濁点なら1文字へ合成し(`ガ`→`ガ`・`パ`→`パ`・`ヴ`→`ヴ`)、全角→半角では2文字へ分解する。句読点・カギ括弧・中黒・長音(`。 、 「 」 ・ ー`)は半角カタカナのブロック(U+FF61〜U+FF9F)に同居しているので**カタカナと同じ文字種**として扱う(`kana: 'han'` は `「テスト」。` を `「テスト」。` にする。句読点を全角で残すなら `keep`)。スペースは U+3000 ⇄ U+0020 に加え、NBSP や欧文の各種スペースも半角側へ寄せる。掃除は `collapseSpaces`(連続する空白を先頭の1文字へ)・`trimLines`・`blankLines`(`keep` / `collapse` / `remove`)・`removeInvisible`(ゼロ幅・BOM・制御文字)・`composeMarks`(NFC。`ガ` が `カ`+結合文字 U+3099 で書かれた原稿向け)。**改行コード(LF/CRLF)とタブ、ひらがな・漢字は書き換えない。** `notes` では出力に残った半角カナ・全角スペース・見えない文字・結合文字の濁点に加え、波ダッシュ `〜`(U+301C)/全角チルダ `~`(U+FF5E)と環境依存文字(`① ㈱ Ⅲ`)を指摘する(後の2つはどちらへ寄せるべきかが届け先で変わるため**指摘のみで自動変換しない**)。**NFKC正規化とは別物**で、`①` が `1` になることはなく、選んだ文字種しか触らない。`inspect: true` なら変換せず点検結果だけを返す | `text?` または `path?`、`outputPath?`、`preset?`(`ja` / `csv` / `han` / `zen`)、`alnum?`、`kana?`、`symbol?`、`space?`(いずれも `keep` / `han` / `zen`)、`collapseSpaces?`、`trimLines?`、`blankLines?`(既定 `keep` / `collapse` / `remove`)、`removeInvisible?`、`composeMarks?`、`inspect?`、`lang?`(既定 `ja` / `en`) |
| `cidr_calc` | CIDR表記・サブネットマスク・ACLのワイルドカードマスク・単体のIPから、ネットワークアドレス・ブロードキャスト・使えるホストの範囲・アドレス数とホスト数・逆引きゾーン・2進表記を返す。tools.first-ch.com/cidr/ と同一ロジック。サーバー設定・ファイアウォール・Basic認証の許可リストを書くときのサブネット計算のための道具。**IPv4とIPv6の両対応**で、`::` の省略・末尾のIPv4表記(`::ffff:192.168.0.1`)・`[...]` 囲み・`%zone` を読み、出力は RFC 5952 に従う。`192.168.1.5 255.255.255.0` や `192.168.1.5/0.0.0.255`(ACLの書き方)も同じ入力欄で受け取る。**ホスト数の例外も織り込む**: `/31` は点対点リンクとして2アドレスとも使え(RFC 3021)、`/32` は1台、IPv6にブロードキャストは無い。渡したアドレスのホスト部にビットが立っていれば、`notes` で**設定ファイルに書くべき本来のCIDR**を返す。**先頭に0の付いたオクテット(`192.168.001.1`)は受け取らない**(8進数として読む実装があり、同じ表記が別のアドレスになるため)。アドレス帯は IANA の主要な予約ブロック(プライベート・CGNAT・ループバック・リンクローカル・文書用・マルチキャスト・ULA ほか)と照合して種別を返し、ネットワークがその帯からはみ出す場合も指摘する。`split` はそのプレフィックス長で均等に割った一覧、`contains` は各アドレスがそのネットワークに入るかの判定、`range` は `10.0.0.5 - 10.0.0.200` のような範囲(CIDR・単体IPと混ぜて1行1件か `;` 区切り)を**重なりと隣接をまとめたうえで過不足なく覆う最小個数のCIDR**へ逆算する。計算はすべてBigInt | `cidr?` と/または `range?`、`split?`(プレフィックス長)、`contains?`(文字列の配列)、`limit?`(1〜4096・既定 256)、`lang?`(既定 `ja` / `en`) |

正確な入力スキーマ(Zod定義)は [`server.mjs`](./server.mjs) を参照してください。

**`marp_render` のPDF出力にはローカルの Chrome/Chromium が必要です。** marp-core によるHTML生成はブラウザ非依存(`npx` 導入を軽く保つため)で、PDFはローカルインストール済みの Chrome/Chromium を headless で駆動して生成します。定番パスから自動検出し、`MARP_CHROME_PATH` で実行ファイルを明示することもできます。見つからない場合は HTML のみを返し `pdf_skipped` を報告します(そのHTMLを開いて印刷→PDF保存でも1スライド=1ページで作成できます)。

### 計測(Telemetry)について

既定では何も記録しません。環境変数 `FIRSTCH_TOOLS_USAGE_LOG` にファイルパスを設定したときのみ、各ツール呼び出しごとに1行のJSON(`{ ts, tool, source }`)をそのローカルファイルへ追記します。ネットワーク送信は一切ありません(未設定であればファイルへの書き込み自体が発生せず、データが端末外に出ることはありません)。

### Web版

同一アルゴリズムを、インストール不要の無料ブラウザツールとしても公開しています: **[tools.first-ch.com](https://tools.first-ch.com)**。UIで使いたいとき・MCPクライアントを持たない相手にリンクを共有したいときはこちらをどうぞ。

### vendor同期(WebPコーデック)

`webp_convert` は [`@jsquash/webp`](https://github.com/jamsinclair/jSquash) v1.5.0 の無改変サブセットを [`vendor/jsquash-webp/`](./vendor/jsquash-webp/) 配下に同梱しています。Web版(tools.first-ch.com)も同じv1.5.0サブセットを無改変で使っており、双方が同一の出力を返します。各リポジトリのCIが独立に、自身のvendorファイルを [`vendor/jsquash-webp/CHECKSUMS.sha256`](./vendor/jsquash-webp/CHECKSUMS.sha256) に対して検証するため([`.github/workflows/ci.yml`](./.github/workflows/ci.yml) 参照)、両者が気づかないうちに乖離することはありません。

### ライセンス

本パッケージ自体は [MIT](./LICENSE) です。

`webp_convert` のために [jSquash](https://github.com/jamsinclair/jSquash) のWebPコーデックの一部を `vendor/jsquash-webp/` 配下に同梱しており、それぞれ別のライセンスが適用されます。

- jSquashのラッパーコード部分は **Apache-2.0** — [`vendor/jsquash-webp/LICENSE`](./vendor/jsquash-webp/LICENSE) を参照
- 内部で使われるlibwebpコーデック本体(WASMバイナリとそのJSグルーコード)は **BSD-3-Clause, Copyright (c) 2010 Google Inc.** — [`vendor/jsquash-webp/codec/LICENSE.codec.md`](./vendor/jsquash-webp/codec/LICENSE.codec.md) を参照

いずれのライセンスファイルも公開npmパッケージに原文のまま同梱しています(BSD-3-Clauseはバイナリ再配布時にも著作権表示・条件・免責事項の再掲を求めるため)。

### 開発者向け

```bash
npm ci
npm test        # ユニットテスト(lib.mjs / webp.mjs / marp.mjs)。詳細は test.mjs
node e2e.mjs     # stdio smokeテスト: server.mjsを子プロセス起動しツール一覧取得・実行を検証
```

CIはNode 18.14.1 / 20 / 22 の3系統に加え、vendorチェックサム検証・公開tarball内容検査を実行します([`.github/workflows/ci.yml`](./.github/workflows/ci.yml) 参照)。

TDQS

A4/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a uniquely defined purpose—contrast checking, character counting, image conversion, JSON-LD generation, llms.txt generation, and encoding conversion—with no functional overlap.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case (e.g., contrast_check, webp_convert), making them predictable and easy to understand.

Tool Count5/5

With 6 tools, the server is well-scoped for a utility toolkit, providing a focused yet sufficient set without being sparse or overwhelming.

Completeness3/5

The tools cover several common web development tasks but lack obvious utilities like HTML/CSS validation or sitemap generation, making the set feel somewhat arbitrary.

Maintenance

ActivityActive
ResponsivenessNo issues