Erweiterung: Stop-Mechanismus, REST-Service und MCP-Adapter
- chatterbox_cli_v4.py: kooperativer Stop-Mechanismus via threading.Event (STOP_REQUESTED, request_stop, clear_stop); PlaybackWorker, synthesize_non_streaming und synthesize_streaming prüfen das Event vor jedem Chunk; --stop CLI-Flag - tts_service.py: FastAPI-Service mit Modell-Caching, Job-Queue und Worker-Thread; Endpunkte: POST /speak, POST /stop, GET /health, GET /status, GET /voices - mcp_adapter.py: MCP-Adapter (stdio/streamable-http) über tts_service; Tools: speak, stop, get_status, list_voices - requirements.txt: fastapi, uvicorn, httpx, mcp ergänzt - CLAUDE.md: Architektur und Startbefehle dokumentiert - .gitignore: Ideen/-Verzeichnis ausgeschlossen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bed29fb1c8
commit
bcf6374c29
6 changed files with 563 additions and 3 deletions
134
mcp_adapter.py
Normal file
134
mcp_adapter.py
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Chatterbox TTS – MCP-Adapter
|
||||
|
||||
Setzt einen laufenden tts_service.py voraus (Standard: http://127.0.0.1:8000).
|
||||
|
||||
Start (streamable-http, Port 8001 – für beliebige MCP-Clients):
|
||||
python mcp_adapter.py
|
||||
|
||||
Start (stdio – für Claude Code / Claude Desktop):
|
||||
python mcp_adapter.py --stdio
|
||||
|
||||
Claude Code Konfiguration (.claude/settings.json):
|
||||
{
|
||||
"mcpServers": {
|
||||
"chatterbox-tts": {
|
||||
"command": "python",
|
||||
"args": ["/home/dschlueter/chatterbox-tts-cli/mcp_adapter.py", "--stdio"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Umgebungsvariable TTS_URL überschreibt die Service-Adresse:
|
||||
TTS_URL=http://192.168.1.10:8000 python mcp_adapter.py --stdio
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
import httpx
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
TTS_URL = os.environ.get("TTS_URL", "http://127.0.0.1:8000").rstrip("/")
|
||||
|
||||
mcp = FastMCP(
|
||||
"Chatterbox TTS",
|
||||
instructions=(
|
||||
"Lokaler Text-to-Speech-Service. Liest Texte auf Deutsch und 20+ weiteren "
|
||||
"Sprachen vor. Unterstützt Voice Cloning, Geschwindigkeitsanpassung und "
|
||||
"Aussprache-Wörterbücher."
|
||||
),
|
||||
port=8001,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tools
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@mcp.tool()
|
||||
async def speak(
|
||||
text: str,
|
||||
lang: str = "de",
|
||||
voice: str | None = None,
|
||||
interrupt: bool = False,
|
||||
speed: float = 1.0,
|
||||
) -> dict:
|
||||
"""Text als Sprache ausgeben.
|
||||
|
||||
Reiht den Text in die Ausgabewarteschlange ein. Das Modell generiert
|
||||
satzweise und beginnt sofort mit der Wiedergabe.
|
||||
|
||||
Args:
|
||||
text: Auszugebender Text (max. 4000 Zeichen).
|
||||
lang: Sprachcode, z. B. 'de', 'en', 'fr'. Standard: 'de'.
|
||||
voice: Optionaler Pfad zu einer WAV-Referenzdatei (10–30s) für
|
||||
Voice Cloning.
|
||||
interrupt: True = laufende Ausgabe sofort unterbrechen und diesen
|
||||
Text vorgezogen abspielen.
|
||||
speed: Wiedergabegeschwindigkeit (0.5–2.0). Pitch bleibt gleich.
|
||||
"""
|
||||
async with httpx.AsyncClient(timeout=15) as client:
|
||||
r = await client.post(f"{TTS_URL}/speak", json={
|
||||
"text": text,
|
||||
"lang": lang,
|
||||
"voice": voice,
|
||||
"interrupt": interrupt,
|
||||
"speed": speed,
|
||||
})
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def stop() -> dict:
|
||||
"""Laufende Sprachausgabe sofort stoppen und Warteschlange leeren."""
|
||||
async with httpx.AsyncClient(timeout=5) as client:
|
||||
r = await client.post(f"{TTS_URL}/stop")
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_status() -> dict:
|
||||
"""Aktuellen Ausgabe-Status abfragen.
|
||||
|
||||
Gibt zurück: laufender Job (mit Chunk-Fortschritt), Queue-Länge und
|
||||
die letzten abgeschlossenen Jobs.
|
||||
"""
|
||||
async with httpx.AsyncClient(timeout=5) as client:
|
||||
r = await client.get(f"{TTS_URL}/status")
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def list_voices() -> dict:
|
||||
"""Unterstützte Sprachen und Hinweise zu Voice Cloning abfragen."""
|
||||
async with httpx.AsyncClient(timeout=5) as client:
|
||||
r = await client.get(f"{TTS_URL}/voices")
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Einstiegspunkt
|
||||
# ---------------------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Chatterbox TTS MCP-Adapter")
|
||||
parser.add_argument(
|
||||
"--stdio", action="store_true",
|
||||
help="stdio-Transport (für Claude Code / Claude Desktop)",
|
||||
)
|
||||
parser.add_argument("--host", default="127.0.0.1",
|
||||
help="Host für streamable-http (Standard: 127.0.0.1)")
|
||||
parser.add_argument("--port", type=int, default=8001,
|
||||
help="Port für streamable-http (Standard: 8001)")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.stdio:
|
||||
mcp.run() # stdio ist der Default-Transport
|
||||
else:
|
||||
mcp.run(transport="streamable-http", host=args.host, port=args.port)
|
||||
Loading…
Add table
Add a link
Reference in a new issue