@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.
Files changed (41) hide show
  1. package/README.md +68 -4
  2. package/dist/bridge.d.ts +1 -0
  3. package/dist/client.d.ts +34 -3
  4. package/dist/client.js +141 -35
  5. package/dist/endpoints.d.ts +34 -0
  6. package/dist/endpoints.js +37 -0
  7. package/dist/error-detail.d.ts +5 -0
  8. package/dist/error-detail.js +20 -0
  9. package/dist/server.js +9 -1
  10. package/dist/tools/account.d.ts +3 -0
  11. package/dist/tools/account.js +105 -0
  12. package/dist/tools/alignment.d.ts +3 -0
  13. package/dist/tools/alignment.js +55 -0
  14. package/dist/tools/audio-isolation.d.ts +3 -0
  15. package/dist/tools/audio-isolation.js +51 -0
  16. package/dist/tools/configure.js +18 -6
  17. package/dist/tools/dialogue.d.ts +3 -0
  18. package/dist/tools/dialogue.js +70 -0
  19. package/dist/tools/dubbing.d.ts +3 -0
  20. package/dist/tools/dubbing.js +234 -0
  21. package/dist/tools/file-input.d.ts +28 -0
  22. package/dist/tools/file-input.js +77 -0
  23. package/dist/tools/index.d.ts +8 -0
  24. package/dist/tools/index.js +8 -0
  25. package/dist/tools/music.js +152 -27
  26. package/dist/tools/path-safety.d.ts +1 -0
  27. package/dist/tools/path-safety.js +4 -1
  28. package/dist/tools/speech.js +90 -34
  29. package/dist/tools/transcription.js +34 -61
  30. package/dist/tools/voice-changer.d.ts +3 -0
  31. package/dist/tools/voice-changer.js +62 -0
  32. package/dist/tools/voice-clone.d.ts +3 -0
  33. package/dist/tools/voice-clone.js +104 -0
  34. package/dist/tools/voice-design.d.ts +3 -0
  35. package/dist/tools/voice-design.js +153 -0
  36. package/dist/tools/voices.js +167 -13
  37. package/dist/types.d.ts +110 -13
  38. package/dist/types.js +20 -4
  39. package/dist/untrusted-content.d.ts +45 -0
  40. package/dist/untrusted-content.js +104 -0
  41. 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. Check usage at https://elevenlabs.io/app/usage';
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
- return 'Invalid request parameters. Check the input values and try again.';
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('content') || msg.includes('policy') || msg.includes('moderation')) {
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 and credits at https://elevenlabs.io/app/settings/api-keys';
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('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
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.2.2",
3
+ "version": "0.4.0",
4
4
  "mcpName": "io.github.mindstone/mcp-server-elevenlabs",
5
- "description": "ElevenLabs MCP server for Model Context Protocol hosts \u2014 music, TTS, sound effects, voices, transcription",
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": {