@rymi/mcp 2.8.1 → 2.8.2
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 +5 -1
- package/dist/index.js +228 -75
- package/package.json +2 -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",
|
|
@@ -263,6 +260,7 @@ var init_studioCatalog = __esm({
|
|
|
263
260
|
"gemini-3.1-flash-tts-preview",
|
|
264
261
|
"tts-1",
|
|
265
262
|
"bulbul:v3",
|
|
263
|
+
"bulbul:v4-flash",
|
|
266
264
|
"gpt-4o-mini-tts",
|
|
267
265
|
"eleven_multilingual_v2",
|
|
268
266
|
// Additional TTS
|
|
@@ -1218,6 +1216,80 @@ var init_variables = __esm({
|
|
|
1218
1216
|
}
|
|
1219
1217
|
});
|
|
1220
1218
|
|
|
1219
|
+
// ../shared-types/dist/safeUrl.js
|
|
1220
|
+
var safeUrl_exports = {};
|
|
1221
|
+
__export(safeUrl_exports, {
|
|
1222
|
+
isSafeAudioUrl: () => isSafeAudioUrl,
|
|
1223
|
+
isSafeUrl: () => isSafeUrl
|
|
1224
|
+
});
|
|
1225
|
+
function unmapIpv4(v6) {
|
|
1226
|
+
const dotted = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(v6);
|
|
1227
|
+
if (dotted)
|
|
1228
|
+
return dotted[1];
|
|
1229
|
+
const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(v6);
|
|
1230
|
+
if (!hex)
|
|
1231
|
+
return null;
|
|
1232
|
+
const high = parseInt(hex[1], 16);
|
|
1233
|
+
const low = parseInt(hex[2], 16);
|
|
1234
|
+
return [high >> 8, high & 255, low >> 8, low & 255].join(".");
|
|
1235
|
+
}
|
|
1236
|
+
function isBlockedHost(host) {
|
|
1237
|
+
const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
1238
|
+
if (bare === "localhost" || bare === "0.0.0.0" || bare === "::")
|
|
1239
|
+
return true;
|
|
1240
|
+
if (bare.endsWith(".local") || bare.endsWith(".internal") || bare.endsWith(".localhost"))
|
|
1241
|
+
return true;
|
|
1242
|
+
if (/^127\./.test(bare))
|
|
1243
|
+
return true;
|
|
1244
|
+
if (/^10\./.test(bare))
|
|
1245
|
+
return true;
|
|
1246
|
+
if (/^192\.168\./.test(bare))
|
|
1247
|
+
return true;
|
|
1248
|
+
if (/^172\.(1[6-9]|2\d|3[01])\./.test(bare))
|
|
1249
|
+
return true;
|
|
1250
|
+
if (/^169\.254\./.test(bare))
|
|
1251
|
+
return true;
|
|
1252
|
+
const v6 = bare.toLowerCase();
|
|
1253
|
+
if (v6 === "::1")
|
|
1254
|
+
return true;
|
|
1255
|
+
if (/^f[cd][0-9a-f]{2}:/.test(v6))
|
|
1256
|
+
return true;
|
|
1257
|
+
if (/^fe[89ab][0-9a-f]:/.test(v6))
|
|
1258
|
+
return true;
|
|
1259
|
+
const mapped = unmapIpv4(v6);
|
|
1260
|
+
if (mapped)
|
|
1261
|
+
return isBlockedHost(mapped);
|
|
1262
|
+
return false;
|
|
1263
|
+
}
|
|
1264
|
+
function isSafeUrl(raw, options = {}) {
|
|
1265
|
+
const protocols = options.protocols ?? ["http:", "https:"];
|
|
1266
|
+
let url;
|
|
1267
|
+
try {
|
|
1268
|
+
url = new URL(raw);
|
|
1269
|
+
} catch {
|
|
1270
|
+
return false;
|
|
1271
|
+
}
|
|
1272
|
+
if (!protocols.includes(url.protocol))
|
|
1273
|
+
return false;
|
|
1274
|
+
return !isBlockedHost(url.hostname.toLowerCase());
|
|
1275
|
+
}
|
|
1276
|
+
function isSafeAudioUrl(raw) {
|
|
1277
|
+
if (!isSafeUrl(raw, { protocols: ["https:"] }))
|
|
1278
|
+
return false;
|
|
1279
|
+
try {
|
|
1280
|
+
const parsed = new URL(raw.trim());
|
|
1281
|
+
const path = parsed.pathname.toLowerCase();
|
|
1282
|
+
return path.endsWith(".wav") || path.endsWith(".mp3");
|
|
1283
|
+
} catch {
|
|
1284
|
+
return false;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
var init_safeUrl = __esm({
|
|
1288
|
+
"../shared-types/dist/safeUrl.js"() {
|
|
1289
|
+
"use strict";
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1221
1293
|
// ../../node_modules/.pnpm/dlv@1.1.3/node_modules/dlv/dist/dlv.es.js
|
|
1222
1294
|
function dlv_es_default(t, e, l, n, r) {
|
|
1223
1295
|
for (e = e.split ? e.split(".") : e, n = 0; n < e.length; n++) t = t ? t[e[n]] : r;
|
|
@@ -1407,9 +1479,10 @@ var AGENT_CONFIG_SCHEMA_VERSION, CHANGE_SET_VALIDATORS, DERIVED_SERIALIZE_RULES,
|
|
|
1407
1479
|
var init_agentConfigSchema = __esm({
|
|
1408
1480
|
"../shared-types/dist/agentConfigSchema.js"() {
|
|
1409
1481
|
"use strict";
|
|
1482
|
+
init_safeUrl();
|
|
1410
1483
|
init_dlv_es();
|
|
1411
1484
|
init_dist();
|
|
1412
|
-
AGENT_CONFIG_SCHEMA_VERSION = "2026-
|
|
1485
|
+
AGENT_CONFIG_SCHEMA_VERSION = "2026-10-23";
|
|
1413
1486
|
CHANGE_SET_VALIDATORS = [
|
|
1414
1487
|
// Catalog validation for enum-catalog model + language fields.
|
|
1415
1488
|
// Skips silently when no catalogs are wired (legacy callers / tests).
|
|
@@ -1453,6 +1526,21 @@ var init_agentConfigSchema = __esm({
|
|
|
1453
1526
|
}
|
|
1454
1527
|
}
|
|
1455
1528
|
return errors.length === 0 ? { valid: true } : { valid: false, errors };
|
|
1529
|
+
},
|
|
1530
|
+
(cfg, changes) => {
|
|
1531
|
+
if (!changes.some((c) => c.key === "background_sound" || c.key === "background_sound_url"))
|
|
1532
|
+
return { valid: true };
|
|
1533
|
+
const errors = [];
|
|
1534
|
+
const url = cfg.background_sound_url;
|
|
1535
|
+
const mode = cfg.background_sound;
|
|
1536
|
+
const hasUrl = typeof url === "string" && url.trim() !== "";
|
|
1537
|
+
if (hasUrl && (url.length > 512 || !isSafeAudioUrl(url))) {
|
|
1538
|
+
errors.push({ field: "background_sound_url", error: "Use a public https:// link to a .wav or .mp3 file (up to 512 characters)." });
|
|
1539
|
+
}
|
|
1540
|
+
if (mode === "custom" && !hasUrl) {
|
|
1541
|
+
errors.push({ field: "background_sound_url", error: "Custom background sound needs a link to a .wav or .mp3 file." });
|
|
1542
|
+
}
|
|
1543
|
+
return errors.length === 0 ? { valid: true } : { valid: false, errors };
|
|
1456
1544
|
}
|
|
1457
1545
|
];
|
|
1458
1546
|
DERIVED_SERIALIZE_RULES = [
|
|
@@ -1718,6 +1806,7 @@ var init_agentConfigSchema = __esm({
|
|
|
1718
1806
|
// ── Features ────────────────────────────────────────────────────────────
|
|
1719
1807
|
{ key: "transcriptionEnabled", label: "Transcription", description: "Generate text transcripts of calls.", default: true, type: { kind: "boolean" }, apiPath: "features.transcription_enabled", section: "features", editableIn: "both" },
|
|
1720
1808
|
{ 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" },
|
|
1809
|
+
{ 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
1810
|
{ 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
1811
|
{ 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
1812
|
{ 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" },
|
|
@@ -1773,7 +1862,10 @@ var init_agentConfigSchema = __esm({
|
|
|
1773
1862
|
{ 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
1863
|
{ 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
1864
|
{ 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: "
|
|
1865
|
+
{ 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" },
|
|
1866
|
+
{ 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" },
|
|
1867
|
+
{ 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" },
|
|
1868
|
+
{ 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
1869
|
{ 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
1870
|
{ 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
1871
|
{ 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 +2243,8 @@ var init_flatDiff = __esm({
|
|
|
2151
2243
|
"features.transcription_enabled": "transcriptionEnabled",
|
|
2152
2244
|
"features.requireConsent": "requireConsent",
|
|
2153
2245
|
"features.require_consent": "requireConsent",
|
|
2246
|
+
"features.taskCallsEnabled": "taskCallsEnabled",
|
|
2247
|
+
"features.task_calls_enabled": "taskCallsEnabled",
|
|
2154
2248
|
"features.postCallSummaryEnabled": "postCallSummaryEnabled",
|
|
2155
2249
|
"features.post_call_summary_enabled": "postCallSummaryEnabled",
|
|
2156
2250
|
"features.postCallExtractionEnabled": "postCallExtractionEnabled",
|
|
@@ -2174,6 +2268,12 @@ var init_flatDiff = __esm({
|
|
|
2174
2268
|
"stt.stt_keywords": "stt_keywords",
|
|
2175
2269
|
"stt.background_denoising": "stt_background_denoising",
|
|
2176
2270
|
"stt.stt_background_denoising": "stt_background_denoising",
|
|
2271
|
+
"noise_cancellation_krisp": "voice_focus",
|
|
2272
|
+
"advanced.noise_cancellation_krisp": "voice_focus",
|
|
2273
|
+
"stt.voice_focus": "voice_focus",
|
|
2274
|
+
"tts.background_sound": "background_sound",
|
|
2275
|
+
"tts.background_sound_volume": "background_sound_volume",
|
|
2276
|
+
"tts.background_sound_url": "background_sound_url",
|
|
2177
2277
|
"stt.eot_confidence": "stt_eot_confidence",
|
|
2178
2278
|
"stt.stt_eot_confidence": "stt_eot_confidence",
|
|
2179
2279
|
"stt.eot_timeout_ms": "stt_eot_timeout_ms",
|
|
@@ -2310,6 +2410,8 @@ var init_pricing = __esm({
|
|
|
2310
2410
|
"gpt-realtime-2.1": 0.096,
|
|
2311
2411
|
"gpt-realtime-2.1-mini": 0.03,
|
|
2312
2412
|
"gemini-3.8-live": 0.035,
|
|
2413
|
+
"gemini-3.8-live-extended-thinking": 0.035,
|
|
2414
|
+
// provisional: same list rates, thinking tokens extra
|
|
2313
2415
|
"gpt-live-1": 0.06,
|
|
2314
2416
|
// 0.05 voice layer + ~0.01 gpt-5.4-mini delegation backend
|
|
2315
2417
|
"grok-voice-think-fast-2.0": 0.08,
|
|
@@ -2446,6 +2548,7 @@ var init_pricing = __esm({
|
|
|
2446
2548
|
"inworld-tts-1": 5e-3,
|
|
2447
2549
|
"speech-1.6": 0.015,
|
|
2448
2550
|
"s2.1-pro": 0.020833,
|
|
2551
|
+
"drama-3": 0.069444,
|
|
2449
2552
|
"polly-neural": 0.012,
|
|
2450
2553
|
"sonic-3.5": 0.037594,
|
|
2451
2554
|
"sonic-3.6": 0.037594,
|
|
@@ -2466,7 +2569,9 @@ var init_pricing = __esm({
|
|
|
2466
2569
|
"PlayDialogTurbo": 0.06,
|
|
2467
2570
|
"speech-02-turbo": 0.024,
|
|
2468
2571
|
"speech-02-hd": 0.04,
|
|
2469
|
-
"bulbul:v3": 0.02842105
|
|
2572
|
+
"bulbul:v3": 0.02842105,
|
|
2573
|
+
// ₹2.00/1K chars PAYG, WebSocket (sarvam.ai/api-pricing, 2026-10-17) at ₹95/$, 900 chars/min.
|
|
2574
|
+
"bulbul:v4-flash": 0.01894737
|
|
2470
2575
|
};
|
|
2471
2576
|
}
|
|
2472
2577
|
});
|
|
@@ -2488,68 +2593,6 @@ var init_knowledge = __esm({
|
|
|
2488
2593
|
}
|
|
2489
2594
|
});
|
|
2490
2595
|
|
|
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
2596
|
// ../shared-types/dist/agentTools.js
|
|
2554
2597
|
var agentTools_exports = {};
|
|
2555
2598
|
__export(agentTools_exports, {
|
|
@@ -3736,6 +3779,20 @@ var init_sttCapabilities = __esm({
|
|
|
3736
3779
|
function resolveManagedPricePerMin(sku) {
|
|
3737
3780
|
return sku.pricePerMin ?? 0;
|
|
3738
3781
|
}
|
|
3782
|
+
function skuAllowsCountry(sku, country, strict) {
|
|
3783
|
+
const allowed = sku.allowedCountries;
|
|
3784
|
+
if (!allowed || allowed.length === 0)
|
|
3785
|
+
return true;
|
|
3786
|
+
if (!country)
|
|
3787
|
+
return !strict;
|
|
3788
|
+
return allowed.includes(country.toUpperCase());
|
|
3789
|
+
}
|
|
3790
|
+
function skuFeaturedInCountry(sku, country) {
|
|
3791
|
+
const featured = sku.featuredCountries;
|
|
3792
|
+
if (!featured || featured.length === 0)
|
|
3793
|
+
return true;
|
|
3794
|
+
return !!country && featured.includes(country.toUpperCase());
|
|
3795
|
+
}
|
|
3739
3796
|
var init_managedAgentSkus = __esm({
|
|
3740
3797
|
"../shared-types/dist/managedAgentSkus.js"() {
|
|
3741
3798
|
"use strict";
|
|
@@ -7011,6 +7068,7 @@ __export(dist_exports, {
|
|
|
7011
7068
|
isE164: () => isE164,
|
|
7012
7069
|
isKnownSessionEndReason: () => isKnownSessionEndReason,
|
|
7013
7070
|
isRealtimeModel: () => isRealtimeModel,
|
|
7071
|
+
isSafeAudioUrl: () => isSafeAudioUrl,
|
|
7014
7072
|
isSafeUrl: () => isSafeUrl,
|
|
7015
7073
|
isSupportedCountry: () => isSupportedCountry2,
|
|
7016
7074
|
isSupportedPhoneNumber: () => isSupportedPhoneNumber,
|
|
@@ -7047,6 +7105,8 @@ __export(dist_exports, {
|
|
|
7047
7105
|
sanitizeTranscriptText: () => sanitizeTranscriptText,
|
|
7048
7106
|
scalarContextKeys: () => scalarContextKeys,
|
|
7049
7107
|
serializeSection: () => serializeSection,
|
|
7108
|
+
skuAllowsCountry: () => skuAllowsCountry,
|
|
7109
|
+
skuFeaturedInCountry: () => skuFeaturedInCountry,
|
|
7050
7110
|
stackCostPerMin: () => stackCostPerMin,
|
|
7051
7111
|
supportedCountry: () => supportedCountry,
|
|
7052
7112
|
toSupportedE164: () => toSupportedE164,
|
|
@@ -8255,7 +8315,7 @@ var require_dist = __commonJS({
|
|
|
8255
8315
|
];
|
|
8256
8316
|
var import_zod11 = require("zod");
|
|
8257
8317
|
var import_agentTools = (init_agentTools(), __toCommonJS(agentTools_exports));
|
|
8258
|
-
var
|
|
8318
|
+
var import_safeUrl3 = (init_safeUrl(), __toCommonJS(safeUrl_exports));
|
|
8259
8319
|
var DEFAULT_NAME = "call_webhook";
|
|
8260
8320
|
var METHODS = ["GET", "POST", "PUT", "PATCH"];
|
|
8261
8321
|
var FIELD_NAME = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
@@ -8293,7 +8353,7 @@ var require_dist = __commonJS({
|
|
|
8293
8353
|
const url = typeof s.url === "string" ? s.url.trim() : "";
|
|
8294
8354
|
if (!url) errors.push("url is required.");
|
|
8295
8355
|
else if (!/^https:\/\//i.test(url)) errors.push("url must be https \u2014 the request carries caller details.");
|
|
8296
|
-
else if (!(0,
|
|
8356
|
+
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
8357
|
if (!METHODS.includes(s.method)) errors.push(`method must be one of ${METHODS.join(", ")}.`);
|
|
8298
8358
|
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
8359
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -8768,6 +8828,98 @@ var require_dist = __commonJS({
|
|
|
8768
8828
|
run: (client, { account_id, user_id }) => client.accounts.members.remove(account_id, user_id)
|
|
8769
8829
|
})
|
|
8770
8830
|
];
|
|
8831
|
+
var import_zod16 = require("zod");
|
|
8832
|
+
var returnField = import_zod16.z.object({
|
|
8833
|
+
name: import_zod16.z.string().regex(/^[a-z][a-z0-9_]{0,39}$/).describe('snake_case field name, e.g. "booked"'),
|
|
8834
|
+
description: import_zod16.z.string().min(1).max(300).describe("What the value means, for the extractor"),
|
|
8835
|
+
type: import_zod16.z.enum(["string", "number", "boolean"]).optional().describe('Default "string"')
|
|
8836
|
+
});
|
|
8837
|
+
var IN_FLIGHT = /* @__PURE__ */ new Set(["queued", "initiated", "ringing", "in_progress", "active", "pending", "dialing"]);
|
|
8838
|
+
var INTEL_DONE = /* @__PURE__ */ new Set(["completed", "partial", "skipped", "failed"]);
|
|
8839
|
+
function isCallDone(call) {
|
|
8840
|
+
if (!call.status || IN_FLIGHT.has(call.status)) return false;
|
|
8841
|
+
if (call.status !== "completed") return true;
|
|
8842
|
+
return INTEL_DONE.has(call.intelligence?.status ?? "");
|
|
8843
|
+
}
|
|
8844
|
+
var POLL_MS = 3e3;
|
|
8845
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
8846
|
+
var taskCallTools = [
|
|
8847
|
+
defineTool({
|
|
8848
|
+
name: "place_task_call",
|
|
8849
|
+
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.",
|
|
8850
|
+
scope: "agent",
|
|
8851
|
+
risk: "sensitive",
|
|
8852
|
+
routes: ["POST /v1/agents/:id/task-calls"],
|
|
8853
|
+
keyScopes: ["calls:write"],
|
|
8854
|
+
input: {
|
|
8855
|
+
agent_id: import_zod16.z.string().describe("Agent with task calls enabled; find one with list_agents (features.task_calls_enabled: true)"),
|
|
8856
|
+
to: import_zod16.z.string().regex(/^\+[1-9]\d{6,14}$/).describe("Number to call, strict E.164, e.g. +919812345678"),
|
|
8857
|
+
on_behalf_of: import_zod16.z.string().min(1).max(120).describe("Business or person the call is for; spoken in the opener"),
|
|
8858
|
+
instructions: import_zod16.z.string().min(1).max(4e3).describe("What the call must do, in plain words"),
|
|
8859
|
+
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"),
|
|
8860
|
+
opening_line: import_zod16.z.string().min(1).max(300).optional().describe("First line spoken; defaults to the agent opener or an AI disclosure"),
|
|
8861
|
+
from_number: import_zod16.z.string().optional().describe("Caller ID registered to the workspace; omit to use the agent's attached number")
|
|
8862
|
+
},
|
|
8863
|
+
run: async (client, p) => {
|
|
8864
|
+
const res = await client.agents.placeTaskCall(p.agent_id, {
|
|
8865
|
+
to: p.to,
|
|
8866
|
+
on_behalf_of: p.on_behalf_of,
|
|
8867
|
+
instructions: p.instructions,
|
|
8868
|
+
return_fields: p.return_fields,
|
|
8869
|
+
...p.opening_line ? { opening_line: p.opening_line } : {},
|
|
8870
|
+
...p.from_number ? { from_number: p.from_number } : {}
|
|
8871
|
+
});
|
|
8872
|
+
return {
|
|
8873
|
+
call_id: res?.call_id ?? res?.id,
|
|
8874
|
+
agent_id: res?.agent_id ?? p.agent_id,
|
|
8875
|
+
status: res?.status,
|
|
8876
|
+
next: "Call wait_for_call with this call_id to get the summary and fields."
|
|
8877
|
+
};
|
|
8878
|
+
}
|
|
8879
|
+
}),
|
|
8880
|
+
defineTool({
|
|
8881
|
+
name: "wait_for_call",
|
|
8882
|
+
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.",
|
|
8883
|
+
scope: "agent",
|
|
8884
|
+
risk: "read",
|
|
8885
|
+
routes: ["GET /v1/calls/:id"],
|
|
8886
|
+
keyScopes: ["calls:read"],
|
|
8887
|
+
input: {
|
|
8888
|
+
call_id: import_zod16.z.string().describe("The call ID from place_task_call or create_call"),
|
|
8889
|
+
timeout_s: import_zod16.z.number().int().min(5).max(240).optional().describe("Max seconds to wait (5\u2013240, default 90)")
|
|
8890
|
+
},
|
|
8891
|
+
run: async (client, { call_id, timeout_s }) => {
|
|
8892
|
+
const deadline = Date.now() + (timeout_s ?? 90) * 1e3;
|
|
8893
|
+
let call = null;
|
|
8894
|
+
for (; ; ) {
|
|
8895
|
+
try {
|
|
8896
|
+
call = await client.calls.retrieve(call_id);
|
|
8897
|
+
} catch {
|
|
8898
|
+
}
|
|
8899
|
+
if (call && isCallDone(call)) {
|
|
8900
|
+
return {
|
|
8901
|
+
done: true,
|
|
8902
|
+
call_id,
|
|
8903
|
+
status: call.status,
|
|
8904
|
+
end_reason: call.end_reason ?? null,
|
|
8905
|
+
duration_seconds: call.bill_duration ?? call.duration_seconds ?? null,
|
|
8906
|
+
summary: call.intelligence?.summary ?? null,
|
|
8907
|
+
fields: call.intelligence?.structured_data ?? null,
|
|
8908
|
+
intelligence_status: call.intelligence?.status ?? null
|
|
8909
|
+
};
|
|
8910
|
+
}
|
|
8911
|
+
if (Date.now() + POLL_MS > deadline) break;
|
|
8912
|
+
await sleep(POLL_MS);
|
|
8913
|
+
}
|
|
8914
|
+
return {
|
|
8915
|
+
done: false,
|
|
8916
|
+
call_id,
|
|
8917
|
+
status: call?.status ?? "unknown",
|
|
8918
|
+
note: "Still running \u2014 call wait_for_call again with the same call_id."
|
|
8919
|
+
};
|
|
8920
|
+
}
|
|
8921
|
+
})
|
|
8922
|
+
];
|
|
8771
8923
|
var import_agentTools2 = (init_agentTools(), __toCommonJS(agentTools_exports));
|
|
8772
8924
|
var REDACTED = "[redacted]";
|
|
8773
8925
|
function redactToolSecrets(value) {
|
|
@@ -8810,15 +8962,16 @@ var require_dist = __commonJS({
|
|
|
8810
8962
|
...publish(shareLinkTools),
|
|
8811
8963
|
...publish(workspaceTools),
|
|
8812
8964
|
...publish(complianceTools),
|
|
8813
|
-
...publish(accountTools)
|
|
8965
|
+
...publish(accountTools),
|
|
8966
|
+
...publish(taskCallTools)
|
|
8814
8967
|
];
|
|
8815
8968
|
var names = /* @__PURE__ */ new Set();
|
|
8816
8969
|
for (const tool of opsToolCatalog2) {
|
|
8817
8970
|
if (names.has(tool.name)) throw new Error(`Duplicate ops tool name: ${tool.name}`);
|
|
8818
8971
|
names.add(tool.name);
|
|
8819
8972
|
}
|
|
8820
|
-
var
|
|
8821
|
-
var WORKSPACE =
|
|
8973
|
+
var import_zod17 = require("zod");
|
|
8974
|
+
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
8975
|
function withWorkspaceParam2(tool) {
|
|
8823
8976
|
return {
|
|
8824
8977
|
...tool,
|
|
@@ -8858,7 +9011,7 @@ function withToolErrors(handler) {
|
|
|
8858
9011
|
// package.json
|
|
8859
9012
|
var package_default = {
|
|
8860
9013
|
name: "@rymi/mcp",
|
|
8861
|
-
version: "2.8.
|
|
9014
|
+
version: "2.8.2",
|
|
8862
9015
|
description: "Rymi MCP server \u2014 manage AI voice agents via Model Context Protocol",
|
|
8863
9016
|
license: "MIT",
|
|
8864
9017
|
author: "Rymi AI <engineering@rymi.live>",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rymi/mcp",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.2",
|
|
4
4
|
"description": "Rymi MCP server — manage AI voice agents via Model Context Protocol",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Rymi AI <engineering@rymi.live>",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.11.1",
|
|
47
|
-
"@rymi/node": "^2.7.
|
|
47
|
+
"@rymi/node": "^2.7.2",
|
|
48
48
|
"zod": "^3.23.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|