@h-ear/mcp-server 0.2.3 → 1.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.
- package/README.md +7 -14
- package/dist/tools/classify-audio.d.ts.map +1 -1
- package/dist/tools/classify-audio.js +86 -31
- package/dist/tools/classify-audio.js.map +1 -1
- package/dist/tools/classify-batch.d.ts.map +1 -1
- package/dist/tools/classify-batch.js +30 -39
- package/dist/tools/classify-batch.js.map +1 -1
- package/package.json +1 -5
- package/dist/chunker.d.ts +0 -8
- package/dist/chunker.d.ts.map +0 -1
- package/dist/chunker.js +0 -9
- package/dist/chunker.js.map +0 -1
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ OAuth 2.1 + PKCE discovery is automatic via [RFC 9728](https://datatracker.ietf.
|
|
|
72
72
|
|
|
73
73
|
| Tool | Description | Auth |
|
|
74
74
|
|------|-------------|------|
|
|
75
|
-
| `classifyAudio` | Classify a single audio file or URL (MP3, WAV, FLAC, OGG, M4A). No file size limit — large local files are
|
|
75
|
+
| `classifyAudio` | Classify a single audio file or URL (MP3, WAV, FLAC, OGG, M4A). No file size limit — large local files are uploaded via byte-slice chunking (no ffmpeg required); URL mode is fetched server-side with no client upload. | API Key |
|
|
76
76
|
| `classifyBatch` | Batch classify up to 50 audio files or URLs. | API Key |
|
|
77
77
|
| `listClasses` | List 521+ supported audio classes across 3 taxonomies (AudioSet/YAMNet, AudioSet/PANNs, Species). | None |
|
|
78
78
|
| `healthCheck` | API liveness check — status, version, deployment timestamp. | None |
|
|
@@ -120,23 +120,17 @@ OAuth 2.1 + PKCE discovery is automatic via [RFC 9728](https://datatracker.ietf.
|
|
|
120
120
|
|
|
121
121
|
## Large File Support
|
|
122
122
|
|
|
123
|
-
Files larger than 25 MB are automatically
|
|
123
|
+
Files larger than 25 MB are automatically uploaded via byte-slice chunking. Raw 25 MB slices of the original file are uploaded sequentially to the server, which reassembles them via Azure AppendBlob. No ffmpeg required. Original file quality preserved.
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
| Platform | Command |
|
|
130
|
-
|----------|---------|
|
|
131
|
-
| macOS | `brew install ffmpeg` |
|
|
132
|
-
| Ubuntu/Debian | `sudo apt-get install ffmpeg` |
|
|
133
|
-
| Windows | `winget install ffmpeg` or `choco install ffmpeg` |
|
|
125
|
+
- **No dependencies** — no ffmpeg, no temp files, no re-encoding
|
|
126
|
+
- **Memory efficient** — one 25 MB buffer at a time
|
|
127
|
+
- **Resumable** — per-chunk retry with exponential backoff
|
|
134
128
|
|
|
135
129
|
### Claude Desktop timeout
|
|
136
130
|
|
|
137
|
-
Claude Desktop has a hardcoded 60-second MCP timeout. Large file
|
|
131
|
+
Claude Desktop has a hardcoded 60-second MCP timeout. Large file uploads can exceed this. Workarounds:
|
|
138
132
|
- **Claude Code:** Set `MCP_TIMEOUT=300000` (5 min) environment variable
|
|
139
|
-
- **Claude Desktop:** Use URL mode (`url` parameter) instead of `filePath` for large files
|
|
133
|
+
- **Claude Desktop:** Use URL mode (`url` parameter) instead of `filePath` for large files — the API fetches the file server-side with no timeout concern
|
|
140
134
|
|
|
141
135
|
## Supported Formats
|
|
142
136
|
|
|
@@ -145,7 +139,6 @@ MP3, WAV, FLAC, OGG, M4A
|
|
|
145
139
|
## Requirements
|
|
146
140
|
|
|
147
141
|
- Node.js >= 18
|
|
148
|
-
- ffmpeg + ffprobe (optional -- only needed for local files >25 MB)
|
|
149
142
|
|
|
150
143
|
## Get an API Key
|
|
151
144
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classify-audio.d.ts","sourceRoot":"","sources":["../../src/tools/classify-audio.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"classify-audio.d.ts","sourceRoot":"","sources":["../../src/tools/classify-audio.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EACH,KAAK,aAAa,EAErB,MAAM,aAAa,CAAC;AA4DrB,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CA6IpF"}
|
|
@@ -1,12 +1,69 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { readFileSync, existsSync, statSync } from 'fs';
|
|
2
|
+
import { readFileSync, existsSync, statSync, openSync, readSync, closeSync } from 'fs';
|
|
3
3
|
import { basename, extname } from 'path';
|
|
4
|
-
import {
|
|
4
|
+
import { createHash } from 'crypto';
|
|
5
|
+
import { HearApiError, HEAR_API, getAudioDuration, } from '@h-ear/core';
|
|
5
6
|
import { createProgressReporter } from '../progress.js';
|
|
7
|
+
/**
|
|
8
|
+
* Compute SHA-256 of a local file without materialising the full contents in
|
|
9
|
+
* memory. Uses a small read loop so a 1 GB upload doesn't blow the heap.
|
|
10
|
+
*/
|
|
11
|
+
function hashFileSync(filePath) {
|
|
12
|
+
const hash = createHash('sha256');
|
|
13
|
+
const fd = openSync(filePath, 'r');
|
|
14
|
+
try {
|
|
15
|
+
const buf = Buffer.allocUnsafe(64 * 1024);
|
|
16
|
+
let bytesRead = 0;
|
|
17
|
+
// eslint-disable-next-line no-constant-condition
|
|
18
|
+
while (true) {
|
|
19
|
+
bytesRead = readSync(fd, buf, 0, buf.length, null);
|
|
20
|
+
if (bytesRead <= 0) {
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
hash.update(buf.subarray(0, bytesRead));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
closeSync(fd);
|
|
28
|
+
}
|
|
29
|
+
return hash.digest('hex');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build the client-captured metadata sidecar for a local file. Everything is
|
|
33
|
+
* best-effort — the server treats these as hints and its ffprobe init pass
|
|
34
|
+
* remains authoritative for duration.
|
|
35
|
+
*
|
|
36
|
+
* - clientReportedDuration: ffprobe on the local file, if available.
|
|
37
|
+
* Returns undefined silently when ffprobe is not on PATH.
|
|
38
|
+
* - recordingStartedAt: filesystem mtime as ISO 8601. The upload-media flow
|
|
39
|
+
* tier 3 fallback; better than "now" for historical recordings.
|
|
40
|
+
* - fileHash: SHA-256 hex for dedupe / audit correlation.
|
|
41
|
+
* - originalFileName: full basename of the client-side path.
|
|
42
|
+
*/
|
|
43
|
+
function buildClientMetadata(filePath, fileStat) {
|
|
44
|
+
const out = {
|
|
45
|
+
recordingStartedAt: fileStat.mtime.toISOString(),
|
|
46
|
+
originalFileName: basename(filePath),
|
|
47
|
+
};
|
|
48
|
+
try {
|
|
49
|
+
out.fileHash = hashFileSync(filePath);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Hash is a hint; failure is non-fatal.
|
|
53
|
+
}
|
|
54
|
+
// getAudioDuration() is the @h-ear/core ffprobe wrapper — it returns 0 (not
|
|
55
|
+
// throws) when ffprobe is unavailable or the file is unreadable, so no
|
|
56
|
+
// separate hasFFmpeg() guard is needed.
|
|
57
|
+
const probed = getAudioDuration(filePath);
|
|
58
|
+
if (probed > 0) {
|
|
59
|
+
out.clientReportedDuration = probed;
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
6
63
|
const formats = HEAR_API.SUPPORTED_FORMATS.join(', ');
|
|
7
64
|
const maxMB = HEAR_API.MAX_FILE_SIZE_BYTES / (1024 * 1024);
|
|
8
65
|
export function registerClassifyAudio(server, client) {
|
|
9
|
-
server.tool('classifyAudio', `Classify a local audio file or public URL for noise/sound detection. Provide filePath (recommended) or url. Returns detected sound classes with confidence scores. Formats: ${formats}. Files over ${maxMB}MB are automatically
|
|
66
|
+
server.tool('classifyAudio', `Classify a local audio file or public URL for noise/sound detection. Provide filePath (recommended) or url. Returns detected sound classes with confidence scores. Formats: ${formats}. Files over ${maxMB}MB are automatically uploaded via byte-slice chunking (no ffmpeg required). Requires API key (HEAR_API_KEY). For large files, may take several minutes to upload. Returns job ID for later retrieval via getJob.`, {
|
|
10
67
|
filePath: z.string().optional()
|
|
11
68
|
.describe('Path to a local audio file (Desktop/CLI only — requires local filesystem access). The server reads and encodes it automatically. Large files are split into chunks. Use url for web/mobile clients.'),
|
|
12
69
|
url: z.string().url().optional()
|
|
@@ -15,8 +72,8 @@ export function registerClassifyAudio(server, client) {
|
|
|
15
72
|
.describe('Confidence threshold (0.0-1.0). Only classes above this score are returned.'),
|
|
16
73
|
filterMinDurationSeconds: z.number().min(0.1).max(595).optional()
|
|
17
74
|
.describe('Minimum event duration filter in seconds.'),
|
|
18
|
-
waitForResult: z.boolean().default(
|
|
19
|
-
.describe('If true
|
|
75
|
+
waitForResult: z.boolean().default(false)
|
|
76
|
+
.describe('If true, polls until classification completes (up to 5 min). Default false — returns immediately with job ID for later retrieval via getJob.'),
|
|
20
77
|
callbackUrl: z.string().url().optional()
|
|
21
78
|
.describe('Webhook URL (HTTPS) for async result delivery. When provided, the server POSTs classification results to this URL on completion — no polling needed. Use getJob to check status manually.'),
|
|
22
79
|
callbackSecret: z.string().optional()
|
|
@@ -57,6 +114,10 @@ export function registerClassifyAudio(server, client) {
|
|
|
57
114
|
}
|
|
58
115
|
const stat = statSync(params.filePath);
|
|
59
116
|
const fileSizeMB = stat.size / (1024 * 1024);
|
|
117
|
+
// Client-captured metadata sidecar — best-effort hints the server
|
|
118
|
+
// persists on the snippet (hash, mtime, optional duration). The server
|
|
119
|
+
// processor ffprobe init pass still owns authoritative duration.
|
|
120
|
+
const clientMetadata = buildClientMetadata(params.filePath, stat);
|
|
60
121
|
// Small file: read and submit via multipart/form-data
|
|
61
122
|
if (stat.size <= HEAR_API.MAX_FILE_SIZE_BYTES) {
|
|
62
123
|
onProgress(`Reading ${basename(params.filePath)} (${fileSizeMB.toFixed(1)}MB)`);
|
|
@@ -66,6 +127,7 @@ export function registerClassifyAudio(server, client) {
|
|
|
66
127
|
filterMinDurationSeconds: params.filterMinDurationSeconds,
|
|
67
128
|
callbackUrl: params.callbackUrl,
|
|
68
129
|
callbackSecret: params.callbackSecret,
|
|
130
|
+
...clientMetadata,
|
|
69
131
|
});
|
|
70
132
|
if (params.callbackUrl || !params.waitForResult) {
|
|
71
133
|
return { content: [{ type: 'text', text: JSON.stringify(accepted, null, 2) }] };
|
|
@@ -73,38 +135,31 @@ export function registerClassifyAudio(server, client) {
|
|
|
73
135
|
const result = await client.pollJob(accepted.requestId, onProgress);
|
|
74
136
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
75
137
|
}
|
|
76
|
-
// Large file:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return { content: [{ type: 'text', text: 'Error: ffmpeg failed to split file into chunks.' }], isError: true };
|
|
138
|
+
// Large file: byte-slice upload (no ffmpeg, no temp files, no re-encoding)
|
|
139
|
+
// Reads 25MB slices from the original file, uploads each via chunked session.
|
|
140
|
+
// Server AppendBlob concatenates raw bytes → original file restored → ONE ML job.
|
|
141
|
+
const fileSize = stat.size;
|
|
142
|
+
const chunkSize = HEAR_API.MAX_FILE_SIZE_BYTES;
|
|
143
|
+
const totalChunks = Math.ceil(fileSize / chunkSize);
|
|
144
|
+
onProgress(`Large file (${fileSizeMB.toFixed(1)}MB) — uploading in ${totalChunks} chunks`);
|
|
145
|
+
const chunks = [];
|
|
146
|
+
for (let i = 0; i < totalChunks; i++) {
|
|
147
|
+
const offset = i * chunkSize;
|
|
148
|
+
const length = Math.min(chunkSize, fileSize - offset);
|
|
149
|
+
chunks.push({ filePath: params.filePath, offset, length, index: i });
|
|
89
150
|
}
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
});
|
|
97
|
-
// Cleanup temp files immediately after reading
|
|
98
|
-
cleanupChunks(chunks);
|
|
99
|
-
// Submit all chunks as a single upload session → ONE job
|
|
100
|
-
const accepted = await client.submitClassifyChunked(chunkBuffers, basename(params.filePath), { threshold: params.threshold, filterMinDurationSeconds: params.filterMinDurationSeconds, callbackUrl: params.callbackUrl, callbackSecret: params.callbackSecret }, onProgress);
|
|
101
|
-
step(totalSteps - 1, totalSteps, `Chunked upload complete — job ${accepted.requestId}`);
|
|
151
|
+
const accepted = await client.submitClassifyChunked(chunks, basename(params.filePath), {
|
|
152
|
+
threshold: params.threshold,
|
|
153
|
+
filterMinDurationSeconds: params.filterMinDurationSeconds,
|
|
154
|
+
callbackUrl: params.callbackUrl,
|
|
155
|
+
callbackSecret: params.callbackSecret,
|
|
156
|
+
...clientMetadata,
|
|
157
|
+
}, onProgress);
|
|
102
158
|
if (params.callbackUrl || !params.waitForResult) {
|
|
103
159
|
return { content: [{ type: 'text', text: JSON.stringify(accepted, null, 2) }] };
|
|
104
160
|
}
|
|
105
161
|
onProgress(`Polling job ${accepted.requestId}`);
|
|
106
162
|
const result = await client.pollJob(accepted.requestId, onProgress);
|
|
107
|
-
step(totalSteps, totalSteps, 'Classification complete');
|
|
108
163
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
109
164
|
}
|
|
110
165
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classify-audio.js","sourceRoot":"","sources":["../../src/tools/classify-audio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"classify-audio.js","sourceRoot":"","sources":["../../src/tools/classify-audio.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classify-batch.d.ts","sourceRoot":"","sources":["../../src/tools/classify-batch.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,KAAK,aAAa,
|
|
1
|
+
{"version":3,"file":"classify-batch.d.ts","sourceRoot":"","sources":["../../src/tools/classify-batch.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,KAAK,aAAa,EAA8B,MAAM,aAAa,CAAC;AAI7E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAoIpF"}
|
|
@@ -34,15 +34,34 @@ export function registerClassifyBatch(server, client) {
|
|
|
34
34
|
continue;
|
|
35
35
|
}
|
|
36
36
|
const stat = statSync(filePath);
|
|
37
|
-
if (stat.size > HEAR_API.MAX_FILE_SIZE_BYTES) {
|
|
38
|
-
jobs.push({ file: filePath, error: `File too large: ${(stat.size / 1024 / 1024).toFixed(1)}MB` });
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
37
|
const ext = extname(filePath).toLowerCase().replace('.', '').toUpperCase();
|
|
42
38
|
if (!HEAR_API.SUPPORTED_FORMATS.includes(ext)) {
|
|
43
39
|
jobs.push({ file: filePath, error: `Unsupported format: ${ext}` });
|
|
44
40
|
continue;
|
|
45
41
|
}
|
|
42
|
+
// Large file: byte-slice chunked upload (no ffmpeg, no temp files)
|
|
43
|
+
if (stat.size > HEAR_API.MAX_FILE_SIZE_BYTES) {
|
|
44
|
+
try {
|
|
45
|
+
const fileSize = stat.size;
|
|
46
|
+
const chunkSize = HEAR_API.MAX_FILE_SIZE_BYTES;
|
|
47
|
+
const totalChunks = Math.ceil(fileSize / chunkSize);
|
|
48
|
+
onProgress(`${basename(filePath)} (${(fileSize / 1024 / 1024).toFixed(1)}MB) — uploading in ${totalChunks} chunks`);
|
|
49
|
+
const chunks = [];
|
|
50
|
+
for (let ci = 0; ci < totalChunks; ci++) {
|
|
51
|
+
const offset = ci * chunkSize;
|
|
52
|
+
const length = Math.min(chunkSize, fileSize - offset);
|
|
53
|
+
chunks.push({ filePath, offset, length, index: ci });
|
|
54
|
+
}
|
|
55
|
+
const accepted = await client.submitClassifyChunked(chunks, basename(filePath), { threshold: params.threshold, filterMinDurationSeconds: params.filterMinDurationSeconds, callbackUrl: params.callbackUrl, callbackSecret: params.callbackSecret }, onProgress);
|
|
56
|
+
jobs.push({ file: basename(filePath), requestId: accepted.requestId });
|
|
57
|
+
step(jobs.length, totalFiles, `Submitted ${basename(filePath)} → ${accepted.requestId} (chunked)`);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
jobs.push({ file: basename(filePath), error: err instanceof Error ? err.message : String(err) });
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Small file: direct upload
|
|
46
65
|
try {
|
|
47
66
|
const fileBuffer = readFileSync(filePath);
|
|
48
67
|
const accepted = await client.submitClassifyFile(fileBuffer, basename(filePath), { threshold: params.threshold, filterMinDurationSeconds: params.filterMinDurationSeconds, callbackUrl: params.callbackUrl, callbackSecret: params.callbackSecret });
|
|
@@ -54,43 +73,15 @@ export function registerClassifyBatch(server, client) {
|
|
|
54
73
|
jobs.push({ file: basename(filePath), error: err instanceof Error ? err.message : String(err) });
|
|
55
74
|
}
|
|
56
75
|
}
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
const summary = {
|
|
60
|
-
mode: 'local-webhook',
|
|
61
|
-
totalFiles: params.filePaths.length,
|
|
62
|
-
submitted: jobs.filter(j => j.requestId).length,
|
|
63
|
-
failed: jobs.filter(j => j.error).length,
|
|
64
|
-
callbackUrl: params.callbackUrl,
|
|
65
|
-
jobs,
|
|
66
|
-
};
|
|
67
|
-
return { content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }] };
|
|
68
|
-
}
|
|
69
|
-
// Phase 2: Poll all submitted jobs for results
|
|
70
|
-
const results = [];
|
|
71
|
-
let pollIndex = 0;
|
|
72
|
-
for (const job of jobs) {
|
|
73
|
-
if (job.error) {
|
|
74
|
-
results.push({ file: job.file, status: 'error', error: job.error });
|
|
75
|
-
pollIndex++;
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
try {
|
|
79
|
-
step(totalFiles + pollIndex + 1, totalFiles * 2, `Polling ${job.file}`);
|
|
80
|
-
const result = await client.pollJob(job.requestId ?? '', onProgress);
|
|
81
|
-
results.push({ file: job.file, status: 'classified', result });
|
|
82
|
-
}
|
|
83
|
-
catch (err) {
|
|
84
|
-
results.push({ file: job.file, status: 'error', error: err instanceof Error ? err.message : String(err) });
|
|
85
|
-
}
|
|
86
|
-
pollIndex++;
|
|
87
|
-
}
|
|
76
|
+
// Default: return submitted job IDs immediately (non-blocking).
|
|
77
|
+
// User can poll individual jobs with getJob.
|
|
88
78
|
const summary = {
|
|
89
|
-
mode: 'local-async',
|
|
79
|
+
mode: params.callbackUrl ? 'local-webhook' : 'local-async',
|
|
90
80
|
totalFiles: params.filePaths.length,
|
|
91
|
-
|
|
92
|
-
failed:
|
|
93
|
-
|
|
81
|
+
submitted: jobs.filter(j => j.requestId).length,
|
|
82
|
+
failed: jobs.filter(j => j.error).length,
|
|
83
|
+
callbackUrl: params.callbackUrl,
|
|
84
|
+
jobs,
|
|
94
85
|
};
|
|
95
86
|
return { content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }] };
|
|
96
87
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classify-batch.js","sourceRoot":"","sources":["../../src/tools/classify-batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEzC,OAAO,
|
|
1
|
+
{"version":3,"file":"classify-batch.js","sourceRoot":"","sources":["../../src/tools/classify-batch.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-ear/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"mcpName": "io.github.badajoz95/h-ear",
|
|
5
5
|
"description": "MCP server for the H-ear World audio classification API — connect Claude, ChatGPT, and other AI agents to 521+ sound classes",
|
|
6
6
|
"type": "module",
|
|
@@ -50,10 +50,6 @@
|
|
|
50
50
|
"engines": {
|
|
51
51
|
"node": ">=18"
|
|
52
52
|
},
|
|
53
|
-
"systemDependencies": {
|
|
54
|
-
"ffmpeg": "optional — large local files (>25MB) are split into chunks via ffmpeg",
|
|
55
|
-
"ffprobe": "optional — audio duration detection for chunk calculation"
|
|
56
|
-
},
|
|
57
53
|
"files": [
|
|
58
54
|
"dist",
|
|
59
55
|
"README.md"
|
package/dist/chunker.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export chunker from @h-ear/core for backwards compatibility.
|
|
3
|
-
*/
|
|
4
|
-
export { hasFFmpeg, getAudioDuration, splitAudioFile, cleanupChunks, mergeChunkResults, CHUNK_DURATION_SEC, CHUNK_OVERLAP_SEC, MIN_TRAILING_CHUNK_SEC, } from '@h-ear/core';
|
|
5
|
-
export type { ChunkInfo } from '@h-ear/core';
|
|
6
|
-
export declare const MCP_CHUNK_DURATION_SEC = 120;
|
|
7
|
-
export declare const MCP_CHUNK_OVERLAP_SEC = 30;
|
|
8
|
-
//# sourceMappingURL=chunker.d.ts.map
|
package/dist/chunker.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chunker.d.ts","sourceRoot":"","sources":["../src/chunker.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACH,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAC3C,aAAa,EAAE,iBAAiB,EAChC,kBAAkB,EAAE,iBAAiB,EAAE,sBAAsB,GAChE,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,eAAO,MAAM,sBAAsB,MAAqB,CAAC;AACzD,eAAO,MAAM,qBAAqB,KAAoB,CAAC"}
|
package/dist/chunker.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export chunker from @h-ear/core for backwards compatibility.
|
|
3
|
-
*/
|
|
4
|
-
export { hasFFmpeg, getAudioDuration, splitAudioFile, cleanupChunks, mergeChunkResults, CHUNK_DURATION_SEC, CHUNK_OVERLAP_SEC, MIN_TRAILING_CHUNK_SEC, } from '@h-ear/core';
|
|
5
|
-
// Backwards-compatible aliases
|
|
6
|
-
import { CHUNK_DURATION_SEC, CHUNK_OVERLAP_SEC } from '@h-ear/core';
|
|
7
|
-
export const MCP_CHUNK_DURATION_SEC = CHUNK_DURATION_SEC;
|
|
8
|
-
export const MCP_CHUNK_OVERLAP_SEC = CHUNK_OVERLAP_SEC;
|
|
9
|
-
//# sourceMappingURL=chunker.js.map
|
package/dist/chunker.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chunker.js","sourceRoot":"","sources":["../src/chunker.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACH,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAC3C,aAAa,EAAE,iBAAiB,EAChC,kBAAkB,EAAE,iBAAiB,EAAE,sBAAsB,GAChE,MAAM,aAAa,CAAC;AAGrB,+BAA+B;AAC/B,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,MAAM,CAAC,MAAM,sBAAsB,GAAG,kBAAkB,CAAC;AACzD,MAAM,CAAC,MAAM,qBAAqB,GAAG,iBAAiB,CAAC"}
|