@velaro/mcp-server 0.6.90 → 0.6.91

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 +157 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velaro/mcp-server",
3
- "version": "0.6.90",
3
+ "version": "0.6.91",
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
@@ -822,6 +822,41 @@ const TOOLS = [
822
822
  },
823
823
  },
824
824
  },
825
+ {
826
+ name: 'canned_learning_suggestions',
827
+ description: 'List AI-proposed canned message improvements awaiting human review (learned from low ratings and never-used messages). Each row shows currentText, proposedText, the reason, the signals it learned from, and a review state (pending_review, approved, rejected, rolled_back). Intelligence Suite entitlement required.',
828
+ inputSchema: { type: 'object', properties: { state: { type: 'string', description: 'pending_review (default), approved, rejected, rolled_back, or all' } } },
829
+ },
830
+ {
831
+ name: 'canned_learning_generate',
832
+ description: 'Analyze this site canned message usage and ratings and queue AI-proposed improvements for human review. Nothing changes until approved. Capped at 5 messages per run and one run per day; text is PII-redacted before it reaches the model.',
833
+ inputSchema: { type: 'object', properties: {} },
834
+ },
835
+ {
836
+ name: 'canned_learning_approve',
837
+ description: 'Approve a suggestion: replaces the live canned message with the proposed text (or editedText if given). The original is snapshotted so canned_learning_rollback can restore it.',
838
+ inputSchema: { type: 'object', properties: { suggestionId: { type: 'number' }, editedText: { type: 'string', description: 'Optional human-edited replacement' } }, required: ['suggestionId'] },
839
+ },
840
+ {
841
+ name: 'canned_learning_reject',
842
+ description: 'Reject a pending canned message suggestion. The live message is untouched.',
843
+ inputSchema: { type: 'object', properties: { suggestionId: { type: 'number' } }, required: ['suggestionId'] },
844
+ },
845
+ {
846
+ name: 'canned_learning_edit_summary',
847
+ description: 'Which canned messages agents edit most before sending (last 90 days): canned response id, shortcut, edit count and average similarity of the sent text to the original. This is the edited-before-send evidence the weekly canned message learning job uses. Intelligence Suite entitlement required.',
848
+ inputSchema: { type: 'object', properties: {} },
849
+ },
850
+ {
851
+ name: 'canned_learning_record_edit',
852
+ description: 'Record that an agent edited a canned response before sending. The sent text is PII-redacted and length-capped server-side; the site comes from the session. Rows are capped per site and expire after 90 days.',
853
+ inputSchema: { type: 'object', properties: { cannedResponseId: { type: 'number' }, finalText: { type: 'string', description: 'The text the agent actually sent' } }, required: ['cannedResponseId', 'finalText'] },
854
+ },
855
+ {
856
+ name: 'canned_learning_rollback',
857
+ description: 'Roll back an approved suggestion, restoring the original canned message text.',
858
+ inputSchema: { type: 'object', properties: { suggestionId: { type: 'number' }, notes: { type: 'string' } }, required: ['suggestionId'] },
859
+ },
825
860
  {
826
861
  name: 'chat_intelligence_recommendations',
827
862
  description: 'List the active (non-dismissed) AI-generated improvement recommendations for this site — widget availability, bot gaps, missed-chat patterns, content-gap drafts. Each row includes id, type, priority, title, body, supportingDataBlob, plus lifecycle fields (appliedAt, appliedArtifactRef, rolledBackAt).',
@@ -2976,6 +3011,26 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
2976
3011
  },
2977
3012
  },
2978
3013
  },
3014
+ {
3015
+ name: 'enterprise_reports',
3016
+ description: 'Enterprise Reports package (enterprise-internal, default OFF; every report returns 403 when the site does not hold that package item). report: program_breakdown (compare websites, teams, platforms, devices or entry pages: volume, missed, transfers, wait, first reply, QA score and coverage, sentiment), agent_scorecard (per agent by name: resolution, transfers, handle time, AI score, supervisor-reviewed score, sentiment impact), qa_coverage (share of resolved chats AI-scored and supervisor-reviewed by week and program), coaching_queue (low-scoring chats nobody reviewed, per agent), sentiment_breakdown (by website, team, agent or topic with change vs prior period), topic_radar (topics by volume, growth, transfer rate), weekly_brief (preview of the Monday brief sections), weekly_brief_send_test (emails this week\'s brief to the signed-in user). Scoped to the caller site.',
3017
+ inputSchema: {
3018
+ type: 'object',
3019
+ properties: {
3020
+ report: { type: 'string', enum: ['program_breakdown', 'agent_scorecard', 'qa_coverage', 'coaching_queue', 'sentiment_breakdown', 'topic_radar', 'weekly_brief', 'weekly_brief_send_test'] },
3021
+ from: { type: 'string', description: 'yyyy-MM-dd start (default: 30 days ago).' },
3022
+ to: { type: 'string', description: 'yyyy-MM-dd end, inclusive (default: today).' },
3023
+ groupBy: { type: 'string', description: 'program_breakdown: website|team|platform|device|entrypage. sentiment_breakdown: website|team|agent|theme.' },
3024
+ deploymentId: { type: 'number', description: 'Optional website (deployment) ID.' },
3025
+ teamId: { type: 'number', description: 'Optional team ID.' },
3026
+ threshold: { type: 'number', description: 'coaching_queue: queue chats scored below this (1-100; default = the rubric low-score threshold or 60).' },
3027
+ perAgent: { type: 'number', description: 'coaching_queue: chats listed per agent (default 5).' },
3028
+ limit: { type: 'number', description: 'topic_radar: max topics (default 25).' },
3029
+ days: { type: 'number', description: 'weekly_brief: days to cover (default 7).' },
3030
+ },
3031
+ required: ['report'],
3032
+ },
3033
+ },
2979
3034
  {
2980
3035
  name: 'report_backfill',
2981
3036
  description: 'Backfill report data for the caller site. Preview by default (reports "would fill N", writes nothing); apply=true writes. Idempotent. type "csat" fills missing survey ratings (SurveySubmission.Rating) from existing data; it does NOT touch ticket CSAT. Site managers only; scoped to the caller site.',
@@ -3156,6 +3211,9 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
3156
3211
  voiceId: { type: 'string', description: 'ElevenLabs voice ID override. Omit to use the site default.' },
3157
3212
  showCallMeOption: { type: 'boolean', description: 'Show the "Call me instead" option (requires EnableClickToCall).' },
3158
3213
  callbackConfirmWindowMinutes: { type: 'number', description: 'Minutes to confirm a callback request before it expires. Default 10.' },
3214
+ displayName: { type: 'string', description: 'Visitor-facing avatar name shown on the badge (e.g. "Moshky"). Empty string resets to "AI".' },
3215
+ showBadge: { type: 'boolean', description: 'Show the name badge over the avatar video. Default true.' },
3216
+ sizePx: { type: 'number', description: 'Max avatar panel width in px (160-640, square). Default 360.' },
3159
3217
  maxAvatarMinutesPerMonth: { type: 'number', description: 'Monthly avatar video minutes cap for the site. Whole number 1-100000; never unlimited (0/negative rejected).' },
3160
3218
  maxAvatarTtsChars: { type: 'number', description: 'Monthly avatar TTS character cap (avatar_tts_chars) for the site. Whole number 1-100000000; never unlimited (0/negative rejected). Overage pricing is a platform-wide ladder, not per site.' },
3161
3219
  },
@@ -3371,7 +3429,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
3371
3429
  },
3372
3430
  {
3373
3431
  name: 'email_inbox_get_status',
3374
- description: 'Check global Gmail OAuth/Domain-Wide-Delegation/Pub-Sub configuration presence (gmail only — these are global App Settings, not per-site). For per-mailbox connection state (either provider) use email_inbox_list_mailboxes instead. Velaro staff only.',
3432
+ description: 'Check global Gmail OAuth/Domain-Wide-Delegation/Pub-Sub configuration presence AND prove the OAuth client id/secret are a matching pair (credentialPair) plus duplicate App Setting spellings (Gmail:X vs Gmail__X) (gmail only — these are global App Settings, not per-site). For per-mailbox connection state (either provider) use email_inbox_list_mailboxes instead. Velaro staff only.',
3375
3433
  inputSchema: {
3376
3434
  type: 'object',
3377
3435
  properties: {
@@ -3380,6 +3438,33 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
3380
3438
  required: ['siteId'],
3381
3439
  },
3382
3440
  },
3441
+ {
3442
+ name: 'email_inbox_resubscribe_outlook',
3443
+ description: 'Recreate the Microsoft Graph inbound-mail subscription for a connected Outlook mailbox. A mailbox with graphSubscriptionId null (see email_inbox_list_mailboxes) shows "connected" but never receives email - this repairs it immediately. Velaro staff only.',
3444
+ inputSchema: {
3445
+ type: 'object',
3446
+ properties: {
3447
+ siteId: { type: 'number', description: 'Site ID' },
3448
+ mailboxId: { type: 'number', description: 'Outlook mailbox integration ID from email_inbox_list_mailboxes' },
3449
+ },
3450
+ required: ['siteId', 'mailboxId'],
3451
+ },
3452
+ },
3453
+ {
3454
+ name: 'site_integration_logs',
3455
+ description: "Read ANY site's integration Activity Log (same rows as the admin Activity Log tab), optionally filtered by integration slug (e.g. OutlookInbox, GmailInbox, NetSuite), severity and message search. Use this first when diagnosing a customer/test-site integration. Velaro staff only.",
3456
+ inputSchema: {
3457
+ type: 'object',
3458
+ properties: {
3459
+ siteId: { type: 'number', description: 'Site ID' },
3460
+ slug: { type: 'string', description: 'Integration slug, e.g. OutlookInbox' },
3461
+ severity: { type: 'string', enum: ['Info', 'Warning', 'Error'], description: 'Optional severity filter' },
3462
+ search: { type: 'string', description: 'Optional substring match on message / additional info' },
3463
+ take: { type: 'number', description: 'Max rows (default 50, max 200)' },
3464
+ },
3465
+ required: ['siteId'],
3466
+ },
3467
+ },
3383
3468
  {
3384
3469
  name: 'email_inbox_set_team',
3385
3470
  description: 'Reassign the team a connected Gmail/Outlook mailbox routes inbound mail to, or clear it back to the site default/catch-all (teamId: 0). Velaro staff only.',
@@ -4396,6 +4481,11 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
4396
4481
  description: 'List all registered EntitlementFeature keys with their display names and categories. Useful for finding the right featureKey before setting overrides. Superadmin only.',
4397
4482
  inputSchema: { type: 'object', properties: {} },
4398
4483
  },
4484
+ {
4485
+ name: 'entitlement_enterprise_reports_package',
4486
+ description: 'Show the Enterprise Reports package (enterprise-internal, default OFF): every item with its feature key, what it builds on, and, when siteId is given, whether it is ON for that site. Superadmin only.',
4487
+ inputSchema: { type: 'object', properties: { siteId: { type: 'number', description: 'Optional site ID to show which items are on' } } },
4488
+ },
4399
4489
  {
4400
4490
  name: 'entitlement_set_override',
4401
4491
  description: 'Set a per-site feature override — enable or disable a specific feature for one site, independent of their plan. Superadmin only. Use entitlement_list_features to find valid featureKey values.',
@@ -5725,6 +5815,20 @@ async function handleTool(name, args) {
5725
5815
  if (args.teamId != null) qs.set('teamId', String(args.teamId));
5726
5816
  return await messagingGet(`/Reports/chat-intelligence/themes?${qs.toString()}`);
5727
5817
  }
5818
+ case 'canned_learning_suggestions':
5819
+ return await messagingGet(`/CannedLearning/suggestions?state=${encodeURIComponent(args.state ?? 'pending_review')}`);
5820
+ case 'canned_learning_generate':
5821
+ return await messagingPost('/CannedLearning/generate', {});
5822
+ case 'canned_learning_approve':
5823
+ return await messagingPost(`/CannedLearning/suggestions/${args.suggestionId}/approve`, { editedText: args.editedText ?? null });
5824
+ case 'canned_learning_reject':
5825
+ return await messagingPost(`/CannedLearning/suggestions/${args.suggestionId}/reject`, {});
5826
+ case 'canned_learning_edit_summary':
5827
+ return await messagingGet('/CannedLearning/edit-events');
5828
+ case 'canned_learning_record_edit':
5829
+ return await messagingPost('/CannedLearning/edit-events', { cannedResponseId: args.cannedResponseId, finalText: args.finalText });
5830
+ case 'canned_learning_rollback':
5831
+ return await messagingPost(`/CannedLearning/suggestions/${args.suggestionId}/rollback`, { notes: args.notes ?? null });
5728
5832
  case 'chat_intelligence_recommendations':
5729
5833
  return await messagingGet(`/Reports/chat-intelligence/recommendations`);
5730
5834
  case 'chat_intelligence_draft_article':
@@ -8266,6 +8370,23 @@ async function handleTool(name, args) {
8266
8370
  return `Subscribed to ${args.reportType} (${args.frequency ?? 'weekly'}, ${args.deliveryHour ?? 7}:00 site time).`;
8267
8371
  }
8268
8372
 
8373
+ case 'enterprise_reports': {
8374
+ const ER_PATHS = {
8375
+ program_breakdown: 'program-breakdown', agent_scorecard: 'agent-scorecard', qa_coverage: 'qa-coverage',
8376
+ coaching_queue: 'coaching-queue', sentiment_breakdown: 'sentiment-breakdown', topic_radar: 'topic-radar',
8377
+ weekly_brief: 'weekly-brief', weekly_brief_send_test: 'weekly-brief/send-test',
8378
+ };
8379
+ const seg = ER_PATHS[args.report];
8380
+ if (!seg) throw new Error('report must be one of: ' + Object.keys(ER_PATHS).join(', '));
8381
+ if (args.report === 'weekly_brief_send_test') return JSON.stringify(await messagingPost('/api/enterprise-reports/' + seg, {}), null, 2);
8382
+ const qs = new URLSearchParams();
8383
+ for (const k of ['from', 'to', 'groupBy', 'deploymentId', 'teamId', 'threshold', 'perAgent', 'limit', 'days']) {
8384
+ if (args[k] != null && args[k] !== '') qs.set(k, String(args[k]));
8385
+ }
8386
+ const q = qs.toString();
8387
+ return JSON.stringify(await messagingGet('/api/enterprise-reports/' + seg + (q ? '?' + q : '')), null, 2);
8388
+ }
8389
+
8269
8390
  case 'experience_impact_report': {
8270
8391
  const qs = new URLSearchParams();
8271
8392
  for (const k of ['from', 'to', 'teamId']) if (args[k] != null && args[k] !== '') qs.set(k, String(args[k]));
@@ -8394,6 +8515,9 @@ async function handleTool(name, args) {
8394
8515
  ` Enabled: ${data.enabled}`,
8395
8516
  ` "Call me": ${data.showCallMeOption}`,
8396
8517
  ` Callback window: ${data.callbackConfirmWindowMinutes} min`,
8518
+ ` Display name: ${data.displayName || '(AI)'}`,
8519
+ ` Badge shown: ${data.showBadge}`,
8520
+ ` Size: ${data.sizePx}px`,
8397
8521
  ].join('\n');
8398
8522
  }
8399
8523
 
@@ -8405,6 +8529,9 @@ async function handleTool(name, args) {
8405
8529
  if (args.enabled !== undefined) body.enabled = args.enabled;
8406
8530
  if (args.showCallMeOption !== undefined) body.showCallMeOption = args.showCallMeOption;
8407
8531
  if (args.callbackConfirmWindowMinutes !== undefined) body.callbackConfirmWindowMinutes = args.callbackConfirmWindowMinutes;
8532
+ if (args.displayName !== undefined) body.displayName = args.displayName;
8533
+ if (args.showBadge !== undefined) body.showBadge = args.showBadge;
8534
+ if (args.sizePx !== undefined) body.sizePx = args.sizePx;
8408
8535
  for (const [arg, max] of [['maxAvatarMinutesPerMonth', 100000], ['maxAvatarTtsChars', 100000000]]) {
8409
8536
  if (args[arg] === undefined) continue;
8410
8537
  if (!Number.isInteger(args[arg]) || args[arg] < 1 || args[arg] > max) throw new Error(`${arg} must be a whole number between 1 and ${max} (never unlimited).`);
@@ -8524,6 +8651,23 @@ async function handleTool(name, args) {
8524
8651
  return JSON.stringify(data, null, 2);
8525
8652
  }
8526
8653
 
8654
+ case 'email_inbox_resubscribe_outlook': {
8655
+ if (!args.siteId || !args.mailboxId) return 'siteId and mailboxId are required.';
8656
+ const data = await messagingPost(`/SupportTools/sites/${args.siteId}/email-inbox/outlook/${args.mailboxId}/resubscribe`, {});
8657
+ return JSON.stringify(data, null, 2);
8658
+ }
8659
+
8660
+ case 'site_integration_logs': {
8661
+ if (!args.siteId) return 'siteId is required.';
8662
+ const q = new URLSearchParams();
8663
+ if (args.slug) q.set('slug', args.slug);
8664
+ if (args.severity) q.set('severity', args.severity);
8665
+ if (args.search) q.set('search', args.search);
8666
+ if (args.take) q.set('take', String(args.take));
8667
+ const data = await messagingGet(`/SupportTools/sites/${args.siteId}/integration-logs?${q.toString()}`);
8668
+ return JSON.stringify(data, null, 2);
8669
+ }
8670
+
8527
8671
  case 'email_inbox_set_team': {
8528
8672
  if (!args.siteId || !args.provider || !args.mailboxId || args.teamId == null) return 'siteId, provider, mailboxId, and teamId are required.';
8529
8673
  const data = await messagingPatch(`/SupportTools/sites/${args.siteId}/email-inbox/${args.provider}/${args.mailboxId}/team`, { teamId: args.teamId });
@@ -9678,6 +9822,18 @@ async function handleTool(name, args) {
9678
9822
  return lines.join('\n');
9679
9823
  }
9680
9824
 
9825
+ case 'entitlement_enterprise_reports_package': {
9826
+ const q = args && args.siteId ? `?siteId=${encodeURIComponent(args.siteId)}` : '';
9827
+ const pkg = await api('GET', `/Entitlements/packages/enterprise-reports${q}`);
9828
+ const lines = [`${pkg.displayName} (${pkg.packageKey}) - plan ${pkg.plan}, default OFF${pkg.siteId ? `, site ${pkg.siteId}` : ''}`, ''];
9829
+ for (const it of pkg.items) {
9830
+ lines.push(`${pkg.siteId ? (it.enabled ? '[ON ] ' : '[off] ') : ''}${it.ask}. ${it.title}`);
9831
+ lines.push(` key: ${it.featureKey}; builds on: ${it.buildsOn.join(', ')}`);
9832
+ lines.push(` ${it.summary}`);
9833
+ }
9834
+ return lines.join(String.fromCharCode(10));
9835
+ }
9836
+
9681
9837
  case 'entitlement_list_features': {
9682
9838
  const features = await api('GET', '/Entitlements/features');
9683
9839
  const byCategory = {};