@h-ear/mcp-server 0.1.0-dev.202603280915
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 +100 -0
- package/dist/api-client.d.ts +6 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +5 -0
- package/dist/api-client.js.map +1 -0
- package/dist/chunker.d.ts +8 -0
- package/dist/chunker.d.ts.map +1 -0
- package/dist/chunker.js +9 -0
- package/dist/chunker.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +26 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +6 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +5 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/classify-audio.d.ts +3 -0
- package/dist/prompts/classify-audio.d.ts.map +1 -0
- package/dist/prompts/classify-audio.js +29 -0
- package/dist/prompts/classify-audio.js.map +1 -0
- package/dist/resources/api-status.d.ts +4 -0
- package/dist/resources/api-status.d.ts.map +1 -0
- package/dist/resources/api-status.js +41 -0
- package/dist/resources/api-status.js.map +1 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +33 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/classify-audio.d.ts +4 -0
- package/dist/tools/classify-audio.d.ts.map +1 -0
- package/dist/tools/classify-audio.js +97 -0
- package/dist/tools/classify-audio.js.map +1 -0
- package/dist/tools/classify-batch.d.ts +4 -0
- package/dist/tools/classify-batch.d.ts.map +1 -0
- package/dist/tools/classify-batch.js +116 -0
- package/dist/tools/classify-batch.js.map +1 -0
- package/dist/tools/get-job.d.ts +4 -0
- package/dist/tools/get-job.d.ts.map +1 -0
- package/dist/tools/get-job.js +19 -0
- package/dist/tools/get-job.js.map +1 -0
- package/dist/tools/health-check.d.ts +4 -0
- package/dist/tools/health-check.d.ts.map +1 -0
- package/dist/tools/health-check.js +15 -0
- package/dist/tools/health-check.js.map +1 -0
- package/dist/tools/list-classes.d.ts +4 -0
- package/dist/tools/list-classes.d.ts.map +1 -0
- package/dist/tools/list-classes.js +25 -0
- package/dist/tools/list-classes.js.map +1 -0
- package/dist/tools/list-jobs.d.ts +4 -0
- package/dist/tools/list-jobs.d.ts.map +1 -0
- package/dist/tools/list-jobs.js +23 -0
- package/dist/tools/list-jobs.js.map +1 -0
- package/dist/tools/usage.d.ts +4 -0
- package/dist/tools/usage.d.ts.map +1 -0
- package/dist/tools/usage.js +15 -0
- package/dist/tools/usage.js.map +1 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# @h-ear/mcp-server
|
|
2
|
+
|
|
3
|
+
MCP server for the [H-ear World](https://h-ear.world) audio classification API. Connect Claude, ChatGPT, Copilot, and other AI agents to 521+ sound classes.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @h-ear/mcp-server --key ncm_sk_your_key
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Claude Desktop
|
|
12
|
+
|
|
13
|
+
Add to `claude_desktop_config.json`:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"h-ear": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "@h-ear/mcp-server"],
|
|
21
|
+
"env": { "HEAR_API_KEY": "ncm_sk_your_key" }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Claude Code
|
|
28
|
+
|
|
29
|
+
Add to `.claude/settings.json`:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"mcpServers": {
|
|
34
|
+
"h-ear": {
|
|
35
|
+
"command": "npx",
|
|
36
|
+
"args": ["-y", "@h-ear/mcp-server"],
|
|
37
|
+
"env": { "HEAR_API_KEY": "ncm_sk_your_key" }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Tools
|
|
44
|
+
|
|
45
|
+
| Tool | Description | Auth |
|
|
46
|
+
|------|-------------|------|
|
|
47
|
+
| `classifyAudio` | Classify a single audio file or URL (MP3, WAV, FLAC, OGG, M4A). Supports files up to 25 MB with automatic chunking for larger files. | API Key |
|
|
48
|
+
| `classifyBatch` | Batch classify up to 50 audio files or URLs. | API Key |
|
|
49
|
+
| `listClasses` | List 521+ supported audio classes across 3 taxonomies (AudioSet/YAMNet, AudioSet/PANNs, Species). | None |
|
|
50
|
+
| `healthCheck` | API liveness check. | None |
|
|
51
|
+
|
|
52
|
+
## Resources
|
|
53
|
+
|
|
54
|
+
| URI | Description |
|
|
55
|
+
|-----|-------------|
|
|
56
|
+
| `h-ear://status` | Live API status with health, version, available taxonomies, and class counts. |
|
|
57
|
+
|
|
58
|
+
## Prompts
|
|
59
|
+
|
|
60
|
+
| Name | Description |
|
|
61
|
+
|------|-------------|
|
|
62
|
+
| `classify-audio` | Pre-built classification workflow prompt with configurable detail level. |
|
|
63
|
+
|
|
64
|
+
## Options
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
--key <key> API key (or set HEAR_API_KEY env var)
|
|
68
|
+
--env <env> Environment: dev, staging, prod (default: prod)
|
|
69
|
+
--base-url <url> Override API base URL
|
|
70
|
+
--help, -h Show help
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Environment Variables
|
|
74
|
+
|
|
75
|
+
| Variable | Description | Default |
|
|
76
|
+
|----------|-------------|---------|
|
|
77
|
+
| `HEAR_API_KEY` | H-ear Enterprise API key | (required for classify tools) |
|
|
78
|
+
| `HEAR_ENV` | Target environment | `prod` |
|
|
79
|
+
| `HEAR_BASE_URL` | Override base URL | Environment default |
|
|
80
|
+
|
|
81
|
+
## Large File Support
|
|
82
|
+
|
|
83
|
+
Files larger than 25 MB are automatically split into 120-second chunks with 30-second overlap using ffmpeg. Results are merged and deduplicated. Requires `ffmpeg` and `ffprobe` on PATH.
|
|
84
|
+
|
|
85
|
+
## Supported Formats
|
|
86
|
+
|
|
87
|
+
MP3, WAV, FLAC, OGG, M4A
|
|
88
|
+
|
|
89
|
+
## Requirements
|
|
90
|
+
|
|
91
|
+
- Node.js >= 18
|
|
92
|
+
- ffmpeg (optional, for large file chunking)
|
|
93
|
+
|
|
94
|
+
## Get an API Key
|
|
95
|
+
|
|
96
|
+
Visit [h-ear.world](https://h-ear.world) to create an account and generate an API key.
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
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, } 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
|
|
@@ -0,0 +1 @@
|
|
|
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,GACxC,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
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
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, } 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
|
|
@@ -0,0 +1 @@
|
|
|
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,GACxC,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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config resolution for the H-ear MCP server.
|
|
3
|
+
* Priority: CLI args > env vars > defaults.
|
|
4
|
+
*/
|
|
5
|
+
import { type ServerConfig } from '@h-ear/core';
|
|
6
|
+
export type { ServerConfig };
|
|
7
|
+
export { apiUrl } from '@h-ear/core';
|
|
8
|
+
export declare function resolveConfig(): ServerConfig;
|
|
9
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAgB,KAAK,YAAY,EAAwB,MAAM,aAAa,CAAC;AAEpF,YAAY,EAAE,YAAY,EAAE,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC,wBAAgB,aAAa,IAAI,YAAY,CAmB5C"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config resolution for the H-ear MCP server.
|
|
3
|
+
* Priority: CLI args > env vars > defaults.
|
|
4
|
+
*/
|
|
5
|
+
import { ENVIRONMENTS } from '@h-ear/core';
|
|
6
|
+
export { apiUrl } from '@h-ear/core';
|
|
7
|
+
function getCliArg(name) {
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
const idx = args.indexOf(`--${name}`);
|
|
10
|
+
return idx >= 0 && args[idx + 1] ? args[idx + 1] : undefined;
|
|
11
|
+
}
|
|
12
|
+
export function resolveConfig() {
|
|
13
|
+
const apiKey = getCliArg('key') || process.env.HEAR_API_KEY || '';
|
|
14
|
+
const envStr = getCliArg('env') || process.env.HEAR_ENV || 'prod';
|
|
15
|
+
const environment = (Object.keys(ENVIRONMENTS).includes(envStr) ? envStr : 'prod');
|
|
16
|
+
const envConfig = ENVIRONMENTS[environment];
|
|
17
|
+
const baseUrl = getCliArg('base-url') || process.env.HEAR_BASE_URL || envConfig.baseUrl;
|
|
18
|
+
const apiPath = envConfig.apiPath;
|
|
19
|
+
if (!apiKey) {
|
|
20
|
+
process.stderr.write('[h-ear-mcp] Warning: No API key provided. Tools requiring auth (classifyAudio, classifyBatch) will fail.\n' +
|
|
21
|
+
' Set HEAR_API_KEY env var or pass --key <key>\n');
|
|
22
|
+
}
|
|
23
|
+
process.stderr.write(`[h-ear-mcp] env=${environment} base=${baseUrl}${apiPath}\n`);
|
|
24
|
+
return { apiKey, environment, baseUrl, apiPath };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAA2C,MAAM,aAAa,CAAC;AAGpF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,SAAS,SAAS,CAAC,IAAY;IAC3B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACtC,OAAO,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,aAAa;IACzB,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;IAClE,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,MAAM,CAAC;IAElE,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAoB,CAAC;IACtG,MAAM,SAAS,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,SAAS,CAAC,OAAO,CAAC;IACxF,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;IAElC,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAChB,4GAA4G;YAC5G,kDAAkD,CACrD,CAAC;IACN,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,WAAW,SAAS,OAAO,GAAG,OAAO,IAAI,CAAC,CAAC;IAEnF,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* H-ear MCP Server — Audio classification for AI agents.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx @h-ear/mcp-server --key ncm_sk_...
|
|
7
|
+
* HEAR_API_KEY=ncm_sk_... npx @h-ear/mcp-server
|
|
8
|
+
* npx @h-ear/mcp-server --key ncm_sk_... --env dev
|
|
9
|
+
*
|
|
10
|
+
* @see https://h-ear.world
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* H-ear MCP Server — Audio classification for AI agents.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx @h-ear/mcp-server --key ncm_sk_...
|
|
7
|
+
* HEAR_API_KEY=ncm_sk_... npx @h-ear/mcp-server
|
|
8
|
+
* npx @h-ear/mcp-server --key ncm_sk_... --env dev
|
|
9
|
+
*
|
|
10
|
+
* @see https://h-ear.world
|
|
11
|
+
*/
|
|
12
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
|
+
import { resolveConfig } from './config.js';
|
|
14
|
+
import { createServer } from './server.js';
|
|
15
|
+
async function main() {
|
|
16
|
+
// Help flag
|
|
17
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
18
|
+
process.stderr.write(`
|
|
19
|
+
H-ear MCP Server — Audio classification for AI agents
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
npx @h-ear/mcp-server [options]
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
--key <key> API key (or set HEAR_API_KEY env var)
|
|
26
|
+
--env <env> Environment: dev, staging, prod (default: prod)
|
|
27
|
+
--base-url <url> Override API base URL
|
|
28
|
+
--help, -h Show this help
|
|
29
|
+
|
|
30
|
+
Tools:
|
|
31
|
+
classifyAudio Classify audio (base64 or URL, sync or async)
|
|
32
|
+
classifyBatch Batch classify up to 50 audio URLs
|
|
33
|
+
listClasses List 521+ supported audio classes
|
|
34
|
+
healthCheck API liveness check
|
|
35
|
+
|
|
36
|
+
Resources:
|
|
37
|
+
h-ear://status Live API status with health, version, taxonomies
|
|
38
|
+
|
|
39
|
+
Prompts:
|
|
40
|
+
classify-audio Pre-built classification workflow prompt
|
|
41
|
+
|
|
42
|
+
Claude Desktop config (claude_desktop_config.json):
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"h-ear": {
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["-y", "@h-ear/mcp-server"],
|
|
48
|
+
"env": { "HEAR_API_KEY": "ncm_sk_your_key" }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
\n`);
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
const config = resolveConfig();
|
|
56
|
+
const { server } = createServer(config);
|
|
57
|
+
const transport = new StdioServerTransport();
|
|
58
|
+
await server.connect(transport);
|
|
59
|
+
process.stderr.write('[h-ear-mcp] Server started on stdio\n');
|
|
60
|
+
}
|
|
61
|
+
main().catch((error) => {
|
|
62
|
+
process.stderr.write(`[h-ear-mcp] Fatal: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,KAAK,UAAU,IAAI;IACf,YAAY;IACZ,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkC1B,CAAC,CAAC;QACG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAExC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;AAClE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify-audio.d.ts","sourceRoot":"","sources":["../../src/prompts/classify-audio.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgCnE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerClassifyAudioPrompt(server) {
|
|
3
|
+
server.prompt('classify-audio', 'Pre-built prompt for audio classification. Guides the AI through classifying audio and interpreting results.', {
|
|
4
|
+
audioSource: z.string().describe('URL or description of the audio to classify.'),
|
|
5
|
+
detail: z.enum(['summary', 'detailed', 'timestamps']).default('summary')
|
|
6
|
+
.describe('Level of detail: summary (top classes), detailed (all classes with low threshold), timestamps (per-second breakdown).'),
|
|
7
|
+
}, ({ audioSource, detail }) => {
|
|
8
|
+
const thresholdHint = detail === 'detailed' ? 'threshold: 0.1' : 'threshold: 0.3';
|
|
9
|
+
const timestampHint = detail === 'timestamps' ? ' Include timestamps in your analysis.' : '';
|
|
10
|
+
return {
|
|
11
|
+
messages: [{
|
|
12
|
+
role: 'user',
|
|
13
|
+
content: {
|
|
14
|
+
type: 'text',
|
|
15
|
+
text: [
|
|
16
|
+
`Classify the following audio: ${audioSource}`,
|
|
17
|
+
'',
|
|
18
|
+
`Use the classifyAudio tool with ${thresholdHint}.${timestampHint}`,
|
|
19
|
+
'After getting results, provide a clear summary of:',
|
|
20
|
+
'1. The dominant sounds detected and their confidence levels',
|
|
21
|
+
'2. The sound environment category (urban, natural, industrial, etc.)',
|
|
22
|
+
'3. Any notable or unusual sounds',
|
|
23
|
+
].join('\n'),
|
|
24
|
+
},
|
|
25
|
+
}],
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=classify-audio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify-audio.js","sourceRoot":"","sources":["../../src/prompts/classify-audio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,UAAU,2BAA2B,CAAC,MAAiB;IACzD,MAAM,CAAC,MAAM,CACT,gBAAgB,EAChB,8GAA8G,EAC9G;QACI,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;QAChF,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;aACnE,QAAQ,CAAC,uHAAuH,CAAC;KACzI,EACD,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE;QACxB,MAAM,aAAa,GAAG,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC;QAClF,MAAM,aAAa,GAAG,MAAM,KAAK,YAAY,CAAC,CAAC,CAAC,uCAAuC,CAAC,CAAC,CAAC,EAAE,CAAC;QAE7F,OAAO;YACH,QAAQ,EAAE,CAAC;oBACP,IAAI,EAAE,MAAe;oBACrB,OAAO,EAAE;wBACL,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE;4BACF,iCAAiC,WAAW,EAAE;4BAC9C,EAAE;4BACF,mCAAmC,aAAa,IAAI,aAAa,EAAE;4BACnE,oDAAoD;4BACpD,6DAA6D;4BAC7D,sEAAsE;4BACtE,kCAAkC;yBACrC,CAAC,IAAI,CAAC,IAAI,CAAC;qBACf;iBACJ,CAAC;SACL,CAAC;IACN,CAAC,CACJ,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { HearApiClient, ServerConfig } from '@h-ear/core';
|
|
3
|
+
export declare function registerApiStatus(server: McpServer, client: HearApiClient, config: ServerConfig): void;
|
|
4
|
+
//# sourceMappingURL=api-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-status.d.ts","sourceRoot":"","sources":["../../src/resources/api-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE/D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA6CtG"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function registerApiStatus(server, client, config) {
|
|
2
|
+
server.resource('api-status', 'h-ear://status', { description: 'Live H-ear API status including health, version, environment, and available taxonomies', mimeType: 'application/json' }, async (uri) => {
|
|
3
|
+
try {
|
|
4
|
+
const [health, classes] = await Promise.all([
|
|
5
|
+
client.health(),
|
|
6
|
+
client.listClasses({ limit: 0 }),
|
|
7
|
+
]);
|
|
8
|
+
const status = {
|
|
9
|
+
...health,
|
|
10
|
+
environment: config.environment,
|
|
11
|
+
baseUrl: config.baseUrl,
|
|
12
|
+
taxonomies: classes.availableTaxonomies,
|
|
13
|
+
totalClasses: classes.totalAvailable,
|
|
14
|
+
categories: classes.categories,
|
|
15
|
+
timestamp: new Date().toISOString(),
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
contents: [{
|
|
19
|
+
uri: uri.href,
|
|
20
|
+
mimeType: 'application/json',
|
|
21
|
+
text: JSON.stringify(status, null, 2),
|
|
22
|
+
}],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return {
|
|
27
|
+
contents: [{
|
|
28
|
+
uri: uri.href,
|
|
29
|
+
mimeType: 'application/json',
|
|
30
|
+
text: JSON.stringify({
|
|
31
|
+
status: 'error',
|
|
32
|
+
error: error instanceof Error ? error.message : String(error),
|
|
33
|
+
environment: config.environment,
|
|
34
|
+
timestamp: new Date().toISOString(),
|
|
35
|
+
}, null, 2),
|
|
36
|
+
}],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=api-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-status.js","sourceRoot":"","sources":["../../src/resources/api-status.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,iBAAiB,CAAC,MAAiB,EAAE,MAAqB,EAAE,MAAoB;IAC5F,MAAM,CAAC,QAAQ,CACX,YAAY,EACZ,gBAAgB,EAChB,EAAE,WAAW,EAAE,wFAAwF,EAAE,QAAQ,EAAE,kBAAkB,EAAE,EACvI,KAAK,EAAE,GAAG,EAAE,EAAE;QACV,IAAI,CAAC;YACD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACxC,MAAM,CAAC,MAAM,EAAE;gBACf,MAAM,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;aACnC,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG;gBACX,GAAG,MAAM;gBACT,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,UAAU,EAAE,OAAO,CAAC,mBAAmB;gBACvC,YAAY,EAAE,OAAO,CAAC,cAAc;gBACpC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACtC,CAAC;YAEF,OAAO;gBACH,QAAQ,EAAE,CAAC;wBACP,GAAG,EAAE,GAAG,CAAC,IAAI;wBACb,QAAQ,EAAE,kBAAkB;wBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;qBACxC,CAAC;aACL,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,QAAQ,EAAE,CAAC;wBACP,GAAG,EAAE,GAAG,CAAC,IAAI;wBACb,QAAQ,EAAE,kBAAkB;wBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACjB,MAAM,EAAE,OAAO;4BACf,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;4BAC7D,WAAW,EAAE,MAAM,CAAC,WAAW;4BAC/B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;yBACtC,EAAE,IAAI,EAAE,CAAC,CAAC;qBACd,CAAC;aACL,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;AACN,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* H-ear MCP Server — registers all tools, resources, and prompts.
|
|
3
|
+
* Exported for testing (InMemoryTransport) and programmatic use.
|
|
4
|
+
*/
|
|
5
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
|
+
import { HearApiClient, type ServerConfig } from '@h-ear/core';
|
|
7
|
+
export declare function createServer(config: ServerConfig): {
|
|
8
|
+
server: McpServer;
|
|
9
|
+
client: HearApiClient;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAW/D,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAwB/F"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* H-ear MCP Server — registers all tools, resources, and prompts.
|
|
3
|
+
* Exported for testing (InMemoryTransport) and programmatic use.
|
|
4
|
+
*/
|
|
5
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
|
+
import { HearApiClient } from '@h-ear/core';
|
|
7
|
+
import { registerHealthCheck } from './tools/health-check.js';
|
|
8
|
+
import { registerListClasses } from './tools/list-classes.js';
|
|
9
|
+
import { registerClassifyAudio } from './tools/classify-audio.js';
|
|
10
|
+
import { registerClassifyBatch } from './tools/classify-batch.js';
|
|
11
|
+
import { registerUsage } from './tools/usage.js';
|
|
12
|
+
import { registerListJobs } from './tools/list-jobs.js';
|
|
13
|
+
import { registerGetJob } from './tools/get-job.js';
|
|
14
|
+
import { registerApiStatus } from './resources/api-status.js';
|
|
15
|
+
import { registerClassifyAudioPrompt } from './prompts/classify-audio.js';
|
|
16
|
+
export function createServer(config) {
|
|
17
|
+
const server = new McpServer({ name: 'h-ear-mcp', version: '0.1.0' }, { capabilities: { logging: {} } });
|
|
18
|
+
const client = new HearApiClient(config);
|
|
19
|
+
// Tools
|
|
20
|
+
registerHealthCheck(server, client);
|
|
21
|
+
registerListClasses(server, client);
|
|
22
|
+
registerClassifyAudio(server, client);
|
|
23
|
+
registerClassifyBatch(server, client);
|
|
24
|
+
registerUsage(server, client);
|
|
25
|
+
registerListJobs(server, client);
|
|
26
|
+
registerGetJob(server, client);
|
|
27
|
+
// Resources
|
|
28
|
+
registerApiStatus(server, client, config);
|
|
29
|
+
// Prompts
|
|
30
|
+
registerClassifyAudioPrompt(server);
|
|
31
|
+
return { server, client };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAqB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAE1E,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC7C,MAAM,MAAM,GAAG,IAAI,SAAS,CACxB,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,EACvC,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CACpC,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;IAEzC,QAAQ;IACR,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE/B,YAAY;IACZ,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAE1C,UAAU;IACV,2BAA2B,CAAC,MAAM,CAAC,CAAC;IAEpC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify-audio.d.ts","sourceRoot":"","sources":["../../src/tools/classify-audio.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EACH,KAAK,aAAa,EAErB,MAAM,aAAa,CAAC;AAKrB,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CA0GpF"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { readFileSync, existsSync, statSync } from 'fs';
|
|
3
|
+
import { basename, extname } from 'path';
|
|
4
|
+
import { HEAR_API, hasFFmpeg, getAudioDuration, splitAudioFile, cleanupChunks, mergeChunkResults, } from '@h-ear/core';
|
|
5
|
+
const formats = HEAR_API.SUPPORTED_FORMATS.join(', ');
|
|
6
|
+
const maxMB = HEAR_API.MAX_FILE_SIZE_BYTES / (1024 * 1024);
|
|
7
|
+
export function registerClassifyAudio(server, client) {
|
|
8
|
+
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 split into overlapping chunks using ffmpeg. Requires API key (HEAR_API_KEY). Runs async — submits job and polls for results.`, {
|
|
9
|
+
filePath: z.string().optional()
|
|
10
|
+
.describe('Path to a local audio file. The server reads and encodes it automatically. Large files are split into chunks. Recommended.'),
|
|
11
|
+
url: z.string().url().optional()
|
|
12
|
+
.describe('Public URL to audio file.'),
|
|
13
|
+
threshold: z.number().min(0).max(1).default(0.3)
|
|
14
|
+
.describe('Confidence threshold (0.0-1.0). Only classes above this score are returned.'),
|
|
15
|
+
filterMinDurationSeconds: z.number().min(0.1).max(595).optional()
|
|
16
|
+
.describe('Minimum event duration filter in seconds.'),
|
|
17
|
+
}, async (params) => {
|
|
18
|
+
try {
|
|
19
|
+
const onProgress = (msg) => process.stderr.write(`[h-ear-mcp] ${msg}\n`);
|
|
20
|
+
// URL mode: pass through directly
|
|
21
|
+
if (params.url) {
|
|
22
|
+
const result = await client.classify({ url: params.url, threshold: params.threshold, filterMinDurationSeconds: params.filterMinDurationSeconds }, onProgress);
|
|
23
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
24
|
+
}
|
|
25
|
+
if (!params.filePath) {
|
|
26
|
+
return { content: [{ type: 'text', text: 'Error: Provide filePath or url.' }], isError: true };
|
|
27
|
+
}
|
|
28
|
+
// Validate file exists and format
|
|
29
|
+
if (!existsSync(params.filePath)) {
|
|
30
|
+
return { content: [{ type: 'text', text: `Error: File not found: ${params.filePath}` }], isError: true };
|
|
31
|
+
}
|
|
32
|
+
const ext = extname(params.filePath).toLowerCase().replace('.', '').toUpperCase();
|
|
33
|
+
if (!HEAR_API.SUPPORTED_FORMATS.includes(ext)) {
|
|
34
|
+
return { content: [{ type: 'text', text: `Error: Unsupported format: ${ext}. Supported: ${formats}` }], isError: true };
|
|
35
|
+
}
|
|
36
|
+
const stat = statSync(params.filePath);
|
|
37
|
+
const fileSizeMB = stat.size / (1024 * 1024);
|
|
38
|
+
// Small file: read, encode, submit directly
|
|
39
|
+
if (stat.size <= HEAR_API.MAX_FILE_SIZE_BYTES) {
|
|
40
|
+
onProgress(`Reading ${basename(params.filePath)} (${fileSizeMB.toFixed(1)}MB)`);
|
|
41
|
+
const result = await client.classify({
|
|
42
|
+
base64: readFileSync(params.filePath).toString('base64'),
|
|
43
|
+
fileName: basename(params.filePath),
|
|
44
|
+
threshold: params.threshold,
|
|
45
|
+
filterMinDurationSeconds: params.filterMinDurationSeconds,
|
|
46
|
+
}, onProgress);
|
|
47
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
48
|
+
}
|
|
49
|
+
// Large file: split into overlapping chunks with ffmpeg
|
|
50
|
+
if (!hasFFmpeg()) {
|
|
51
|
+
return { content: [{ type: 'text', text: `Error: File is ${fileSizeMB.toFixed(1)}MB (over ${maxMB}MB limit). ffmpeg is required to split large files but was not found in PATH. Install ffmpeg and retry.` }], isError: true };
|
|
52
|
+
}
|
|
53
|
+
onProgress(`Large file (${fileSizeMB.toFixed(1)}MB) — splitting into chunks with 30s overlap`);
|
|
54
|
+
const totalDuration = getAudioDuration(params.filePath);
|
|
55
|
+
if (totalDuration <= 0) {
|
|
56
|
+
return { content: [{ type: 'text', text: 'Error: Could not determine audio duration. Is the file valid?' }], isError: true };
|
|
57
|
+
}
|
|
58
|
+
onProgress(`Duration: ${totalDuration.toFixed(1)}s — splitting into overlapping chunks`);
|
|
59
|
+
const chunks = splitAudioFile(params.filePath, totalDuration);
|
|
60
|
+
if (chunks.length === 0) {
|
|
61
|
+
return { content: [{ type: 'text', text: 'Error: ffmpeg failed to split file into chunks.' }], isError: true };
|
|
62
|
+
}
|
|
63
|
+
onProgress(`Split into ${chunks.length} chunks — submitting all async`);
|
|
64
|
+
// Submit all chunks async (fast — each returns 202)
|
|
65
|
+
const submitted = [];
|
|
66
|
+
for (const chunk of chunks) {
|
|
67
|
+
const base64 = readFileSync(chunk.path).toString('base64');
|
|
68
|
+
const accepted = await client.submitClassify({
|
|
69
|
+
base64,
|
|
70
|
+
fileName: `${basename(params.filePath)}_chunk${chunk.index}.mp3`,
|
|
71
|
+
threshold: params.threshold,
|
|
72
|
+
filterMinDurationSeconds: params.filterMinDurationSeconds,
|
|
73
|
+
});
|
|
74
|
+
submitted.push({ chunk, requestId: accepted.requestId });
|
|
75
|
+
onProgress(`Chunk ${chunk.index} submitted: ${accepted.requestId} (${chunk.startTime}s-${chunk.startTime + chunk.duration}s)`);
|
|
76
|
+
}
|
|
77
|
+
// Poll all chunks for results
|
|
78
|
+
const chunkResults = [];
|
|
79
|
+
for (const { chunk, requestId } of submitted) {
|
|
80
|
+
onProgress(`Polling chunk ${chunk.index} (${requestId})...`);
|
|
81
|
+
const result = await client.pollJob(requestId, onProgress);
|
|
82
|
+
chunkResults.push({ chunk, result });
|
|
83
|
+
}
|
|
84
|
+
// Cleanup temp files
|
|
85
|
+
cleanupChunks(chunks);
|
|
86
|
+
// Merge results with timestamp offsets and deduplication
|
|
87
|
+
const merged = mergeChunkResults(chunkResults, basename(params.filePath), totalDuration);
|
|
88
|
+
onProgress(`Merged: ${merged.eventCount} events from ${chunks.length} chunks`);
|
|
89
|
+
return { content: [{ type: 'text', text: JSON.stringify(merged, null, 2) }] };
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
93
|
+
return { content: [{ type: 'text', text: `Classification failed: ${msg}` }], isError: true };
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=classify-audio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify-audio.js","sourceRoot":"","sources":["../../src/tools/classify-audio.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +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,EAAY,MAAM,aAAa,CAAC;AAK3D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAsHpF"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { readFileSync, existsSync, statSync } from 'fs';
|
|
3
|
+
import { basename, extname } from 'path';
|
|
4
|
+
import { HEAR_API } from '@h-ear/core';
|
|
5
|
+
const formats = HEAR_API.SUPPORTED_FORMATS.join(', ');
|
|
6
|
+
const maxMB = HEAR_API.MAX_FILE_SIZE_BYTES / (1024 * 1024);
|
|
7
|
+
export function registerClassifyBatch(server, client) {
|
|
8
|
+
server.tool('classifyBatch', `Classify multiple audio files. Provide filePaths for local files (recommended) or files with URLs. Local files: submits all async, then polls for results. URL files: uses batch endpoint with webhook. Max ${HEAR_API.MAX_BATCH_FILES} files. Requires API key (HEAR_API_KEY).`, {
|
|
9
|
+
filePaths: z.array(z.string()).min(1).max(HEAR_API.MAX_BATCH_FILES).optional()
|
|
10
|
+
.describe('Array of local audio file paths to classify. Recommended.'),
|
|
11
|
+
files: z.array(z.object({
|
|
12
|
+
url: z.string().url().describe('Public URL to audio file.'),
|
|
13
|
+
id: z.string().optional().describe('Client-provided tracking ID.'),
|
|
14
|
+
})).min(1).max(HEAR_API.MAX_BATCH_FILES).optional()
|
|
15
|
+
.describe('Array of public audio URLs (alternative to filePaths). Requires callbackUrl.'),
|
|
16
|
+
callbackUrl: z.string().url().optional()
|
|
17
|
+
.describe('Webhook URL (HTTPS) for async results. Required when using URL-based files.'),
|
|
18
|
+
callbackSecret: z.string().optional()
|
|
19
|
+
.describe('HMAC-SHA256 secret for webhook signature verification.'),
|
|
20
|
+
threshold: z.number().min(0).max(1).default(0.3)
|
|
21
|
+
.describe('Confidence threshold for all files.'),
|
|
22
|
+
filterMinDurationSeconds: z.number().min(0.1).max(595).optional()
|
|
23
|
+
.describe('Minimum event duration filter in seconds.'),
|
|
24
|
+
}, async (params) => {
|
|
25
|
+
try {
|
|
26
|
+
// Local file mode: submit all async, then poll all
|
|
27
|
+
if (params.filePaths) {
|
|
28
|
+
const jobs = [];
|
|
29
|
+
// Phase 1: Submit all files (fast — each returns 202 immediately)
|
|
30
|
+
for (const filePath of params.filePaths) {
|
|
31
|
+
if (!existsSync(filePath)) {
|
|
32
|
+
jobs.push({ file: filePath, error: 'File not found' });
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const stat = statSync(filePath);
|
|
36
|
+
if (stat.size > HEAR_API.MAX_FILE_SIZE_BYTES) {
|
|
37
|
+
jobs.push({ file: filePath, error: `File too large: ${(stat.size / 1024 / 1024).toFixed(1)}MB` });
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const ext = extname(filePath).toLowerCase().replace('.', '').toUpperCase();
|
|
41
|
+
if (!HEAR_API.SUPPORTED_FORMATS.includes(ext)) {
|
|
42
|
+
jobs.push({ file: filePath, error: `Unsupported format: ${ext}` });
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const base64 = readFileSync(filePath).toString('base64');
|
|
47
|
+
const accepted = await client.submitClassify({
|
|
48
|
+
base64,
|
|
49
|
+
fileName: basename(filePath),
|
|
50
|
+
threshold: params.threshold,
|
|
51
|
+
filterMinDurationSeconds: params.filterMinDurationSeconds,
|
|
52
|
+
});
|
|
53
|
+
jobs.push({ file: basename(filePath), requestId: accepted.requestId });
|
|
54
|
+
process.stderr.write(`[h-ear-mcp] Submitted ${basename(filePath)} → ${accepted.requestId}\n`);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
jobs.push({ file: basename(filePath), error: err instanceof Error ? err.message : String(err) });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Phase 2: Poll all submitted jobs for results
|
|
61
|
+
const results = [];
|
|
62
|
+
for (const job of jobs) {
|
|
63
|
+
if (job.error) {
|
|
64
|
+
results.push({ file: job.file, status: 'error', error: job.error });
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const onProgress = (msg) => process.stderr.write(`[h-ear-mcp] ${msg}\n`);
|
|
69
|
+
const result = await client.pollJob(job.requestId, onProgress);
|
|
70
|
+
results.push({ file: job.file, status: 'classified', result });
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
results.push({ file: job.file, status: 'error', error: err instanceof Error ? err.message : String(err) });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const summary = {
|
|
77
|
+
mode: 'local-async',
|
|
78
|
+
totalFiles: params.filePaths.length,
|
|
79
|
+
classified: results.filter(r => r.status === 'classified').length,
|
|
80
|
+
failed: results.filter(r => r.status === 'error').length,
|
|
81
|
+
results,
|
|
82
|
+
};
|
|
83
|
+
return { content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }] };
|
|
84
|
+
}
|
|
85
|
+
// URL mode: use batch endpoint
|
|
86
|
+
if (params.files) {
|
|
87
|
+
if (!params.callbackUrl) {
|
|
88
|
+
return {
|
|
89
|
+
content: [{ type: 'text', text: 'Error: callbackUrl is required when using URL-based files.' }],
|
|
90
|
+
isError: true,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const result = await client.classifyBatch({
|
|
94
|
+
files: params.files,
|
|
95
|
+
callbackUrl: params.callbackUrl,
|
|
96
|
+
callbackSecret: params.callbackSecret,
|
|
97
|
+
threshold: params.threshold,
|
|
98
|
+
filterMinDurationSeconds: params.filterMinDurationSeconds,
|
|
99
|
+
});
|
|
100
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
content: [{ type: 'text', text: 'Error: Provide filePaths (local files) or files (URLs).' }],
|
|
104
|
+
isError: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
109
|
+
return {
|
|
110
|
+
content: [{ type: 'text', text: `Batch classification failed: ${msg}` }],
|
|
111
|
+
isError: true,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=classify-batch.js.map
|
|
@@ -0,0 +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,EAAsB,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,OAAO,GAAG,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtD,MAAM,KAAK,GAAG,QAAQ,CAAC,mBAAmB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AAE3D,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,MAAqB;IAC1E,MAAM,CAAC,IAAI,CACP,eAAe,EACf,+MAA+M,QAAQ,CAAC,eAAe,0CAA0C,EACjR;QACI,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,QAAQ,EAAE;aACzE,QAAQ,CAAC,2DAA2D,CAAC;QAC1E,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;YACpB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;YAC3D,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;SACrE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,QAAQ,EAAE;aAC9C,QAAQ,CAAC,8EAA8E,CAAC;QAC7F,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;aACnC,QAAQ,CAAC,6EAA6E,CAAC;QAC5F,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;aAChC,QAAQ,CAAC,wDAAwD,CAAC;QACvE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;aAC3C,QAAQ,CAAC,qCAAqC,CAAC;QACpD,wBAAwB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;aAC5D,QAAQ,CAAC,2CAA2C,CAAC;KAC7D,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACb,IAAI,CAAC;YACD,mDAAmD;YACnD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACnB,MAAM,IAAI,GAAgE,EAAE,CAAC;gBAE7E,kEAAkE;gBAClE,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;oBACtC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;wBACxB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;wBACvD,SAAS;oBACb,CAAC;oBACD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;oBAChC,IAAI,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC,mBAAmB,EAAE,CAAC;wBAC3C,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,mBAAmB,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;wBAClG,SAAS;oBACb,CAAC;oBACD,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;oBAC3E,IAAI,CAAE,QAAQ,CAAC,iBAAuC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;wBACnE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,uBAAuB,GAAG,EAAE,EAAE,CAAC,CAAC;wBACnE,SAAS;oBACb,CAAC;oBAED,IAAI,CAAC;wBACD,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;wBACzD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC;4BACzC,MAAM;4BACN,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC;4BAC5B,SAAS,EAAE,MAAM,CAAC,SAAS;4BAC3B,wBAAwB,EAAE,MAAM,CAAC,wBAAwB;yBAC5D,CAAC,CAAC;wBACH,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;wBACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,QAAQ,CAAC,QAAQ,CAAC,MAAM,QAAQ,CAAC,SAAS,IAAI,CAAC,CAAC;oBAClG,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACX,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACrG,CAAC;gBACL,CAAC;gBAED,+CAA+C;gBAC/C,MAAM,OAAO,GAA8E,EAAE,CAAC;gBAE9F,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;oBACrB,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;wBACZ,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;wBACpE,SAAS;oBACb,CAAC;oBACD,IAAI,CAAC;wBACD,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;wBACjF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,SAAU,EAAE,UAAU,CAAC,CAAC;wBAChE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;oBACnE,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACX,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBAC/G,CAAC;gBACL,CAAC;gBAED,MAAM,OAAO,GAAG;oBACZ,IAAI,EAAE,aAAa;oBACnB,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,MAAM;oBACnC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC,MAAM;oBACjE,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,MAAM;oBACxD,OAAO;iBACV,CAAC;gBAEF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;YAC5F,CAAC;YAED,+BAA+B;YAC/B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;oBACtB,OAAO;wBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,4DAA4D,EAAE,CAAC;wBACxG,OAAO,EAAE,IAAI;qBAChB,CAAC;gBACN,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC;oBACtC,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,cAAc,EAAE,MAAM,CAAC,cAAc;oBACrC,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,wBAAwB,EAAE,MAAM,CAAC,wBAAwB;iBAC5D,CAAC,CAAC;gBACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;YAC3F,CAAC;YAED,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,yDAAyD,EAAE,CAAC;gBACrG,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,gCAAgC,GAAG,EAAE,EAAE,CAAC;gBACjF,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-job.d.ts","sourceRoot":"","sources":["../../src/tools/get-job.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAoB7E"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerGetJob(server, client) {
|
|
3
|
+
server.tool('getJob', 'Get detailed results for a specific classification job, including all detected sound events and classifications. Requires API key (HEAR_API_KEY).', {
|
|
4
|
+
jobId: z.string().min(1)
|
|
5
|
+
.describe('The job ID returned from classifyAudio or classifyBatch.'),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
const result = await client.getJob(params.jobId);
|
|
9
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
return {
|
|
13
|
+
content: [{ type: 'text', text: `Get job failed: ${error instanceof Error ? error.message : String(error)}` }],
|
|
14
|
+
isError: true,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=get-job.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-job.js","sourceRoot":"","sources":["../../src/tools/get-job.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,cAAc,CAAC,MAAiB,EAAE,MAAqB;IACnE,MAAM,CAAC,IAAI,CACP,QAAQ,EACR,mJAAmJ,EACnJ;QACI,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;aACnB,QAAQ,CAAC,0DAA0D,CAAC;KAC5E,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACb,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACjD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC3F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,mBAAmB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;gBACvH,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-check.d.ts","sourceRoot":"","sources":["../../src/tools/health-check.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAiBlF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function registerHealthCheck(server, client) {
|
|
2
|
+
server.tool('healthCheck', 'Check H-ear API health and liveness. No authentication required. Returns status, version, and deployment timestamp.', {}, async () => {
|
|
3
|
+
try {
|
|
4
|
+
const result = await client.health();
|
|
5
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
return {
|
|
9
|
+
content: [{ type: 'text', text: `Health check failed: ${error instanceof Error ? error.message : String(error)}` }],
|
|
10
|
+
isError: true,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=health-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-check.js","sourceRoot":"","sources":["../../src/tools/health-check.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,MAAqB;IACxE,MAAM,CAAC,IAAI,CACP,aAAa,EACb,qHAAqH,EACrH,EAAE,EACF,KAAK,IAAI,EAAE;QACP,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;YACrC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC3F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;gBAC5H,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-classes.d.ts","sourceRoot":"","sources":["../../src/tools/list-classes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CA0BlF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerListClasses(server, client) {
|
|
3
|
+
server.tool('listClasses', 'List supported audio classification classes. Default taxonomy: audioset-yamnet-521 (521 classes across 7 categories). Also available: audioset-panns-527 (527 classes), species (6522 bird species). No authentication required.', {
|
|
4
|
+
taxonomy: z.enum(['audioset-yamnet-521', 'audioset-panns-527', 'species']).optional()
|
|
5
|
+
.describe('Taxonomy to query. Defaults to audioset-yamnet-521.'),
|
|
6
|
+
category: z.string().optional()
|
|
7
|
+
.describe('Filter by category name (case-insensitive partial match). E.g. "Animal", "Human sounds", "Music".'),
|
|
8
|
+
limit: z.number().int().positive().optional()
|
|
9
|
+
.describe('Max classes to return (pagination).'),
|
|
10
|
+
offset: z.number().int().min(0).optional()
|
|
11
|
+
.describe('Number of classes to skip (pagination).'),
|
|
12
|
+
}, async (params) => {
|
|
13
|
+
try {
|
|
14
|
+
const result = await client.listClasses(params);
|
|
15
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: 'text', text: `List classes failed: ${error instanceof Error ? error.message : String(error)}` }],
|
|
20
|
+
isError: true,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=list-classes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-classes.js","sourceRoot":"","sources":["../../src/tools/list-classes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,MAAqB;IACxE,MAAM,CAAC,IAAI,CACP,aAAa,EACb,kOAAkO,EAClO;QACI,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,qBAAqB,EAAE,oBAAoB,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE;aAChF,QAAQ,CAAC,qDAAqD,CAAC;QACpE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;aAC1B,QAAQ,CAAC,mGAAmG,CAAC;QAClH,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;aACxC,QAAQ,CAAC,qCAAqC,CAAC;QACpD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;aACrC,QAAQ,CAAC,yCAAyC,CAAC;KAC3D,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACb,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAChD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC3F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;gBAC5H,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-jobs.d.ts","sourceRoot":"","sources":["../../src/tools/list-jobs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAwB/E"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerListJobs(server, client) {
|
|
3
|
+
server.tool('listJobs', 'List recent classification jobs with status, file name, event count, and timestamps. Supports pagination. Requires API key (HEAR_API_KEY).', {
|
|
4
|
+
limit: z.number().int().positive().max(100).default(10)
|
|
5
|
+
.describe('Max jobs to return (1-100, default 10).'),
|
|
6
|
+
offset: z.number().int().min(0).default(0)
|
|
7
|
+
.describe('Number of jobs to skip (pagination).'),
|
|
8
|
+
status: z.enum(['processing', 'completed', 'failed']).optional()
|
|
9
|
+
.describe('Filter by job status.'),
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
try {
|
|
12
|
+
const result = await client.listJobs(params);
|
|
13
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: 'text', text: `List jobs failed: ${error instanceof Error ? error.message : String(error)}` }],
|
|
18
|
+
isError: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=list-jobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-jobs.js","sourceRoot":"","sources":["../../src/tools/list-jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,gBAAgB,CAAC,MAAiB,EAAE,MAAqB;IACrE,MAAM,CAAC,IAAI,CACP,UAAU,EACV,4IAA4I,EAC5I;QACI,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;aAClD,QAAQ,CAAC,yCAAyC,CAAC;QACxD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;aACrC,QAAQ,CAAC,sCAAsC,CAAC;QACrD,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;aAC3D,QAAQ,CAAC,uBAAuB,CAAC;KACzC,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACb,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC7C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC3F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,qBAAqB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;gBACzH,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/tools/usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAiB5E"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function registerUsage(server, client) {
|
|
2
|
+
server.tool('usage', 'Get H-ear API usage statistics: minutes used, calls today, quota remaining, active keys. Requires API key (HEAR_API_KEY).', {}, async () => {
|
|
3
|
+
try {
|
|
4
|
+
const result = await client.usage();
|
|
5
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
return {
|
|
9
|
+
content: [{ type: 'text', text: `Usage check failed: ${error instanceof Error ? error.message : String(error)}` }],
|
|
10
|
+
isError: true,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/tools/usage.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,aAAa,CAAC,MAAiB,EAAE,MAAqB;IAClE,MAAM,CAAC,IAAI,CACP,OAAO,EACP,2HAA2H,EAC3H,EAAE,EACF,KAAK,IAAI,EAAE;QACP,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACpC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC3F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,uBAAuB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;gBAC3H,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;AACN,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export all types from @h-ear/core for backwards compatibility.
|
|
3
|
+
*/
|
|
4
|
+
export type { ClassifyRequest, ClassifyResult, Classification, NoiseEvent, AsyncAccepted, BatchFile, BatchRequest, BatchAccepted, AudioClass, ClassesResult, HealthResult, UsageResult, JobSummary, JobsResult, JobResult, WebhookRegister, WebhookResult, ApiErrorBody, } from '@h-ear/core';
|
|
5
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,YAAY,EACR,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAC1E,SAAS,EAAE,YAAY,EAAE,aAAa,EACtC,UAAU,EAAE,aAAa,EACzB,YAAY,EACZ,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAC9C,eAAe,EAAE,aAAa,EAC9B,YAAY,GACf,MAAM,aAAa,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@h-ear/mcp-server",
|
|
3
|
+
"version": "0.1.0-dev.202603280915",
|
|
4
|
+
"description": "MCP server for the H-ear World audio classification API — connect Claude, ChatGPT, and other AI agents to 521+ sound classes",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"h-ear-mcp": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./server": {
|
|
17
|
+
"import": "./dist/server.js",
|
|
18
|
+
"types": "./dist/server.d.ts"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"build:watch": "tsc --watch",
|
|
27
|
+
"clean": "rm -rf dist",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:unit": "vitest run --config vitest.config.ts",
|
|
30
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
31
|
+
"prepublishOnly": "npm run build",
|
|
32
|
+
"version:patch": "npm version patch --no-git-tag-version",
|
|
33
|
+
"version:minor": "npm version minor --no-git-tag-version",
|
|
34
|
+
"version:major": "npm version major --no-git-tag-version"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@h-ear/core": "*",
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.26.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.11.0",
|
|
42
|
+
"typescript": "^5.9.3",
|
|
43
|
+
"vitest": "^3.1.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"README.md"
|
|
51
|
+
],
|
|
52
|
+
"keywords": [
|
|
53
|
+
"mcp",
|
|
54
|
+
"model-context-protocol",
|
|
55
|
+
"audio",
|
|
56
|
+
"classification",
|
|
57
|
+
"h-ear",
|
|
58
|
+
"noise",
|
|
59
|
+
"sound",
|
|
60
|
+
"ai-agent"
|
|
61
|
+
],
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"repository": {
|
|
64
|
+
"type": "git",
|
|
65
|
+
"url": "https://github.com/noise-control-monitor/ncm-monorepo",
|
|
66
|
+
"directory": "packages/mcp-server"
|
|
67
|
+
}
|
|
68
|
+
}
|