jev-av-analysis-mcp
# jev-av-analysis-mcp
> π¨π³ [δΈζζζ‘£](README.zh-CN.md)
> Local-first, zero-key structured audio/video analysis: **download β transcribe β decide** in one pipeline.
> Transcription runs on your own machine with yt-dlp + whisper.cpp; analysis uses [TypeSafe Jev](https://typesafe.ai) (System One decision model) to emit **structured labels**, not prose.
An MCP server that turns any video/audio into "transcript + structured analysis": topic tags, sentiment tone,
sensitivity flags (profanity / violence / medical advice / financial advice / politics / copyrighted music / harassment), and a **publish gate**
(publish / review / block). Built for podcasters, video creators, YouTubers β anyone who wants an agent to auto-tag and screen their content.
```
npm install && npm run build
node dist/index.js doctor # print transcription deps + Jev mode diagnostics
```
Connect `node dist/index.js` in your MCP client and use the four tools below.
---
## The four tools
| Tool | Purpose |
|---|---|
| `transcribe_media` | URL or local file β transcript (URL via yt-dlp, local/downloaded via whisper.cpp) |
| `fetch_media` | Use yt-dlp to pull the best audio track of a video/audio to local; returns path + title + duration |
| `transcribe_file` | Local audio/video file β transcript |
| `analyze_transcript` | Transcript β Jev structured analysis (see below) |
`analyze_transcript` takes a piece of text (usually from the three transcription tools above) and returns:
```json
{
"topics": { "tech": 0.9, "business": 0.1, "education": 0.3, "...": 0.0 },
"dominant_topics": ["tech"],
"sentiment": { "score": 3, "label": "positive", "confidence": 0.8, "probabilities": {"0":0,"1":0,"2":0.1,"3":0.8,"4":0.1} },
"sensitivity": { "profanity": 0.1, "violence": 0.0, "medical_advice": 0.2, "financial_advice": 0.9, "...": 0.0 },
"flagged_sensitivity": ["financial_advice"],
"has_key_claims": 0.8,
"has_engagement_hook": 0.4,
"publish_gate": "review",
"confidence": 0.8,
"mocked": false
}
```
**Fields**
| Field | Meaning |
|---|---|
| `topics` / `dominant_topics` | Membership probabilities over 8 default topics (>0.5 β dominant); override via `categories` |
| `sentiment` | 5-level ordinal: very_negative β very_positive, with distribution and confidence |
| `sensitivity` / `flagged_sensitivity` | Probabilities over 7 sensitivity classes; >0.7 β flagged |
| `has_key_claims` | Whether it contains factual claims that need verification (noul probability) |
| `has_engagement_hook` | Whether the opening has a strong hook (good for short-form clips) |
| `publish_gate` | `publish` / `review` / `block` (see gate logic) |
| `confidence` | Calibrated confidence of the sentiment dimension |
**publish_gate gate logic**
- `publish`: no sensitivity class > 0.7
- `review`: a sensitivity class in 0.7β0.9 β human review before publishing
- `block`: any sensitivity class > 0.9 β high risk, block
> β οΈ **Capability boundary (important)**: Jev is a decision model β it **labels / scores**, it does not write section headings or summaries.
> Do chapter segmentation and summarization yourself based on the transcript `segments` (or plug in a separate generative model). Before any publishing action,
> check `publish_gate` + `confidence` first β **do not** auto-publish just because one probability is high.
---
## Zero-key / local-first
- **Transcription layer**: `MEDIA_MCP_MOCK=1` β skip yt-dlp/whisper and return an offline stub (zero-config for CI/demos).
Real mode requires `yt-dlp` and a compiled `whisper.cpp` on your machine, plus `WHISPER_MODEL_PATH` configured.
- **Decision layer**: no `TYPESAFE_API_KEY` β automatic Jev mock mode (deterministic offline stub); set it β call the real API.
**Environment variables**
| Variable | Default | Description |
|---|---|---|
| `TYPESAFE_API_KEY` | β | Required for real Jev calls; blank falls back to mock |
| `JEV_MCP_MOCK` | `0` | `1` forces Jev mock |
| `MEDIA_MCP_MOCK` | `0` | `1` skips transcription binaries |
| `YT_DLP_BIN` / `WHISPER_BIN` | `yt-dlp` / `whisper-cli` | Binary paths |
| `WHISPER_MODEL_PATH` | β | Absolute path to the `.ggml` model file |
| `WHISPER_MODEL` | `base.en` | Model name hint |
| `MEDIA_MCP_TIMEOUT_MS` | `600000` | Transcription timeout |
**Install engines locally (real transcription)**
```bash
pip install yt-dlp
git clone https://github.com/ggerganov/whisper.cpp && cd whisper.cpp && make
# download a small model, e.g.:
bash ./models/download-ggml-model.sh base.en
export WHISPER_MODEL_PATH="$PWD/models/ggml-base.en.bin"
```
---
## Extremely low cost
Transcription runs on your own machine (CPU/GPU, zero call fees); decisions are a single Jev call, tens of thousands of tokens per call.
There's no token-usage chart because it's already cheap β think of it as "one cheap gate in your content pipeline".
---
## Tests
```bash
npm test # smoke + MCP protocol handshake (dual mock mode, no key needed)
```
---
## License
MIT
TDQS
Scored across 4 tools
transcribe_media and transcribe_file overlap for local files, and transcribe_media already handles URL fetching that fetch_media also provides. However, the descriptions make each tool's primary role fairly clear: fetch_media is download-only, transcribe_file is local-only, and transcribe_media is the all-in-one path.
All tool names follow a consistent verb_noun snake_case pattern: transcribe_media, fetch_media, transcribe_file, analyze_transcript. The naming is predictable and easy to navigate.
Four tools is a reasonable size for this audio/video analysis pipeline. The count is only slightly higher than necessary because transcribe_file is mostly redundant with transcribe_media, but it is not bloated.
The server covers the full workflow: fetch media, transcribe it, and analyze the transcript with the Jev model. There are no obvious missing operations for the stated AV-analysis purpose.