@velaro/mcp-server 0.6.55 → 0.6.56
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 +7 -3
- package/server.js +223 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velaro/mcp-server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.56",
|
|
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": {
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"qs": "^6.16.0"
|
|
15
15
|
},
|
|
16
16
|
"overrides": {
|
|
17
|
-
"qs": "^6.16.0"
|
|
17
|
+
"qs": "^6.16.0",
|
|
18
|
+
"hono": "^4.13.5"
|
|
18
19
|
},
|
|
19
20
|
"scripts": {
|
|
20
21
|
"start": "node server.js"
|
|
@@ -43,5 +44,8 @@
|
|
|
43
44
|
"server.js",
|
|
44
45
|
"server.json",
|
|
45
46
|
"README.md"
|
|
46
|
-
]
|
|
47
|
+
],
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"license-checker-rseidelsohn": "^4.4.2"
|
|
50
|
+
}
|
|
47
51
|
}
|
package/server.js
CHANGED
|
@@ -62,6 +62,12 @@ const MESSAGING_API = process.env.VELARO_MESSAGING_API || null;
|
|
|
62
62
|
const MCP_KEY = process.env.VELARO_MCP_KEY;
|
|
63
63
|
const JWT = process.env.VELARO_JWT;
|
|
64
64
|
|
|
65
|
+
// mcp/staff-server.js defaults to staging instead -- the two servers' defaults intentionally
|
|
66
|
+
// differ, but a zero-result lookup gives no hint which environment was actually searched
|
|
67
|
+
// either way. See ENV_LABEL in staff-server.js for the incident this class of confusion caused
|
|
68
|
+
// (a real production customer read as "doesn't exist" from a staging-scoped search).
|
|
69
|
+
const ENV_LABEL = API_BASE.includes('staging') ? 'staging' : 'production';
|
|
70
|
+
|
|
65
71
|
// Known admin API hosts, for tools that let a caller explicitly target one environment
|
|
66
72
|
// regardless of what this server process's own API_BASE is configured to (see the `env` param
|
|
67
73
|
// on the calendly_* superadmin tools below). Deliberately NOT used as a default anywhere —
|
|
@@ -561,6 +567,18 @@ const TOOLS = [
|
|
|
561
567
|
},
|
|
562
568
|
},
|
|
563
569
|
|
|
570
|
+
{
|
|
571
|
+
name: 'site_scraper_usage',
|
|
572
|
+
description: 'Velaro staff only. Scraper/upload/chunk consumption for ANY site in one call: pages this month vs budget (both the admin and messaging ledgers), upload pages, OCR pages, jobs today vs daily cap, and chunks indexed vs MaxIndexedChunkTotal with chunks grouped by index name. Wraps GET ScraperJobs/StaffUsage/{siteId} (IsVelaroAdminAsync + super-admin enforced server-side). Read-only.',
|
|
573
|
+
inputSchema: {
|
|
574
|
+
type: 'object',
|
|
575
|
+
properties: {
|
|
576
|
+
siteId: { type: 'number', description: 'Target site ID (need not be your own)' },
|
|
577
|
+
},
|
|
578
|
+
required: ['siteId'],
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
|
|
564
582
|
// ── Startup migration diagnostics (AdminStartupDiagnostics canary table) ──
|
|
565
583
|
{
|
|
566
584
|
name: 'admin_startup_diagnostics',
|
|
@@ -1593,6 +1611,120 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
1593
1611
|
description: 'Show what is currently indexed for the site: document counts per source type (kb_article, scraper_page, bigcommerce_product, etc). Use to verify ingestion completed and data is present.',
|
|
1594
1612
|
inputSchema: { type: 'object', properties: {} },
|
|
1595
1613
|
},
|
|
1614
|
+
{
|
|
1615
|
+
name: 'index_move_preview',
|
|
1616
|
+
description: 'Preview moving (or copying) documents between two knowledge indexes on the site. Writes nothing. Returns document/chunk/page counts, per-document method (copy_vectors, rechunk, already_in_target, blocked), quota effect, and whether it can run. No rescan and no OCR is ever needed. Moving into a troubleshooting index needs the EnableTroubleshootingKnowledge entitlement. Wraps POST IndexMove/Preview (velaro-messaging); the site comes from the caller auth, never an argument.',
|
|
1617
|
+
inputSchema: {
|
|
1618
|
+
type: 'object',
|
|
1619
|
+
properties: {
|
|
1620
|
+
fromIndex: { type: 'string', description: 'Source index name' },
|
|
1621
|
+
toIndex: { type: 'string', description: 'Target index name' },
|
|
1622
|
+
kind: { type: 'string', enum: ['move', 'copy'], description: 'Default move' },
|
|
1623
|
+
skipBlocked: { type: 'boolean', description: 'Leave blocked documents in place and move the rest' },
|
|
1624
|
+
selector: {
|
|
1625
|
+
type: 'object',
|
|
1626
|
+
description: 'What to move. Needs at least one field or all:true. Fields: all (bool), jobId, urlPrefix, sourceIds[], fileNames[], docIds[] (numbers), sourceType (website_page | video_transcript | uploaded_document | onedrive_file | custom_content).',
|
|
1627
|
+
},
|
|
1628
|
+
},
|
|
1629
|
+
required: ['fromIndex', 'toIndex', 'selector'],
|
|
1630
|
+
},
|
|
1631
|
+
},
|
|
1632
|
+
{
|
|
1633
|
+
name: 'index_move_execute',
|
|
1634
|
+
description: 'Run a move/copy between knowledge indexes. Always call index_move_preview first and check canExecute. Write-then-delete and resumable: content is never missing from both indexes. Returns a moveId; poll index_move_status. Wraps POST IndexMove/Execute.',
|
|
1635
|
+
inputSchema: {
|
|
1636
|
+
type: 'object',
|
|
1637
|
+
properties: {
|
|
1638
|
+
fromIndex: { type: 'string' },
|
|
1639
|
+
toIndex: { type: 'string' },
|
|
1640
|
+
kind: { type: 'string', enum: ['move', 'copy'] },
|
|
1641
|
+
skipBlocked: { type: 'boolean' },
|
|
1642
|
+
selector: {
|
|
1643
|
+
type: 'object',
|
|
1644
|
+
description: 'What to move. Needs at least one field or all:true. Fields: all (bool), jobId, urlPrefix, sourceIds[], fileNames[], docIds[] (numbers), sourceType (website_page | video_transcript | uploaded_document | onedrive_file | custom_content).',
|
|
1645
|
+
},
|
|
1646
|
+
},
|
|
1647
|
+
required: ['fromIndex', 'toIndex', 'selector'],
|
|
1648
|
+
},
|
|
1649
|
+
},
|
|
1650
|
+
{
|
|
1651
|
+
name: 'index_split_preview',
|
|
1652
|
+
description: 'Detect manual-like documents in a general index and preview moving them into an existing troubleshooting-purpose index. Writes nothing. Wraps POST IndexMove/Split/Preview. Use index_split_execute to run it.',
|
|
1653
|
+
inputSchema: {
|
|
1654
|
+
type: 'object',
|
|
1655
|
+
properties: {
|
|
1656
|
+
fromIndex: { type: 'string' },
|
|
1657
|
+
toIndex: { type: 'string', description: 'Existing troubleshooting-purpose index' },
|
|
1658
|
+
docIds: { type: 'array', items: { type: 'number' }, description: 'Only these documents (default: every detected manual)' },
|
|
1659
|
+
skipBlocked: { type: 'boolean' },
|
|
1660
|
+
},
|
|
1661
|
+
required: ['fromIndex', 'toIndex'],
|
|
1662
|
+
},
|
|
1663
|
+
},
|
|
1664
|
+
{
|
|
1665
|
+
name: 'index_split_execute',
|
|
1666
|
+
description: 'Run a split: move detected manuals from a general index into an existing troubleshooting-purpose index. Call index_split_preview first. Resumable and undoable via index_move_control. Wraps POST IndexMove/Split/Execute.',
|
|
1667
|
+
inputSchema: {
|
|
1668
|
+
type: 'object',
|
|
1669
|
+
properties: {
|
|
1670
|
+
fromIndex: { type: 'string' },
|
|
1671
|
+
toIndex: { type: 'string' },
|
|
1672
|
+
docIds: { type: 'array', items: { type: 'number' } },
|
|
1673
|
+
skipBlocked: { type: 'boolean' },
|
|
1674
|
+
},
|
|
1675
|
+
required: ['fromIndex', 'toIndex'],
|
|
1676
|
+
},
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
name: 'index_move_status',
|
|
1680
|
+
description: 'Status of one move (moveId) or the recent moves (omit moveId): status, per-document progress, canResume/canAbort/canUndo. Wraps GET IndexMove/Moves[/{id}].',
|
|
1681
|
+
inputSchema: {
|
|
1682
|
+
type: 'object',
|
|
1683
|
+
properties: { moveId: { type: 'string' } },
|
|
1684
|
+
},
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
name: 'index_move_control',
|
|
1688
|
+
description: 'Resume a stopped move, abort a queued/failed move that has not started removing content, or undo a completed move. Wraps POST IndexMove/Moves/{id}/Resume|Abort|Undo.',
|
|
1689
|
+
inputSchema: {
|
|
1690
|
+
type: 'object',
|
|
1691
|
+
properties: {
|
|
1692
|
+
moveId: { type: 'string' },
|
|
1693
|
+
action: { type: 'string', enum: ['resume', 'abort', 'undo'] },
|
|
1694
|
+
},
|
|
1695
|
+
required: ['moveId', 'action'],
|
|
1696
|
+
},
|
|
1697
|
+
},
|
|
1698
|
+
{
|
|
1699
|
+
name: 'index_mixed_content',
|
|
1700
|
+
description: 'Check whether a general index holds manual-like documents that belong in a Troubleshooting Assistant index. Read-only. Wraps GET IndexMove/MixedContent.',
|
|
1701
|
+
inputSchema: {
|
|
1702
|
+
type: 'object',
|
|
1703
|
+
properties: { indexName: { type: 'string' } },
|
|
1704
|
+
required: ['indexName'],
|
|
1705
|
+
},
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
name: 'index_integrity_check',
|
|
1709
|
+
description: 'Read-only integrity report for a knowledge index: orphan chunks, records without chunks, count mismatches, duplicate records. Wraps GET IndexMove/Integrity.',
|
|
1710
|
+
inputSchema: {
|
|
1711
|
+
type: 'object',
|
|
1712
|
+
properties: { indexName: { type: 'string' } },
|
|
1713
|
+
required: ['indexName'],
|
|
1714
|
+
},
|
|
1715
|
+
},
|
|
1716
|
+
{
|
|
1717
|
+
name: 'index_integrity_repair',
|
|
1718
|
+
description: 'Repair a knowledge index. Additive and idempotent: corrects counts and adopts orphan chunks into tracking rows, never deletes vectors. removeDuplicateRecords also removes duplicate tracking rows. Run index_integrity_check first. Wraps POST IndexMove/Integrity/Repair.',
|
|
1719
|
+
inputSchema: {
|
|
1720
|
+
type: 'object',
|
|
1721
|
+
properties: {
|
|
1722
|
+
indexName: { type: 'string' },
|
|
1723
|
+
removeDuplicateRecords: { type: 'boolean' },
|
|
1724
|
+
},
|
|
1725
|
+
required: ['indexName'],
|
|
1726
|
+
},
|
|
1727
|
+
},
|
|
1596
1728
|
{
|
|
1597
1729
|
name: 'index_ingest',
|
|
1598
1730
|
description: 'Trigger ingestion of a scraper job into the knowledge index. Fires in the background; use index_status after ~2 minutes to verify chunks appeared.',
|
|
@@ -2331,6 +2463,17 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
2331
2463
|
required: ['siteId'],
|
|
2332
2464
|
},
|
|
2333
2465
|
},
|
|
2466
|
+
{
|
|
2467
|
+
name: 'tour_insights',
|
|
2468
|
+
description: 'Moshky setup-tour funnel and feedback (Velaro staff only). Without siteId: per-tour, per-step counts of offered/opened/step_viewed/arrived/stuck/completed/dismissed/load_error over the last N days plus recent scrubbed feedback. With siteId: that one site\'s tour event timeline and feedback. Feedback comments are PII-scrubbed and for staff eyes only.',
|
|
2469
|
+
inputSchema: {
|
|
2470
|
+
type: 'object',
|
|
2471
|
+
properties: {
|
|
2472
|
+
siteId: { type: 'number', description: 'Optional. When set, returns that site\'s tour timeline instead of the cross-site funnel.' },
|
|
2473
|
+
days: { type: 'number', description: 'Look-back window in days (default 30, max 365)' },
|
|
2474
|
+
},
|
|
2475
|
+
},
|
|
2476
|
+
},
|
|
2334
2477
|
{
|
|
2335
2478
|
name: 'support_extend_subscription',
|
|
2336
2479
|
description: 'Extend a customer site\'s subscription expiration by N days. SuperAdmin only.',
|
|
@@ -2935,6 +3078,18 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
2935
3078
|
},
|
|
2936
3079
|
},
|
|
2937
3080
|
|
|
3081
|
+
{
|
|
3082
|
+
name: 'magento_diagnose',
|
|
3083
|
+
description: 'Run the Magento live-connection diagnostic for a site: config present, access token valid, /rest/V1 reachability, store code/base URL, and ACL scope probes. Calls messaging GET diagnostics/test-magento?siteId=<id>. Read-only.',
|
|
3084
|
+
inputSchema: {
|
|
3085
|
+
type: 'object',
|
|
3086
|
+
properties: {
|
|
3087
|
+
siteId: { type: 'number', description: 'Site to diagnose (e.g. 1032)' },
|
|
3088
|
+
},
|
|
3089
|
+
required: ['siteId'],
|
|
3090
|
+
},
|
|
3091
|
+
},
|
|
3092
|
+
|
|
2938
3093
|
// ── CallRail Attribution ──────────────────────────────────────────────────────
|
|
2939
3094
|
{
|
|
2940
3095
|
name: 'callrail_get_attributions',
|
|
@@ -4583,6 +4738,11 @@ async function handleTool(name, args) {
|
|
|
4583
4738
|
return JSON.stringify(result, null, 2);
|
|
4584
4739
|
}
|
|
4585
4740
|
|
|
4741
|
+
case 'site_scraper_usage': {
|
|
4742
|
+
const result = await api('GET', `ScraperJobs/StaffUsage/${encodeURIComponent(args.siteId)}`);
|
|
4743
|
+
return JSON.stringify(result, null, 2);
|
|
4744
|
+
}
|
|
4745
|
+
|
|
4586
4746
|
case 'apple_health': {
|
|
4587
4747
|
const h = await messagingGet(`/SupportTools/sites/${args.siteId}/apple/health`);
|
|
4588
4748
|
return JSON.stringify(h, null, 2);
|
|
@@ -5848,6 +6008,53 @@ async function handleTool(name, args) {
|
|
|
5848
6008
|
return lines.join('\n');
|
|
5849
6009
|
}
|
|
5850
6010
|
|
|
6011
|
+
case 'index_move_preview': {
|
|
6012
|
+
const { fromIndex, toIndex, kind, skipBlocked, selector } = args;
|
|
6013
|
+
const r = await messagingPost('/IndexMove/Preview', { fromIndex, toIndex, kind, skipBlocked, selector });
|
|
6014
|
+
return JSON.stringify(r, null, 2);
|
|
6015
|
+
}
|
|
6016
|
+
case 'index_move_execute': {
|
|
6017
|
+
const { fromIndex, toIndex, kind, skipBlocked, selector } = args;
|
|
6018
|
+
const r = await messagingPost('/IndexMove/Execute', { fromIndex, toIndex, kind, skipBlocked, selector });
|
|
6019
|
+
return JSON.stringify(r, null, 2);
|
|
6020
|
+
}
|
|
6021
|
+
case 'index_split_preview': {
|
|
6022
|
+
const body = { fromIndex: args.fromIndex, toIndex: args.toIndex, docIds: args.docIds, skipBlocked: args.skipBlocked };
|
|
6023
|
+
const r = await messagingPost('/IndexMove/Split/Preview', body);
|
|
6024
|
+
return JSON.stringify(r, null, 2);
|
|
6025
|
+
}
|
|
6026
|
+
case 'index_split_execute': {
|
|
6027
|
+
const body = { fromIndex: args.fromIndex, toIndex: args.toIndex, docIds: args.docIds, skipBlocked: args.skipBlocked };
|
|
6028
|
+
const r = await messagingPost('/IndexMove/Split/Execute', body);
|
|
6029
|
+
return JSON.stringify(r, null, 2);
|
|
6030
|
+
}
|
|
6031
|
+
case 'index_move_status': {
|
|
6032
|
+
const r = await messagingGet(args.moveId
|
|
6033
|
+
? `/IndexMove/Moves/${encodeURIComponent(args.moveId)}`
|
|
6034
|
+
: '/IndexMove/Moves?take=25');
|
|
6035
|
+
return JSON.stringify(r, null, 2);
|
|
6036
|
+
}
|
|
6037
|
+
case 'index_move_control': {
|
|
6038
|
+
const verb = { resume: 'Resume', abort: 'Abort', undo: 'Undo' }[args.action];
|
|
6039
|
+
if (!verb) throw new Error('action must be resume, abort or undo');
|
|
6040
|
+
const r = await messagingPost(`/IndexMove/Moves/${encodeURIComponent(args.moveId)}/${verb}`);
|
|
6041
|
+
return JSON.stringify(r, null, 2);
|
|
6042
|
+
}
|
|
6043
|
+
case 'index_mixed_content': {
|
|
6044
|
+
const r = await messagingGet(`/IndexMove/MixedContent?index=${encodeURIComponent(args.indexName)}`);
|
|
6045
|
+
return JSON.stringify(r, null, 2);
|
|
6046
|
+
}
|
|
6047
|
+
case 'index_integrity_check': {
|
|
6048
|
+
const r = await messagingGet(`/IndexMove/Integrity?index=${encodeURIComponent(args.indexName)}`);
|
|
6049
|
+
return JSON.stringify(r, null, 2);
|
|
6050
|
+
}
|
|
6051
|
+
case 'index_integrity_repair': {
|
|
6052
|
+
const r = await messagingPost('/IndexMove/Integrity/Repair', {
|
|
6053
|
+
indexName: args.indexName,
|
|
6054
|
+
removeDuplicateRecords: !!args.removeDuplicateRecords,
|
|
6055
|
+
});
|
|
6056
|
+
return JSON.stringify(r, null, 2);
|
|
6057
|
+
}
|
|
5851
6058
|
case 'index_status': {
|
|
5852
6059
|
const sources = await api('GET', '/AzureIndexes/IntegrationSources');
|
|
5853
6060
|
if (!Array.isArray(sources) || !sources.length) return 'No indexed sources found. Run index_ingest to populate.';
|
|
@@ -6737,7 +6944,7 @@ async function handleTool(name, args) {
|
|
|
6737
6944
|
|
|
6738
6945
|
case 'support_user_lookup': {
|
|
6739
6946
|
const users = await api('GET', `/SupportTools/users/search?q=${encodeURIComponent(args.query)}`);
|
|
6740
|
-
if (!Array.isArray(users) || !users.length) return `No users found matching "${args.query}".`;
|
|
6947
|
+
if (!Array.isArray(users) || !users.length) return `No users found matching "${args.query}" in ${ENV_LABEL}. If this looks like a real customer, re-run with VELARO_ADMIN_API set to the other environment before concluding they don't exist.`;
|
|
6741
6948
|
return users.map(u => {
|
|
6742
6949
|
const roles = u.sites?.map(s =>
|
|
6743
6950
|
` site ${s.siteId} (${s.companyName}) — ${s.isAdministrator ? 'Admin' : s.isManager ? 'Manager' : 'Agent'}${s.isActive === false ? ' INACTIVE' : ''}`
|
|
@@ -6753,7 +6960,7 @@ async function handleTool(name, args) {
|
|
|
6753
6960
|
|
|
6754
6961
|
case 'support_site_lookup': {
|
|
6755
6962
|
const sites = await api('GET', `/SupportTools/sites/search?q=${encodeURIComponent(args.query)}`);
|
|
6756
|
-
if (!Array.isArray(sites) || !sites.length) return `No sites found matching "${args.query}".`;
|
|
6963
|
+
if (!Array.isArray(sites) || !sites.length) return `No sites found matching "${args.query}" in ${ENV_LABEL}. If this looks like a real customer, re-run with VELARO_ADMIN_API set to the other environment before concluding they don't exist.`;
|
|
6757
6964
|
return sites.map(s => {
|
|
6758
6965
|
const admins = s.admins?.map(a => ` ${a.userName} (${a.firstName} ${a.lastName})`).join('\n') ?? 'none';
|
|
6759
6966
|
return `[siteId=${s.siteId}] ${s.companyName}${s.isTestSite ? ' [TEST]' : ''}\n URL: ${s.companyURL ?? '—'} | Created: ${s.dateCreated ? s.dateCreated.slice(0,10) : '—'}\n Admins:\n${admins}`;
|
|
@@ -6850,6 +7057,14 @@ async function handleTool(name, args) {
|
|
|
6850
7057
|
return lines.join('\n');
|
|
6851
7058
|
}
|
|
6852
7059
|
|
|
7060
|
+
case 'tour_insights': {
|
|
7061
|
+
const days = Math.min(365, Math.max(1, args.days ?? 30));
|
|
7062
|
+
const path = args.siteId
|
|
7063
|
+
? `/SupportTools/sites/${args.siteId}/tour-events?days=${days}`
|
|
7064
|
+
: `/SupportTools/tour-insights?days=${days}`;
|
|
7065
|
+
return JSON.stringify(await api('GET', path), null, 2);
|
|
7066
|
+
}
|
|
7067
|
+
|
|
6853
7068
|
case 'support_extend_subscription': {
|
|
6854
7069
|
const res = await api('POST', `/SupportTools/sites/${args.siteId}/extend-subscription`, { days: args.days });
|
|
6855
7070
|
return res?.success
|
|
@@ -7429,6 +7644,12 @@ async function handleTool(name, args) {
|
|
|
7429
7644
|
return lines.join('\n');
|
|
7430
7645
|
}
|
|
7431
7646
|
|
|
7647
|
+
case 'magento_diagnose': {
|
|
7648
|
+
if (!args.siteId) return 'siteId is required.';
|
|
7649
|
+
const r = await api('GET', `/diagnostics/test-magento?siteId=${encodeURIComponent(args.siteId)}`);
|
|
7650
|
+
return JSON.stringify(r, null, 2);
|
|
7651
|
+
}
|
|
7652
|
+
|
|
7432
7653
|
// ── CallRail Attribution ──────────────────────────────────────────────────
|
|
7433
7654
|
case 'callrail_get_attributions': {
|
|
7434
7655
|
const limit = Math.min(100, args.limit ?? 25);
|