Remove MCP server from project
Mirror and run GitLab CI / build (push) Has been cancelled
Ruff / ruff (push) Has been cancelled

- Delete mcp_server.py
- Remove MCP startup from __main__.py
- Remove process_mcp_transcribe_task from tasks.py
- Clean MCP references from README.md
This commit is contained in:
admin
2026-06-20 01:40:27 +00:00
parent 42afe111bd
commit 63832d01d3
4 changed files with 0 additions and 424 deletions
-24
View File
@@ -7,7 +7,6 @@ ScrAIbe is a transcription and summarization service that:
- Provides: - Provides:
- A web GUI for uploading audio and receiving transcripts via email. - A web GUI for uploading audio and receiving transcripts via email.
- A CLI and Python API for direct integration. - A CLI and Python API for direct integration.
- An MCP-style HTTP API (OpenAPI) for LLMs and external systems.
- A watch-folder mode for automatic transcription, summarization, and email delivery. - A watch-folder mode for automatic transcription, summarization, and email delivery.
No local speech models or heavy dependencies are required. ScrAIbe is designed as a thin client in front of your own AI services. No local speech models or heavy dependencies are required. ScrAIbe is designed as a thin client in front of your own AI services.
@@ -35,13 +34,6 @@ For more information: https://apstrom.ca
- Final transcript (MD + DOCX + JSON) when ready. - Final transcript (MD + DOCX + JSON) when ready.
- Summary as MD + DOCX (if requested). - Summary as MD + DOCX (if requested).
- Error notification if processing fails. - Error notification if processing fails.
- MCP-style HTTP API (optional):
- Exposes an OpenAPI-compliant REST endpoint for external LLMs or services.
- Allows:
- Audio upload for transcription.
- Job status checks.
- Retrieval of transcript JSON (no summary).
- Enabled via MCP_SERVER_ENABLED=true.
- Watch-folder mode (optional): - Watch-folder mode (optional):
- Monitors a directory for audio files. - Monitors a directory for audio files.
- For each file: - For each file:
@@ -81,7 +73,6 @@ For more information: https://apstrom.ca
- Output formatting (e.g., .md with transcript + summary) - Output formatting (e.g., .md with transcript + summary)
- Runs: - Runs:
- Web GUI (Gradio) always enabled - Web GUI (Gradio) always enabled
- MCP-style HTTP API (FastAPI) optional
- Watch-folder mode optional - Watch-folder mode optional
- Celery worker (async processing) - Celery worker (async processing)
- Redis (in-container by default) - Redis (in-container by default)
@@ -233,19 +224,6 @@ Accent color (UI and emails):
- Email headings, links, and email addresses - Email headings, links, and email addresses
- Default: #7C6DA0 - Default: #7C6DA0
MCP-style HTTP API:
- MCP_SERVER_ENABLED:
- Enable MCP-style HTTP API (default: false).
- Values: true/false.
- MCP_SERVER_HOST:
- Bind address (default: 0.0.0.0).
- MCP_SERVER_PORT:
- Port (default: 8000).
- MCP_USE_CELERY:
- Use Celery for async transcription (default: true).
- If false, transcription runs in-process.
Watch-folder mode: Watch-folder mode:
- WATCH_ENABLED: - WATCH_ENABLED:
@@ -355,8 +333,6 @@ Core runtime dependencies:
- celery[redis] - celery[redis]
- redis - redis
- python-docx - python-docx
- fastapi
- uvicorn
- ffmpeg (for audio preprocessing) - ffmpeg (for audio preprocessing)
No local Whisper, PyTorch, or Pyannote models are required. No local Whisper, PyTorch, or Pyannote models are required.
-31
View File
@@ -5,12 +5,9 @@ Entrypoint for running ScrAIbe as a module:
Always launches the Web GUI (Gradio). Always launches the Web GUI (Gradio).
Optionally launches: Optionally launches:
- MCP-style API server
- Watch-folder mode - Watch-folder mode
""" """
import os
import threading
import logging import logging
logger = logging.getLogger("scraibe.__main__") logger = logging.getLogger("scraibe.__main__")
@@ -18,35 +15,7 @@ logger = logging.getLogger("scraibe.__main__")
from .webui import create_app from .webui import create_app
def _run_mcp_server():
"""
Run MCP server in a separate thread.
"""
import uvicorn
from . import mcp_server
host = os.getenv("MCP_SERVER_HOST", "0.0.0.0")
port = int(os.getenv("MCP_SERVER_PORT", "8000"))
uvicorn.run(
mcp_server.app,
host=host,
port=port,
log_level="info",
)
if __name__ == "__main__": if __name__ == "__main__":
# Optionally start MCP server in background (non-blocking)
mcp_enabled = os.getenv("MCP_SERVER_ENABLED", "false").strip().lower() in ("true", "1", "yes")
if mcp_enabled:
try:
t = threading.Thread(target=_run_mcp_server, daemon=True)
t.start()
logger.info("MCP server started in background.")
except Exception as e:
logger.warning("Failed to start MCP server (WebUI will continue): %s", e)
# Optionally start watch-folder mode (non-blocking) # Optionally start watch-folder mode (non-blocking)
try: try:
from .watcher import start_watcher from .watcher import start_watcher
-304
View File
@@ -1,304 +0,0 @@
"""
MCP-style HTTP server for ScrAIbe.
- Exposes an OpenAPI-compliant endpoint for external LLMs to:
- Submit audio (as base64, URL, or internal path) via JSON.
- Receive transcript JSON (no summary).
- WebUI remains always enabled; this is additive.
Configuration (env):
- MCP_SERVER_ENABLED: "true"/"false" (default: false)
- MCP_SERVER_HOST: bind address (default: 0.0.0.0)
- MCP_SERVER_PORT: port (default: 8000)
- MCP_USE_CELERY: "true"/"false" (default: true)
- If true, uses Celery tasks; if false, runs synchronously.
"""
import os
import time
import uuid
import base64
import logging
from typing import Optional
import httpx
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from .autotranscript import Scraibe
logger = logging.getLogger("scraibe.mcp_server")
app = FastAPI(
title="ScrAIbe MCP Transcription API",
version="0.1.0",
description=(
"MCP-style HTTP API for ScrAIbe. "
"Allows external LLMs to submit audio and receive transcript JSON."
),
openapi_tags=[
{"name": "transcription", "description": "Transcription endpoints"}
],
)
# In-memory job store for MCP (simple; can be replaced with Redis later)
_mcp_jobs: dict = {}
class TranscribeRequest(BaseModel):
"""
Input for transcription.
Exactly one of audio_base64, audio_url, or audio_path must be provided.
"""
audio_base64: Optional[str] = Field(
None,
description="Base64-encoded audio file content."
)
audio_url: Optional[str] = Field(
None,
description="Public URL to the audio file."
)
audio_path: Optional[str] = Field(
None,
description="Internal file path on the server (for internal use only)."
)
class TranscribeResponse(BaseModel):
job_id: str
status: str
message: str
class JobStatusResponse(BaseModel):
job_id: str
status: str
message: str
class TranscriptJSONResponse(BaseModel):
job_id: str
transcript: str
segments: list
def _job_id() -> str:
return str(uuid.uuid4())
def _save_audio_from_request(req: TranscribeRequest) -> str:
"""
Save audio to a temporary file from base64, URL, or path.
Returns the local file path.
"""
upload_dir = os.getenv("SCRAIBE_UPLOAD_DIR", "/tmp/scraibe_uploads")
os.makedirs(upload_dir, exist_ok=True)
if req.audio_base64:
try:
data = base64.b64decode(req.audio_base64)
except Exception as e:
raise HTTPException(status_code=400, detail=f"Invalid base64 audio: {e}")
ts = time.strftime("%Y%m%d%H%M%S")
tmp_name = f"mcp_upload_{ts}_{uuid.uuid4().hex[:8]}.wav"
file_path = os.path.join(upload_dir, tmp_name)
with open(file_path, "wb") as f:
f.write(data)
return file_path
if req.audio_url:
try:
with httpx.stream("GET", req.audio_url, timeout=60) as resp:
if resp.status_code != 200:
raise HTTPException(
status_code=400,
detail=f"Failed to download audio from URL: {resp.status_code}",
)
ts = time.strftime("%Y%m%d%H%M%S")
tmp_name = f"mcp_url_{ts}_{uuid.uuid4().hex[:8]}.wav"
file_path = os.path.join(upload_dir, tmp_name)
with open(file_path, "wb") as f:
for chunk in resp.iter_bytes():
f.write(chunk)
return file_path
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=400, detail=f"Error downloading audio: {e}")
if req.audio_path:
path = req.audio_path
if not os.path.isfile(path):
raise HTTPException(status_code=400, detail="audio_path does not exist")
return path
raise HTTPException(
status_code=400,
detail="Provide exactly one of: audio_base64, audio_url, or audio_path",
)
@app.get("/health", tags=["transcription"])
async def health():
return {"status": "ok"}
@app.post(
"/transcribe",
tags=["transcription"],
operation_id="transcribe",
response_model=TranscribeResponse,
)
async def transcribe(req: TranscribeRequest):
"""
Submit an audio file for transcription.
Input (JSON body):
- audio_base64: base64-encoded audio file
- audio_url: URL to audio file
- audio_path: local file path (for internal use)
Returns:
{
"job_id": "<id>",
"status": "queued" | "processing",
"message": "..."
}
Use GET /transcribe/{job_id}/status and /json to retrieve results.
"""
use_celery = os.getenv("MCP_USE_CELERY", "true").strip().lower() in ("true", "1", "yes")
# Save audio to a temporary file
try:
file_path = _save_audio_from_request(req)
except HTTPException:
raise
except Exception as e:
logger.error("Error saving MCP upload: %s", e)
raise HTTPException(status_code=500, detail=f"Error saving file: {e}")
job_id = _job_id()
if use_celery:
try:
from .tasks import process_mcp_transcribe_task
except ImportError:
# Fallback: run synchronously
use_celery = False
if use_celery:
try:
process_mcp_transcribe_task.delay(
audio_path=file_path,
job_id=job_id,
language=None,
num_speakers=None,
)
except Exception as e:
logger.error("Error enqueuing MCP job: %s", e)
_mcp_jobs[job_id] = {
"status": "error",
"message": f"Error enqueuing job: {e}",
"file_path": file_path,
}
return TranscribeResponse(
job_id=job_id,
status="error",
message=_mcp_jobs[job_id]["message"],
)
_mcp_jobs[job_id] = {
"status": "queued",
"message": "Job queued for processing.",
"file_path": file_path,
}
return TranscribeResponse(
job_id=job_id,
status="queued",
message=_mcp_jobs[job_id]["message"],
)
# Synchronous path
_mcp_jobs[job_id] = {
"status": "processing",
"message": "Transcription started (synchronous).",
"file_path": file_path,
}
def _run_sync():
try:
scraibe = Scraibe(verbose=False)
result = scraibe.transcribe(
audio_file=file_path,
language=None,
num_speakers=None,
verbose=False,
for_export=True,
)
transcript_text = result.get("transcript", "")
segments = result.get("segments", [])
_mcp_jobs[job_id]["status"] = "completed"
_mcp_jobs[job_id]["transcript"] = transcript_text
_mcp_jobs[job_id]["segments"] = segments
_mcp_jobs[job_id]["message"] = "Transcription completed."
except Exception as e:
logger.error("MCP sync transcription error: %s", e)
_mcp_jobs[job_id]["status"] = "error"
_mcp_jobs[job_id]["message"] = f"Transcription error: {e}"
import threading
t = threading.Thread(target=_run_sync, daemon=True)
t.start()
return TranscribeResponse(
job_id=job_id,
status="processing",
message=_mcp_jobs[job_id]["message"],
)
@app.get(
"/transcribe/{job_id}/status",
tags=["transcription"],
operation_id="get_status",
response_model=JobStatusResponse,
)
async def get_status(job_id: str):
job = _mcp_jobs.get(job_id)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
return JobStatusResponse(
job_id=job_id,
status=job["status"],
message=job.get("message", ""),
)
@app.get(
"/transcribe/{job_id}/json",
tags=["transcription"],
operation_id="get_json",
response_model=TranscriptJSONResponse,
)
async def get_json(job_id: str):
job = _mcp_jobs.get(job_id)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
if job["status"] != "completed":
raise HTTPException(
status_code=400,
detail=f"Job not completed. Current status: {job['status']}",
)
transcript_text = job.get("transcript", "")
segments = job.get("segments", [])
return TranscriptJSONResponse(
job_id=job_id,
transcript=transcript_text,
segments=segments,
)
-65
View File
@@ -506,71 +506,6 @@ def process_transcription_task(
logger.info("Cleanup completed for job %s.", task_id) logger.info("Cleanup completed for job %s.", task_id)
@celery_app.task(
name="scraibe.tasks.process_mcp_transcribe_task",
bind=True,
max_retries=1,
task_time_limit=14400,
task_soft_time_limit=13500,
)
def process_mcp_transcribe_task(
self,
audio_path: str,
job_id: str,
language: str,
num_speakers: int,
):
"""
Async task used by MCP-style API:
- Transcribe audio
- Store transcript + segments in shared MCP job store
- Clean up temporary file
"""
from .mcp_server import _mcp_jobs
log_level = os.getenv("LOG_LEVEL", "INFO")
setup_logging(level=log_level)
# Initialize status
_mcp_jobs.setdefault(
job_id,
{
"status": "processing",
"message": "Transcription started (async).",
"file_path": audio_path,
},
)
try:
scraibe = Scraibe(verbose=True)
result = scraibe.transcribe(
audio_file=audio_path,
language=language or None,
num_speakers=int(num_speakers) if num_speakers else None,
verbose=True,
for_export=True,
)
transcript_text = result.get("transcript", "")
segments = result.get("segments", [])
_mcp_jobs[job_id]["status"] = "completed"
_mcp_jobs[job_id]["transcript"] = transcript_text
_mcp_jobs[job_id]["segments"] = segments
_mcp_jobs[job_id]["message"] = "Transcription completed."
logger.info("MCP job %s completed.", job_id)
except Exception as e:
logger.error("MCP job %s failed: %s", job_id, e, exc_info=True)
_mcp_jobs[job_id]["status"] = "error"
_mcp_jobs[job_id]["message"] = f"Transcription error: {e}"
finally:
_remove_file(audio_path)
logger.info("MCP job %s cleanup completed.", job_id)
@celery_app.task( @celery_app.task(
name="scraibe.tasks.process_watch_file_task", name="scraibe.tasks.process_watch_file_task",
bind=True, bind=True,