New
Apps 1,439Makers 1,437This week 1,439
Promote your app−30%
Lettras screenshot 1
0views in
the last 30 days

Lettras

Word searches, crosswords, sudoku and Unicode text styles in 6 languages (es, en, pt, fr, de...

Built with Claude Code

by artificialss · Unclaimed · from CRCosta Rica · added Oct 29, 2026
no ratings0 comments
Lettras

Lettras SDK

Word searches, crosswords, sudoku and Unicode text styles in six languages, with every accent intact.

Rust MCP License

Developer tools for Lettras, the free word-puzzle platform. Generate word searches from any word list in Spanish, English, Portuguese, French, German and Italian, with native letters (Ñ, Ç, Ã, Ä, ẞ, È…) kept as one cell each, from your own code, a terminal, or an AI assistant. Besides word searches there are crosswords (crucigramas), sudoku and Unicode text styles.

Tool What it is Install / connect Source
lettras on npm JavaScript/TypeScript library and CLI. Runs locally, no network or API key npm install lettras (npm) npm/
lettras for Kotlin Kotlin library for the JVM and Android. Same engine, runs locally org.lettras.artificialss:lettras (Maven Central) kotlin/
lettras for Java The Kotlin library works from plain Java (JVM 17+) org.lettras.artificialss:lettras kotlin/
lettras for Go Go module, pure Go (no cgo), every OS and architecture go get github.com/Artificialss/lettras-sdk/go go/
lettras for Swift Swift package for Apple platforms and Linux https://github.com/Artificialss/lettras-sdk (SwiftPM) swift/
Lettras for .NET C# library for .NET 8+ on Windows, macOS and Linux dotnet add package Lettras dotnet/
lettras for Rust Rust crate, engine embedded and run in-process cargo add lettras rust/
lettras for Dart and Flutter Dart package, engine embedded and run in-process dart pub add lettras (pub.dev) dart/
Lettras for VS Code Extension: write text in 22 Unicode styles, insert word searches and sudokus VS Code Marketplace, artificialss.lettras vscode/
Lettras for JetBrains Plugin for IntelliJ IDEA, PyCharm, WebStorm, Android Studio and more JetBrains Marketplace, "Lettras" (id ai.artificialss.lettras) jetbrains/, upload guide
lettras for Homebrew The command-line tool as a formula, no Node setup brew install artificialss/lettras/lettras homebrew/
Lettras MCP server Lets Claude and other MCP clients create puzzles. Written in Rust, runs on Vercel https://mcp.lettras.org/mcp (MCP Registry) mcp/

Published. Live today: the npm package, the Kotlin library on Maven Central (which also serves Java), and the hosted MCP server (also listed in the official MCP Registry). The Rust, Go, Swift, .NET and Dart libraries, the VS Code extension, the JetBrains plugin and the Homebrew formula are built and tested here and are being published to their own stores (crates.io, pkg.go.dev, Swift Package Index, NuGet, pub.dev, the VS Code and JetBrains marketplaces, a Homebrew tap). A documentation site is planned.

Every library on one page: docs/LIBRARIES.md.

Table of contents

npm package

npm install lettras
import { generate, render } from 'lettras';

const puzzle = generate({
  words: ['gato', 'perro', 'piña', 'mono', 'cebra'],
  rows: 9,
  cols: 12,
  position: 'mixed', // horizontal | vertical | mixed (all 8 directions)
  seed: 8,           // same input and seed, same grid
});

console.log(render(puzzle));
9×12  seed 8  engine 0.5.0

- - - - - - - - - - - -
- - - - - - - - - - - -
- - - - C - - - - - - -
- - - - - E - O N O M -
- - A - - - B - R - - -
O - - Ñ - - - R - - - -
T - - - I - E - A - - -
A - - - - P - - - - - -
G - - - - - - - - - - -

Words: gato, perro, piña, mono, cebra

piña takes four cells (the Ñ is one), mono is written right to left, and perro and cebra run on diagonals. The grid is plain data: puzzle.grid is an array of rows, each an array of one-letter strings.

CLI

npx lettras --words gato,perro,piña --rows 9 --cols 12 --position mixed --seed 8
npx lettras --words sol,luna,mar --rows 8 --json          # raw JSON
npx lettras --words sol,luna,mar --rows 8 --solution      # only the hidden words

Options

Option Values
words Words to hide, in normal spelling. Accents are kept.
rows, cols Grid size. They can differ for a rectangular grid.
position horizontal (left→right), vertical (top→bottom), mixed (all 8 directions, diagonals included).
difficulty 1–4. Sets the directions when position is not given.
clustering 0 words apart · 1 words crossing · default 0.5.
seed Repeatable puzzles.
lang es (default) en pt fr de it.
classicMode Strip accents in the grid (ñ becomes N).
fill Character for empty cells. Default -.

Fill the empty cells with random letters. generate leaves empty cells as -. Pass the result to fill, with the language and accents on or off:

import { generate, fill } from 'lettras';

const puzzle = generate({ words: ['gato', 'perro', 'piña'], rows: 9, cols: 12, position: 'mixed', seed: 8 });
const done = fill(puzzle, { lang: 'es', accents: true });   // or: fill(puzzle.grid, { lang: 'es', accents: false })
done.grid;   // the same matrix, every "-" replaced by a random letter
fill option Meaning
lang es (default) en pt fr de it. Letters follow how common they are in that language.
accents true (default): include the language's accented/native letters (Ñ, Ç, Ã, Ä, ẞ…). false: plain A-Z only.
seed Repeatable filler. Without one, every call gives different letters.
words The hidden words. Added automatically when you pass the puzzle; the filler never creates an extra copy of one.

On the CLI: lettras --words gato,piña --rows 9 --random --accents off.

Example. The same puzzle before and after fill (Spanish, seed 5):

const puzzle = generate({ words: ['gato', 'perro', 'piña', 'mono', 'cebra'], rows: 8, cols: 10, position: 'mixed', seed: 8 });
const done = fill(puzzle, { lang: 'es', accents: true, seed: 5 });

puzzle.grid has - in every empty cell; done.grid replaces them (58 cells here):

C E B R A - O - - -
- - - - - - - T O -
- - - - M - - R A -
- - - O - - R - - G
- - N - - E - - - -
- O - - P - - - - -
- - - - - - - - - -
- - - - - - A Ñ I P
C E B R A R O G G J
D R A E E R C T O D
B Ó E E M Í M R A Ñ
T S C O N R R A E G
T R N E A E R O E M
I O O E P S N P A O
O C O O A P P A U A
I R E A A S A Ñ I P

With accents: false the filler uses plain A-Z only (the one Ñ left is the hidden word piña, which keeps its spelling):

C E B R A R O G G I
D Q A E E P C T O D
B Y E E M X M R A N
S R C O N R R A E G
S R N E A E R O E M
I O O E P S N O A O
O B O O A P O A T A
H R E A A S A Ñ I P

Same grid, language, accents and seed always give the same filler on every platform. Without a seed the wrappers pick a random one, so every call gives different letters.

Result: grid, placements (word, start r/c, step dr/dc, length), words (placed), unplaced (words that did not fit, never dropped silently), rejected (with reasons), seed, engineVersion. TypeScript types are included. Full notes: npm/README.md.

Kotlin library

For JVM and Android apps (and the mobile game). Same engine, same results, no network.

val lettras = Lettras()   // create once, reuse
val puzzle = lettras.generate(
    PuzzleRequest(words = listOf("gato", "perro", "piña"), rows = 9, cols = 12, position = Position.MIXED, seed = 8),
)
puzzle.grid        // List<List<String>>, one letter per cell
puzzle.placements  // where each word is hidden
puzzle.render()    // text view

val done = lettras.fill(puzzle, lang = Language.ES, accents = true)   // empty cells become random letters
done.grid

The engine is the same WebAssembly binary the MCP server uses, run by Chicory (pure Java, no native libraries to build per CPU). On the JVM it is compiled to bytecode; on Android it uses the interpreter. The tests check it returns exactly the grids the npm package returns. Install notes, API, performance and Android details: kotlin/README.md.

MCP server

An MCP server that lets an AI assistant create puzzles for you. Ask for "a 12×12 sopa de letras about animals, mixed directions, with random letters" and it calls generate_word_search, then fill_word_search. It can also write text in Unicode styles with stylize_text.

  • Transport: streamable HTTP, POST /mcp (JSON-RPC). Protocol versions 2025-06-18, 2025-03-26, 2024-11-05.
  • Runs the engine locally: the compiled engine is embedded in the server and executed in-process. It makes no outside calls and needs no credentials to start.
  • Written in Rust on Vercel's Rust runtime (axum).

Connect a client (hosted endpoint: https://mcp.lettras.org/mcp):

# Claude Code
claude mcp add --transport http lettras https://mcp.lettras.org/mcp
// Claude Desktop, Cursor and other clients that take a remote server URL
{ "mcpServers": { "lettras": { "url": "https://mcp.lettras.org/mcp" } } }

Tools

Tool Purpose
generate_word_search Create a puzzle. Arguments match the options above (words, rows, cols, position, difficulty, clustering, seed, lang, classicMode). Returns a readable grid and the full structured result.
fill_word_search Complete a puzzle: takes the grid from generate_word_search (empty cells -) and fills them with random letters in the chosen lang, with accents on or off. Pass the puzzle's words so the filler never creates an extra copy. Does not use up the puzzle limit; it has its own, larger one.
stylize_text Text in Unicode styles (bold, italic, script, fraktur, circled, strikethrough... 22 in all) that keep accents. style is one id, several, or omitted for all; classic strips accents first. Reports in notNative and parts[].missing what a style cannot draw (ß, Æ, Œ, Ø, non-Latin scripts, accents that only exist as combining marks), so you can warn the user. No usage limit.
list_styles The style ids stylize_text accepts, with an English name and a category. No usage limit.
crossword A crossword (crucigrama) from entries ({ word, clue }) inside rows x cols: returns a puzzle matrix (- blank, word numbers, 0, and a/d such as 3/4 where an across and a down word start in the same cell), a solution matrix and placements with the clues. No usage limit. See docs/CROSSWORD.md.
sudoku A sudoku with exactly one solution: size 4, 6, 9, 12 or 16, difficulty 1 to 4, seed. No usage limit.
solve_sudoku Solves a grid (0 = empty) and says whether the solution is unique. No usage limit. See docs/SUDOKU.md.
list_languages The supported languages and the native letters each adds to its grid.

Invalid arguments come back as a tool error written for the model to act on (for example rows: an integer from 6 to 30 is required).

Free tiers

What Free use
generate_word_search, small: up to 10×10 with at most 10 words Unlimited. Never counted.
generate_word_search, larger: a bigger grid (up to 30×30) or more than 10 words 5 puzzles per client, in total, forever (there is no daily reset).
fill_word_search 100 calls per client, separate from the puzzles.
stylize_text, list_styles, crossword, sudoku, solve_sudoku, list_languages No limit.

After a limit the tool answers with "Free limit reached… Visit https://lettras.org". Invalid requests never count.

Limits and privacy. A client is identified by a SHA-256 hash of its IP address, with the whole IPv6 /64 treated as one client so that rotating addresses does not dodge the limit. Because the limit never resets, the server keeps one row per client indefinitely: the hash, a count and a timestamp (no addresses, no words, no puzzles). The request log carries the same hash prefix, never the address. A hash of an IP address can still count as personal data in some jurisdictions, so mention it in your own privacy notice if you operate your own copy. If the database is unreachable the request is allowed (and a usage_store_error line is logged), so a storage outage never takes the service down. The hosted endpoint is public and unauthenticated; small puzzles, styles and languages are free to use, while bigger puzzles are a free trial of five, not a bulk service. To run your own copy with a daily limit instead, set LETTRAS_LIMIT_WINDOW_SECS=86400.

Deploy on Vercel

  1. Create a Vercel project from this repository and set Root Directory to mcp.
  2. Optional but recommended: a Postgres database so the 5-puzzle counter is shared across instances (without it the counter lives in each instance's memory and is not reliable). Create a Neon project, copy its pooled connection string into the project's environment variables as DATABASE_URL (mark it sensitive). The server creates its own mcp_usage table on first use; it stores a hash, a count and a timestamp per client.
  3. Optional settings, all in .env.example: LETTRAS_FREE_LIMIT, LETTRAS_FILL_LIMIT, LETTRAS_LIMIT_WINDOW_SECS (0 = lifetime total, the default), LETTRAS_UPGRADE_URL.
  4. Optional: add your own domain under the project's Settings → Domains (a CNAME pointing to cname.vercel-dns.com; do not change the domain's nameservers).
  5. Deploy. Every puzzle request writes one JSON line to the function logs (hashed client, count, allowed or blocked, grid size, language), which you can read in Vercel Logs or forward with a log drain.

The server is described for MCP registries in server.json.

No secret is stored in this repository. Real values belong in Vercel's project settings.

Text styles

Besides puzzles, the same engine writes text in Unicode styles that keep accents (22 styles). It is what powers Social Media Styles at lettras.org.

import { stylize, unstyle, listStyles } from 'lettras';

const out = stylize('Lettras ejemplo: piña, corazón', { style: ['bold', 'script'] });
out.results[0].text;      // 𝐋𝐞𝐭𝐭𝐫𝐚𝐬 𝐞𝐣𝐞𝐦𝐩𝐥𝐨: 𝐩𝐢𝐧̃𝐚, 𝐜𝐨𝐫𝐚𝐳𝐨́𝐧   (ñ = styled n + combining tilde)
out.results[0].notNative; // ['ñ', 'ó']: accents that exist in no style as one character
unstyle(out.results[0].text); // 'Lettras ejemplo: piña, corazón'
listStyles();             // [{ id: 'bold', label: 'Bold', category: 'bold-italic' }, ...]

style is one id, an array of ids, or omitted for all 22. classic: true strips accents first. parts cuts the output into pieces, and missing: true marks what a style cannot draw (ß, Æ, Œ, Ø, non-Latin scripts), so a UI can show an empty box like a phone without that glyph. Text is limited to 10,000 characters. The same functions exist in Kotlin (stylize, unstyle, listStyles) and as the MCP tools stylize_text and list_styles.

The name in every style

stylize('Lettras') (real output). The list is in display order, which is also the order of listStyles().

Style id Name Lettras
bold Bold 𝐋𝐞𝐭𝐭𝐫𝐚𝐬
italic Italic 𝐿𝑒𝑡𝑡𝑟𝑎𝑠
bold-italic Bold italic 𝑳𝒆𝒕𝒕𝒓𝒂𝒔
sans Sans-serif 𝖫𝖾𝗍𝗍𝗋𝖺𝗌
sans-bold Sans-serif bold 𝗟𝗲𝘁𝘁𝗿𝗮𝘀
sans-italic Sans-serif italic 𝘓𝘦𝘵𝘵𝘳𝘢𝘴
sans-bold-italic Sans-serif bold italic 𝙇𝙚𝙩𝙩𝙧𝙖𝙨
script Script ℒℯ𝓉𝓉𝓇𝒶𝓈
bold-script Bold script 𝓛𝓮𝓽𝓽𝓻𝓪𝓼
fraktur Fraktur 𝔏𝔢𝔱𝔱𝔯𝔞𝔰
bold-fraktur Bold fraktur 𝕷𝖊𝖙𝖙𝖗𝖆𝖘
double-struck Double-struck 𝕃𝕖𝕥𝕥𝕣𝕒𝕤
monospace Monospace 𝙻𝚎𝚝𝚝𝚛𝚊𝚜
small-caps Small caps ʟᴇᴛᴛʀᴀꜱ
fullwidth Fullwidth Lettras
circled Circled Ⓛⓔⓣⓣⓡⓐⓢ
negative-circled Negative circled 🅛🅔🅣🅣🅡🅐🅢
squared Squared 🄻🄴🅃🅃🅁🄰🅂
negative-squared Negative squared 🅻🅴🆃🆃🆁🅰🆂
parenthesized Parenthesized 🄛⒠⒯⒯⒭⒜⒮
strikethrough Strikethrough L̶e̶t̶t̶r̶a̶s̶
underline Underline L̲e̲t̲t̲r̲a̲s̲

Examples

Every style applied to Lettras ejemplo: piña (real output of stylize). The accent in piña is the styled n plus a combining tilde, so how it looks depends on the font; the decoration styles (strikethrough, underline) mark every character, accents included.

Style id Result
bold 𝐋𝐞𝐭𝐭𝐫𝐚𝐬 𝐞𝐣𝐞𝐦𝐩𝐥𝐨: 𝐩𝐢𝐧̃𝐚
italic 𝐿𝑒𝑡𝑡𝑟𝑎𝑠 𝑒𝑗𝑒𝑚𝑝𝑙𝑜: 𝑝𝑖𝑛̃𝑎
bold-italic 𝑳𝒆𝒕𝒕𝒓𝒂𝒔 𝒆𝒋𝒆𝒎𝒑𝒍𝒐: 𝒑𝒊𝒏̃𝒂
sans 𝖫𝖾𝗍𝗍𝗋𝖺𝗌 𝖾𝗃𝖾𝗆𝗉𝗅𝗈: 𝗉𝗂𝗇̃𝖺
sans-bold 𝗟𝗲𝘁𝘁𝗿𝗮𝘀 𝗲𝗷𝗲𝗺𝗽𝗹𝗼: 𝗽𝗶𝗻̃𝗮
sans-italic 𝘓𝘦𝘵𝘵𝘳𝘢𝘴 𝘦𝘫𝘦𝘮𝘱𝘭𝘰: 𝘱𝘪𝘯̃𝘢
sans-bold-italic 𝙇𝙚𝙩𝙩𝙧𝙖𝙨 𝙚𝙟𝙚𝙢𝙥𝙡𝙤: 𝙥𝙞𝙣̃𝙖
script ℒℯ𝓉𝓉𝓇𝒶𝓈 ℯ𝒿ℯ𝓂𝓅𝓁ℴ: 𝓅𝒾𝓃̃𝒶
bold-script 𝓛𝓮𝓽𝓽𝓻𝓪𝓼 𝓮𝓳𝓮𝓶𝓹𝓵𝓸: 𝓹𝓲𝓷̃𝓪
fraktur 𝔏𝔢𝔱𝔱𝔯𝔞𝔰 𝔢𝔧𝔢𝔪𝔭𝔩𝔬: 𝔭𝔦𝔫̃𝔞
bold-fraktur 𝕷𝖊𝖙𝖙𝖗𝖆𝖘 𝖊𝖏𝖊𝖒𝖕𝖑𝖔: 𝖕𝖎𝖓̃𝖆
double-struck 𝕃𝕖𝕥𝕥𝕣𝕒𝕤 𝕖𝕛𝕖𝕞𝕡𝕝𝕠: 𝕡𝕚𝕟̃𝕒
monospace 𝙻𝚎𝚝𝚝𝚛𝚊𝚜 𝚎𝚓𝚎𝚖𝚙𝚕𝚘: 𝚙𝚒𝚗̃𝚊
small-caps ʟᴇᴛᴛʀᴀꜱ ᴇᴊᴇᴍᴘʟᴏ: ᴘɪɴ̃ᴀ
fullwidth Lettras ejemplo: piña
circled Ⓛⓔⓣⓣⓡⓐⓢ ⓔⓙⓔⓜⓟⓛⓞ: ⓟⓘⓝ̃ⓐ
negative-circled 🅛🅔🅣🅣🅡🅐🅢 🅔🅙🅔🅜🅟🅛🅞: 🅟🅘🅝̃🅐
squared 🄻🄴🅃🅃🅁🄰🅂 🄴🄹🄴🄼🄿🄻🄾: 🄿🄸🄽̃🄰
`negative-square

Description taken from the repository README.

Ratings

–
0 ratings
5 0
4 0
3 0
2 0
1 0

Rate this app

Sign in to rate

Comments 0

Sign in to comment

No comments yet.