Skip to main content
Glama
thrid3v

MCP Audio Server

by thrid3v
README.md
# ๐ŸŽ™๏ธ MCP Audio Server

A **Model Context Protocol (MCP)** server that gives AI agents the ability to process audio files โ€” transcribe speech to text, detect spoken languages, and extract audio metadata. Built with [OpenAI Whisper](https://github.com/openai/whisper) and served over **Streamable HTTP** transport for seamless integration with any MCP-compatible client.

---

## โœจ Features

| Tool | Description |
|---|---|
| **`speech_to_text`** | Transcribes spoken dialogue from an audio file into structured text using Whisper |
| **`detect_audio_language`** | Analyzes the first 30 seconds of audio to predict the primary spoken language with a confidence score |
| **`get_audio_metadata`** | Extracts technical specs โ€” duration, bitrate, sample rate, channels, format, and file size via `ffprobe` |

### Highlights

- ๐Ÿง  **Thread-safe model caching** โ€” Whisper models are loaded once and reused across requests
- ๐Ÿ”’ **Strict input validation** โ€” All inputs are validated with Pydantic (file existence, extension support, model size)
- ๐Ÿ“ก **Streamable HTTP transport** โ€” served over Streamable HTTP at `127.0.0.1:8000/mcp`; binds to loopback by default, so the MCP client must be able to reach that host (clients in Docker or on another machine need extra networking โ€” see below)
- ๐ŸŽ›๏ธ **Multiple Whisper models** โ€” Choose from `tiny`, `base`, `small`, `medium`, or `large` depending on accuracy/speed tradeoff
- ๐ŸŽต **Wide format support** โ€” `.mp3`, `.wav`, `.flac`, `.m4a`, `.ogg`, `.mp4`, `.aac`

---

## ๐Ÿ“ Project Structure

```
mcp-audio-server/
โ”œโ”€โ”€ server.py              # MCP server entry point โ€” registers tools, runs Streamable HTTP transport
โ”œโ”€โ”€ audio_processor.py     # Core processing logic โ€” transcription, language detection, metadata
โ”œโ”€โ”€ models.py              # Pydantic models โ€” request validation & standardized response format
โ”œโ”€โ”€ requirements.txt       # Python dependencies
โ”œโ”€โ”€ speech-text-MCP.json   # Pre-built n8n workflow for AI agent integration
โ””โ”€โ”€ tests/
    โ””โ”€โ”€ test_models.py     # Unit tests for input validation and response serialization
```

---

## ๐Ÿ› ๏ธ Prerequisites

- **Python 3.10+**
- **ffmpeg** (required for audio metadata extraction and Whisper audio loading)
  - Windows: `winget install ffmpeg` or download from [ffmpeg.org](https://ffmpeg.org/download.html)
  - macOS: `brew install ffmpeg`
  - Linux: `sudo apt install ffmpeg`
- **GPU (optional)** โ€” Whisper will use CUDA if available, otherwise falls back to CPU

---

## ๐Ÿš€ Getting Started

### 1. Clone the repository

```bash
git clone https://github.com/<your-username>/mcp-audio-server.git
cd mcp-audio-server
```

### 2. Create a virtual environment and install dependencies

Using `uv` (recommended):

```bash
uv venv
uv pip install -r requirements.txt
```

Or with standard `pip`:

```bash
python -m venv .venv

# Windows
.venv\Scripts\activate

# macOS / Linux
source .venv/bin/activate

pip install -r requirements.txt
```

### 3. Start the server

```bash
python server.py
```

The server starts on **`http://127.0.0.1:8000`** with the following endpoint:

| Endpoint | Purpose |
|---|---|
| `http://127.0.0.1:8000/mcp` | Streamable HTTP endpoint for MCP clients |

> **Note:** The server binds to `127.0.0.1` (loopback) and maintains per-session state over Streamable HTTP (it issues an `Mcp-Session-Id`), so it is only reachable from the **same machine**. If your MCP client runs elsewhere โ€” n8n in Docker, or a different host โ€” it must connect to an address that can reach this machine, not `127.0.0.1`. For Docker on the same machine, use `http://host.docker.internal:8000/mcp`.

---

## ๐Ÿงช Testing

### MCP Inspector

The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is the easiest way to test the server interactively:

```bash
npx @modelcontextprotocol/inspector
```

1. Open the Inspector UI in your browser
2. Set **Transport Type** โ†’ `Streamable HTTP`
3. Set **URL** โ†’ `http://127.0.0.1:8000/mcp`
4. Click **Connect**
5. Select any tool and provide an absolute path to an audio file

### Unit Tests

```bash
pytest tests/ -v
```

---

## ๐Ÿ”Œ Integration

### n8n Workflow

A pre-built [n8n](https://n8n.io) workflow is included in [`speech-text-MCP.json`](speech-text-MCP.json). It sets up a complete AI agent pipeline:

```
Chat Trigger โ†’ AI Agent โ†’ Google Gemini LLM
                  โ†•              โ†•
            MCP Client     Buffer Memory
        (this server)
```

**To import:**
1. Start n8n (`npx n8n`)
2. Go to **Workflows** โ†’ **Import from File**
3. Select `speech-text-MCP.json`
4. Configure your Google Gemini API credentials in the **Google Gemini Chat Model** node
5. Ensure this MCP server is running and reachable from n8n. If n8n runs natively on the same machine, the bundled `http://127.0.0.1:8000/mcp` endpoint works as-is. **If n8n runs in Docker, edit the MCP Client node to use `http://host.docker.internal:8000/mcp`** โ€” inside the container, `127.0.0.1` points at the container itself, not your host.
6. Activate the workflow and start chatting โ€” the AI agent can now transcribe audio, detect languages, and extract metadata on demand

> **Note on chat models:** The agent can only invoke these MCP tools if its chat model supports **function/tool calling**. The bundled workflow uses Google Gemini, which does. If you swap in another model (e.g. via OpenRouter), pick one that advertises tool/function-calling support โ€” many free or base models don't, and the agent will silently answer without ever calling the tools.

### Claude Desktop

Add to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "audio-server": {
      "url": "http://127.0.0.1:8000/mcp"
    }
  }
}
```

### Any MCP Client

Connect to `http://127.0.0.1:8000/mcp` using any MCP-compatible client that supports Streamable HTTP, provided the client can reach the server's host (the server binds to loopback by default โ€” see the note under [Start the server](#3-start-the-server)). The server exposes three tools that are automatically discoverable through the MCP protocol.

---

## ๐Ÿ“– API Reference

### `speech_to_text`

Transcribes audio to text using OpenAI Whisper.

**Parameters:**

| Parameter | Type | Default | Description |
|---|---|---|---|
| `audio_path` | `string` | *required* | Absolute path to the audio file |
| `model_size` | `string` | `"base"` | Whisper model variant: `tiny`, `base`, `small`, `medium`, `large` |

**Response:**
```json
{
  "status": "success",
  "data": {
    "text": "The transcribed text content...",
    "language": "en"
  }
}
```

---

### `detect_audio_language`

Identifies the spoken language from the first 30 seconds of audio.

**Parameters:**

| Parameter | Type | Default | Description |
|---|---|---|---|
| `audio_path` | `string` | *required* | Absolute path to the audio file |

**Response:**
```json
{
  "status": "success",
  "data": {
    "detected_language": "en",
    "confidence_score": 0.9847
  }
}
```

---

### `get_audio_metadata`

Extracts technical metadata using `ffprobe`.

**Parameters:**

| Parameter | Type | Default | Description |
|---|---|---|---|
| `audio_path` | `string` | *required* | Absolute path to the audio file |

**Response:**
```json
{
  "status": "success",
  "data": {
    "format_name": "mp3",
    "duration_seconds": 245.67,
    "size_bytes": 3932160,
    "bit_rate": "128000",
    "sample_rate": "44100",
    "channels": 2
  }
}
```

---

### Error Response

All tools return a standardized error format on failure:

```json
{
  "status": "error",
  "message": "Validation failed: The path '/bad/path.mp3' does not exist on this machine."
}
```

---

## โš™๏ธ Architecture

```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     MCP Client                          โ”‚
โ”‚         (Claude, n8n, Inspector, etc.)                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ”‚ Streamable HTTP
                       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  server.py โ€” FastMCP Server                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ speech_to_text โ”‚ detect_language   โ”‚ get_metadata   โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚          โ”‚                 โ”‚                 โ”‚            โ”‚
โ”‚          โ–ผ                 โ–ผ                 โ–ผ            โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚  models.py โ€” Pydantic Validation Layer            โ”‚   โ”‚
โ”‚  โ”‚  (AudioPathMixin, TranscriptionRequest, etc.)     โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                          โ–ผ                               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚  audio_processor.py โ€” Processing Engine           โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚   Whisper    โ”‚  โ”‚  Whisper   โ”‚  โ”‚  ffprobe   โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ transcribe() โ”‚  โ”‚ detect()  โ”‚  โ”‚  metadata  โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

---

## ๐Ÿ“ License

This project is open source. See [LICENSE](LICENSE) for details.