vmaf-mcp 1.0.0rc1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. vmaf_mcp-1.0.0rc1/.gitignore +233 -0
  2. vmaf_mcp-1.0.0rc1/Dockerfile +58 -0
  3. vmaf_mcp-1.0.0rc1/PKG-INFO +116 -0
  4. vmaf_mcp-1.0.0rc1/README.md +78 -0
  5. vmaf_mcp-1.0.0rc1/claude-desktop-config-example.json +21 -0
  6. vmaf_mcp-1.0.0rc1/pyproject.toml +95 -0
  7. vmaf_mcp-1.0.0rc1/requirements-dev-lock.txt +1597 -0
  8. vmaf_mcp-1.0.0rc1/requirements-production-lock.txt +1538 -0
  9. vmaf_mcp-1.0.0rc1/requirements-runtime-lock.txt +521 -0
  10. vmaf_mcp-1.0.0rc1/src/vmaf_mcp/__init__.py +18 -0
  11. vmaf_mcp-1.0.0rc1/src/vmaf_mcp/http_scoring.py +66 -0
  12. vmaf_mcp-1.0.0rc1/src/vmaf_mcp/http_transport.py +873 -0
  13. vmaf_mcp-1.0.0rc1/src/vmaf_mcp/server.py +4572 -0
  14. vmaf_mcp-1.0.0rc1/tests/test_backend_dispatch.py +123 -0
  15. vmaf_mcp-1.0.0rc1/tests/test_backend_probe_and_allowlist_0511.py +268 -0
  16. vmaf_mcp-1.0.0rc1/tests/test_coverage_round2.py +1393 -0
  17. vmaf_mcp-1.0.0rc1/tests/test_coverage_round3.py +1264 -0
  18. vmaf_mcp-1.0.0rc1/tests/test_coverage_round4.py +1295 -0
  19. vmaf_mcp-1.0.0rc1/tests/test_coverage_round6.py +762 -0
  20. vmaf_mcp-1.0.0rc1/tests/test_http_transport.py +584 -0
  21. vmaf_mcp-1.0.0rc1/tests/test_http_transport_round5.py +655 -0
  22. vmaf_mcp-1.0.0rc1/tests/test_import_graph.py +144 -0
  23. vmaf_mcp-1.0.0rc1/tests/test_iserror_invariant.py +680 -0
  24. vmaf_mcp-1.0.0rc1/tests/test_mcp2_registration.py +99 -0
  25. vmaf_mcp-1.0.0rc1/tests/test_mcp_hardening_wave1.py +433 -0
  26. vmaf_mcp-1.0.0rc1/tests/test_mcp_http_edge_cases_adr1075.py +328 -0
  27. vmaf_mcp-1.0.0rc1/tests/test_mcp_p0_adr0608.py +339 -0
  28. vmaf_mcp-1.0.0rc1/tests/test_p1_tools.py +559 -0
  29. vmaf_mcp-1.0.0rc1/tests/test_parity_argv.py +201 -0
  30. vmaf_mcp-1.0.0rc1/tests/test_path_and_bench_env.py +237 -0
  31. vmaf_mcp-1.0.0rc1/tests/test_probe_backend_pr850.py +106 -0
  32. vmaf_mcp-1.0.0rc1/tests/test_probe_findings_2026_05_17.py +318 -0
  33. vmaf_mcp-1.0.0rc1/tests/test_pytest_pythonpath.py +81 -0
  34. vmaf_mcp-1.0.0rc1/tests/test_python_surfaces_bug_audit.py +183 -0
  35. vmaf_mcp-1.0.0rc1/tests/test_score_extras_adr1117.py +448 -0
  36. vmaf_mcp-1.0.0rc1/tests/test_server.py +586 -0
  37. vmaf_mcp-1.0.0rc1/tests/test_sidecar_tools_1240.py +406 -0
  38. vmaf_mcp-1.0.0rc1/tests/test_smoke_e2e.py +220 -0
@@ -0,0 +1,233 @@
1
+ # Caches, build artifacts
2
+ .DS_Store
3
+ .cache/
4
+ # Rust build artifacts
5
+ target/
6
+ # Editor / refactor scratch backups — never in tree (ADR-0546 audit cleanup
7
+ # bundle surfaced a 142 KB `tools/vmaf-tune/src/vmaftune/cli.py.bak` that
8
+ # had been polluting greps for weeks).
9
+ *.bak
10
+ *.orig
11
+ *.egg*
12
+ .idea/
13
+ *.py[cod]
14
+ .tox/
15
+ .*version
16
+ # Virtual environments. No trailing slash on the second pattern: `.venv*/`
17
+ # matches only DIRECTORIES, so a symlink named `.venv` sailed past it and was
18
+ # committed by #1231. That symlink pointed at the maintainer's own absolute
19
+ # venv path, so every fresh checkout materialised a self-referential loop and
20
+ # `git pull` replaced anyone's real (ignored, hence disposable) venv with it.
21
+ .venv*/
22
+ .venv*
23
+ __pycache__/
24
+ build/
25
+ build-golden/
26
+ core/build-golden/
27
+ dist/
28
+ vmaf_output.xml
29
+ compile_commands.json
30
+
31
+ # tiny-AI training artifacts (re-generated by ai/scripts/train_konvid.py).
32
+ # The contents of ai/data/ are gitignored EXCEPT the ADR-0203 Python
33
+ # modules. We use the "ignore everything inside the dir, then re-allow
34
+ # .py" pattern so the dir itself stays tracked for the modules below.
35
+ ai/data/*
36
+ !ai/data/__init__.py
37
+ !ai/data/netflix_loader.py
38
+ !ai/data/feature_extractor.py
39
+ !ai/data/scores.py
40
+ # Phase F.5 calibrated recipe overrides (ADR-0325).
41
+ # Tracked as a tiny JSON snapshot — regenerated by
42
+ # ai/scripts/calibrate_phase_f_recipes.py against a real corpus.
43
+ !ai/data/phase_f_recipes_calibrated.json
44
+ # ADR-0335 — hardware-capability prior table is small, hand-curated,
45
+ # and committed in-tree (not a generated artifact).
46
+ !ai/data/hardware_caps.csv
47
+ runs/
48
+
49
+ # Large test data (generated via testdata/generate.sh)
50
+ testdata/*_1280x720_*.yuv
51
+ testdata/*_1920x1080_*.yuv
52
+ testdata/*_3840x2160_*.yuv
53
+ testdata/bbb/*.yuv
54
+
55
+ # Large binary files
56
+ *.mp4
57
+ *.mkv
58
+
59
+ # Upstream MATLAB MEX compiled artefacts (platform-specific binaries).
60
+ # Rebuild locally via `mex file.c` if you need the MATLAB reference path.
61
+ # Paths follow ADR-0700 rename (python/vmaf/ → compat/python-vmaf/).
62
+ # See Scorecard Binary-Artifacts / D38.
63
+ compat/python-vmaf/matlab/**/*.mex
64
+ compat/python-vmaf/matlab/**/*.mex[0-9]*
65
+ compat/python-vmaf/matlab/**/*.mexa64
66
+ compat/python-vmaf/matlab/**/*.mexglx
67
+ compat/python-vmaf/matlab/**/*.mexlx
68
+ compat/python-vmaf/matlab/**/*.mexmac
69
+ compat/python-vmaf/matlab/**/*.mexmaci64
70
+ compat/python-vmaf/matlab/**/*.mexsol
71
+ compat/python-vmaf/matlab/**/*.mexw32
72
+ compat/python-vmaf/matlab/**/*.mexw64
73
+ compat/python-vmaf/matlab/**/*.dll
74
+ compat/python-vmaf/matlab/**/*.exp
75
+ compat/python-vmaf/matlab/**/*.lib
76
+ compat/python-vmaf/matlab/**/*.o
77
+
78
+ # Cython-generated C from compat/python-vmaf/core/adm_dwt2_cy.pyx
79
+ # (produced by `python setup.py build_ext`; never tracked).
80
+ compat/python-vmaf/core/adm_dwt2_cy.c
81
+ compat/python-vmaf/core/adm_dwt2_cy.cpp
82
+
83
+ # Local corpus data — large YUV/MP4 fixtures, never committed.
84
+ # Was only in .git/info/exclude; now visible to all contributors.
85
+ .corpus/
86
+
87
+ # Working/scratch directories
88
+ .workingdir/
89
+ # Also match it as a symlink. A trailing slash only matches a real
90
+ # directory, so an agent worktree that symlinks this shared state dir
91
+ # in (the praetor flavor audit wants them present) leaves an untracked
92
+ # entry that `git add -A` happily commits -- an absolute path to one
93
+ # machine, landing in a PR. Caught exactly that way; see bug ledger L-78.
94
+ .workingdir
95
+
96
+ # vmaf-tune Phase A scratch outputs (ADR-0237)
97
+ corpus.jsonl
98
+ # ...but never a committed test fixture. The bare pattern above matches at any
99
+ # depth, which silently excluded pkg/benchmark/testdata/corpus.jsonl from its
100
+ # own PR and left the package's tests failing on a missing file.
101
+ !**/testdata/corpus.jsonl
102
+ tools/vmaf-tune/build/
103
+ tools/vmaf-tune/dist/
104
+ tools/vmaf-tune/*.egg-info/
105
+
106
+ # external-bench harness scratch / binary downloads (ADR-0332)
107
+ tools/external-bench/**/build/
108
+ tools/external-bench/**/dist/
109
+ tools/external-bench/**/*.bin
110
+ tools/external-bench/**/output*.json
111
+ tools/external-bench/**/__pycache__/
112
+
113
+ # Zed per-user local settings (auth tokens, window state — machine-specific).
114
+ # .zed/ itself IS tracked (shared project config); only the local/ subdir is not.
115
+ .zed/local/
116
+
117
+ # Claude Code local (machine-specific) settings
118
+ .claude/settings.local.json
119
+ .claude/scheduled_tasks.lock
120
+ # Agent isolation worktrees (created by parallel-agent harness; machine-local).
121
+ .claude/worktrees/
122
+ # Local merge-train nudger + its log (machine-local operator tooling).
123
+ .claude/mergetrain/
124
+
125
+ # Generated benchmark results
126
+ testdata/bbb/results/
127
+ build-docs/
128
+ preprints202604.0035.v1.pdf
129
+
130
+ # Meson wrap-fetched subprojects (downloaded at meson setup time).
131
+ # Only the .wrap files + packagefiles/ are tracked; the unpacked
132
+ # upstream trees are gitignored.
133
+ core/subprojects/*/
134
+ !core/subprojects/packagefiles/
135
+ core/subprojects/packagecache/
136
+ core/subprojects/.wraplock
137
+
138
+ # Per-run training output (per-epoch ONNX checkpoints)
139
+ model/tiny/training_runs/
140
+
141
+ # u2netp fork-local mirror (ADR-0325). Binary lives only in GitHub
142
+ # Releases; the scaffold (license, model card, operator doc, release
143
+ # workflow guard) is tracked, the binary itself is not.
144
+ model/u2netp_mirror.onnx
145
+ model/u2netp_mirror.pth
146
+
147
+ # Go build artifacts (ADR-0702)
148
+ # Compiled binaries at the repo root (from `go build ./cmd/...`).
149
+ # Anchored with a leading "/" so the patterns match only files at the repo
150
+ # root, not directories named "vmafx-tune" anywhere in the tree (e.g.
151
+ # cmd/vmafx-tune/). Leading slash also prevents hiding new test files.
152
+ /vmafx-server
153
+ /vmafx-mcp
154
+ /vmafx-tune
155
+ /vmafx-controller
156
+ /vmafx-node
157
+ /vmafx-operator
158
+ /vmafx-ort-runner
159
+ # Per-package test binaries
160
+ *.test
161
+ # Go build cache is stored in $GOPATH/pkg, not the repo; go.sum is tracked.
162
+ # Dist directories from per-cmd Makefile targets
163
+ /cmd/*/dist/
164
+
165
+ # Rust build artifacts (ADR-0702)
166
+ # Workspace target directory (cargo output)
167
+ /target/
168
+ # Rust source backup files (rustfmt temp)
169
+ **/*.rs.bk
170
+ # Cargo.lock: kept for workspace binaries (this workspace's root Cargo.lock
171
+ # is tracked); per-crate library Cargo.lock files are gitignored below.
172
+ # Library crates inside bindings/ produce their own Cargo.lock which we
173
+ # do NOT track (libraries let consumers pin).
174
+ bindings/rust/**/Cargo.lock
175
+
176
+ # Nsight Compute profiling artifacts
177
+ *.ncu-rep
178
+
179
+ # Generated upscaled perf benchmark fixtures (reproducible from 576x324 native)
180
+ testdata/ref_1920x1080_48f.yuv
181
+ testdata/dis_1920x1080_48f.yuv
182
+ testdata/ref_2560x1440_48f.yuv
183
+ testdata/dis_2560x1440_48f.yuv
184
+ testdata/bbb/ref_3840x2160_30f.yuv
185
+ testdata/bbb/dis_3840x2160_30f.yuv
186
+
187
+ # Geometry-bearing wrappers generated from committed Kubernetes E2E raw clips.
188
+ test/e2e/fixtures/ref.y4m
189
+ test/e2e/fixtures/dist.y4m
190
+ # kuttl may materialize a credential-bearing kubeconfig in its working
191
+ # directory; the harness uses only the dedicated file below RUNNER_TEMP.
192
+ /kubeconfig
193
+
194
+ # Pyright strict-audit config (ADR-0888). Intentionally local-only —
195
+ # shipping it would surface ~1,600 stub-cascade errors as a CI gate
196
+ # before the long-tail cleanup is done. Operators re-run with
197
+ # `pyright -p pyrightconfig.audit.json --outputjson <pkg>/`.
198
+ pyrightconfig.audit.json
199
+
200
+ # Fuzzer-generated corpus growth (seed entries are not tracked)
201
+ core/test/fuzz/*_corpus/
202
+
203
+ # Netflix golden-data YUV fixtures (CLAUDE.md section 8). Downloaded by
204
+ # scripts/test/fetch-test-yuvs.sh, deliberately untracked because of their size
205
+ # -- but they are the numerical ground truth, so they must never be reachable by
206
+ # `git clean -xfd`. Ignoring them makes clean skip them and keeps them out of
207
+ # `git status` noise.
208
+ python/test/resource/yuv/
209
+ python/test/resource/test_image_yuv/
210
+
211
+ # Cython extension built in-place by compat/python-vmaf (setup.py build_ext).
212
+ compat/python-vmaf/core/*.so
213
+
214
+ # Per-host sidecar identity cache, written at runtime by vmafx-tune sidecar.
215
+ # Machine-local state, never a build input (ADR-0237-style scratch).
216
+ relcache/
217
+
218
+ # Test artifact: vmaftune.encode probes ffmpeg with `[ffmpeg_bin, "-version"]`,
219
+ # and a stubbed binary in the test suite leaves a file literally named
220
+ # `-version` behind. A leading dash also makes `pre-commit run --files` parse
221
+ # the path as an option, so the large-file hook never sees it.
222
+ /tools/vmaf-tune/-version
223
+ build-upstream-ab/
224
+
225
+ # local-only agent scratch (bench fixtures, fan-out logs, tmp scratch)
226
+ .claude/bench-data/
227
+ .claude/fanout/
228
+ .claude/tmpsp/
229
+
230
+ # Vendor skill mirrors. `.claude/skills/` is canonical and tracked; the
231
+ # per-vendor copies drift and are regenerated from it.
232
+ .agents/skills/
233
+ /.standards/worktrees/
@@ -0,0 +1,58 @@
1
+ # syntax=docker/dockerfile:1.27@sha256:bde3983e9c939224420ddaf6b784cc30e09b035a4dea01f581230c50809f372e
2
+ #
3
+ # Sandboxed vmaf-mcp server — MCP over stdio + pre-built libvmaf binary.
4
+ #
5
+ # Build from the repo root so the build context has libvmaf and mcp-server/:
6
+ # docker build -f mcp-server/vmaf-mcp/Dockerfile -t vmaf-mcp .
7
+ #
8
+ # Run (mount your YUV corpus read-only, keep stdio open for MCP):
9
+ # docker run --rm -i \
10
+ # -v /path/to/yuv-corpus:/data:ro \
11
+ # -e VMAF_MCP_ALLOW=/data \
12
+ # vmaf-mcp
13
+
14
+ FROM ubuntu:26.04@sha256:61ebaa5cc23ca45450db85eac015435199ec569e28ec222ea13f2aed2110b8a6 AS build
15
+
16
+ ENV DEBIAN_FRONTEND=noninteractive
17
+ # hadolint ignore=DL3008
18
+ RUN apt-get update && apt-get install -y --no-install-recommends \
19
+ build-essential meson ninja-build pkg-config nasm \
20
+ python3 python3-pip python3-venv \
21
+ ca-certificates git \
22
+ && rm -rf /var/lib/apt/lists/*
23
+
24
+ WORKDIR /src
25
+ COPY . /src
26
+ WORKDIR /src/libvmaf
27
+ RUN meson setup ../build -Denable_cuda=false -Denable_sycl=false --buildtype=release \
28
+ && ninja -C ../build
29
+ WORKDIR /src
30
+ RUN python3 -m pip install --no-cache-dir --break-system-packages --require-hashes \
31
+ -r requirements/locks/package-build.txt \
32
+ && python3 -m build --no-isolation --wheel --outdir /wheels mcp-server/vmaf-mcp
33
+
34
+ FROM ubuntu:26.04@sha256:61ebaa5cc23ca45450db85eac015435199ec569e28ec222ea13f2aed2110b8a6
35
+
36
+ ENV DEBIAN_FRONTEND=noninteractive \
37
+ VMAF_BIN=/opt/vmaf/vmaf \
38
+ PATH=/opt/vmaf:$PATH
39
+
40
+ # hadolint ignore=DL3008
41
+ RUN apt-get update && apt-get install -y --no-install-recommends \
42
+ python3 python3-pip python3-venv \
43
+ ca-certificates \
44
+ && rm -rf /var/lib/apt/lists/*
45
+
46
+ COPY --from=build /src/build/tools/vmaf /opt/vmaf/vmaf
47
+ COPY --from=build /wheels/vmaf_mcp-*.whl /tmp/
48
+ COPY --from=build /src/mcp-server/vmaf-mcp/requirements-runtime-lock.txt /tmp/
49
+ COPY --from=build /src/model /opt/vmaf/model
50
+ COPY --from=build /src/testdata /opt/vmaf/testdata
51
+
52
+ RUN pip install --no-cache-dir --break-system-packages --require-hashes \
53
+ -r /tmp/requirements-runtime-lock.txt \
54
+ && pip install --no-cache-dir --break-system-packages --no-deps \
55
+ /tmp/vmaf_mcp-*.whl
56
+
57
+ WORKDIR /opt/vmaf
58
+ ENTRYPOINT ["vmaf-mcp"]
@@ -0,0 +1,116 @@
1
+ Metadata-Version: 2.5
2
+ Name: vmaf-mcp
3
+ Version: 1.0.0-rc.1
4
+ Summary: MCP server exposing VMAF scoring, model listing, and benchmark runs via JSON-RPC.
5
+ Project-URL: Homepage, https://github.com/VMAFx/vmafx
6
+ Project-URL: Repository, https://github.com/VMAFx/vmafx
7
+ Project-URL: Documentation, https://vmafx.github.io/vmafx/mcp/
8
+ Project-URL: Issues, https://github.com/VMAFx/vmafx/issues
9
+ Project-URL: Changelog, https://github.com/VMAFx/vmafx/blob/master/CHANGELOG.md
10
+ Author-email: Lusoris <lusoris@pm.me>
11
+ License-Expression: BSD-2-Clause-Patent
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: anyio>=4.15.1
14
+ Requires-Dist: mcp>=2.2.0
15
+ Requires-Dist: pydantic>=2.13.5
16
+ Provides-Extra: dev
17
+ Requires-Dist: mypy>=2.3.1; extra == 'dev'
18
+ Requires-Dist: prometheus-client>=0.26.0; extra == 'dev'
19
+ Requires-Dist: pytest-aiohttp>=1.1.1; extra == 'dev'
20
+ Requires-Dist: pytest-asyncio>=1.4.0; extra == 'dev'
21
+ Requires-Dist: pytest>=9.1.1; extra == 'dev'
22
+ Requires-Dist: ruff>=0.16.8; extra == 'dev'
23
+ Provides-Extra: eval
24
+ Requires-Dist: numpy>=2.5.3; extra == 'eval'
25
+ Requires-Dist: onnxruntime>=1.30.0; extra == 'eval'
26
+ Requires-Dist: pandas>=3.0.6; extra == 'eval'
27
+ Requires-Dist: pyarrow>=25.0.1; extra == 'eval'
28
+ Requires-Dist: scipy>=1.18.1; extra == 'eval'
29
+ Provides-Extra: http
30
+ Requires-Dist: aiohttp>=3.14.3; extra == 'http'
31
+ Requires-Dist: prometheus-client>=0.26.0; extra == 'http'
32
+ Provides-Extra: vlm
33
+ Requires-Dist: accelerate>=1.15.0; extra == 'vlm'
34
+ Requires-Dist: pillow>=12.3.0; extra == 'vlm'
35
+ Requires-Dist: torch>=2.14.0; extra == 'vlm'
36
+ Requires-Dist: transformers>=5.17.0; extra == 'vlm'
37
+ Description-Content-Type: text/markdown
38
+
39
+ <!-- markdownlint-disable MD060 -->
40
+ # vmaf-mcp
41
+
42
+ > **DEPRECATED (ADR-1229).** The MCP server is now the Go binary `vmafx-mcp`
43
+ > (`cmd/vmafx-mcp/`), installed at `/usr/local/bin/vmafx-mcp` in every container
44
+ > image. Attach with `docker exec -i vmaf-dev-mcp vmafx-mcp`. This Python package
45
+ > implements the same fifteen tools and is retained for one release as a
46
+ > reference implementation; it is no longer installed by `dev/Containerfile` and
47
+ > a follow-up removes it. Do not add tools here — add them to `cmd/vmafx-mcp/`.
48
+
49
+ MCP (Model Context Protocol) server that exposes the VMAFx fork's
50
+ scoring CLI to LLM tooling via JSON-RPC over stdio.
51
+
52
+ ## Tools
53
+
54
+ | Tool | Description |
55
+ | ----------------------- | ---------------------------------------------------------------------------------- |
56
+ | `vmaf_score` | Score a (ref, dis) raw YUV pair. Returns the full JSON report. |
57
+ | `vmaf_score_encoded` | Score encoded video (MP4/MKV/Y4M/…) — decodes via ffmpeg, then scores. (ADR-0608) |
58
+ | `list_models` | Enumerate models under `model/` (`.json`, `.pkl`, `.onnx`). |
59
+ | `list_backends` | Report which backends (`cpu`/`cuda`/`sycl`/`hip`/`metal`) are compiled in. |
60
+ | `probe_backend` | Runtime health check: compiled-in vs driver-functional distinction. (ADR-0608) |
61
+ | `vmaf_version` | Return binary path, version string, and build flags. (ADR-0608) |
62
+ | `run_benchmark` | Run `testdata/bench_all.sh` on the built-in fixture pairs. |
63
+ | `eval_model_on_split` | Evaluate an ONNX tiny-AI model on a parquet feature split. |
64
+ | `compare_models` | Rank ONNX models on the same split by PLCC. |
65
+ | Tool | Description |
66
+ | --------------- | ------------------------------------------------------------ |
67
+ | `vmaf_score` | Score a (ref, dis) YUV pair. Returns the full JSON report. |
68
+ | `list_models` | Enumerate models under `model/` (`.json`, `.pkl`, `.onnx`). |
69
+ | `list_backends` | Report which backends (`cpu`/`cuda`/`sycl`/`hip`) are live. |
70
+ | `run_benchmark` | Run `testdata/bench_all.sh` on a pair. |
71
+ | `eval_model_on_split` | Evaluate an ONNX tiny-AI model on a parquet feature split. |
72
+ | `compare_models` | Rank ONNX models on the same split by PLCC. |
73
+ | `describe_worst_frames` | Extract the lowest-VMAF frames and describe visible artefacts with local VLM extras. |
74
+
75
+ ## Install
76
+
77
+ ```bash
78
+ cd mcp-server/vmaf-mcp
79
+ pip install -e .
80
+ ```
81
+
82
+ Requires a built `libvmaf` binary at `build/tools/vmaf` (override via
83
+ `VMAF_BIN=/abs/path/to/vmaf`).
84
+
85
+ ## Run
86
+
87
+ ```bash
88
+ # Stdio transport (default for Claude Desktop, Cursor, etc.)
89
+ vmaf-mcp
90
+ ```
91
+
92
+ ## Path allowlisting
93
+
94
+ For safety, the server only reads files under `testdata/`,
95
+ `python/test/resource/`, and `model/`. Extend via colon-separated
96
+ `VMAF_MCP_ALLOW`:
97
+
98
+ ```bash
99
+ VMAF_MCP_ALLOW=/data/my-corpus:/mnt/yuv vmaf-mcp
100
+ ```
101
+
102
+ ## Claude Desktop config
103
+
104
+ ```json
105
+ {
106
+ "mcpServers": {
107
+ "vmaf": {
108
+ "command": "vmaf-mcp",
109
+ "env": {
110
+ "VMAF_BIN": "/home/you/dev/vmaf/build/tools/vmaf",
111
+ "VMAF_MCP_ALLOW": "/data/yuv-corpus"
112
+ }
113
+ }
114
+ }
115
+ }
116
+ ```
@@ -0,0 +1,78 @@
1
+ <!-- markdownlint-disable MD060 -->
2
+ # vmaf-mcp
3
+
4
+ > **DEPRECATED (ADR-1229).** The MCP server is now the Go binary `vmafx-mcp`
5
+ > (`cmd/vmafx-mcp/`), installed at `/usr/local/bin/vmafx-mcp` in every container
6
+ > image. Attach with `docker exec -i vmaf-dev-mcp vmafx-mcp`. This Python package
7
+ > implements the same fifteen tools and is retained for one release as a
8
+ > reference implementation; it is no longer installed by `dev/Containerfile` and
9
+ > a follow-up removes it. Do not add tools here — add them to `cmd/vmafx-mcp/`.
10
+
11
+ MCP (Model Context Protocol) server that exposes the VMAFx fork's
12
+ scoring CLI to LLM tooling via JSON-RPC over stdio.
13
+
14
+ ## Tools
15
+
16
+ | Tool | Description |
17
+ | ----------------------- | ---------------------------------------------------------------------------------- |
18
+ | `vmaf_score` | Score a (ref, dis) raw YUV pair. Returns the full JSON report. |
19
+ | `vmaf_score_encoded` | Score encoded video (MP4/MKV/Y4M/…) — decodes via ffmpeg, then scores. (ADR-0608) |
20
+ | `list_models` | Enumerate models under `model/` (`.json`, `.pkl`, `.onnx`). |
21
+ | `list_backends` | Report which backends (`cpu`/`cuda`/`sycl`/`hip`/`metal`) are compiled in. |
22
+ | `probe_backend` | Runtime health check: compiled-in vs driver-functional distinction. (ADR-0608) |
23
+ | `vmaf_version` | Return binary path, version string, and build flags. (ADR-0608) |
24
+ | `run_benchmark` | Run `testdata/bench_all.sh` on the built-in fixture pairs. |
25
+ | `eval_model_on_split` | Evaluate an ONNX tiny-AI model on a parquet feature split. |
26
+ | `compare_models` | Rank ONNX models on the same split by PLCC. |
27
+ | Tool | Description |
28
+ | --------------- | ------------------------------------------------------------ |
29
+ | `vmaf_score` | Score a (ref, dis) YUV pair. Returns the full JSON report. |
30
+ | `list_models` | Enumerate models under `model/` (`.json`, `.pkl`, `.onnx`). |
31
+ | `list_backends` | Report which backends (`cpu`/`cuda`/`sycl`/`hip`) are live. |
32
+ | `run_benchmark` | Run `testdata/bench_all.sh` on a pair. |
33
+ | `eval_model_on_split` | Evaluate an ONNX tiny-AI model on a parquet feature split. |
34
+ | `compare_models` | Rank ONNX models on the same split by PLCC. |
35
+ | `describe_worst_frames` | Extract the lowest-VMAF frames and describe visible artefacts with local VLM extras. |
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ cd mcp-server/vmaf-mcp
41
+ pip install -e .
42
+ ```
43
+
44
+ Requires a built `libvmaf` binary at `build/tools/vmaf` (override via
45
+ `VMAF_BIN=/abs/path/to/vmaf`).
46
+
47
+ ## Run
48
+
49
+ ```bash
50
+ # Stdio transport (default for Claude Desktop, Cursor, etc.)
51
+ vmaf-mcp
52
+ ```
53
+
54
+ ## Path allowlisting
55
+
56
+ For safety, the server only reads files under `testdata/`,
57
+ `python/test/resource/`, and `model/`. Extend via colon-separated
58
+ `VMAF_MCP_ALLOW`:
59
+
60
+ ```bash
61
+ VMAF_MCP_ALLOW=/data/my-corpus:/mnt/yuv vmaf-mcp
62
+ ```
63
+
64
+ ## Claude Desktop config
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "vmaf": {
70
+ "command": "vmaf-mcp",
71
+ "env": {
72
+ "VMAF_BIN": "/home/you/dev/vmaf/build/tools/vmaf",
73
+ "VMAF_MCP_ALLOW": "/data/yuv-corpus"
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
@@ -0,0 +1,21 @@
1
+ {
2
+ "//": "Drop this into Claude Desktop's mcpServers config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%/Claude/claude_desktop_config.json on Windows). Adjust paths for your machine.",
3
+ "mcpServers": {
4
+ "vmaf-local": {
5
+ "command": "vmaf-mcp",
6
+ "env": {
7
+ "VMAF_BIN": "/home/you/dev/vmaf/build/tools/vmaf",
8
+ "VMAF_MCP_ALLOW": "/home/you/yuv-corpus:/home/you/renders"
9
+ }
10
+ },
11
+ "vmaf-docker": {
12
+ "command": "docker",
13
+ "args": [
14
+ "run", "--rm", "-i",
15
+ "-v", "/home/you/yuv-corpus:/data:ro",
16
+ "-e", "VMAF_MCP_ALLOW=/data",
17
+ "ghcr.io/VMAFx/vmafx-mcp:latest"
18
+ ]
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,95 @@
1
+ [project]
2
+ name = "vmaf-mcp"
3
+ version = "1.0.0-rc.1" # x-release-please-version
4
+ description = "MCP server exposing VMAF scoring, model listing, and benchmark runs via JSON-RPC."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "BSD-2-Clause-Patent"
8
+ authors = [
9
+ { name = "Lusoris", email = "lusoris@pm.me" },
10
+ ]
11
+ dependencies = [
12
+ "mcp>=2.2.0",
13
+ "pydantic>=2.13.5",
14
+ "anyio>=4.15.1",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "pytest>=9.1.1",
20
+ "pytest-asyncio>=1.4.0",
21
+ "pytest-aiohttp>=1.1.1",
22
+ "prometheus-client>=0.26.0",
23
+ "ruff>=0.16.8",
24
+ "mypy>=2.3.1",
25
+ ]
26
+ # Optional HTTP operator surface (--transport http). Kept separate so stdio
27
+ # clients do not need aiohttp or Prometheus at runtime (ADR-0701).
28
+ http = [
29
+ "aiohttp>=3.14.3",
30
+ "prometheus-client>=0.26.0",
31
+ ]
32
+ # Optional: enables `eval_model_on_split` + `compare_models`. Pulls in the
33
+ # ONNX / pandas / scipy stack, so it's off by default to keep the base MCP
34
+ # install light for callers that only want `vmaf_score`.
35
+ eval = [
36
+ "numpy>=2.5.3",
37
+ "pandas>=3.0.6",
38
+ "pyarrow>=25.0.1",
39
+ "onnxruntime>=1.30.0",
40
+ "scipy>=1.18.1",
41
+ ]
42
+ # Optional: enables `describe_worst_frames` with vision-language fallback
43
+ # (SmolVLM → Moondream2 → metadata-only). Pulls in transformers + torch.
44
+ # Heavy install; off by default so the base MCP keeps a light footprint
45
+ # (ADR-0172 / T6-6).
46
+ vlm = [
47
+ "transformers>=5.17.0",
48
+ "torch>=2.14.0",
49
+ "Pillow>=12.3.0",
50
+ "accelerate>=1.15.0",
51
+ ]
52
+
53
+ [project.scripts]
54
+ vmaf-mcp = "vmaf_mcp.server:main"
55
+ vmafx-mcp = "vmaf_mcp.server:main"
56
+
57
+ [project.urls]
58
+ Homepage = "https://github.com/VMAFx/vmafx"
59
+ Repository = "https://github.com/VMAFx/vmafx"
60
+ Documentation = "https://vmafx.github.io/vmafx/mcp/"
61
+ Issues = "https://github.com/VMAFx/vmafx/issues"
62
+ Changelog = "https://github.com/VMAFx/vmafx/blob/master/CHANGELOG.md"
63
+
64
+ [build-system]
65
+ requires = ["hatchling>=1.32.0"]
66
+ build-backend = "hatchling.build"
67
+
68
+ [tool.hatch.build.targets.wheel]
69
+ packages = ["src/vmaf_mcp"]
70
+
71
+ [tool.pytest.ini_options]
72
+ pythonpath = ["src"]
73
+ testpaths = ["tests"]
74
+ filterwarnings = ["error"]
75
+ asyncio_default_fixture_loop_scope = "function"
76
+ markers = [
77
+ # Tests >30 s wall (or that exercise real GPUs / containers and may grow
78
+ # past 30 s). Exclude with `pytest -m 'not slow'` for fast iteration.
79
+ # See docs/adr/0908-slow-test-audit-2026-05-30.md.
80
+ "slow: marks tests as slow (deselect with -m 'not slow')",
81
+ ]
82
+
83
+ [tool.ruff]
84
+ line-length = 100
85
+ target-version = "py310"
86
+
87
+ [tool.ruff.lint]
88
+ select = ["E", "F", "W", "I", "B", "SIM", "RUF"]
89
+ ignore = [
90
+ "E501", # line length handled by black
91
+ "B008", # typer.Option / FastAPI Depends pattern — idiomatic
92
+ "RUF001", # ambiguous unicode — em-dashes in strings are intentional
93
+ "RUF002", # ambiguous unicode in docstrings
94
+ "RUF003", # ambiguous unicode in comments
95
+ ]