@velaro/mcp-server 0.6.91 → 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 +181 -15
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
|
@@ -857,6 +857,46 @@ const TOOLS = [
|
|
|
857
857
|
description: 'Roll back an approved suggestion, restoring the original canned message text.',
|
|
858
858
|
inputSchema: { type: 'object', properties: { suggestionId: { type: 'number' }, notes: { type: 'string' } }, required: ['suggestionId'] },
|
|
859
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
|
+
},
|
|
860
900
|
{
|
|
861
901
|
name: 'chat_intelligence_recommendations',
|
|
862
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).',
|
|
@@ -3214,6 +3254,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3214
3254
|
displayName: { type: 'string', description: 'Visitor-facing avatar name shown on the badge (e.g. "Moshky"). Empty string resets to "AI".' },
|
|
3215
3255
|
showBadge: { type: 'boolean', description: 'Show the name badge over the avatar video. Default true.' },
|
|
3216
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.' },
|
|
3217
3258
|
maxAvatarMinutesPerMonth: { type: 'number', description: 'Monthly avatar video minutes cap for the site. Whole number 1-100000; never unlimited (0/negative rejected).' },
|
|
3218
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.' },
|
|
3219
3260
|
},
|
|
@@ -4939,6 +4980,52 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
4939
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.',
|
|
4940
4981
|
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4941
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
|
+
},
|
|
4942
5029
|
// Recruiting/Applications/ExportEeoc (voluntary self-ID CSV export) is intentionally NOT
|
|
4943
5030
|
// exposed as an MCP tool -- it returns sensitive EEOC/OFCCP self-identification PII in bulk.
|
|
4944
5031
|
// It is administrator-only and audit-logged server-side (403 for ordinary agents); the
|
|
@@ -5815,6 +5902,28 @@ async function handleTool(name, args) {
|
|
|
5815
5902
|
if (args.teamId != null) qs.set('teamId', String(args.teamId));
|
|
5816
5903
|
return await messagingGet(`/Reports/chat-intelligence/themes?${qs.toString()}`);
|
|
5817
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') };
|
|
5818
5927
|
case 'canned_learning_suggestions':
|
|
5819
5928
|
return await messagingGet(`/CannedLearning/suggestions?state=${encodeURIComponent(args.state ?? 'pending_review')}`);
|
|
5820
5929
|
case 'canned_learning_generate':
|
|
@@ -6077,7 +6186,7 @@ async function handleTool(name, args) {
|
|
|
6077
6186
|
return await rpc('ai_config_set_reply_format', args);
|
|
6078
6187
|
|
|
6079
6188
|
case 'site_identity_secret_status': {
|
|
6080
|
-
const settings = await api('GET', '/SiteSettings');
|
|
6189
|
+
const settings = await api('GET', '/api/SiteSettings');
|
|
6081
6190
|
const configured = settings?.identitySigningSecretConfigured ?? false;
|
|
6082
6191
|
const masked = settings?.identitySigningSecretMasked ?? null;
|
|
6083
6192
|
if (!configured) {
|
|
@@ -6117,7 +6226,7 @@ async function handleTool(name, args) {
|
|
|
6117
6226
|
}
|
|
6118
6227
|
|
|
6119
6228
|
case 'index_list': {
|
|
6120
|
-
const indexes = await messagingApi('/AzureIndexes/
|
|
6229
|
+
const indexes = await messagingApi('/AzureIndexes/List');
|
|
6121
6230
|
if (!Array.isArray(indexes) || !indexes.length) {
|
|
6122
6231
|
return 'No knowledge collections found for this site.';
|
|
6123
6232
|
}
|
|
@@ -6449,7 +6558,8 @@ async function handleTool(name, args) {
|
|
|
6449
6558
|
}
|
|
6450
6559
|
|
|
6451
6560
|
case 'routing_delete': {
|
|
6452
|
-
|
|
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 });
|
|
6453
6563
|
return `Deleted routing rule id=${args.id}`;
|
|
6454
6564
|
}
|
|
6455
6565
|
|
|
@@ -6988,7 +7098,7 @@ async function handleTool(name, args) {
|
|
|
6988
7098
|
|
|
6989
7099
|
// ── Layer 7: Admin Activity Log — recent channel/auth/integration events
|
|
6990
7100
|
// Useful for detecting send failures, auth events, channel errors not in integration logs.
|
|
6991
|
-
const activityLogP = safeJson(() =>
|
|
7101
|
+
const activityLogP = safeJson(() => messagingRequest('GET', '/AdminActivityLog?pageSize=10'));
|
|
6992
7102
|
|
|
6993
7103
|
const [health, ci, aiEx, velaroLogs, loggly, intLogPage, skillErr, activityLog] = await Promise.all([healthP, ciP, aiExP, velaroLoggerP, logglyP, intLogP, skillErrP, activityLogP]);
|
|
6994
7104
|
|
|
@@ -7113,7 +7223,7 @@ async function handleTool(name, args) {
|
|
|
7113
7223
|
const q = args.query ?? '';
|
|
7114
7224
|
const top = args.top ?? 5;
|
|
7115
7225
|
if (!q) return 'query is required.';
|
|
7116
|
-
const results = await
|
|
7226
|
+
const results = await messagingRequest('GET', `/AzureIndexes/content-search?q=${encodeURIComponent(q)}&top=${top}`);
|
|
7117
7227
|
if (results?.error) return `Index search error: ${results.error}`;
|
|
7118
7228
|
if (!Array.isArray(results) || !results.length) return `No results for "${q}". Index may be empty — trigger ingestion first.`;
|
|
7119
7229
|
const lines = [
|
|
@@ -7176,7 +7286,7 @@ async function handleTool(name, args) {
|
|
|
7176
7286
|
return JSON.stringify(r, null, 2);
|
|
7177
7287
|
}
|
|
7178
7288
|
case 'index_status': {
|
|
7179
|
-
const sources = await
|
|
7289
|
+
const sources = await messagingRequest('GET', '/AzureIndexes/IntegrationSources');
|
|
7180
7290
|
if (!Array.isArray(sources) || !sources.length) return 'No indexed sources found. Run index_ingest to populate.';
|
|
7181
7291
|
const lines = [
|
|
7182
7292
|
'=== Index Status ===',
|
|
@@ -7189,7 +7299,7 @@ async function handleTool(name, args) {
|
|
|
7189
7299
|
if (!args.jobId) return 'jobId is required.';
|
|
7190
7300
|
const body = { jobId: args.jobId };
|
|
7191
7301
|
if (args.indexName) body.indexName = args.indexName;
|
|
7192
|
-
const result = await
|
|
7302
|
+
const result = await messagingRequest('POST', '/AzureIndexes/IngestJobDirect', body);
|
|
7193
7303
|
if (result?.error) return `Ingestion error: ${result.error}`;
|
|
7194
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.`;
|
|
7195
7305
|
}
|
|
@@ -7214,7 +7324,7 @@ async function handleTool(name, args) {
|
|
|
7214
7324
|
}
|
|
7215
7325
|
|
|
7216
7326
|
case 'uploaded_files_list': {
|
|
7217
|
-
const files = await
|
|
7327
|
+
const files = await messagingRequest('GET', '/uploadedFiles/list/AI');
|
|
7218
7328
|
if (!Array.isArray(files) || !files.length) return 'No AI training files uploaded for this site.';
|
|
7219
7329
|
const lines = ['=== Uploaded AI Training Files ===',
|
|
7220
7330
|
...files.map(f => ` [id=${f.id}] ${f.fileName} size=${f.fileLength ? Math.round(f.fileLength/1024)+'KB' : '?'}`)];
|
|
@@ -7227,7 +7337,7 @@ async function handleTool(name, args) {
|
|
|
7227
7337
|
if (args.fileId) body.id = args.fileId;
|
|
7228
7338
|
if (args.fileName) body.fileName = args.fileName;
|
|
7229
7339
|
if (args.indexName) body.indexName = args.indexName;
|
|
7230
|
-
const result = await
|
|
7340
|
+
const result = await messagingRequest('POST', '/uploadedFiles/reindex', body);
|
|
7231
7341
|
if (result?.error) return `Reindex error: ${result.error}`;
|
|
7232
7342
|
return `✅ Re-indexed '${result.fileName}': ${result.chunksIndexed} chunks indexed.\n\nUse index_status to verify the chunks appear in the knowledge index.`;
|
|
7233
7343
|
}
|
|
@@ -8532,6 +8642,7 @@ async function handleTool(name, args) {
|
|
|
8532
8642
|
if (args.displayName !== undefined) body.displayName = args.displayName;
|
|
8533
8643
|
if (args.showBadge !== undefined) body.showBadge = args.showBadge;
|
|
8534
8644
|
if (args.sizePx !== undefined) body.sizePx = args.sizePx;
|
|
8645
|
+
if (args.allowVisitorCollapse !== undefined) body.allowVisitorCollapse = args.allowVisitorCollapse;
|
|
8535
8646
|
for (const [arg, max] of [['maxAvatarMinutesPerMonth', 100000], ['maxAvatarTtsChars', 100000000]]) {
|
|
8536
8647
|
if (args[arg] === undefined) continue;
|
|
8537
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).`);
|
|
@@ -9133,7 +9244,7 @@ async function handleTool(name, args) {
|
|
|
9133
9244
|
|
|
9134
9245
|
case 'magento_diagnose': {
|
|
9135
9246
|
if (!args.siteId) return 'siteId is required.';
|
|
9136
|
-
const r = await
|
|
9247
|
+
const r = await messagingRequest('GET', `/diagnostics/test-magento?siteId=${encodeURIComponent(args.siteId)}`);
|
|
9137
9248
|
return JSON.stringify(r, null, 2);
|
|
9138
9249
|
}
|
|
9139
9250
|
|
|
@@ -9141,18 +9252,18 @@ async function handleTool(name, args) {
|
|
|
9141
9252
|
case 'sap_diagnose': {
|
|
9142
9253
|
if (!args.siteId) return 'siteId is required.';
|
|
9143
9254
|
const deep = args.deep === false ? 'false' : 'true';
|
|
9144
|
-
const r = await
|
|
9255
|
+
const r = await messagingRequest('GET', `/diagnostics/test-sap?siteId=${encodeURIComponent(args.siteId)}&deep=${deep}`);
|
|
9145
9256
|
return JSON.stringify(r, null, 2);
|
|
9146
9257
|
}
|
|
9147
9258
|
|
|
9148
9259
|
case 'business_action_list': {
|
|
9149
|
-
const r = await
|
|
9260
|
+
const r = await messagingRequest('GET', '/BusinessActions');
|
|
9150
9261
|
return JSON.stringify(r, null, 2);
|
|
9151
9262
|
}
|
|
9152
9263
|
|
|
9153
9264
|
case 'business_action_route': {
|
|
9154
9265
|
if (!args.text) return 'text is required.';
|
|
9155
|
-
const r = await
|
|
9266
|
+
const r = await messagingRequest('POST', '/BusinessActions/Route', { text: args.text, max: args.max });
|
|
9156
9267
|
return JSON.stringify(r, null, 2);
|
|
9157
9268
|
}
|
|
9158
9269
|
|
|
@@ -9162,7 +9273,7 @@ async function handleTool(name, args) {
|
|
|
9162
9273
|
const qs = [];
|
|
9163
9274
|
if (args.idempotencyKey) qs.push(`idempotencyKey=${encodeURIComponent(args.idempotencyKey)}`);
|
|
9164
9275
|
if (args.conversationId) qs.push(`conversationId=${encodeURIComponent(args.conversationId)}`);
|
|
9165
|
-
const r = await
|
|
9276
|
+
const r = await messagingRequest('POST', `/BusinessActions/${encodeURIComponent(args.name)}/Execute${qs.length ? '?' + qs.join('&') : ''}`, args.request);
|
|
9166
9277
|
return JSON.stringify(r, null, 2);
|
|
9167
9278
|
}
|
|
9168
9279
|
|
|
@@ -9705,7 +9816,7 @@ async function handleTool(name, args) {
|
|
|
9705
9816
|
}
|
|
9706
9817
|
|
|
9707
9818
|
case 'billing_plan_status': {
|
|
9708
|
-
const s = await api('GET', '/BillingPayment/plan-status'
|
|
9819
|
+
const s = await api('GET', '/api/BillingPayment/plan-status');
|
|
9709
9820
|
const lines = [`Site ${s.siteId} — Subscription Status`];
|
|
9710
9821
|
if (s.subscription) {
|
|
9711
9822
|
lines.push(`Seats: ${s.subscription.maxLicensedSeats} | Conv/mo: ${s.subscription.maxConversationsPerMonth || '∞'} | Self-serve: ${s.subscription.enableSelfServeSeats ? 'YES' : 'no'}`);
|
|
@@ -10201,6 +10312,61 @@ async function handleTool(name, args) {
|
|
|
10201
10312
|
return JSON.stringify(listing, null, 2);
|
|
10202
10313
|
}
|
|
10203
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
|
+
|
|
10204
10370
|
case 'jobboard_move_application_stage': {
|
|
10205
10371
|
const app = await messagingPut(`/Recruiting/Applications/${args.applicationId}/Stage`, { stage: args.stage });
|
|
10206
10372
|
return JSON.stringify(app, null, 2);
|