@velaro/mcp-server 0.6.96 → 0.6.98
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 +131 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velaro/mcp-server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.98",
|
|
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
|
@@ -160,9 +160,17 @@ function avatarOverrideBody(args) {
|
|
|
160
160
|
for (const k of ['providerSlug', 'avatarId', 'personalityPresetSlug', 'voiceId', 'displayName', 'backgroundMode', 'backgroundValue']) {
|
|
161
161
|
if (args[k] !== undefined) body[k] = args[k];
|
|
162
162
|
}
|
|
163
|
+
if (args.motionLevel !== undefined) body.motionLevel = parseAvatarMotionLevel(args.motionLevel);
|
|
163
164
|
return body;
|
|
164
165
|
}
|
|
165
166
|
|
|
167
|
+
/** Closed vocabulary for avatar body motion; "" resets/inherits. Fails readably instead of a bare 400. */
|
|
168
|
+
function parseAvatarMotionLevel(raw) {
|
|
169
|
+
const level = String(raw).trim().toLowerCase();
|
|
170
|
+
if (!['', 'off', 'subtle', 'natural', 'expressive'].includes(level)) throw new Error('motionLevel must be off, subtle, natural or expressive (empty string resets/inherits).');
|
|
171
|
+
return level;
|
|
172
|
+
}
|
|
173
|
+
|
|
166
174
|
async function messagingRequest(method, path, body) {
|
|
167
175
|
if (!MESSAGING_API) throw new Error(
|
|
168
176
|
'VELARO_MESSAGING_API is not configured. Add "VELARO_MESSAGING_API": "https://velaro-messaging-api-staging.azurewebsites.net" to your MCP env config.'
|
|
@@ -3386,7 +3394,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3386
3394
|
// ── AI Video Avatar management (superadmin) ────────────────────────────────
|
|
3387
3395
|
{
|
|
3388
3396
|
name: 'avatar_get_config',
|
|
3389
|
-
description: 'Get the AI Video Avatar (Simli/D-ID/HeyGen) configuration for any customer site — provider, avatar/face ID, voice ID, enabled state, "call me" option, callback confirm window. Velaro admin only. Never returns provider API keys (Velaro uses its own master keys, never per-site).',
|
|
3397
|
+
description: 'Get the AI Video Avatar (Simli/D-ID/HeyGen) configuration for any customer site — provider, avatar/face ID, voice ID, enabled state, "call me" option, callback confirm window, body motion level and strength. Velaro admin only. Never returns provider API keys (Velaro uses its own master keys, never per-site).',
|
|
3390
3398
|
inputSchema: {
|
|
3391
3399
|
type: 'object',
|
|
3392
3400
|
properties: {
|
|
@@ -3412,6 +3420,8 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3412
3420
|
showBadge: { type: 'boolean', description: 'Show the name badge over the avatar video. Default true.' },
|
|
3413
3421
|
sizePx: { type: 'number', description: 'Max avatar panel width in px (160-640, square). Default 360.' },
|
|
3414
3422
|
allowVisitorCollapse: { type: 'boolean', description: 'Let visitors hide or collapse the avatar panel (up/down arrow and X). Default true.' },
|
|
3423
|
+
motionLevel: { type: 'string', enum: ['', 'off', 'subtle', 'natural', 'expressive'], description: 'Body motion preset layered over the lip-sync (shoulders, breathing, head, brows). Empty string resets to the platform default (natural). Visitors with reduced-motion enabled always get a still avatar.' },
|
|
3424
|
+
motionIntensityPercent: { type: 'integer', minimum: 25, maximum: 150, description: 'Body motion strength, whole percent 25-150 (default 100); multiplies every motion amplitude.' },
|
|
3415
3425
|
maxAvatarMinutesPerMonth: { type: 'number', description: 'Monthly avatar video minutes cap for the site. Whole number 1-100000; never unlimited (0/negative rejected).' },
|
|
3416
3426
|
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.' },
|
|
3417
3427
|
},
|
|
@@ -3461,6 +3471,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3461
3471
|
displayName: { type: 'string', description: 'Visitor-facing avatar name.' },
|
|
3462
3472
|
backgroundMode: { type: 'string', description: 'none | color | gradient | image. "none" = explicitly no backdrop.' },
|
|
3463
3473
|
backgroundValue: { type: 'string', description: 'Hex color, "#a,#b" gradient, or https image URL.' },
|
|
3474
|
+
motionLevel: { type: 'string', enum: ['', 'off', 'subtle', 'natural', 'expressive'], description: 'Body motion preset for this group (perWorkflow ?? perGroup ?? perSite ?? natural). Empty string inherits.' },
|
|
3464
3475
|
replace: { type: 'boolean', description: 'true = replace the whole override (omitted fields inherit) instead of merging.' },
|
|
3465
3476
|
},
|
|
3466
3477
|
required: ['groupId'],
|
|
@@ -3490,6 +3501,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3490
3501
|
displayName: { type: 'string' },
|
|
3491
3502
|
backgroundMode: { type: 'string', description: 'none | color | gradient | image' },
|
|
3492
3503
|
backgroundValue: { type: 'string' },
|
|
3504
|
+
motionLevel: { type: 'string', enum: ['', 'off', 'subtle', 'natural', 'expressive'], description: 'Body motion preset for this workflow. Empty string inherits.' },
|
|
3493
3505
|
replace: { type: 'boolean' },
|
|
3494
3506
|
},
|
|
3495
3507
|
required: ['workflowId'],
|
|
@@ -5333,6 +5345,11 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
5333
5345
|
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.',
|
|
5334
5346
|
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5335
5347
|
},
|
|
5348
|
+
{
|
|
5349
|
+
name: 'jobboard_applicant_accounts_status',
|
|
5350
|
+
description: 'Applicant accounts on the hosted job board (applicant sign-in with an emailed one-time code, my applications, saved jobs, resume library). Returns { entitled, hostedEnabled, signInUrl, accounts, signedInLast30Days, deletionPending, savedJobs, linkedApplications } (counts only, never emails or resumes); { entitled:false } without the Job Board applicant accounts package.',
|
|
5351
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5352
|
+
},
|
|
5336
5353
|
{
|
|
5337
5354
|
name: 'jobboard_hosted_set',
|
|
5338
5355
|
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).',
|
|
@@ -5379,6 +5396,60 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
5379
5396
|
required: ['applicationId', 'stage'],
|
|
5380
5397
|
},
|
|
5381
5398
|
},
|
|
5399
|
+
// -- Job Board employers + moderation (package "job-board-employers", default OFF: 403 job_board_employers_not_entitled without it) --
|
|
5400
|
+
{
|
|
5401
|
+
name: 'jobboard_employers_list',
|
|
5402
|
+
description: 'List the employers on this site\'s job board with status, trusted flag (first posting approved), member count, live/total listings and how many listings wait for moderation. Requires the Job board employers package (403 otherwise).',
|
|
5403
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5404
|
+
},
|
|
5405
|
+
{
|
|
5406
|
+
name: 'jobboard_employer_create',
|
|
5407
|
+
description: 'Create an employer on the job board (name 2-200 characters, unique per site; optional verified email domain). Administrator only. Invite its first member with jobboard_employer_invite.',
|
|
5408
|
+
inputSchema: {
|
|
5409
|
+
type: 'object',
|
|
5410
|
+
properties: { name: { type: 'string' }, verifiedDomain: { type: 'string', description: 'Optional, e.g. example.com' } },
|
|
5411
|
+
required: ['name'],
|
|
5412
|
+
},
|
|
5413
|
+
},
|
|
5414
|
+
{
|
|
5415
|
+
name: 'jobboard_employer_invite',
|
|
5416
|
+
description: 'Email a single-use invitation (valid 7 days) to join an employer as Owner, Recruiter (default) or Viewer. Employers manage only their own listings and applicants and never see EEO data. Administrator only; 409 if the email already belongs to another employer or the hosted board is off.',
|
|
5417
|
+
inputSchema: {
|
|
5418
|
+
type: 'object',
|
|
5419
|
+
properties: {
|
|
5420
|
+
employerId: { type: 'number' },
|
|
5421
|
+
email: { type: 'string' },
|
|
5422
|
+
displayName: { type: 'string' },
|
|
5423
|
+
role: { type: 'string', description: 'Owner | Recruiter | Viewer (default Recruiter)' },
|
|
5424
|
+
},
|
|
5425
|
+
required: ['employerId', 'email'],
|
|
5426
|
+
},
|
|
5427
|
+
},
|
|
5428
|
+
{
|
|
5429
|
+
name: 'jobboard_employer_suspend',
|
|
5430
|
+
description: 'Suspend an employer: its members are signed out at once and every published listing is paused (off the public board). A reason of 3-500 characters is required. Administrator only.',
|
|
5431
|
+
inputSchema: { type: 'object', properties: { employerId: { type: 'number' }, reason: { type: 'string' } }, required: ['employerId', 'reason'] },
|
|
5432
|
+
},
|
|
5433
|
+
{
|
|
5434
|
+
name: 'jobboard_employer_reactivate',
|
|
5435
|
+
description: 'Reactivate a suspended employer. Its paused listings stay paused until the employer resumes them. Administrator only.',
|
|
5436
|
+
inputSchema: { type: 'object', properties: { employerId: { type: 'number' } }, required: ['employerId'] },
|
|
5437
|
+
},
|
|
5438
|
+
{
|
|
5439
|
+
name: 'jobboard_moderation_list',
|
|
5440
|
+
description: 'The moderation queue: employer listings waiting for a decision (an employer\'s first posting, or any posting that matched a scam signal such as fee requests, SSN/bank requests, gift cards, wire transfers, WhatsApp-only contact). Each item carries the matched flags. Pending listings are invisible on the public board.',
|
|
5441
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5442
|
+
},
|
|
5443
|
+
{
|
|
5444
|
+
name: 'jobboard_moderation_approve',
|
|
5445
|
+
description: 'Approve a pending employer listing: it goes live, the employer becomes trusted (later clean postings publish at once), and the decision is audited. Administrator only; 409 unless the listing is waiting for moderation.',
|
|
5446
|
+
inputSchema: { type: 'object', properties: { listingId: { type: 'number' } }, required: ['listingId'] },
|
|
5447
|
+
},
|
|
5448
|
+
{
|
|
5449
|
+
name: 'jobboard_moderation_reject',
|
|
5450
|
+
description: 'Reject a pending employer listing with a reason (3-500 characters) the employer will see; the listing stays hidden and the decision is audited. Administrator only.',
|
|
5451
|
+
inputSchema: { type: 'object', properties: { listingId: { type: 'number' }, reason: { type: 'string' } }, required: ['listingId', 'reason'] },
|
|
5452
|
+
},
|
|
5382
5453
|
// -- Job Board pipeline (package "job-board-pipeline", default OFF: 403 job_board_pipeline_not_entitled without it, except jobboard_pipeline_get) --
|
|
5383
5454
|
{
|
|
5384
5455
|
name: 'jobboard_pipeline_get',
|
|
@@ -9074,6 +9145,7 @@ async function handleTool(name, args) {
|
|
|
9074
9145
|
` Display name: ${data.displayName || '(AI)'}`,
|
|
9075
9146
|
` Badge shown: ${data.showBadge}`,
|
|
9076
9147
|
` Size: ${data.sizePx}px`,
|
|
9148
|
+
` Body motion: ${data.motionLevel || '(default: natural)'} @ ${data.motionIntensityPercent ?? 100}%`,
|
|
9077
9149
|
].join('\n');
|
|
9078
9150
|
}
|
|
9079
9151
|
|
|
@@ -9089,6 +9161,11 @@ async function handleTool(name, args) {
|
|
|
9089
9161
|
if (args.showBadge !== undefined) body.showBadge = args.showBadge;
|
|
9090
9162
|
if (args.sizePx !== undefined) body.sizePx = args.sizePx;
|
|
9091
9163
|
if (args.allowVisitorCollapse !== undefined) body.allowVisitorCollapse = args.allowVisitorCollapse;
|
|
9164
|
+
if (args.motionLevel !== undefined) body.motionLevel = parseAvatarMotionLevel(args.motionLevel);
|
|
9165
|
+
if (args.motionIntensityPercent !== undefined) {
|
|
9166
|
+
if (!Number.isInteger(args.motionIntensityPercent) || args.motionIntensityPercent < 25 || args.motionIntensityPercent > 150) throw new Error('motionIntensityPercent must be a whole number between 25 and 150.');
|
|
9167
|
+
body.motionIntensityPercent = args.motionIntensityPercent;
|
|
9168
|
+
}
|
|
9092
9169
|
for (const [arg, max] of [['maxAvatarMinutesPerMonth', 100000], ['maxAvatarTtsChars', 100000000]]) {
|
|
9093
9170
|
if (args[arg] === undefined) continue;
|
|
9094
9171
|
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).`);
|
|
@@ -10857,6 +10934,10 @@ async function handleTool(name, args) {
|
|
|
10857
10934
|
const r = await messagingGet('/Recruiting/Hosted');
|
|
10858
10935
|
return JSON.stringify(r, null, 2);
|
|
10859
10936
|
}
|
|
10937
|
+
case 'jobboard_applicant_accounts_status': {
|
|
10938
|
+
const r = await messagingGet('/Recruiting/Applicants');
|
|
10939
|
+
return JSON.stringify(r, null, 2);
|
|
10940
|
+
}
|
|
10860
10941
|
case 'jobboard_hosted_set': {
|
|
10861
10942
|
const body = {};
|
|
10862
10943
|
if (args.enabled !== undefined) body.enabled = Boolean(args.enabled);
|
|
@@ -10895,6 +10976,55 @@ async function handleTool(name, args) {
|
|
|
10895
10976
|
return JSON.stringify(r, null, 2);
|
|
10896
10977
|
}
|
|
10897
10978
|
|
|
10979
|
+
case 'jobboard_employers_list': {
|
|
10980
|
+
return JSON.stringify(await messagingGet('/Recruiting/Employers'), null, 2);
|
|
10981
|
+
}
|
|
10982
|
+
case 'jobboard_employer_create': {
|
|
10983
|
+
const name = String(args.name ?? '').trim();
|
|
10984
|
+
if (name.length < 2 || name.length > 200) throw new Error('name must be 2 to 200 characters.');
|
|
10985
|
+
const body = { name };
|
|
10986
|
+
if (args.verifiedDomain !== undefined && String(args.verifiedDomain).trim()) body.verifiedDomain = String(args.verifiedDomain).trim();
|
|
10987
|
+
return JSON.stringify(await messagingPost('/Recruiting/Employers', body), null, 2);
|
|
10988
|
+
}
|
|
10989
|
+
case 'jobboard_employer_invite': {
|
|
10990
|
+
const id = Number(args.employerId);
|
|
10991
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('employerId must be a positive integer.');
|
|
10992
|
+
const email = String(args.email ?? '').trim();
|
|
10993
|
+
if (!/^[^@\s]+@[^@\s]+\.[^@\s]{2,}$/.test(email)) throw new Error('email must be a valid email address.');
|
|
10994
|
+
const role = ['Owner', 'Recruiter', 'Viewer'].find((r) => r.toLowerCase() === String(args.role ?? 'Recruiter').trim().toLowerCase());
|
|
10995
|
+
if (!role) throw new Error('role must be Owner, Recruiter or Viewer.');
|
|
10996
|
+
const body = { email, role };
|
|
10997
|
+
if (args.displayName !== undefined && String(args.displayName).trim()) body.displayName = String(args.displayName).trim();
|
|
10998
|
+
return JSON.stringify(await messagingPost(`/Recruiting/Employers/${id}/Invite`, body), null, 2);
|
|
10999
|
+
}
|
|
11000
|
+
case 'jobboard_employer_suspend': {
|
|
11001
|
+
const id = Number(args.employerId);
|
|
11002
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('employerId must be a positive integer.');
|
|
11003
|
+
const reason = String(args.reason ?? '').trim();
|
|
11004
|
+
if (reason.length < 3 || reason.length > 500) throw new Error('reason must be 3 to 500 characters.');
|
|
11005
|
+
return JSON.stringify(await messagingPost(`/Recruiting/Employers/${id}/Suspend`, { reason }), null, 2);
|
|
11006
|
+
}
|
|
11007
|
+
case 'jobboard_employer_reactivate': {
|
|
11008
|
+
const id = Number(args.employerId);
|
|
11009
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('employerId must be a positive integer.');
|
|
11010
|
+
return JSON.stringify(await messagingPost(`/Recruiting/Employers/${id}/Reactivate`, {}), null, 2);
|
|
11011
|
+
}
|
|
11012
|
+
case 'jobboard_moderation_list': {
|
|
11013
|
+
return JSON.stringify(await messagingGet('/Recruiting/Moderation'), null, 2);
|
|
11014
|
+
}
|
|
11015
|
+
case 'jobboard_moderation_approve': {
|
|
11016
|
+
const id = Number(args.listingId);
|
|
11017
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('listingId must be a positive integer.');
|
|
11018
|
+
return JSON.stringify(await messagingPost(`/Recruiting/Moderation/${id}/Approve`, {}), null, 2);
|
|
11019
|
+
}
|
|
11020
|
+
case 'jobboard_moderation_reject': {
|
|
11021
|
+
const id = Number(args.listingId);
|
|
11022
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('listingId must be a positive integer.');
|
|
11023
|
+
const reason = String(args.reason ?? '').trim();
|
|
11024
|
+
if (reason.length < 3 || reason.length > 500) throw new Error('reason must be 3 to 500 characters.');
|
|
11025
|
+
return JSON.stringify(await messagingPost(`/Recruiting/Moderation/${id}/Reject`, { reason }), null, 2);
|
|
11026
|
+
}
|
|
11027
|
+
|
|
10898
11028
|
case 'jobboard_move_application_stage': {
|
|
10899
11029
|
const id = Number(args.applicationId);
|
|
10900
11030
|
if (!Number.isInteger(id) || id <= 0) throw new Error('applicationId must be a positive integer.');
|