Skip to main content
Glama

JOONS LISTEN MCP

Local meeting transcription for MCP clients: Whisper large-v3 and Qwen3-ASR-1.7B on your own GPU, with speaker labels, fact flags and optional Korean translation and minutes.

Part of the JOONS ecosystem: MCP-Based AI Infrastructure Ecosystem.

Korean: README.ko.md

Status: 0.1.0 Preview. Read LIMITATIONS.md before relying on it.

At a glance

  • What: a small local service plus a stdio MCP server. You give it a meeting or call recording; it returns a transcript with speaker turns, timestamps, "check this" flags on numbers, prices, dates and product codes, and optionally a Korean translation and minutes written by a local LLM.

  • For: people who record meetings or calls (Chinese, Korean, English, mixed) and want the audio to stay on their own machine, and want an MCP client such as Claude Desktop or Claude Code (anything that can start a local stdio server) or a script to drive it. ChatGPT cannot start local stdio servers; it needs a remote MCP endpoint, so put the service behind an MCP gateway you control.

  • Not for: real-time captions, cloud processing, legal-grade transcripts, or machines without a CUDA GPU (it is tested only on an 8 GB NVIDIA card; CPU-only use is untested).

  • How: install the dependencies, start the service, point your MCP client at python -m joons_listen mcp, then ask the client to transcribe a file.

  • Trust: the service binds to loopback only, reads audio only from folders you allow, and sends meeting text only to a local LLM. Every number it extracts is marked "verify": nothing is auto-confirmed. See SECURITY.md.

Related MCP server: meeting-scribe

What it does

audio file
  -> ffmpeg decode (16 kHz mono)
  -> silero-vad speech segments (gaps the VAD misses are filled, see LIMITATIONS)
  -> speaker turns: ECAPA embeddings + agglomerative clustering
  -> ASR: Qwen3-ASR-1.7B (zh, ko, mixed) or Whisper large-v3 (en), chosen per language
  -> fact extraction (price, quantity, date, code) + uncertainty flags
  -> optional Korean translation and minutes via a local Ollama model
  -> txt / md / srt / json / minutes files
  • Two ASR engines only, loaded one at a time so an 8 GB GPU is enough.

  • Jobs run one by one in a queue and survive restarts: each stage writes a checkpoint, so a stopped job resumes instead of starting over.

  • A private dictionary (names, replacement rules) biases recognition and fixes recurring mistakes.

  • Hallucination guards: repeated-text detection, a filter for dictionary hints echoed back as text on noisy audio, low-confidence flags.

Requirements

  • Python 3.10 or newer, ffmpeg on PATH (or JOONS_LISTEN_FFMPEG).

  • An NVIDIA GPU with roughly 8 GB of memory and a CUDA build of PyTorch.

  • Python packages: numpy, soundfile, scikit-learn, torch, faster-whisper, qwen-asr, silero-vad, speechbrain, huggingface_hub.

  • Optional, for translation and minutes: Ollama with a local model (default qwen3:8b).

  • Model weights (about 10 GB) are downloaded from Hugging Face on first use into the data folder. They are not part of this repository.

Install and run

git clone https://github.com/dbdefault/joons-listen-mcp
cd joons-listen-mcp
pip install -e .
pip install torch faster-whisper qwen-asr silero-vad speechbrain huggingface_hub   # pick the torch build for your CUDA

python -m joons_listen check      # GPU, engines, local LLM
python -m joons_listen serve      # the HTTP service on 127.0.0.1:18796

The package is installed from source; it is not published to PyPI.

Without an MCP client you can process one file directly (stop the service first, both would share one job database):

python -m joons_listen run meeting.m4a --translate --minutes

Connect an MCP client

The MCP server speaks JSON-RPC over stdio and only calls the running service on loopback. Example client configuration (also in examples/mcp-client-config.json):

{
  "mcpServers": {
    "joons-listen": {
      "command": "python",
      "args": ["-m", "joons_listen", "mcp"],
      "env": { "JOONS_LISTEN_URL": "http://127.0.0.1:18796" }
    }
  }
}

Tools:

Tool

Purpose

Read-only

listen_transcribe

queue a transcription job, returns job_id immediately

no

listen_diarize

queue a speaker-turn job (no transcript)

no

listen_summarize

minutes or brief summary from a finished job

no

listen_status

one job's progress, or service health and recent jobs

yes

listen_result

read a result in chunks (json, txt, md, srt, minutes)

yes

listen_models

engines, state and language routing

yes

Long recordings take time (a 56-minute recording took about 20 minutes on an RTX 4060 Ti), so the tools never wait: call listen_status until the state is done, then listen_result.

Allowed folders

The service reads only audio files inside allowed folders. The default is the data folder's inbox, ~/Desktop and ~/Downloads. Set JOONS_LISTEN_ROOTS (a path-separator list) to change it. A bare file name is looked up inside the allowed folders. Files whose names look like secrets are refused.

Configuration

All settings are environment variables. The main ones:

Variable

Default

Meaning

JOONS_LISTEN_DATA

~/.joons-listen

jobs, database, models, dictionary

JOONS_LISTEN_PORT

18796

service port (loopback only)

JOONS_LISTEN_ROOTS

inbox, Desktop, Downloads

allowed audio folders

JOONS_LISTEN_URL

http://127.0.0.1:18796

where the MCP server finds the service (loopback only)

JOONS_LISTEN_LLM_URL / JOONS_LISTEN_LLM_MODEL

Ollama on loopback / qwen3:8b

translation and minutes

JOONS_LISTEN_MIRROR_DIR

empty (off)

copy finished results (no audio) as zip files to this folder

JOONS_LISTEN_MIRROR_CMD

empty (off)

instead of a folder, run this command per file: a JSON array such as ["rsync", "-a", "{file}", "host:/dir/"]; {file} is replaced by the file path, no shell is used, the zip is sent before its .ok marker

JOONS_LISTEN_DICT

<data>/dictionary.json

private dictionary, see examples/dictionary.example.json

Evaluation

docs/EVALUATION.md has error rates and speed on a public speech set, and a field test on a hard far-field phone recording. tools/eval_fleurs.py and tools/eval_recording.py reproduce them. The measurements are small and meant to find weaknesses, not to rank the two engines.

Documentation

Third-party models

Component

License

Whisper large-v3 (CTranslate2 build Systran/faster-whisper-large-v3)

MIT

Qwen3-ASR-1.7B

Apache-2.0

SpeechBrain ECAPA (speechbrain/spkrec-ecapa-voxceleb)

Apache-2.0

silero-vad

MIT

FLEURS (evaluation data, not included)

CC-BY-4.0

Check each upstream license yourself before redistributing weights. This repository contains none.

License

Apache License 2.0, see LICENSE.

Available Tools

6 tools
listen_diarizeB

Queue a speaker-turn job (who spoke when, no transcript).

Inputs: audio_path (required), title, num_speakers.

listen_modelsA
Read-only

List the engines, their state and the language routing.

Inputs: no named parameters.

listen_resultB
Read-only

Read a finished job's result in chunks (format json|txt|md|srt|minutes).

Inputs: job_id (required), format, offset, max_chars.

listen_statusA
Read-only

Status of one job, or service health plus recent jobs when job_id is omitted.

Inputs: job_id.

listen_summarizeB

Create minutes or a brief summary from a finished transcription job.

Inputs: job_id (required), force, style.

listen_transcribeA

Queue a transcription job for a local audio file (Whisper large-v3 / Qwen3-ASR-1.7B, speaker labels, optional Korean translation and minutes). Returns a job_id immediately; poll listen_status, then fetch listen_result.

Inputs: audio_path (required), title, engine, diarize, +5 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 6 tools

    Disambiguation4/5

    Each tool targets a distinct stage of the async pipeline: queue (transcribe/diarize/summarize), poll (status), fetch (result), and inspect (models). The only friction is mild overlap between listen_transcribe's optional minutes flag and listen_summarize, plus status vs result needing the description to clarify state-vs-content. Descriptions do resolve this, so misselection risk is low.

    Naming Consistency5/5

    All six tools use a uniform listen_ prefix followed by a clear noun/verb (transcribe, status, diarize, summarize, result, models) in consistent snake_case. The pattern is predictable enough that an agent can guess a tool name before reading it.

    Tool Count5/5

    Six tools is a tight, well-scoped surface for an asynchronous audio-processing service: one per job type plus the status/result/models trio needed to drive the queue-and-poll workflow. No tool looks redundant or padded.

    Completeness4/5

    The lifecycle is largely covered: submit jobs, check status/health, retrieve results in multiple formats, summarize, and inspect engines. Gaps are administrative rather than core — no cancel/delete job, no explicit list-jobs tool (folded into listen_status), and no way to submit remote/uploaded audio, but agents can work around these.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers