io.github.First-CH/tools-mcp
MCP tools: WCAG contrast, JP char/X-weight count, WebP convert, JSON-LD, llms.txt.
Open source Open in the app JSON README (API)
About
MCP tools: WCAG contrast, JP char/X-weight count, WebP convert, JSON-LD, llms.txt.
Details
- Kind
- MCP servers
- Topic
- No topic detected
- Publisher
- first-ch
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 0.4.1
- Last push
- 2026-09-03T18:34:27Z
- Repository state
- ativo
- Language
- JavaScript
- License
- MIT
- Added
- 2026-08-29 03:01:54
- Updated
- 2026-08-29 03:01:54
- Origin id
io.github.First-CH/tools-mcp
README
# @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, and CSV/TSV ⇄ JSON conversion — 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 `&`, `'` and `あ` 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 `'` or `'` (`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 `€` 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`) |
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"` では `&` `'` `あ` などの文字参照を元の文字へ戻す。tools.first-ch.com/html-escape/ と同一ロジック。エスケープは `&` を最初に処理するため、1回目の出力をもう一度通しても二重エスケープにならない。名前付きと数値文字参照の切り替え(`numeric`)、引用符を変換するか(`quotes`・属性値へ入れるなら必須)、`'` を `'` と `'` のどちらで書くか(`apos`・`'` はHTML 4.01に無い)、非ASCII文字を参照にするか(`nonAscii`・文字コードが伝わらない経路への保険)を選べる。デコードはHTML 4.01の名前付き文字参照252個すべてと10進/16進に対応し、`€` のような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?`、`c