@mindstone/mcp-server-elevenlabs 0.2.2 → 0.4.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 +68 -4
- package/dist/bridge.d.ts +1 -0
- package/dist/client.d.ts +34 -3
- package/dist/client.js +141 -35
- package/dist/endpoints.d.ts +34 -0
- package/dist/endpoints.js +37 -0
- package/dist/error-detail.d.ts +5 -0
- package/dist/error-detail.js +20 -0
- package/dist/server.js +9 -1
- package/dist/tools/account.d.ts +3 -0
- package/dist/tools/account.js +105 -0
- package/dist/tools/alignment.d.ts +3 -0
- package/dist/tools/alignment.js +55 -0
- package/dist/tools/audio-isolation.d.ts +3 -0
- package/dist/tools/audio-isolation.js +51 -0
- package/dist/tools/configure.js +18 -6
- package/dist/tools/dialogue.d.ts +3 -0
- package/dist/tools/dialogue.js +70 -0
- package/dist/tools/dubbing.d.ts +3 -0
- package/dist/tools/dubbing.js +234 -0
- package/dist/tools/file-input.d.ts +28 -0
- package/dist/tools/file-input.js +77 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.js +8 -0
- package/dist/tools/music.js +152 -27
- package/dist/tools/path-safety.d.ts +1 -0
- package/dist/tools/path-safety.js +4 -1
- package/dist/tools/speech.js +90 -34
- package/dist/tools/transcription.js +34 -61
- package/dist/tools/voice-changer.d.ts +3 -0
- package/dist/tools/voice-changer.js +62 -0
- package/dist/tools/voice-clone.d.ts +3 -0
- package/dist/tools/voice-clone.js +104 -0
- package/dist/tools/voice-design.d.ts +3 -0
- package/dist/tools/voice-design.js +153 -0
- package/dist/tools/voices.js +167 -13
- package/dist/types.d.ts +110 -13
- package/dist/types.js +20 -4
- package/dist/untrusted-content.d.ts +45 -0
- package/dist/untrusted-content.js +104 -0
- package/package.json +2 -2
package/dist/types.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export const REQUEST_TIMEOUT_MS = 30_000;
|
|
2
|
+
/** Per-call override for slow synchronous endpoints (dialogue, voice design). */
|
|
3
|
+
export const LONG_REQUEST_TIMEOUT_MS = 120_000;
|
|
2
4
|
export class ElevenLabsError extends Error {
|
|
3
5
|
code;
|
|
4
6
|
resolution;
|
|
@@ -9,6 +11,9 @@ export class ElevenLabsError extends Error {
|
|
|
9
11
|
this.name = 'ElevenLabsError';
|
|
10
12
|
}
|
|
11
13
|
}
|
|
14
|
+
import { envelopeApiErrorDetail } from './error-detail.js';
|
|
15
|
+
/** Actionable resolution when a voice_id or voice_name cannot be resolved. */
|
|
16
|
+
export const VOICE_NOT_FOUND_RESOLUTION = 'Use list_voices to browse voices on this account, or search_shared_voices to find voices in the public library. Pass the exact voice_id to generate_speech.';
|
|
12
17
|
/**
|
|
13
18
|
* Resolve an error status code to an actionable resolution string.
|
|
14
19
|
*/
|
|
@@ -18,17 +23,28 @@ export function getErrorResolution(status, detail) {
|
|
|
18
23
|
return 'Authentication failed. Check your ElevenLabs API key in Settings. Get one at https://elevenlabs.io/app/settings/api-keys';
|
|
19
24
|
}
|
|
20
25
|
if (status === 403 || msg.includes('quota') || msg.includes('limit') || msg.includes('credits')) {
|
|
21
|
-
return 'Insufficient credits or quota exceeded.
|
|
26
|
+
return ('Insufficient credits or quota exceeded. Call check_subscription to see remaining characters and the next reset date, ' +
|
|
27
|
+
'or check usage at https://elevenlabs.io/app/usage');
|
|
22
28
|
}
|
|
23
29
|
if (status === 422 || msg.includes('validation')) {
|
|
24
|
-
|
|
30
|
+
const base = 'Invalid request parameters. Check the input values and try again.';
|
|
31
|
+
if (detail) {
|
|
32
|
+
return `${base} Field issues: ${envelopeApiErrorDetail(detail)}`;
|
|
33
|
+
}
|
|
34
|
+
return base;
|
|
25
35
|
}
|
|
26
36
|
if (status === 429) {
|
|
27
37
|
return 'Rate limited. Wait a moment and try again.';
|
|
28
38
|
}
|
|
29
|
-
if (msg.includes('
|
|
39
|
+
if (msg.includes('unsupported_content_type')) {
|
|
40
|
+
return "The uploaded file type isn't supported for this operation. Provide a supported audio/video format (mp3, wav, mp4, …).";
|
|
41
|
+
}
|
|
42
|
+
if (msg.includes('content policy') ||
|
|
43
|
+
msg.includes('moderation') ||
|
|
44
|
+
msg.includes('flagged') ||
|
|
45
|
+
msg.includes('policy violation')) {
|
|
30
46
|
return 'Content policy violation. Try a different prompt.';
|
|
31
47
|
}
|
|
32
|
-
return 'Please try again. If the issue persists, check your API key
|
|
48
|
+
return 'Please try again. If the issue persists, call check_subscription for credit status or check your API key at https://elevenlabs.io/app/settings/api-keys';
|
|
33
49
|
}
|
|
34
50
|
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AGENTS.md security invariant #6 — content fetched from an external system
|
|
3
|
+
* MUST be wrapped in an `<untrusted-content source="…">…</untrusted-content>`
|
|
4
|
+
* envelope (with close-tag breakout escaping) before it is returned to the
|
|
5
|
+
* LLM, so the model treats third-party / attacker-controllable text as DATA,
|
|
6
|
+
* not as instructions.
|
|
7
|
+
*
|
|
8
|
+
* This is the canonical implementation a new connector ships with. It is a
|
|
9
|
+
* VENDORED copy of the shared reference in `test-harness/src/untrusted-content.ts`
|
|
10
|
+
* — connectors cannot `import` the test-harness at runtime (it is a
|
|
11
|
+
* test/dev-only `file:` dependency that is never published into a connector's
|
|
12
|
+
* `dist/`), so the helper lives in the connector's own runtime source. Keep
|
|
13
|
+
* this byte-for-byte in sync with the shared reference; do NOT weaken the
|
|
14
|
+
* escaping back to a simple `replaceAll` (that family misses whitespace / case
|
|
15
|
+
* close-tag variants like `</untrusted-content >` / `</UNTRUSTED-CONTENT>`).
|
|
16
|
+
*
|
|
17
|
+
* `scripts/check-untrusted-coverage.mjs` greps for a reference to
|
|
18
|
+
* `untrusted-content` in any connector that talks to an external system; this
|
|
19
|
+
* file (and the call sites that import from it) is what satisfies that gate.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Wrap a single untrusted string in an `<untrusted-content source="…">`
|
|
23
|
+
* envelope, escaping any embedded close-tag variant so the envelope cannot be
|
|
24
|
+
* broken out of. `undefined` passes through untouched. Idempotent for the same
|
|
25
|
+
* `source`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function wrapUntrusted(text: string | undefined, source: string): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Strip one `<untrusted-content>` envelope from `text` if present, returning raw
|
|
30
|
+
* strings unchanged. This is intentionally one-layer and idempotent for already
|
|
31
|
+
* raw input so callers can accept either displayed wrapped content or manually
|
|
32
|
+
* authored content.
|
|
33
|
+
*/
|
|
34
|
+
export declare function unwrapUntrusted(text: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Recursively wrap every string key and value reachable inside `value`.
|
|
37
|
+
* Non-string leaves pass through unchanged.
|
|
38
|
+
*/
|
|
39
|
+
export declare function wrapUntrustedJsonStrings<T>(value: T, source: string): T;
|
|
40
|
+
/**
|
|
41
|
+
* Recursively unwrap every string key and value reachable inside `value`.
|
|
42
|
+
* Non-string leaves pass through unchanged.
|
|
43
|
+
*/
|
|
44
|
+
export declare function unwrapUntrustedJsonStrings<T>(value: T): T;
|
|
45
|
+
//# sourceMappingURL=untrusted-content.d.ts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AGENTS.md security invariant #6 — content fetched from an external system
|
|
3
|
+
* MUST be wrapped in an `<untrusted-content source="…">…</untrusted-content>`
|
|
4
|
+
* envelope (with close-tag breakout escaping) before it is returned to the
|
|
5
|
+
* LLM, so the model treats third-party / attacker-controllable text as DATA,
|
|
6
|
+
* not as instructions.
|
|
7
|
+
*
|
|
8
|
+
* This is the canonical implementation a new connector ships with. It is a
|
|
9
|
+
* VENDORED copy of the shared reference in `test-harness/src/untrusted-content.ts`
|
|
10
|
+
* — connectors cannot `import` the test-harness at runtime (it is a
|
|
11
|
+
* test/dev-only `file:` dependency that is never published into a connector's
|
|
12
|
+
* `dist/`), so the helper lives in the connector's own runtime source. Keep
|
|
13
|
+
* this byte-for-byte in sync with the shared reference; do NOT weaken the
|
|
14
|
+
* escaping back to a simple `replaceAll` (that family misses whitespace / case
|
|
15
|
+
* close-tag variants like `</untrusted-content >` / `</UNTRUSTED-CONTENT>`).
|
|
16
|
+
*
|
|
17
|
+
* `scripts/check-untrusted-coverage.mjs` greps for a reference to
|
|
18
|
+
* `untrusted-content` in any connector that talks to an external system; this
|
|
19
|
+
* file (and the call sites that import from it) is what satisfies that gate.
|
|
20
|
+
*/
|
|
21
|
+
const UNTRUSTED_CLOSE_TAG_VARIANT = /<\/untrusted-content\s*>/gi;
|
|
22
|
+
const ESCAPED_UNTRUSTED_CLOSE_TAG = '<\\/untrusted-content>';
|
|
23
|
+
const UNTRUSTED_ENVELOPE = /^<untrusted-content source="[^"]*">([\s\S]*)<\/untrusted-content>$/;
|
|
24
|
+
function escapeAttr(s) {
|
|
25
|
+
return s.replaceAll('&', '&').replaceAll('"', '"').replaceAll('<', '<').replaceAll('>', '>');
|
|
26
|
+
}
|
|
27
|
+
function escapeCloseTagSentinels(s) {
|
|
28
|
+
return s.replace(UNTRUSTED_CLOSE_TAG_VARIANT, ESCAPED_UNTRUSTED_CLOSE_TAG);
|
|
29
|
+
}
|
|
30
|
+
function unescapeCloseTagSentinels(s) {
|
|
31
|
+
return s.replaceAll(ESCAPED_UNTRUSTED_CLOSE_TAG, '</untrusted-content>');
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Wrap a single untrusted string in an `<untrusted-content source="…">`
|
|
35
|
+
* envelope, escaping any embedded close-tag variant so the envelope cannot be
|
|
36
|
+
* broken out of. `undefined` passes through untouched. Idempotent for the same
|
|
37
|
+
* `source`.
|
|
38
|
+
*/
|
|
39
|
+
export function wrapUntrusted(text, source) {
|
|
40
|
+
if (text === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
const open = `<untrusted-content source="${escapeAttr(source)}">`;
|
|
43
|
+
const close = '</untrusted-content>';
|
|
44
|
+
if (text.startsWith(open) && text.endsWith(close) && text.length >= open.length + close.length) {
|
|
45
|
+
const inner = text.slice(open.length, text.length - close.length);
|
|
46
|
+
if (!UNTRUSTED_CLOSE_TAG_VARIANT.test(inner)) {
|
|
47
|
+
UNTRUSTED_CLOSE_TAG_VARIANT.lastIndex = 0;
|
|
48
|
+
return text;
|
|
49
|
+
}
|
|
50
|
+
UNTRUSTED_CLOSE_TAG_VARIANT.lastIndex = 0;
|
|
51
|
+
}
|
|
52
|
+
return `${open}${escapeCloseTagSentinels(text)}${close}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Strip one `<untrusted-content>` envelope from `text` if present, returning raw
|
|
56
|
+
* strings unchanged. This is intentionally one-layer and idempotent for already
|
|
57
|
+
* raw input so callers can accept either displayed wrapped content or manually
|
|
58
|
+
* authored content.
|
|
59
|
+
*/
|
|
60
|
+
export function unwrapUntrusted(text) {
|
|
61
|
+
const match = UNTRUSTED_ENVELOPE.exec(text);
|
|
62
|
+
if (!match)
|
|
63
|
+
return text;
|
|
64
|
+
return unescapeCloseTagSentinels(match[1]);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Recursively wrap every string key and value reachable inside `value`.
|
|
68
|
+
* Non-string leaves pass through unchanged.
|
|
69
|
+
*/
|
|
70
|
+
export function wrapUntrustedJsonStrings(value, source) {
|
|
71
|
+
if (typeof value === 'string') {
|
|
72
|
+
return wrapUntrusted(value, source);
|
|
73
|
+
}
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
return value.map((item) => wrapUntrustedJsonStrings(item, source));
|
|
76
|
+
}
|
|
77
|
+
if (value && typeof value === 'object') {
|
|
78
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
79
|
+
wrapUntrusted(key, source) ?? key,
|
|
80
|
+
wrapUntrustedJsonStrings(item, source),
|
|
81
|
+
]));
|
|
82
|
+
}
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Recursively unwrap every string key and value reachable inside `value`.
|
|
87
|
+
* Non-string leaves pass through unchanged.
|
|
88
|
+
*/
|
|
89
|
+
export function unwrapUntrustedJsonStrings(value) {
|
|
90
|
+
if (typeof value === 'string') {
|
|
91
|
+
return unwrapUntrusted(value);
|
|
92
|
+
}
|
|
93
|
+
if (Array.isArray(value)) {
|
|
94
|
+
return value.map((item) => unwrapUntrustedJsonStrings(item));
|
|
95
|
+
}
|
|
96
|
+
if (value && typeof value === 'object') {
|
|
97
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
98
|
+
unwrapUntrusted(key),
|
|
99
|
+
unwrapUntrustedJsonStrings(item),
|
|
100
|
+
]));
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=untrusted-content.js.map
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindstone/mcp-server-elevenlabs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"mcpName": "io.github.mindstone/mcp-server-elevenlabs",
|
|
5
|
-
"description": "ElevenLabs MCP server for Model Context Protocol hosts
|
|
5
|
+
"description": "ElevenLabs MCP server for Model Context Protocol hosts — music, TTS, sound effects, voices, transcription",
|
|
6
6
|
"license": "FSL-1.1-MIT",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"bin": {
|