@sylphx/cue 0.1.2

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SylphxAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,274 @@
1
+ <div align="center">
2
+
3
+ # Cue
4
+
5
+ ### Timeline proof for agents
6
+
7
+ **Cue** (transitional package `@sylphx/video-reader-mcp`) — local video timeline evidence, not frame-by-frame VLM.
8
+
9
+ <p align="center">
10
+ <img src="https://mark.sylphx.com/api/v1/banner?type=mesh&theme=tokyonight&text=video+reader+mcp&desc=Evidence-first+video+reading+for+AI+agents+%E2%80%94+ffprobe%2C+subtitles%2C+scenes%2C+transcr&height=200&animation=rise&credit=0" alt="video-reader-mcp — Sylphx Mark banner" width="100%" />
11
+ </p>
12
+
13
+ ### Your agent watched the video. **Did it read the timeline?**
14
+
15
+ Evidence-first video reading for AI agents. One call turns any local video into a
16
+ **timeline document** — ffprobe streams, chapters, embedded subtitles, scene
17
+ boundaries, and warnings you can cite without frame-by-frame vision LLM calls.
18
+
19
+ [![npm version](https://img.shields.io/npm/v/@sylphx/video-reader-mcp?style=flat-square)](https://www.npmjs.com/package/@sylphx/video-reader-mcp)
20
+ [![License](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](https://opensource.org/licenses/MIT)
21
+ [![CI/CD](https://img.shields.io/github/actions/workflow/status/SylphxAI/video-reader-mcp/ci.yml?style=flat-square&label=CI/CD)](https://github.com/SylphxAI/video-reader-mcp/actions/workflows/ci.yml)
22
+ [![TypeScript](https://img.shields.io/badge/TypeScript-7.0-blue.svg?style=flat-square)](https://www.typescriptlang.org/)
23
+
24
+ **Local-first** · **One smart `read_video` call** · **Timeline evidence + provenance** · **20 tests**
25
+
26
+ SOTA family roadmap: [docs/roadmap/sota-family-roadmap.md](docs/roadmap/sota-family-roadmap.md).
27
+
28
+ [⭐ Star this repo](https://github.com/SylphxAI/video-reader-mcp) if agents should read video timelines with proof, not sampled frame captions.
29
+ · [Quick start](#quick-start) · [See it work](#see-it-work) · [Why not frame-by-frame vision?](#why-not-frame-by-frame-vision)
30
+
31
+ This repository is product SSOT. Sibling agent tools live in separate repos.
32
+ Each instrument is an independent repository (marketplace + stars).
33
+
34
+ </div>
35
+
36
+ ---
37
+
38
+
39
+
40
+ ## Read video structure (not N-second frame spam)
41
+
42
+ Cue is **local-first timeline architecture**: streams, dialogue, scene cuts, **structural keyframes**, and **agent_index** for text-only agents.
43
+
44
+ Spec: [docs/specs/agent-video-read-contract.md](docs/specs/agent-video-read-contract.md)
45
+
46
+ ## Product docs
47
+
48
+ | Doc | Purpose |
49
+ | --- | --- |
50
+ | [docs/POSITIONING.md](docs/POSITIONING.md) | Strategic positioning |
51
+ | [docs/COMPETITIVE.md](docs/COMPETITIVE.md) | Peer anchors and wedge |
52
+ | [docs/EVIDENCE_CONTRACT.md](docs/EVIDENCE_CONTRACT.md) | Evidence = result contract |
53
+ | [docs/TOOL_SURFACE.md](docs/TOOL_SURFACE.md) | Few clear tools policy |
54
+ | [docs/PRODUCT_INDEPENDENCE.md](docs/PRODUCT_INDEPENDENCE.md) | This repo is SSOT |
55
+ | [docs/IPPB.md](docs/IPPB.md) | Independent public product bar |
56
+ | [docs/PUBLISH.md](docs/PUBLISH.md) | npm/git publish status |
57
+
58
+ ## The problem
59
+
60
+ Videos are not a pile of frames. They are codecs, chapters, embedded subtitles,
61
+ scene cuts, variable frame rates, and timelines measured in milliseconds.
62
+
63
+ Most agent stacks sample frames and ask a **vision LLM** what it sees. Subtitles
64
+ get skipped. Scene boundaries vanish. Duration and stream metadata never reach
65
+ context. Citations become "around minute two, I think." Then the agent
66
+ hallucinates — confidently.
67
+
68
+ **Video Reader MCP is built for the moment your agent needs a citeable timeline,
69
+ not a slideshow summary.**
70
+
71
+ ## Why not frame-by-frame vision?
72
+
73
+ | Typical vision path | Video Reader MCP |
74
+ | --- | --- |
75
+ | Sample N frames into a vision model | ffprobe format + stream metadata in one call |
76
+ | Paraphrased "what happens" | Embedded subtitle cues with `start_ms`, `end_ms`, and provenance |
77
+ | Scene changes guessed from captions | Optional ffmpeg scene filter with timestamp evidence |
78
+ | Missing audio / VFR silently ignored | Warnings for missing ffmpeg/ffprobe, VFR, missing audio, skipped ASR |
79
+ | Cloud API by default | **Local-first** — ffprobe + ffmpeg on your machine |
80
+ | Ship and pray | **20** tests on parsers, fixture corpus, doctor, release gate, and integration |
81
+
82
+ ## See it work
83
+
84
+ ### Install (30 seconds)
85
+
86
+ ```bash
87
+ npm install -g @sylphx/video-reader-mcp
88
+ cue doctor
89
+ claude mcp add cue -- npx @sylphx/video-reader-mcp
90
+ ```
91
+
92
+ **Install once. Call once.**
93
+
94
+ ```bash
95
+ claude mcp add video-reader -- npx @sylphx/video-reader-mcp
96
+ ```
97
+
98
+ ```json
99
+ {
100
+ "sources": [{ "path": "/absolute/path/to/demo.mp4" }],
101
+ "include_subtitles": true,
102
+ "include_scenes": true
103
+ }
104
+ ```
105
+
106
+ `read_video` builds a timeline document per source — no per-frame vision LLM
107
+ calls:
108
+
109
+ ```json
110
+ {
111
+ "source": "/absolute/path/to/demo.mp4",
112
+ "success": true,
113
+ "data": {
114
+ "provenance": {
115
+ "source": "/absolute/path/to/demo.mp4",
116
+ "tool": "read_video",
117
+ "version": "0.1.0",
118
+ "extracted_at": "2026-07-09T12:00:00.000Z"
119
+ },
120
+ "format": {
121
+ "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
122
+ "duration_ms": 125500
123
+ },
124
+ "streams": [
125
+ { "index": 0, "codec_type": "video", "width": 1920, "height": 1080 },
126
+ { "index": 1, "codec_type": "audio", "channels": 2, "sample_rate": 48000 }
127
+ ],
128
+ "chapters": [
129
+ { "id": 0, "start_ms": 0, "end_ms": 60250, "title": "Intro" }
130
+ ],
131
+ "subtitles": [
132
+ {
133
+ "index": 0,
134
+ "start_ms": 1200,
135
+ "end_ms": 3400,
136
+ "text": "Welcome to the demo.",
137
+ "provenance": { "method": "ffmpeg_extract", "format": "srt" }
138
+ }
139
+ ],
140
+ "scenes": [
141
+ {
142
+ "index": 0,
143
+ "time_ms": 45200,
144
+ "provenance": { "method": "ffmpeg_scene_filter", "threshold": 0.4 }
145
+ }
146
+ ],
147
+ "warnings": []
148
+ }
149
+ }
150
+ ```
151
+
152
+ Abbreviated shape — optional local ASR transcript hooks skip gracefully when no
153
+ adapter is wired.
154
+
155
+ ## Prerequisites
156
+
157
+ - Node.js `>=22.13`
158
+ - **ffprobe** (required) and **ffmpeg** (recommended for subtitles + scenes) on `PATH`
159
+
160
+ ## MCP Tool Surface
161
+
162
+ | Tool | Use it when the agent needs to... |
163
+ | --- | --- |
164
+ | `read_video` | Read one or more local videos and return ffprobe metadata, chapters, subtitles, scenes, and timeline warnings. |
165
+
166
+ Supported formats: MP4, M4V, MKV, MOV, WebM, and other formats ffprobe can inspect.
167
+
168
+ ## Quick Start
169
+
170
+ ### Claude Code
171
+
172
+ ```bash
173
+ claude mcp add video-reader -- npx @sylphx/video-reader-mcp
174
+ ```
175
+
176
+ ### Claude Desktop
177
+
178
+ Add this to `claude_desktop_config.json`:
179
+
180
+ ```json
181
+ {
182
+ "mcpServers": {
183
+ "video-reader": {
184
+ "command": "npx",
185
+ "args": ["@sylphx/video-reader-mcp"]
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
191
+ ### Any MCP Client
192
+
193
+ ```bash
194
+ npx @sylphx/video-reader-mcp
195
+ ```
196
+
197
+ ### HTTP transport (optional)
198
+
199
+ ```bash
200
+ MCP_TRANSPORT=http MCP_HTTP_PORT=8080 npx @sylphx/video-reader-mcp
201
+ ```
202
+
203
+ ## Security model
204
+
205
+ - **Local-first** — `read_video` inspects local files; remote URLs are not fetched by default.
206
+ - **ffprobe/ffmpeg boundary** — probe and frame tools shell out to configured binaries on PATH; missing tools return explicit errors.
207
+ - **Fixture corpus** — CI validates parser and safety fixtures; corrupted inputs fail closed with structured diagnostics.
208
+ - **Evidence envelope** — timestamps, frame indices, and extraction routes are preserved so agents can verify claims.
209
+
210
+ ## Release proof
211
+
212
+ Claims are backed by CI `benchmark:release-gate`, fixture corpus checks, and the shipped-path matrix (Rust-default primary tools).
213
+
214
+ ```bash
215
+ bun run benchmark:release-gate
216
+ ```
217
+
218
+ Artifact: `benchmark-artifacts/video_reader_release_gate.json` — must report `status: passed` before release.
219
+
220
+ ## Development
221
+
222
+ ```bash
223
+ git clone https://github.com/SylphxAI/video-reader-mcp.git
224
+ cd video-reader-mcp
225
+ bun install
226
+ bun run build
227
+ bun test
228
+ bun run doctor
229
+ bun run benchmark:release-gate
230
+ ```
231
+
232
+ Useful checks:
233
+
234
+ ```bash
235
+ bun run check
236
+ bun run typecheck
237
+ bun run benchmark:release-gate
238
+ ```
239
+
240
+ Example `read_video` requests live in [`examples/`](examples/). CI runs parser,
241
+ fixture corpus, doctor, and release-gate checks; integration tests exercise ffmpeg
242
+ when available on the runner.
243
+
244
+ ## Support
245
+
246
+ - [Issues](https://github.com/SylphxAI/video-reader-mcp/issues)
247
+ - [npm package](https://www.npmjs.com/package/@sylphx/video-reader-mcp)
248
+ - Portfolio orchestration: [smart-reader-mcp](https://github.com/SylphxAI/smart-reader-mcp)
249
+
250
+ ## Help this reach more builders
251
+
252
+ If frame-by-frame vision guesses have wasted your context, your citations, or
253
+ your trust in agent output, you are exactly who this project is for.
254
+
255
+ **[⭐ Star the repo](https://github.com/SylphxAI/video-reader-mcp)** — it is the
256
+ fastest way to help more agent builders find evidence-first video reading. Share
257
+ it in your MCP client setup, team wiki, or agent stack README.
258
+
259
+ ### Discovery (in progress)
260
+
261
+ | Channel | Status |
262
+ | --- | --- |
263
+ | [Glama MCP directory](https://glama.ai/mcp/servers/SylphxAI/video-reader-mcp) | Listed — [claim server](https://glama.ai/mcp/servers/SylphxAI/video-reader-mcp/admin) for full discoverability |
264
+ | [Official MCP Registry](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.SylphxAI/video-reader-mcp) | Listed — `io.github.SylphxAI/video-reader-mcp` @ v0.1.0 |
265
+ | [TensorBlock MCP Index PR #1113](https://github.com/TensorBlock/awesome-mcp-servers/pull/1113) | Open — multimedia/document processing listing |
266
+ | [MCP servers community issue #4500](https://github.com/modelcontextprotocol/servers/issues/4500) | Open — community server highlight |
267
+ | [mcp.so listing issue #3068](https://github.com/chatmcp/mcpso/issues/3068) | Open — directory submission request |
268
+ | [mcpservers.org submit](https://mcpservers.org/submit) | Not listed yet — free web-form submission |
269
+
270
+ Know another MCP directory? [Open an issue](https://github.com/SylphxAI/video-reader-mcp/issues/new) with the link.
271
+
272
+ ## License
273
+
274
+ MIT © [SylphxAI](https://github.com/SylphxAI)
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+ # Video Reader MCP launcher — Rust rmcp only (fail-closed, no TS stdio fallback).
3
+ set -euo pipefail
4
+
5
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
6
+
7
+ resolve_rust_bin() {
8
+ if [[ -n "${VIDEO_READER_MCP_RUST_BIN:-}" && -x "${VIDEO_READER_MCP_RUST_BIN}" ]]; then
9
+ printf '%s\n' "${VIDEO_READER_MCP_RUST_BIN}"
10
+ return 0
11
+ fi
12
+
13
+ for candidate in \
14
+ "$ROOT/bin/native/video-reader-mcp-server" \
15
+ "$ROOT/target/release/video-reader-mcp-server" \
16
+ "$ROOT/target/debug/video-reader-mcp-server"; do
17
+ if [[ -x "$candidate" ]]; then
18
+ printf '%s\n' "$candidate"
19
+ return 0
20
+ fi
21
+ done
22
+
23
+ return 1
24
+ }
25
+
26
+ resolve_transport() {
27
+ if [[ -n "${VIDEO_READER_MCP_TRANSPORT:-}" ]]; then
28
+ printf '%s\n' "${VIDEO_READER_MCP_TRANSPORT}"
29
+ return 0
30
+ fi
31
+ if [[ -n "${MCP_TRANSPORT:-}" ]]; then
32
+ printf '%s\n' "${MCP_TRANSPORT}"
33
+ return 0
34
+ fi
35
+ printf '%s\n' "stdio"
36
+ }
37
+
38
+ if bin="$(resolve_rust_bin)"; then
39
+ transport="$(resolve_transport)"
40
+ if [[ "$transport" == "http" ]]; then
41
+ export MCP_TRANSPORT=http
42
+ export VIDEO_READER_MCP_TRANSPORT=http
43
+ fi
44
+ exec "$bin" "$@"
45
+ fi
46
+
47
+ echo "[video-reader-mcp] Rust MCP server (rmcp) is not built. Run: bun run build:rust" >&2
48
+ exit 1
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/doctor-cli.ts
4
+ import { createRequire } from "node:module";
5
+
6
+ // src/doctor.ts
7
+ import { spawnSync } from "node:child_process";
8
+ import { existsSync as existsSync2 } from "node:fs";
9
+ import path2 from "node:path";
10
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
11
+
12
+ // src/engine/rust-timeline.ts
13
+ import { existsSync } from "node:fs";
14
+ import path from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+ var here = path.dirname(fileURLToPath(import.meta.url));
17
+ function resolveRustCliBinary() {
18
+ const env = process.env.VIDEO_READER_CLI;
19
+ if (env && existsSync(env)) {
20
+ return env;
21
+ }
22
+ const release = path.join(here, "../../target/release/video-reader-cli");
23
+ if (existsSync(release)) {
24
+ return release;
25
+ }
26
+ const debug = path.join(here, "../../target/debug/video-reader-cli");
27
+ if (existsSync(debug)) {
28
+ return debug;
29
+ }
30
+ return "video-reader-cli";
31
+ }
32
+
33
+ // src/utils/exec.ts
34
+ import { execFile } from "node:child_process";
35
+ import { promisify } from "node:util";
36
+ var execFileAsync = promisify(execFile);
37
+ var DEFAULT_MAX_BUFFER = 16 * 1024 * 1024;
38
+ var binaryCache = new Map;
39
+ var isBinaryAvailable = async (binary) => {
40
+ const cached = binaryCache.get(binary);
41
+ if (cached !== undefined)
42
+ return cached;
43
+ try {
44
+ await execFileAsync(binary, ["-version"], { timeout: 5000, encoding: "utf8" });
45
+ binaryCache.set(binary, true);
46
+ return true;
47
+ } catch {
48
+ try {
49
+ await execFileAsync(binary, ["--version"], { timeout: 5000, encoding: "utf8" });
50
+ binaryCache.set(binary, true);
51
+ return true;
52
+ } catch {
53
+ binaryCache.set(binary, false);
54
+ return false;
55
+ }
56
+ }
57
+ };
58
+
59
+ // src/doctor.ts
60
+ var here2 = path2.dirname(fileURLToPath2(import.meta.url));
61
+ var probeBinary = async (id, binary, required) => {
62
+ const available = await isBinaryAvailable(binary);
63
+ if (available) {
64
+ return {
65
+ id,
66
+ status: "ok",
67
+ message: `${binary} is installed and responds to -version.`
68
+ };
69
+ }
70
+ return {
71
+ id,
72
+ status: required ? "fail" : "warn",
73
+ message: required ? `${binary} is required for read_video but was not found on PATH.` : `${binary} is optional for some extraction paths.`
74
+ };
75
+ };
76
+ var probeRustTimelineCli = () => {
77
+ const binary = resolveRustCliBinary();
78
+ if (binary !== "video-reader-cli" && existsSync2(binary)) {
79
+ const probe = spawnSync(binary, [], {
80
+ input: JSON.stringify({
81
+ tool: "build_cache_key",
82
+ input: {
83
+ source_hash: "abc123",
84
+ options: { include_streams: true, include_chapters: true }
85
+ }
86
+ }),
87
+ encoding: "utf8",
88
+ timeout: 5000
89
+ });
90
+ if (probe.status === 0) {
91
+ return {
92
+ id: "rust_timeline_cli",
93
+ status: "ok",
94
+ message: `Rust timeline CLI is available at ${binary}.`
95
+ };
96
+ }
97
+ }
98
+ const release = path2.join(here2, "../target/release/video-reader-cli");
99
+ const debug = path2.join(here2, "../target/debug/video-reader-cli");
100
+ if (existsSync2(release) || existsSync2(debug)) {
101
+ return {
102
+ id: "rust_timeline_cli",
103
+ status: "ok",
104
+ message: "Rust timeline CLI is built locally."
105
+ };
106
+ }
107
+ return {
108
+ id: "rust_timeline_cli",
109
+ status: "warn",
110
+ message: "Rust timeline CLI is not built. Run `cargo build --release` to enable VIDEO_READER_USE_RUST_TIMELINE=1."
111
+ };
112
+ };
113
+ var probeNode = () => {
114
+ const version = process.versions.node;
115
+ const major = Number.parseInt(version.split(".")[0] ?? "0", 10);
116
+ if (major >= 22) {
117
+ return {
118
+ id: "node",
119
+ status: "ok",
120
+ message: `Node.js ${version} meets the >=22.13 requirement.`
121
+ };
122
+ }
123
+ return {
124
+ id: "node",
125
+ status: "warn",
126
+ message: `Node.js ${version} is below the recommended >=22.13 runtime.`
127
+ };
128
+ };
129
+ var aggregateStatus = (checks) => {
130
+ if (checks.some((check) => check.status === "fail")) {
131
+ return "unavailable";
132
+ }
133
+ if (checks.some((check) => check.status === "warn")) {
134
+ return "degraded";
135
+ }
136
+ return "ready";
137
+ };
138
+ async function runDoctor(version) {
139
+ const checks = [
140
+ probeNode(),
141
+ probeRustTimelineCli(),
142
+ await probeBinary("ffprobe", "ffprobe", true),
143
+ await probeBinary("ffmpeg", "ffmpeg", false)
144
+ ];
145
+ return {
146
+ profile: "video_reader_doctor",
147
+ version,
148
+ status: aggregateStatus(checks),
149
+ checks
150
+ };
151
+ }
152
+ function formatDoctorReport(report) {
153
+ return JSON.stringify(report, null, 2);
154
+ }
155
+
156
+ // src/doctor-cli.ts
157
+ var require2 = createRequire(import.meta.url);
158
+ var packageJson = require2("../package.json");
159
+ async function main() {
160
+ const report = await runDoctor(packageJson.version);
161
+ console.log(formatDoctorReport(report));
162
+ process.exit(report.status === "unavailable" ? 1 : 0);
163
+ }
164
+ main().catch((error) => {
165
+ console.error("[Video Reader MCP] Doctor error:", error);
166
+ process.exit(1);
167
+ });
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@sylphx/cue",
3
+ "version": "0.1.2",
4
+ "mcpName": "io.github.SylphxAI/video-reader-mcp",
5
+ "description": "Cue — Evidence-first video reading for AI agents — ffprobe, subtitles, scenes, transcripts, and timelines without frame-by-frame LLM vision.",
6
+ "type": "module",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "license": "MIT",
11
+ "bin": {
12
+ "video-reader-mcp": "./bin/video-reader-mcp",
13
+ "cue": "./bin/video-reader-mcp"
14
+ },
15
+ "files": [
16
+ "bin/",
17
+ "dist/",
18
+ "README.md",
19
+ "LICENSE",
20
+ "src/"
21
+ ],
22
+ "exports": {
23
+ ".": "./dist/doctor-cli.js",
24
+ "./sdk": "./src/sdk.ts",
25
+ "./cue": "./src/sdk.ts"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/SylphxAI/video-reader-mcp.git"
30
+ },
31
+ "homepage": "https://github.com/SylphxAI/video-reader-mcp#readme",
32
+ "keywords": [
33
+ "mcp",
34
+ "model-context-protocol",
35
+ "video",
36
+ "reader",
37
+ "evidence",
38
+ "ffprobe",
39
+ "ffmpeg",
40
+ "subtitles",
41
+ "sylphx"
42
+ ],
43
+ "engines": {
44
+ "node": ">=22.13.0"
45
+ },
46
+ "scripts": {
47
+ "build:rust": "cargo build --release -p video-reader-core -p video-reader-cli -p video-reader-mcp-server && bun scripts/stage-rust-mcp.ts",
48
+ "test:rust": "cargo test",
49
+ "build": "bun build src/doctor-cli.ts --outdir dist --target node && chmod +x dist/doctor-cli.js",
50
+ "typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.test.json",
51
+ "test": "bun test",
52
+ "test:rust-timeline": "cargo test && bun test test/rustTimeline.boundary.test.ts",
53
+ "check": "biome check .",
54
+ "check:fix": "biome check --write .",
55
+ "sync:server-json": "bun scripts/sync-server-json.ts",
56
+ "version-packages": "changeset version && bun run sync:server-json",
57
+ "start": "./bin/video-reader-mcp",
58
+ "clean": "rm -rf dist coverage",
59
+ "release": "bun run typecheck && bun run check && bun run build && bun test && changeset publish",
60
+ "doctor": "bun run src/doctor-cli.ts",
61
+ "benchmark:release-gate": "bun scripts/release-gate.ts",
62
+ "benchmark:public-proof": "bun scripts/public-proof.ts",
63
+ "brand:pack-plan": "bun scripts/brand-pack-plan.ts"
64
+ },
65
+ "dependencies": {
66
+ "@modelcontextprotocol/sdk": "^1.29.0",
67
+ "zod": "^4.4.3"
68
+ },
69
+ "devDependencies": {
70
+ "@biomejs/biome": "^2.4.16",
71
+ "@changesets/changelog-github": "^0.7.0",
72
+ "@changesets/cli": "^2.31.0",
73
+ "@types/bun": "^1.2.19",
74
+ "@types/node": "^25.9.1",
75
+ "typescript": "^7.0.2"
76
+ },
77
+ "packageManager": "bun@1.3.12",
78
+ "private": false
79
+ }
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createRequire } from 'node:module';
4
+ import { formatDoctorReport, runDoctor } from './doctor.js';
5
+
6
+ const require = createRequire(import.meta.url);
7
+ const packageJson = require('../package.json') as { version: string };
8
+
9
+ async function main(): Promise<void> {
10
+ const report = await runDoctor(packageJson.version);
11
+ console.log(formatDoctorReport(report));
12
+ process.exit(report.status === 'unavailable' ? 1 : 0);
13
+ }
14
+
15
+ main().catch((error: unknown) => {
16
+ console.error('[Video Reader MCP] Doctor error:', error);
17
+ process.exit(1);
18
+ });