@velaro/mcp-server 0.6.64 → 0.6.66

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/package.json +1 -1
  2. package/server.js +68 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velaro/mcp-server",
3
- "version": "0.6.64",
3
+ "version": "0.6.66",
4
4
  "description": "Velaro MCP server — connect Claude and other AI agents directly to your Velaro account: KB, workflows, bots, conversations, contacts, routing, and more.",
5
5
  "type": "module",
6
6
  "bin": {
package/server.js CHANGED
@@ -1544,7 +1544,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
1544
1544
  preferPreviousAgentOnTimeout: { type: 'boolean', description: 'On timeout, prefer agent from visitor\'s prior session (loyalty routing)' },
1545
1545
  agentFollowUpFlagEnabled: { type: 'boolean', description: 'Flag conversations that required multiple reroutes' },
1546
1546
  queueUnavailableGracePeriodSeconds: { type: 'number', description: 'Wait before returning unavailable when agents are transiently busy (seconds)' },
1547
- terminalActionOverride: { type: 'number', description: 'Terminal action: 0=Survey 1=CreateTicket 2=SurveyAndCreateTicket 3=Queue 4=OfflineMessage 5=NoOp 6=EndChat 7=NotifyManager 8=RunWorkflow (needs inactivityTerminalWorkflowId). Null = default (Survey).' },
1547
+ terminalActionOverride: { type: 'number', description: 'Terminal action: 0=Survey 1=CreateTicket 2=SurveyAndCreateTicket 3=Queue 4=OfflineMessage 5=NoOp 6=EndChat 7=NotifyManager 8=RunWorkflow (needs inactivityTerminalWorkflowId) 9=ReturnToBot (needs advancedTerminalBotProfileId). Null = default (Survey).' },
1548
1548
  exhaustionMessageTemplate: { type: 'string', description: 'Message sent to the visitor with the final action (max 500 characters).' },
1549
1549
  reassignmentMode: { type: 'string', enum: ['Observe','QueueOnly','Reassign'], description: "Observe (default) only records what would happen; QueueOnly returns a silent agent's chat to the group queue; Reassign hands it to the next agent. Backup group and no-agent-available steps need Reassign." },
1550
1550
  inactivityChainEnabled: { type: 'boolean', description: 'Master switch for the agent inactivity fallback chain. Off by default.' },
@@ -1559,6 +1559,14 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
1559
1559
  queueEscalationAfterMinutes: { type: 'number', description: 'Widen the queue alert to backup group and managers after this many minutes (1-240).' },
1560
1560
  inactivityVisitorText: { type: 'string', description: 'Message the visitor sees when the chat is moved (max 500 characters).' },
1561
1561
  inactivityAgentText: { type: 'string', description: 'Message agents see on a re-queued chat (max 500 characters).' },
1562
+ advancedNoTypingAfterSeconds: { type: 'number', description: 'Advanced (opt-in): seconds an agent can go without typing in a waiting chat before the no-typing steps run (10-3600). Inert unless the inactivity chain is on.' },
1563
+ advancedNoTypingVisitorMessage: { type: 'string', description: 'Advanced: message sent to the visitor when the agent steps away (max 500 characters).' },
1564
+ advancedNoTypingAlertSupervisor: { type: 'boolean', description: 'Advanced: alert a supervisor when the agent stops typing.' },
1565
+ advancedNoTypingReroute: { type: 'boolean', description: 'Advanced: move the chat to another agent when the agent stops typing.' },
1566
+ advancedEscalationTeamId: { type: 'number', description: 'Advanced: group alerted when the chat stays unattended (escalation tier).' },
1567
+ advancedEscalationAfterSeconds: { type: 'number', description: 'Advanced: seconds unattended before the escalation group is alerted (30-14400). Needs advancedEscalationTeamId.' },
1568
+ advancedTerminalBotProfileId: { type: 'number', description: 'Advanced: bot (AI configuration id) the chat returns to when terminalActionOverride is 9 (ReturnToBot).' },
1569
+ advancedApplyToRoutingActions: { type: 'number', description: 'Advanced: 1 = auto-routing only, 2 = queue only. Omit to leave unchanged (default covers both).' },
1562
1570
  },
1563
1571
  required: [],
1564
1572
  },
@@ -2562,6 +2570,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
2562
2570
  from: { type: 'string', description: 'yyyy-MM-dd start (default: 7 days ago).' },
2563
2571
  to: { type: 'string', description: 'yyyy-MM-dd end (default: today).' },
2564
2572
  teamId: { type: 'number', description: 'Optional team ID to narrow the report.' },
2573
+ includeDemo: { type: 'boolean', description: 'true = show only the seeded sample (demo) data instead of real conversations. Default false. The response demoAvailable flag says whether sample data exists for the range.' },
2565
2574
  },
2566
2575
  },
2567
2576
  },
@@ -4630,7 +4639,7 @@ function parseBusinessHours(input) {
4630
4639
 
4631
4640
  // -- Agent inactivity chain helpers (velaro-messaging PR #1471, v20 ONLY) ------------------------
4632
4641
  // Terminal action ints are persisted by messaging (append-only enum). The API returns/accepts the NAME.
4633
- const TERMINAL_ACTION_NAMES = ['Survey','CreateTicket','SurveyAndCreateTicket','Queue','OfflineMessage','NoOp','EndChat','NotifyManager','RunWorkflow'];
4642
+ const TERMINAL_ACTION_NAMES = ['Survey','CreateTicket','SurveyAndCreateTicket','Queue','OfflineMessage','NoOp','EndChat','NotifyManager','RunWorkflow','ReturnToBot'];
4634
4643
  function terminalActionLabel(v) {
4635
4644
  if (v === null || v === undefined || v === '') return 'Default (Survey)';
4636
4645
  if (typeof v === 'number') return TERMINAL_ACTION_NAMES[v] ?? String(v);
@@ -4650,6 +4659,37 @@ function terminalActionForSave(v) {
4650
4659
  if (!hit) throw new Error(`Unknown terminal action "${v}". Use one of: ${valid}`);
4651
4660
  return hit;
4652
4661
  }
4662
+ // Advanced inactivity routing (stepped away / not typing). Mirrors the messaging validation; the server stays the authority.
4663
+ function validateAdvancedPatch(a) {
4664
+ const int = (v) => Number.isInteger(v);
4665
+ if (a.noTypingAfterSeconds !== undefined && !(int(a.noTypingAfterSeconds) && a.noTypingAfterSeconds >= 10 && a.noTypingAfterSeconds <= 3600))
4666
+ return 'advancedNoTypingAfterSeconds must be a whole number from 10 to 3600.';
4667
+ if (a.escalationAfterSeconds !== undefined && !(int(a.escalationAfterSeconds) && a.escalationAfterSeconds >= 30 && a.escalationAfterSeconds <= 14400))
4668
+ return 'advancedEscalationAfterSeconds must be a whole number from 30 to 14400.';
4669
+ if (typeof a.noTypingVisitorMessage === 'string' && a.noTypingVisitorMessage.length > 500)
4670
+ return 'advancedNoTypingVisitorMessage must be 500 characters or fewer.';
4671
+ if (a.applyToRoutingActions !== undefined && a.applyToRoutingActions !== 1 && a.applyToRoutingActions !== 2)
4672
+ return 'advancedApplyToRoutingActions must be 1 (auto-routing) or 2 (queue). Omit it to leave the setting unchanged.';
4673
+ return null;
4674
+ }
4675
+
4676
+ function describeAdvanced(adv) {
4677
+ if (!adv) return ['', '(This messaging build does not report the advanced stepped-away settings.)'];
4678
+ const from = (f) => (adv.inheritedFrom?.[f] ? ` [from ${adv.inheritedFrom[f]}]` : '');
4679
+ const v = (x) => (x === null || x === undefined ? 'not set' : x);
4680
+ return [
4681
+ '',
4682
+ 'Advanced (stepped away / not typing):',
4683
+ ` No typing after: ${adv.noTypingAfterSeconds != null ? `${adv.noTypingAfterSeconds}s` : 'off'}${from('noTypingAfterSeconds')}`,
4684
+ ` Visitor message: ${v(adv.noTypingVisitorMessage)}${from('noTypingVisitorMessage')}`,
4685
+ ` Alert supervisor: ${v(adv.noTypingAlertSupervisor)}${from('noTypingAlertSupervisor')}`,
4686
+ ` Move to another agent: ${v(adv.noTypingReroute)}${from('noTypingReroute')}`,
4687
+ ` Escalation group: ${v(adv.escalationTeamId)}${adv.escalationAfterSeconds != null ? ` after ${adv.escalationAfterSeconds}s` : ''}${from('escalationTeamId')}`,
4688
+ ` Return-to-bot bot: ${v(adv.terminalBotProfileId)}${from('terminalBotProfileId')}`,
4689
+ ` Applies to: ${adv.applyToRoutingActions === 1 ? 'auto-routing only' : adv.applyToRoutingActions === 2 ? 'queue only' : 'auto-routing and queue'}${from('applyToRoutingActions')}`,
4690
+ ];
4691
+ }
4692
+
4653
4693
  const CHAIN_FIELDS = [
4654
4694
  'enabled','backupTeamId','backupTeamAfterSeconds','noTargetActionAfterSeconds','rerouteOnAgentUnavailable',
4655
4695
  'alertAvailableAgentsOnRelease','notifyManagersOnInactivity','rerouteCooldownSeconds','queueEscalationAfterMinutes',
@@ -5614,6 +5654,7 @@ async function handleTool(name, args) {
5614
5654
  ` Terminal workflow: ${chain.terminalWorkflowId ?? 'none'}`,
5615
5655
  ] : []),
5616
5656
  ] : ['', '(This messaging build does not report the inactivity chain.)'];
5657
+ chainLines.push(...describeAdvanced(p.advanced));
5617
5658
  return [
5618
5659
  `Enabled: ${p.enabled}`,
5619
5660
  `Initial response timeout: ${p.initialResponseTimeoutSeconds}s`,
@@ -5678,6 +5719,29 @@ async function handleTool(name, args) {
5678
5719
  for (const f of CHAIN_FIELDS) if (chainPatch[f] !== undefined) chain[f] = chainPatch[f];
5679
5720
  merged.inactivityChain = chain;
5680
5721
  }
5722
+ // Advanced block (stepped away / not typing). null = unchanged on the server, so send only what was passed.
5723
+ const advancedPatch = {
5724
+ noTypingAfterSeconds: args.advancedNoTypingAfterSeconds,
5725
+ noTypingVisitorMessage: args.advancedNoTypingVisitorMessage,
5726
+ noTypingAlertSupervisor: args.advancedNoTypingAlertSupervisor,
5727
+ noTypingReroute: args.advancedNoTypingReroute,
5728
+ escalationTeamId: args.advancedEscalationTeamId,
5729
+ escalationAfterSeconds: args.advancedEscalationAfterSeconds,
5730
+ terminalBotProfileId: args.advancedTerminalBotProfileId,
5731
+ applyToRoutingActions: args.advancedApplyToRoutingActions,
5732
+ };
5733
+ delete merged.advanced; // never echo the read-side block (it carries inheritedFrom) back unchanged
5734
+ if (Object.values(advancedPatch).some((v) => v !== undefined)) {
5735
+ if (!('advanced' in current)) {
5736
+ throw new Error('This messaging build does not report the advanced inactivity settings yet; they cannot be saved.');
5737
+ }
5738
+ const advErr = validateAdvancedPatch(advancedPatch);
5739
+ if (advErr) throw new Error(advErr);
5740
+ merged.advanced = Object.fromEntries(Object.entries(advancedPatch).map(([k, v]) => [k, v === undefined ? null : v]));
5741
+ }
5742
+ if (merged.terminalActionOverride === 'ReturnToBot' && args.advancedTerminalBotProfileId == null && current.advanced?.terminalBotProfileId == null) {
5743
+ throw new Error('Terminal action 9 (ReturnToBot) needs advancedTerminalBotProfileId.');
5744
+ }
5681
5745
  await messagingPut('/AutoRoutingPolicy', merged);
5682
5746
  // PUT answers { updated: true }, not the policy: re-read so the reply reports what is really stored.
5683
5747
  const saved = await messagingGet('/AutoRoutingPolicy');
@@ -7245,6 +7309,7 @@ async function handleTool(name, args) {
7245
7309
  case 'experience_impact_report': {
7246
7310
  const qs = new URLSearchParams();
7247
7311
  for (const k of ['from', 'to', 'teamId']) if (args[k] != null && args[k] !== '') qs.set(k, String(args[k]));
7312
+ if (args.includeDemo === true) qs.set('includeDemo', 'true');
7248
7313
  const r = await messagingGet(`/Reports/experience-impact${qs.toString() ? '?' + qs : ''}`);
7249
7314
  const sm = r.summary ?? {};
7250
7315
  const lines = [
@@ -7252,6 +7317,7 @@ async function handleTool(name, args) {
7252
7317
  `Experience index: ${sm.experienceIndex ?? '-'} / 100`,
7253
7318
  `Adherence ${sm.adherencePct ?? '-'}% | Service level ${sm.serviceLevelPct ?? '-'}% | Missed chats ${sm.missedChats ?? '-'} | Avg wait ${sm.avgWaitSec ?? '-'}s | CSAT ${sm.csatAvg ?? '-'} (${sm.csatResponses ?? 0} responses)`,
7254
7319
  ...(r.insights ?? []).map(i => ` [${i.severity}] ${i.text}`),
7320
+ ...(r.demoAvailable ? ['Sample (demo) data is available for this range; pass includeDemo=true to view it.'] : []),
7255
7321
  `Intervals: ${(r.intervals ?? []).length} | Agents: ${(r.agents ?? []).length}`,
7256
7322
  ];
7257
7323
  if (r.botVsHuman) {