---
name: yomeru-ai-mcp
description: Use any yomeru.ai MCP tool for Japanese-learning workflows. Covers `word_lab` (dictionary lookup: kanji breakdown, SVG stroke paths, examples, compounds, verb conjugation tables), `grammar_lab_fast` (fast cached sentence grammar — default), `grammar_lab` (deep sentence analysis, slower), `example_sentences` (examples-only). Trigger on 'what does this Japanese word mean', 'look up', 'explain this sentence', 'break down the grammar of', 'JLPT', 'kanji breakdown', 'stroke order', 'particles', 'conjugate', 'te-form', 'past tense', or any Japanese reading-comprehension task — even without naming yomeru, when Japanese text appears. Ships display recipes for plain markdown (terminal) and HTML/SVG widgets (Claude.ai) — dictionary cards with stroke order, conjugation tables, and grammar breakdowns from the payload, no extra calls. Renders in yomeru.ai brand style: emerald accents, sharp corners, neutral chrome, Samsung Sharp Sans. Skip romaji and full-paragraph translation (tools need ≥1 Japanese character).
---

# yomeru.ai Japanese MCP

Four tools sitting on top of JMDict + JMnedict + Tatoeba + a Japanese-tuned LLM, exposed via the public yomeru.ai MCP server. They cover the full single-word and single-sentence Japanese-learning loop. This skill is a usage manual + display recipe book, not just an API reference.

## Quick mental model

Two scopes: **word** and **sentence**. Two priorities: **cheap and cached** vs **deep and slow**. Pick the cell:

| Scope | Default (use first) | Heavy variant | Examples-only |
|---|---|---|---|
| Word | `word_lab` | (no fast/slow split) | `example_sentences` |
| Sentence | `grammar_lab_fast` | `grammar_lab` | — |

Routing rules:

- One Japanese word in → `word_lab`. Examples and kanji breakdown are already inside its `details` payload, so do not also call `example_sentences`.
- A sentence (≥2 words, has particles or verb endings) in → `grammar_lab_fast` first. Reach for `grammar_lab` only when the user explicitly wants a deeper pass or the fast tool's output is too thin for an unusually rare construction.
- Only example sentences, nothing else, and no kanji or compound info needed → `example_sentences`. This is rare; prefer `word_lab` if there is any chance the user will want more than examples.
- Conjugated surface form ("食べました") → `word_lab` with `baseForm: "食べる"` so the dictionary entry resolves correctly.
- Homonym kanji where the reading matters (e.g. 行く is いく vs ゆく) → `word_lab` with `reading` set.
- Word appearing in a known sentence → pass `context: { sentence: "..." }` to `word_lab` to disambiguate sense (used vs neutral, idiomatic vs literal).

## Tool reference

### `Yomeru.ai Japanese MCP:word_lab`

**Input**
- `word` (required, 1–50 chars, must contain ≥1 Japanese char)
- `baseForm` (optional, dictionary form when `word` is conjugated)
- `reading` (optional, kana hint for ambiguous kanji readings)
- `context.sentence` (optional, ≤200 chars, for sense disambiguation)

**Output shape (top level)**
```
{
  tool, query, slug, url, primaryWord,
  entries[]:        // 1..N entries — primary first, alternates after
    word, reading, alternateReadings, partOfSpeech[], partOfSpeechDisplay,
    meanings[], jlpt, common, frequencyRank, frequencyTier, tags[], source,
    status, entryId,
    details?:       // ONLY on the primary entry
      kanji[]:      // one per kanji in the word
        literal, grade, strokeCount, frequency, jlptLevel,
        processedReadings: { onYomi, kunYomi, nanori, pinyin, ... },
        processedMeanings: { english },
        radicals[], kradComponents[], hasDetailedData,
        strokeOrder: { character, stroke_count, strokes[]: { order, path, type } }
      examples[]: { sourceType, sourceValue, text, japanese, english }
      exampleCount,
      compounds[]: { surface, reading, meanings[], partOfSpeech[], isCommon, source }
      compoundCount,
      inflections?:           // PRESENT for inflectable POS — verb / i-adjective / na-adjective
        kind,                 // "verb" | "i-adjective" | "na-adjective" — DISCRIMINATOR; switch on this
        // ── kind === "verb" ─────────────────────────────────────────────
        verbType,             // "v1" | "v5" | "vs" | "vk"
        stem,                 // dictionary-form stem used to derive the table
        // ── kind === "i-adjective" ──────────────────────────────────────
        irregular,            // true for いい / 良い (POS adj-ix) — conjugations switch to よ-stem
        stem,                 // surface minus trailing い (regular) OR よ/良 (irregular)
        // ── kind === "na-adjective" ─────────────────────────────────────
        stem,                 // bare adjective (e.g. "静か" — JMDict strips trailing だ)
        // ── shared ──────────────────────────────────────────────────────
        conjugations[]:       // ~20–35 rows depending on POS, covering common everyday usage
          japanese,           // the conjugated form
          english,            // English label, e.g. "Te form", "It is X (Polite Present)"
          category            // "base" | "formal" | "informal" | "special" | "compound" — group by this to render
  totalCount, generating
}
```

**Important payload facts**
- `details` is only present on `entries[0]` (the primary). Alternates are basic cards — JLPT, frequency, meanings, that's it.
- `strokeOrder.strokes[]` are KanjiVG-format SVG path data (`d` attribute), viewBox `0 0 109 109`. Render directly — see widget recipe below.
- `frequencyTier` values: `TOP300`, `TOP1500`, `TOP6000`, `TOP10000`, `RARE`. Useful for visual badging.
- `source: "ai_generated"` means the entry came from the LLM fallback layer (out-of-dict words, neologisms, slang). The response will include `tags: ["AI-generated", ...]` and `status: "approved"` once vetted, or `generating: true` if still polling.
- AI fallback polls synchronously up to ~25s. If the call returns with `generating: true`, retry the same call after a short delay.

**Sample call**
```
word_lab(word="食べる")
→ 3 entries (primary 食べる + 2 AI-generated alternates たべ, 喰べ)
→ primary has 1 kanji (食) with full stroke data, 5 Tatoeba examples, 5 compounds
```

### `Yomeru.ai Japanese MCP:grammar_lab_fast`

**Input**
- `sentence` (required, 2–200 chars, ≥1 Japanese char)

Note: the fast variant does NOT accept `context`; the slow `grammar_lab` does.

**Output shape**
```
{
  tool, sentence, normalizedSentence, slug, url,
  grammarAnalysis: {
    text:  "...",     // unstructured prose mash — usable but noisy
    html:  "..."      // structured: <h2> sections, <table> with data-structure JSON, <ul>, <p>
  },
  vocabulary[]:        // FULL word_lab-style entries for every substantive word in the sentence
    word, reading, partOfSpeech[], partOfSpeechDisplay, meanings[], jlpt, common,
    frequencyRank, frequencyTier, tags[], source, status,
    details: { kanji[], examples[], compounds[], ... }
  modelUsed, sources[], searchQueries[], cached, status, generatedAt
}
```

**Key insight: `vocabulary[]` is the gift.** It contains complete dictionary entries for every meaningful word in the sentence — same shape as a `word_lab` primary entry, including `details` with kanji and examples. This means a clickable "tap a word in the breakdown to see its full card" UI requires **zero additional MCP calls**. Cache it locally and read from it.

**HTML structure inside `grammarAnalysis.html`**

Sections appear in this order, each preceded by `<h2 class="section-header">`:
1. Structure — a `<div class="grammar-structure" data-structure='{"table":[...], "breakdown":"..."}'>` followed by a rendered `<table class="grammar-table">`. The `data-structure` attribute holds the parsed JSON: each row has `Part`, `Function`, `Reading`, `DictionaryForm`, `WordType` (Noun / Verb / Particle / Punctuation / etc.), and `Relationships[]` (`{ type, target }` edges). Parse this JSON for any custom rendering — it is cleaner than scraping the table.
2. Breakdown — single `<p>`.
3. Context Translation — a `<p class="context-translation">` then a `<p>` of translation notes.
4. Grammar Points — bullet list (`• **point name**: explanation`).
5. Nuance & Usage — single `<p>`.
6. Related Patterns — bullet list.
7. Context — single `<p>`.

**Cold cache behavior**
- Documented contract: on a cold cache miss, the tool may return `NOT_READY` with a `queryHash` and `retryAfterSec`. Re-call with the same sentence after the delay.
- Hot path returns the full payload immediately.

### `Yomeru.ai Japanese MCP:grammar_lab`

Same input/output shape as `grammar_lab_fast` but routes through a deeper model. 5–15s on fresh generation, cached afterward. Use only when:
- The user explicitly asks for a deep pass.
- `grammar_lab_fast` returned shallow or unsatisfying analysis on a complex/rare sentence.
- The sentence has classical/archaic forms, dialectal grammar, or obscure idioms where the fast model is likely to flatten nuance.

Default to `_fast` first. Fall through to slow only on demand.

### `Yomeru.ai Japanese MCP:example_sentences`

**Input**
- `word` (required, 1–50 chars, ≥1 Japanese char)
- `limit` (optional, 1–5, default 5)

**Output**
```
{
  tool, word,
  examples[]: { n, japanese, english, source, status, aiMeta? }
  aiPadded,           // true if at least one row is ai_generated
  totalCount,
  circuitBreakerOpen  // true if upstream is rate-limited
}
```

**Behavior**
- Returns up to 4 canonical examples (sources `tatoeba` or `jmdict`), pads to `limit` with one AI-generated row tagged `source: "ai_generated"`, `status: "pending"`, with `aiMeta: { model, promptVersion, generatedAt }`.
- If only 0–3 canonical examples exist, will still pad with one AI row.
- Set `limit: 4` (or lower) if you want canonical-only output without AI padding. Then check `aiPadded === false` to confirm.
- `circuitBreakerOpen: true` means the upstream example service is throttled; retry later or fall back to `word_lab`'s embedded examples.

**Use this tool only when** the user wants ONLY example sentences. For dictionary-card workflows, use `word_lab` — its `details.examples` returns the same canonical examples without the extra call.

## Display recipes

The skill targets two environments: text-mode (Claude Code, terminal) and visualizer-mode (Claude.ai web/desktop with `show_widget`). Both recipes draw from the same payload.

### Markdown recipe — word_lab

```markdown
## 食べる · たべる
**JLPT N5** · Top 300 (#266) · Ichidan verb · transitive

1. to eat
2. to live on (e.g. a salary)
3. to live off · to subsist on

### Kanji
- **食** — Grade 2, 9 strokes, freq #328, "eat, food"
  - ON: ショク, ジキ
  - KUN: く.う, く.らう, た.べる, は.む

### Examples
- そんなにがつがつ食べるんじゃないよ。 — Don't eat like a pig.
- ギリシア人もよく魚を食べる。 — Greeks often eat fish, too.
- 父は料理が得意だ。母はといえば、食べるのが得意だ。 — Father is good at cooking. As for Mother, she is good at eating.

### Used in
- 食べるラー油 (chili oil w/ chopped garlic)
- 一口食べる (to take a bite)
- ぼりぼり食べる (to eat with a crunch)

### Conjugations
| Form | Japanese | English |
|---|---|---|
| Dictionary | 食べる | I eat (informal) |
| Te-form | 食べて | "and then…" link / request stem |
| Ta-form | 食べた | I ate (informal past) |
| Formal present | 食べます | I eat (polite) |
| Formal negative | 食べません | I don't eat (polite) |
| Formal past | 食べました | I ate (polite) |
| Informal negative | 食べない | I don't eat |
| Potential | 食べられる | I can eat |
| Volitional | 食べよう | Let's eat |
| Progressive | 食べている | I am eating |
| Request | 食べてください | Please eat |

(Render only the rows the user is likely to want — group by `category`. The full table has ~30 rows.)
```

### Markdown recipe — word_lab i-adjective

Same skeleton as the verb recipe; replace the conjugation block with:

```markdown
### Conjugations
| Form | Japanese | English |
|---|---|---|
| Dictionary | 高い | It is expensive |
| Adverbial | 高く | (used before て and pre-verb) |
| Te-form | 高くて | "and then…" / "and also…" |
| Negative te-form | 高くなくて | "not expensive, and…" |
| Plain negative | 高くない | It is not expensive |
| Plain past | 高かった | It was expensive |
| Plain past negative | 高くなかった | It wasn't expensive |
| Polite present | 高いです | It is expensive (polite) |
| Polite negative | 高くないです / 高くありません | It is not expensive (polite) |
| Conditional | 高ければ | If it's expensive |
| Looks like | 高そう | Looks expensive |
| Too ~ | 高すぎる | Too expensive |
| Becomes ~ | 高くなる | Becomes expensive |
| Because (informal) | 高いから | Because it's expensive |
| Because (formal) | 高いので | Because it's expensive (explanatory) |
| Even though | 高いのに | Even though it's expensive |
| And also | 高いし | "It's expensive, and also..." (additive) |
| Should have been | 高ければよかった | "Should have been expensive" (regret) |
```

For いい/良い (POS `adj-ix`), `inflections.irregular === true` — the table will use the よ-stem (よくない / よかった / よくて) automatically.

### Markdown recipe — word_lab na-adjective

```markdown
### Conjugations
| Form | Japanese | English |
|---|---|---|
| Stem | 静か | bare adjective |
| Attributive | 静かな | (modifies a following noun: 静かな部屋) |
| Adverbial | 静かに | (used as adverb: 静かに歩く) |
| Te-form | 静かで | "and…" connector |
| Negative te-form | 静かじゃなくて / 静かではなくて | "not quiet, and…" |
| Plain present | 静かだ | It is quiet |
| Plain negative | 静かじゃない / 静かではない | It is not quiet |
| Plain past | 静かだった | It was quiet |
| Polite present | 静かです | It is quiet (polite) |
| Polite negative | 静かじゃないです / 静かではありません | It is not quiet (polite) |
| Polite past | 静かでした | It was quiet (polite) |
| Conditional | 静かなら | If it's quiet |
| Becomes ~ | 静かになる | Becomes quiet |
| Make/keep ~ | 静かにする | Make/keep it quiet |
| Because (informal) | 静かだから | Because it's quiet |
| Because (formal) | 静かなので | Because it's quiet (explanatory) |
| And also | 静かだし | "It's quiet, and also..." (additive) |
| Even though | 静かなのに | Even though it's quiet |
| Formal conditional | 静かであれば | If it's quiet (formal) |
```

### Markdown recipe — grammar_lab

```markdown
## 毎朝コーヒーを飲んでから出かけます。
*"I go out after drinking coffee every morning."*

### Breakdown
| Part | Reading | Function | Type |
|---|---|---|---|
| 毎朝 | まいあさ | time adverb | noun |
| コーヒー | こーひー | direct object | noun |
| を | を | object marker | particle |
| 飲んで | のんで → 飲む | te-form verb | verb |
| から | から | sequence marker | particle |
| 出かけます | でかけます → 出かける | main verb | verb |

### Grammar points
- **Te-form + から**: sequence of habitual/sequential actions ("after doing X, do Y").
- **Polite present (ます-form)**: ongoing habitual action in polite register.

### Vocabulary
- **毎朝 (まいあさ)** — N5, every morning
- **飲む (のむ)** — N5, Godan -mu, transitive: to drink, to swallow, to take (medicine)
- **出かける (でかける)** — N5, Ichidan, intransitive: to go out, to leave
```

### Widget recipe — word_lab card (HTML/SVG)

Use this when the host renders `show_widget`. Pulls directly from the JSON payload — no scraping, no transforms beyond field reads.

**Brand language: yomeru.ai house style.** Emerald accents, sharp corners (no border-radius), neutral chrome, Samsung Sharp Sans for the display kanji. The CSS is fully self-contained — Claude.ai widgets render in sandboxed iframes that don't see host theme tokens, so all colors and fonts are inlined as CSS variables.

```html
<style>
  /* yomeru brand tokens (inline — no theme dependency) */
  :root {
    --y-bg:          #ffffff;
    --y-bg-muted:   #fafaf9;                    /* neutral-50 */
    --y-fg:          #1f1f1d;
    --y-fg-muted:   #525252;                    /* neutral-600 */
    --y-fg-faint:   #a3a3a3;                    /* neutral-400 */
    --y-border:     #e5e5e5;                    /* neutral-200 */
    --y-accent:     #047857;                    /* emerald-700 — primary brand */
    --y-accent-soft: rgba(16,185,129,0.10);
    --y-accent-line: rgba(16,185,129,0.40);
    --y-shadow:     0 12px 36px -22px rgba(0,0,0,0.28);
    --y-mono:        ui-monospace, "JetBrains Mono", "SF Mono", monospace;
    --y-display:    "Samsung Sharp Sans", "Inter", system-ui, sans-serif;
    --y-sans:        "Samsung One", "Inter", system-ui, sans-serif;
  }
  @media (prefers-color-scheme: dark) {
    :root {
      --y-bg:          #0a0a0a;
      --y-bg-muted:   rgba(255,255,255,0.03);
      --y-fg:          #fafafa;
      --y-fg-muted:   #d4d4d4;
      --y-fg-faint:   #525252;
      --y-border:     rgba(255,255,255,0.10);
      --y-accent:     #34d399;                  /* emerald-400 in dark */
      --y-shadow:     0 12px 36px -22px rgba(16,185,129,0.18);
    }
  }

  .yw-card { font-family: var(--y-sans); background: var(--y-bg);
             color: var(--y-fg); border: 1px solid var(--y-border);
             box-shadow: var(--y-shadow); padding: 24px; border-radius: 0; }

  .yw-tag-row { font-family: var(--y-mono); font-size: 11px; font-weight: 500;
                color: var(--y-accent); text-transform: uppercase;
                letter-spacing: 0.18em; margin-bottom: 12px; }

  .yw-head { display: flex; align-items: baseline; gap: 16px;
             margin-bottom: 6px; flex-wrap: wrap; }
  .yw-kanji { font-family: var(--y-display); font-size: 38px; font-weight: 700;
              line-height: 1; color: var(--y-fg); position: relative; }
  .yw-kanji::after { content: ""; position: absolute; left: 0; right: 0;
                     bottom: -2px; height: 10px;
                     background: var(--y-accent-soft); z-index: -1; }
  .yw-reading { font-size: 18px; color: var(--y-fg-muted); font-weight: 400; }

  .yw-badges { display: flex; gap: 6px; margin: 12px 0 18px; flex-wrap: wrap; }
  .yw-badge { font-family: var(--y-mono); font-size: 10px;
              text-transform: uppercase; letter-spacing: 0.16em;
              padding: 4px 10px; border: 1px solid var(--y-border);
              border-radius: 0; }
  .b-jlpt { color: var(--y-accent); border-color: var(--y-accent-line);
            background: var(--y-accent-soft); }
  .b-freq { color: var(--y-fg-muted); }
  .b-pos  { color: var(--y-fg-faint); }

  .yw-meanings { margin: 0 0 24px; padding-left: 20px; }
  .yw-meanings li { font-size: 15px; line-height: 1.7; color: var(--y-fg); }

  .yw-section-h { font-family: var(--y-mono); font-size: 11px; font-weight: 500;
                  color: var(--y-accent); margin: 22px 0 10px;
                  text-transform: uppercase; letter-spacing: 0.18em; }

  .yw-kanji-row { display: flex; gap: 16px; align-items: center; padding: 14px;
                   background: var(--y-bg-muted); border: 1px solid var(--y-border);
                   margin-bottom: 8px; }
  .yw-stroke-svg { width: 84px; height: 84px; flex-shrink: 0;
                   background: var(--y-bg); border: 1px solid var(--y-border); }
  .yw-stroke-svg path { stroke: var(--y-fg); fill: none; stroke-width: 3;
                        stroke-linecap: round; stroke-linejoin: round; }
  .yw-kanji-info { font-family: var(--y-sans); font-size: 13px; line-height: 1.6; }
  .yw-kanji-info .literal { font-family: var(--y-display); font-size: 22px;
                            font-weight: 700; margin-right: 8px; color: var(--y-fg); }
  .yw-kanji-info .meta { color: var(--y-fg-muted); display: inline; }
  .yw-kanji-info .read-label { color: var(--y-accent); font-family: var(--y-mono);
                               font-size: 10px; margin-right: 6px; font-weight: 500;
                               text-transform: uppercase; letter-spacing: 0.16em; }

  .yw-ex-row { padding: 10px 14px; border-left: 2px solid var(--y-accent-line);
               background: var(--y-bg-muted); margin-bottom: 6px; }
  .yw-ex-jp { font-size: 15px; line-height: 1.5; color: var(--y-fg); }
  .yw-ex-en { font-size: 13px; color: var(--y-fg-muted); margin-top: 2px; }

  .yw-compound { display: inline-flex; align-items: baseline; font-size: 13px;
                 padding: 6px 12px; background: var(--y-bg-muted);
                 border: 1px solid var(--y-border); margin: 0 6px 6px 0;
                 border-radius: 0; }
  .yw-compound .surface { font-weight: 500; color: var(--y-fg); }
  .yw-compound .gloss { color: var(--y-fg-muted); margin-left: 8px; font-size: 12px; }

  .yw-source { font-family: var(--y-mono); font-size: 10px; color: var(--y-fg-faint);
               text-transform: uppercase; letter-spacing: 0.18em; margin-left: 8px; }
</style>

<h2 class="sr-only">Dictionary entry for {primaryWord}</h2>
<div class="yw-card">
  <div class="yw-tag-row">Yomeru · word_lab · JLPT {entry.jlpt} · {entry.frequencyTier}</div>

  <div class="yw-head">
    <span class="yw-kanji">{entry.word}</span>
    <span class="yw-reading">{entry.reading}</span>
  </div>
  <div class="yw-badges">
    <span class="yw-badge b-jlpt">JLPT {entry.jlpt}</span>
    <span class="yw-badge b-freq">{entry.frequencyTier} · #{entry.frequencyRank}</span>
    <span class="yw-badge b-pos">{entry.partOfSpeechDisplay}</span>
  </div>
  <ol class="yw-meanings">
    {entry.meanings.map(m => <li>{m}</li>)}
  </ol>

  <!-- For each kanji in entry.details.kanji[] -->
  <div class="yw-section-h">Kanji</div>
  <div class="yw-kanji-row">
    <svg class="yw-stroke-svg" viewBox="0 0 109 109" xmlns="http://www.w3.org/2000/svg">
      <line x1="54.5" y1="0" x2="54.5" y2="109" stroke="var(--y-border)" stroke-width="0.5" stroke-dasharray="2 2"/>
      <line x1="0" y1="54.5" x2="109" y2="54.5" stroke="var(--y-border)" stroke-width="0.5" stroke-dasharray="2 2"/>
      {strokes.map(s => <path d={s.path}/>)}
    </svg>
    <div class="yw-kanji-info">
      <span class="literal">{kanji.literal}</span>
      <span class="meta">Grade {kanji.grade} · {kanji.strokeCount} strokes · Freq #{kanji.frequency} · "{kanji.processedMeanings.english.join(', ')}"</span>
      <div><span class="read-label">ON</span>{kanji.processedReadings.onYomi.join(', ')}</div>
      <div><span class="read-label">KUN</span>{kanji.processedReadings.kunYomi.join(', ')}</div>
    </div>
  </div>

  <!-- examples and compounds follow the same field-mapping pattern -->
</div>
```

### Widget recipe — grammar_lab card

Parse `grammarAnalysis.html`'s embedded `data-structure` JSON to get the parts table. Use `vocabulary[]` directly for inline mini-cards. Reuse the yomeru brand tokens from the `word_lab` recipe above (`--y-bg`, `--y-fg`, `--y-accent`, etc.).

WordType pill colors map to yomeru's emerald/neutral palette — emerald-soft for nouns, inverted ink for verbs, neutral chip for particles, faint text for punctuation:

```html
<style>
  /* Assumes yomeru brand tokens from the word_lab recipe are already in scope */

  .yg-card { font-family: var(--y-sans); background: var(--y-bg);
             color: var(--y-fg); border: 1px solid var(--y-border);
             box-shadow: var(--y-shadow); padding: 24px; border-radius: 0; }

  .yg-tag-row { font-family: var(--y-mono); font-size: 11px;
                color: var(--y-accent); text-transform: uppercase;
                letter-spacing: 0.18em; margin-bottom: 12px; }

  .yg-sentence { font-family: var(--y-display); font-size: 26px; font-weight: 700;
                 line-height: 1.3; color: var(--y-fg); margin-bottom: 4px; }
  .yg-trans    { font-size: 14px; color: var(--y-fg-muted);
                 font-style: italic; margin-bottom: 18px; }

  .yg-table { width: 100%; border-collapse: collapse; margin: 16px 0;
              border: 1px solid var(--y-border); }
  .yg-table th { font-family: var(--y-mono); font-size: 10px; font-weight: 500;
                  text-transform: uppercase; letter-spacing: 0.18em;
                  color: var(--y-accent); text-align: left;
                  padding: 10px 12px; border-bottom: 1px solid var(--y-border);
                  background: var(--y-bg-muted); }
  .yg-table td { padding: 10px 12px; font-size: 14px; color: var(--y-fg);
                  border-bottom: 1px solid var(--y-border); vertical-align: top; }
  .yg-table tr:last-child td { border-bottom: none; }
  .yg-jp       { font-family: var(--y-display); font-weight: 700; font-size: 16px; }
  .yg-reading  { font-size: 13px; color: var(--y-fg-muted); font-family: var(--y-mono); }

  .yg-pill { display: inline-block; font-family: var(--y-mono); font-size: 10px;
              text-transform: uppercase; letter-spacing: 0.16em;
              padding: 3px 10px; border: 1px solid transparent; border-radius: 0; }
  .pill-noun        { background: var(--y-accent-soft); color: var(--y-accent);
                      border-color: var(--y-accent-line); }
  .pill-verb        { background: var(--y-fg); color: var(--y-bg); }
  .pill-particle    { background: var(--y-bg-muted); color: var(--y-fg-muted);
                      border-color: var(--y-border); }
  .pill-punctuation { color: var(--y-fg-faint); }
  .pill-adjective   { background: var(--y-accent-soft); color: var(--y-accent);
                      border-color: var(--y-accent-line); }
  .pill-adverb      { background: var(--y-bg-muted); color: var(--y-fg-muted);
                      border-color: var(--y-border); }

  .yg-points { margin: 18px 0; padding-left: 20px; }
  .yg-points li { font-size: 14px; line-height: 1.7; color: var(--y-fg);
                  margin-bottom: 6px; }
  .yg-points strong { color: var(--y-accent); font-weight: 600; }

  .yg-vocab-grid { display: grid; gap: 10px;
                   grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
                   margin-top: 12px; }
  .yg-vocab-card { padding: 12px 14px; background: var(--y-bg-muted);
                   border: 1px solid var(--y-border); border-left: 2px solid var(--y-accent-line); }
  .yg-vocab-card .head { font-family: var(--y-display); font-size: 17px;
                         font-weight: 700; color: var(--y-fg); }
  .yg-vocab-card .reading { font-family: var(--y-mono); font-size: 12px;
                            color: var(--y-fg-muted); margin-left: 8px; }
  .yg-vocab-card .gloss { font-size: 13px; color: var(--y-fg-muted);
                          margin-top: 4px; line-height: 1.5; }
  .yg-vocab-card .jlpt { font-family: var(--y-mono); font-size: 9px;
                         color: var(--y-accent); text-transform: uppercase;
                         letter-spacing: 0.18em; margin-top: 6px; display: block; }
</style>

<div class="yg-card">
  <div class="yg-tag-row">Yomeru · grammar_lab · {modelUsed}</div>

  <div class="yg-sentence">{sentence}</div>
  <div class="yg-trans">"{translation_from_html_section}"</div>

  <table class="yg-table">
    <thead><tr><th>Part</th><th>Reading</th><th>Function</th><th>Type</th></tr></thead>
    <tbody>
      {dataStructure.table.map(row => (
        <tr>
          <td class="yg-jp">{row.Part}</td>
          <td class="yg-reading">{row.Reading} → {row.DictionaryForm}</td>
          <td>{row.Function}</td>
          <td><span class="yg-pill pill-{row.WordType.toLowerCase()}">{row.WordType.toLowerCase()}</span></td>
        </tr>
      ))}
    </tbody>
  </table>

  <!-- Grammar points: parse from the markdown bullet list in grammarAnalysis.html -->
  <ul class="yg-points">
    {grammarPoints.map(p => <li><strong>{p.name}</strong>: {p.explanation}</li>)}
  </ul>

  <!-- Vocabulary: render mini-cards from vocabulary[] (all data already in payload) -->
  <div class="yg-vocab-grid">
    {vocabulary.map(v => (
      <div class="yg-vocab-card">
        <span class="head">{v.word}</span>
        <span class="reading">{v.reading}</span>
        <div class="gloss">{v.meanings.slice(0, 2).join('; ')}</div>
        <span class="jlpt">JLPT {v.jlpt} · {v.frequencyTier}</span>
      </div>
    ))}
  </div>
</div>
```

For the live demo widgets used while authoring this skill, see the rendered samples in the conversation that produced this file.

## Common workflows

**1. "What does 〜 mean?"**
Single word in. Call `word_lab`. Render the dictionary card (markdown or widget per environment). If the word is conjugated, also pass `baseForm`.

**2. "Help me understand this sentence."**
Sentence in. Call `grammar_lab_fast`. Render the breakdown card. The `vocabulary[]` array means follow-up "what does X mean" questions can usually be answered without another call — read from the cached array.

**3. "I'm reading manga and hovered this word."**
Pass the whole bubble's text as `context.sentence` to `word_lab` so sense disambiguation works. Especially relevant for verbs like 取る, つく, etc. with many senses.

**4. "Build me flashcards for 〜."**
`word_lab` for each word — its `details.examples` already gives 5 examples. `example_sentences` is only needed if you want examples without the rest of the card metadata (rare).

**5. "Compare these two readings of [kanji]."**
`word_lab` with `reading` set to disambiguate. For kanji with many readings (行く: いく vs ゆく), call twice with each reading.

## Quirks and gotchas

- **Examples can be reading-prefix matches, not semantic matches.** Looked up 橋 (はし, "bridge") and the example sentences came back about はしおしお, はしきり, はしくじれない — string-prefix matches on the reading, not actual bridge sentences. Skim the examples and drop ones that obviously don't use the looked-up word. Yomeru's web UI presumably filters this; the MCP currently does not.
- **Only the primary entry has `details`.** If a homonym lookup returns 10 entries, only `entries[0]` will have kanji breakdowns and examples. To get details on a non-primary sense, look up the word again with a `reading` hint that targets that sense.
- **Conjugation / inflection tables ARE in the response.** For verbs, i-adjectives, AND na-adjectives, the primary card carries `details.inflections` with `kind` discriminating the three. Verbs (ichidan/godan/suru/kuru/zuru): ~25–35 forms — base 1–5, te/ta, formal × tense × polarity, informal × tense × polarity, desire (-tai), potential, conditional, imperative, volitional, plus common compound forms (progressive ている, request てください, prohibition てはいけません, experience たことがある, obligation なければなりません, etc.). i-adjectives (regular adj-i AND irregular いい/良い adj-ix): stem, adverbial く, te-form くて, conditional ければ, plain/polite × tense × polarity, looks-そう, too-すぎる, becomes-くなる, -さ nominalizer, sentence patterns. na-adjectives (adj-na): attributive な, adverbial に, te-form で, conditional なら, full copula × tense × polarity, plus the same special/compound rows. Group by `category` to render. For non-inflectable POS (nouns, adverbs, particles) the field is omitted.
- **AI-generated entries** (`source: "ai_generated"`) cover out-of-dict words — neologisms, slang, technical, regional. Tagged with `["AI-generated", "<source name>"]`. Treat them as advisory, not authoritative; flag the AI provenance in the rendered card if it matters to the learner.
- **`grammar_lab_fast` cold-cache misses** can return `NOT_READY` with a `queryHash` and `retryAfterSec`. Wait then re-call with the same sentence.
- **`grammar_lab` (slow) also caches** — once a sentence has been deeply analyzed, subsequent calls are fast. So a 5–15s wait is one-time per sentence.
- **`example_sentences` always pads with one AI row** unless `limit` is low enough or canonical pool is large enough to fill the request without padding. Inspect `aiPadded` and per-row `source` if you need to label provenance.
- **No romaji input.** All four tools enforce a regex requiring at least one Japanese character (hiragana, katakana, or CJK ideograph). Reject romaji-only queries upstream.
- **`grammar_lab_fast` rejects `context`.** Only the slow `grammar_lab` accepts surrounding-paragraph context. If context matters and latency does not, use the slow one.
- **`generating: true` on word_lab** means the AI fallback layer is still polling. Either retry the same call after a few seconds or surface the partial response to the user with a "still resolving" hint.

## SVG stroke order — render notes

`details.kanji[i].strokeOrder` is KanjiVG-format. The viewBox is implicit `0 0 109 109` (the standard KanjiVG canvas).

To render a static stroke-order glyph:
1. Set `<svg viewBox="0 0 109 109">`.
2. For each `strokes[i]`, emit `<path d="{path}" fill="none" stroke="..." stroke-width="3" stroke-linecap="round" />`.
3. Add a faint center cross (vertical + horizontal dashed line at x=54.5 / y=54.5) to mimic the practice grid.

To render an animated draw:
- Each path can be drawn by setting `stroke-dasharray` to a generous value (200) and animating `stroke-dashoffset` from 200 to 0. Stagger animations by `stroke i × 400ms` to draw strokes in canonical order.
- Skip animation if `prefers-reduced-motion` is set.

To render stroke numbers:
- Use the `strokes[i].order` field (1-indexed). Compute the path's start point with `getPointAtLength(0)` after mounting, place a small numbered label there.

## Authentication / access

The yomeru.ai MCP is the public service hosted at `https://yomeru.ai/api/mcp` (registered as `Yomeru.ai Japanese MCP` in this Claude install). No auth setup is required from the MCP-tool side; rate limits, if any, are enforced by the upstream service.

## When NOT to use these tools

- **Full-paragraph translation** — these are word-and-sentence scoped. For a paragraph, split into sentences and loop, or use a translation-first tool.
- **Romaji-only input** — tools reject input without ≥1 Japanese character.
- **Reading classical or pre-modern Japanese** — coverage is modern-Japanese-tuned. Classical particles, kango, and bungo forms may parse strangely.
- **Compound morphological analysis** at sub-token level (e.g. wanting individual mora) — not exposed.
- **Listening / pronunciation audio** — no audio in this MCP. Yomeru.ai's web platform has voice (ElevenLabs), but the MCP surface is text-only.
