@velaro/mcp-server 0.6.92 → 0.6.94
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 +2 -2
- package/server.js +322 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velaro/mcp-server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.94",
|
|
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": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"node": ">=18.0.0"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
13
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
14
14
|
"qs": "^6.16.0"
|
|
15
15
|
},
|
|
16
16
|
"overrides": {
|
package/server.js
CHANGED
|
@@ -151,6 +151,18 @@ function extractLogEntries(response) {
|
|
|
151
151
|
return [];
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Body for PUT avatar/groups|workflows/{id}/override. Merge by default (only passed fields change, "" clears one);
|
|
156
|
+
* `replace: true` replaces the whole override. Mirrors cli/lib/commands/avatar.js.
|
|
157
|
+
*/
|
|
158
|
+
function avatarOverrideBody(args) {
|
|
159
|
+
const body = { merge: args.replace !== true };
|
|
160
|
+
for (const k of ['providerSlug', 'avatarId', 'personalityPresetSlug', 'voiceId', 'displayName', 'backgroundMode', 'backgroundValue']) {
|
|
161
|
+
if (args[k] !== undefined) body[k] = args[k];
|
|
162
|
+
}
|
|
163
|
+
return body;
|
|
164
|
+
}
|
|
165
|
+
|
|
154
166
|
async function messagingRequest(method, path, body) {
|
|
155
167
|
if (!MESSAGING_API) throw new Error(
|
|
156
168
|
'VELARO_MESSAGING_API is not configured. Add "VELARO_MESSAGING_API": "https://velaro-messaging-api-staging.azurewebsites.net" to your MCP env config.'
|
|
@@ -1048,6 +1060,7 @@ Key node shapes:
|
|
|
1048
1060
|
propName:"myAnswerVar" } // variable that stores the result as JSON array string
|
|
1049
1061
|
• condition: { type:"action", actionType:"condition", conditionOptions:[...] }
|
|
1050
1062
|
• transferToAI: { type:"action", actionType:"transferToAI", aiConfigId:N }
|
|
1063
|
+
AI follow-up suggestions ("AI mode"): add suggestFollowUps:true|false (omit = use the account default, set via ai_followups_site_set), suggestFollowUpCount:2-4, suggestFollowUpInstruction:"<=300 chars". After each web-chat bot reply the bot offers short tappable follow-up replies; typing stays available.
|
|
1051
1064
|
|
|
1052
1065
|
For multiSelect dynamic options: the variable named in dynamicOptionsVariable must contain a JSON array like [{"label":"Red","value":"red"},{"label":"Blue","value":"blue"}]. Populate it via an HTTP Request node before the ask node.`,
|
|
1053
1066
|
inputSchema: {
|
|
@@ -1061,6 +1074,20 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
1061
1074
|
},
|
|
1062
1075
|
},
|
|
1063
1076
|
|
|
1077
|
+
{
|
|
1078
|
+
name: 'ai_followups_site_get',
|
|
1079
|
+
description: "Read this account's default for AI follow-up suggestions (the AI Chatbot node's \"AI mode\": after each web-chat bot reply the bot offers 2-4 short tappable follow-up replies). A node's own suggestFollowUps setting wins over this default; platform default is off.",
|
|
1080
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
name: 'ai_followups_site_set',
|
|
1084
|
+
description: "Set this account's default for AI follow-up suggestions. enabled=true/false, or omit/null to clear (platform default: off). Per-node settings (workflow_update_node: suggestFollowUps) override this. Returns the value read back from the server.",
|
|
1085
|
+
inputSchema: {
|
|
1086
|
+
type: 'object',
|
|
1087
|
+
properties: { enabled: { type: ['boolean', 'null'], description: 'true = on, false = off, null/omitted = clear' } },
|
|
1088
|
+
required: [],
|
|
1089
|
+
},
|
|
1090
|
+
},
|
|
1064
1091
|
// ── Surveys ──────────────────────────────────────────────────────────────
|
|
1065
1092
|
{
|
|
1066
1093
|
name: 'survey_list',
|
|
@@ -1594,10 +1621,28 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
1594
1621
|
},
|
|
1595
1622
|
buttonLabel: { type: 'string', description: 'Optional action button label' },
|
|
1596
1623
|
buttonUrl: { type: 'string', description: 'Optional action button URL (required if buttonLabel is set)' },
|
|
1624
|
+
mentions: { type: 'array', description: 'People to @mention (real Teams notification). Velaro staff only. Uses the ops Teams sender instead of a webhook.', items: { type: 'object', properties: { user: { type: 'string', description: 'Entra object id or UPN, e.g. andrea@velaro.com' }, name: { type: 'string', description: 'Display name shown in the @mention' } }, required: ['user'] } },
|
|
1625
|
+
destination: { type: 'string', description: 'Named channel in the DevOps team instead of General: prs, workflows, deploys, alerts, qa, general. Velaro staff only; uses the ops Teams sender instead of a webhook.' },
|
|
1597
1626
|
},
|
|
1598
1627
|
required: ['title'],
|
|
1599
1628
|
},
|
|
1600
1629
|
},
|
|
1630
|
+
{
|
|
1631
|
+
name: 'teams_send_direct',
|
|
1632
|
+
description: 'Send a 1:1 Teams message (Adaptive Card) directly to any user in the Velaro tenant by UPN/email or Entra object id. Velaro staff only. Graph chat first, Velaro Bot proactive fallback.',
|
|
1633
|
+
inputSchema: {
|
|
1634
|
+
type: 'object',
|
|
1635
|
+
properties: {
|
|
1636
|
+
user: { type: 'string', description: 'UPN/email (alex@velaro.com) or Entra object id' },
|
|
1637
|
+
title: { type: 'string', description: 'Card title' },
|
|
1638
|
+
message: { type: 'string', description: 'Message text' },
|
|
1639
|
+
fields: { type: 'array', description: 'Optional fact rows', items: { type: 'object', properties: { label: { type: 'string' }, value: { type: 'string' } }, required: ['label', 'value'] } },
|
|
1640
|
+
buttonLabel: { type: 'string', description: 'Optional button label' },
|
|
1641
|
+
buttonUrl: { type: 'string', description: 'Optional button URL' },
|
|
1642
|
+
},
|
|
1643
|
+
required: ['user', 'title'],
|
|
1644
|
+
},
|
|
1645
|
+
},
|
|
1601
1646
|
|
|
1602
1647
|
// ── Slack ─────────────────────────────────────────────────────────────────
|
|
1603
1648
|
{
|
|
@@ -1682,6 +1727,8 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
1682
1727
|
v20_site_id: { type: 'number', description: 'V20 site ID to migrate TO (defaults to the authenticated site)' },
|
|
1683
1728
|
dry_run: { type: 'boolean', description: 'Preview what would be created without creating. Default: false.' },
|
|
1684
1729
|
enable: { type: 'boolean', description: 'Enable created workflows immediately. Default: false.' },
|
|
1730
|
+
include_non_routing: { type: 'boolean', description: 'Also migrate v10 rules with IsRouting=false (group-assignment rules). Default: false.' },
|
|
1731
|
+
route_bot_groups_to_team: { type: 'boolean', description: 'Route to the v20 team named after the v10 group even if it had a v10 bot. Default: false.' },
|
|
1685
1732
|
team_mappings: { type: 'array', description: 'Override auto-resolution: [{v10GroupId, v20TeamId}]', items: { type: 'object' } },
|
|
1686
1733
|
ai_config_mappings: { type: 'array', description: 'Override auto-resolution: [{v10GroupId, v20AiConfigId}]', items: { type: 'object' } },
|
|
1687
1734
|
},
|
|
@@ -1696,10 +1743,25 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
1696
1743
|
properties: {
|
|
1697
1744
|
v10_site_id: { type: 'number', description: 'V10 (Livefluence) site ID' },
|
|
1698
1745
|
v20_site_id: { type: 'number', description: 'V20 site ID to match against (for name suggestions)' },
|
|
1746
|
+
include_non_routing: { type: 'boolean', description: 'Also include v10 rules with IsRouting=false. Default: false.' },
|
|
1699
1747
|
},
|
|
1700
1748
|
required: ['v10_site_id'],
|
|
1701
1749
|
},
|
|
1702
1750
|
},
|
|
1751
|
+
{
|
|
1752
|
+
name: 'livefluence_create_teams',
|
|
1753
|
+
description: 'Copy v10 (Livefluence) groups to v20 teams on a site. v10 is read-only; idempotent by name; "Default Group" maps to the site default team; members are not copied. Use dry_run=true first.',
|
|
1754
|
+
inputSchema: {
|
|
1755
|
+
type: 'object',
|
|
1756
|
+
properties: {
|
|
1757
|
+
v10_site_id: { type: 'number', description: 'V10 (Livefluence) site ID to copy FROM' },
|
|
1758
|
+
v20_site_id: { type: 'number', description: 'V20 site ID to create teams on' },
|
|
1759
|
+
group_names: { type: 'array', items: { type: 'string' }, description: 'Group names to copy (default: every non-deleted v10 group)' },
|
|
1760
|
+
dry_run: { type: 'boolean', description: 'Preview without creating. Default: false.' },
|
|
1761
|
+
},
|
|
1762
|
+
required: ['v10_site_id', 'v20_site_id'],
|
|
1763
|
+
},
|
|
1764
|
+
},
|
|
1703
1765
|
|
|
1704
1766
|
// ── Smart Routing Engine ──────────────────────────────────────────────────
|
|
1705
1767
|
{
|
|
@@ -3057,7 +3119,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3057
3119
|
inputSchema: {
|
|
3058
3120
|
type: 'object',
|
|
3059
3121
|
properties: {
|
|
3060
|
-
report: { type: 'string', enum: ['program_breakdown', 'agent_scorecard', 'qa_coverage', 'coaching_queue', 'sentiment_breakdown', 'topic_radar', 'weekly_brief', 'weekly_brief_send_test'] },
|
|
3122
|
+
report: { type: 'string', enum: ['program_breakdown', 'agent_scorecard', 'qa_coverage', 'coaching_queue', 'sentiment_breakdown', 'topic_radar', 'weekly_brief', 'weekly_brief_send_test', 'repeat_contact', 'demand_coverage', 'bot_containment', 'effort_index', 'promise_ledger', 'promise_dismiss'] },
|
|
3061
3123
|
from: { type: 'string', description: 'yyyy-MM-dd start (default: 30 days ago).' },
|
|
3062
3124
|
to: { type: 'string', description: 'yyyy-MM-dd end, inclusive (default: today).' },
|
|
3063
3125
|
groupBy: { type: 'string', description: 'program_breakdown: website|team|platform|device|entrypage. sentiment_breakdown: website|team|agent|theme.' },
|
|
@@ -3067,6 +3129,14 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3067
3129
|
perAgent: { type: 'number', description: 'coaching_queue: chats listed per agent (default 5).' },
|
|
3068
3130
|
limit: { type: 'number', description: 'topic_radar: max topics (default 25).' },
|
|
3069
3131
|
days: { type: 'number', description: 'weekly_brief: days to cover (default 7).' },
|
|
3132
|
+
windowDays: { type: 'number', description: 'repeat_contact: days a contact has to come back to count as a repeat (1-30, default 7).' },
|
|
3133
|
+
weeks: { type: 'number', description: 'demand_coverage: whole local weeks of history (1-12, default 6). from/to are IGNORED by demand_coverage.' },
|
|
3134
|
+
handleMinutes: { type: 'number', description: 'demand_coverage: assumed minutes per chat (1-120, default 10).' },
|
|
3135
|
+
maxConcurrent: { type: 'number', description: 'demand_coverage: assumed chats an agent handles at once (1-10, default 3).' },
|
|
3136
|
+
timeZone: { type: 'string', description: 'demand_coverage: IANA time zone (default America/Los_Angeles).' },
|
|
3137
|
+
lowScore: { type: 'number', description: 'bot_containment: QA score below which a handed-off chat counts as regret (1-100, default 60).' },
|
|
3138
|
+
includeDemo: { type: 'boolean', description: 'true = show only the seeded sample (demo) data. Default false.' },
|
|
3139
|
+
promiseId: { type: 'string', description: 'promise_dismiss: the promise id to dismiss (managers only).' },
|
|
3070
3140
|
},
|
|
3071
3141
|
required: ['report'],
|
|
3072
3142
|
},
|
|
@@ -3287,19 +3357,24 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3287
3357
|
},
|
|
3288
3358
|
{
|
|
3289
3359
|
name: 'avatar_get_group_effective',
|
|
3290
|
-
description: 'Show the effective avatar settings for a group
|
|
3291
|
-
inputSchema: { type: 'object', properties: { groupId: { type: 'number' } }, required: ['groupId'] },
|
|
3360
|
+
description: 'Show the effective avatar settings (person, personality, voice, display name, backdrop) for a group, layered perWorkflow ?? perGroup ?? perSite. Pass workflowId too to see what a conversation in that workflow inside that group gets. Also returns whether each override exists.',
|
|
3361
|
+
inputSchema: { type: 'object', properties: { groupId: { type: 'number', description: 'Team (group) id.' }, workflowId: { type: 'number', description: 'Optional workflow id to layer on top of the group.' } }, required: ['groupId'] },
|
|
3292
3362
|
},
|
|
3293
3363
|
{
|
|
3294
3364
|
name: 'avatar_set_group_override',
|
|
3295
|
-
description: 'Override avatar/personality/
|
|
3365
|
+
description: 'Override the avatar person/personality/voice/display name/backdrop for one group (a group is a Team). Only the fields you pass change (merge); pass an empty string to clear one field back to the site default. A library avatarId always uses the velaro provider. Backdrop: backgroundMode none|color|gradient|image with backgroundValue (hex, "#a,#b", or an https image URL).',
|
|
3296
3366
|
inputSchema: {
|
|
3297
3367
|
type: 'object',
|
|
3298
3368
|
properties: {
|
|
3299
3369
|
groupId: { type: 'number' },
|
|
3300
3370
|
providerSlug: { type: 'string' },
|
|
3301
|
-
avatarId: { type: 'string' },
|
|
3371
|
+
avatarId: { type: 'string', description: 'Library slug (see avatar_list_library), e.g. priya.' },
|
|
3302
3372
|
personalityPresetSlug: { type: 'string' },
|
|
3373
|
+
voiceId: { type: 'string', description: 'ElevenLabs voice id.' },
|
|
3374
|
+
displayName: { type: 'string', description: 'Visitor-facing avatar name.' },
|
|
3375
|
+
backgroundMode: { type: 'string', description: 'none | color | gradient | image. "none" = explicitly no backdrop.' },
|
|
3376
|
+
backgroundValue: { type: 'string', description: 'Hex color, "#a,#b" gradient, or https image URL.' },
|
|
3377
|
+
replace: { type: 'boolean', description: 'true = replace the whole override (omitted fields inherit) instead of merging.' },
|
|
3303
3378
|
},
|
|
3304
3379
|
required: ['groupId'],
|
|
3305
3380
|
},
|
|
@@ -3309,9 +3384,38 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3309
3384
|
description: 'Reset one group to the site default avatar settings (removes its override).',
|
|
3310
3385
|
inputSchema: { type: 'object', properties: { groupId: { type: 'number' } }, required: ['groupId'] },
|
|
3311
3386
|
},
|
|
3387
|
+
{
|
|
3388
|
+
name: 'avatar_get_workflow_effective',
|
|
3389
|
+
description: 'Show the effective avatar settings for a workflow (workflow override, else its group, else site). Pass groupId to include a group override beneath the workflow.',
|
|
3390
|
+
inputSchema: { type: 'object', properties: { workflowId: { type: 'number' }, groupId: { type: 'number' } }, required: ['workflowId'] },
|
|
3391
|
+
},
|
|
3392
|
+
{
|
|
3393
|
+
name: 'avatar_set_workflow_override',
|
|
3394
|
+
description: 'Override the avatar person/personality/voice/display name/backdrop for one workflow (wins over its group and the site). Same fields and merge semantics as avatar_set_group_override. Example: library avatar priya with the Timken HR office backdrop https://admin.velaro.com/avatar-backdrops/timken-hr-office.png for the recruiter workflow.',
|
|
3395
|
+
inputSchema: {
|
|
3396
|
+
type: 'object',
|
|
3397
|
+
properties: {
|
|
3398
|
+
workflowId: { type: 'number' },
|
|
3399
|
+
providerSlug: { type: 'string' },
|
|
3400
|
+
avatarId: { type: 'string' },
|
|
3401
|
+
personalityPresetSlug: { type: 'string' },
|
|
3402
|
+
voiceId: { type: 'string' },
|
|
3403
|
+
displayName: { type: 'string' },
|
|
3404
|
+
backgroundMode: { type: 'string', description: 'none | color | gradient | image' },
|
|
3405
|
+
backgroundValue: { type: 'string' },
|
|
3406
|
+
replace: { type: 'boolean' },
|
|
3407
|
+
},
|
|
3408
|
+
required: ['workflowId'],
|
|
3409
|
+
},
|
|
3410
|
+
},
|
|
3411
|
+
{
|
|
3412
|
+
name: 'avatar_reset_workflow',
|
|
3413
|
+
description: 'Reset one workflow to its group / site default avatar settings (removes its override).',
|
|
3414
|
+
inputSchema: { type: 'object', properties: { workflowId: { type: 'number' } }, required: ['workflowId'] },
|
|
3415
|
+
},
|
|
3312
3416
|
{
|
|
3313
3417
|
name: 'avatar_reset_all_overrides',
|
|
3314
|
-
description: 'Reset ALL group overrides for the calling site back to the site default.',
|
|
3418
|
+
description: 'Reset ALL group and workflow overrides for the calling site back to the site default.',
|
|
3315
3419
|
inputSchema: { type: 'object', properties: {} },
|
|
3316
3420
|
},
|
|
3317
3421
|
|
|
@@ -3750,6 +3854,15 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3750
3854
|
required: [],
|
|
3751
3855
|
},
|
|
3752
3856
|
},
|
|
3857
|
+
{
|
|
3858
|
+
name: 'ai_federation',
|
|
3859
|
+
description: 'SuperAdmin: Anthropic Workload Identity Federation for the AI gateway. action=status shows enabled, circuit breaker state, last success/failure reason, federated vs API-key fallback counts and token expiry. action=test does a real token exchange plus a 1-token Haiku message and reports which path (federated, apikey, apikey-fallback) served it. action=reset_breaker closes the breaker so the next call retries federation.',
|
|
3860
|
+
inputSchema: {
|
|
3861
|
+
type: 'object',
|
|
3862
|
+
properties: { action: { type: 'string', enum: ['status', 'test', 'reset_breaker'], description: 'Default status.' } },
|
|
3863
|
+
required: [],
|
|
3864
|
+
},
|
|
3865
|
+
},
|
|
3753
3866
|
{
|
|
3754
3867
|
name: 'status_incident_list',
|
|
3755
3868
|
description: 'Velaro staff only (SuperAdmin). Lists public status page incidents, including component overrides (IncidentType "Override").',
|
|
@@ -4998,6 +5111,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
4998
5111
|
properties: {
|
|
4999
5112
|
publicApplyEnabled: { type: 'boolean' },
|
|
5000
5113
|
autoCreateCaseTypeId: { type: 'number', description: 'Case type id; 0 clears the auto-create link' },
|
|
5114
|
+
defaultListingDays: { type: 'number', description: '1-365: how many days a new listing stays live when its feed gives no expiry. Requires the Job Board lifecycle package.' },
|
|
5001
5115
|
},
|
|
5002
5116
|
required: [],
|
|
5003
5117
|
},
|
|
@@ -5103,6 +5217,68 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
5103
5217
|
required: ['sourceId', 'title'],
|
|
5104
5218
|
},
|
|
5105
5219
|
},
|
|
5220
|
+
// -- Job Board listing lifecycle (package "job-board-lifecycle", default OFF: 403 for a site without it) --
|
|
5221
|
+
{
|
|
5222
|
+
name: 'jobboard_lifecycle_status',
|
|
5223
|
+
description: 'Does this site have the Job Board listing lifecycle package (close/renew, expiry, JSON-LD listing pages), and what is its default listing duration? Returns { entitled, defaultListingDays }.',
|
|
5224
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5225
|
+
},
|
|
5226
|
+
{
|
|
5227
|
+
name: 'jobboard_close_listing',
|
|
5228
|
+
description: 'Close a job listing: it stops appearing in search and the public board, the public listing page answers 410, and applying to it answers 410. Idempotent. Requires the Job Board lifecycle package (403 otherwise); 404 for an id that is not on this site.',
|
|
5229
|
+
inputSchema: { type: 'object', properties: { listingId: { type: 'number' } }, required: ['listingId'] },
|
|
5230
|
+
},
|
|
5231
|
+
{
|
|
5232
|
+
name: 'jobboard_renew_listing',
|
|
5233
|
+
description: 'Renew a job listing: extends its expiry by `days` (default: the site\'s default listing days) from the later of its current expiry and now, increments its renewal count and republishes it if it was closed or expired. Requires the Job Board lifecycle package (403 otherwise).',
|
|
5234
|
+
inputSchema: {
|
|
5235
|
+
type: 'object',
|
|
5236
|
+
properties: {
|
|
5237
|
+
listingId: { type: 'number' },
|
|
5238
|
+
days: { type: 'number', description: '1-365; omit for the site default' },
|
|
5239
|
+
},
|
|
5240
|
+
required: ['listingId'],
|
|
5241
|
+
},
|
|
5242
|
+
},
|
|
5243
|
+
// -- Job Board hosted pages (package "job-board-hosted", default OFF: 403 for a site without it) --
|
|
5244
|
+
{
|
|
5245
|
+
name: 'jobboard_hosted_status',
|
|
5246
|
+
description: 'Hosted public job pages for this site: is the package included, are the pages published, and the opaque board link, sitemap, Indeed feed and Google feed URLs (the feed URLs contain a secret key). Returns { entitled, enabled, boardUrl, sitemapUrl, indeedFeedUrl, googleFeedUrl, publicApplyEnabled, eeocSelfIdAvailable, liveListings }; { entitled:false } without the package.',
|
|
5247
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5248
|
+
},
|
|
5249
|
+
{
|
|
5250
|
+
name: 'jobboard_hosted_set',
|
|
5251
|
+
description: 'Publish or unpublish the hosted job pages (apply-if-present). enabled=true issues the opaque board link and feed key the first time. regenerateLink=true issues a NEW board link and every old hosted URL answers 404 (administrators only). Requires the Job Board hosted pages package (403 otherwise).',
|
|
5252
|
+
inputSchema: {
|
|
5253
|
+
type: 'object',
|
|
5254
|
+
properties: {
|
|
5255
|
+
enabled: { type: 'boolean' },
|
|
5256
|
+
regenerateLink: { type: 'boolean', description: 'Retire the current board link and issue a new one (administrators only)' },
|
|
5257
|
+
},
|
|
5258
|
+
required: [],
|
|
5259
|
+
},
|
|
5260
|
+
},
|
|
5261
|
+
{
|
|
5262
|
+
name: 'jobboard_rotate_feed_key',
|
|
5263
|
+
description: 'Rotate the outbound feed key: the current Indeed and Google feed URLs stop working immediately and the response carries the new ones. Administrators only; requires the Job Board hosted pages package (403 otherwise).',
|
|
5264
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5265
|
+
},
|
|
5266
|
+
{
|
|
5267
|
+
name: 'jobboard_set_listing_pay',
|
|
5268
|
+
description: 'Set or clear the pay range of a job listing (pay transparency): shown on the hosted page and in its schema.org baseSalary. A manual value is never overwritten by a feed sync. Pass clear=true to remove it. Requires the Job Board hosted pages package (403 otherwise); 404 for an id that is not on this site.',
|
|
5269
|
+
inputSchema: {
|
|
5270
|
+
type: 'object',
|
|
5271
|
+
properties: {
|
|
5272
|
+
listingId: { type: 'number' },
|
|
5273
|
+
min: { type: 'number', description: 'Minimum (or only) amount, greater than 0' },
|
|
5274
|
+
max: { type: 'number', description: 'Maximum amount, at least min (optional)' },
|
|
5275
|
+
currency: { type: 'string', description: '3-letter ISO code, default USD' },
|
|
5276
|
+
period: { type: 'string', description: 'Hour | Day | Week | Month | Year' },
|
|
5277
|
+
clear: { type: 'boolean', description: 'Remove the stated pay' },
|
|
5278
|
+
},
|
|
5279
|
+
required: ['listingId'],
|
|
5280
|
+
},
|
|
5281
|
+
},
|
|
5106
5282
|
{
|
|
5107
5283
|
name: 'jobboard_move_application_stage',
|
|
5108
5284
|
description: 'Move a candidate application to a new HR/agency-side pipeline stage on the applicant Kanban board (Applied, Screened, Interview, Offer, Hired, Rejected).',
|
|
@@ -5994,6 +6170,17 @@ async function handleTool(name, args) {
|
|
|
5994
6170
|
// [Authorize] does not accept vel_live_* service keys (only a handful of controllers opt
|
|
5995
6171
|
// into that manually) — these tools need VELARO_JWT, same requirement as the existing
|
|
5996
6172
|
// notifications_get/ai_config_staff_get tools they're modeled on.
|
|
6173
|
+
case 'ai_followups_site_get': {
|
|
6174
|
+
const r = await messagingGet('/SiteSettings/AiFollowUps');
|
|
6175
|
+
return JSON.stringify({ enabled: r?.enabled ?? null }, null, 2);
|
|
6176
|
+
}
|
|
6177
|
+
|
|
6178
|
+
case 'ai_followups_site_set': {
|
|
6179
|
+
await messagingPut('/SiteSettings/AiFollowUps', { enabled: typeof args.enabled === 'boolean' ? args.enabled : null });
|
|
6180
|
+
const r = await messagingGet('/SiteSettings/AiFollowUps');
|
|
6181
|
+
return JSON.stringify({ enabled: r?.enabled ?? null }, null, 2);
|
|
6182
|
+
}
|
|
6183
|
+
|
|
5997
6184
|
case 'survey_list': {
|
|
5998
6185
|
const surveys = (await messagingGet('/Surveys/list')) ?? [];
|
|
5999
6186
|
if (!surveys.length) return 'No surveys found.';
|
|
@@ -6492,7 +6679,26 @@ async function handleTool(name, args) {
|
|
|
6492
6679
|
return await api('PUT', '/TeamsBot/ChannelConfig', body);
|
|
6493
6680
|
}
|
|
6494
6681
|
|
|
6682
|
+
case 'teams_send_direct': {
|
|
6683
|
+
const r = await api('POST', '/TeamsMessaging/Direct', {
|
|
6684
|
+
user: args.user, title: args.title, message: args.message || '',
|
|
6685
|
+
facts: (args.fields || []).map(f => ({ label: f.label, value: f.value })),
|
|
6686
|
+
actionUrl: args.buttonUrl, actionLabel: args.buttonLabel,
|
|
6687
|
+
});
|
|
6688
|
+
return r?.sent ? `Direct message sent to ${args.user} via ${r.path}.` : `Send failed: ${r?.error || 'unknown error'} ${(r?.warnings || []).join('; ')}`;
|
|
6689
|
+
}
|
|
6690
|
+
|
|
6495
6691
|
case 'teams_notify_card': {
|
|
6692
|
+
if ((args.mentions && args.mentions.length) || args.destination) {
|
|
6693
|
+
const r = await api('POST', '/TeamsMessaging/Card', {
|
|
6694
|
+
title: args.title,
|
|
6695
|
+
facts: (args.fields || []).map(f => ({ label: f.label, value: f.value })),
|
|
6696
|
+
actionUrl: args.buttonUrl, actionLabel: args.buttonLabel,
|
|
6697
|
+
destination: args.destination,
|
|
6698
|
+
mentions: (args.mentions || []).map(m => ({ user: m.user, name: m.name })),
|
|
6699
|
+
});
|
|
6700
|
+
return r?.sent ? `Card sent via ${r.path}${r.warnings?.length ? ' (' + r.warnings.join('; ') + ')' : ''}.` : `Send failed: ${r?.error || 'unknown error'}`;
|
|
6701
|
+
}
|
|
6496
6702
|
const webhooks = await api('GET', '/TeamsNotification');
|
|
6497
6703
|
if (!Array.isArray(webhooks) || !webhooks.length)
|
|
6498
6704
|
return 'No Teams webhooks configured. Go to Integrations → Microsoft Teams to add one, or call velaro teams create <name> <webhookUrl> via CLI.';
|
|
@@ -6820,7 +7026,10 @@ async function handleTool(name, args) {
|
|
|
6820
7026
|
|
|
6821
7027
|
// ── Livefluence Migration ──────────────────────────────────────────────
|
|
6822
7028
|
case 'livefluence_export': {
|
|
6823
|
-
const
|
|
7029
|
+
const params = [];
|
|
7030
|
+
if (args.v20_site_id) params.push(`v20SiteId=${args.v20_site_id}`);
|
|
7031
|
+
if (args.include_non_routing) params.push('includeNonRouting=true');
|
|
7032
|
+
const qs = params.length ? `?${params.join('&')}` : '';
|
|
6824
7033
|
const data = await api('GET', `/DatabaseTool/V10Export/${args.v10_site_id}${qs}`);
|
|
6825
7034
|
const lines = [
|
|
6826
7035
|
`V10 Site ${data.v10SiteId} — ${data.ruleCount} routing rule(s), ${data.groupCount} group(s)`,
|
|
@@ -6845,6 +7054,16 @@ async function handleTool(name, args) {
|
|
|
6845
7054
|
return lines.join('\n');
|
|
6846
7055
|
}
|
|
6847
7056
|
|
|
7057
|
+
case 'livefluence_create_teams': {
|
|
7058
|
+
const result = await api('POST', '/DatabaseTool/V10CreateTeams', {
|
|
7059
|
+
v10SiteId: args.v10_site_id,
|
|
7060
|
+
v20SiteId: args.v20_site_id,
|
|
7061
|
+
dryRun: args.dry_run ?? false,
|
|
7062
|
+
groupNames: args.group_names ?? [],
|
|
7063
|
+
});
|
|
7064
|
+
return JSON.stringify(result, null, 2);
|
|
7065
|
+
}
|
|
7066
|
+
|
|
6848
7067
|
case 'livefluence_seed_url_rules': {
|
|
6849
7068
|
const result = await api('POST', '/DatabaseTool/V10SeedUrlRules', {
|
|
6850
7069
|
siteId: args.site_id, aiConfigId: args.ai_config_id, dryRun: !!args.dry_run, rules: args.rules,
|
|
@@ -6858,6 +7077,8 @@ async function handleTool(name, args) {
|
|
|
6858
7077
|
v20SiteId: args.v20_site_id ?? null,
|
|
6859
7078
|
dryRun: args.dry_run ?? false,
|
|
6860
7079
|
enable: args.enable ?? false,
|
|
7080
|
+
includeNonRouting: args.include_non_routing ?? false,
|
|
7081
|
+
routeBotGroupsToTeam: args.route_bot_groups_to_team ?? false,
|
|
6861
7082
|
teamMappings: args.team_mappings ?? [],
|
|
6862
7083
|
aiConfigMappings: args.ai_config_mappings ?? [],
|
|
6863
7084
|
});
|
|
@@ -6873,6 +7094,7 @@ async function handleTool(name, args) {
|
|
|
6873
7094
|
for (const item of result.createdItems) {
|
|
6874
7095
|
lines.push(` [#${item.priority}] ${item.ruleName}${item.workflowId ? ` → id=${item.workflowId}` : ''}`);
|
|
6875
7096
|
lines.push(` Action: ${item.action}${item.v2AiConfigId ? `, aiConfigId=${item.v2AiConfigId}` : ''}${item.v2TeamId ? `, teamId=${item.v2TeamId}` : ''}`);
|
|
7097
|
+
for (const u of item.unconverted ?? []) lines.push(` not converted: ${u}`);
|
|
6876
7098
|
}
|
|
6877
7099
|
lines.push('');
|
|
6878
7100
|
}
|
|
@@ -8485,12 +8707,18 @@ async function handleTool(name, args) {
|
|
|
8485
8707
|
program_breakdown: 'program-breakdown', agent_scorecard: 'agent-scorecard', qa_coverage: 'qa-coverage',
|
|
8486
8708
|
coaching_queue: 'coaching-queue', sentiment_breakdown: 'sentiment-breakdown', topic_radar: 'topic-radar',
|
|
8487
8709
|
weekly_brief: 'weekly-brief', weekly_brief_send_test: 'weekly-brief/send-test',
|
|
8710
|
+
repeat_contact: 'repeat-contact', demand_coverage: 'demand-coverage', bot_containment: 'bot-containment',
|
|
8711
|
+
effort_index: 'effort-index', promise_ledger: 'promise-ledger',
|
|
8488
8712
|
};
|
|
8713
|
+
if (args.report === 'promise_dismiss') {
|
|
8714
|
+
if (!args.promiseId) throw new Error('promiseId is required for promise_dismiss');
|
|
8715
|
+
return JSON.stringify(await messagingPost('/api/enterprise-reports/promise-ledger/' + encodeURIComponent(String(args.promiseId)) + '/dismiss', {}), null, 2);
|
|
8716
|
+
}
|
|
8489
8717
|
const seg = ER_PATHS[args.report];
|
|
8490
8718
|
if (!seg) throw new Error('report must be one of: ' + Object.keys(ER_PATHS).join(', '));
|
|
8491
8719
|
if (args.report === 'weekly_brief_send_test') return JSON.stringify(await messagingPost('/api/enterprise-reports/' + seg, {}), null, 2);
|
|
8492
8720
|
const qs = new URLSearchParams();
|
|
8493
|
-
for (const k of ['from', 'to', 'groupBy', 'deploymentId', 'teamId', 'threshold', 'perAgent', 'limit', 'days']) {
|
|
8721
|
+
for (const k of ['from', 'to', 'groupBy', 'deploymentId', 'teamId', 'threshold', 'perAgent', 'limit', 'days', 'windowDays', 'weeks', 'handleMinutes', 'maxConcurrent', 'timeZone', 'lowScore', 'includeDemo']) {
|
|
8494
8722
|
if (args[k] != null && args[k] !== '') qs.set(k, String(args[k]));
|
|
8495
8723
|
}
|
|
8496
8724
|
const q = qs.toString();
|
|
@@ -8661,12 +8889,17 @@ async function handleTool(name, args) {
|
|
|
8661
8889
|
return JSON.stringify(await messagingRequest('POST', '/avatar/site-default', {
|
|
8662
8890
|
providerSlug: args.providerSlug, avatarId: args.avatarId, personalityPresetSlug: args.personalityPresetSlug }), null, 2);
|
|
8663
8891
|
case 'avatar_get_group_effective':
|
|
8664
|
-
return JSON.stringify(await messagingRequest('GET', `/avatar/groups/${Number(args.groupId)}/effective`), null, 2);
|
|
8892
|
+
return JSON.stringify(await messagingRequest('GET', `/avatar/groups/${Number(args.groupId)}/effective${args.workflowId != null ? `?workflowId=${Number(args.workflowId)}` : ''}`), null, 2);
|
|
8665
8893
|
case 'avatar_set_group_override':
|
|
8666
|
-
return JSON.stringify(await messagingRequest('PUT', `/avatar/groups/${Number(args.groupId)}/override`,
|
|
8667
|
-
providerSlug: args.providerSlug, avatarId: args.avatarId, personalityPresetSlug: args.personalityPresetSlug }), null, 2);
|
|
8894
|
+
return JSON.stringify(await messagingRequest('PUT', `/avatar/groups/${Number(args.groupId)}/override`, avatarOverrideBody(args)), null, 2);
|
|
8668
8895
|
case 'avatar_reset_group':
|
|
8669
8896
|
return JSON.stringify(await messagingRequest('DELETE', `/avatar/groups/${Number(args.groupId)}/override`), null, 2);
|
|
8897
|
+
case 'avatar_get_workflow_effective':
|
|
8898
|
+
return JSON.stringify(await messagingRequest('GET', `/avatar/workflows/${Number(args.workflowId)}/effective${args.groupId != null ? `?groupId=${Number(args.groupId)}` : ''}`), null, 2);
|
|
8899
|
+
case 'avatar_set_workflow_override':
|
|
8900
|
+
return JSON.stringify(await messagingRequest('PUT', `/avatar/workflows/${Number(args.workflowId)}/override`, avatarOverrideBody(args)), null, 2);
|
|
8901
|
+
case 'avatar_reset_workflow':
|
|
8902
|
+
return JSON.stringify(await messagingRequest('DELETE', `/avatar/workflows/${Number(args.workflowId)}/override`), null, 2);
|
|
8670
8903
|
case 'avatar_reset_all_overrides':
|
|
8671
8904
|
return JSON.stringify(await messagingRequest('POST', '/avatar/group-overrides/reset-all', {}), null, 2);
|
|
8672
8905
|
|
|
@@ -8908,6 +9141,13 @@ async function handleTool(name, args) {
|
|
|
8908
9141
|
return JSON.stringify(data, null, 2);
|
|
8909
9142
|
}
|
|
8910
9143
|
|
|
9144
|
+
case 'ai_federation': {
|
|
9145
|
+
const action = args.action || 'status';
|
|
9146
|
+
if (action === 'test') return JSON.stringify(await api('POST', '/AiFederation/test', {}), null, 2);
|
|
9147
|
+
if (action === 'reset_breaker') return JSON.stringify(await api('POST', '/AiFederation/reset-breaker', {}), null, 2);
|
|
9148
|
+
return JSON.stringify(await api('GET', '/AiFederation/status'), null, 2);
|
|
9149
|
+
}
|
|
9150
|
+
|
|
8911
9151
|
case 'status_incident_list': {
|
|
8912
9152
|
const rows = await messagingRequest('GET', `/superadmin/status-incidents?filter=${encodeURIComponent(args.filter || 'active')}`);
|
|
8913
9153
|
return JSON.stringify(rows, null, 2);
|
|
@@ -10330,7 +10570,12 @@ async function handleTool(name, args) {
|
|
|
10330
10570
|
if (!Number.isInteger(n) || n < 0) throw new Error('autoCreateCaseTypeId must be an integer >= 0 (0 clears).');
|
|
10331
10571
|
body.autoCreateCaseTypeId = n;
|
|
10332
10572
|
}
|
|
10333
|
-
if (
|
|
10573
|
+
if (args.defaultListingDays !== undefined) {
|
|
10574
|
+
const d = Number(args.defaultListingDays);
|
|
10575
|
+
if (!Number.isInteger(d) || d < 1 || d > 365) throw new Error('defaultListingDays must be an integer from 1 to 365.');
|
|
10576
|
+
body.defaultListingDays = d;
|
|
10577
|
+
}
|
|
10578
|
+
if (!Object.keys(body).length) throw new Error('Specify publicApplyEnabled, autoCreateCaseTypeId and/or defaultListingDays.');
|
|
10334
10579
|
const r = await messagingPut('/Recruiting/ApplySettings', body);
|
|
10335
10580
|
return JSON.stringify(r, null, 2);
|
|
10336
10581
|
}
|
|
@@ -10367,6 +10612,71 @@ async function handleTool(name, args) {
|
|
|
10367
10612
|
}, null, 2);
|
|
10368
10613
|
}
|
|
10369
10614
|
|
|
10615
|
+
case 'jobboard_lifecycle_status': {
|
|
10616
|
+
const r = await messagingGet('/Recruiting/Lifecycle');
|
|
10617
|
+
return JSON.stringify(r, null, 2);
|
|
10618
|
+
}
|
|
10619
|
+
case 'jobboard_close_listing': {
|
|
10620
|
+
const id = Number(args.listingId);
|
|
10621
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('listingId must be a positive integer.');
|
|
10622
|
+
const r = await messagingPost(`/Recruiting/Jobs/${id}/close`, {});
|
|
10623
|
+
return JSON.stringify(r, null, 2);
|
|
10624
|
+
}
|
|
10625
|
+
case 'jobboard_renew_listing': {
|
|
10626
|
+
const id = Number(args.listingId);
|
|
10627
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('listingId must be a positive integer.');
|
|
10628
|
+
const body = {};
|
|
10629
|
+
if (args.days !== undefined) {
|
|
10630
|
+
const d = Number(args.days);
|
|
10631
|
+
if (!Number.isInteger(d) || d < 1 || d > 365) throw new Error('days must be an integer from 1 to 365.');
|
|
10632
|
+
body.days = d;
|
|
10633
|
+
}
|
|
10634
|
+
const r = await messagingPost(`/Recruiting/Jobs/${id}/renew`, body);
|
|
10635
|
+
return JSON.stringify(r, null, 2);
|
|
10636
|
+
}
|
|
10637
|
+
|
|
10638
|
+
case 'jobboard_hosted_status': {
|
|
10639
|
+
const r = await messagingGet('/Recruiting/Hosted');
|
|
10640
|
+
return JSON.stringify(r, null, 2);
|
|
10641
|
+
}
|
|
10642
|
+
case 'jobboard_hosted_set': {
|
|
10643
|
+
const body = {};
|
|
10644
|
+
if (args.enabled !== undefined) body.enabled = Boolean(args.enabled);
|
|
10645
|
+
if (args.regenerateLink) body.regenerateSlug = true;
|
|
10646
|
+
if (!Object.keys(body).length) throw new Error('Specify enabled and/or regenerateLink (use jobboard_hosted_status to read).');
|
|
10647
|
+
const r = await messagingPut('/Recruiting/Hosted', body);
|
|
10648
|
+
return JSON.stringify(r, null, 2);
|
|
10649
|
+
}
|
|
10650
|
+
case 'jobboard_rotate_feed_key': {
|
|
10651
|
+
const r = await messagingPost('/Recruiting/Hosted/FeedKey/Rotate', {});
|
|
10652
|
+
return JSON.stringify(r, null, 2);
|
|
10653
|
+
}
|
|
10654
|
+
case 'jobboard_set_listing_pay': {
|
|
10655
|
+
const id = Number(args.listingId);
|
|
10656
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('listingId must be a positive integer.');
|
|
10657
|
+
let body;
|
|
10658
|
+
if (args.clear) {
|
|
10659
|
+
body = { clear: true };
|
|
10660
|
+
} else {
|
|
10661
|
+
const lo = Number(args.min);
|
|
10662
|
+
if (args.min === undefined || !Number.isFinite(lo) || lo <= 0) throw new Error('min must be a number greater than 0 (or pass clear=true).');
|
|
10663
|
+
body = { salaryMin: lo };
|
|
10664
|
+
if (args.max !== undefined) {
|
|
10665
|
+
const hi = Number(args.max);
|
|
10666
|
+
if (!Number.isFinite(hi) || hi < lo) throw new Error('max must be a number greater than or equal to min.');
|
|
10667
|
+
body.salaryMax = hi;
|
|
10668
|
+
}
|
|
10669
|
+
const cur = String(args.currency ?? 'USD').trim().toUpperCase();
|
|
10670
|
+
if (!/^[A-Z]{3}$/.test(cur)) throw new Error('currency must be a 3-letter ISO code such as USD.');
|
|
10671
|
+
body.salaryCurrency = cur;
|
|
10672
|
+
const per = ['Hour', 'Day', 'Week', 'Month', 'Year'].find((p) => p.toLowerCase() === String(args.period ?? '').trim().toLowerCase());
|
|
10673
|
+
if (!per) throw new Error('period must be one of: Hour, Day, Week, Month, Year.');
|
|
10674
|
+
body.salaryPeriod = per;
|
|
10675
|
+
}
|
|
10676
|
+
const r = await messagingPut(`/Recruiting/Jobs/${id}/Pay`, body);
|
|
10677
|
+
return JSON.stringify(r, null, 2);
|
|
10678
|
+
}
|
|
10679
|
+
|
|
10370
10680
|
case 'jobboard_move_application_stage': {
|
|
10371
10681
|
const app = await messagingPut(`/Recruiting/Applications/${args.applicationId}/Stage`, { stage: args.stage });
|
|
10372
10682
|
return JSON.stringify(app, null, 2);
|