Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
list_booksA

List all books in the content directory with their publication IDs.

chapter_statusA

Get the status of all lang/level combos for a chapter. Reports which files exist locally (chapter.md, audio, marks, translations).

Args: book: Book directory name (e.g. "1984", "everyday-life") chapter_number: Chapter number (e.g. 7)

create_chapter_from_marksA

Create one chapter variant (single language+level) via cwbe's /chapters/from-marks pipeline: cwtts → Gemini sentence translate → awesome-align → ingest. Polls the resulting Job until terminal. To build all 9 language variants of the same chapter, call this once per source language — one chapter at a time, no concurrent runs (see Concurrency Cap in CLAUDE.md).

Returns JSON with status ("COMPLETED" | "FAILED" | "CANCELLED" | "TIMEOUT"), job_id, message, and — on success — chapter_id. On failure the message includes sourceAudioBlobName=... scraped from cwbe logs; pass that back to retry without regenerating audio.

If a chapter with the same (publication, language, level, title) already exists, cwbe returns that chapter's UUID immediately — safe to retry.

Args: publication_id: Publication UUID on cwbe. title: Localized chapter title (e.g. "0005 - Les plans"). language: Source language code (EN | FR | ES | DE | IT | PT | ZH | JA | KO). level: Difficulty (B1 | B2). marks: Pre-split sentence list in the source language. No blanks. source_audio_blob_name: Optional retry hint — skip phase 0 (TTS) by reusing a cached audio bundle from a previous failed run. Scrape from cwbe logs after a failure. tokens_per_mark: Pre-segmented source tokens, one list per mark (parallel to marks). Required for CJK source — obtain via the segment tool (one batched /segment call), then feed the same tokens to the cjk-glosser agent. Passing them here makes cwbe trust the caller's tokenisation and skip its internal cwseg call, eliminating agent-vs-cwseg split mismatches. Optional for EU source. token_glosses: Per-token gloss overrides. Required and non-empty for CJK-source chapters — cwbe rejects with HTTP 400 if any (non-punctuation source token, Asian target) cell is missing. Each entry is a dict with keys token, sourceLanguage, targetLanguage, gloss — e.g. [{"token": "你好", "sourceLanguage": "ZH", "targetLanguage": "JA", "gloss": "こんにちは"}]. Use gloss: "" for explicit ML Kit fallback. Entries are request-scoped and never written to any cache.

validate_marksA

Dry-run a /from-marks ingest without TTS or DB persistence. Runs the full pipeline (sentence translate + awesome-align + per-token gloss

  • structural validation) and returns every issue found, at once.

Use this BEFORE every create_chapter_from_marks call. Catches: blank Gemini translations, EU↔EU alignment coverage below threshold (70%), CJK token failures, missing target languages, bad alignment ranges, empty tokens. Cheap and idempotent.

Returns the cwbe ValidationResult (top-level ok, issues[], stats). Each issue has at least kind, message, and a markIndex (null for whole-batch issues).

Issue kind values worth recognising at decision time:

  • BLANK_TRANSLATION → Gemini returned empty for that target.

  • SOURCE_COVERAGE / TARGET_COVERAGE → awesome-align below 70% (EU) or 40% (CJK). context.coverage and context.minimum are percent.

  • MISSING_LANGUAGES → some target langs absent.

  • EMPTY_TOKENS → cwseg produced no tokens (degenerate input).

Args: language: Source language code (EN | FR | ES | DE | IT | PT | ZH | JA | KO). level: B1 | B2. marks: Pre-split sentence list in the source language. No blanks. tokens_per_mark: Optional pre-segmented source tokens, one list per mark. Validate is permissive (it's the exploratory step) — pass them once you have agent glosses ready so validate sees what /from-marks will see. Omit on a first pass to inspect cwseg's tokens via /segment instead. token_glosses: Same shape as on create_chapter_from_marks. Pass the exact list you intend to send to /from-marks so the validator judges what would actually ship. Without overrides, validate reports issues on Gemini cells that overrides would replace — false positives for the caller-supplied flow.

lint_marksA

Pre-submission linter for source mark texts. ZERO API calls — runs locally in milliseconds. Catches issues that historically tripped the pipeline before you waste a Gemini call on them.

Checks per mark:

  • Empty / whitespace-only (validate-marks would reject anyway)

  • Control characters (need stripping)

  • Length above thresholds (cwseg fragmentation, Gemini context risk)

  • Missing sentence-final punctuation (alignment edge case)

  • Historically-tricky tokens for the source language (e.g. ZH 那 demonstrative-vs-relative; KO 일 day-vs-event)

  • CJK + embedded Latin words that confuse cwseg

Returns: {summary: {markCount, warningCount, errorCount, verdict}, marks: [...]} verdict: "ok" | "watch" (warnings only) | "blocked" (errors present)

Run this BEFORE create_chapter_from_marks. If verdict is "blocked", fix the source text. If "watch", spot-check the flagged marks after upload.

Args: source_language: Language of the marks (EN/FR/ES/DE/IT/PT/ZH/JA/KO). marks: List of source-language sentences. Same input you'd pass to create_chapter_from_marks.

regloss_chapter_tokensA

Re-run Gemini per-token glossing on a CJK-source chapter using the current GeminiTokenTranslator prompt and overwrite its stored zip blob in place. Only EU target glosses (EN/FR/ES/DE/IT/PT) are refreshed — Asian target glosses (ZH/JA/KO) are preserved as-is from the original ingest's caller-supplied token_glosses, since the server no longer produces Asian glosses. Sentence translations and EU↔EU alignments are preserved.

Use after a Gemini prompt fix to refresh EU glosses on chapters glossed under the older prompt. To fix bad Asian glosses on an existing chapter, recreate it via create_chapter_from_marks with corrected token_glosses instead — regloss can't repair what it can't produce. Non-CJK chapters return skipped=true.

Returns {chapterId, sourceLanguage, skipped, skipReason?, markCount, cellsChanged, totalCells}. cellsChanged/totalCells is the diagnostic: if 0, EU glosses were already up-to-date.

Args: publication_id: Publication UUID. chapter_id: Chapter UUID.

clear_gemini_cacheA

Wipe cwbe's Gemini sentence + token caches. Use sparingly — primary cases are recovery from a poisoned cache state or forcing a cold-cache re-run for testing. Normal day-to-day work should never need this.

Schema in Swagger → service-controllerDELETE /debug/gemini/cache.

gemini_cache_statsA

Caffeine stats (hit rate, size, evictions) for cwbe's Gemini sentence and token caches. Useful for debugging unexpected validate/ingest costs or confirming cache key parity between validate and /from-marks.

Schema in Swagger → service-controllerGET /debug/gemini/cache/stats.

generate_audioA

Break-glass: call cwtts directly to generate audio for a single lang/level. Returns base64 MP3 + per-mark UUIDs + millisecond timings. Normal chapter creation should go through create_chapter_from_marks which invokes cwtts internally.

Args: language: Source language code (EN | FR | ES | DE | IT | PT | ZH | JA | KO). marks: Pre-split sentence list in that language.

translate_textsB

Break-glass: translate a list of texts from one source language to all 8 other langs via Gemini. Returns {lang: [texts]}. Parallel to phase 1 of /from-marks.

segmentA

Tokenise a list of marks via cwseg (jieba/fugashi/kiwipiepy for CJK, regex for EU). Always batch every mark in ONE call — per-mark calls flake under rate limits.

Returns {"tokens_per_mark": [["tok1","tok2",...], ...]} parallel to marks — feed this directly to cjk-glosser and back into create_chapter_from_marks(tokens_per_mark=...) so cwbe and the agent see the same tokens.

Args: language: Source language code (EN | FR | ES | DE | IT | PT | ZH | JA | KO). marks: List of mark texts. All in the same language.

alignA

Break-glass: call awesome-align for EU↔EU token-alignment between one source sentence and its per-target translations. targets is {lang: translated_text}. CJK targets are not aligned — cwseg handles those during ingest.

Args: source_language: Source lang code (must be EU: EN/FR/ES/DE/IT/PT). source_text: Source sentence. targets: {lang: text} for each EU target you want aligned.

gloss_tokensA

Break-glass: ask Gemini for per-token offline glosses into EU targets only (EN/FR/ES/DE/IT/PT). Returns a list of {lang: gloss} — one entry per input token in the same order. Asian target glosses (ZH/JA/KO) are not produced by this tool — the server-side guard rejects them; supply them as token_glosses on create_chapter_from_marks instead.

Args: source_language: Source lang code (typically CJK: ZH / JA / KO). sentence_text: The source sentence containing the tokens. sentence_translations: Sentence-level translations, e.g. {"EN": "...", "FR": "..."} — richer context = better glosses. tokens: List of token strings (substrings of sentence_text).

upload_chapter_from_zipA

Break-glass: POST (or PUT if chapter_id given) a manually-assembled chapter zip via /chapters/from-audio. Use this only when create_chapter_from_marks isn't right — e.g., you have hand-patched translations + alignments locally and need to push the exact bytes.

Args: publication_id: Publication UUID. audio_path: Absolute path to audio.mp3. marks_path: Absolute path to marks.json. marks_in_ms_path: Absolute path to marks_in_milliseconds.json. translations_path: Absolute path to translations.json. title: Localized chapter title (e.g. "0005 - Les plans"). language: Source lang code. level: B1 | B2. chapter_id: Optional — if given, PUTs (updates) instead of POSTs.

download_chaptersB

Download all chapters for a publication to a local directory.

Args: publication_id: Publication UUID from cwbe output_dir: Local directory to save files to

list_publicationsA

List all publications from cwbe with their IDs, titles, and types.

list_uploaded_chaptersC

List all chapters uploaded to cwbe for a publication.

Args: publication_id: Publication UUID

get_publication_readmeC

Get the readme for a publication from cwbe.

Args: publication_id: Publication UUID

update_publication_readmeB

Replace a publication's readme markdown. Partial update — all other fields (title, headers, descriptions, flags) are preserved.

update_publication_titlesA

Partial update of the publication title and/or per-language headers and descriptions. headers and descriptions are merged — pass only the languages you want to change; omitted langs keep their current value. Omit all three args for a no-op.

update_publication_flagsB

Partial update of the publication isComplete / archived flags.

create_publicationA

Create a new publication on cwbe. All 9 langs are required in both headers and descriptions. cover_path is an absolute local path to a JPEG cover image (required by cwbe).

Args: title: Canonical title (e.g. "The Iliad"). publication_type: "ONETIME_UPLOAD" | "CONTINUOUS_UPLOADS". copyright_terms: List of terms (e.g. ["PUBLICATION_PLUS_95", "LIFE_PLUS_70", "UNKNOWN", "LIFE_PLUS_100"]). headers: Per-lang display title, ALL 9 langs required. descriptions: Per-lang summary, ALL 9 langs required. readme: Authoring README markdown (style guide, glossary, etc.). cover_path: Absolute path to cover JPEG. archived: Default False. is_complete: Default False.

delete_publicationA

Delete a publication and EVERY chapter + blob it owns. Irreversible. Requires explicit confirm=True; any other value returns a refusal.

delete_chapterA

Delete a single chapter variant and its blob. Irreversible. Requires explicit confirm=True; any other value returns a refusal.

update_chapter_metadataA

Update chapter metadata (title, language, level) without re-uploading audio.

Args: publication_id: Publication UUID chapter_id: Chapter UUID title: New chapter title language: Language code (e.g. "EN", "FR", "JA") level: "B1" or "B2"

chapter_release_sanity_checkA

Verify a chapter release — all 18 variants (9 langs × 2 levels) of one story chapter. Run this after every chapter release as the final sign-off before declaring the chapter done.

Downloads each variant zip and checks structural integrity:

  • All 18 (lang, level) combos present.

  • mark UUIDs consistent across marks.json, mark_ids_to_translation.json, and marks_in_milli_seconds.json.

  • Mark timings strictly monotonic.

  • Each mark has exactly the 8 expected target languages.

  • No blank target translations (the bug class that bit Ministry of Quiet ch4).

  • EU↔EU pairs have non-empty tokenAlignments with in-bounds ranges.

  • CJK pairs have non-empty tokens (no stray alignments).

  • audio.mp3 present and non-trivial in size.

Args: publication_id: Publication UUID. title_prefix: Title prefix that matches all 18 variants of the story chapter (e.g. "0005 - " for Iliad ch5). Variants are matched by title.startswith(title_prefix).

Returns the report shape (top-level ok, variants_found, missing_combos, errors, warnings, variants[]). ok=true means every variant passed and all 18 combos are present.

This check is structural only — it does not verify the audio language matches the chapter language (use Whisper tiny separately for that) or that translations are semantically correct (manual review).

query_logsA

Query Grafana Loki for cwbe logs. Primary use: scrape sourceAudioBlobName and translationsBlobName from a failed /chapters/from-marks job for retry, or follow a job's progress across phases.

Exactly one of job_id, filter_text, or logql should be given (in that precedence). If none, returns all recent cwbe lines (noisy).

Args: job_id: Filter to lines containing this cwbe job UUID. filter_text: Filter to lines containing this literal substring (e.g. "from-marks", "blob=", "Google Translate raw response"). logql: Raw LogQL string, used verbatim. Caller handles escaping. minutes_back: Time window in minutes (default 30). limit: Max lines returned (default 500, newest-first).

Returns JSON {"count": N, "entries": [{"timestamp": "<ns>", "line": "..."}]}. Requires grafana_user and grafana_password in ~/.cwmcp/config.properties.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/paulmichaelstafford/cwmcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server