@rymi/mcp 2.8.1 → 2.9.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 (3) hide show
  1. package/README.md +5 -1
  2. package/dist/index.js +274 -92
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -104,6 +104,10 @@ Run the server on your own machine — it speaks **stdio** by default and talks
104
104
  <td><code>create_call</code> · <code>batch_call</code> · <code>end_call</code> · <code>add_call_participant</code></td>
105
105
  </tr>
106
106
  <tr>
107
+ <td><b>Task calls</b></td>
108
+ <td><code>place_task_call</code> · <code>wait_for_call</code></td>
109
+ </tr>
110
+ <tr>
107
111
  <td><b>Numbers</b></td>
108
112
  <td><code>list_numbers</code> · <code>register_number</code> · <code>attach_number</code> · <code>remove_number</code></td>
109
113
  </tr>
@@ -151,7 +155,7 @@ Run the server on your own machine — it speaks **stdio** by default and talks
151
155
 
152
156
  ### ⚠️ Gated tools
153
157
 
154
- `create_call` and `batch_call` place **real, billable** outbound calls; `publish_agent` flips an agent **live** to end users.
158
+ `place_task_call`, `create_call` and `batch_call` place **real, billable** outbound calls; `publish_agent` flips an agent **live** to end users.
155
159
 
156
160
  What restrains them depends on which server you're talking to.
157
161
 
package/dist/index.js CHANGED
@@ -153,6 +153,7 @@ var init_studioCatalog = __esm({
153
153
  "gpt-realtime-2.1-mini",
154
154
  "gpt-live-1",
155
155
  "gemini-3.8-live",
156
+ "gemini-3.8-live-extended-thinking",
156
157
  "grok-voice-3.0",
157
158
  "grok-voice-think-fast-2.0",
158
159
  "ultravox-v0.7",
@@ -176,10 +177,6 @@ var init_studioCatalog = __esm({
176
177
  "gemini-2.5-flash-native-audio-preview-12-2025",
177
178
  "nova-sonic-1",
178
179
  "nova-sonic-2",
179
- "llama-3.1-8b-instant",
180
- "llama-3.3-70b-versatile",
181
- "meta-llama/llama-4-scout-17b-16e-instruct",
182
- "qwen/qwen3-32b",
183
180
  "openai/gpt-oss-20b",
184
181
  "openai/gpt-oss-120b",
185
182
  "openai/gpt-oss-safeguard-20b",
@@ -190,6 +187,7 @@ var init_studioCatalog = __esm({
190
187
  "llama-4-scout",
191
188
  "deepseek-v4-flash",
192
189
  "deepseek-v4-pro",
190
+ "grok-4.7",
193
191
  "grok-4-1-fast",
194
192
  "grok-3-mini",
195
193
  "grok-3-mini-fast",
@@ -253,6 +251,7 @@ var init_studioCatalog = __esm({
253
251
  ],
254
252
  tts: [
255
253
  "aura-2",
254
+ "flux-tts",
256
255
  "gemini-2.5-pro-preview-tts",
257
256
  "sonic-3.5",
258
257
  "sonic-turbo",
@@ -263,6 +262,7 @@ var init_studioCatalog = __esm({
263
262
  "gemini-3.1-flash-tts-preview",
264
263
  "tts-1",
265
264
  "bulbul:v3",
265
+ "bulbul:v4-flash",
266
266
  "gpt-4o-mini-tts",
267
267
  "eleven_multilingual_v2",
268
268
  // Additional TTS
@@ -1218,6 +1218,80 @@ var init_variables = __esm({
1218
1218
  }
1219
1219
  });
1220
1220
 
1221
+ // ../shared-types/dist/safeUrl.js
1222
+ var safeUrl_exports = {};
1223
+ __export(safeUrl_exports, {
1224
+ isSafeAudioUrl: () => isSafeAudioUrl,
1225
+ isSafeUrl: () => isSafeUrl
1226
+ });
1227
+ function unmapIpv4(v6) {
1228
+ const dotted = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(v6);
1229
+ if (dotted)
1230
+ return dotted[1];
1231
+ const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(v6);
1232
+ if (!hex)
1233
+ return null;
1234
+ const high = parseInt(hex[1], 16);
1235
+ const low = parseInt(hex[2], 16);
1236
+ return [high >> 8, high & 255, low >> 8, low & 255].join(".");
1237
+ }
1238
+ function isBlockedHost(host) {
1239
+ const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
1240
+ if (bare === "localhost" || bare === "0.0.0.0" || bare === "::")
1241
+ return true;
1242
+ if (bare.endsWith(".local") || bare.endsWith(".internal") || bare.endsWith(".localhost"))
1243
+ return true;
1244
+ if (/^127\./.test(bare))
1245
+ return true;
1246
+ if (/^10\./.test(bare))
1247
+ return true;
1248
+ if (/^192\.168\./.test(bare))
1249
+ return true;
1250
+ if (/^172\.(1[6-9]|2\d|3[01])\./.test(bare))
1251
+ return true;
1252
+ if (/^169\.254\./.test(bare))
1253
+ return true;
1254
+ const v6 = bare.toLowerCase();
1255
+ if (v6 === "::1")
1256
+ return true;
1257
+ if (/^f[cd][0-9a-f]{2}:/.test(v6))
1258
+ return true;
1259
+ if (/^fe[89ab][0-9a-f]:/.test(v6))
1260
+ return true;
1261
+ const mapped = unmapIpv4(v6);
1262
+ if (mapped)
1263
+ return isBlockedHost(mapped);
1264
+ return false;
1265
+ }
1266
+ function isSafeUrl(raw, options = {}) {
1267
+ const protocols = options.protocols ?? ["http:", "https:"];
1268
+ let url;
1269
+ try {
1270
+ url = new URL(raw);
1271
+ } catch {
1272
+ return false;
1273
+ }
1274
+ if (!protocols.includes(url.protocol))
1275
+ return false;
1276
+ return !isBlockedHost(url.hostname.toLowerCase());
1277
+ }
1278
+ function isSafeAudioUrl(raw) {
1279
+ if (!isSafeUrl(raw, { protocols: ["https:"] }))
1280
+ return false;
1281
+ try {
1282
+ const parsed = new URL(raw.trim());
1283
+ const path = parsed.pathname.toLowerCase();
1284
+ return path.endsWith(".wav") || path.endsWith(".mp3");
1285
+ } catch {
1286
+ return false;
1287
+ }
1288
+ }
1289
+ var init_safeUrl = __esm({
1290
+ "../shared-types/dist/safeUrl.js"() {
1291
+ "use strict";
1292
+ }
1293
+ });
1294
+
1221
1295
  // ../../node_modules/.pnpm/dlv@1.1.3/node_modules/dlv/dist/dlv.es.js
1222
1296
  function dlv_es_default(t, e, l, n, r) {
1223
1297
  for (e = e.split ? e.split(".") : e, n = 0; n < e.length; n++) t = t ? t[e[n]] : r;
@@ -1407,9 +1481,10 @@ var AGENT_CONFIG_SCHEMA_VERSION, CHANGE_SET_VALIDATORS, DERIVED_SERIALIZE_RULES,
1407
1481
  var init_agentConfigSchema = __esm({
1408
1482
  "../shared-types/dist/agentConfigSchema.js"() {
1409
1483
  "use strict";
1484
+ init_safeUrl();
1410
1485
  init_dlv_es();
1411
1486
  init_dist();
1412
- AGENT_CONFIG_SCHEMA_VERSION = "2026-05-20";
1487
+ AGENT_CONFIG_SCHEMA_VERSION = "2026-10-23";
1413
1488
  CHANGE_SET_VALIDATORS = [
1414
1489
  // Catalog validation for enum-catalog model + language fields.
1415
1490
  // Skips silently when no catalogs are wired (legacy callers / tests).
@@ -1453,6 +1528,21 @@ var init_agentConfigSchema = __esm({
1453
1528
  }
1454
1529
  }
1455
1530
  return errors.length === 0 ? { valid: true } : { valid: false, errors };
1531
+ },
1532
+ (cfg, changes) => {
1533
+ if (!changes.some((c) => c.key === "background_sound" || c.key === "background_sound_url"))
1534
+ return { valid: true };
1535
+ const errors = [];
1536
+ const url = cfg.background_sound_url;
1537
+ const mode = cfg.background_sound;
1538
+ const hasUrl = typeof url === "string" && url.trim() !== "";
1539
+ if (hasUrl && (url.length > 512 || !isSafeAudioUrl(url))) {
1540
+ errors.push({ field: "background_sound_url", error: "Use a public https:// link to a .wav or .mp3 file (up to 512 characters)." });
1541
+ }
1542
+ if (mode === "custom" && !hasUrl) {
1543
+ errors.push({ field: "background_sound_url", error: "Custom background sound needs a link to a .wav or .mp3 file." });
1544
+ }
1545
+ return errors.length === 0 ? { valid: true } : { valid: false, errors };
1456
1546
  }
1457
1547
  ];
1458
1548
  DERIVED_SERIALIZE_RULES = [
@@ -1718,6 +1808,7 @@ var init_agentConfigSchema = __esm({
1718
1808
  // ── Features ────────────────────────────────────────────────────────────
1719
1809
  { key: "transcriptionEnabled", label: "Transcription", description: "Generate text transcripts of calls.", default: true, type: { kind: "boolean" }, apiPath: "features.transcription_enabled", section: "features", editableIn: "both" },
1720
1810
  { key: "requireConsent", label: "Require Consent", description: "Ask caller for recording consent before proceeding.", default: true, type: { kind: "boolean" }, apiPath: "features.require_consent", section: "features", editableIn: "both" },
1811
+ { key: "taskCallsEnabled", label: "Allow Task Calls", description: "Outside AI agents (MCP/API) can place one-off calls through this agent.", default: false, type: { kind: "boolean" }, apiPath: "features.task_calls_enabled", section: "features", editableIn: "both" },
1721
1812
  { key: "postCallSummaryEnabled", label: "Post-Call Summary", description: "Generate an AI summary after each call.", default: true, type: { kind: "boolean" }, apiPath: "post_call.summary.enabled", section: "features", editableIn: "both" },
1722
1813
  { key: "postCallSummaryPrompt", label: "Summary Prompt", description: "Custom prompt for post-call summary generation.", default: "Summarize the call in a few sentences: the caller's main reason for calling, what was discussed or resolved, and any agreed next steps.", type: { kind: "string", maxLength: 2e3 }, apiPath: "post_call.summary.prompt", section: "features", editableIn: "both" },
1723
1814
  { key: "postCallExtractionEnabled", label: "Post-Call Extraction", description: "Extract structured data from call transcripts.", default: false, type: { kind: "boolean" }, apiPath: "post_call.structured_extraction.prompt", section: "features", editableIn: "both" },
@@ -1748,7 +1839,7 @@ var init_agentConfigSchema = __esm({
1748
1839
  },
1749
1840
  // ── Runtime Timing ──────────────────────────────────────────────────────
1750
1841
  { key: "maxTurnLength", label: "Max Turn Length", description: "Maximum number of conversational turns.", default: 50, type: { kind: "number", min: 1, max: 200 }, apiPath: "advanced.maxTurnLength", section: "advanced", editableIn: "both" },
1751
- { key: "postSilenceHangup", label: "Post-Silence Hangup", description: "Hang up after this many seconds of silence.", default: 30, type: { kind: "number", min: 1, max: 6e4, unit: "s" }, apiPath: "advanced.postSilenceHangup", section: "advanced", editableIn: "both" },
1842
+ { key: "postSilenceHangup", label: "Post-Silence Hangup", description: "Hang up after this many seconds of silence.", default: 60, type: { kind: "number", min: 1, max: 6e4, unit: "s" }, apiPath: "advanced.postSilenceHangup", section: "advanced", editableIn: "both" },
1752
1843
  { key: "silencePromptMaxCount", label: "Silence Check-ins", description: "How many times the agent checks on a silent caller before the silence goodbye.", default: 2, type: { kind: "number", min: 1, max: 3 }, apiPath: "advanced.silencePromptMaxCount", section: "advanced", editableIn: "both" },
1753
1844
  { key: "silenceHangupMessage", label: "Silence Goodbye", description: "Exact line spoken before a silence hangup. Leave empty to let the agent say a short goodbye in the caller's language.", default: "", type: { kind: "string", maxLength: 200 }, apiPath: "advanced.silenceHangupMessage", section: "advanced", editableIn: "both" },
1754
1845
  { key: "maxCallDuration", label: "Max Call Duration", description: "Hard limit on call length.", default: 600, type: { kind: "number", min: 30, max: 7200, unit: "s" }, apiPath: "advanced.maxCallDuration", section: "advanced", editableIn: "both" },
@@ -1773,7 +1864,10 @@ var init_agentConfigSchema = __esm({
1773
1864
  { key: "stt_keywords", label: "STT Keywords", description: "Comma-separated keywords to boost STT accuracy.", default: "", type: { kind: "string", maxLength: 1e3 }, apiPath: "advanced.stt_keywords", section: "advanced", editableIn: "both" },
1774
1865
  { key: "pronunciations", label: "Pronunciations", aliases: ["pronunciation", "pronounce", "say as", "how to say"], description: "Words the agent should say a specific way: each word with how to say it, plus optional IPA where the voice engine reads it.", default: [], type: { kind: "list-object", itemShape: [{ key: "word", type: { kind: "string", maxLength: 64 }, required: true }, { key: "say_as", type: { kind: "string", maxLength: 128 }, required: true }, { key: "ipa", type: { kind: "string", maxLength: 128 }, required: false }] }, apiPath: "advanced.pronunciations", section: "advanced", editableIn: "both" },
1775
1866
  { key: "stt_background_denoising", label: "Background Denoising", aliases: ["denoise", "noise suppression", "background noise"], description: "Server-side noise suppression on the inbound audio (Deepgram smart-format).", default: true, type: { kind: "boolean" }, apiPath: "advanced.stt_background_denoising", section: "advanced", editableIn: "both" },
1776
- { key: "noise_cancellation_krisp", label: "Krisp Noise Cancellation", aliases: ["krisp", "noise cancellation", "inbound denoising", "background voice"], description: "Transport-level inbound noise + background-voice cancellation (LiveKit Krisp). On by default for cloud calls; turn off to feed the raw caller audio to the recognizer.", default: true, type: { kind: "boolean" }, apiPath: "advanced.noise_cancellation_krisp", section: "advanced", editableIn: "both" },
1867
+ { key: "voice_focus", label: "Voice Focus", aliases: ["krisp", "noise cancellation", "inbound denoising", "background voice", "voice focus", "noise_cancellation_krisp"], description: "Transport-level inbound speech isolation (RNNoise). Suppresses steady room noise before VAD and speech recognition; off by default.", default: "off", type: { kind: "enum", options: [{ value: "off", label: "Off" }, { value: "standard", label: "Standard" }] }, apiPath: "advanced.voice_focus", section: "advanced", editableIn: "both" },
1868
+ { key: "background_sound", label: "Background Sound", aliases: ["ambience", "background audio", "office sound", "call ambience"], description: "Continuous low-volume ambient audio mixed under the agent voice on the outbound call track (`off`, built-in `office`, or `custom` URL).", default: "off", type: { kind: "enum", options: [{ value: "off", label: "Off" }, { value: "office", label: "Office" }, { value: "custom", label: "Custom URL" }] }, apiPath: "advanced.background_sound", section: "advanced", editableIn: "both" },
1869
+ { key: "background_sound_volume", label: "Background Sound Volume", aliases: ["ambience volume", "office sound volume"], description: "Base linear gain for outbound background sound (0.00 to 0.40, default 0.15). Ducks automatically by 8 dB while the agent speaks.", default: 0.15, type: { kind: "number", min: 0, max: 0.4 }, apiPath: "advanced.background_sound_volume", section: "advanced", editableIn: "both" },
1870
+ { key: "background_sound_url", label: "Background Sound URL", aliases: ["ambience url", "custom background sound url"], description: "Public HTTPS URL to a mono/stereo .wav or .mp3 file (3\u2013120 s, max 15 MB) used when background_sound is set to custom.", default: "", type: { kind: "string", maxLength: 512 }, apiPath: "advanced.background_sound_url", section: "advanced", editableIn: "both" },
1777
1871
  { key: "stt_eot_confidence", label: "End-of-Turn Confidence", description: "Deepgram Flux: how sure the model must be before deciding you finished speaking.", default: 0.7, type: { kind: "number", min: 0.5, max: 0.9 }, apiPath: "advanced.stt_eot_confidence", section: "advanced", editableIn: "both" },
1778
1872
  { key: "stt_eot_timeout_ms", label: "End-of-Turn Timeout", description: "Deepgram Flux: max silence before end-of-turn is forced.", default: 5e3, type: { kind: "number", min: 500, max: 1e4, unit: "ms" }, apiPath: "advanced.stt_eot_timeout_ms", section: "advanced", editableIn: "both" },
1779
1873
  { key: "stt_min_turn_silence_ms", label: "Min Turn Silence", description: "AssemblyAI: minimum silence before detecting end-of-turn when confident.", default: 160, type: { kind: "number", min: 0, max: 3e3, unit: "ms" }, apiPath: "advanced.stt_min_turn_silence_ms", section: "advanced", editableIn: "both" },
@@ -2151,6 +2245,8 @@ var init_flatDiff = __esm({
2151
2245
  "features.transcription_enabled": "transcriptionEnabled",
2152
2246
  "features.requireConsent": "requireConsent",
2153
2247
  "features.require_consent": "requireConsent",
2248
+ "features.taskCallsEnabled": "taskCallsEnabled",
2249
+ "features.task_calls_enabled": "taskCallsEnabled",
2154
2250
  "features.postCallSummaryEnabled": "postCallSummaryEnabled",
2155
2251
  "features.post_call_summary_enabled": "postCallSummaryEnabled",
2156
2252
  "features.postCallExtractionEnabled": "postCallExtractionEnabled",
@@ -2174,6 +2270,12 @@ var init_flatDiff = __esm({
2174
2270
  "stt.stt_keywords": "stt_keywords",
2175
2271
  "stt.background_denoising": "stt_background_denoising",
2176
2272
  "stt.stt_background_denoising": "stt_background_denoising",
2273
+ "noise_cancellation_krisp": "voice_focus",
2274
+ "advanced.noise_cancellation_krisp": "voice_focus",
2275
+ "stt.voice_focus": "voice_focus",
2276
+ "tts.background_sound": "background_sound",
2277
+ "tts.background_sound_volume": "background_sound_volume",
2278
+ "tts.background_sound_url": "background_sound_url",
2177
2279
  "stt.eot_confidence": "stt_eot_confidence",
2178
2280
  "stt.stt_eot_confidence": "stt_eot_confidence",
2179
2281
  "stt.eot_timeout_ms": "stt_eot_timeout_ms",
@@ -2310,6 +2412,8 @@ var init_pricing = __esm({
2310
2412
  "gpt-realtime-2.1": 0.096,
2311
2413
  "gpt-realtime-2.1-mini": 0.03,
2312
2414
  "gemini-3.8-live": 0.035,
2415
+ "gemini-3.8-live-extended-thinking": 0.035,
2416
+ // provisional: same list rates, thinking tokens extra
2313
2417
  "gpt-live-1": 0.06,
2314
2418
  // 0.05 voice layer + ~0.01 gpt-5.4-mini delegation backend
2315
2419
  "grok-voice-think-fast-2.0": 0.08,
@@ -2400,6 +2504,7 @@ var init_pricing = __esm({
2400
2504
  "llama-4-scout": 56e-5,
2401
2505
  "deepseek-v4-flash": 34e-5,
2402
2506
  "deepseek-v4-pro": 418e-5,
2507
+ "grok-4.7": 64e-4,
2403
2508
  "grok-4-1-fast": 56e-5,
2404
2509
  "grok-3-mini": 64e-5,
2405
2510
  "grok-3-mini-fast": 48e-5,
@@ -2429,10 +2534,12 @@ var init_pricing = __esm({
2429
2534
  "Qwen/Qwen2.5-72B-Instruct": 22e-4,
2430
2535
  "deepseek-ai/DeepSeek-V3": 14e-4,
2431
2536
  "sarvam-30b": 10526e-8,
2432
- "sarvam-105b": 77053e-8,
2433
- "sarvam-105b-conversations": 77053e-8,
2537
+ "sarvam-105b": 0.01024,
2538
+ // measured 2026-10-25, see rates.py
2539
+ "sarvam-105b-conversations": 0.01024,
2434
2540
  // TTS
2435
2541
  "aura-2": 0.027,
2542
+ "flux-tts": 0.0405,
2436
2543
  "gemini-2.5-flash-preview-tts": 0.015,
2437
2544
  "gemini-3.1-flash-tts-preview": 0.015,
2438
2545
  "gpt-4o-mini-tts": 0.015,
@@ -2446,6 +2553,7 @@ var init_pricing = __esm({
2446
2553
  "inworld-tts-1": 5e-3,
2447
2554
  "speech-1.6": 0.015,
2448
2555
  "s2.1-pro": 0.020833,
2556
+ "drama-3": 0.069444,
2449
2557
  "polly-neural": 0.012,
2450
2558
  "sonic-3.5": 0.037594,
2451
2559
  "sonic-3.6": 0.037594,
@@ -2466,7 +2574,9 @@ var init_pricing = __esm({
2466
2574
  "PlayDialogTurbo": 0.06,
2467
2575
  "speech-02-turbo": 0.024,
2468
2576
  "speech-02-hd": 0.04,
2469
- "bulbul:v3": 0.02842105
2577
+ "bulbul:v3": 0.02842105,
2578
+ // ₹2.00/1K chars PAYG, WebSocket (sarvam.ai/api-pricing, 2026-10-17) at ₹95/$, 900 chars/min.
2579
+ "bulbul:v4-flash": 0.01894737
2470
2580
  };
2471
2581
  }
2472
2582
  });
@@ -2488,68 +2598,6 @@ var init_knowledge = __esm({
2488
2598
  }
2489
2599
  });
2490
2600
 
2491
- // ../shared-types/dist/safeUrl.js
2492
- var safeUrl_exports = {};
2493
- __export(safeUrl_exports, {
2494
- isSafeUrl: () => isSafeUrl
2495
- });
2496
- function unmapIpv4(v6) {
2497
- const dotted = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(v6);
2498
- if (dotted)
2499
- return dotted[1];
2500
- const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(v6);
2501
- if (!hex)
2502
- return null;
2503
- const high = parseInt(hex[1], 16);
2504
- const low = parseInt(hex[2], 16);
2505
- return [high >> 8, high & 255, low >> 8, low & 255].join(".");
2506
- }
2507
- function isBlockedHost(host) {
2508
- const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
2509
- if (bare === "localhost" || bare === "0.0.0.0" || bare === "::")
2510
- return true;
2511
- if (bare.endsWith(".local") || bare.endsWith(".internal") || bare.endsWith(".localhost"))
2512
- return true;
2513
- if (/^127\./.test(bare))
2514
- return true;
2515
- if (/^10\./.test(bare))
2516
- return true;
2517
- if (/^192\.168\./.test(bare))
2518
- return true;
2519
- if (/^172\.(1[6-9]|2\d|3[01])\./.test(bare))
2520
- return true;
2521
- if (/^169\.254\./.test(bare))
2522
- return true;
2523
- const v6 = bare.toLowerCase();
2524
- if (v6 === "::1")
2525
- return true;
2526
- if (/^f[cd][0-9a-f]{2}:/.test(v6))
2527
- return true;
2528
- if (/^fe[89ab][0-9a-f]:/.test(v6))
2529
- return true;
2530
- const mapped = unmapIpv4(v6);
2531
- if (mapped)
2532
- return isBlockedHost(mapped);
2533
- return false;
2534
- }
2535
- function isSafeUrl(raw, options = {}) {
2536
- const protocols = options.protocols ?? ["http:", "https:"];
2537
- let url;
2538
- try {
2539
- url = new URL(raw);
2540
- } catch {
2541
- return false;
2542
- }
2543
- if (!protocols.includes(url.protocol))
2544
- return false;
2545
- return !isBlockedHost(url.hostname.toLowerCase());
2546
- }
2547
- var init_safeUrl = __esm({
2548
- "../shared-types/dist/safeUrl.js"() {
2549
- "use strict";
2550
- }
2551
- });
2552
-
2553
2601
  // ../shared-types/dist/agentTools.js
2554
2602
  var agentTools_exports = {};
2555
2603
  __export(agentTools_exports, {
@@ -3736,6 +3784,20 @@ var init_sttCapabilities = __esm({
3736
3784
  function resolveManagedPricePerMin(sku) {
3737
3785
  return sku.pricePerMin ?? 0;
3738
3786
  }
3787
+ function skuAllowsCountry(sku, country, strict) {
3788
+ const allowed = sku.allowedCountries;
3789
+ if (!allowed || allowed.length === 0)
3790
+ return true;
3791
+ if (!country)
3792
+ return !strict;
3793
+ return allowed.includes(country.toUpperCase());
3794
+ }
3795
+ function skuFeaturedInCountry(sku, country) {
3796
+ const featured = sku.featuredCountries;
3797
+ if (!featured || featured.length === 0)
3798
+ return true;
3799
+ return !!country && featured.includes(country.toUpperCase());
3800
+ }
3739
3801
  var init_managedAgentSkus = __esm({
3740
3802
  "../shared-types/dist/managedAgentSkus.js"() {
3741
3803
  "use strict";
@@ -7011,6 +7073,7 @@ __export(dist_exports, {
7011
7073
  isE164: () => isE164,
7012
7074
  isKnownSessionEndReason: () => isKnownSessionEndReason,
7013
7075
  isRealtimeModel: () => isRealtimeModel,
7076
+ isSafeAudioUrl: () => isSafeAudioUrl,
7014
7077
  isSafeUrl: () => isSafeUrl,
7015
7078
  isSupportedCountry: () => isSupportedCountry2,
7016
7079
  isSupportedPhoneNumber: () => isSupportedPhoneNumber,
@@ -7034,6 +7097,7 @@ __export(dist_exports, {
7034
7097
  populatedFocusedStudioSections: () => populatedFocusedStudioSections,
7035
7098
  pronunciationSupport: () => pronunciationSupport,
7036
7099
  providerIdsForTool: () => providerIdsForTool,
7100
+ providerRequiresExplicitVoice: () => providerRequiresExplicitVoice,
7037
7101
  publishRequiredKeys: () => publishRequiredKeys,
7038
7102
  readFocusedSectionField: () => readFocusedSectionField,
7039
7103
  readFromPayload: () => readFromPayload,
@@ -7047,6 +7111,8 @@ __export(dist_exports, {
7047
7111
  sanitizeTranscriptText: () => sanitizeTranscriptText,
7048
7112
  scalarContextKeys: () => scalarContextKeys,
7049
7113
  serializeSection: () => serializeSection,
7114
+ skuAllowsCountry: () => skuAllowsCountry,
7115
+ skuFeaturedInCountry: () => skuFeaturedInCountry,
7050
7116
  stackCostPerMin: () => stackCostPerMin,
7051
7117
  supportedCountry: () => supportedCountry,
7052
7118
  toSupportedE164: () => toSupportedE164,
@@ -7142,6 +7208,7 @@ var require_dist = __commonJS({
7142
7208
  keyAllowsTool: () => keyAllowsTool2,
7143
7209
  opsToolCatalog: () => opsToolCatalog2,
7144
7210
  resolveToolDisposition: () => resolveToolDisposition,
7211
+ toolAnnotations: () => toolAnnotations2,
7145
7212
  withWorkspaceParam: () => withWorkspaceParam2
7146
7213
  });
7147
7214
  module2.exports = __toCommonJS2(index_exports);
@@ -7179,20 +7246,20 @@ var require_dist = __commonJS({
7179
7246
  var agentConfigFields = {
7180
7247
  system_prompt: import_zod.z.string().optional().describe("Full system prompt for the agent. If given without persona/playbook, the server auto-structures it."),
7181
7248
  voice: import_zod.z.string().optional().describe('Voice ID (e.g. "Aoede", "Charon"). Call list_llm_options for valid values. Leave empty for realtime LLMs and Deepgram TTS, which carry their own voice.'),
7182
- language: import_zod.z.string().optional().describe('Primary BCP-47 language tag (e.g. "hi-IN", "en-US").'),
7183
- supported_languages: import_zod.z.array(import_zod.z.string()).optional().describe('All BCP-47 languages the agent should handle, e.g. ["hi-IN","en-US"] for a bilingual agent. The primary `language` is added automatically if omitted here.'),
7184
- llm_provider: import_zod.z.enum(import_shared_types.AGENT_LLM_PROVIDERS).optional(),
7185
- llm_model: import_zod.z.string().optional().describe('LLM model id from list_llm_options (e.g. "gemini-2.5-flash", "sarvam-105b").'),
7186
- llm_fallback_provider: import_zod.z.string().nullable().optional().describe("Fallback LLM provider; null clears it."),
7187
- llm_fallback_model: import_zod.z.string().nullable().optional().describe("Fallback LLM model; null clears it."),
7188
- stt_provider: import_zod.z.string().optional().describe('Speech-to-text provider (e.g. "deepgram", "sarvam").'),
7189
- stt_model: import_zod.z.string().optional(),
7190
- stt_fallback_provider: import_zod.z.string().nullable().optional(),
7191
- stt_fallback_model: import_zod.z.string().nullable().optional(),
7192
- tts_provider: import_zod.z.string().optional().describe('Text-to-speech provider (e.g. "elevenlabs", "sarvam", "cartesia").'),
7193
- tts_model: import_zod.z.string().optional(),
7194
- tts_fallback_provider: import_zod.z.string().nullable().optional(),
7195
- tts_fallback_model: import_zod.z.string().nullable().optional(),
7249
+ language: import_zod.z.string().optional().describe('Primary BCP-47 language tag (e.g. "hi-IN", "en-US"). For managed agents, must belong to the SKU\u2019s supported_languages.'),
7250
+ supported_languages: import_zod.z.array(import_zod.z.string()).optional().describe('All BCP-47 languages the agent should handle, e.g. ["hi-IN","en-US"] for a bilingual custom agent. Fixed to [language] by the plan for managed agents.'),
7251
+ llm_provider: import_zod.z.enum(import_shared_types.AGENT_LLM_PROVIDERS).optional().describe("LLM provider (custom agents only; fixed by the plan for managed agents)."),
7252
+ llm_model: import_zod.z.string().optional().describe('LLM model id from list_llm_options (e.g. "gemini-2.5-flash", "sarvam-105b"). Fixed by the plan for managed agents.'),
7253
+ llm_fallback_provider: import_zod.z.string().nullable().optional().describe("Fallback LLM provider; null clears it. Fixed by the plan for managed agents."),
7254
+ llm_fallback_model: import_zod.z.string().nullable().optional().describe("Fallback LLM model; null clears it. Fixed by the plan for managed agents."),
7255
+ stt_provider: import_zod.z.string().optional().describe('Speech-to-text provider (e.g. "deepgram", "sarvam"). Fixed by the plan for managed agents.'),
7256
+ stt_model: import_zod.z.string().optional().describe("Speech-to-text model id. Fixed by the plan for managed agents."),
7257
+ stt_fallback_provider: import_zod.z.string().nullable().optional().describe("Fallback STT provider. Fixed by the plan for managed agents."),
7258
+ stt_fallback_model: import_zod.z.string().nullable().optional().describe("Fallback STT model. Fixed by the plan for managed agents."),
7259
+ tts_provider: import_zod.z.string().optional().describe('Text-to-speech provider (e.g. "elevenlabs", "sarvam", "cartesia"). Fixed by the plan for managed agents.'),
7260
+ tts_model: import_zod.z.string().optional().describe("Text-to-speech model id. Fixed by the plan for managed agents."),
7261
+ tts_fallback_provider: import_zod.z.string().nullable().optional().describe("Fallback TTS provider. Fixed by the plan for managed agents."),
7262
+ tts_fallback_model: import_zod.z.string().nullable().optional().describe("Fallback TTS model. Fixed by the plan for managed agents."),
7196
7263
  custom_llm_url: import_zod.z.string().nullable().optional().describe("Self-hosted LLM endpoint (https:// or wss://). Enterprise only; null clears."),
7197
7264
  custom_voice_url: import_zod.z.string().nullable().optional().describe("Self-hosted TTS endpoint (https:// or wss://). Enterprise only; null clears."),
7198
7265
  custom_voice_mode: import_zod.z.enum(["rymi", "openai-compat"]).optional().describe("Wire format for custom_voice_url."),
@@ -8255,7 +8322,7 @@ var require_dist = __commonJS({
8255
8322
  ];
8256
8323
  var import_zod11 = require("zod");
8257
8324
  var import_agentTools = (init_agentTools(), __toCommonJS(agentTools_exports));
8258
- var import_safeUrl2 = (init_safeUrl(), __toCommonJS(safeUrl_exports));
8325
+ var import_safeUrl3 = (init_safeUrl(), __toCommonJS(safeUrl_exports));
8259
8326
  var DEFAULT_NAME = "call_webhook";
8260
8327
  var METHODS = ["GET", "POST", "PUT", "PATCH"];
8261
8328
  var FIELD_NAME = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
@@ -8293,7 +8360,7 @@ var require_dist = __commonJS({
8293
8360
  const url = typeof s.url === "string" ? s.url.trim() : "";
8294
8361
  if (!url) errors.push("url is required.");
8295
8362
  else if (!/^https:\/\//i.test(url)) errors.push("url must be https \u2014 the request carries caller details.");
8296
- else if (!(0, import_safeUrl2.isSafeUrl)(url, { protocols: ["https:"] })) errors.push("url must be a valid public host; private and loopback addresses are refused at call time.");
8363
+ else if (!(0, import_safeUrl3.isSafeUrl)(url, { protocols: ["https:"] })) errors.push("url must be a valid public host; private and loopback addresses are refused at call time.");
8297
8364
  if (!METHODS.includes(s.method)) errors.push(`method must be one of ${METHODS.join(", ")}.`);
8298
8365
  if (!Number.isInteger(b.timeout_ms) || b.timeout_ms < 1e3 || b.timeout_ms > 1e4) errors.push("timeout_ms must be an integer from 1000 to 10000.");
8299
8366
  const seen = /* @__PURE__ */ new Set();
@@ -8768,6 +8835,98 @@ var require_dist = __commonJS({
8768
8835
  run: (client, { account_id, user_id }) => client.accounts.members.remove(account_id, user_id)
8769
8836
  })
8770
8837
  ];
8838
+ var import_zod16 = require("zod");
8839
+ var returnField = import_zod16.z.object({
8840
+ name: import_zod16.z.string().regex(/^[a-z][a-z0-9_]{0,39}$/).describe('snake_case field name, e.g. "booked"'),
8841
+ description: import_zod16.z.string().min(1).max(300).describe("What the value means, for the extractor"),
8842
+ type: import_zod16.z.enum(["string", "number", "boolean"]).optional().describe('Default "string"')
8843
+ });
8844
+ var IN_FLIGHT = /* @__PURE__ */ new Set(["queued", "initiated", "ringing", "in_progress", "active", "pending", "dialing"]);
8845
+ var INTEL_DONE = /* @__PURE__ */ new Set(["completed", "partial", "skipped", "failed"]);
8846
+ function isCallDone(call) {
8847
+ if (!call.status || IN_FLIGHT.has(call.status)) return false;
8848
+ if (call.status !== "completed") return true;
8849
+ return INTEL_DONE.has(call.intelligence?.status ?? "");
8850
+ }
8851
+ var POLL_MS = 3e3;
8852
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8853
+ var taskCallTools = [
8854
+ defineTool({
8855
+ name: "place_task_call",
8856
+ description: "WARNING: places a real, billable phone call. Places a real, billable call through an agent with task calls enabled. Use list_agents to find one (features.task_calls_enabled: true), then call wait_for_call with the returned call_id to get the results.",
8857
+ scope: "agent",
8858
+ risk: "sensitive",
8859
+ routes: ["POST /v1/agents/:id/task-calls"],
8860
+ keyScopes: ["calls:write"],
8861
+ input: {
8862
+ agent_id: import_zod16.z.string().describe("Agent with task calls enabled; find one with list_agents (features.task_calls_enabled: true)"),
8863
+ to: import_zod16.z.string().regex(/^\+[1-9]\d{6,14}$/).describe("Number to call, strict E.164, e.g. +919812345678"),
8864
+ on_behalf_of: import_zod16.z.string().min(1).max(120).describe("Business or person the call is for; spoken in the opener"),
8865
+ instructions: import_zod16.z.string().min(1).max(4e3).describe("What the call must do, in plain words"),
8866
+ return_fields: import_zod16.z.array(returnField).min(1).max(20).refine((fs) => new Set(fs.map((f) => f.name)).size === fs.length, "return_fields names must be unique").describe("Fields to extract from the call and return via wait_for_call"),
8867
+ opening_line: import_zod16.z.string().min(1).max(300).optional().describe("First line spoken; defaults to the agent opener or an AI disclosure"),
8868
+ from_number: import_zod16.z.string().optional().describe("Caller ID registered to the workspace; omit to use the agent's attached number")
8869
+ },
8870
+ run: async (client, p) => {
8871
+ const res = await client.agents.placeTaskCall(p.agent_id, {
8872
+ to: p.to,
8873
+ on_behalf_of: p.on_behalf_of,
8874
+ instructions: p.instructions,
8875
+ return_fields: p.return_fields,
8876
+ ...p.opening_line ? { opening_line: p.opening_line } : {},
8877
+ ...p.from_number ? { from_number: p.from_number } : {}
8878
+ });
8879
+ return {
8880
+ call_id: res?.call_id ?? res?.id,
8881
+ agent_id: res?.agent_id ?? p.agent_id,
8882
+ status: res?.status,
8883
+ next: "Call wait_for_call with this call_id to get the summary and fields."
8884
+ };
8885
+ }
8886
+ }),
8887
+ defineTool({
8888
+ name: "wait_for_call",
8889
+ description: "Wait (up to timeout_s, default 90) for a call to finish and its post-call results to be ready, then return status, summary and the extracted fields. Returns done:false if still running \u2014 just call it again.",
8890
+ scope: "agent",
8891
+ risk: "read",
8892
+ routes: ["GET /v1/calls/:id"],
8893
+ keyScopes: ["calls:read"],
8894
+ input: {
8895
+ call_id: import_zod16.z.string().describe("The call ID from place_task_call or create_call"),
8896
+ timeout_s: import_zod16.z.number().int().min(5).max(240).optional().describe("Max seconds to wait (5\u2013240, default 90)")
8897
+ },
8898
+ run: async (client, { call_id, timeout_s }) => {
8899
+ const deadline = Date.now() + (timeout_s ?? 90) * 1e3;
8900
+ let call = null;
8901
+ for (; ; ) {
8902
+ try {
8903
+ call = await client.calls.retrieve(call_id);
8904
+ } catch {
8905
+ }
8906
+ if (call && isCallDone(call)) {
8907
+ return {
8908
+ done: true,
8909
+ call_id,
8910
+ status: call.status,
8911
+ end_reason: call.end_reason ?? null,
8912
+ duration_seconds: call.bill_duration ?? call.duration_seconds ?? null,
8913
+ summary: call.intelligence?.summary ?? null,
8914
+ fields: call.intelligence?.structured_data ?? null,
8915
+ intelligence_status: call.intelligence?.status ?? null
8916
+ };
8917
+ }
8918
+ if (Date.now() + POLL_MS > deadline) break;
8919
+ await sleep(POLL_MS);
8920
+ }
8921
+ return {
8922
+ done: false,
8923
+ call_id,
8924
+ status: call?.status ?? "unknown",
8925
+ note: "Still running \u2014 call wait_for_call again with the same call_id."
8926
+ };
8927
+ }
8928
+ })
8929
+ ];
8771
8930
  var import_agentTools2 = (init_agentTools(), __toCommonJS(agentTools_exports));
8772
8931
  var REDACTED = "[redacted]";
8773
8932
  function redactToolSecrets(value) {
@@ -8810,15 +8969,16 @@ var require_dist = __commonJS({
8810
8969
  ...publish(shareLinkTools),
8811
8970
  ...publish(workspaceTools),
8812
8971
  ...publish(complianceTools),
8813
- ...publish(accountTools)
8972
+ ...publish(accountTools),
8973
+ ...publish(taskCallTools)
8814
8974
  ];
8815
8975
  var names = /* @__PURE__ */ new Set();
8816
8976
  for (const tool of opsToolCatalog2) {
8817
8977
  if (names.has(tool.name)) throw new Error(`Duplicate ops tool name: ${tool.name}`);
8818
8978
  names.add(tool.name);
8819
8979
  }
8820
- var import_zod16 = require("zod");
8821
- var WORKSPACE = import_zod16.z.string().optional().describe("Act in this workspace, e.g. one of your client workspaces (id from list_workspaces). Default: the workspace this connection uses.");
8980
+ var import_zod17 = require("zod");
8981
+ var WORKSPACE = import_zod17.z.string().optional().describe("Act in this workspace, e.g. one of your client workspaces (id from list_workspaces). Default: the workspace this connection uses.");
8822
8982
  function withWorkspaceParam2(tool) {
8823
8983
  return {
8824
8984
  ...tool,
@@ -8829,6 +8989,26 @@ var require_dist = __commonJS({
8829
8989
  }
8830
8990
  };
8831
8991
  }
8992
+ var DESTRUCTIVE_NAMES = /* @__PURE__ */ new Set([
8993
+ "end_call",
8994
+ "undo_agent_change",
8995
+ "regenerate_share_link",
8996
+ "rotate_campaign_intake"
8997
+ ]);
8998
+ var ACRONYMS = { dnc: "DNC", llm: "LLM" };
8999
+ function titleOf(name) {
9000
+ const words = name.split("_").map((w) => ACRONYMS[w] ?? w);
9001
+ return words.join(" ").replace(/^./, (c) => c.toUpperCase());
9002
+ }
9003
+ function toolAnnotations2(tool) {
9004
+ const readOnly = tool.risk === "read";
9005
+ return {
9006
+ title: titleOf(tool.name),
9007
+ readOnlyHint: readOnly,
9008
+ destructiveHint: !readOnly && (DESTRUCTIVE_NAMES.has(tool.name) || tool.routes.some((r) => r.startsWith("DELETE "))),
9009
+ openWorldHint: !readOnly
9010
+ };
9011
+ }
8832
9012
  }
8833
9013
  });
8834
9014
 
@@ -8858,7 +9038,8 @@ function withToolErrors(handler) {
8858
9038
  // package.json
8859
9039
  var package_default = {
8860
9040
  name: "@rymi/mcp",
8861
- version: "2.8.1",
9041
+ mcpName: "io.github.rymi-live/rymi-mcp",
9042
+ version: "2.9.0",
8862
9043
  description: "Rymi MCP server \u2014 manage AI voice agents via Model Context Protocol",
8863
9044
  license: "MIT",
8864
9045
  author: "Rymi AI <engineering@rymi.live>",
@@ -8954,6 +9135,7 @@ async function createServer(apiKey2, options = {}) {
8954
9135
  tool.name,
8955
9136
  tool.description,
8956
9137
  tool.input,
9138
+ (0, import_ops_tools.toolAnnotations)(tool),
8957
9139
  withToolErrors(async (params) => {
8958
9140
  const result2 = await tool.run(rymi, params);
8959
9141
  return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@rymi/mcp",
3
- "version": "2.8.1",
3
+ "mcpName": "io.github.rymi-live/rymi-mcp",
4
+ "version": "2.9.0",
4
5
  "description": "Rymi MCP server — manage AI voice agents via Model Context Protocol",
5
6
  "license": "MIT",
6
7
  "author": "Rymi AI <engineering@rymi.live>",
@@ -44,7 +45,7 @@
44
45
  },
45
46
  "dependencies": {
46
47
  "@modelcontextprotocol/sdk": "^1.11.1",
47
- "@rymi/node": "^2.7.1",
48
+ "@rymi/node": "^2.7.2",
48
49
  "zod": "^3.23.0"
49
50
  },
50
51
  "devDependencies": {