@velaro/mcp-server 0.6.59 → 0.6.61
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 +144 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velaro/mcp-server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.61",
|
|
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
|
@@ -245,19 +245,73 @@ function mcpFindUnresolved(value) {
|
|
|
245
245
|
.map(([k]) => k);
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
// The hosted gateway requires an initialized session (Mcp-Session-Id + MCP-Protocol-Version) on
|
|
249
|
+
// every call after "initialize". Cache one per process and re-initialize when it expires (404).
|
|
250
|
+
let gatewaySession = null;
|
|
251
|
+
async function gatewayInit() {
|
|
252
|
+
const res = await fetch(`${API_BASE}/mcp`, {
|
|
253
|
+
method: 'POST',
|
|
254
|
+
headers: { Authorization: authHeader(), 'Content-Type': 'application/json' },
|
|
255
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2025-11-25', capabilities: {}, clientInfo: { name: 'velaro-mcp-server', version: '1' } } }),
|
|
256
|
+
});
|
|
257
|
+
if (!res.ok) {
|
|
258
|
+
const text = await res.text().catch(() => '');
|
|
259
|
+
throw new Error(`Velaro MCP gateway initialize -> ${res.status}: ${text.slice(0, 300)}`);
|
|
260
|
+
}
|
|
261
|
+
const sid = res.headers.get('mcp-session-id');
|
|
262
|
+
if (!sid) throw new Error('Velaro MCP gateway initialize returned no Mcp-Session-Id');
|
|
263
|
+
gatewaySession = { sid, version: res.headers.get('mcp-protocol-version') || '2025-11-25' };
|
|
264
|
+
return gatewaySession;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Tools served by velaro-messaging's admin-tools gateway (McpAdminGatewayController, POST /mcp/v1/admin-tools/call,
|
|
268
|
+
// no session). The admin /mcp gateway only serves kb_* tools, so these used to fail there.
|
|
269
|
+
const MESSAGING_ADMIN_TOOLS = new Set([
|
|
270
|
+
'workflow_create', 'workflow_list', 'workflow_get', 'workflow_toggle',
|
|
271
|
+
'bot_create', 'bot_list', 'bot_get', 'bot_update_prompt', 'bot_set_knowledge',
|
|
272
|
+
'ai_config_attach_index', 'call_pop_settings_get', 'call_pop_settings_set',
|
|
273
|
+
'agent_list', 'team_list', 'conversation_list', 'conversation_get', 'conversation_search',
|
|
274
|
+
'contact_search', 'contact_get', 'routing_list', 'routing_get', 'site_info',
|
|
275
|
+
'ms_bookings_cancel_appointment', 'ms_bookings_check_availability_range', 'ms_bookings_reschedule_appointment',
|
|
276
|
+
'outlook_calendar_cancel', 'outlook_calendar_get_available_range',
|
|
277
|
+
]);
|
|
278
|
+
|
|
279
|
+
async function messagingAdminRpc(toolName, args) {
|
|
280
|
+
if (!MESSAGING_API) throw new Error('VELARO_MESSAGING_API is not configured.');
|
|
281
|
+
const res = await fetch(`${MESSAGING_API}/mcp/v1/admin-tools/call`, {
|
|
282
|
+
method: 'POST',
|
|
283
|
+
headers: { Authorization: authHeader(), 'Content-Type': 'application/json' },
|
|
284
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: toolName, arguments: args ?? {} } }),
|
|
285
|
+
signal: AbortSignal.timeout(30000),
|
|
286
|
+
});
|
|
287
|
+
const data = await res.json().catch(() => ({}));
|
|
288
|
+
if (!res.ok || data.error) {
|
|
289
|
+
throw new Error(`Velaro admin-tools ${toolName} -> ${res.status}: ${data.error?.message || JSON.stringify(data).slice(0, 300)}`);
|
|
290
|
+
}
|
|
291
|
+
const content = data.result?.content?.[0]?.text;
|
|
292
|
+
if (content === undefined) throw new Error('Empty response from admin-tools gateway');
|
|
293
|
+
return content;
|
|
294
|
+
}
|
|
295
|
+
|
|
248
296
|
// Call the hosted MCP gateway (JSON-RPC) for tools that are dispatched server-side.
|
|
249
297
|
async function rpc(toolName, args) {
|
|
298
|
+
if (MESSAGING_ADMIN_TOOLS.has(toolName)) return messagingAdminRpc(toolName, args);
|
|
250
299
|
const body = {
|
|
251
300
|
jsonrpc: '2.0',
|
|
252
301
|
id: 1,
|
|
253
302
|
method: 'tools/call',
|
|
254
303
|
params: { name: toolName, arguments: args ?? {} },
|
|
255
304
|
};
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
305
|
+
const send = async () => {
|
|
306
|
+
const s = gatewaySession || await gatewayInit();
|
|
307
|
+
return fetch(`${API_BASE}/mcp`, {
|
|
308
|
+
method: 'POST',
|
|
309
|
+
headers: { Authorization: authHeader(), 'Content-Type': 'application/json', 'Mcp-Session-Id': s.sid, 'MCP-Protocol-Version': s.version },
|
|
310
|
+
body: JSON.stringify(body),
|
|
311
|
+
});
|
|
312
|
+
};
|
|
313
|
+
let res = await send();
|
|
314
|
+
if (res.status === 404) { gatewaySession = null; res = await send(); }
|
|
261
315
|
if (!res.ok) {
|
|
262
316
|
const text = await res.text().catch(() => '');
|
|
263
317
|
throw new Error(`Velaro MCP gateway ${toolName} -> ${res.status}: ${text.slice(0, 300)}`);
|
|
@@ -2017,6 +2071,39 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
2017
2071
|
},
|
|
2018
2072
|
},
|
|
2019
2073
|
|
|
2074
|
+
// -- Email delivery visibility (Velaro staff only; velaro-messaging SuperAdmin) --
|
|
2075
|
+
{
|
|
2076
|
+
name: 'email_queue_status',
|
|
2077
|
+
description: 'Velaro staff only. Live ACS email send-queue health: queued/parked counts, oldest open item age, cooldown state, retries, 429s, sends in the last hour and a drain-time estimate. Aggregates only, no message bodies. Wraps GET SuperAdmin/email-delivery/status on velaro-messaging.',
|
|
2078
|
+
inputSchema: {
|
|
2079
|
+
type: 'object',
|
|
2080
|
+
properties: { siteId: { type: 'number', description: 'Optional: limit to one site ID' } },
|
|
2081
|
+
required: [],
|
|
2082
|
+
},
|
|
2083
|
+
},
|
|
2084
|
+
{
|
|
2085
|
+
name: 'email_delivery_stats',
|
|
2086
|
+
description: 'Velaro staff only. Email throughput per day and per hour, retry counts, priority/tag breakdown, attempts histogram, and ACS-side delivery outcomes (succeeded/failed/pending). Wraps GET SuperAdmin/email-delivery/stats on velaro-messaging.',
|
|
2087
|
+
inputSchema: {
|
|
2088
|
+
type: 'object',
|
|
2089
|
+
properties: {
|
|
2090
|
+
siteId: { type: 'number', description: 'Optional: limit to one site ID' },
|
|
2091
|
+
days: { type: 'number', description: 'Daily window, 1-30 (default 30; ACS-side capped at 14)' },
|
|
2092
|
+
hours: { type: 'number', description: 'Hourly window, 1-168 (default 48)' },
|
|
2093
|
+
},
|
|
2094
|
+
required: [],
|
|
2095
|
+
},
|
|
2096
|
+
},
|
|
2097
|
+
{
|
|
2098
|
+
name: 'email_queue_replay',
|
|
2099
|
+
description: 'Velaro staff ADMIN ONLY. Replays the queued ACS email backlog now. dryRun defaults to TRUE and only reports what a replay would send; pass dryRun=false to clear the cooldown and drain the queue immediately. Wraps POST SuperAdmin/email-delivery/replay on velaro-messaging.',
|
|
2100
|
+
inputSchema: {
|
|
2101
|
+
type: 'object',
|
|
2102
|
+
properties: { dryRun: { type: 'boolean', description: 'Default true. Set false to actually replay.' } },
|
|
2103
|
+
required: [],
|
|
2104
|
+
},
|
|
2105
|
+
},
|
|
2106
|
+
|
|
2020
2107
|
// ── Unified Outbound Activity (Klaviyo-parity Gap 6) ──────────────────────
|
|
2021
2108
|
{
|
|
2022
2109
|
name: 'outbound_activity_list',
|
|
@@ -2448,6 +2535,26 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
2448
2535
|
required: ['siteId'],
|
|
2449
2536
|
},
|
|
2450
2537
|
},
|
|
2538
|
+
// -- Staff: schedule adherence by site + role (SupportTools-style: explicit siteId, never auth-derived) --
|
|
2539
|
+
// Backend: velaro-messaging ReportsController.ScheduleAdherenceDiagnosticsAsync, gated by
|
|
2540
|
+
// IsSuperAdminAsync(allowSupport: true) -> 403 for non-staff. role=agent REQUIRES agentId (fails closed).
|
|
2541
|
+
{
|
|
2542
|
+
name: 'support_schedule_adherence',
|
|
2543
|
+
description: 'Velaro staff only. Schedule-adherence overview (totals + per-agent metrics), per-day breakdown (incl. nonAvailableMinutes), current status (intraday) and range-end-day timeline for a site, scoped as a manager (all agents) or as one agent (own row only) -- the exact scope the customer UI applies. Use for schedule-adherence QA instead of a browser pass.',
|
|
2544
|
+
inputSchema: {
|
|
2545
|
+
type: 'object',
|
|
2546
|
+
properties: {
|
|
2547
|
+
siteId: { type: 'number', description: 'Target customer site ID' },
|
|
2548
|
+
role: { type: 'string', enum: ['manager', 'agent'], description: 'Scope to apply. Default manager.' },
|
|
2549
|
+
agentId: { type: 'number', description: 'WorkspaceUser id; required when role=agent.' },
|
|
2550
|
+
start: { type: 'string', description: 'yyyy-MM-dd (default: 6 days ago, site tz).' },
|
|
2551
|
+
end: { type: 'string', description: 'yyyy-MM-dd (default: today, site tz).' },
|
|
2552
|
+
tz: { type: 'string', description: 'IANA time zone override.' },
|
|
2553
|
+
},
|
|
2554
|
+
required: ['siteId'],
|
|
2555
|
+
},
|
|
2556
|
+
},
|
|
2557
|
+
|
|
2451
2558
|
{
|
|
2452
2559
|
name: 'support_site_agents',
|
|
2453
2560
|
description: 'List all agents/admins on a customer site with their roles, active status, and last login date. Velaro staff only.',
|
|
@@ -6638,6 +6745,30 @@ async function handleTool(name, args) {
|
|
|
6638
6745
|
return lines.join('\n');
|
|
6639
6746
|
}
|
|
6640
6747
|
|
|
6748
|
+
// -- Email delivery visibility (Velaro staff only) --
|
|
6749
|
+
case 'email_queue_status': {
|
|
6750
|
+
const q = args.siteId != null ? `?siteId=${encodeURIComponent(args.siteId)}` : '';
|
|
6751
|
+
const r = await messagingGet(`/SuperAdmin/email-delivery/status${q}`);
|
|
6752
|
+
return JSON.stringify(r, null, 2);
|
|
6753
|
+
}
|
|
6754
|
+
|
|
6755
|
+
case 'email_delivery_stats': {
|
|
6756
|
+
const params = new URLSearchParams();
|
|
6757
|
+
if (args.siteId != null) params.set('siteId', String(args.siteId));
|
|
6758
|
+
if (args.days != null) params.set('days', String(args.days));
|
|
6759
|
+
if (args.hours != null) params.set('hours', String(args.hours));
|
|
6760
|
+
const qs = params.toString();
|
|
6761
|
+
const r = await messagingGet(`/SuperAdmin/email-delivery/stats${qs ? '?' + qs : ''}`);
|
|
6762
|
+
return JSON.stringify(r, null, 2);
|
|
6763
|
+
}
|
|
6764
|
+
|
|
6765
|
+
case 'email_queue_replay': {
|
|
6766
|
+
// Safe by default: anything other than an explicit boolean false stays a dry run.
|
|
6767
|
+
const dryRun = args.dryRun !== false;
|
|
6768
|
+
const r = await messagingPost(`/SuperAdmin/email-delivery/replay?dryRun=${dryRun}`);
|
|
6769
|
+
return JSON.stringify(r, null, 2);
|
|
6770
|
+
}
|
|
6771
|
+
|
|
6641
6772
|
// ── Unified Outbound Activity ──────────────────────────────────────────
|
|
6642
6773
|
case 'outbound_activity_list':
|
|
6643
6774
|
case 'outbound_activity_for_contact': {
|
|
@@ -7074,6 +7205,13 @@ async function handleTool(name, args) {
|
|
|
7074
7205
|
).join('\n');
|
|
7075
7206
|
}
|
|
7076
7207
|
|
|
7208
|
+
case 'support_schedule_adherence': {
|
|
7209
|
+
if (args.role === 'agent' && !args.agentId) throw new Error('agentId is required when role=agent');
|
|
7210
|
+
const qs = new URLSearchParams({ detail: 'true' });
|
|
7211
|
+
for (const k of ['role', 'agentId', 'start', 'end', 'tz']) if (args[k] != null && args[k] !== '') qs.set(k, String(args[k]));
|
|
7212
|
+
return JSON.stringify(await messagingGet(`/Reports/sites/${encodeURIComponent(args.siteId)}/schedule-adherence-diagnostics?${qs}`), null, 2);
|
|
7213
|
+
}
|
|
7214
|
+
|
|
7077
7215
|
case 'support_site_agents': {
|
|
7078
7216
|
const agents = await api('GET', `/SupportTools/sites/${args.siteId}/agents`);
|
|
7079
7217
|
if (!Array.isArray(agents) || !agents.length) return `No agents found for site ${args.siteId}.`;
|
|
@@ -8249,7 +8387,7 @@ async function handleTool(name, args) {
|
|
|
8249
8387
|
}
|
|
8250
8388
|
|
|
8251
8389
|
case 'billing_plan_status': {
|
|
8252
|
-
const s = await api('GET', '/BillingPayment/plan-status');
|
|
8390
|
+
const s = await api('GET', '/BillingPayment/plan-status', undefined, MESSAGING_API);
|
|
8253
8391
|
const lines = [`Site ${s.siteId} — Subscription Status`];
|
|
8254
8392
|
if (s.subscription) {
|
|
8255
8393
|
lines.push(`Seats: ${s.subscription.maxLicensedSeats} | Conv/mo: ${s.subscription.maxConversationsPerMonth || '∞'} | Self-serve: ${s.subscription.enableSelfServeSeats ? 'YES' : 'no'}`);
|