@velaro/mcp-server 0.6.66 → 0.6.68

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.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/server.js +63 -6
package/README.md CHANGED
@@ -132,6 +132,8 @@ This works with any MCP client that supports HTTP/SSE transport — including Cl
132
132
  | `team_list` | List all teams for this site |
133
133
  | `routing_list` | List all routing rules ordered by priority |
134
134
  | `routing_get` | Get routing rule details including rule and value JSON |
135
+ | `routing_team_get_config` | Team-level routing config (routing type, queue timeout, idle alerts); omit teamId for site default |
136
+ | `routing_team_update_config` | Partial update of team-level routing config; unknown fields pass through |
135
137
  | `routing_get_policy` | Site auto-routing policy including reassignment mode and the agent inactivity chain |
136
138
  | `routing_update_policy` | Update the policy, reassignment mode and inactivity chain (mode is always sent back) |
137
139
  | `routing_simulate_inactivity` | DRY RUN of the inactivity chain for a mocked scenario, nothing is sent |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velaro/mcp-server",
3
- "version": "0.6.66",
3
+ "version": "0.6.68",
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
@@ -166,6 +166,12 @@ const MCP_SUBMISSION_LIMIT = 50;
166
166
  const messagingGet = (path) => messagingRequest('GET', path);
167
167
  const messagingPost = (path, body) => messagingRequest('POST', path, body);
168
168
  const messagingPut = (path, body) => messagingRequest('PUT', path, body);
169
+ function teamRoutingConfigPath(teamId) {
170
+ if (teamId === undefined || teamId === null || teamId === '') return '/RoutingConfig';
171
+ const n = Number(teamId);
172
+ if (!Number.isInteger(n) || n <= 0) throw new Error('teamId must be a positive integer.');
173
+ return `/RoutingConfig?teamId=${n}`;
174
+ }
169
175
  const messagingPatch = (path, body) => messagingRequest('PATCH', path, body);
170
176
  const messagingDel = (path) => messagingRequest('DELETE', path);
171
177
 
@@ -1571,6 +1577,35 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
1571
1577
  required: [],
1572
1578
  },
1573
1579
  },
1580
+ {
1581
+ name: 'routing_team_get_config',
1582
+ description: 'Get team-level routing config (routing type roundRobin/leastBusy/longestIdle, routing action, queue timeout, idle queue alert settings, schedule-aware routing). Pass teamId for a team row, omit for the site default row. Site is taken from auth.',
1583
+ inputSchema: { type: 'object', properties: { teamId: { type: 'number', description: 'Team (group) id; omit for the site default row' } }, required: [] },
1584
+ },
1585
+ {
1586
+ name: 'routing_team_update_config',
1587
+ description: 'Partially update team-level routing config. Only fields you pass change; null means inherit. Any extra field in `config` is passed through to messaging unchanged (new routing types and fields work without an MCP release).',
1588
+ inputSchema: {
1589
+ type: 'object',
1590
+ properties: {
1591
+ teamId: { type: 'number', description: 'Team (group) id; omit for the site default row' },
1592
+ routingType: { type: 'string', description: 'roundRobin | leastBusy | longestIdle (validated by messaging)' },
1593
+ routingAction: { type: 'string', description: 'Routing action (validated by messaging)' },
1594
+ queueTimeoutEnabled: { type: ['boolean', 'null'] },
1595
+ queueTimeoutMinutes: { type: ['number', 'null'] },
1596
+ prioritizeAgents: { type: ['boolean', 'null'] },
1597
+ idleQueueAlertEnabled: { type: ['boolean', 'null'] },
1598
+ idleQueueAlertAfterMinutes: { type: ['number', 'null'] },
1599
+ idleQueueAlertRepeatMinutes: { type: ['number', 'null'] },
1600
+ idleQueueAlertMaxPerConversation: { type: ['number', 'null'] },
1601
+ idleQueueAlertTitle: { type: ['string', 'null'] },
1602
+ idleQueueAlertBody: { type: ['string', 'null'] },
1603
+ scheduleAwareRoutingEnabled: { type: ['boolean', 'null'] },
1604
+ config: { type: 'object', additionalProperties: true, description: 'Any other RoutingConfig fields, passed through as-is' },
1605
+ },
1606
+ required: [],
1607
+ },
1608
+ },
1574
1609
  {
1575
1610
  name: 'routing_get_decision_log',
1576
1611
  description: 'Get the routing decision log for a specific conversation — shows every routing step, reroute attempt, agent assignments, terminal action, and rule source. Use to diagnose "why was this conversation handled this way?"',
@@ -3989,15 +4024,16 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
3989
4024
  // Moshky AI safety rule and ConversationTriageService's prompt assembly (velaro-messaging).
3990
4025
  {
3991
4026
  name: 'focus_get_triage_rules',
3992
- description: 'Get this site\'s current AI triage rules: which of the 5 fixed labels (urgent, needs_reply, offer, fyi, waiting) are enabled, plus the per-channel triage-on-off and priority weight already owned by the channel-config endpoint, shown here for context. A label missing/never configured defaults to enabled.',
4027
+ description: 'Get this site\'s current AI triage rules: which of the 5 fixed labels (urgent, needs_reply, offer, fyi, waiting) are enabled, plus the per-channel triage-on-off and priority weight already owned by the channel-config endpoint, shown here for context. A label missing/never configured defaults to enabled. Also returns triageMode ("off" or "economy").',
3993
4028
  inputSchema: { type: 'object', properties: {} },
3994
4029
  },
3995
4030
  {
3996
4031
  name: 'focus_update_triage_rules',
3997
- description: 'Enable or disable one or more of the 5 fixed AI triage labels (urgent, needs_reply, offer, fyi, waiting) for this site. Apply-if-present -- only labels included are changed. Rejects any label name outside the fixed set. Disabling all 5 fails safe back to all-enabled server-side rather than breaking triage.',
4032
+ description: 'Enable or disable one or more of the 5 fixed AI triage labels (urgent, needs_reply, offer, fyi, waiting) for this site. Apply-if-present -- only labels included are changed. Rejects any label name outside the fixed set. Disabling all 5 fails safe back to all-enabled server-side rather than breaking triage. Optionally set triageMode: "economy" classifies obvious messages (auto-replies, clear urgency) without an AI call, lowering AI usage; "off" keeps current behavior. Unspecified fields are preserved.',
3998
4033
  inputSchema: {
3999
4034
  type: 'object',
4000
4035
  properties: {
4036
+ triageMode: { type: 'string', enum: ['off', 'economy'], description: 'Optional. "economy" skips the AI call for obvious messages; "off" (default) keeps current behavior.' },
4001
4037
  labels: {
4002
4038
  type: 'array',
4003
4039
  description: 'Labels to update.',
@@ -4011,7 +4047,6 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
4011
4047
  },
4012
4048
  },
4013
4049
  },
4014
- required: ['labels'],
4015
4050
  },
4016
4051
  },
4017
4052
 
@@ -5635,6 +5670,21 @@ async function handleTool(name, args) {
5635
5670
  return 'Circuit breaker reset. Smart routing is now active again.';
5636
5671
  }
5637
5672
 
5673
+ case 'routing_team_get_config': {
5674
+ return JSON.stringify(await messagingGet(teamRoutingConfigPath(args.teamId)), null, 2);
5675
+ }
5676
+
5677
+ case 'routing_team_update_config': {
5678
+ const { teamId, config, ...rest } = args;
5679
+ const body = {};
5680
+ for (const [k, v] of Object.entries({ ...(config && typeof config === 'object' ? config : {}), ...rest })) {
5681
+ if (v !== undefined) body[k] = v;
5682
+ }
5683
+ if (!Object.keys(body).length) throw new Error('No fields to update. Pass at least one field.');
5684
+ await messagingPut(teamRoutingConfigPath(teamId), body);
5685
+ return `Team routing config updated (${teamId ? `team ${teamId}` : 'site default'}): ${Object.keys(body).join(', ')}.`;
5686
+ }
5687
+
5638
5688
  case 'routing_get_policy': {
5639
5689
  const p = await messagingGet('/AutoRoutingPolicy');
5640
5690
  const terminalLabel = terminalActionLabel(p.terminalActionOverride);
@@ -8776,10 +8826,17 @@ async function handleTool(name, args) {
8776
8826
  case 'focus_get_triage_rules':
8777
8827
  return await messagingGet('/Focus/triage-rules');
8778
8828
  case 'focus_update_triage_rules': {
8779
- if (!Array.isArray(args.labels) || args.labels.length === 0) {
8780
- throw new Error('labels is required and must be a non-empty array.');
8829
+ const hasLabels = Array.isArray(args.labels) && args.labels.length > 0;
8830
+ const hasMode = args.triageMode !== undefined && args.triageMode !== null;
8831
+ if (!hasLabels && !hasMode) {
8832
+ throw new Error('Provide labels (non-empty array) and/or triageMode.');
8833
+ }
8834
+ if (hasMode && !['off', 'economy'].includes(args.triageMode)) {
8835
+ throw new Error('triageMode must be "off" or "economy".');
8781
8836
  }
8782
- const body = args.labels.map((l) => ({ Label: l.label, Enabled: l.enabled }));
8837
+ const labels = hasLabels ? args.labels.map((l) => ({ Label: l.label, Enabled: l.enabled })) : undefined;
8838
+ // Legacy array body when only labels are sent; object body when triageMode is involved.
8839
+ const body = hasMode ? { ...(labels ? { labels } : {}), triageMode: args.triageMode } : labels;
8783
8840
  return await messagingPut('/Focus/triage-rules', body);
8784
8841
  }
8785
8842