Skip to content
Conduit

mcp server

language-mcp

Local MCP server for Dutch and US English spelling checks, dictionary lookups, and US English style suggestions for AI agents.

seppegadeyne/language-mcpcommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 28, 2026

language-mcp

A local MCP server for Dutch and US English language checks, built for AI agents (Hermes, Claude, and others) that write resumes, cover letters, blog articles, and website copy.

Currently supported: Dutch and US English. Additional languages can be implemented by adding dictionaries, language-specific rules, and MCP tools; they are not supported automatically by selecting an arbitrary language.

Tools

ToolSourceNetworkPurpose
check_us_english_texthunspell en_US + British form detection (local)NoCheck text for typos and British spelling or vocabulary, with US replacements
check_us_english_b1_texthunspell en_US -m + rule layers (local)NoB1 plain-language proxies: readability (Flesch Reading Ease, Flesch-Kincaid, ARI), sentence/paragraph length, passive voice, jargon, wordy phrases, hidden verbs, filler, idioms, nominalization density, "you" address
validate_us_english_wordhunspell en_US + British form detection (local)NoCheck a single English word for correct US English spelling and suggest replacements
check_dutch_textOpenTaal/hunspell + selected spelling rules (local)NoCheck words and selected compounds, hyphens, and calendar capitalization; return suggestions, positions, rule IDs, explanations, confidence, and source links
check_dutch_b1_textOpenTaal/hunspell -m + rule layers (local)NoB1 simplicity proxies: readability (Flesch-Douma, ARI, LIX, Brouwer Leesindex), sentence/paragraph length, passive voice, officialese jargon, filler, idioms, rare-word flags from a Zipf frequency table with plain-synonym suggestions (Open Dutch WordNet), nominalization density, je/u voice
validate_dutch_wordOpenTaal/hunspell (local)NoCheck a single Dutch word locally for correct spelling, with suggestions
get_dutch_word_detailswoordenlijst.org (MolexServe)YesRetrieve lemma details: part of speech, pronunciation, hyphenation, paradigm, and diminutive forms

The local tools use bundled hunspell dictionaries: OpenTaal for Dutch (v2.20.23) and SCOWL/LibreOffice en_US for US English. The detector for British forms catches terms that hunspell does not flag (whilst, towards, bespoke, webshop, sole trader, findability, etc.), including categories from the Straffe Sites US English guidelines, and provides a US replacement for each match.

get_dutch_word_details queries the internal XML service at woordenlijst.org (INT/Taalunie). This unofficial integration uses caching and rate limiting (1.2 seconds between calls); use it only for individual important words. Tool descriptions and status messages are in US English; Dutch words and upstream dictionary labels retain their original language.

Dutch spelling rules

check_dutch_text also checks selected patterns that a word dictionary misses: btw nummer → btw-nummer, keuken tafel → keukentafel, auto ongeluk → auto-ongeluk, and 3D printer → 3D-printer. Calendar cues such as op Maandag and in Januari produce medium-confidence suggestions for lowercase spelling. Each rule finding includes its ID, explanation, original source positions, confidence, and a precise Team Taaladvies source link.

The rule layer protects code, marked headings, quotations, and other selected non-prose spans. Pass optional protected_terms for exact case-sensitive names or unmarked titles; horizontal whitespace may vary. This protection does not whitelist Hunspell dictionary words. Accepted variants such as rode kool / rodekool, optional clarity hyphens, and meaning-dependent word groups are not automatically joined or rewritten.

This is a small, local spelling layer, not a general grammar checker. See the rule catalog, exceptions, limits, and attribution. B1 readability remains a separate check.

B1 simplicity checks

Both B1 tools report deterministic proxies with the same structure: a readability block, passive voice, rule hits with plain alternatives and positions in the source text, nominalization density, and a reader-address check. Thresholds are tool parameters because conventions differ by sector.

Dutch

check_dutch_b1_text reports deterministic B1 proxies, not a validated B1 verdict: "B1" applied to texts is an editorial convention (Rijksoverheid guideline: write new material at B1, aiming for A2), not an empirically validated text norm. What the tool measures:

  • Readability: Flesch-Douma reading ease (NL-adapted Flesch; practical B1 band ~60-70), ARI as a second, language-neutral indication, LIX (Björnsson 1968; Swedish scale, not validated for Dutch — compounds inflate it), and Brouwer Leesindex A (1963 Dutch Flesch recalibration that weighs sentence length twice as heavily as Flesch-Douma). Formulas are suppressed below 50 words / 3 sentences (short-sample guard; the guard is intentional, see the evaluation baseline).
  • Word frequency: rare-word flags from a bundled Zipf table of the top 50,000 Dutch word forms (derived from FrequencyWords/OpenSubtitles2018 — spoken-language bias, documented in assets/frequency/README.md). Words the table knows but scores rare (default below Zipf 3, configurable via rare_word_zipf_max) are flagged with value, count, and position. Unknown words are never flagged: Dutch compounds and proper names dominate that set. When Open Dutch WordNet lists a clearly more frequent single-word synonym (at least half a Zipf point), the flag includes concrete suggestions ("staken → try: ophouden, stoppen"); suggestions are review hints, never automatic replacements.
  • Sentence and paragraph length against configurable thresholds (defaults: warn >15, flag >20 words per sentence; 150 words per paragraph, matching common editorial guidelines).
  • Passive voice: hunspell -m morphology batch plus an auxiliary window (wordt/worden/werd + past participle = high confidence; is/zijn + past participle = low-confidence zijn-passive; "door" agent noted). These are review flags: context-free morphology cannot tell a genuine passive from a copula construction, so editors judge each hit.
  • Officialese jargon with plain alternatives (betreffende → over, thans → nu, in het kader van → voor...), filler words, and idioms to avoid for NT2 readers, each with the match position in the source text.
  • Nominalization density (-ing/-tie/-heid/-iteit words per 100 words) and je/u voice consistency (default je, configurable to u).

All checks are local and offline: the same bundled OpenTaal assets and hunspell binary as the spelling tools, plus pure TypeScript rule layers in the spirit of the British-form detector.

The Dutch B1 proxies are grounded in an evaluation baseline: the bundled City of Amsterdam complex–simple corpus (1,311 sentence pairs, EUPL-1.2, evaluation-only — nothing from it runs at check time) with a harness that measures how much more often each proxy flags the complex side of a pair (tests/eval/b1-amsterdam.ts, committed baseline in tests/eval/baseline-v1.json, protocol in docs/b1-eval.md). On that set, passive voice separates +11.8 percentage points and sentence length +10.4; the rare-word signal separates +3.8; nominalization density and the long-word share barely separate, and the readability formulas have 0% coverage on single sentences because of the short-sample guard.

US English

check_us_english_b1_text applies the same approach to US English, with English formulas, thresholds, and rule sources:

  • Readability: Flesch Reading Ease (practical B1 band 60-70, "plain English" in Flesch's table), Flesch-Kincaid grade level (default target 9 or lower), and ARI. A zero-dependency syllable counter agrees with the CMU Pronouncing Dictionary on about 94% of the 10,000 most common US English words. The same short-sample guard applies.
  • Sentence and paragraph length: defaults warn above 20 and flag above 25 words per sentence (plainlanguage.gov average and the GOV.UK limit), and the longest flagged sentences are listed with positions.
  • Passive voice: hunspell -m with the bundled en_US dictionary marks regular -ed forms (affix flag D); a closed list covers irregular participles (written, built, sold). Detects be-passives, get-passives, and pronoun contractions ("it's built"), notes a "by" agent, skips "used to", and marks adjectival uses ("is located") as low confidence.
  • Formal jargon and buzzwords (utilize → use, prior to → before, robust, leverage), wordy phrases (in order to → to, due to the fact that → because), hidden verbs (make a decision → decide, provide assistance → help), filler words, and idioms, based on the Federal Plain Language Guidelines and the GOV.UK "words to avoid" list. British spellings stay in check_us_english_text.
  • Nominalization density (-tion/-sion/-ment/-ance/-ence/-ity per 100 words, everyday nouns excluded) and reader address: third-person references to the reader ("users", "the customer") are reported when the expected address is "you" (default).

Checking a file: path, ranges, and hashes

The four check tools (check_dutch_text, check_dutch_b1_text, check_us_english_text, check_us_english_b1_text) accept either text or path. With path, the server reads the local UTF-8 file itself, so an agent never has to retype a long document into a tool call. Every report includes an Input: line with the SHA-256 of the exact text that was checked:

Input: file /abs/cv.md, range [0, 5401) of 5401 code points, 5401 code points checked, input_sha256 9f2c…, file_sha256 9f2c…
ArgumentMeaning
pathAbsolute path of a UTF-8 text file (max 2 MiB; binary and invalid UTF-8 are refused).
start, endOptional half-open range [start, end) in Unicode code points of the file.
expected_sha256Optional hex SHA-256 the checked text must have. On a mismatch nothing is checked and the tool returns an error. Works with text too.

Hashes cover the exact UTF-8 bytes without normalization, so for a whole file input_sha256 equals sha256sum <file>. With a range, finding positions are relative to the range start. To limit which folders the server may read, set LANGUAGE_MCP_ROOTS to one or more folders (separated by :, or ; on Windows); without it, any file readable by the server process is allowed. A set variable that names no folder refuses every path.

Spell checks stop after 20,000 words. When that happens the report starts with INCOMPLETE: and the tool returns an error, so a partial check never counts as full coverage: split the file into ranges instead.

This supports full-coverage review: a lead agent records the file hash or chunk hashes in its manifest, delegates the checks per chunk, and verifies that each report's input_sha256 matches.

Example: check US English copy

Call check_us_english_text with:

{
  "text": "We build custom online stores with colorful designs and clear navigation."
}

The tool reports the number of words checked, unknown words, and British forms. It provides suggestions and positions for flagged words. Review suggestions in context: proper names and technical terms may be valid even when flagged. For plain-language checks on the same copy, call check_us_english_b1_text with the same text. For Dutch copy, use check_dutch_text and check_dutch_b1_text instead.

Installation

Requirements: Node 20+ and hunspell on PATH (pacman -S hunspell).

npm install
npm run build   # Compile TypeScript into dist/

Tests

npm test        # Run node:test through tsx

Hermes configuration (stdio)

mcp_servers:
  language:
    command: node
    args: [/absolute/path/to/language-mcp/dist/cli.js]
    connect_timeout: 60
    enabled: true
    timeout: 120

License and attribution

Code: MIT.

Dictionary assets:

  • assets/nl.dic / assets/nl.aff: © OpenTaal — Revised BSD License and/or CC BY 3.0. Full terms are in assets/LICENSE.txt; source: https://github.com/OpenTaal/opentaal-hunspell. Reusing these assets requires attribution to OpenTaal.
  • assets/en_US.dic / assets/en_US.aff: based on the SCOWL word list (Kevin Atkinson, LGPL), with an affix file from Geoff Kuenning's Ispell (BSD), distributed through LibreOffice/dictionaries. See assets/en_US-LICENSE.txt and assets/en_US-README.txt; source: https://github.com/LibreOffice/dictionaries/tree/master/en.

Data assets (each bundled with its own license and attribution file, kept separate from the MIT code):

  • assets/frequency/nl-zipf.tsv: Zipf frequencies for the top 50,000 Dutch word forms, derived from hermitdave/FrequencyWords nl_50k.txt (OpenSubtitles2018). Content CC BY-SA 4.0; see assets/frequency/README.md (includes the spoken-language bias caveat).
  • assets/synonyms/nl-synonyms.tsv: Dutch synonym table (31,199 lemmas), derived from Open Dutch WordNet release 1.4 (VU Amsterdam, CLTL). CC BY-SA 4.0; see assets/synonyms/LICENSE and assets/synonyms/README.md. When using this data, cite: Postma, Van Miltenburg, Segers, Schoen & Vossen (2016), "Open Dutch WordNet", Proceedings of the 8th Global WordNet Conference. CC BY-SA obligations apply to distributing the derived data, not to texts checked with the tool.
  • assets/eval/amsterdam-complex-simple/: City of Amsterdam complex–simple sentence pairs (EUPL-1.2) and the Hobo 2022 lexical-simplification set, bundled as evaluation-only assets for the B1 harness; never used at runtime. See the README in that directory.

Evaluation baselines (tests/eval/) are committed artifacts of the runs documented in docs/b1-eval.md.

get_dutch_word_details data: woordenlijst.org (Instituut voor de Nederlandse Taal / Taalunie), accessed through its public web service without a separate API agreement.

↑↓ navigate openesc close