@velaro/mcp-server 2.0.0 → 2.0.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velaro/mcp-server",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
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
@@ -1067,6 +1067,8 @@ Key node shapes:
1067
1067
  propName:"myAnswerVar" } // variable that stores the result as JSON array string
1068
1068
  • condition: { type:"action", actionType:"condition", conditionOptions:[...] }
1069
1069
  • transferToAI: { type:"action", actionType:"transferToAI", aiConfigId:N }
1070
+ • selectPreChat: { type:"action", actionType:"selectPreChat", surveyId:N, surveyCompact:true|false|null, child:N }
1071
+ surveyCompact (optional tri-state): null/omitted = follow the survey's own compactMode, true = compact, false = regular.
1070
1072
  • sendSurvey: { type:"action", actionType:"sendSurvey", sendSurveyId:N, surveyDeliveryMode:"conversational"|"form", closeAfterSurvey:true|false, child:N }
1071
1073
  closeAfterSurvey (optional, default false): when true the chat closes once the visitor completes the survey (use for Unavailable/after-hours surveys that replace a live chat). Omit to keep the old behaviour.
1072
1074
  AI follow-up suggestions ("AI mode"): add suggestFollowUps:true|false (omit = use the account default, set via ai_followups_site_set), suggestFollowUpCount:2-4, suggestFollowUpInstruction:"<=300 chars". After each web-chat bot reply the bot offers short tappable follow-up replies; typing stays available.
@@ -1132,13 +1134,14 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
1132
1134
  },
1133
1135
  {
1134
1136
  name: 'survey_upsert',
1135
- description: 'Create (omit id or set id=0) or update (set id) a survey. body must be the full SurveyViewModel JSON shape (id, name, questions[], each question with type/text/options/required/variable/etc.). Use survey_get on an existing survey to fetch this full shape as a template rather than guessing the question schema from scratch. Each question also accepts isThemeAnchor (boolean, default false: this answer sets the Chat Intelligence topic) and themeMapping (string: JSON object mapping answer text to a site topic, e.g. "{\\"Pay my bill\\":\\"Billing Questions\\"}"). Both only take effect when the site has the multi-topic setting on.',
1137
+ description: 'Create (omit id or set id=0) or update (set id) a survey. body must be the full SurveyViewModel JSON shape (id, name, questions[], each question with type/text/options/required/variable/etc.). Use survey_get on an existing survey to fetch this full shape as a template rather than guessing the question schema from scratch. Each question also accepts isThemeAnchor (boolean, default false: this answer sets the Chat Intelligence topic) and themeMapping (string: JSON object mapping answer text to a site topic, e.g. "{\\"Pay my bill\\":\\"Billing Questions\\"}"). Both only take effect when the site has the multi-topic setting on. body.compactMode is tri-state: null/omitted = follow default (regular size), true = compact, false = regular.',
1136
1138
  inputSchema: {
1137
1139
  type: 'object',
1138
1140
  properties: { body: {
1139
1141
  type: 'object',
1140
1142
  description: 'Full SurveyViewModel JSON',
1141
1143
  properties: {
1144
+ compactMode: { type: ['boolean', 'null'], description: 'Tri-state survey size: null/omitted = follow default (regular size), true = compact, false = regular' },
1142
1145
  questions: {
1143
1146
  type: 'array',
1144
1147
  items: {
@@ -1582,6 +1585,28 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
1582
1585
  },
1583
1586
  },
1584
1587
 
1588
+ {
1589
+ name: 'agent_force_sign_out',
1590
+ description: 'Security control: force one agent to sign in again. Revokes every active session of that agent now (their existing tokens stop working) and sets them Offline. Administrator only; the agent must belong to your site; audited (who/when/target). You cannot force sign out your own account.',
1591
+ inputSchema: {
1592
+ type: 'object',
1593
+ properties: { userId: { type: 'number', description: 'Agent user id from agent_list' } },
1594
+ required: ['userId'],
1595
+ },
1596
+ },
1597
+ {
1598
+ name: 'agent_set_status',
1599
+ description: 'Set an agent availability status (Available, Busy, Away, Offline). Use Offline or Away to make an agent Unavailable for routing. Site manager only; the agent must belong to your site; audited with the acting admin.',
1600
+ inputSchema: {
1601
+ type: 'object',
1602
+ properties: {
1603
+ userId: { type: 'number', description: 'Agent user id from agent_list' },
1604
+ status: { type: 'string', enum: ['Available', 'Busy', 'Away', 'Offline'] },
1605
+ },
1606
+ required: ['userId', 'status'],
1607
+ },
1608
+ },
1609
+
1585
1610
  // ── Dynamics per-team overrides (effective = team override ?? connection default) ──
1586
1611
  {
1587
1612
  name: 'dynamics_team_overrides_get',
@@ -2061,6 +2086,27 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
2061
2086
  },
2062
2087
  },
2063
2088
  },
2089
+ {
2090
+ name: 'safety_policy_get',
2091
+ description: 'Get the safety controls policy: per control (emergency_self_harm, abuse_ladder, threat_detection, input_jailbreak, output_filter) whether it is on for AI bot and human agent, any overrides, the always-on floor, a warning when anything is off, and the acknowledgement text and version needed to turn a control off. Wraps GET SafetyPolicy.',
2092
+ inputSchema: { type: 'object', properties: {} },
2093
+ },
2094
+ {
2095
+ name: 'safety_policy_set',
2096
+ description: 'Turn a safety control on, off, or back to default. Every control is ON by default. Turning one OFF is refused unless acknowledged=true AND ackTextVersion equals the current acknowledgement version (read it from safety_policy_get and show the acknowledgement text to the account owner first). This tool never accepts the acknowledgement on anyone\'s behalf. Turning on or resetting needs no acknowledgement. Wraps PUT SafetyPolicy.',
2097
+ inputSchema: {
2098
+ type: 'object',
2099
+ properties: {
2100
+ control: { type: 'string', enum: ['emergency_self_harm', 'abuse_ladder', 'threat_detection', 'input_jailbreak', 'output_filter'] },
2101
+ audience: { type: 'string', enum: ['ai_bot', 'human_agent'] },
2102
+ featureKey: { type: 'string', description: 'Optional per-feature/channel scope (chat, voice, sms, whatsapp, email). Omit for the whole account.' },
2103
+ enabled: { type: ['boolean', 'null'], description: 'true = on, false = off, null = clear the override (default on)' },
2104
+ acknowledged: { type: 'boolean', description: 'Required true when enabled=false: the account owner accepted the acknowledgement text' },
2105
+ ackTextVersion: { type: 'string', description: 'Required when enabled=false: the acknowledgement version from safety_policy_get' },
2106
+ },
2107
+ required: ['control', 'audience', 'enabled'],
2108
+ },
2109
+ },
2064
2110
  {
2065
2111
  name: 'safety_settings_test',
2066
2112
  description: 'Send a test safety alert to the configured recipients. Wraps POST SafetyEvents/settings/test.',
@@ -3314,6 +3360,34 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
3314
3360
  },
3315
3361
 
3316
3362
 
3363
+ {
3364
+ name: 'magento_get_sidebar_fields',
3365
+ description: 'Get the Magento fields shown in the agent sidebar panel for the authenticated site (empty list = out-of-box defaults). Calls messaging GET Magento/data/sidebar-fields. Site comes from the bearer token only. Read-only.',
3366
+ inputSchema: { type: 'object', properties: {} },
3367
+ },
3368
+ {
3369
+ name: 'magento_set_sidebar_fields',
3370
+ description: 'Set the Magento fields shown in the agent sidebar panel for the authenticated site. Partial update: omit fields to leave the saved list untouched; an empty array clears it. Site comes from the bearer token only. Calls messaging PUT Magento/data/sidebar-fields.',
3371
+ inputSchema: {
3372
+ type: 'object',
3373
+ properties: {
3374
+ fields: {
3375
+ type: 'array',
3376
+ description: 'Sidebar fields to show, in order',
3377
+ items: {
3378
+ type: 'object',
3379
+ properties: {
3380
+ fieldName: { type: 'string', description: 'Magento field name, e.g. "email"' },
3381
+ entity: { type: 'string', description: 'Entity the field belongs to, e.g. "customer" or "order"' },
3382
+ label: { type: 'string', description: 'Optional display label' },
3383
+ },
3384
+ required: ['fieldName'],
3385
+ },
3386
+ },
3387
+ },
3388
+ },
3389
+ },
3390
+
3317
3391
  {
3318
3392
  name: 'business_action_list',
3319
3393
  description: 'List the shared business actions registered on the messaging API (e.g. get_customer_pricing, create_quote): name, whether it is a write, data class, freshness window, request JSON schema and the AI skill tool names that project it. One definition is served by the Velaro catalog or a connected ERP (SAP) beneath it. Site scope comes from the caller\'s auth context. Calls GET /BusinessActions.',
@@ -3480,6 +3554,29 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
3480
3554
  inputSchema: { type: 'object', properties: {}, required: [] },
3481
3555
  },
3482
3556
 
3557
+ {
3558
+ name: 'teams_phone_list_agents',
3559
+ description: "List the site's agents with their Microsoft Teams user Object ID mapping (agent -> Entra user), used to route inbound Teams Phone calls to the right agent. Admin only. Lives in velaro-messaging — requires VELARO_MESSAGING_API.",
3560
+ inputSchema: { type: 'object', properties: {}, required: [] },
3561
+ },
3562
+ {
3563
+ name: 'teams_phone_set_agent',
3564
+ description: 'Map one Velaro agent (workspace user id) to a Microsoft Entra user Object ID (GUID) so inbound Teams Phone calls reach them. Upsert; pass "" to clear. Must be unique per site. Lives in velaro-messaging — requires VELARO_MESSAGING_API.',
3565
+ inputSchema: {
3566
+ type: 'object',
3567
+ properties: {
3568
+ agentId: { type: 'number', description: 'Velaro agent (workspace user) id from teams_phone_list_agents.' },
3569
+ teamsUserId: { type: 'string', description: 'Entra user Object ID (GUID). "" clears the mapping.' },
3570
+ },
3571
+ required: ['agentId', 'teamsUserId'],
3572
+ },
3573
+ },
3574
+ {
3575
+ name: 'teams_phone_delete_agent',
3576
+ description: "Clear one agent's Teams user mapping. Calls to that Teams user will no longer route to the agent.",
3577
+ inputSchema: { type: 'object', properties: { agentId: { type: 'number', description: 'Velaro agent (workspace user) id.' } }, required: ['agentId'] },
3578
+ },
3579
+
3483
3580
  // ── Webflow (config CRUD) ──────────────────────────────────────────────────
3484
3581
  // Lives in velaro-admin's own WebflowController.cs (this API), not velaro-messaging.
3485
3582
  // The other 6 Webflow AI skills (webflow_list_sites, webflow_get_site,
@@ -3827,6 +3924,8 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
3827
3924
  },
3828
3925
  },
3829
3926
 
3927
+ // ── Billing agents + package builder (staff; docs/architecture/billing-agents-and-package-builder.md) ──
3928
+ { name: 'package_builder_my', description: 'Customer-safe: the caller\'s own package name and what it includes. No prices or staff data.', inputSchema: { type: 'object', properties: {} } },
3830
3929
  // ── Pricing Catalog ───────────────────────────────────────────────────────
3831
3930
  {
3832
3931
  name: 'pricing_catalog_list',
@@ -5809,6 +5908,19 @@ reply: ${r?.reply}`;
5809
5908
  case 'agent_list':
5810
5909
  return await rpc('agent_list', args);
5811
5910
 
5911
+ case 'agent_force_sign_out': {
5912
+ if (!Number.isInteger(args.userId) || args.userId <= 0) return 'userId is required (positive integer).';
5913
+ await messagingPost(`/Users/${args.userId}/ForceSignOut`, {});
5914
+ return `Agent ${args.userId} was signed out of every session and set to Offline. They must sign in again.`;
5915
+ }
5916
+
5917
+ case 'agent_set_status': {
5918
+ if (!Number.isInteger(args.userId) || args.userId <= 0) return 'userId is required (positive integer).';
5919
+ if (!['Available', 'Busy', 'Away', 'Offline'].includes(args.status)) return 'status must be Available, Busy, Away or Offline.';
5920
+ await messagingPost('/UserStatus/admin', { userId: args.userId, status: args.status });
5921
+ return `Agent ${args.userId} set to ${args.status}.`;
5922
+ }
5923
+
5812
5924
  // ── Teams ──────────────────────────────────────────────────────────────
5813
5925
  case 'team_list':
5814
5926
  return await rpc('team_list', args);
@@ -6386,6 +6498,21 @@ reply: ${r?.reply}`;
6386
6498
  }
6387
6499
  return JSON.stringify(await messagingPut('/SafetyEvents/settings', next), null, 2);
6388
6500
  }
6501
+ case 'safety_policy_get':
6502
+ return JSON.stringify(await messagingGet('/SafetyPolicy'), null, 2);
6503
+ case 'safety_policy_set': {
6504
+ if (args.enabled === false && (args.acknowledged !== true || !args.ackTextVersion)) {
6505
+ throw new Error('Turning a safety control off requires acknowledged=true and ackTextVersion (from safety_policy_get). This tool never accepts the acknowledgement automatically.');
6506
+ }
6507
+ const body = {
6508
+ control: args.control,
6509
+ audience: args.audience,
6510
+ featureKey: args.featureKey ?? null,
6511
+ enabled: args.enabled === undefined ? null : args.enabled,
6512
+ ...(args.enabled === false ? { acknowledged: true, ackTextVersion: args.ackTextVersion } : {}),
6513
+ };
6514
+ return JSON.stringify(await messagingPut('/SafetyPolicy', body), null, 2);
6515
+ }
6389
6516
  case 'safety_settings_test':
6390
6517
  return JSON.stringify(await messagingPost('/SafetyEvents/settings/test'), null, 2);
6391
6518
  case 'safety_need_help':
@@ -7577,6 +7704,21 @@ reply: ${r?.reply}`;
7577
7704
  return lines.join('\n');
7578
7705
  }
7579
7706
 
7707
+ case 'magento_get_sidebar_fields': {
7708
+ const r = await messagingRequest('GET', '/Magento/data/sidebar-fields');
7709
+ return JSON.stringify(r, null, 2);
7710
+ }
7711
+
7712
+ case 'magento_set_sidebar_fields': {
7713
+ const body = {};
7714
+ if (args.fields !== undefined) {
7715
+ if (!Array.isArray(args.fields)) return 'fields must be an array.';
7716
+ body.fields = args.fields;
7717
+ }
7718
+ const r = await messagingRequest('PUT', '/Magento/data/sidebar-fields', body);
7719
+ return JSON.stringify(r, null, 2);
7720
+ }
7721
+
7580
7722
  case 'business_action_list': {
7581
7723
  const r = await messagingRequest('GET', '/BusinessActions');
7582
7724
  return JSON.stringify(r, null, 2);
@@ -7730,6 +7872,20 @@ reply: ${r?.reply}`;
7730
7872
  return lines.join(String.fromCharCode(10));
7731
7873
  }
7732
7874
 
7875
+ case 'teams_phone_list_agents': {
7876
+ const agents = await messagingGet('/TeamsPhone/Agents');
7877
+ if (!Array.isArray(agents) || agents.length === 0) return 'No agents found.';
7878
+ return agents.map(a => `${a.id} ${`${a.firstName ?? ''} ${a.lastName ?? ''}`.trim()} ${a.email ?? ''} ${a.teamsUserId || '(unmapped)'}`).join(String.fromCharCode(10));
7879
+ }
7880
+ case 'teams_phone_set_agent': {
7881
+ const r = await messagingPut(`/TeamsPhone/Agents/${encodeURIComponent(args.agentId)}`, { teamsUserId: args.teamsUserId });
7882
+ return r?.configured ? `Agent ${args.agentId} mapped to Teams user ${r.teamsUserId}.` : `Agent ${args.agentId} mapping cleared.`;
7883
+ }
7884
+ case 'teams_phone_delete_agent': {
7885
+ await messagingDel(`/TeamsPhone/Agents/${encodeURIComponent(args.agentId)}`);
7886
+ return `Agent ${args.agentId} mapping cleared.`;
7887
+ }
7888
+
7733
7889
  // ── Shopify write policy (velaro-messaging /api/Shopify/WritePolicy) ───────
7734
7890
  case 'shopify_get_write_policy': {
7735
7891
  return JSON.stringify(await messagingGet('/Shopify/WritePolicy'), null, 2);
@@ -8154,6 +8310,9 @@ ${JSON.stringify(result, null, 2)}`;
8154
8310
  return lines.join('\n');
8155
8311
  }
8156
8312
 
8313
+ // ── Billing agents + package builder ──────────────────────────────────────
8314
+ case 'package_builder_my': return JSON.stringify(await api('GET', '/PackageBuilder/my'), null, 2);
8315
+
8157
8316
  // ── Pricing Catalog ───────────────────────────────────────────────────────
8158
8317
  case 'pricing_catalog_list': {
8159
8318
  const pkgs = await api('GET', '/Pricing/catalog');
@@ -26,7 +26,7 @@ const MUTATING = new Set([
26
26
  'pickup_update_config', 'payment_recovery_update_config', 'recruiting_sync_jobs',
27
27
  'ai_config_set_reply_format', 'ai_model_set_site_thinking', 'ai_model_set_site_pin',
28
28
  'entitlement_reconcile', 'entitlement_sync_stripe', 'entitlement_backfill_subscriptions',
29
- 'calendly_configure', 'acuity_configure', 'ms_bookings_configure',
29
+ 'calendly_configure', 'acuity_configure', 'ms_bookings_configure', 'magento_set_sidebar_fields',
30
30
  'zoom_phone_set_config',
31
31
  ]);
32
32