Transcript Correction MCP Architecture
Accent-aware post-correction server for Whisper ASR transcripts. Combines a phonetic knowledge base of known accent mishearings, TF-IDF / Qdrant RAG retrieval of previously verified corrections, and a multi-LLM fallback chain (6 providers) to fix systematic misrecognitions in accented English speech. Initially built for Chilean and Indian English accents.
Exposes corrections as both a REST API (/api/*) and an
MCP tool server over Streamable HTTP (/mcp), allowing
Claude Code and other MCP clients to correct transcripts inline during conversations.
Workflow: Raw Whisper transcript → Phonetic KB deterministic fixes →
RAG few-shot example retrieval → LLM correction with accent context →
Markdown diff output → Human review → Accept pairs into RAG store (feedback loop).
MCP Tools
6
REST Endpoints
6
LLM Providers
6
Python Modules
7
Port
8000
Runtime
Python 3.12
2. Runtime Architecture
Diagram A: Runtime Architecture
Request Flow
- Client sends text or file path via MCP tool call (
/mcp) or REST POST (/api/correct) - Parser extracts timestamped segments from Whisper output format (
[00:15.32 --> 00:15.38] text) - Phonetic KB applies deterministic fixes for known accent mishearings (single-candidate substitutions)
- RAG Store retrieves the 3 most similar previously-verified correction pairs via TF-IDF cosine similarity
- LLM Chain sends the segments + speaker profile + RAG examples to the first available provider (6-provider fallback)
- Engine merges LLM output with KB fixes (KB is authoritative), flags uncertain corrections
- Output is a markdown diff table with columns: Timestamp, Original, Corrected, Flag
- Accept flow: human reviews diff, calls
transcript_acceptto extract verified pairs back into the RAG store
3. Correction Pipeline
Diagram B: Per-Segment Correction Flow
flowchart TD
A[Input Segment] --> B{Hallucination?}
B -- Yes --> C["[UNINTELLIGIBLE]"]
B -- No --> D[Phonetic KB Lookup]
D --> E{Single candidate?}
E -- Yes --> F[Apply deterministic fix]
E -- No / None --> G[Keep original]
F --> H[Collect for LLM batch]
G --> H
H --> I[TF-IDF RAG Retrieval]
I --> J[Build prompt with speaker profile + RAG examples]
J --> K[LLM Chain Generate]
K --> L{LLM succeeded?}
L -- Yes --> M[Merge LLM output with KB fixes]
L -- No --> N["Flag: llm_unavailable"]
M --> O{Contains UNCERTAIN?}
O -- Yes --> P["Flag: uncertain"]
O -- No --> Q[Final corrected text]
P --> Q
N --> Q
C --> Q
Correction Priority
| Priority | Source | Behavior |
|---|---|---|
| 1 (highest) | Hallucination detection | Known hallucination markers immediately produce [UNINTELLIGIBLE] |
| 2 | Phonetic KB | Deterministic single-candidate substitutions are applied first and preserved through LLM merge |
| 3 | LLM correction | Context-aware correction using speaker profile, domain vocabulary, and RAG examples |
| 4 (lowest) | Original text | If no correction is confident, the original ASR text is preserved |
5. Public Interfaces
MCP Tools (Streamable HTTP at /mcp)
| Tool | Parameters | Read-Only | Description |
|---|---|---|---|
transcript_correct |
text, file_path, speaker, model |
No | Correct accent-related ASR errors in a transcript. Provide raw text or a file path relative to transcripts/. Writes corrected output to corrected/ when using file mode. |
transcript_correct_batch |
speaker, model |
No | Batch-correct all uncorrected transcript files in the transcripts/ directory. Returns list of corrected and failed files. |
transcript_accept |
file_path |
No | Accept a reviewed correction file: extract correction pairs into the RAG store and move file to reviewed/. |
transcript_stats |
(none) | Yes | Return statistics about the correction store (total pairs, breakdown by speaker). |
transcript_list_uncorrected |
(none) | Yes | List transcript files that have not yet been corrected. |
transcript_health |
(none) | Yes | Check server health: per-provider LLM chain status and store size. |
REST API Endpoints
| Method | Path | Description |
|---|---|---|
GET | /health | Server health check (LLM chain status, store pair count) |
POST | /api/correct | Correct a single transcript segment. Body: {"text", "speaker", "model"} |
POST | /api/correct-batch | Batch-correct all uncorrected files. Body: {"speaker", "model"} |
POST | /api/accept | Accept a corrected file into the RAG store. Body: {"file_path"} |
GET | /api/stats | Correction store statistics |
GET | /api/uncorrected | List pending uncorrected transcript files |
MCP Client Configuration
{
"transcript-correction": {
"type": "streamable-http",
"url": "http://transcript-correction.home/mcp"
}
}
6. Repository Structure
_transcript-correction-mcp/
server.py # FastAPI + FastMCP dual-mode entry point (uvicorn)
requirements.txt # Python dependencies (7 packages)
Dockerfile # Python 3.12-slim, port 8000
docker-compose.yml # Single service on hosting_web network
README.md # Usage docs and troubleshooting
correction/ # Core correction pipeline package
__init__.py # Package marker
engine.py # CorrectionEngine: orchestrates KB + RAG + LLM
llm_chain.py # LLMChain: 6-provider fallback (proxies + Ollama)
rag_store.py # CorrectionStore: TF-IDF + Qdrant RAG retrieval
parser.py # Whisper transcript segment parser
phonetic_kb.py # PhoneticKB: speaker profiles + accent substitutions
ollama_client.py # Legacy Ollama client (superseded by llm_chain.py)
phonetic_kb.json # Knowledge base data (speakers, substitutions)
correction_store.json # Correction pair storage (grows via accept flow)
prompts/
correction.txt # LLM system prompt template
defaults/ # First-run seed data (copied to /data if missing)
correction/
phonetic_kb.json
correction_store.json
prompts/
correction.txt
docs/
ARCHITECTURE.html # Previous architecture document
Component Classification
| Component | Classification | Role |
|---|---|---|
server.py | Primary | Application entry point, MCP tool definitions, REST endpoints, app factory |
correction/engine.py | Primary | Core pipeline orchestrator: chunk-based correction with KB/RAG/LLM merge |
correction/llm_chain.py | Primary | Multi-LLM fallback chain with AgentProxy and Ollama providers |
correction/rag_store.py | Secondary | TF-IDF retrieval of correction pairs with optional Qdrant vector fallback |
correction/phonetic_kb.py | Secondary | Speaker-specific accent profile and deterministic substitution rules |
correction/parser.py | Auxiliary | Whisper timestamp format parser ([start --> end] text) |
correction/ollama_client.py | Legacy | Superseded by OllamaProvider in llm_chain.py |
7. Deployment & Operations
Docker Container
| Property | Value |
|---|---|
| Image | Custom build from python:3.12-slim |
| Container Name | transcript-correction-mcp |
| Port | 8000 (internal only, no host mapping) |
| Network | hosting_web (shared Docker bridge) |
| Restart Policy | unless-stopped |
| Volume | transcript_correction_data:/data |
| Traefik Labels | traefik.enable=false (routed via dynamic.yml file provider) |
| Healthcheck | GET http://localhost:8000/health every 30s, 15s timeout, 3 retries |
Environment Variables
| Variable | Default | Description |
|---|---|---|
DATA_DIR | /data | Persistent data directory (transcripts, corrections, KB) |
DEFAULTS_DIR | /app/defaults | Seed data for first-run initialization |
DEFAULT_MODEL | qwen2.5:7b | Default LLM model name |
DEFAULT_SPEAKER | rod_chilean | Default speaker accent profile |
LLM_CHAIN | qwen,claude,codex,gemini,ollama_gpu,ollama | Comma-separated provider fallback order |
QWEN_PROXY_URL | http://172.17.0.1:3301 | Qwen CLI proxy endpoint |
CLAUDE_PROXY_URL | http://172.17.0.1:3300 | Claude CLI proxy endpoint |
CODEX_PROXY_URL | http://172.17.0.1:3303 | Codex CLI proxy endpoint |
GEMINI_PROXY_URL | http://172.17.0.1:3302 | Gemini CLI proxy endpoint |
OLLAMA_GPU_URL | http://10.0.1.3:11434 | Ollama with RTX 4090 on rod-ml |
OLLAMA_URL | http://ollama:11434 | Ollama CPU fallback (GTX 1650 on rod-server) |
OLLAMA_FALLBACK_MODEL | llama3.2:3b | Small model for CPU Ollama fallback |
QDRANT_URL | http://qdrant:6333 | Qdrant vector DB for RAG (optional) |
Operations Commands
# Deploy / rebuild cd /home/rod/_rod/_transcript-correction-mcp docker compose up -d --build # View logs docker compose logs -f transcript-correction-mcp # Health check curl -s http://localhost:8000/health | python3 -m json.tool # Stop docker compose down
Data Directory Layout (/data volume)
/data/
transcripts/ # Input: raw Whisper transcript files
corrected/ # Output: markdown diff tables (pending review)
reviewed/ # Archive: accepted corrections (moved from corrected/)
correction/
phonetic_kb.json # Speaker profiles and accent substitution rules
correction_store.json # RAG store: verified correction pairs (grows over time)
prompts/
correction.txt # LLM system prompt template
8. Dependencies
Python Packages (requirements.txt)
| Package | Version | Purpose |
|---|---|---|
fastapi | >=0.110.0 | REST API framework and ASGI application |
uvicorn[standard] | >=0.27.0 | ASGI server (production runner) |
mcp[cli] | >=1.0.0 | MCP SDK with FastMCP for Streamable HTTP transport |
requests | >=2.31.0 | HTTP client for LLM provider calls |
scikit-learn | >=1.3.0 | TF-IDF vectorizer and cosine similarity for RAG retrieval |
pydantic | >=2.0.0 | Request/response validation models |
qdrant-client | >=1.7.0 | Optional Qdrant vector DB client for enhanced RAG |
External Services
| Service | Location | Required | Role |
|---|---|---|---|
| Qwen CLI Proxy | 172.17.0.1:3301 | Optional | Primary LLM provider (agent proxy pattern) |
| Claude CLI Proxy | 172.17.0.1:3300 | Optional | Second LLM fallback |
| Codex CLI Proxy | 172.17.0.1:3303 | Optional | Third LLM fallback |
| Gemini CLI Proxy | 172.17.0.1:3302 | Optional | Fourth LLM fallback |
| Ollama GPU (rod-ml) | 10.0.1.3:11434 | Optional | Fifth fallback (RTX 4090, qwen2.5:7b) |
| Ollama CPU (rod-server) | ollama:11434 | Optional | Last-resort fallback (GTX 1650, llama3.2:3b) |
| Qdrant | qdrant:6333 | Optional | Vector DB for enhanced RAG (falls back to TF-IDF) |
| Traefik | 10.0.1.6:443 | Required | Reverse proxy and TLS termination |
Degradation model: The server starts and serves requests even when all LLM providers are down.
Phonetic KB corrections still apply deterministically. RAG retrieval still works via TF-IDF.
Only the LLM-enhanced correction step is skipped (flagged as
llm_unavailable).
At least one LLM provider must be reachable for full correction quality.
9. Constraints & Risks
| Category | Item | Severity | Details |
|---|---|---|---|
| Availability | LLM provider dependency | Medium | All 6 LLM providers are external (CLI proxies on host, Ollama on rod-ml and rod-server). If all are down, corrections are KB-only (reduced quality). Agent CLI proxies require WSL to be running on rod-ml. |
| Performance | TF-IDF recomputation | Medium | TF-IDF matrix is recomputed on every retrieval call (no caching). Will degrade as the correction store grows past thousands of pairs. |
| Storage | JSON persistence | Low | Correction store and phonetic KB are stored as flat JSON files. Adequate for current scale but not suitable for concurrent writes or very large stores. |
| Scope | Two speaker profiles | Low | Currently supports rod_chilean and nikhil_indian speaker profiles. Adding new accents requires manual KB entries. |
| Legacy | ollama_client.py | Low | Legacy Ollama client module exists alongside llm_chain.py. Not imported by any current code but should be removed. |