@velaro/mcp-server 0.6.72 → 0.6.74

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 +133 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velaro/mcp-server",
3
- "version": "0.6.72",
3
+ "version": "0.6.74",
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
@@ -179,6 +179,12 @@ function teamRoutingConfigPath(teamId) {
179
179
  return `/RoutingConfig?teamId=${n}`;
180
180
  }
181
181
  const messagingPatch = (path, body) => messagingRequest('PATCH', path, body);
182
+ function execInsightsRange(days) {
183
+ const d = Math.min(Math.max(Number(days) || 7, 1), 92);
184
+ const to = new Date();
185
+ return new URLSearchParams({ from: new Date(to.getTime() - d * 86400000).toISOString(), to: to.toISOString() });
186
+ }
187
+
182
188
  const messagingDel = (path) => messagingRequest('DELETE', path);
183
189
 
184
190
  async function api(method, path, body, base = API_BASE) {
@@ -2103,6 +2109,33 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
2103
2109
  required: ['reportType', 'isEnabled'],
2104
2110
  },
2105
2111
  },
2112
+ {
2113
+ name: 'report_subscriptions_list',
2114
+ description: "List the caller's own scheduled report email subscriptions (velaro-messaging /report-subscriptions): id, report type, frequency, active, delivery hour, format (pdf/html), extra recipients.",
2115
+ inputSchema: { type: 'object', properties: {} },
2116
+ },
2117
+ {
2118
+ name: 'report_subscription_update',
2119
+ description: "Update one of the caller's report subscriptions by id. Only passed fields change. Extra recipients must be other users on the same account (max 10); pass an empty string to clear.",
2120
+ inputSchema: {
2121
+ type: 'object',
2122
+ properties: {
2123
+ id: { type: 'number', description: 'Subscription id from report_subscriptions_list' },
2124
+ frequency: { type: 'string', enum: ['daily', 'weekly', 'bi-monthly', 'monthly'] },
2125
+ deliveryHour: { type: 'number', description: 'Site-local hour 0-23 to deliver at or after' },
2126
+ clearDeliveryHour: { type: 'boolean', description: 'Remove the delivery hour (send at the first run)' },
2127
+ format: { type: 'string', enum: ['pdf', 'html'] },
2128
+ extraRecipients: { type: 'string', description: 'Comma-separated emails; empty string clears' },
2129
+ isActive: { type: 'boolean', description: 'false pauses, true resumes' },
2130
+ },
2131
+ required: ['id'],
2132
+ },
2133
+ },
2134
+ {
2135
+ name: 'report_subscription_delete',
2136
+ description: "Delete one of the caller's report subscriptions by id (not by report type).",
2137
+ inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Subscription id' } }, required: ['id'] },
2138
+ },
2106
2139
  {
2107
2140
  name: 'report_export_status',
2108
2141
  description: 'Check the status of a queued async report export (Reports/ExportStatus/{id}) — pending/processing/completed/failed and the download URL once ready.',
@@ -2774,6 +2807,46 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
2774
2807
  properties: { report: { type: 'string', description: 'Report key. Omit to list all.' } },
2775
2808
  },
2776
2809
  },
2810
+ {
2811
+ name: 'friction_radar',
2812
+ description: 'Friction Radar: conversation topics ranked by estimated agent cost (handle time plus a fixed penalty per escalation and abandoned chat), with volume, week-over-week growth versus the prior period of equal length and up to 3 example conversation ids per topic. Demo data excluded. Chat Intelligence entitlement required. Scoped to the caller site.',
2813
+ inputSchema: {
2814
+ type: 'object',
2815
+ properties: {
2816
+ days: { type: 'number', description: 'Look-back window in days, 1-92 (default 7).' },
2817
+ limit: { type: 'number', description: 'Max topics, 1-100 (default 20).' },
2818
+ },
2819
+ },
2820
+ },
2821
+ {
2822
+ name: 'executive_brief',
2823
+ description: 'Executive brief for the last N days: conversation volume and growth, sentiment split, average QA rubric score, Experience Impact (index, CSAT, service level, missed chats), fastest rising keywords and top friction topics. Same data as the Monday executive-brief email. Chat Intelligence entitlement required.',
2824
+ inputSchema: {
2825
+ type: 'object',
2826
+ properties: { days: { type: 'number', description: 'Look-back window in days, 1-92 (default 7).' } },
2827
+ },
2828
+ },
2829
+ {
2830
+ name: 'report_data_export',
2831
+ description: 'Export conversations as CSV (sentiment, QA score, tag, escalations, handle seconds, abandoned, keywords) for the last N days; max 20,000 rows. To schedule it by email use executive_insights_subscribe with reportType data-export. Chat Intelligence entitlement required.',
2832
+ inputSchema: {
2833
+ type: 'object',
2834
+ properties: { days: { type: 'number', description: 'Look-back window in days, 1-92 (default 7).' } },
2835
+ },
2836
+ },
2837
+ {
2838
+ name: 'executive_insights_subscribe',
2839
+ description: 'Email the caller the executive-brief (weekly sends on Monday at deliveryHour site time, default 7 = 7:00 AM) or a scheduled data-export CSV. Creates or updates the caller report subscription.',
2840
+ inputSchema: {
2841
+ type: 'object',
2842
+ properties: {
2843
+ reportType: { type: 'string', enum: ['executive-brief', 'data-export'] },
2844
+ frequency: { type: 'string', enum: ['daily', 'biweekly', 'weekly', 'monthly'], description: 'Default weekly.' },
2845
+ deliveryHour: { type: 'number', description: 'Site-local hour 0-23 (default 7).' },
2846
+ },
2847
+ required: ['reportType'],
2848
+ },
2849
+ },
2777
2850
  {
2778
2851
  name: 'experience_impact_report',
2779
2852
  description: 'Experience Impact report: shows how staffing and schedule adherence affect customer experience. Returns a 0-100 experience index (adherence 25, service level 30, missed-chat rate 20, CSAT 25), per-interval staffing vs chat demand (scheduled vs actual agents, missed chats, wait time, CSAT), per-agent rows, a bot-vs-human breakdown (bot only, bot-to-human handoff, human only, with wait, missed, CSAT and bot containment rate), and plain-language insights. Scoped to the caller site.',
@@ -6901,6 +6974,33 @@ async function handleTool(name, args) {
6901
6974
  : `Failed to save schedule for "${args.reportType}".`;
6902
6975
  }
6903
6976
 
6977
+ case 'report_subscriptions_list': {
6978
+ const rows = await messagingGet('/report-subscriptions');
6979
+ if (!Array.isArray(rows) || !rows.length) return 'No report subscriptions.';
6980
+ return rows.map(x =>
6981
+ `#${x.id} ${x.reportType}: ${x.isActive ? 'active' : 'paused'}, ${x.frequency}, ${x.deliveryHour == null ? 'any hour' : String(x.deliveryHour).padStart(2, '0') + ':00'}, ${x.format ?? 'pdf'}${x.extraRecipients ? ' | also to ' + x.extraRecipients : ''}`
6982
+ ).join('\n');
6983
+ }
6984
+
6985
+ case 'report_subscription_update': {
6986
+ if (!Number.isInteger(args.id)) return 'Error: id is required.';
6987
+ const body = {};
6988
+ for (const k of ['frequency', 'format', 'extraRecipients', 'isActive', 'clearDeliveryHour']) if (args[k] !== undefined) body[k] = args[k];
6989
+ if (args.deliveryHour !== undefined) {
6990
+ if (!Number.isInteger(args.deliveryHour) || args.deliveryHour < 0 || args.deliveryHour > 23) return 'Error: deliveryHour must be an integer 0-23.';
6991
+ body.deliveryHour = args.deliveryHour;
6992
+ }
6993
+ if (!Object.keys(body).length) return 'Error: pass at least one field to update.';
6994
+ await messagingRequest('PUT', `/report-subscriptions/${args.id}`, body);
6995
+ return `Subscription #${args.id} updated.`;
6996
+ }
6997
+
6998
+ case 'report_subscription_delete': {
6999
+ if (!Number.isInteger(args.id)) return 'Error: id is required.';
7000
+ await messagingRequest('DELETE', `/report-subscriptions/${args.id}`);
7001
+ return `Subscription #${args.id} deleted.`;
7002
+ }
7003
+
6904
7004
  case 'report_export_status': {
6905
7005
  if (!args.exportId) return 'Error: exportId is required.';
6906
7006
  const s = await api('GET', `/Reports/ExportStatus/${args.exportId}`);
@@ -7843,6 +7943,39 @@ async function handleTool(name, args) {
7843
7943
  return [`=== ${r.name} ===`, `Answers: ${r.answers}`, `Sources: ${r.sources}`, `Setup: ${r.prerequisites}`, `How to read: ${r.interpret}`, `Why zero/empty: ${r.whyZero}`, `What to do: ${r.fix}`, `Related: ${r.related}`].join('\n');
7844
7944
  }
7845
7945
 
7946
+ case 'friction_radar': {
7947
+ const qs = execInsightsRange(args.days);
7948
+ qs.set('limit', String(Math.min(Math.max(Number(args.limit) || 20, 1), 100)));
7949
+ const r = await messagingGet(`/executive-insights/friction-radar?${qs}`);
7950
+ const lines = [`=== Friction Radar (${r.conversationsScanned} conversations${r.truncated ? ', truncated' : ''}) ===`];
7951
+ for (const t of r.topics ?? []) {
7952
+ const growth = t.isNew ? 'new' : t.growthPct == null ? 'n/a' : `${t.growthPct}%`;
7953
+ lines.push(`#${t.costRank} ${t.topic}: ${t.volume} chats (volume rank ${t.volumeRank}, ${growth}), ${t.escalations} escalated, ${t.abandoned} abandoned, ~${Math.round(t.estimatedCostMinutes)} agent min, examples ${(t.exampleConversationIds ?? []).join(', ') || '-'}`);
7954
+ }
7955
+ if (!(r.topics ?? []).length) lines.push('No tagged topics in this period.');
7956
+ return lines.join('\n');
7957
+ }
7958
+
7959
+ case 'executive_brief': {
7960
+ const r = await messagingGet(`/executive-insights/brief?${execInsightsRange(args.days)}`);
7961
+ return JSON.stringify(r.brief, null, 2);
7962
+ }
7963
+
7964
+ case 'report_data_export': {
7965
+ const r = await messagingGet(`/executive-insights/export?${execInsightsRange(args.days)}`);
7966
+ return `${r.rows} rows${r.truncated ? ' (truncated at the row cap)' : ''}\n${r.csv}`;
7967
+ }
7968
+
7969
+ case 'executive_insights_subscribe': {
7970
+ if (!['executive-brief', 'data-export'].includes(args.reportType)) return 'Error: reportType must be executive-brief or data-export.';
7971
+ await messagingPost('/report-subscriptions', {
7972
+ reportType: args.reportType,
7973
+ frequency: args.frequency ?? 'weekly',
7974
+ deliveryHour: args.deliveryHour ?? 7,
7975
+ });
7976
+ return `Subscribed to ${args.reportType} (${args.frequency ?? 'weekly'}, ${args.deliveryHour ?? 7}:00 site time).`;
7977
+ }
7978
+
7846
7979
  case 'experience_impact_report': {
7847
7980
  const qs = new URLSearchParams();
7848
7981
  for (const k of ['from', 'to', 'teamId']) if (args[k] != null && args[k] !== '') qs.set(k, String(args[k]));