@rymi/mcp 2.9.0 → 2.10.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 (2) hide show
  1. package/dist/index.js +404 -103
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -6953,6 +6953,263 @@ var init_supportedCountries = __esm({
6953
6953
  }
6954
6954
  });
6955
6955
 
6956
+ // ../shared-types/dist/whatsappTemplates.js
6957
+ function hasModelSlot(entry) {
6958
+ if (entry.variables?.some((v) => v.source === "model"))
6959
+ return true;
6960
+ if (entry.buttons?.some((b) => b.source === "model"))
6961
+ return true;
6962
+ return false;
6963
+ }
6964
+ function valueRuleError(value) {
6965
+ if (typeof value !== "string")
6966
+ return "must be a string";
6967
+ if (value.includes("\n") || value.includes("\r")) {
6968
+ return "must not contain newlines";
6969
+ }
6970
+ if (value.includes(" ")) {
6971
+ return "must not contain tabs";
6972
+ }
6973
+ if (/[ ]{5,}/.test(value)) {
6974
+ return "must not contain more than 4 consecutive spaces";
6975
+ }
6976
+ if (value.length > 300) {
6977
+ return `exceeds 300 characters (${value.length})`;
6978
+ }
6979
+ return null;
6980
+ }
6981
+ function langCode(key) {
6982
+ const k = key.trim().toLowerCase();
6983
+ if (k === "english")
6984
+ return "en";
6985
+ if (k === "hindi")
6986
+ return "hi";
6987
+ return k;
6988
+ }
6989
+ function resolveLangTag(key) {
6990
+ const k = key.trim().toLowerCase();
6991
+ if (k === "english")
6992
+ return "en";
6993
+ if (k === "hindi")
6994
+ return "hi";
6995
+ if (k === "hinglish")
6996
+ return "hinglish";
6997
+ return k;
6998
+ }
6999
+ function pickSyncedTemplate(templates, name, languageKey) {
7000
+ if (!templates || !Array.isArray(templates) || templates.length === 0)
7001
+ return null;
7002
+ const matching = templates.filter((t) => t.name === name);
7003
+ if (matching.length === 0)
7004
+ return null;
7005
+ if (matching.length === 1)
7006
+ return matching[0];
7007
+ const target = langCode(languageKey).toLowerCase();
7008
+ const directMatch = matching.find((t) => t.language.toLowerCase().startsWith(target));
7009
+ if (directMatch)
7010
+ return directMatch;
7011
+ if (languageKey.trim().toLowerCase() === "hinglish") {
7012
+ const enFallback = matching.find((t) => t.language.toLowerCase().startsWith("en"));
7013
+ if (enFallback)
7014
+ return enFallback;
7015
+ }
7016
+ return null;
7017
+ }
7018
+ function templateShapeError(entry, synced) {
7019
+ if (synced.variable_count !== void 0 && synced.variable_count !== null) {
7020
+ const entryVarCount = entry.variables?.length ?? 0;
7021
+ if (entryVarCount !== synced.variable_count) {
7022
+ return `variable count mismatch: entry has ${entryVarCount}, synced template has ${synced.variable_count}`;
7023
+ }
7024
+ }
7025
+ if (synced.buttons !== void 0 && synced.buttons !== null) {
7026
+ const entryButtons = entry.buttons ?? [];
7027
+ const syncedButtons = synced.buttons;
7028
+ if (entryButtons.length !== syncedButtons.length) {
7029
+ return `button count mismatch: entry has ${entryButtons.length}, synced template has ${syncedButtons.length}`;
7030
+ }
7031
+ const syncedByIndex = new Map(syncedButtons.map((b) => [b.index, b]));
7032
+ for (const eb of entryButtons) {
7033
+ const sb = syncedByIndex.get(eb.index);
7034
+ if (!sb) {
7035
+ return `button index ${eb.index} missing in synced template`;
7036
+ }
7037
+ if (eb.type !== sb.type) {
7038
+ return `button index ${eb.index} type mismatch: entry has ${eb.type}, synced template has ${sb.type}`;
7039
+ }
7040
+ }
7041
+ }
7042
+ return null;
7043
+ }
7044
+ function buildComponents(entry, languageKey, values, buttonValues = {}) {
7045
+ const components = [];
7046
+ const btnVals = buttonValues ?? {};
7047
+ if (values && values.length > 0) {
7048
+ components.push({
7049
+ type: "body",
7050
+ parameters: values.map((text) => ({ type: "text", text }))
7051
+ });
7052
+ }
7053
+ const buttons = entry.buttons ?? [];
7054
+ const sortedButtons = [...buttons].sort((a, b) => a.index - b.index);
7055
+ for (const btn of sortedButtons) {
7056
+ const idxStr = String(btn.index);
7057
+ if (btn.type === "url") {
7058
+ const val = btnVals[btn.index] ?? btnVals[idxStr] ?? btn.value;
7059
+ if (val !== void 0 && val !== null && String(val) !== "") {
7060
+ components.push({
7061
+ type: "button",
7062
+ sub_type: "url",
7063
+ index: idxStr,
7064
+ parameters: [{ type: "text", text: String(val) }]
7065
+ });
7066
+ }
7067
+ } else if (btn.type === "quick_reply") {
7068
+ let payload = btnVals[btn.index] ?? btnVals[idxStr];
7069
+ if (!payload && btn.payload) {
7070
+ payload = btn.payload.replace(/\{lang\}/g, resolveLangTag(languageKey));
7071
+ }
7072
+ if (payload !== void 0 && payload !== null && String(payload) !== "") {
7073
+ components.push({
7074
+ type: "button",
7075
+ sub_type: "quick_reply",
7076
+ index: idxStr,
7077
+ parameters: [{ type: "payload", payload: String(payload) }]
7078
+ });
7079
+ }
7080
+ }
7081
+ }
7082
+ return components;
7083
+ }
7084
+ function whatsappSenderId(creds, tenantId) {
7085
+ const pnId = creds?.phone_number_id?.trim();
7086
+ if (pnId)
7087
+ return pnId;
7088
+ const src = creds?.src_phone?.trim();
7089
+ if (src)
7090
+ return src;
7091
+ return `simulated:${tenantId || "unknown"}`;
7092
+ }
7093
+ function isCountryAllowed(phone, allowedCountries) {
7094
+ if (!phone || typeof phone !== "string")
7095
+ return false;
7096
+ const country = phoneNumberCountry(phone);
7097
+ if (!country)
7098
+ return false;
7099
+ const allowed = allowedCountries && allowedCountries.length > 0 ? allowedCountries.map((c) => c.toUpperCase()) : SUPPORTED_COUNTRY_CODES;
7100
+ return allowed.includes(country.toUpperCase());
7101
+ }
7102
+ function whatsappTemplateBindingErrors(tools) {
7103
+ if (!Array.isArray(tools))
7104
+ return [];
7105
+ const errors = [];
7106
+ for (const tool of tools) {
7107
+ if (!tool || typeof tool !== "object")
7108
+ continue;
7109
+ if (tool.tool_id !== "send_whatsapp_message")
7110
+ continue;
7111
+ const settings = tool.provider_settings;
7112
+ if (!settings || typeof settings !== "object")
7113
+ continue;
7114
+ const templates = settings.templates;
7115
+ if (templates === void 0)
7116
+ continue;
7117
+ if (!Array.isArray(templates)) {
7118
+ errors.push("send_whatsapp_message: provider_settings.templates must be an array");
7119
+ continue;
7120
+ }
7121
+ for (let i = 0; i < templates.length; i++) {
7122
+ const entry = templates[i];
7123
+ const parsed = boundTemplateEntrySchema.safeParse(entry);
7124
+ if (!parsed.success) {
7125
+ const keyName = entry && typeof entry === "object" && entry.key ? String(entry.key) : `index_${i}`;
7126
+ for (const issue of parsed.error.issues) {
7127
+ const pathStr = issue.path.length > 0 ? issue.path.join(".") : "root";
7128
+ errors.push(`send_whatsapp_message template "${keyName}": ${pathStr}: ${issue.message}`);
7129
+ }
7130
+ }
7131
+ }
7132
+ }
7133
+ return errors;
7134
+ }
7135
+ var import_zod, SUPPORTED_WHATSAPP_COUNTRIES, slotSourceSchema, slotSchema, buttonTypeSchema, buttonSchema, basePolicySchema, boundPolicySchema, baseEntryShape, templateEntrySchema, boundTemplateEntrySchema;
7136
+ var init_whatsappTemplates = __esm({
7137
+ "../shared-types/dist/whatsappTemplates.js"() {
7138
+ "use strict";
7139
+ import_zod = require("zod");
7140
+ init_supportedCountries();
7141
+ SUPPORTED_WHATSAPP_COUNTRIES = SUPPORTED_COUNTRY_CODES;
7142
+ slotSourceSchema = import_zod.z.enum([
7143
+ "fixed",
7144
+ "agent_field",
7145
+ "captured",
7146
+ "call",
7147
+ "model"
7148
+ ]);
7149
+ slotSchema = import_zod.z.object({
7150
+ source: slotSourceSchema,
7151
+ path: import_zod.z.string().optional(),
7152
+ value: import_zod.z.string().optional()
7153
+ });
7154
+ buttonTypeSchema = import_zod.z.enum(["url", "quick_reply"]);
7155
+ buttonSchema = import_zod.z.object({
7156
+ index: import_zod.z.number().int().min(0),
7157
+ type: buttonTypeSchema,
7158
+ source: slotSourceSchema.optional(),
7159
+ path: import_zod.z.string().optional(),
7160
+ value: import_zod.z.string().optional(),
7161
+ payload: import_zod.z.string().optional(),
7162
+ reply: import_zod.z.record(import_zod.z.string()).optional()
7163
+ });
7164
+ basePolicySchema = import_zod.z.object({
7165
+ once_per_number: import_zod.z.boolean(),
7166
+ daily_cap: import_zod.z.number().int().min(1).max(3).optional(),
7167
+ allowed_countries: import_zod.z.array(import_zod.z.string()).optional(),
7168
+ max_per_call: import_zod.z.number().int().min(1).optional(),
7169
+ recipient: import_zod.z.enum(["caller_phone", "spoken_confirmed"]).optional(),
7170
+ offer: import_zod.z.enum(["before_goodbye", "on_request"]).optional(),
7171
+ offer_instruction: import_zod.z.string().min(1).optional()
7172
+ });
7173
+ boundPolicySchema = basePolicySchema.extend({
7174
+ max_per_call: import_zod.z.number().int().min(1),
7175
+ recipient: import_zod.z.enum(["caller_phone", "spoken_confirmed"]),
7176
+ offer: import_zod.z.enum(["before_goodbye", "on_request"]),
7177
+ offer_instruction: import_zod.z.string().min(1)
7178
+ });
7179
+ baseEntryShape = {
7180
+ key: import_zod.z.string().min(1),
7181
+ label: import_zod.z.string().min(1),
7182
+ languages: import_zod.z.record(import_zod.z.string().min(1)),
7183
+ variables: import_zod.z.array(slotSchema),
7184
+ buttons: import_zod.z.array(buttonSchema).optional()
7185
+ };
7186
+ templateEntrySchema = import_zod.z.object({
7187
+ ...baseEntryShape,
7188
+ policy: basePolicySchema
7189
+ }).superRefine((data, ctx) => {
7190
+ if (data.key !== "free_form" && hasModelSlot(data)) {
7191
+ ctx.addIssue({
7192
+ code: import_zod.z.ZodIssueCode.custom,
7193
+ message: "model slot is only allowed on the free_form entry",
7194
+ path: ["variables"]
7195
+ });
7196
+ }
7197
+ });
7198
+ boundTemplateEntrySchema = import_zod.z.object({
7199
+ ...baseEntryShape,
7200
+ policy: boundPolicySchema
7201
+ }).superRefine((data, ctx) => {
7202
+ if (data.key !== "free_form" && hasModelSlot(data)) {
7203
+ ctx.addIssue({
7204
+ code: import_zod.z.ZodIssueCode.custom,
7205
+ message: "model slot is only allowed on the free_form entry",
7206
+ path: ["variables"]
7207
+ });
7208
+ }
7209
+ });
7210
+ }
7211
+ });
7212
+
6956
7213
  // ../shared-types/dist/index.js
6957
7214
  var dist_exports = {};
6958
7215
  __export(dist_exports, {
@@ -7018,6 +7275,7 @@ __export(dist_exports, {
7018
7275
  STUDIO_SUPPORTED_LANGUAGES: () => STUDIO_SUPPORTED_LANGUAGES,
7019
7276
  SUPPORTED_COUNTRIES: () => SUPPORTED_COUNTRIES,
7020
7277
  SUPPORTED_COUNTRY_CODES: () => SUPPORTED_COUNTRY_CODES,
7278
+ SUPPORTED_WHATSAPP_COUNTRIES: () => SUPPORTED_WHATSAPP_COUNTRIES,
7021
7279
  SYSTEM_VARIABLES: () => SYSTEM_VARIABLES,
7022
7280
  SYSTEM_VARIABLE_KEYS: () => SYSTEM_VARIABLE_KEYS,
7023
7281
  TRACE_ID_HEADER: () => TRACE_ID_HEADER,
@@ -7030,9 +7288,15 @@ __export(dist_exports, {
7030
7288
  applyFlatDiff: () => applyFlatDiff,
7031
7289
  applyWritesToConfig: () => applyWritesToConfig,
7032
7290
  assertE164: () => assertE164,
7291
+ basePolicySchema: () => basePolicySchema,
7292
+ boundPolicySchema: () => boundPolicySchema,
7293
+ boundTemplateEntrySchema: () => boundTemplateEntrySchema,
7294
+ buildComponents: () => buildComponents,
7033
7295
  buildPlans: () => buildPlans,
7034
7296
  buildSystemBusinessValues: () => buildSystemBusinessValues,
7035
7297
  buildTranscriptText: () => buildTranscriptText,
7298
+ buttonSchema: () => buttonSchema,
7299
+ buttonTypeSchema: () => buttonTypeSchema,
7036
7300
  canonicalizeLlmProvider: () => canonicalizeLlmProvider,
7037
7301
  changedFocusedStudioSections: () => changedFocusedStudioSections,
7038
7302
  clarifyableKeys: () => clarifyableKeys,
@@ -7068,6 +7332,7 @@ __export(dist_exports, {
7068
7332
  inferCallerMetadataKey: () => inferCallerMetadataKey2,
7069
7333
  isAgentToolEnabled: () => isAgentToolEnabled,
7070
7334
  isApiKeyScope: () => isApiKeyScope,
7335
+ isCountryAllowed: () => isCountryAllowed,
7071
7336
  isCuratedBusinessType: () => isCuratedBusinessType,
7072
7337
  isDemoDialableCountry: () => isDemoDialableCountry,
7073
7338
  isE164: () => isE164,
@@ -7082,6 +7347,7 @@ __export(dist_exports, {
7082
7347
  isValidTraceId: () => isValidTraceId,
7083
7348
  isVoicemailEndReason: () => isVoicemailEndReason,
7084
7349
  isWithinCallingWindow: () => isWithinCallingWindow,
7350
+ langCode: () => langCode,
7085
7351
  listActiveConnectors: () => listActiveConnectors,
7086
7352
  listContextFields: () => listContextFields,
7087
7353
  mergePostCallConfig: () => mergePostCallConfig,
@@ -7093,6 +7359,7 @@ __export(dist_exports, {
7093
7359
  normalizeVariableAcquisition: () => normalizeVariableAcquisition2,
7094
7360
  phoneNumberCountry: () => phoneNumberCountry,
7095
7361
  phoneNumberIssue: () => phoneNumberIssue,
7362
+ pickSyncedTemplate: () => pickSyncedTemplate,
7096
7363
  pollUntil: () => pollUntil,
7097
7364
  populatedFocusedStudioSections: () => populatedFocusedStudioSections,
7098
7365
  pronunciationSupport: () => pronunciationSupport,
@@ -7106,6 +7373,7 @@ __export(dist_exports, {
7106
7373
  reinitKeys: () => reinitKeys,
7107
7374
  renderFieldReference: () => renderFieldReference,
7108
7375
  requiredConsentChannels: () => requiredConsentChannels,
7376
+ resolveLangTag: () => resolveLangTag,
7109
7377
  resolveManagedPricePerMin: () => resolveManagedPricePerMin,
7110
7378
  resolveSystemAlias: () => resolveSystemAlias,
7111
7379
  sanitizeTranscriptText: () => sanitizeTranscriptText,
@@ -7113,13 +7381,20 @@ __export(dist_exports, {
7113
7381
  serializeSection: () => serializeSection,
7114
7382
  skuAllowsCountry: () => skuAllowsCountry,
7115
7383
  skuFeaturedInCountry: () => skuFeaturedInCountry,
7384
+ slotSchema: () => slotSchema,
7385
+ slotSourceSchema: () => slotSourceSchema,
7116
7386
  stackCostPerMin: () => stackCostPerMin,
7117
7387
  supportedCountry: () => supportedCountry,
7388
+ templateEntrySchema: () => templateEntrySchema,
7389
+ templateShapeError: () => templateShapeError,
7118
7390
  toSupportedE164: () => toSupportedE164,
7119
7391
  toToolCapabilitySafeView: () => toToolCapabilitySafeView,
7120
7392
  validateAgentTemplate: () => validateAgentTemplate,
7121
7393
  validatePublishConfig: () => validatePublishConfig,
7394
+ valueRuleError: () => valueRuleError,
7122
7395
  variablesFromRequiredSlots: () => variablesFromRequiredSlots2,
7396
+ whatsappSenderId: () => whatsappSenderId,
7397
+ whatsappTemplateBindingErrors: () => whatsappTemplateBindingErrors,
7123
7398
  writeToPayload: () => writeToPayload
7124
7399
  });
7125
7400
  var deriveExtractionFields2, deriveRequiredSlots2, humanizeKey2, inferCallerMetadataKey2, normalizeVariableAcquisition2, normalizeKey2, registerVariable2, variablesFromRequiredSlots2;
@@ -7169,6 +7444,7 @@ var init_dist2 = __esm({
7169
7444
  init_safeUrl();
7170
7445
  init_connectorCatalog();
7171
7446
  init_supportedCountries();
7447
+ init_whatsappTemplates();
7172
7448
  deriveExtractionFields2 = deriveExtractionFields;
7173
7449
  deriveRequiredSlots2 = deriveRequiredSlots;
7174
7450
  humanizeKey2 = humanizeKey;
@@ -7227,7 +7503,7 @@ var require_dist = __commonJS({
7227
7503
  if (p.scope === "account" && !PRIVILEGED.has(p.role)) return "deny";
7228
7504
  return p.risk === "write" ? "propose" : "confirm";
7229
7505
  }
7230
- var import_zod = require("zod");
7506
+ var import_zod2 = require("zod");
7231
7507
  var import_shared_types = (init_dist2(), __toCommonJS(dist_exports));
7232
7508
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7233
7509
  var norm2 = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
@@ -7244,32 +7520,32 @@ var require_dist = __commonJS({
7244
7520
  throw new Error(`No agent named "${raw}" found \u2014 call list_agents to get a valid agent UUID.`);
7245
7521
  }
7246
7522
  var agentConfigFields = {
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."),
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.'),
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."),
7263
- custom_llm_url: import_zod.z.string().nullable().optional().describe("Self-hosted LLM endpoint (https:// or wss://). Enterprise only; null clears."),
7264
- custom_voice_url: import_zod.z.string().nullable().optional().describe("Self-hosted TTS endpoint (https:// or wss://). Enterprise only; null clears."),
7265
- custom_voice_mode: import_zod.z.enum(["rymi", "openai-compat"]).optional().describe("Wire format for custom_voice_url."),
7266
- custom_transcriber_url: import_zod.z.string().nullable().optional().describe("Self-hosted STT endpoint (https:// or wss://). Enterprise only; null clears."),
7267
- prompt_mode: import_zod.z.enum(["builder", "raw"]).optional().describe('How the system prompt is interpreted: "builder" (server structures persona/playbook from system_prompt) or "raw" (use system_prompt verbatim \u2014 requires system_prompt). Defaults to "builder".'),
7268
- persona: import_zod.z.record(import_zod.z.any()).optional().describe("Persona object. persona.name is the name the agent speaks and introduces itself as (also shown on share/tester pages); it defaults to the label when omitted. Note: callerPersonas must be objects of shape {type, approach, detectedWhen}, NOT plain strings."),
7269
- playbook: import_zod.z.record(import_zod.z.any()).optional().describe("Playbook configuration object"),
7270
- advanced: import_zod.z.record(import_zod.z.any()).optional(),
7271
- features: import_zod.z.record(import_zod.z.any()).optional(),
7272
- post_call: import_zod.z.record(import_zod.z.any()).optional()
7523
+ system_prompt: import_zod2.z.string().optional().describe("Full system prompt for the agent. If given without persona/playbook, the server auto-structures it."),
7524
+ voice: import_zod2.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.'),
7525
+ language: import_zod2.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.'),
7526
+ supported_languages: import_zod2.z.array(import_zod2.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.'),
7527
+ llm_provider: import_zod2.z.enum(import_shared_types.AGENT_LLM_PROVIDERS).optional().describe("LLM provider (custom agents only; fixed by the plan for managed agents)."),
7528
+ llm_model: import_zod2.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.'),
7529
+ llm_fallback_provider: import_zod2.z.string().nullable().optional().describe("Fallback LLM provider; null clears it. Fixed by the plan for managed agents."),
7530
+ llm_fallback_model: import_zod2.z.string().nullable().optional().describe("Fallback LLM model; null clears it. Fixed by the plan for managed agents."),
7531
+ stt_provider: import_zod2.z.string().optional().describe('Speech-to-text provider (e.g. "deepgram", "sarvam"). Fixed by the plan for managed agents.'),
7532
+ stt_model: import_zod2.z.string().optional().describe("Speech-to-text model id. Fixed by the plan for managed agents."),
7533
+ stt_fallback_provider: import_zod2.z.string().nullable().optional().describe("Fallback STT provider. Fixed by the plan for managed agents."),
7534
+ stt_fallback_model: import_zod2.z.string().nullable().optional().describe("Fallback STT model. Fixed by the plan for managed agents."),
7535
+ tts_provider: import_zod2.z.string().optional().describe('Text-to-speech provider (e.g. "elevenlabs", "sarvam", "cartesia"). Fixed by the plan for managed agents.'),
7536
+ tts_model: import_zod2.z.string().optional().describe("Text-to-speech model id. Fixed by the plan for managed agents."),
7537
+ tts_fallback_provider: import_zod2.z.string().nullable().optional().describe("Fallback TTS provider. Fixed by the plan for managed agents."),
7538
+ tts_fallback_model: import_zod2.z.string().nullable().optional().describe("Fallback TTS model. Fixed by the plan for managed agents."),
7539
+ custom_llm_url: import_zod2.z.string().nullable().optional().describe("Self-hosted LLM endpoint (https:// or wss://). Enterprise only; null clears."),
7540
+ custom_voice_url: import_zod2.z.string().nullable().optional().describe("Self-hosted TTS endpoint (https:// or wss://). Enterprise only; null clears."),
7541
+ custom_voice_mode: import_zod2.z.enum(["rymi", "openai-compat"]).optional().describe("Wire format for custom_voice_url."),
7542
+ custom_transcriber_url: import_zod2.z.string().nullable().optional().describe("Self-hosted STT endpoint (https:// or wss://). Enterprise only; null clears."),
7543
+ prompt_mode: import_zod2.z.enum(["builder", "raw"]).optional().describe('How the system prompt is interpreted: "builder" (server structures persona/playbook from system_prompt) or "raw" (use system_prompt verbatim \u2014 requires system_prompt). Defaults to "builder".'),
7544
+ persona: import_zod2.z.record(import_zod2.z.any()).optional().describe("Persona object. persona.name is the name the agent speaks and introduces itself as (also shown on share/tester pages); it defaults to the label when omitted. Note: callerPersonas must be objects of shape {type, approach, detectedWhen}, NOT plain strings."),
7545
+ playbook: import_zod2.z.record(import_zod2.z.any()).optional().describe("Playbook configuration object"),
7546
+ advanced: import_zod2.z.record(import_zod2.z.any()).optional(),
7547
+ features: import_zod2.z.record(import_zod2.z.any()).optional(),
7548
+ post_call: import_zod2.z.record(import_zod2.z.any()).optional()
7273
7549
  };
7274
7550
  function foldPromptMode(params) {
7275
7551
  const { prompt_mode, ...rest } = params;
@@ -7306,8 +7582,8 @@ var require_dist = __commonJS({
7306
7582
  routes: ["GET /v1/agents"],
7307
7583
  keyScopes: ["agents:read"],
7308
7584
  input: {
7309
- limit: import_zod.z.number().int().min(1).max(500).optional().describe("Max agents to return (default 50)"),
7310
- offset: import_zod.z.number().int().min(0).optional().describe("Pagination offset")
7585
+ limit: import_zod2.z.number().int().min(1).max(500).optional().describe("Max agents to return (default 50)"),
7586
+ offset: import_zod2.z.number().int().min(0).optional().describe("Pagination offset")
7311
7587
  },
7312
7588
  run: (client, { limit, offset }) => client.agents.list({ limit, offset })
7313
7589
  }),
@@ -7318,7 +7594,7 @@ var require_dist = __commonJS({
7318
7594
  risk: "read",
7319
7595
  routes: ["GET /v1/agents/:id"],
7320
7596
  keyScopes: ["agents:read"],
7321
- input: { agent_id: import_zod.z.string().uuid().describe("The agent UUID") },
7597
+ input: { agent_id: import_zod2.z.string().uuid().describe("The agent UUID") },
7322
7598
  run: (client, { agent_id }) => client.agents.retrieve(agent_id)
7323
7599
  }),
7324
7600
  defineTool({
@@ -7339,8 +7615,8 @@ var require_dist = __commonJS({
7339
7615
  routes: ["GET /v1/agents/llm-options"],
7340
7616
  keyScopes: ["agents:read"],
7341
7617
  input: {
7342
- provider: import_zod.z.string().optional().describe('Filter to one voice provider, e.g. "elevenlabs", "sarvam", "gemini", "cartesia", "deepgram".'),
7343
- model_id: import_zod.z.string().optional().describe("Filter to voices compatible with a given model id (matched against the voice's supported_model_ids).")
7618
+ provider: import_zod2.z.string().optional().describe('Filter to one voice provider, e.g. "elevenlabs", "sarvam", "gemini", "cartesia", "deepgram".'),
7619
+ model_id: import_zod2.z.string().optional().describe("Filter to voices compatible with a given model id (matched against the voice's supported_model_ids).")
7344
7620
  },
7345
7621
  run: async (client, { provider, model_id }) => {
7346
7622
  const { voices } = await client.agents.llmOptions();
@@ -7373,9 +7649,9 @@ var require_dist = __commonJS({
7373
7649
  routes: ["POST /v1/agents/stack-preview"],
7374
7650
  keyScopes: ["agents:write"],
7375
7651
  input: {
7376
- supported_languages: import_zod.z.array(import_zod.z.string()).min(1).describe('BCP-47 languages to resolve a stack for, e.g. ["hi-IN","en-US"].'),
7377
- language: import_zod.z.string().optional().describe("Primary BCP-47 language (defaults to the first supported language)."),
7378
- current_provider_config: import_zod.z.record(import_zod.z.any()).optional().describe("Existing provider_config to diff against, if any.")
7652
+ supported_languages: import_zod2.z.array(import_zod2.z.string()).min(1).describe('BCP-47 languages to resolve a stack for, e.g. ["hi-IN","en-US"].'),
7653
+ language: import_zod2.z.string().optional().describe("Primary BCP-47 language (defaults to the first supported language)."),
7654
+ current_provider_config: import_zod2.z.record(import_zod2.z.any()).optional().describe("Existing provider_config to diff against, if any.")
7379
7655
  },
7380
7656
  run: (client, params) => client.agents.previewStack(params)
7381
7657
  }),
@@ -7387,10 +7663,10 @@ var require_dist = __commonJS({
7387
7663
  routes: ["GET /v1/agents/:id/calls"],
7388
7664
  keyScopes: ["agents:read"],
7389
7665
  input: {
7390
- agent_id: import_zod.z.string().uuid(),
7391
- limit: import_zod.z.number().int().min(1).max(200).optional(),
7392
- offset: import_zod.z.number().int().min(0).optional(),
7393
- status: import_zod.z.string().optional().describe("Filter by call status")
7666
+ agent_id: import_zod2.z.string().uuid(),
7667
+ limit: import_zod2.z.number().int().min(1).max(200).optional(),
7668
+ offset: import_zod2.z.number().int().min(0).optional(),
7669
+ status: import_zod2.z.string().optional().describe("Filter by call status")
7394
7670
  },
7395
7671
  run: (client, { agent_id, ...params }) => client.agents.listCalls(agent_id, params)
7396
7672
  }),
@@ -7402,11 +7678,11 @@ var require_dist = __commonJS({
7402
7678
  routes: ["POST /v1/agents/validate-publish"],
7403
7679
  keyScopes: ["agents:write"],
7404
7680
  input: {
7405
- agent_id: import_zod.z.string().uuid().optional().describe("Merge validation with a persisted agent"),
7406
- name: import_zod.z.string().optional(),
7407
- voice: import_zod.z.string().optional(),
7408
- persona: import_zod.z.record(import_zod.z.any()).optional(),
7409
- playbook: import_zod.z.record(import_zod.z.any()).optional()
7681
+ agent_id: import_zod2.z.string().uuid().optional().describe("Merge validation with a persisted agent"),
7682
+ name: import_zod2.z.string().optional(),
7683
+ voice: import_zod2.z.string().optional(),
7684
+ persona: import_zod2.z.record(import_zod2.z.any()).optional(),
7685
+ playbook: import_zod2.z.record(import_zod2.z.any()).optional()
7410
7686
  },
7411
7687
  run: (client, params) => client.agents.validatePublish(params)
7412
7688
  }),
@@ -7418,10 +7694,10 @@ var require_dist = __commonJS({
7418
7694
  routes: ["GET /v1/agents/:id", "POST /v1/agents/apply-changes"],
7419
7695
  keyScopes: ["agents:read", "agents:write"],
7420
7696
  input: {
7421
- agent_id: import_zod.z.string().uuid().describe("Agent to load currentConfig from (recommended)"),
7422
- changes: import_zod.z.array(import_zod.z.object({ key: import_zod.z.string(), value: import_zod.z.any() })).describe("Array of {key, value} field changes"),
7423
- mode: import_zod.z.enum(["create", "edit"]).describe("create = new agent, edit = updating existing"),
7424
- lenient: import_zod.z.boolean().optional().describe("Skip unknown-field hard-fail (not recommended)")
7697
+ agent_id: import_zod2.z.string().uuid().describe("Agent to load currentConfig from (recommended)"),
7698
+ changes: import_zod2.z.array(import_zod2.z.object({ key: import_zod2.z.string(), value: import_zod2.z.any() })).describe("Array of {key, value} field changes"),
7699
+ mode: import_zod2.z.enum(["create", "edit"]).describe("create = new agent, edit = updating existing"),
7700
+ lenient: import_zod2.z.boolean().optional().describe("Skip unknown-field hard-fail (not recommended)")
7425
7701
  },
7426
7702
  run: async (client, { agent_id, changes, mode, lenient }) => {
7427
7703
  const current = await client.agents.retrieve(agent_id);
@@ -7442,8 +7718,8 @@ var require_dist = __commonJS({
7442
7718
  routes: ["POST /v1/agents/enrich-company"],
7443
7719
  keyScopes: ["agents:write"],
7444
7720
  input: {
7445
- company_name: import_zod.z.string().min(1),
7446
- website_url: import_zod.z.string().url()
7721
+ company_name: import_zod2.z.string().min(1),
7722
+ website_url: import_zod2.z.string().url()
7447
7723
  },
7448
7724
  run: (client, { company_name, website_url }) => client.agents.enrichCompany({ companyName: company_name, websiteUrl: website_url })
7449
7725
  }),
@@ -7455,10 +7731,10 @@ var require_dist = __commonJS({
7455
7731
  routes: ["POST /v1/agents/generate"],
7456
7732
  keyScopes: ["agents:write"],
7457
7733
  input: {
7458
- prompt: import_zod.z.string().min(1).describe("Description of the agent you want to create"),
7459
- mode: import_zod.z.enum(["create", "edit"]).optional().default("create"),
7460
- current_config: import_zod.z.record(import_zod.z.any()).optional().describe("Existing config when editing"),
7461
- options: import_zod.z.record(import_zod.z.any()).optional().describe("Override hints e.g. {voice, llm_provider}")
7734
+ prompt: import_zod2.z.string().min(1).describe("Description of the agent you want to create"),
7735
+ mode: import_zod2.z.enum(["create", "edit"]).optional().default("create"),
7736
+ current_config: import_zod2.z.record(import_zod2.z.any()).optional().describe("Existing config when editing"),
7737
+ options: import_zod2.z.record(import_zod2.z.any()).optional().describe("Override hints e.g. {voice, llm_provider}")
7462
7738
  },
7463
7739
  run: (client, params) => client.agents.generate({ prompt: params.prompt, options: params.options })
7464
7740
  }),
@@ -7470,7 +7746,7 @@ var require_dist = __commonJS({
7470
7746
  routes: ["POST /v1/agents"],
7471
7747
  keyScopes: ["agents:write"],
7472
7748
  input: {
7473
- name: import_zod.z.string().min(1).describe("Studio label: the name shown in the sidebar/agent list only. The name the agent speaks is persona.name."),
7749
+ name: import_zod2.z.string().min(1).describe("Studio label: the name shown in the sidebar/agent list only. The name the agent speaks is persona.name."),
7474
7750
  ...agentConfigFields
7475
7751
  },
7476
7752
  run: (client, params) => client.agents.create(normalizeLlmProvider(foldPromptMode(params)))
@@ -7483,10 +7759,10 @@ var require_dist = __commonJS({
7483
7759
  routes: ["GET /v1/agents", "PUT /v1/agents/:id"],
7484
7760
  keyScopes: ["agents:read", "agents:write"],
7485
7761
  input: {
7486
- agent_id: import_zod.z.string().uuid().describe("The agent UUID to update"),
7487
- name: import_zod.z.string().optional().describe("Studio label (sidebar/agent list only). To change the spoken name, set persona.name."),
7762
+ agent_id: import_zod2.z.string().uuid().describe("The agent UUID to update"),
7763
+ name: import_zod2.z.string().optional().describe("Studio label (sidebar/agent list only). To change the spoken name, set persona.name."),
7488
7764
  ...agentConfigFields,
7489
- chat_summary: import_zod.z.string().nullable().optional()
7765
+ chat_summary: import_zod2.z.string().nullable().optional()
7490
7766
  },
7491
7767
  run: async (client, { agent_id, ...params }) => client.agents.update(await resolveAgentId(client, agent_id), normalizeLlmProvider(foldPromptMode(params)))
7492
7768
  }),
@@ -7497,7 +7773,7 @@ var require_dist = __commonJS({
7497
7773
  risk: "sensitive",
7498
7774
  routes: ["GET /v1/agents", "DELETE /v1/agents/:id"],
7499
7775
  keyScopes: ["agents:read", "agents:write"],
7500
- input: { agent_id: import_zod.z.string().uuid().describe("The agent UUID to delete") },
7776
+ input: { agent_id: import_zod2.z.string().uuid().describe("The agent UUID to delete") },
7501
7777
  run: async (client, { agent_id }) => client.agents.delete(await resolveAgentId(client, agent_id))
7502
7778
  }),
7503
7779
  defineTool({
@@ -7507,11 +7783,11 @@ var require_dist = __commonJS({
7507
7783
  risk: "write",
7508
7784
  routes: ["GET /v1/agents", "POST /v1/agents/:id/clone"],
7509
7785
  keyScopes: ["agents:read", "agents:write"],
7510
- input: { agent_id: import_zod.z.string().uuid().describe("The agent UUID to clone") },
7786
+ input: { agent_id: import_zod2.z.string().uuid().describe("The agent UUID to clone") },
7511
7787
  run: async (client, { agent_id }) => client.agents.clone(await resolveAgentId(client, agent_id))
7512
7788
  })
7513
7789
  ];
7514
- var import_zod2 = require("zod");
7790
+ var import_zod22 = require("zod");
7515
7791
  var callTools = [
7516
7792
  defineTool({
7517
7793
  name: "list_calls",
@@ -7521,10 +7797,10 @@ var require_dist = __commonJS({
7521
7797
  routes: ["GET /v1/calls"],
7522
7798
  keyScopes: ["calls:read"],
7523
7799
  input: {
7524
- limit: import_zod2.z.number().int().min(1).max(200).optional().describe("Max calls to return"),
7525
- offset: import_zod2.z.number().int().min(0).optional().describe("Pagination offset"),
7526
- cursor: import_zod2.z.string().optional().describe("Opaque cursor for keyset pagination"),
7527
- status: import_zod2.z.string().optional().describe("Filter by call status (e.g. completed, in_progress, failed)")
7800
+ limit: import_zod22.z.number().int().min(1).max(200).optional().describe("Max calls to return"),
7801
+ offset: import_zod22.z.number().int().min(0).optional().describe("Pagination offset"),
7802
+ cursor: import_zod22.z.string().optional().describe("Opaque cursor for keyset pagination"),
7803
+ status: import_zod22.z.string().optional().describe("Filter by call status (e.g. completed, in_progress, failed)")
7528
7804
  },
7529
7805
  run: (client, params) => client.calls.list(params)
7530
7806
  }),
@@ -7536,8 +7812,8 @@ var require_dist = __commonJS({
7536
7812
  routes: ["GET /v1/calls/active"],
7537
7813
  keyScopes: ["calls:read"],
7538
7814
  input: {
7539
- limit: import_zod2.z.number().int().min(1).max(200).optional(),
7540
- offset: import_zod2.z.number().int().min(0).optional()
7815
+ limit: import_zod22.z.number().int().min(1).max(200).optional(),
7816
+ offset: import_zod22.z.number().int().min(0).optional()
7541
7817
  },
7542
7818
  run: (client, params) => client.calls.active(params)
7543
7819
  }),
@@ -7548,7 +7824,7 @@ var require_dist = __commonJS({
7548
7824
  risk: "read",
7549
7825
  routes: ["GET /v1/calls/:id"],
7550
7826
  keyScopes: ["calls:read"],
7551
- input: { call_id: import_zod2.z.string().describe("The call ID") },
7827
+ input: { call_id: import_zod22.z.string().describe("The call ID") },
7552
7828
  run: (client, { call_id }) => client.calls.retrieve(call_id)
7553
7829
  }),
7554
7830
  defineTool({
@@ -7558,7 +7834,7 @@ var require_dist = __commonJS({
7558
7834
  risk: "read",
7559
7835
  routes: ["GET /v1/calls/:id/summary"],
7560
7836
  keyScopes: ["calls:read"],
7561
- input: { call_id: import_zod2.z.string().describe("The call ID") },
7837
+ input: { call_id: import_zod22.z.string().describe("The call ID") },
7562
7838
  run: (client, { call_id }) => client.calls.summary(call_id)
7563
7839
  }),
7564
7840
  defineTool({
@@ -7568,7 +7844,7 @@ var require_dist = __commonJS({
7568
7844
  risk: "read",
7569
7845
  routes: ["GET /v1/calls/:id/transcript"],
7570
7846
  keyScopes: ["calls:read"],
7571
- input: { call_id: import_zod2.z.string().describe("The call ID") },
7847
+ input: { call_id: import_zod22.z.string().describe("The call ID") },
7572
7848
  run: (client, { call_id }) => client.calls.transcript(call_id)
7573
7849
  }),
7574
7850
  defineTool({
@@ -7578,7 +7854,7 @@ var require_dist = __commonJS({
7578
7854
  risk: "read",
7579
7855
  routes: ["GET /v1/calls/:id/recording"],
7580
7856
  keyScopes: ["calls:read"],
7581
- input: { call_id: import_zod2.z.string().describe("The call ID") },
7857
+ input: { call_id: import_zod22.z.string().describe("The call ID") },
7582
7858
  run: (client, { call_id }) => client.calls.recording(call_id)
7583
7859
  }),
7584
7860
  defineTool({
@@ -7598,29 +7874,41 @@ var require_dist = __commonJS({
7598
7874
  risk: "write",
7599
7875
  routes: ["POST /v1/calls/:id/reprocess"],
7600
7876
  keyScopes: ["calls:write"],
7601
- input: { call_id: import_zod2.z.string().describe("The call ID to reprocess") },
7877
+ input: { call_id: import_zod22.z.string().describe("The call ID to reprocess") },
7602
7878
  run: (client, { call_id }) => client.calls.reprocess(call_id)
7603
7879
  }),
7604
7880
  defineTool({
7605
7881
  name: "create_call",
7606
- description: "WARNING: places a real, billable PSTN call. `from_number` is the caller ID shown to the recipient and must be a phone number registered to your account \u2014 omit it to use the agent's attached number. `identity` is the destination in strict E.164 (e.g. +15555550123).",
7882
+ description: "WARNING: places a real, billable PSTN call. `from_number` is the caller ID shown to the recipient and must be a phone number registered to your account \u2014 omit it to use the agent's attached number. `identity` is the destination in strict E.164 (e.g. +15555550123). Set `test` true to mark the call as a test and dial from the shared test number (no from_number; the destination must be a confirmed number).",
7607
7883
  scope: "agent",
7608
7884
  risk: "sensitive",
7609
7885
  routes: ["POST /v1/calls"],
7610
7886
  keyScopes: ["calls:write"],
7611
7887
  input: {
7612
- agent_id: import_zod2.z.string().uuid().describe("The agent that will handle the call"),
7613
- participants: import_zod2.z.array(import_zod2.z.object({
7614
- transport: import_zod2.z.enum(["webrtc", "pstn"]),
7615
- identity: import_zod2.z.string().describe("Destination phone number (pstn) or participant identity (webrtc)"),
7616
- from_number: import_zod2.z.string().optional().describe("Caller ID / from number for pstn"),
7617
- metadata: import_zod2.z.record(import_zod2.z.any()).optional()
7888
+ agent_id: import_zod22.z.string().uuid().describe("The agent that will handle the call"),
7889
+ participants: import_zod22.z.array(import_zod22.z.object({
7890
+ transport: import_zod22.z.enum(["webrtc", "pstn"]),
7891
+ identity: import_zod22.z.string().describe("Destination phone number (pstn) or participant identity (webrtc)"),
7892
+ from_number: import_zod22.z.string().optional().describe("Caller ID / from number for pstn"),
7893
+ metadata: import_zod22.z.record(import_zod22.z.any()).optional()
7618
7894
  })).min(1),
7619
- metadata: import_zod2.z.record(import_zod2.z.any()).optional(),
7620
- variables: import_zod2.z.record(import_zod2.z.any()).optional().describe("Playbook variables to seed the call"),
7621
- post_call: import_zod2.z.record(import_zod2.z.any()).optional()
7895
+ metadata: import_zod22.z.record(import_zod22.z.any()).optional(),
7896
+ variables: import_zod22.z.record(import_zod22.z.any()).optional().describe("Playbook variables to seed the call"),
7897
+ post_call: import_zod22.z.record(import_zod22.z.any()).optional(),
7898
+ test: import_zod22.z.boolean().optional().describe("Mark the call as a test and use the shared test number. Do not also set from_number.")
7622
7899
  },
7623
- run: (client, params) => client.calls.create(params)
7900
+ run: (client, params) => {
7901
+ const { test, metadata, ...rest } = params;
7902
+ if (test) {
7903
+ return client.calls.create({
7904
+ ...rest,
7905
+ test: true,
7906
+ use_test_pool: true,
7907
+ metadata: { ...metadata || {}, source: "mcp_test" }
7908
+ });
7909
+ }
7910
+ return client.calls.create({ ...rest, ...metadata ? { metadata } : {} });
7911
+ }
7624
7912
  }),
7625
7913
  defineTool({
7626
7914
  name: "batch_call",
@@ -7630,18 +7918,18 @@ var require_dist = __commonJS({
7630
7918
  routes: ["POST /v1/calls/batch"],
7631
7919
  keyScopes: ["calls:write"],
7632
7920
  input: {
7633
- agent_id: import_zod2.z.string().uuid().describe("The agent that will handle the calls"),
7634
- to: import_zod2.z.array(import_zod2.z.string()).optional().describe("Simple list of destination phone numbers"),
7635
- recipients: import_zod2.z.array(import_zod2.z.object({
7636
- to: import_zod2.z.string().optional(),
7637
- from_number: import_zod2.z.string().optional(),
7638
- metadata: import_zod2.z.record(import_zod2.z.any()).optional()
7921
+ agent_id: import_zod22.z.string().uuid().describe("The agent that will handle the calls"),
7922
+ to: import_zod22.z.array(import_zod22.z.string()).optional().describe("Simple list of destination phone numbers"),
7923
+ recipients: import_zod22.z.array(import_zod22.z.object({
7924
+ to: import_zod22.z.string().optional(),
7925
+ from_number: import_zod22.z.string().optional(),
7926
+ metadata: import_zod22.z.record(import_zod22.z.any()).optional()
7639
7927
  })).optional().describe("Per-recipient targets with optional from_number/metadata"),
7640
- from_number: import_zod2.z.string().optional().describe("Default caller ID / from number"),
7641
- batch_id: import_zod2.z.string().optional(),
7642
- metadata: import_zod2.z.record(import_zod2.z.any()).optional(),
7643
- variables: import_zod2.z.record(import_zod2.z.any()).optional(),
7644
- post_call: import_zod2.z.record(import_zod2.z.any()).optional()
7928
+ from_number: import_zod22.z.string().optional().describe("Default caller ID / from number"),
7929
+ batch_id: import_zod22.z.string().optional(),
7930
+ metadata: import_zod22.z.record(import_zod22.z.any()).optional(),
7931
+ variables: import_zod22.z.record(import_zod22.z.any()).optional(),
7932
+ post_call: import_zod22.z.record(import_zod22.z.any()).optional()
7645
7933
  },
7646
7934
  run: (client, params) => client.calls.batch(params)
7647
7935
  }),
@@ -7652,7 +7940,7 @@ var require_dist = __commonJS({
7652
7940
  risk: "sensitive",
7653
7941
  routes: ["POST /v1/calls/:id/end"],
7654
7942
  keyScopes: ["calls:write"],
7655
- input: { call_id: import_zod2.z.string().describe("The call ID to end") },
7943
+ input: { call_id: import_zod22.z.string().describe("The call ID to end") },
7656
7944
  run: (client, { call_id }) => client.calls.end(call_id)
7657
7945
  }),
7658
7946
  defineTool({
@@ -7663,12 +7951,12 @@ var require_dist = __commonJS({
7663
7951
  routes: ["POST /v1/calls/:id/participants"],
7664
7952
  keyScopes: ["calls:write"],
7665
7953
  input: {
7666
- call_id: import_zod2.z.string().describe("The in-progress call ID"),
7667
- participants: import_zod2.z.array(import_zod2.z.object({
7668
- transport: import_zod2.z.enum(["webrtc", "pstn"]),
7669
- identity: import_zod2.z.string().describe("Destination phone number (pstn, E.164) or participant identity (webrtc)"),
7670
- from_number: import_zod2.z.string().optional().describe("Caller ID for pstn"),
7671
- metadata: import_zod2.z.record(import_zod2.z.any()).optional()
7954
+ call_id: import_zod22.z.string().describe("The in-progress call ID"),
7955
+ participants: import_zod22.z.array(import_zod22.z.object({
7956
+ transport: import_zod22.z.enum(["webrtc", "pstn"]),
7957
+ identity: import_zod22.z.string().describe("Destination phone number (pstn, E.164) or participant identity (webrtc)"),
7958
+ from_number: import_zod22.z.string().optional().describe("Caller ID for pstn"),
7959
+ metadata: import_zod22.z.record(import_zod22.z.any()).optional()
7672
7960
  })).min(1)
7673
7961
  },
7674
7962
  run: (client, { call_id, participants }) => client.calls.addParticipants(call_id, { participants })
@@ -8993,8 +9281,20 @@ var require_dist = __commonJS({
8993
9281
  "end_call",
8994
9282
  "undo_agent_change",
8995
9283
  "regenerate_share_link",
8996
- "rotate_campaign_intake"
9284
+ "rotate_campaign_intake",
9285
+ // Overwrite or drop stored config through a PUT, so no DELETE route gives them away.
9286
+ "remove_agent_tool",
9287
+ "set_agent_tools",
9288
+ "set_tool_secret"
8997
9289
  ]);
9290
+ var IDEMPOTENT_NAMES = /* @__PURE__ */ new Set(["end_call", "pause_campaign"]);
9291
+ var NOT_IDEMPOTENT_NAMES = /* @__PURE__ */ new Set(["add_agent_tool"]);
9292
+ function isIdempotent(tool) {
9293
+ if (IDEMPOTENT_NAMES.has(tool.name)) return true;
9294
+ if (NOT_IDEMPOTENT_NAMES.has(tool.name)) return false;
9295
+ const writes = tool.routes.filter((r) => !r.startsWith("GET "));
9296
+ return writes.length > 0 && writes.every((r) => /^(PUT|PATCH|DELETE) /.test(r));
9297
+ }
8998
9298
  var ACRONYMS = { dnc: "DNC", llm: "LLM" };
8999
9299
  function titleOf(name) {
9000
9300
  const words = name.split("_").map((w) => ACRONYMS[w] ?? w);
@@ -9006,6 +9306,7 @@ var require_dist = __commonJS({
9006
9306
  title: titleOf(tool.name),
9007
9307
  readOnlyHint: readOnly,
9008
9308
  destructiveHint: !readOnly && (DESTRUCTIVE_NAMES.has(tool.name) || tool.routes.some((r) => r.startsWith("DELETE "))),
9309
+ idempotentHint: readOnly || isIdempotent(tool),
9009
9310
  openWorldHint: !readOnly
9010
9311
  };
9011
9312
  }
@@ -9039,7 +9340,7 @@ function withToolErrors(handler) {
9039
9340
  var package_default = {
9040
9341
  name: "@rymi/mcp",
9041
9342
  mcpName: "io.github.rymi-live/rymi-mcp",
9042
- version: "2.9.0",
9343
+ version: "2.10.0",
9043
9344
  description: "Rymi MCP server \u2014 manage AI voice agents via Model Context Protocol",
9044
9345
  license: "MIT",
9045
9346
  author: "Rymi AI <engineering@rymi.live>",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rymi/mcp",
3
3
  "mcpName": "io.github.rymi-live/rymi-mcp",
4
- "version": "2.9.0",
4
+ "version": "2.10.0",
5
5
  "description": "Rymi MCP server — manage AI voice agents via Model Context Protocol",
6
6
  "license": "MIT",
7
7
  "author": "Rymi AI <engineering@rymi.live>",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@modelcontextprotocol/sdk": "^1.11.1",
48
- "@rymi/node": "^2.7.2",
48
+ "@rymi/node": "^2.8.0",
49
49
  "zod": "^3.23.0"
50
50
  },
51
51
  "devDependencies": {