Back to Browse

Firstch Tools MCP Server

Developer ToolsModerate6.8MCP RegistryLocal
Free

Server data from the Official MCP Registry

MCP tools: WCAG contrast, JP char/X-weight count, WebP convert, JSON-LD, llms.txt.

About

MCP tools: WCAG contrast, JP char/X-weight count, WebP convert, JSON-LD, llms.txt.

Security Report

6.8
Moderate6.8Moderate Risk

This is a well-structured MCP server providing text processing and image conversion utilities. Authentication and authorization are appropriately scoped (none required—tools are stateless utilities). The codebase demonstrates good security practices with optional telemetry, proper file handling, and vendored dependencies with checksum verification. Minor code quality observations (broad exception handling in WebP conversion, lack of explicit input validation helpers) do not significantly impact the security posture. Permissions are well-justified by the server's stated purpose. Supply chain analysis found 1 known vulnerability in dependencies (0 critical, 1 high severity). Package verification found 1 issue.

3 files analyzed · 5 issues found

Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.

Permissions Required

This plugin requests these system permissions. Most are normal for its category.

File System Read

Reads files on your machine. Normal for tools that analyze or process local data.

File System Write

Writes or modifies files on your machine. Check that this is expected for the tool.

env_vars

Check that this permission is expected for this type of plugin.

What You'll Need

Set these up before or after installing:

Optional. Path to a local JSONL file for opt-in usage logging. No logging when unset.Optional

Environment variable: FIRSTCH_TOOLS_USAGE_LOG

How to Install

Add this to your MCP configuration file:

{
  "mcpServers": {
    "io-github-first-ch-tools-mcp": {
      "env": {
        "FIRSTCH_TOOLS_USAGE_LOG": "your-firstch-tools-usage-log-here"
      },
      "args": [
        "-y",
        "@first-ch/tools-mcp"
      ],
      "command": "npx"
    }
  }
}

Documentation

View on GitHub

From the project's GitHub README.

@first-ch/tools-mcp

MCP server exposing First CH Tools' 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, and case conversion between camelCase, snake_case and kebab-case — 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

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):

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

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

Tools

ToolWhat it doesMain input
contrast_checkComputes 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_charsCounts 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_convertConverts 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 extensionpaths[] (absolute paths), quality? (1-100), outputDir?
jsonld_generateGenerates 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> snippettype, plus the matching organization / faq / service / breadcrumb object
llmstxt_generateGenerates an llms.txt file (per the llmstxt.org proposed format) summarizing a site for AI crawlers/agentssiteName, summary?, notes?, sections?
encoding_convertDetects 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_renderRenders Marp 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: / ``) is honored.markdown or inputPath, theme?, formats? (html | pdf), outputPath?, title?
testdata_generateGenerates 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 reproduciblemode?, rows?, fields?, format?, locale?, encoding?, newline?, bom?, header?, seed?, preset?, length?, outputPath?
diff_checkCompares 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 defaulta/b (text) or pathA/pathB (absolute paths), format? (unified | blocks | both), context?, ignoreWhitespace?, ignoreCase?, words?
cron_explainExplains 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 transitionsexpression, timeZone? (IANA, default UTC), count? (default 5, max 100), from? (ISO 8601)
base64_encodeEncodes 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 claimsmode? (encode default / decode), text? or path?, base64?, outputPath?, urlSafe?, wrap? (76 for MIME), dataUri?, mimeType?, snippets?
url_paramsBreaks 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 fetchedurl?, mode? (parse default / encode / decode), text?, scheme?, set?, remove?, utm?, removeTracking?, sort?, reencode?, spaceAsPlus?
html_escapeEscapes < > & " ' 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_yamlConverts 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 intsconfig.json) is read and converted, with notes saying it is not valid JSON (relaxed=false` rejects it strictly instead)
yaml_to_jsonConverts 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
px_rem_convertConverts 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% trickvalue? (24 / "1.5rem") or css? / path?, unit?, outputPath?, direction?, root?, parent?, precision? (auto or 0-6), minPx?, zeroUnitless?, skipMedia?, ignoreProps?, scale?
color_convertConverts 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 greycolor, alpha? (0-1, 0-100 or "50%"), background? (default #ffffff), syntax? (modern default / legacy), uppercase?, alphaPercent?, step? (1-50, default 10), alphaTable?, palette?
hash_generateComputes 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 storagetext? or path?, algorithms? (default ["md5","sha1","sha256","sha512"]), format? (hex default / HEX / base64 / base64url), newline?, bom?, expected?
jwt_decodeDecodes 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 intoken, key?, keyEncoding? (utf8 default / base64url / base64 / hex), clockTolerance? (seconds), now? (Unix seconds)
user_agent_parseParses 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 typeua? or uas? (array), includeTokens? (default: on for 20 strings or fewer)
uuid_generateGenerates 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_calcCalculates 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 shiftratio? 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_tableTurns 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 spreadsheettext 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_formatFormats 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_generateTurns 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_convertConverts 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 workinput, 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_generateAssembles 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 anythingsiteUrl?, 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_convertConverts 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 (XMLHttpRequestXML / Http / Request), and digits stay attached to the word before them (sha256Hashsha256 / 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 (URLParserurlParser). 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 fortext? or path?, outputPath?, format? (camel default), scope? (lines default / items / whole), splitDigits?, keepAcronyms?, allFormats?, listFormats?, lang? (ja default / en)

See 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 — 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 v1.5.0 under 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 (see .github/workflows/ci.yml), so a silent, unnoticed drift between the two copies isn't possible.

License

The package itself is licensed under MIT.

It bundles a subset of jSquash's WebP codec under vendor/jsquash-webp/ to power webp_convert, which carries its own licenses:

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

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.

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(無料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 などの文字列ケース変換 — をAIエージェント(Claude Code等)向けMCPツールとして提供するサーバーです。

インストール

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

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

1. npm(Claude Code CLI)
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 等)に以下を追加します。

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

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

ツール一覧

Documentation truncated — see the full README on GitHub.

Reviews

No reviews yet

Be the first to review this server!