@velaro/mcp-server 0.6.90 → 0.6.92
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/package.json +1 -1
- package/server.js +338 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velaro/mcp-server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.92",
|
|
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,81 @@ 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
|
+
},
|
|
860
|
+
{
|
|
861
|
+
name: 'chat_topics_get',
|
|
862
|
+
description: 'Chat Intelligence topics for this site: the active topic list (hint for the AI, not a rulebook), industry templates, groups, merge/rename mapping and limits. Intelligence Suite entitlement required.',
|
|
863
|
+
inputSchema: { type: 'object', properties: {} },
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
name: 'chat_topics_set',
|
|
867
|
+
description: 'Replace the active topic list (max 60 topics, 60 chars each, duplicates removed). Used for new classifications only; existing history is untouched. Optionally load a template first with industry.',
|
|
868
|
+
inputSchema: { type: 'object', properties: { topics: { type: 'array', items: { type: 'string' } }, industry: { type: 'string', description: 'Load a template instead: insurance, healthcare, retail, ecommerce, financial, saas, government, manufacturing' } } },
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
name: 'chat_topics_edit',
|
|
872
|
+
description: 'Merge, rename, retire or group topics. Applied when reports are read, so history follows the new names with zero AI cost. op = merge|rename|retire|group; from = topic names; to = target (merge/rename); group = group name (empty clears).',
|
|
873
|
+
inputSchema: { type: 'object', properties: { op: { type: 'string' }, from: { type: 'array', items: { type: 'string' } }, to: { type: 'string' }, group: { type: 'string' } }, required: ['op', 'from'] },
|
|
874
|
+
},
|
|
875
|
+
{
|
|
876
|
+
name: 'chat_topics_group_suggestions',
|
|
877
|
+
description: 'Suggested groups for the current topics, computed from topic names and counts with no AI call.',
|
|
878
|
+
inputSchema: { type: 'object', properties: { days: { type: 'number' } } },
|
|
879
|
+
},
|
|
880
|
+
{
|
|
881
|
+
name: 'chat_topics_suggestions',
|
|
882
|
+
description: 'Suggested topics: the short topic the AI proposed for chats that fit nothing on the list, with counts and example conversation ids.',
|
|
883
|
+
inputSchema: { type: 'object', properties: { days: { type: 'number' } } },
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
name: 'chat_topics_resolve_suggestion',
|
|
887
|
+
description: 'Accept (adds to the list), merge or rename into an existing topic (target), or dismiss a suggested topic. History follows with no AI cost.',
|
|
888
|
+
inputSchema: { type: 'object', properties: { suggestion: { type: 'string' }, action: { type: 'string', description: 'accept|merge|rename|dismiss' }, target: { type: 'string' } }, required: ['suggestion', 'action'] },
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
name: 'chat_topics_reclassify',
|
|
892
|
+
description: 'Optional: re-run the classifier on older chats. Call with confirm omitted/false to get the cost estimate (scope other = only unlisted chats, all = everything; hard cap 500). Resend with confirm=true to queue it. Never needed for merge/rename/group.',
|
|
893
|
+
inputSchema: { type: 'object', properties: { scope: { type: 'string' }, days: { type: 'number' }, max: { type: 'number' }, confirm: { type: 'boolean' } } },
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
name: 'chat_topics_analysis',
|
|
897
|
+
description: 'Optional one-call analysis (cheap model, aggregate counts only, PII redacted, cached, one run per day per site) that proposes merges/renames/groups. confirm omitted/false returns the estimate and any cached result; confirm=true runs it. Then call with proposalId + accept to apply or reject one proposal.',
|
|
898
|
+
inputSchema: { type: 'object', properties: { confirm: { type: 'boolean' }, proposalId: { type: 'string' }, accept: { type: 'boolean' } } },
|
|
899
|
+
},
|
|
825
900
|
{
|
|
826
901
|
name: 'chat_intelligence_recommendations',
|
|
827
902
|
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 +3051,26 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
2976
3051
|
},
|
|
2977
3052
|
},
|
|
2978
3053
|
},
|
|
3054
|
+
{
|
|
3055
|
+
name: 'enterprise_reports',
|
|
3056
|
+
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.',
|
|
3057
|
+
inputSchema: {
|
|
3058
|
+
type: 'object',
|
|
3059
|
+
properties: {
|
|
3060
|
+
report: { type: 'string', enum: ['program_breakdown', 'agent_scorecard', 'qa_coverage', 'coaching_queue', 'sentiment_breakdown', 'topic_radar', 'weekly_brief', 'weekly_brief_send_test'] },
|
|
3061
|
+
from: { type: 'string', description: 'yyyy-MM-dd start (default: 30 days ago).' },
|
|
3062
|
+
to: { type: 'string', description: 'yyyy-MM-dd end, inclusive (default: today).' },
|
|
3063
|
+
groupBy: { type: 'string', description: 'program_breakdown: website|team|platform|device|entrypage. sentiment_breakdown: website|team|agent|theme.' },
|
|
3064
|
+
deploymentId: { type: 'number', description: 'Optional website (deployment) ID.' },
|
|
3065
|
+
teamId: { type: 'number', description: 'Optional team ID.' },
|
|
3066
|
+
threshold: { type: 'number', description: 'coaching_queue: queue chats scored below this (1-100; default = the rubric low-score threshold or 60).' },
|
|
3067
|
+
perAgent: { type: 'number', description: 'coaching_queue: chats listed per agent (default 5).' },
|
|
3068
|
+
limit: { type: 'number', description: 'topic_radar: max topics (default 25).' },
|
|
3069
|
+
days: { type: 'number', description: 'weekly_brief: days to cover (default 7).' },
|
|
3070
|
+
},
|
|
3071
|
+
required: ['report'],
|
|
3072
|
+
},
|
|
3073
|
+
},
|
|
2979
3074
|
{
|
|
2980
3075
|
name: 'report_backfill',
|
|
2981
3076
|
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 +3251,10 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3156
3251
|
voiceId: { type: 'string', description: 'ElevenLabs voice ID override. Omit to use the site default.' },
|
|
3157
3252
|
showCallMeOption: { type: 'boolean', description: 'Show the "Call me instead" option (requires EnableClickToCall).' },
|
|
3158
3253
|
callbackConfirmWindowMinutes: { type: 'number', description: 'Minutes to confirm a callback request before it expires. Default 10.' },
|
|
3254
|
+
displayName: { type: 'string', description: 'Visitor-facing avatar name shown on the badge (e.g. "Moshky"). Empty string resets to "AI".' },
|
|
3255
|
+
showBadge: { type: 'boolean', description: 'Show the name badge over the avatar video. Default true.' },
|
|
3256
|
+
sizePx: { type: 'number', description: 'Max avatar panel width in px (160-640, square). Default 360.' },
|
|
3257
|
+
allowVisitorCollapse: { type: 'boolean', description: 'Let visitors hide or collapse the avatar panel (up/down arrow and X). Default true.' },
|
|
3159
3258
|
maxAvatarMinutesPerMonth: { type: 'number', description: 'Monthly avatar video minutes cap for the site. Whole number 1-100000; never unlimited (0/negative rejected).' },
|
|
3160
3259
|
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
3260
|
},
|
|
@@ -3371,7 +3470,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3371
3470
|
},
|
|
3372
3471
|
{
|
|
3373
3472
|
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.',
|
|
3473
|
+
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
3474
|
inputSchema: {
|
|
3376
3475
|
type: 'object',
|
|
3377
3476
|
properties: {
|
|
@@ -3380,6 +3479,33 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3380
3479
|
required: ['siteId'],
|
|
3381
3480
|
},
|
|
3382
3481
|
},
|
|
3482
|
+
{
|
|
3483
|
+
name: 'email_inbox_resubscribe_outlook',
|
|
3484
|
+
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.',
|
|
3485
|
+
inputSchema: {
|
|
3486
|
+
type: 'object',
|
|
3487
|
+
properties: {
|
|
3488
|
+
siteId: { type: 'number', description: 'Site ID' },
|
|
3489
|
+
mailboxId: { type: 'number', description: 'Outlook mailbox integration ID from email_inbox_list_mailboxes' },
|
|
3490
|
+
},
|
|
3491
|
+
required: ['siteId', 'mailboxId'],
|
|
3492
|
+
},
|
|
3493
|
+
},
|
|
3494
|
+
{
|
|
3495
|
+
name: 'site_integration_logs',
|
|
3496
|
+
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.",
|
|
3497
|
+
inputSchema: {
|
|
3498
|
+
type: 'object',
|
|
3499
|
+
properties: {
|
|
3500
|
+
siteId: { type: 'number', description: 'Site ID' },
|
|
3501
|
+
slug: { type: 'string', description: 'Integration slug, e.g. OutlookInbox' },
|
|
3502
|
+
severity: { type: 'string', enum: ['Info', 'Warning', 'Error'], description: 'Optional severity filter' },
|
|
3503
|
+
search: { type: 'string', description: 'Optional substring match on message / additional info' },
|
|
3504
|
+
take: { type: 'number', description: 'Max rows (default 50, max 200)' },
|
|
3505
|
+
},
|
|
3506
|
+
required: ['siteId'],
|
|
3507
|
+
},
|
|
3508
|
+
},
|
|
3383
3509
|
{
|
|
3384
3510
|
name: 'email_inbox_set_team',
|
|
3385
3511
|
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 +4522,11 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
4396
4522
|
description: 'List all registered EntitlementFeature keys with their display names and categories. Useful for finding the right featureKey before setting overrides. Superadmin only.',
|
|
4397
4523
|
inputSchema: { type: 'object', properties: {} },
|
|
4398
4524
|
},
|
|
4525
|
+
{
|
|
4526
|
+
name: 'entitlement_enterprise_reports_package',
|
|
4527
|
+
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.',
|
|
4528
|
+
inputSchema: { type: 'object', properties: { siteId: { type: 'number', description: 'Optional site ID to show which items are on' } } },
|
|
4529
|
+
},
|
|
4399
4530
|
{
|
|
4400
4531
|
name: 'entitlement_set_override',
|
|
4401
4532
|
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.',
|
|
@@ -4849,6 +4980,52 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
4849
4980
|
description: 'List candidate applications submitted via the Recruiting Bot for this site (name, email, phone, role interest, work authorization, availability, submitted timestamp). Never contains EEOC self-ID data.',
|
|
4850
4981
|
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4851
4982
|
},
|
|
4983
|
+
{
|
|
4984
|
+
name: 'recruiting_ats_failures',
|
|
4985
|
+
description: 'List candidate applications whose ATS write-back is Pending, Failed or Abandoned (status, attempts, last error, next retry). Requires EnableRecruitingBot.',
|
|
4986
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4987
|
+
},
|
|
4988
|
+
{
|
|
4989
|
+
name: 'recruiting_ats_retry',
|
|
4990
|
+
description: 'Retry the ATS write-back for one candidate application right now (resets the attempt counter). Returns the new sync status.',
|
|
4991
|
+
inputSchema: { type: 'object', properties: { applicationId: { type: 'number' } }, required: ['applicationId'] },
|
|
4992
|
+
},
|
|
4993
|
+
{
|
|
4994
|
+
name: 'recruiting_apply_settings',
|
|
4995
|
+
description: 'Set public apply form options for this site (apply-if-present): publicApplyEnabled toggles the public apply endpoint; autoCreateCaseTypeId picks the case type auto-created per application (0 clears).',
|
|
4996
|
+
inputSchema: {
|
|
4997
|
+
type: 'object',
|
|
4998
|
+
properties: {
|
|
4999
|
+
publicApplyEnabled: { type: 'boolean' },
|
|
5000
|
+
autoCreateCaseTypeId: { type: 'number', description: 'Case type id; 0 clears the auto-create link' },
|
|
5001
|
+
},
|
|
5002
|
+
required: [],
|
|
5003
|
+
},
|
|
5004
|
+
},
|
|
5005
|
+
{
|
|
5006
|
+
name: 'recruiting_bot_settings',
|
|
5007
|
+
description: 'Get or set the recruiting bot customer controls (apply-if-present; no fields = read): pageContextEnabled (use the job from the host page Velaro context call), revealPostedDate, revealDescription, revealApplyLink, publicApplyEnabled, applyMode (both|link|inproduct), maxResults (1-10), greeting (max 500), tone (friendly|professional|concise).',
|
|
5008
|
+
inputSchema: {
|
|
5009
|
+
type: 'object',
|
|
5010
|
+
properties: {
|
|
5011
|
+
pageContextEnabled: { type: 'boolean' },
|
|
5012
|
+
revealPostedDate: { type: 'boolean' },
|
|
5013
|
+
revealDescription: { type: 'boolean' },
|
|
5014
|
+
revealApplyLink: { type: 'boolean' },
|
|
5015
|
+
publicApplyEnabled: { type: 'boolean' },
|
|
5016
|
+
applyMode: { type: 'string', enum: ['both', 'link', 'inproduct'] },
|
|
5017
|
+
maxResults: { type: 'number' },
|
|
5018
|
+
greeting: { type: 'string' },
|
|
5019
|
+
tone: { type: 'string', enum: ['friendly', 'professional', 'concise'] },
|
|
5020
|
+
},
|
|
5021
|
+
required: [],
|
|
5022
|
+
},
|
|
5023
|
+
},
|
|
5024
|
+
{
|
|
5025
|
+
name: 'recruiting_download_resume',
|
|
5026
|
+
description: 'Download a candidate application resume. Returns { fileName, contentType, sizeBytes, base64 }. Files over 5 MB are refused; use the CLI (velaro recruiting applications resume) for those.',
|
|
5027
|
+
inputSchema: { type: 'object', properties: { applicationId: { type: 'number' } }, required: ['applicationId'] },
|
|
5028
|
+
},
|
|
4852
5029
|
// Recruiting/Applications/ExportEeoc (voluntary self-ID CSV export) is intentionally NOT
|
|
4853
5030
|
// exposed as an MCP tool -- it returns sensitive EEOC/OFCCP self-identification PII in bulk.
|
|
4854
5031
|
// It is administrator-only and audit-logged server-side (403 for ordinary agents); the
|
|
@@ -5725,6 +5902,42 @@ async function handleTool(name, args) {
|
|
|
5725
5902
|
if (args.teamId != null) qs.set('teamId', String(args.teamId));
|
|
5726
5903
|
return await messagingGet(`/Reports/chat-intelligence/themes?${qs.toString()}`);
|
|
5727
5904
|
}
|
|
5905
|
+
case 'chat_topics_get':
|
|
5906
|
+
return await messagingGet('/ChatIntelligenceTopics');
|
|
5907
|
+
case 'chat_topics_set':
|
|
5908
|
+
return args.industry
|
|
5909
|
+
? await messagingPost('/ChatIntelligenceTopics/template', { industry: args.industry })
|
|
5910
|
+
: await messagingPut('/ChatIntelligenceTopics', { topics: args.topics ?? [] });
|
|
5911
|
+
case 'chat_topics_edit':
|
|
5912
|
+
return await messagingPost('/ChatIntelligenceTopics/mapping', { ops: [{ op: args.op, from: args.from ?? [], to: args.to ?? null, group: args.group ?? null }] });
|
|
5913
|
+
case 'chat_topics_group_suggestions':
|
|
5914
|
+
return await messagingGet(`/ChatIntelligenceTopics/group-suggestions?days=${args.days ?? 90}`);
|
|
5915
|
+
case 'chat_topics_suggestions':
|
|
5916
|
+
return await messagingGet(`/ChatIntelligenceTopics/suggestions?days=${args.days ?? 90}`);
|
|
5917
|
+
case 'chat_topics_resolve_suggestion':
|
|
5918
|
+
return await messagingPost('/ChatIntelligenceTopics/suggestions/resolve', { suggestion: args.suggestion, action: args.action, target: args.target ?? null });
|
|
5919
|
+
case 'chat_topics_reclassify':
|
|
5920
|
+
return args.confirm
|
|
5921
|
+
? await messagingPost('/ChatIntelligenceTopics/reclassify', { scope: args.scope ?? 'other', days: args.days ?? 90, max: args.max ?? null, confirm: true })
|
|
5922
|
+
: await messagingGet(`/ChatIntelligenceTopics/reclassify/estimate?scope=${args.scope ?? 'other'}&days=${args.days ?? 90}${args.max ? `&max=${args.max}` : ''}`);
|
|
5923
|
+
case 'chat_topics_analysis':
|
|
5924
|
+
if (args.proposalId) return await messagingPost('/ChatIntelligenceTopics/analysis/proposals', { id: args.proposalId, accept: args.accept !== false });
|
|
5925
|
+
if (args.confirm) return await messagingPost('/ChatIntelligenceTopics/analysis', { confirm: true });
|
|
5926
|
+
return { estimate: await messagingGet('/ChatIntelligenceTopics/analysis/estimate'), cached: await messagingGet('/ChatIntelligenceTopics/analysis') };
|
|
5927
|
+
case 'canned_learning_suggestions':
|
|
5928
|
+
return await messagingGet(`/CannedLearning/suggestions?state=${encodeURIComponent(args.state ?? 'pending_review')}`);
|
|
5929
|
+
case 'canned_learning_generate':
|
|
5930
|
+
return await messagingPost('/CannedLearning/generate', {});
|
|
5931
|
+
case 'canned_learning_approve':
|
|
5932
|
+
return await messagingPost(`/CannedLearning/suggestions/${args.suggestionId}/approve`, { editedText: args.editedText ?? null });
|
|
5933
|
+
case 'canned_learning_reject':
|
|
5934
|
+
return await messagingPost(`/CannedLearning/suggestions/${args.suggestionId}/reject`, {});
|
|
5935
|
+
case 'canned_learning_edit_summary':
|
|
5936
|
+
return await messagingGet('/CannedLearning/edit-events');
|
|
5937
|
+
case 'canned_learning_record_edit':
|
|
5938
|
+
return await messagingPost('/CannedLearning/edit-events', { cannedResponseId: args.cannedResponseId, finalText: args.finalText });
|
|
5939
|
+
case 'canned_learning_rollback':
|
|
5940
|
+
return await messagingPost(`/CannedLearning/suggestions/${args.suggestionId}/rollback`, { notes: args.notes ?? null });
|
|
5728
5941
|
case 'chat_intelligence_recommendations':
|
|
5729
5942
|
return await messagingGet(`/Reports/chat-intelligence/recommendations`);
|
|
5730
5943
|
case 'chat_intelligence_draft_article':
|
|
@@ -5973,7 +6186,7 @@ async function handleTool(name, args) {
|
|
|
5973
6186
|
return await rpc('ai_config_set_reply_format', args);
|
|
5974
6187
|
|
|
5975
6188
|
case 'site_identity_secret_status': {
|
|
5976
|
-
const settings = await api('GET', '/SiteSettings');
|
|
6189
|
+
const settings = await api('GET', '/api/SiteSettings');
|
|
5977
6190
|
const configured = settings?.identitySigningSecretConfigured ?? false;
|
|
5978
6191
|
const masked = settings?.identitySigningSecretMasked ?? null;
|
|
5979
6192
|
if (!configured) {
|
|
@@ -6013,7 +6226,7 @@ async function handleTool(name, args) {
|
|
|
6013
6226
|
}
|
|
6014
6227
|
|
|
6015
6228
|
case 'index_list': {
|
|
6016
|
-
const indexes = await messagingApi('/AzureIndexes/
|
|
6229
|
+
const indexes = await messagingApi('/AzureIndexes/List');
|
|
6017
6230
|
if (!Array.isArray(indexes) || !indexes.length) {
|
|
6018
6231
|
return 'No knowledge collections found for this site.';
|
|
6019
6232
|
}
|
|
@@ -6345,7 +6558,8 @@ async function handleTool(name, args) {
|
|
|
6345
6558
|
}
|
|
6346
6559
|
|
|
6347
6560
|
case 'routing_delete': {
|
|
6348
|
-
|
|
6561
|
+
// RulesController.DeleteAsync takes the rule in the BODY (DELETE /Rules {id}); DELETE /rules/{id} never existed.
|
|
6562
|
+
await messagingRequest('DELETE', '/Rules', { id: args.id });
|
|
6349
6563
|
return `Deleted routing rule id=${args.id}`;
|
|
6350
6564
|
}
|
|
6351
6565
|
|
|
@@ -6884,7 +7098,7 @@ async function handleTool(name, args) {
|
|
|
6884
7098
|
|
|
6885
7099
|
// ── Layer 7: Admin Activity Log — recent channel/auth/integration events
|
|
6886
7100
|
// Useful for detecting send failures, auth events, channel errors not in integration logs.
|
|
6887
|
-
const activityLogP = safeJson(() =>
|
|
7101
|
+
const activityLogP = safeJson(() => messagingRequest('GET', '/AdminActivityLog?pageSize=10'));
|
|
6888
7102
|
|
|
6889
7103
|
const [health, ci, aiEx, velaroLogs, loggly, intLogPage, skillErr, activityLog] = await Promise.all([healthP, ciP, aiExP, velaroLoggerP, logglyP, intLogP, skillErrP, activityLogP]);
|
|
6890
7104
|
|
|
@@ -7009,7 +7223,7 @@ async function handleTool(name, args) {
|
|
|
7009
7223
|
const q = args.query ?? '';
|
|
7010
7224
|
const top = args.top ?? 5;
|
|
7011
7225
|
if (!q) return 'query is required.';
|
|
7012
|
-
const results = await
|
|
7226
|
+
const results = await messagingRequest('GET', `/AzureIndexes/content-search?q=${encodeURIComponent(q)}&top=${top}`);
|
|
7013
7227
|
if (results?.error) return `Index search error: ${results.error}`;
|
|
7014
7228
|
if (!Array.isArray(results) || !results.length) return `No results for "${q}". Index may be empty — trigger ingestion first.`;
|
|
7015
7229
|
const lines = [
|
|
@@ -7072,7 +7286,7 @@ async function handleTool(name, args) {
|
|
|
7072
7286
|
return JSON.stringify(r, null, 2);
|
|
7073
7287
|
}
|
|
7074
7288
|
case 'index_status': {
|
|
7075
|
-
const sources = await
|
|
7289
|
+
const sources = await messagingRequest('GET', '/AzureIndexes/IntegrationSources');
|
|
7076
7290
|
if (!Array.isArray(sources) || !sources.length) return 'No indexed sources found. Run index_ingest to populate.';
|
|
7077
7291
|
const lines = [
|
|
7078
7292
|
'=== Index Status ===',
|
|
@@ -7085,7 +7299,7 @@ async function handleTool(name, args) {
|
|
|
7085
7299
|
if (!args.jobId) return 'jobId is required.';
|
|
7086
7300
|
const body = { jobId: args.jobId };
|
|
7087
7301
|
if (args.indexName) body.indexName = args.indexName;
|
|
7088
|
-
const result = await
|
|
7302
|
+
const result = await messagingRequest('POST', '/AzureIndexes/IngestJobDirect', body);
|
|
7089
7303
|
if (result?.error) return `Ingestion error: ${result.error}`;
|
|
7090
7304
|
return `Ingestion started for job ${args.jobId}.\n Physical index: ${result?.physicalIndex ?? 'unknown'}\n\nEmbedding runs in the background (~1-2 min). Use index_status to verify chunks appear.`;
|
|
7091
7305
|
}
|
|
@@ -7110,7 +7324,7 @@ async function handleTool(name, args) {
|
|
|
7110
7324
|
}
|
|
7111
7325
|
|
|
7112
7326
|
case 'uploaded_files_list': {
|
|
7113
|
-
const files = await
|
|
7327
|
+
const files = await messagingRequest('GET', '/uploadedFiles/list/AI');
|
|
7114
7328
|
if (!Array.isArray(files) || !files.length) return 'No AI training files uploaded for this site.';
|
|
7115
7329
|
const lines = ['=== Uploaded AI Training Files ===',
|
|
7116
7330
|
...files.map(f => ` [id=${f.id}] ${f.fileName} size=${f.fileLength ? Math.round(f.fileLength/1024)+'KB' : '?'}`)];
|
|
@@ -7123,7 +7337,7 @@ async function handleTool(name, args) {
|
|
|
7123
7337
|
if (args.fileId) body.id = args.fileId;
|
|
7124
7338
|
if (args.fileName) body.fileName = args.fileName;
|
|
7125
7339
|
if (args.indexName) body.indexName = args.indexName;
|
|
7126
|
-
const result = await
|
|
7340
|
+
const result = await messagingRequest('POST', '/uploadedFiles/reindex', body);
|
|
7127
7341
|
if (result?.error) return `Reindex error: ${result.error}`;
|
|
7128
7342
|
return `✅ Re-indexed '${result.fileName}': ${result.chunksIndexed} chunks indexed.\n\nUse index_status to verify the chunks appear in the knowledge index.`;
|
|
7129
7343
|
}
|
|
@@ -8266,6 +8480,23 @@ async function handleTool(name, args) {
|
|
|
8266
8480
|
return `Subscribed to ${args.reportType} (${args.frequency ?? 'weekly'}, ${args.deliveryHour ?? 7}:00 site time).`;
|
|
8267
8481
|
}
|
|
8268
8482
|
|
|
8483
|
+
case 'enterprise_reports': {
|
|
8484
|
+
const ER_PATHS = {
|
|
8485
|
+
program_breakdown: 'program-breakdown', agent_scorecard: 'agent-scorecard', qa_coverage: 'qa-coverage',
|
|
8486
|
+
coaching_queue: 'coaching-queue', sentiment_breakdown: 'sentiment-breakdown', topic_radar: 'topic-radar',
|
|
8487
|
+
weekly_brief: 'weekly-brief', weekly_brief_send_test: 'weekly-brief/send-test',
|
|
8488
|
+
};
|
|
8489
|
+
const seg = ER_PATHS[args.report];
|
|
8490
|
+
if (!seg) throw new Error('report must be one of: ' + Object.keys(ER_PATHS).join(', '));
|
|
8491
|
+
if (args.report === 'weekly_brief_send_test') return JSON.stringify(await messagingPost('/api/enterprise-reports/' + seg, {}), null, 2);
|
|
8492
|
+
const qs = new URLSearchParams();
|
|
8493
|
+
for (const k of ['from', 'to', 'groupBy', 'deploymentId', 'teamId', 'threshold', 'perAgent', 'limit', 'days']) {
|
|
8494
|
+
if (args[k] != null && args[k] !== '') qs.set(k, String(args[k]));
|
|
8495
|
+
}
|
|
8496
|
+
const q = qs.toString();
|
|
8497
|
+
return JSON.stringify(await messagingGet('/api/enterprise-reports/' + seg + (q ? '?' + q : '')), null, 2);
|
|
8498
|
+
}
|
|
8499
|
+
|
|
8269
8500
|
case 'experience_impact_report': {
|
|
8270
8501
|
const qs = new URLSearchParams();
|
|
8271
8502
|
for (const k of ['from', 'to', 'teamId']) if (args[k] != null && args[k] !== '') qs.set(k, String(args[k]));
|
|
@@ -8394,6 +8625,9 @@ async function handleTool(name, args) {
|
|
|
8394
8625
|
` Enabled: ${data.enabled}`,
|
|
8395
8626
|
` "Call me": ${data.showCallMeOption}`,
|
|
8396
8627
|
` Callback window: ${data.callbackConfirmWindowMinutes} min`,
|
|
8628
|
+
` Display name: ${data.displayName || '(AI)'}`,
|
|
8629
|
+
` Badge shown: ${data.showBadge}`,
|
|
8630
|
+
` Size: ${data.sizePx}px`,
|
|
8397
8631
|
].join('\n');
|
|
8398
8632
|
}
|
|
8399
8633
|
|
|
@@ -8405,6 +8639,10 @@ async function handleTool(name, args) {
|
|
|
8405
8639
|
if (args.enabled !== undefined) body.enabled = args.enabled;
|
|
8406
8640
|
if (args.showCallMeOption !== undefined) body.showCallMeOption = args.showCallMeOption;
|
|
8407
8641
|
if (args.callbackConfirmWindowMinutes !== undefined) body.callbackConfirmWindowMinutes = args.callbackConfirmWindowMinutes;
|
|
8642
|
+
if (args.displayName !== undefined) body.displayName = args.displayName;
|
|
8643
|
+
if (args.showBadge !== undefined) body.showBadge = args.showBadge;
|
|
8644
|
+
if (args.sizePx !== undefined) body.sizePx = args.sizePx;
|
|
8645
|
+
if (args.allowVisitorCollapse !== undefined) body.allowVisitorCollapse = args.allowVisitorCollapse;
|
|
8408
8646
|
for (const [arg, max] of [['maxAvatarMinutesPerMonth', 100000], ['maxAvatarTtsChars', 100000000]]) {
|
|
8409
8647
|
if (args[arg] === undefined) continue;
|
|
8410
8648
|
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 +8762,23 @@ async function handleTool(name, args) {
|
|
|
8524
8762
|
return JSON.stringify(data, null, 2);
|
|
8525
8763
|
}
|
|
8526
8764
|
|
|
8765
|
+
case 'email_inbox_resubscribe_outlook': {
|
|
8766
|
+
if (!args.siteId || !args.mailboxId) return 'siteId and mailboxId are required.';
|
|
8767
|
+
const data = await messagingPost(`/SupportTools/sites/${args.siteId}/email-inbox/outlook/${args.mailboxId}/resubscribe`, {});
|
|
8768
|
+
return JSON.stringify(data, null, 2);
|
|
8769
|
+
}
|
|
8770
|
+
|
|
8771
|
+
case 'site_integration_logs': {
|
|
8772
|
+
if (!args.siteId) return 'siteId is required.';
|
|
8773
|
+
const q = new URLSearchParams();
|
|
8774
|
+
if (args.slug) q.set('slug', args.slug);
|
|
8775
|
+
if (args.severity) q.set('severity', args.severity);
|
|
8776
|
+
if (args.search) q.set('search', args.search);
|
|
8777
|
+
if (args.take) q.set('take', String(args.take));
|
|
8778
|
+
const data = await messagingGet(`/SupportTools/sites/${args.siteId}/integration-logs?${q.toString()}`);
|
|
8779
|
+
return JSON.stringify(data, null, 2);
|
|
8780
|
+
}
|
|
8781
|
+
|
|
8527
8782
|
case 'email_inbox_set_team': {
|
|
8528
8783
|
if (!args.siteId || !args.provider || !args.mailboxId || args.teamId == null) return 'siteId, provider, mailboxId, and teamId are required.';
|
|
8529
8784
|
const data = await messagingPatch(`/SupportTools/sites/${args.siteId}/email-inbox/${args.provider}/${args.mailboxId}/team`, { teamId: args.teamId });
|
|
@@ -8989,7 +9244,7 @@ async function handleTool(name, args) {
|
|
|
8989
9244
|
|
|
8990
9245
|
case 'magento_diagnose': {
|
|
8991
9246
|
if (!args.siteId) return 'siteId is required.';
|
|
8992
|
-
const r = await
|
|
9247
|
+
const r = await messagingRequest('GET', `/diagnostics/test-magento?siteId=${encodeURIComponent(args.siteId)}`);
|
|
8993
9248
|
return JSON.stringify(r, null, 2);
|
|
8994
9249
|
}
|
|
8995
9250
|
|
|
@@ -8997,18 +9252,18 @@ async function handleTool(name, args) {
|
|
|
8997
9252
|
case 'sap_diagnose': {
|
|
8998
9253
|
if (!args.siteId) return 'siteId is required.';
|
|
8999
9254
|
const deep = args.deep === false ? 'false' : 'true';
|
|
9000
|
-
const r = await
|
|
9255
|
+
const r = await messagingRequest('GET', `/diagnostics/test-sap?siteId=${encodeURIComponent(args.siteId)}&deep=${deep}`);
|
|
9001
9256
|
return JSON.stringify(r, null, 2);
|
|
9002
9257
|
}
|
|
9003
9258
|
|
|
9004
9259
|
case 'business_action_list': {
|
|
9005
|
-
const r = await
|
|
9260
|
+
const r = await messagingRequest('GET', '/BusinessActions');
|
|
9006
9261
|
return JSON.stringify(r, null, 2);
|
|
9007
9262
|
}
|
|
9008
9263
|
|
|
9009
9264
|
case 'business_action_route': {
|
|
9010
9265
|
if (!args.text) return 'text is required.';
|
|
9011
|
-
const r = await
|
|
9266
|
+
const r = await messagingRequest('POST', '/BusinessActions/Route', { text: args.text, max: args.max });
|
|
9012
9267
|
return JSON.stringify(r, null, 2);
|
|
9013
9268
|
}
|
|
9014
9269
|
|
|
@@ -9018,7 +9273,7 @@ async function handleTool(name, args) {
|
|
|
9018
9273
|
const qs = [];
|
|
9019
9274
|
if (args.idempotencyKey) qs.push(`idempotencyKey=${encodeURIComponent(args.idempotencyKey)}`);
|
|
9020
9275
|
if (args.conversationId) qs.push(`conversationId=${encodeURIComponent(args.conversationId)}`);
|
|
9021
|
-
const r = await
|
|
9276
|
+
const r = await messagingRequest('POST', `/BusinessActions/${encodeURIComponent(args.name)}/Execute${qs.length ? '?' + qs.join('&') : ''}`, args.request);
|
|
9022
9277
|
return JSON.stringify(r, null, 2);
|
|
9023
9278
|
}
|
|
9024
9279
|
|
|
@@ -9561,7 +9816,7 @@ async function handleTool(name, args) {
|
|
|
9561
9816
|
}
|
|
9562
9817
|
|
|
9563
9818
|
case 'billing_plan_status': {
|
|
9564
|
-
const s = await api('GET', '/BillingPayment/plan-status'
|
|
9819
|
+
const s = await api('GET', '/api/BillingPayment/plan-status');
|
|
9565
9820
|
const lines = [`Site ${s.siteId} — Subscription Status`];
|
|
9566
9821
|
if (s.subscription) {
|
|
9567
9822
|
lines.push(`Seats: ${s.subscription.maxLicensedSeats} | Conv/mo: ${s.subscription.maxConversationsPerMonth || '∞'} | Self-serve: ${s.subscription.enableSelfServeSeats ? 'YES' : 'no'}`);
|
|
@@ -9678,6 +9933,18 @@ async function handleTool(name, args) {
|
|
|
9678
9933
|
return lines.join('\n');
|
|
9679
9934
|
}
|
|
9680
9935
|
|
|
9936
|
+
case 'entitlement_enterprise_reports_package': {
|
|
9937
|
+
const q = args && args.siteId ? `?siteId=${encodeURIComponent(args.siteId)}` : '';
|
|
9938
|
+
const pkg = await api('GET', `/Entitlements/packages/enterprise-reports${q}`);
|
|
9939
|
+
const lines = [`${pkg.displayName} (${pkg.packageKey}) - plan ${pkg.plan}, default OFF${pkg.siteId ? `, site ${pkg.siteId}` : ''}`, ''];
|
|
9940
|
+
for (const it of pkg.items) {
|
|
9941
|
+
lines.push(`${pkg.siteId ? (it.enabled ? '[ON ] ' : '[off] ') : ''}${it.ask}. ${it.title}`);
|
|
9942
|
+
lines.push(` key: ${it.featureKey}; builds on: ${it.buildsOn.join(', ')}`);
|
|
9943
|
+
lines.push(` ${it.summary}`);
|
|
9944
|
+
}
|
|
9945
|
+
return lines.join(String.fromCharCode(10));
|
|
9946
|
+
}
|
|
9947
|
+
|
|
9681
9948
|
case 'entitlement_list_features': {
|
|
9682
9949
|
const features = await api('GET', '/Entitlements/features');
|
|
9683
9950
|
const byCategory = {};
|
|
@@ -10045,6 +10312,61 @@ async function handleTool(name, args) {
|
|
|
10045
10312
|
return JSON.stringify(listing, null, 2);
|
|
10046
10313
|
}
|
|
10047
10314
|
|
|
10315
|
+
case 'recruiting_ats_failures': {
|
|
10316
|
+
const rows = await messagingGet('/Recruiting/Applications/AtsFailures');
|
|
10317
|
+
return JSON.stringify(rows, null, 2);
|
|
10318
|
+
}
|
|
10319
|
+
case 'recruiting_ats_retry': {
|
|
10320
|
+
const id = Number(args.applicationId);
|
|
10321
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('applicationId must be a positive integer.');
|
|
10322
|
+
const r = await messagingPost(`/Recruiting/Applications/${id}/AtsRetry`, {});
|
|
10323
|
+
return JSON.stringify(r, null, 2);
|
|
10324
|
+
}
|
|
10325
|
+
case 'recruiting_apply_settings': {
|
|
10326
|
+
const body = {};
|
|
10327
|
+
if (args.publicApplyEnabled !== undefined) body.publicApplyEnabled = Boolean(args.publicApplyEnabled);
|
|
10328
|
+
if (args.autoCreateCaseTypeId !== undefined) {
|
|
10329
|
+
const n = Number(args.autoCreateCaseTypeId);
|
|
10330
|
+
if (!Number.isInteger(n) || n < 0) throw new Error('autoCreateCaseTypeId must be an integer >= 0 (0 clears).');
|
|
10331
|
+
body.autoCreateCaseTypeId = n;
|
|
10332
|
+
}
|
|
10333
|
+
if (!Object.keys(body).length) throw new Error('Specify publicApplyEnabled and/or autoCreateCaseTypeId.');
|
|
10334
|
+
const r = await messagingPut('/Recruiting/ApplySettings', body);
|
|
10335
|
+
return JSON.stringify(r, null, 2);
|
|
10336
|
+
}
|
|
10337
|
+
case 'recruiting_bot_settings': {
|
|
10338
|
+
const fields = ['pageContextEnabled', 'revealPostedDate', 'revealDescription', 'revealApplyLink', 'publicApplyEnabled', 'applyMode', 'maxResults', 'greeting', 'tone'];
|
|
10339
|
+
const body = {};
|
|
10340
|
+
for (const f of fields) if (args[f] !== undefined) body[f] = args[f];
|
|
10341
|
+
if (body.applyMode !== undefined && !['both', 'link', 'inproduct'].includes(String(body.applyMode).toLowerCase())) throw new Error('applyMode must be both, link or inproduct.');
|
|
10342
|
+
if (body.tone !== undefined && !['friendly', 'professional', 'concise'].includes(String(body.tone).toLowerCase())) throw new Error('tone must be friendly, professional or concise.');
|
|
10343
|
+
if (body.maxResults !== undefined && !(Number.isInteger(Number(body.maxResults)) && Number(body.maxResults) >= 1 && Number(body.maxResults) <= 10)) throw new Error('maxResults must be an integer from 1 to 10.');
|
|
10344
|
+
const r = Object.keys(body).length ? await messagingPut('/Recruiting/BotSettings', body) : await messagingGet('/Recruiting/BotSettings');
|
|
10345
|
+
return JSON.stringify(r, null, 2);
|
|
10346
|
+
}
|
|
10347
|
+
case 'recruiting_download_resume': {
|
|
10348
|
+
const id = Number(args.applicationId);
|
|
10349
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('applicationId must be a positive integer.');
|
|
10350
|
+
if (!MESSAGING_API) throw new Error('VELARO_MESSAGING_API is not configured.');
|
|
10351
|
+
const res = await fetch(`${MESSAGING_API}/Recruiting/Applications/${id}/Resume`, {
|
|
10352
|
+
headers: { Authorization: authHeader() },
|
|
10353
|
+
signal: AbortSignal.timeout(30000),
|
|
10354
|
+
});
|
|
10355
|
+
if (!res.ok) {
|
|
10356
|
+
const text = await res.text().catch(() => '');
|
|
10357
|
+
throw new Error(`/Recruiting/Applications/${id}/Resume -> ${res.status}: ${text.slice(0, 300)}`);
|
|
10358
|
+
}
|
|
10359
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
10360
|
+
if (buf.length > 5 * 1024 * 1024) throw new Error(`Resume is ${buf.length} bytes (over the 5 MB MCP limit); use the CLI: velaro recruiting applications resume ${id}`);
|
|
10361
|
+
const cd = /filename\*?=(?:UTF-8'')?"?([^";]+)"?/i.exec(res.headers.get('content-disposition') || '');
|
|
10362
|
+
return JSON.stringify({
|
|
10363
|
+
fileName: cd ? decodeURIComponent(cd[1]) : `resume-${id}`,
|
|
10364
|
+
contentType: res.headers.get('content-type') || 'application/octet-stream',
|
|
10365
|
+
sizeBytes: buf.length,
|
|
10366
|
+
base64: buf.toString('base64'),
|
|
10367
|
+
}, null, 2);
|
|
10368
|
+
}
|
|
10369
|
+
|
|
10048
10370
|
case 'jobboard_move_application_stage': {
|
|
10049
10371
|
const app = await messagingPut(`/Recruiting/Applications/${args.applicationId}/Stage`, { stage: args.stage });
|
|
10050
10372
|
return JSON.stringify(app, null, 2);
|