media-analysis-mcp 2.0.0

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.
@@ -0,0 +1,146 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { chmod, mkdir, open, readFile, rename, unlink } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ /** Temporary oss:// objects last ~48h; keep a 1h safety margin. */
5
+ export const OSS_CACHE_TTL_MS = 47 * 60 * 60 * 1000;
6
+ function isDiskCacheFile(value) {
7
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
8
+ return false;
9
+ }
10
+ const record = value;
11
+ if (record.version !== 1 || typeof record.entries !== "object" || record.entries === null) {
12
+ return false;
13
+ }
14
+ return true;
15
+ }
16
+ /**
17
+ * Cache entries therefore depend on: the file's identity (path, size, mtime), a bounded
18
+ * content fingerprint of that file, the model, the upload endpoint and a one-way
19
+ * fingerprint of the credential.
20
+ */
21
+ /**
22
+ * One-way fingerprint of the configured credential. Bailian temporary URLs are
23
+ * bound to the uploading account, so a cached URL must not be reused after the
24
+ * API key changes. The fingerprint is a domain-separated SHA-256 prefix: it cannot
25
+ * be turned back into the key and is the only key-derived value that is persisted.
26
+ */
27
+ export function credentialFingerprint(apiKey) {
28
+ return createHash("sha256")
29
+ .update(`media-analysis-mcp/upload-cache/v1\0${apiKey}`, "utf8")
30
+ .digest("hex")
31
+ .slice(0, 16);
32
+ }
33
+ export function localUploadCacheKey(media, model, uploadUrl, credential) {
34
+ if (media.identityKey.length === 0 || media.contentFingerprint.length === 0) {
35
+ return undefined;
36
+ }
37
+ return `${media.identityKey}\0${media.contentFingerprint}\0${model}\0${uploadUrl}\0${credential}`;
38
+ }
39
+ function isAborted(signal) {
40
+ return signal.aborted;
41
+ }
42
+ async function readDiskEntries(path, now) {
43
+ const map = new Map();
44
+ try {
45
+ const parsed = JSON.parse(await readFile(path, "utf8"));
46
+ if (!isDiskCacheFile(parsed)) {
47
+ return map;
48
+ }
49
+ for (const [key, entry] of Object.entries(parsed.entries)) {
50
+ if (typeof entry.url === "string" &&
51
+ entry.url.startsWith("oss://") &&
52
+ typeof entry.expiresAt === "number" &&
53
+ entry.expiresAt > now) {
54
+ map.set(key, { url: entry.url, expiresAt: entry.expiresAt });
55
+ }
56
+ }
57
+ }
58
+ catch {
59
+ // Missing or corrupt cache is a miss. Never log the body.
60
+ }
61
+ return map;
62
+ }
63
+ async function writeDiskEntries(path, map, now) {
64
+ const entries = {};
65
+ for (const [key, entry] of map) {
66
+ if (entry.expiresAt > now) {
67
+ entries[key] = entry;
68
+ }
69
+ }
70
+ const dir = dirname(path);
71
+ await mkdir(dir, { recursive: true, mode: 0o700 });
72
+ const tmp = join(dir, `.upload-cache.${String(process.pid)}.${randomUUID()}.tmp`);
73
+ const handle = await open(tmp, "w", 0o600);
74
+ try {
75
+ await handle.writeFile(JSON.stringify({ version: 1, entries }), "utf8");
76
+ await handle.sync();
77
+ }
78
+ finally {
79
+ await handle.close();
80
+ }
81
+ try {
82
+ await rename(tmp, path);
83
+ await chmod(path, 0o600).catch(() => undefined);
84
+ }
85
+ catch {
86
+ await unlink(tmp).catch(() => undefined);
87
+ }
88
+ }
89
+ export function createCachedUploader(cfg, inner, clock = { now: Date.now }) {
90
+ const ttlMs = clock.ttlMs ?? OSS_CACHE_TTL_MS;
91
+ const persistPath = cfg.uploadCache === false ? undefined : cfg.uploadCachePath;
92
+ const memory = new Map();
93
+ let loaded = persistPath === undefined;
94
+ let loading;
95
+ async function ensureLoaded() {
96
+ if (loaded) {
97
+ return;
98
+ }
99
+ if (loading === undefined) {
100
+ const path = persistPath;
101
+ loading = (async () => {
102
+ if (path !== undefined) {
103
+ const disk = await readDiskEntries(path, clock.now());
104
+ for (const [key, entry] of disk) {
105
+ memory.set(key, entry);
106
+ }
107
+ }
108
+ loaded = true;
109
+ })();
110
+ }
111
+ await loading;
112
+ }
113
+ return {
114
+ async upload(media, signal) {
115
+ if (cfg.uploadCache === false) {
116
+ return inner.upload(media, signal);
117
+ }
118
+ const key = localUploadCacheKey(media, cfg.model, cfg.uploadUrl ?? "", credentialFingerprint(cfg.apiKey));
119
+ await ensureLoaded();
120
+ if (key !== undefined) {
121
+ const hit = memory.get(key);
122
+ if (hit !== undefined && hit.expiresAt > clock.now()) {
123
+ return { url: hit.url, requiresOssResolve: true, reused: true };
124
+ }
125
+ }
126
+ const uploaded = await inner.upload(media, signal);
127
+ // The underlying upload may settle successfully at the same time the caller
128
+ // cancels. A cancelled call must not create a reusable cache entry.
129
+ if (isAborted(signal)) {
130
+ return uploaded;
131
+ }
132
+ if (key !== undefined) {
133
+ memory.set(key, { url: uploaded.url, expiresAt: clock.now() + ttlMs });
134
+ if (persistPath !== undefined) {
135
+ await writeDiskEntries(persistPath, memory, clock.now()).catch(() => undefined);
136
+ if (isAborted(signal)) {
137
+ memory.delete(key);
138
+ await writeDiskEntries(persistPath, memory, clock.now()).catch(() => undefined);
139
+ }
140
+ }
141
+ }
142
+ return uploaded;
143
+ },
144
+ };
145
+ }
146
+ //# sourceMappingURL=upload-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-cache.js","sourceRoot":"","sources":["../src/upload-cache.ts"],"names":[],"mappings":"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"}
package/dist/upload.js ADDED
@@ -0,0 +1,368 @@
1
+ import { randomBytes, randomUUID } from "node:crypto";
2
+ import { request as httpRequest } from "node:http";
3
+ import { request as httpsRequest } from "node:https";
4
+ import { Readable } from "node:stream";
5
+ import { z } from "zod";
6
+ import { BYTES_PER_MIB } from "./config.js";
7
+ import { MediaError } from "./errors.js";
8
+ /**
9
+ * Bailian's field table documents these two as strings while its example response
10
+ * uses numbers, so accept either spelling instead of failing the whole upload.
11
+ */
12
+ const positiveNumber = z.preprocess((raw) => {
13
+ if (typeof raw === "string") {
14
+ const trimmed = raw.trim();
15
+ if (trimmed !== "" && Number.isFinite(Number(trimmed))) {
16
+ return Number(trimmed);
17
+ }
18
+ }
19
+ return raw;
20
+ }, z.number().positive());
21
+ const policySchema = z.looseObject({
22
+ request_id: z.string().min(1),
23
+ data: z.looseObject({
24
+ policy: z.string().min(1),
25
+ signature: z.string().min(1),
26
+ upload_dir: z.string().min(1),
27
+ upload_host: z.string().min(1),
28
+ expire_in_seconds: positiveNumber,
29
+ max_file_size_mb: positiveNumber,
30
+ oss_access_key_id: z.string().min(1),
31
+ x_oss_object_acl: z.string().min(1),
32
+ x_oss_forbid_overwrite: z.string().min(1),
33
+ }),
34
+ });
35
+ const NUMERIC_POLICY_FIELDS = new Set(["expire_in_seconds", "max_file_size_mb"]);
36
+ function policyFailure(reason, extra) {
37
+ const diagnostic = { parse_reason: reason };
38
+ if (extra?.field !== undefined) {
39
+ diagnostic.field = extra.field;
40
+ }
41
+ return new MediaError({
42
+ code: "UPLOAD_POLICY_FAILED",
43
+ stage: "policy_acquired",
44
+ ...(extra?.httpStatus !== undefined ? { httpStatus: extra.httpStatus } : {}),
45
+ diagnostic,
46
+ });
47
+ }
48
+ function policyReasonFromIssues(issues) {
49
+ const first = issues[0];
50
+ const path = (first?.path ?? []).map(String).filter((part) => part !== "data");
51
+ const leaf = path[path.length - 1];
52
+ const field = path.length > 0 ? `data.${path.join(".")}` : undefined;
53
+ const numericField = leaf !== undefined && NUMERIC_POLICY_FIELDS.has(leaf);
54
+ const reason = first?.code === "invalid_type" && !numericField ? "shape_mismatch" : "field_type_mismatch";
55
+ return field === undefined ? { reason } : { reason, field };
56
+ }
57
+ const ERROR_BODY_LIMIT = 2048;
58
+ function mergeSignals(external, timeoutMs) {
59
+ return AbortSignal.any([external, AbortSignal.timeout(timeoutMs)]);
60
+ }
61
+ function assertSafePartValue(value) {
62
+ if (value.includes("\r") || value.includes("\n")) {
63
+ throw new MediaError({ code: "UPLOAD_POLICY_FAILED", stage: "policy_acquired" });
64
+ }
65
+ }
66
+ function httpsHost(raw) {
67
+ let parsed;
68
+ try {
69
+ parsed = new URL(raw);
70
+ }
71
+ catch {
72
+ throw policyFailure("upload_host_invalid", { field: "data.upload_host" });
73
+ }
74
+ if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "") {
75
+ throw policyFailure("upload_host_invalid", { field: "data.upload_host" });
76
+ }
77
+ return parsed.toString();
78
+ }
79
+ export function objectKey(uploadDir, objectName, extension = "mp4") {
80
+ return `${uploadDir.replace(/\/+$/, "")}/${objectName ?? `${randomUUID()}.${extension}`}`;
81
+ }
82
+ export function encodeMultipart(params) {
83
+ const chunks = [];
84
+ for (const [name, value] of params.fields) {
85
+ assertSafePartValue(name);
86
+ assertSafePartValue(value);
87
+ chunks.push(`--${params.boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`);
88
+ }
89
+ const fileName = params.fileName ?? "video.mp4";
90
+ const contentType = params.contentType ?? "video/mp4";
91
+ assertSafePartValue(fileName);
92
+ assertSafePartValue(contentType);
93
+ chunks.push(`--${params.boundary}\r\nContent-Disposition: form-data; name="file"; filename="${fileName}"\r\nContent-Type: ${contentType}\r\n\r\n`);
94
+ const preamble = Buffer.from(chunks.join(""), "utf8");
95
+ const epilogue = Buffer.from(`\r\n--${params.boundary}--\r\n`, "utf8");
96
+ return {
97
+ preamble,
98
+ epilogue,
99
+ contentLength: preamble.length + params.fileSize + epilogue.length,
100
+ };
101
+ }
102
+ const STREAM_CHUNK_BYTES = 64 * 1024;
103
+ export function fileMultipartStream(media, preamble, epilogue) {
104
+ const fileStream = media.handle.createReadStream({
105
+ autoClose: false,
106
+ start: 0,
107
+ highWaterMark: STREAM_CHUNK_BYTES,
108
+ });
109
+ const combined = Readable.from((async function* () {
110
+ yield preamble;
111
+ for await (const chunk of fileStream) {
112
+ yield chunk;
113
+ }
114
+ yield epilogue;
115
+ })(), { objectMode: false, highWaterMark: STREAM_CHUNK_BYTES });
116
+ const stop = () => {
117
+ fileStream.destroy();
118
+ };
119
+ combined.once("close", stop);
120
+ combined.once("error", stop);
121
+ return combined;
122
+ }
123
+ const TRANSPORT_CODES = new Set([
124
+ "ECONNRESET",
125
+ "ECONNREFUSED",
126
+ "ETIMEDOUT",
127
+ "ENOTFOUND",
128
+ "EAI_AGAIN",
129
+ "EPIPE",
130
+ "ERR_TLS_CERT_ALTNAME_INVALID",
131
+ "CERT_HAS_EXPIRED",
132
+ "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
133
+ ]);
134
+ class UploadTransportFailure extends Error {
135
+ reason;
136
+ transportCode;
137
+ constructor(reason, transportCode) {
138
+ super("upload request failed");
139
+ this.reason = reason;
140
+ this.transportCode = transportCode;
141
+ }
142
+ }
143
+ function transportCode(error) {
144
+ if (typeof error !== "object" || error === null || !("code" in error))
145
+ return undefined;
146
+ return typeof error.code === "string" && TRANSPORT_CODES.has(error.code) ? error.code : undefined;
147
+ }
148
+ function abortReason(signal) {
149
+ const reason = signal.reason;
150
+ return reason instanceof Error && reason.name === "TimeoutError" ? "upload_timeout" : "cancelled";
151
+ }
152
+ async function readLimitedIncoming(res) {
153
+ let seen = 0;
154
+ try {
155
+ for await (const chunk of res) {
156
+ if (chunk instanceof Uint8Array) {
157
+ seen += chunk.byteLength;
158
+ if (seen >= ERROR_BODY_LIMIT) {
159
+ res.destroy();
160
+ break;
161
+ }
162
+ }
163
+ }
164
+ }
165
+ catch {
166
+ res.destroy();
167
+ }
168
+ }
169
+ export function postMultipartStream(url, headers, body, signal) {
170
+ if (signal.aborted) {
171
+ body.destroy();
172
+ return Promise.reject(new UploadTransportFailure(abortReason(signal)));
173
+ }
174
+ const parsed = new URL(url);
175
+ const request = parsed.protocol === "http:" ? httpRequest : httpsRequest;
176
+ return new Promise((resolve, reject) => {
177
+ let settled = false;
178
+ const fail = (failure) => {
179
+ if (settled) {
180
+ return;
181
+ }
182
+ settled = true;
183
+ signal.removeEventListener("abort", onAbort);
184
+ body.destroy();
185
+ reject(failure);
186
+ };
187
+ const req = request(parsed, { method: "POST", headers }, (res) => {
188
+ void readLimitedIncoming(res)
189
+ .then(() => {
190
+ if (settled) {
191
+ return;
192
+ }
193
+ settled = true;
194
+ signal.removeEventListener("abort", onAbort);
195
+ resolve({ status: res.statusCode ?? 0 });
196
+ })
197
+ .catch(() => {
198
+ fail(new UploadTransportFailure("request_failed"));
199
+ });
200
+ });
201
+ const onAbort = () => {
202
+ req.destroy();
203
+ fail(new UploadTransportFailure(abortReason(signal)));
204
+ };
205
+ req.on("error", (error) => {
206
+ fail(new UploadTransportFailure("request_failed", transportCode(error)));
207
+ });
208
+ body.on("error", () => {
209
+ fail(new UploadTransportFailure("file_read_failed"));
210
+ req.destroy();
211
+ });
212
+ body.pipe(req);
213
+ signal.addEventListener("abort", onAbort, { once: true });
214
+ });
215
+ }
216
+ async function readLimitedText(res) {
217
+ const body = res.body;
218
+ if (body === null) {
219
+ return;
220
+ }
221
+ const reader = body.getReader();
222
+ let seen = 0;
223
+ try {
224
+ for (;;) {
225
+ const raw = await reader.read();
226
+ if (typeof raw !== "object" || raw === null || !("done" in raw)) {
227
+ break;
228
+ }
229
+ if (raw.done === true) {
230
+ break;
231
+ }
232
+ if (!("value" in raw) || !(raw.value instanceof Uint8Array)) {
233
+ break;
234
+ }
235
+ seen += raw.value.byteLength;
236
+ if (seen >= ERROR_BODY_LIMIT) {
237
+ await reader.cancel();
238
+ break;
239
+ }
240
+ }
241
+ }
242
+ catch {
243
+ await reader.cancel().catch(() => undefined);
244
+ }
245
+ }
246
+ export async function fetchUploadPolicy(cfg, signal) {
247
+ const url = `${cfg.uploadUrl}?action=getPolicy&model=${encodeURIComponent(cfg.model)}`;
248
+ let res;
249
+ try {
250
+ res = await fetch(url, {
251
+ method: "GET",
252
+ headers: {
253
+ Authorization: `Bearer ${cfg.apiKey}`,
254
+ "Content-Type": "application/json",
255
+ },
256
+ signal: mergeSignals(signal, cfg.uploadTimeoutMs),
257
+ });
258
+ }
259
+ catch {
260
+ throw policyFailure("request_failed");
261
+ }
262
+ if (!res.ok) {
263
+ await readLimitedText(res);
264
+ throw policyFailure("http_error", { httpStatus: res.status });
265
+ }
266
+ let json;
267
+ try {
268
+ json = await res.json();
269
+ }
270
+ catch {
271
+ throw policyFailure("invalid_json");
272
+ }
273
+ const parsed = policySchema.safeParse(json);
274
+ if (!parsed.success) {
275
+ const { reason, field } = policyReasonFromIssues(parsed.error.issues);
276
+ throw policyFailure(reason, field === undefined ? {} : { field });
277
+ }
278
+ httpsHost(parsed.data.data.upload_host);
279
+ return parsed.data;
280
+ }
281
+ export async function uploadLocalMedia(cfg, media, signal, poster = postMultipartStream) {
282
+ if (media.sizeBytes <= 0) {
283
+ throw new MediaError({ code: "UNSUPPORTED_MEDIA", stage: "authorized" });
284
+ }
285
+ if (media.sizeBytes > cfg.maxLocalMediaBytes) {
286
+ throw new MediaError({
287
+ code: "MEDIA_FILE_TOO_LARGE",
288
+ stage: "authorized",
289
+ diagnostic: { size_bytes: media.sizeBytes },
290
+ });
291
+ }
292
+ const policy = await fetchUploadPolicy(cfg, signal);
293
+ const maxPolicyBytes = policy.data.max_file_size_mb * BYTES_PER_MIB;
294
+ if (media.sizeBytes > maxPolicyBytes) {
295
+ throw new MediaError({
296
+ code: "MEDIA_FILE_TOO_LARGE",
297
+ stage: "policy_acquired",
298
+ diagnostic: { size_bytes: media.sizeBytes },
299
+ });
300
+ }
301
+ const key = objectKey(policy.data.upload_dir, undefined, media.objectExtension);
302
+ const boundary = `----QwenMedia${randomBytes(16).toString("hex")}`;
303
+ const fields = [
304
+ ["OSSAccessKeyId", policy.data.oss_access_key_id],
305
+ ["Signature", policy.data.signature],
306
+ ["policy", policy.data.policy],
307
+ ["x-oss-object-acl", policy.data.x_oss_object_acl],
308
+ ["x-oss-forbid-overwrite", policy.data.x_oss_forbid_overwrite],
309
+ ["key", key],
310
+ ["success_action_status", "200"],
311
+ ];
312
+ const encoded = encodeMultipart({
313
+ boundary,
314
+ fields,
315
+ fileSize: media.sizeBytes,
316
+ fileName: media.uploadName,
317
+ contentType: media.contentType,
318
+ });
319
+ const body = fileMultipartStream(media, encoded.preamble, encoded.epilogue);
320
+ const uploadHost = httpsHost(policy.data.upload_host);
321
+ const headers = {
322
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
323
+ "Content-Length": String(encoded.contentLength),
324
+ };
325
+ let posted;
326
+ const uploadSignal = mergeSignals(signal, cfg.uploadTimeoutMs);
327
+ try {
328
+ posted = await poster(uploadHost, headers, body, uploadSignal);
329
+ }
330
+ catch (error) {
331
+ body.destroy();
332
+ const reason = uploadSignal.aborted
333
+ ? abortReason(uploadSignal)
334
+ : error instanceof UploadTransportFailure
335
+ ? error.reason
336
+ : "request_failed";
337
+ throw new MediaError({
338
+ code: "MEDIA_UPLOAD_FAILED",
339
+ stage: "uploaded",
340
+ diagnostic: {
341
+ parse_reason: reason,
342
+ ...(error instanceof UploadTransportFailure && error.transportCode !== undefined
343
+ ? { error_code: error.transportCode }
344
+ : {}),
345
+ },
346
+ });
347
+ }
348
+ if (posted.status !== 200) {
349
+ throw new MediaError({
350
+ code: "MEDIA_UPLOAD_FAILED",
351
+ stage: "uploaded",
352
+ httpStatus: posted.status,
353
+ diagnostic: { parse_reason: "http_error" },
354
+ });
355
+ }
356
+ return { url: `oss://${key}`, requiresOssResolve: true, reused: false };
357
+ }
358
+ export function createTemporaryUploader(cfg, poster) {
359
+ return {
360
+ upload(media, signal) {
361
+ if (poster === undefined) {
362
+ return uploadLocalMedia(cfg, media, signal);
363
+ }
364
+ return uploadLocalMedia(cfg, media, signal, poster);
365
+ },
366
+ };
367
+ }
368
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /** Single source for package.json and MCP initialize. */
2
+ export const PACKAGE_VERSION = "2.0.0";
3
+ export function formatPackageBanner(gitCommit) {
4
+ const commit = gitCommit?.trim();
5
+ if (commit !== undefined && commit.length > 0 && commit !== "unknown") {
6
+ return `media-analysis-mcp ${PACKAGE_VERSION} (git ${commit})`;
7
+ }
8
+ return `media-analysis-mcp ${PACKAGE_VERSION}`;
9
+ }
10
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AAEvC,MAAM,UAAU,mBAAmB,CAAC,SAAkB;IACpD,MAAM,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,CAAC;IACjC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACtE,OAAO,sBAAsB,eAAe,SAAS,MAAM,GAAG,CAAC;IACjE,CAAC;IACD,OAAO,sBAAsB,eAAe,EAAE,CAAC;AACjD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "media-analysis-mcp",
3
+ "version": "2.0.0",
4
+ "description": "MCP media gateway for agent-directed MP4, MOV and MP3 analysis",
5
+ "keywords": [
6
+ "mcp",
7
+ "model-context-protocol",
8
+ "video",
9
+ "qwen",
10
+ "dashscope",
11
+ "cursor",
12
+ "claude"
13
+ ],
14
+ "license": "MIT",
15
+ "homepage": "https://github.com/JaylanJerry/media-analysis-mcp#readme",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/JaylanJerry/media-analysis-mcp.git"
19
+ },
20
+ "type": "module",
21
+ "engines": {
22
+ "node": "24.x"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "bin": {
28
+ "media-analysis-mcp": "dist/index.js"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "scripts/prepare.mjs",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsc -p tsconfig.build.json",
38
+ "dev": "node scripts/dev.mjs",
39
+ "start": "node dist/index.js",
40
+ "lint": "eslint . --max-warnings 0",
41
+ "lint:fix": "eslint . --fix",
42
+ "format": "prettier --write .",
43
+ "format:check": "prettier --check .",
44
+ "typecheck": "tsc --noEmit",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "test:live": "LIVE=1 vitest run test/live.test.ts",
48
+ "test:install": "tsx scripts/install-e2e.ts",
49
+ "test:pack-install": "tsx scripts/pack-install-e2e.ts",
50
+ "test:npx-github": "tsx scripts/npx-github-e2e.ts",
51
+ "coverage": "vitest run --coverage",
52
+ "prepare": "node scripts/prepare.mjs"
53
+ },
54
+ "dependencies": {
55
+ "@modelcontextprotocol/sdk": "^1.30.0",
56
+ "zod": "^4.4.3"
57
+ },
58
+ "devDependencies": {
59
+ "@eslint/js": "^10.0.1",
60
+ "@types/node": "^26.6.2",
61
+ "@vitest/coverage-v8": "^5.0.1",
62
+ "eslint": "^10.11.0",
63
+ "eslint-config-prettier": "^10.1.8",
64
+ "husky": "^9.0.0",
65
+ "lint-staged": "^17.3.0",
66
+ "msw": "^2.0.0",
67
+ "prettier": "^3.0.0",
68
+ "tsx": "^4.0.0",
69
+ "typescript": "^5.5.0",
70
+ "typescript-eslint": "^8.0.0",
71
+ "vitest": "^5.0.1"
72
+ }
73
+ }