# Case study: italki-cli

CLI + MCP server for italki's undocumented public API. TypeScript, Bun, Zod — reverse-engineered by sending real requests and writing down what actually comes back.

**Source:** [github.com/LOsioChico/italki-cli](https://github.com/LOsioChico/italki-cli)

## Problem

italki has no official API. Searching teachers, comparing prices, checking schedules, booking lessons — all locked inside the web UI. Personal tooling and agents need structured calls and JSON out.

## Four layers, one direction

`services/` fetches raw (pure functions, no console) → `transforms/` translates codes to domain objects (cents→dollars, tag codes→names, 15-min units→minutes) → `presenters/` formats ANSI text → `commands/` + `mcp/` are thin surfaces. Each layer may only import from beneath it.

## Decisions and what they beat

- Zod schemas as the contract over hand-written interfaces: 50+ nested fields verified against real responses; types inferred, cannot drift.
- Architecture enforced by 5 custom oxlint rules (no cross-layer imports, no classes/console in services…) — violations are build errors, not review comments.
- Every API behavior curl-verified: `sort_by` accepts 12 values and all return identical order (sort client-side); `page_size` real max is 99, not 20.
- Three runtime deps (zod, citty, MCP SDK) over the usual CLI pile — login is pure HTTP + AES signature, no browser automation.
- MCP tools return translated domain JSON (dollars, names, minutes) — not raw codes.

## Numbers

- 13 MCP tools on one service layer: search → book → confirm
- 50+ response fields validated at runtime; 99 tag codes, 7 categories, 13 filters verified
- `bun run verify`: tsc --noEmit + oxlint — 0 errors, 68 files, 101 rules

## What is verified vs. what is not

- Booking/reschedule/confirm POSTs: HAR-verified against captured traffic, not yet live-fired from the CLI.
- Unfiltered `--all` search takes ~15s (44 pages, ~4,300 records through Zod).
- The REST layer the separation rule was designed for does not exist yet — additive by construction, unproven until shipped.
