@velaro/mcp-server 0.1.0 → 0.4.0
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/README.md +230 -16
- package/package.json +32 -32
- package/server.js +666 -1
package/README.md
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
# @velaro/mcp-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The only customer support platform with a native, self-updating MCP server.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Connect Claude, Claude Code, Claude Desktop, or any MCP-compatible AI agent directly to your Velaro account — knowledge base, workflows, conversation data, and 596 AI skills across 20+ commerce and CRM platforms.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npx @velaro/mcp-server
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Set your
|
|
12
|
-
|
|
11
|
+
Set your key and go:
|
|
13
12
|
```bash
|
|
14
13
|
VELARO_MCP_KEY=vel_live_... npx @velaro/mcp-server
|
|
15
14
|
```
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Why Velaro MCP Is Different
|
|
19
|
+
|
|
20
|
+
Most chat platforms require webhooks, custom API wrappers, or point-to-point integrations to give an AI agent access to customer data. Velaro ships a published npm package that turns Claude into a first-class operator of your entire support infrastructure — no wrapper code, no custom authentication layer, no waiting for an API integration to be built.
|
|
21
|
+
|
|
22
|
+
- **One command install** — `npx @velaro/mcp-server` pulls the latest version automatically
|
|
23
|
+
- **Hosted HTTP endpoint** — no Node.js required; point Claude Desktop at `https://velaro-admin-staging.azurewebsites.net/mcp` with a Bearer token
|
|
24
|
+
- **Auto-updates** — the package is republished on every production deploy; `npx` always runs the latest
|
|
25
|
+
- **596 AI skills** — Shopify, BigCommerce, WooCommerce, Magento, HubSpot, Salesforce, NetSuite, ServiceNow, Dynamics 365, SAP, Square, QuickBooks, Jobber, and more — all callable from within Claude
|
|
26
|
+
- **25 built-in tools** — conversations, workflows, bots, contacts, agents, teams, routing rules, conversion tracking, and full KB management
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### Claude Code
|
|
18
33
|
|
|
19
34
|
Add to `~/.claude/settings.json`:
|
|
20
35
|
|
|
@@ -32,9 +47,9 @@ Add to `~/.claude/settings.json`:
|
|
|
32
47
|
}
|
|
33
48
|
```
|
|
34
49
|
|
|
35
|
-
|
|
50
|
+
### Claude Desktop
|
|
36
51
|
|
|
37
|
-
Add to `~/.claude/claude_desktop_config.json
|
|
52
|
+
Add to `~/.config/claude/claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
38
53
|
|
|
39
54
|
```json
|
|
40
55
|
{
|
|
@@ -50,24 +65,223 @@ Add to `~/.claude/claude_desktop_config.json`:
|
|
|
50
65
|
}
|
|
51
66
|
```
|
|
52
67
|
|
|
68
|
+
### Hosted HTTP Endpoint (No Node Required)
|
|
69
|
+
|
|
70
|
+
If you prefer not to run a local Node process, point directly at Velaro's hosted MCP endpoint:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"velaro": {
|
|
76
|
+
"url": "https://api-admin-us-east.velaro.com/mcp",
|
|
77
|
+
"headers": {
|
|
78
|
+
"Authorization": "Bearer vel_live_..."
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This works with any MCP client that supports HTTP/SSE transport — including Claude Desktop 0.8+.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
53
89
|
## Available Tools
|
|
54
90
|
|
|
55
|
-
|
|
91
|
+
### Knowledge Base
|
|
92
|
+
| Tool | What It Does |
|
|
56
93
|
|------|-------------|
|
|
57
|
-
| `kb_list_topics` | List all
|
|
58
|
-
| `kb_search_articles` | Search articles by
|
|
59
|
-
| `kb_get_article` |
|
|
60
|
-
| `kb_create_article` | Create a new article |
|
|
61
|
-
| `kb_update_article` | Update
|
|
94
|
+
| `kb_list_topics` | List all knowledge base topics |
|
|
95
|
+
| `kb_search_articles` | Search articles by keyword, topic, or publish status |
|
|
96
|
+
| `kb_get_article` | Retrieve full article content by ID |
|
|
97
|
+
| `kb_create_article` | Create and optionally publish a new article |
|
|
98
|
+
| `kb_update_article` | Update title, body, tags, or publish status |
|
|
62
99
|
| `kb_delete_article` | Delete an article |
|
|
63
100
|
|
|
64
|
-
|
|
101
|
+
### Conversations
|
|
102
|
+
| Tool | What It Does |
|
|
103
|
+
|------|-------------|
|
|
104
|
+
| `conversation_list` | List recent conversations filtered by channel, status, or agent |
|
|
105
|
+
| `conversation_get` | Get full details for a single conversation |
|
|
106
|
+
| `conversation_search` | Search conversations by phone number or call SID |
|
|
107
|
+
|
|
108
|
+
### Workflows
|
|
109
|
+
| Tool | What It Does |
|
|
110
|
+
|------|-------------|
|
|
111
|
+
| `workflow_list` | List all workflows with enabled state and trigger type |
|
|
112
|
+
| `workflow_get` | Get full workflow details by ID |
|
|
113
|
+
| `workflow_toggle` | Enable or disable a workflow |
|
|
114
|
+
|
|
115
|
+
### Bots & AI Configurations
|
|
116
|
+
| Tool | What It Does |
|
|
117
|
+
|------|-------------|
|
|
118
|
+
| `bot_list` | List all bot/AI configurations for this site |
|
|
119
|
+
| `bot_get` | Get full bot config including system prompt |
|
|
120
|
+
| `bot_update_prompt` | Update a bot's system prompt |
|
|
121
|
+
|
|
122
|
+
### Contacts
|
|
123
|
+
| Tool | What It Does |
|
|
124
|
+
|------|-------------|
|
|
125
|
+
| `contact_search` | Search contacts by name, email, phone, or company |
|
|
126
|
+
| `contact_get` | Get full contact details by ID |
|
|
127
|
+
|
|
128
|
+
### Team & Routing
|
|
129
|
+
| Tool | What It Does |
|
|
130
|
+
|------|-------------|
|
|
131
|
+
| `agent_list` | List agents with availability status; filter by team |
|
|
132
|
+
| `team_list` | List all teams for this site |
|
|
133
|
+
| `routing_list` | List all routing rules ordered by priority |
|
|
134
|
+
| `routing_get` | Get routing rule details including rule and value JSON |
|
|
135
|
+
|
|
136
|
+
### Site
|
|
137
|
+
| Tool | What It Does |
|
|
138
|
+
|------|-------------|
|
|
139
|
+
| `site_info` | Get site settings, active channels, subscription features, and resource counts |
|
|
140
|
+
|
|
141
|
+
### Live Operations
|
|
142
|
+
| Tool | What It Does |
|
|
143
|
+
|------|-------------|
|
|
144
|
+
| `kpi_snapshot` | Real-time ops dashboard — agents online/available/busy, open/queued conversations, bot-active count, resolved today, queue wait times, top agents, channel breakdown, per-team coverage status |
|
|
145
|
+
|
|
146
|
+
### CSAT
|
|
147
|
+
| Tool | What It Does |
|
|
148
|
+
|------|-------------|
|
|
149
|
+
| `csat_summary` | Customer satisfaction — avg rating, satisfaction rate, star breakdown, recent verbatim comments |
|
|
150
|
+
|
|
151
|
+
### Bot Diagnostics
|
|
152
|
+
| Tool | What It Does |
|
|
153
|
+
|------|-------------|
|
|
154
|
+
| `bot_analytics_summary` | AI bot health — total skill calls, error count, success rate, top skill, training count |
|
|
155
|
+
| `bot_skill_usage` | Per-skill call counts by integration + error counts — identifies over/underused skills and failing integrations |
|
|
156
|
+
| `bot_recent_errors` | Last N AI skill errors with integration, message, and timestamp — for active troubleshooting |
|
|
157
|
+
|
|
158
|
+
### Service Level
|
|
159
|
+
| Tool | What It Does |
|
|
160
|
+
|------|-------------|
|
|
161
|
+
| `service_level_report` | SLA compliance per period — % within threshold, avg response time, avg handle time. Granularity: hour/day/week/month |
|
|
162
|
+
|
|
163
|
+
### Reporting & Analytics
|
|
164
|
+
| Tool | What It Does |
|
|
165
|
+
|------|-------------|
|
|
166
|
+
| `report_roi_summary` | Full ROI report — bot deflections, hours saved, cost savings, AI skill executions, resolution rate, response time, trends vs prior period, monthly breakdown, top skills by call count, channel breakdown |
|
|
167
|
+
| `report_agent_utilization` | Per-agent stats — conversation count, avg handle time, active/away breakdown for any date range |
|
|
168
|
+
| `report_campaign_performance` | Email campaign KPIs — open rate, click rate, delivery rate, complaint rate per campaign + aggregate totals |
|
|
169
|
+
| `report_campaign_deliverability` | Deliverability health check — bounce rate, complaint rate, unsubscribe rate, open rate with traffic-light status and fix recommendations |
|
|
170
|
+
| `report_campaign_monthly` | Month-by-month email volume for the last 12 months |
|
|
171
|
+
| `report_audience_growth` | Total active contacts, net contacts (minus unsubscribes), month-by-month additions |
|
|
172
|
+
|
|
173
|
+
### Conversion Tracking
|
|
174
|
+
| Tool | What It Does |
|
|
175
|
+
|------|-------------|
|
|
176
|
+
| `conversion_list_goals` | List all conversion goals |
|
|
177
|
+
| `conversion_create_goal` | Create a new conversion goal (URL, Shopify, Square, custom event) |
|
|
178
|
+
| `conversion_get_report` | Get conversion attribution report — revenue, rates, timeline |
|
|
179
|
+
|
|
180
|
+
More tools are added on every production release. Run `tools/list` to see the current full list from your Claude client.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Getting Your API Key
|
|
185
|
+
|
|
186
|
+
1. Log in to your Velaro admin at [admin.velaro.com](https://admin.velaro.com)
|
|
187
|
+
2. Go to **Settings → API → MCP Keys**
|
|
188
|
+
3. Click **Create Key** → give it a label (e.g. "Claude Code")
|
|
189
|
+
4. Copy the `vel_live_...` key — it's shown once
|
|
65
190
|
|
|
66
|
-
|
|
67
|
-
|
|
191
|
+
Keys are site-isolated: the key is bound to your Velaro account and can only read/write data for that account. You can create multiple keys and revoke them independently.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Auth Options
|
|
196
|
+
|
|
197
|
+
| Variable | When to Use |
|
|
198
|
+
|----------|-------------|
|
|
199
|
+
| `VELARO_MCP_KEY=vel_live_...` | Production — create in Settings > API > MCP Keys |
|
|
200
|
+
| `VELARO_JWT=<token>` | Development only — short-lived JWT from `velaro login` |
|
|
201
|
+
|
|
202
|
+
---
|
|
68
203
|
|
|
69
204
|
## Staging / Custom API
|
|
70
205
|
|
|
71
206
|
```bash
|
|
72
207
|
VELARO_ADMIN_API=https://velaro-admin-staging.azurewebsites.net VELARO_MCP_KEY=vel_live_... npx @velaro/mcp-server
|
|
73
208
|
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## HTTP Mode (Self-Hosted)
|
|
213
|
+
|
|
214
|
+
Run as an HTTP/SSE server instead of stdio — useful for shared deployments:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
VELARO_MCP_MODE=http PORT=3000 VELARO_MCP_KEY=vel_live_... node server.js
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Clients connect via `GET /mcp` (SSE stream) and `POST /mcp` (JSON-RPC 2.0).
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## What You Can Do with Velaro + Claude
|
|
225
|
+
|
|
226
|
+
Once connected, Claude has direct operator access to your support infrastructure:
|
|
227
|
+
|
|
228
|
+
**Knowledge Base Management**
|
|
229
|
+
> "Search my KB for any articles about refund policy and update them to reflect the new 30-day window we announced last week."
|
|
230
|
+
|
|
231
|
+
**Workflow Diagnostics**
|
|
232
|
+
> "List all my disabled workflows and tell me which ones are AI-enhanced so I can decide which to turn back on."
|
|
233
|
+
|
|
234
|
+
**Bot Prompt Tuning**
|
|
235
|
+
> "Read the current system prompt for bot #3 and rewrite it to be more concise and focus on checkout support."
|
|
236
|
+
|
|
237
|
+
**Live Operations**
|
|
238
|
+
> "Show me the last 20 open conversations on the SMS channel and tell me which agents have the most active chats right now."
|
|
239
|
+
|
|
240
|
+
**Contact Lookup**
|
|
241
|
+
> "Search for the contact with email orders@example.com and show me their full profile."
|
|
242
|
+
|
|
243
|
+
**Routing Audit**
|
|
244
|
+
> "List all routing rules in priority order — I want to review what fires before the 'Default Team' fallback."
|
|
245
|
+
|
|
246
|
+
**Conversion Tracking**
|
|
247
|
+
> "Create a conversion goal that fires when a customer hits /order-confirmed, with a 24-hour attribution window and $50 default value."
|
|
248
|
+
|
|
249
|
+
**ROI & Analytics**
|
|
250
|
+
> "Pull the last 30-day ROI report. How many conversations did the bot deflect, how many hours did that save, and which AI skills were called the most?"
|
|
251
|
+
|
|
252
|
+
**Deliverability Monitoring**
|
|
253
|
+
> "Check our email deliverability health — is our bounce rate or complaint rate in the red? What should we fix?"
|
|
254
|
+
|
|
255
|
+
**Site Health Check**
|
|
256
|
+
> "Give me a full site overview — active channels, agent count, workflow status, and which subscription features are enabled."
|
|
257
|
+
|
|
258
|
+
Claude handles these operations natively. No exports, no copy-paste, no waiting for a developer.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## How It's Updated
|
|
263
|
+
|
|
264
|
+
This package is automatically published to npm on every Velaro production deploy. When Velaro ships new tools or improves existing ones, your next `npx` invocation pulls the latest version automatically. You don't need to update a package.json or run `npm install`.
|
|
265
|
+
|
|
266
|
+
If you want a pinned version for reproducible environments:
|
|
267
|
+
```bash
|
|
268
|
+
npx @velaro/mcp-server@0.1.0
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Competitive Context
|
|
274
|
+
|
|
275
|
+
Intercom, Zendesk, and Freshdesk do not publish MCP servers. Connecting Claude to those platforms requires building a custom MCP wrapper against their REST APIs, handling authentication yourself, and maintaining the integration as their APIs change.
|
|
276
|
+
|
|
277
|
+
Velaro ships the MCP server, hosts it, auto-updates it, and publishes it to npm. The integration is one `npx` command.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Links
|
|
282
|
+
|
|
283
|
+
- [Velaro Admin](https://admin.velaro.com)
|
|
284
|
+
- [Documentation](https://help.velaro.com)
|
|
285
|
+
- [npm package](https://www.npmjs.com/package/@velaro/mcp-server)
|
|
286
|
+
- [Source](https://github.com/velaro/velaro-admin/tree/master/mcp)
|
|
287
|
+
- [MCP Specification](https://modelcontextprotocol.io)
|
package/package.json
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@velaro/mcp-server",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Velaro MCP server — connect Claude and other AI agents directly to your Velaro KB and
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"velaro-mcp": "server.js"
|
|
8
|
-
},
|
|
9
|
-
"engines": {
|
|
10
|
-
"node": ">=18.0.0"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
14
|
-
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"start": "node server.js"
|
|
17
|
-
},
|
|
18
|
-
"publishConfig": {
|
|
19
|
-
"access": "public",
|
|
20
|
-
"registry": "https://registry.npmjs.org/"
|
|
21
|
-
},
|
|
22
|
-
"repository": {
|
|
23
|
-
"type": "git",
|
|
24
|
-
"url": "https://github.com/velaro/velaro-admin"
|
|
25
|
-
},
|
|
26
|
-
"keywords": ["velaro", "mcp", "knowledge-base", "claude", "ai", "model-context-protocol"],
|
|
27
|
-
"license": "MIT",
|
|
28
|
-
"files": [
|
|
29
|
-
"server.js",
|
|
30
|
-
"README.md"
|
|
31
|
-
]
|
|
32
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@velaro/mcp-server",
|
|
3
|
+
"version": "0.4.0",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"velaro-mcp": "server.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18.0.0"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"start": "node server.js"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public",
|
|
20
|
+
"registry": "https://registry.npmjs.org/"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/velaro/velaro-admin"
|
|
25
|
+
},
|
|
26
|
+
"keywords": ["velaro", "mcp", "knowledge-base", "claude", "ai", "model-context-protocol", "customer-support", "workflows", "conversations"],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"files": [
|
|
29
|
+
"server.js",
|
|
30
|
+
"README.md"
|
|
31
|
+
]
|
|
32
|
+
}
|
package/server.js
CHANGED
|
@@ -74,6 +74,30 @@ async function api(method, path, body) {
|
|
|
74
74
|
return text ? JSON.parse(text) : null;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
// Call the hosted MCP gateway (JSON-RPC) for tools that are dispatched server-side.
|
|
78
|
+
async function rpc(toolName, args) {
|
|
79
|
+
const body = {
|
|
80
|
+
jsonrpc: '2.0',
|
|
81
|
+
id: 1,
|
|
82
|
+
method: 'tools/call',
|
|
83
|
+
params: { name: toolName, arguments: args ?? {} },
|
|
84
|
+
};
|
|
85
|
+
const res = await fetch(`${API_BASE}/mcp`, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { Authorization: authHeader(), 'Content-Type': 'application/json' },
|
|
88
|
+
body: JSON.stringify(body),
|
|
89
|
+
});
|
|
90
|
+
if (!res.ok) {
|
|
91
|
+
const text = await res.text().catch(() => '');
|
|
92
|
+
throw new Error(`Velaro MCP gateway ${toolName} -> ${res.status}: ${text.slice(0, 300)}`);
|
|
93
|
+
}
|
|
94
|
+
const data = await res.json();
|
|
95
|
+
if (data.error) throw new Error(data.error.message || JSON.stringify(data.error));
|
|
96
|
+
const content = data.result?.content?.[0]?.text;
|
|
97
|
+
if (content === undefined) throw new Error('Empty response from MCP gateway');
|
|
98
|
+
return content;
|
|
99
|
+
}
|
|
100
|
+
|
|
77
101
|
// ── Tool definitions ──────────────────────────────────────────────────────────
|
|
78
102
|
|
|
79
103
|
const TOOLS = [
|
|
@@ -149,6 +173,324 @@ const TOOLS = [
|
|
|
149
173
|
required: ['id'],
|
|
150
174
|
},
|
|
151
175
|
},
|
|
176
|
+
|
|
177
|
+
// ── Conversations ─────────────────────────────────────────────────────────
|
|
178
|
+
{
|
|
179
|
+
name: 'conversation_list',
|
|
180
|
+
description: 'List recent conversations. Returns id, status, phoneNumber, assignedUserId, teamId, createdAt.',
|
|
181
|
+
inputSchema: {
|
|
182
|
+
type: 'object',
|
|
183
|
+
properties: {
|
|
184
|
+
limit: { type: 'number', description: 'Max results (default 20, max 50)' },
|
|
185
|
+
channel: { type: 'string', description: 'Filter by channel (e.g. web, sms, ivr)' },
|
|
186
|
+
status: { type: 'string', description: 'Filter by status: open or closed' },
|
|
187
|
+
agentId: { type: 'number', description: 'Filter by assigned agent ID' },
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: 'conversation_get',
|
|
193
|
+
description: 'Get details for a single conversation by ID.',
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: { id: { type: 'number', description: 'Conversation ID' } },
|
|
197
|
+
required: ['id'],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: 'conversation_search',
|
|
202
|
+
description: 'Search conversations by phone number or call SID.',
|
|
203
|
+
inputSchema: {
|
|
204
|
+
type: 'object',
|
|
205
|
+
properties: {
|
|
206
|
+
q: { type: 'string', description: 'Phone number or call SID to search' },
|
|
207
|
+
limit: { type: 'number', description: 'Max results (default 10, max 50)' },
|
|
208
|
+
},
|
|
209
|
+
required: ['q'],
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
// ── Workflows ─────────────────────────────────────────────────────────────
|
|
214
|
+
{
|
|
215
|
+
name: 'workflow_list',
|
|
216
|
+
description: 'List all workflows. Returns id, name, enabled, triggerType, AIEnhanced, category.',
|
|
217
|
+
inputSchema: {
|
|
218
|
+
type: 'object',
|
|
219
|
+
properties: {
|
|
220
|
+
enabled: { type: 'boolean', description: 'Filter by enabled state (omit for all)' },
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: 'workflow_get',
|
|
226
|
+
description: 'Get workflow details by ID.',
|
|
227
|
+
inputSchema: {
|
|
228
|
+
type: 'object',
|
|
229
|
+
properties: { id: { type: 'number', description: 'Workflow ID' } },
|
|
230
|
+
required: ['id'],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'workflow_toggle',
|
|
235
|
+
description: 'Enable or disable a workflow by ID.',
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: 'object',
|
|
238
|
+
properties: {
|
|
239
|
+
id: { type: 'number', description: 'Workflow ID' },
|
|
240
|
+
enabled: { type: 'boolean', description: 'true to enable, false to disable' },
|
|
241
|
+
},
|
|
242
|
+
required: ['id', 'enabled'],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
// ── Bots / AI Configurations ──────────────────────────────────────────────
|
|
247
|
+
{
|
|
248
|
+
name: 'bot_list',
|
|
249
|
+
description: 'List all bot/AI configurations for this site.',
|
|
250
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: 'bot_get',
|
|
254
|
+
description: 'Get full bot configuration including system prompt.',
|
|
255
|
+
inputSchema: {
|
|
256
|
+
type: 'object',
|
|
257
|
+
properties: { id: { type: 'number', description: 'Bot configuration ID' } },
|
|
258
|
+
required: ['id'],
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: 'bot_update_prompt',
|
|
263
|
+
description: 'Update a bot\'s system prompt. Use bot_get first to read the current prompt.',
|
|
264
|
+
inputSchema: {
|
|
265
|
+
type: 'object',
|
|
266
|
+
properties: {
|
|
267
|
+
id: { type: 'number', description: 'Bot configuration ID' },
|
|
268
|
+
prompt: { type: 'string', description: 'New system prompt text' },
|
|
269
|
+
},
|
|
270
|
+
required: ['id', 'prompt'],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
// ── Contacts ──────────────────────────────────────────────────────────────
|
|
275
|
+
{
|
|
276
|
+
name: 'contact_search',
|
|
277
|
+
description: 'Search contacts by name, email, phone, or company.',
|
|
278
|
+
inputSchema: {
|
|
279
|
+
type: 'object',
|
|
280
|
+
properties: {
|
|
281
|
+
q: { type: 'string', description: 'Name, email, phone, or company to search' },
|
|
282
|
+
limit: { type: 'number', description: 'Max results (default 10, max 50)' },
|
|
283
|
+
},
|
|
284
|
+
required: ['q'],
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
name: 'contact_get',
|
|
289
|
+
description: 'Get full contact details by ID.',
|
|
290
|
+
inputSchema: {
|
|
291
|
+
type: 'object',
|
|
292
|
+
properties: { id: { type: 'number', description: 'Contact ID' } },
|
|
293
|
+
required: ['id'],
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
// ── Agents ────────────────────────────────────────────────────────────────
|
|
298
|
+
{
|
|
299
|
+
name: 'agent_list',
|
|
300
|
+
description: 'List agents with their status (Available, Busy, Away, Offline). Optionally filter by team.',
|
|
301
|
+
inputSchema: {
|
|
302
|
+
type: 'object',
|
|
303
|
+
properties: {
|
|
304
|
+
teamId: { type: 'number', description: 'Filter by team ID (omit for all agents)' },
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
// ── Teams ─────────────────────────────────────────────────────────────────
|
|
310
|
+
{
|
|
311
|
+
name: 'team_list',
|
|
312
|
+
description: 'List all teams for this site.',
|
|
313
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
// ── Routing Rules ─────────────────────────────────────────────────────────
|
|
317
|
+
{
|
|
318
|
+
name: 'routing_list',
|
|
319
|
+
description: 'List all routing rules ordered by priority.',
|
|
320
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: 'routing_get',
|
|
324
|
+
description: 'Get routing rule details including rule and value JSON.',
|
|
325
|
+
inputSchema: {
|
|
326
|
+
type: 'object',
|
|
327
|
+
properties: { id: { type: 'number', description: 'Routing rule ID' } },
|
|
328
|
+
required: ['id'],
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
// ── Site ──────────────────────────────────────────────────────────────────
|
|
333
|
+
{
|
|
334
|
+
name: 'site_info',
|
|
335
|
+
description: 'Get site settings, active channels, subscription features, and counts of agents, teams, bots, and active workflows.',
|
|
336
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
// ── Live Operations ───────────────────────────────────────────────────────
|
|
340
|
+
{
|
|
341
|
+
name: 'kpi_snapshot',
|
|
342
|
+
description: 'Real-time operations snapshot: agents online/available/busy, open/queued conversations, bot-active count, resolved today, missed/abandoned today, avg and longest queue wait, avg handle time, top agents by resolved count, channel breakdown, and per-team detail (coverage status, agent availability, unassigned conversations). Perfect for a live ops health check.',
|
|
343
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
// ── CSAT ──────────────────────────────────────────────────────────────────
|
|
347
|
+
{
|
|
348
|
+
name: 'csat_summary',
|
|
349
|
+
description: 'Customer satisfaction (CSAT) report — avg rating, satisfaction rate (% rated 4-5 stars), breakdown by star rating, and up to 10 recent verbatim comments with channel and date. Use to gauge customer happiness and spot qualitative feedback trends.',
|
|
350
|
+
inputSchema: {
|
|
351
|
+
type: 'object',
|
|
352
|
+
properties: {
|
|
353
|
+
days: { type: 'number', description: 'Lookback window in days (1–365). Default: 30' },
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
// ── Bot Diagnostics ───────────────────────────────────────────────────────
|
|
359
|
+
{
|
|
360
|
+
name: 'bot_analytics_summary',
|
|
361
|
+
description: 'AI bot health summary — total AI skill calls, error count, success rate %, top skill name and call count, training conversations indexed. Quick pulse check on bot reliability.',
|
|
362
|
+
inputSchema: {
|
|
363
|
+
type: 'object',
|
|
364
|
+
properties: {
|
|
365
|
+
days: { type: 'number', description: 'Lookback window in days (1–365). Default: 30' },
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: 'bot_skill_usage',
|
|
371
|
+
description: 'Per-skill call counts broken down by integration — shows which AI tools are being used, how often, and error counts per integration. Use to identify overused or underused skills and diagnose high-error integrations.',
|
|
372
|
+
inputSchema: {
|
|
373
|
+
type: 'object',
|
|
374
|
+
properties: {
|
|
375
|
+
days: { type: 'number', description: 'Lookback window in days (1–365). Default: 30' },
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: 'bot_recent_errors',
|
|
381
|
+
description: 'Recent AI skill errors — returns the last N error log entries with integration name, error message, and timestamp. Use to diagnose why a specific integration or skill is failing.',
|
|
382
|
+
inputSchema: {
|
|
383
|
+
type: 'object',
|
|
384
|
+
properties: {
|
|
385
|
+
days: { type: 'number', description: 'Lookback window in days (1–90). Default: 7' },
|
|
386
|
+
limit: { type: 'number', description: 'Max errors to return (1–100). Default: 20' },
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
// ── Service Level ─────────────────────────────────────────────────────────
|
|
392
|
+
{
|
|
393
|
+
name: 'service_level_report',
|
|
394
|
+
description: 'SLA / service level report — per-period rows showing conversation volume, % answered within SLA threshold, avg response time, avg handle time. Granularity: hour, day, week, or month.',
|
|
395
|
+
inputSchema: {
|
|
396
|
+
type: 'object',
|
|
397
|
+
properties: {
|
|
398
|
+
startDate: { type: 'string', description: 'ISO 8601 start date, e.g. 2026-01-01 (default: 30 days ago)' },
|
|
399
|
+
endDate: { type: 'string', description: 'ISO 8601 end date, e.g. 2026-04-19 (default: now)' },
|
|
400
|
+
granularity: { type: 'string', enum: ['hour', 'day', 'week', 'month'], description: 'Bucketing period. Default: day' },
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
|
|
405
|
+
// ── Reporting ─────────────────────────────────────────────────────────────
|
|
406
|
+
{
|
|
407
|
+
name: 'report_roi_summary',
|
|
408
|
+
description: 'ROI / Value Report — bot deflections, hours saved, estimated cost savings, AI skill executions, resolution rate, avg first-response time, trends vs prior period, monthly breakdown, top skills, channel breakdown. No PII returned.',
|
|
409
|
+
inputSchema: {
|
|
410
|
+
type: 'object',
|
|
411
|
+
properties: {
|
|
412
|
+
days: { type: 'number', description: 'Lookback window in days (1–365). Default: 30' },
|
|
413
|
+
agentCostPerHour: { type: 'number', description: 'Agent hourly cost in USD for savings estimate. Default: 25' },
|
|
414
|
+
avgHandleMinutes: { type: 'number', description: 'Average handle time per conversation in minutes. Default: 8' },
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: 'report_agent_utilization',
|
|
420
|
+
description: 'Agent utilization report — per-agent conversation counts, handle times, and availability breakdown for a date range.',
|
|
421
|
+
inputSchema: {
|
|
422
|
+
type: 'object',
|
|
423
|
+
properties: {
|
|
424
|
+
startDate: { type: 'string', description: 'ISO 8601 start date, e.g. 2026-01-01 (default: 30 days ago)' },
|
|
425
|
+
endDate: { type: 'string', description: 'ISO 8601 end date, e.g. 2026-04-19 (default: now)' },
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
name: 'report_campaign_performance',
|
|
431
|
+
description: 'Email campaign performance: open rate, click rate, delivery rate, complaint rate per campaign + aggregate KPIs.',
|
|
432
|
+
inputSchema: {
|
|
433
|
+
type: 'object',
|
|
434
|
+
properties: {
|
|
435
|
+
days: { type: 'number', description: 'Lookback window in days. Default: 90' },
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'report_campaign_deliverability',
|
|
441
|
+
description: 'Deliverability health check — bounce rate, complaint rate, unsubscribe rate, open rate. Each metric has a traffic-light status (green/yellow/red) and actionable recommendations.',
|
|
442
|
+
inputSchema: {
|
|
443
|
+
type: 'object',
|
|
444
|
+
properties: {
|
|
445
|
+
days: { type: 'number', description: 'Lookback window in days. Default: 30' },
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: 'report_campaign_monthly',
|
|
451
|
+
description: 'Monthly email volume trend — last 12 months of sent, opened, clicked counts for charting or analysis.',
|
|
452
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
name: 'report_audience_growth',
|
|
456
|
+
description: 'Audience growth — total active contacts, unsubscribes, net contacts, and month-by-month contact additions for the last 12 months.',
|
|
457
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
// ── Conversion tracking tools ─────────────────────────────────────────────
|
|
461
|
+
{
|
|
462
|
+
name: 'conversion_list_goals',
|
|
463
|
+
description: 'List all conversion goals for this site. Returns id, name, triggerType, defaultAmount, attributionWindowHours, isEnabled.',
|
|
464
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
name: 'conversion_create_goal',
|
|
468
|
+
description: 'Create a new conversion goal. Use triggerType="url" for URL-based, "shopify_order"/"square_payment"/"bc_order"/"woo_order" for integration-native, "custom_event" for JS SDK.',
|
|
469
|
+
inputSchema: {
|
|
470
|
+
type: 'object',
|
|
471
|
+
properties: {
|
|
472
|
+
name: { type: 'string', description: 'Goal name, e.g. "Purchase Completed"' },
|
|
473
|
+
triggerType: { type: 'string', enum: ['url', 'shopify_order', 'square_payment', 'bc_order', 'woo_order', 'magento_order', 'custom_event'], description: 'What triggers this conversion' },
|
|
474
|
+
triggerUrl: { type: 'string', description: 'Conversion URL (for triggerType=url)' },
|
|
475
|
+
eventName: { type: 'string', description: 'Custom event name (for triggerType=custom_event)' },
|
|
476
|
+
defaultAmount: { type: 'number', description: 'Default revenue amount in USD (default: 0)' },
|
|
477
|
+
revenueVariableName: { type: 'string', description: 'JS variable name to read actual order amount from' },
|
|
478
|
+
allowOnce: { type: 'boolean', description: 'Count each visitor only once (default: false)' },
|
|
479
|
+
attributionWindowHours: { type: 'number', description: 'Hours after chat to attribute conversion: 1, 24, 168 (7d), 720 (30d). Default: 24' },
|
|
480
|
+
},
|
|
481
|
+
required: ['name', 'triggerType'],
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
name: 'conversion_get_report',
|
|
486
|
+
description: 'Get conversion attribution report: total revenue, attributed revenue, attribution rate, conversions by goal, timeline. Shows how chat drives sales.',
|
|
487
|
+
inputSchema: {
|
|
488
|
+
type: 'object',
|
|
489
|
+
properties: {
|
|
490
|
+
days: { type: 'number', description: 'Lookback window in days (7, 30, or 90). Default: 30' },
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
},
|
|
152
494
|
];
|
|
153
495
|
|
|
154
496
|
// ── Tool handlers ─────────────────────────────────────────────────────────────
|
|
@@ -237,6 +579,329 @@ async function handleTool(name, args) {
|
|
|
237
579
|
return `Article ${args.id} deleted.`;
|
|
238
580
|
}
|
|
239
581
|
|
|
582
|
+
// ── Conversations ──────────────────────────────────────────────────────
|
|
583
|
+
case 'conversation_list':
|
|
584
|
+
return await rpc('conversation_list', args);
|
|
585
|
+
|
|
586
|
+
case 'conversation_get':
|
|
587
|
+
return await rpc('conversation_get', args);
|
|
588
|
+
|
|
589
|
+
case 'conversation_search':
|
|
590
|
+
return await rpc('conversation_search', args);
|
|
591
|
+
|
|
592
|
+
// ── Workflows ──────────────────────────────────────────────────────────
|
|
593
|
+
case 'workflow_list':
|
|
594
|
+
return await rpc('workflow_list', args);
|
|
595
|
+
|
|
596
|
+
case 'workflow_get':
|
|
597
|
+
return await rpc('workflow_get', args);
|
|
598
|
+
|
|
599
|
+
case 'workflow_toggle':
|
|
600
|
+
return await rpc('workflow_toggle', args);
|
|
601
|
+
|
|
602
|
+
// ── Bots / AI Configurations ───────────────────────────────────────────
|
|
603
|
+
case 'bot_list':
|
|
604
|
+
return await rpc('bot_list', args);
|
|
605
|
+
|
|
606
|
+
case 'bot_get':
|
|
607
|
+
return await rpc('bot_get', args);
|
|
608
|
+
|
|
609
|
+
case 'bot_update_prompt':
|
|
610
|
+
return await rpc('bot_update_prompt', args);
|
|
611
|
+
|
|
612
|
+
// ── Contacts ───────────────────────────────────────────────────────────
|
|
613
|
+
case 'contact_search':
|
|
614
|
+
return await rpc('contact_search', args);
|
|
615
|
+
|
|
616
|
+
case 'contact_get':
|
|
617
|
+
return await rpc('contact_get', args);
|
|
618
|
+
|
|
619
|
+
// ── Agents ─────────────────────────────────────────────────────────────
|
|
620
|
+
case 'agent_list':
|
|
621
|
+
return await rpc('agent_list', args);
|
|
622
|
+
|
|
623
|
+
// ── Teams ──────────────────────────────────────────────────────────────
|
|
624
|
+
case 'team_list':
|
|
625
|
+
return await rpc('team_list', args);
|
|
626
|
+
|
|
627
|
+
// ── Routing Rules ──────────────────────────────────────────────────────
|
|
628
|
+
case 'routing_list':
|
|
629
|
+
return await rpc('routing_list', args);
|
|
630
|
+
|
|
631
|
+
case 'routing_get':
|
|
632
|
+
return await rpc('routing_get', args);
|
|
633
|
+
|
|
634
|
+
// ── Site ───────────────────────────────────────────────────────────────
|
|
635
|
+
case 'site_info':
|
|
636
|
+
return await rpc('site_info', args);
|
|
637
|
+
|
|
638
|
+
case 'conversion_list_goals': {
|
|
639
|
+
const goals = await api('GET', '/Conversions/goals');
|
|
640
|
+
if (!Array.isArray(goals) || !goals.length) return 'No conversion goals configured.';
|
|
641
|
+
return goals.map(g =>
|
|
642
|
+
`[${g.id}] "${g.name}" | type: ${g.triggerType} | window: ${g.attributionWindowHours}h | default: $${g.defaultAmount} | ${g.isEnabled ? 'enabled' : 'paused'}`
|
|
643
|
+
).join('\n');
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
case 'conversion_create_goal': {
|
|
647
|
+
const res = await api('POST', '/Conversions/goals', {
|
|
648
|
+
name: args.name,
|
|
649
|
+
triggerType: args.triggerType,
|
|
650
|
+
triggerUrl: args.triggerUrl || null,
|
|
651
|
+
eventName: args.eventName || null,
|
|
652
|
+
defaultAmount: args.defaultAmount ?? 0,
|
|
653
|
+
revenueVariableName: args.revenueVariableName || null,
|
|
654
|
+
allowOnce: args.allowOnce ?? false,
|
|
655
|
+
attributionWindowHours: args.attributionWindowHours ?? 24,
|
|
656
|
+
isEnabled: true,
|
|
657
|
+
});
|
|
658
|
+
return `Created conversion goal "${args.name}" with ID ${res.id}.`;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
case 'conversion_get_report': {
|
|
662
|
+
const days = args.days ?? 30;
|
|
663
|
+
const r = await api('GET', `/Conversions/report?days=${days}`);
|
|
664
|
+
if (!r.hasData) {
|
|
665
|
+
return `No conversion data in the last ${days} days. ${r.goalCount} goal(s) configured — events will appear once the widget fires.`;
|
|
666
|
+
}
|
|
667
|
+
const s = r.summary;
|
|
668
|
+
const lines = [
|
|
669
|
+
`=== Conversion Attribution (last ${days} days) ===`,
|
|
670
|
+
`Attributed Revenue: $${s.attributedRevenue.toFixed(2)} of $${s.totalRevenue.toFixed(2)} total`,
|
|
671
|
+
`Attribution Rate: ${(s.attributionRate * 100).toFixed(1)}% (${s.attributedConversions} of ${s.totalConversions} conversions had a recent chat)`,
|
|
672
|
+
`Avg Order Value: $${s.avgOrderValue.toFixed(2)}`,
|
|
673
|
+
'',
|
|
674
|
+
'--- By Goal ---',
|
|
675
|
+
...r.byGoal.map(g =>
|
|
676
|
+
` ${g.goalName}: ${g.conversions} conversions, $${g.revenue.toFixed(2)} revenue, ${(g.attributionRate * 100).toFixed(1)}% attributed`
|
|
677
|
+
),
|
|
678
|
+
];
|
|
679
|
+
return lines.join('\n');
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// ── Live Operations ───────────────────────────────────────────────────
|
|
683
|
+
case 'kpi_snapshot': {
|
|
684
|
+
const r = await api('GET', '/LiveDashboard/snapshot');
|
|
685
|
+
const k = r.kpis ?? {};
|
|
686
|
+
const lines = [
|
|
687
|
+
'=== Live Operations Snapshot ===',
|
|
688
|
+
`Agents: ${k.agentsAvailable} available, ${k.agentsBusy} busy, ${k.agentsOnline} online`,
|
|
689
|
+
`Conversations: ${k.openConversations} open | ${k.queuedConversations} queued | ${k.botActive} bot-active`,
|
|
690
|
+
`Today: ${k.resolvedToday} resolved | ${k.missedAbandonedToday} missed/abandoned`,
|
|
691
|
+
`Queue Wait: avg ${k.avgWaitMinutes}m | longest ${k.longestWaitMinutes}m`,
|
|
692
|
+
`Avg Handle Time: ${k.avgHandleTimeMinutes}m | Avg First Response: ${k.avgFirstResponseMinutes}m`,
|
|
693
|
+
'',
|
|
694
|
+
'--- Top Agents (resolved today) ---',
|
|
695
|
+
...(r.topAgents?.length
|
|
696
|
+
? r.topAgents.map(a => ` ${a.name}: ${a.resolved} resolved, ${a.active} active`)
|
|
697
|
+
: [' (no data yet today)']),
|
|
698
|
+
'',
|
|
699
|
+
'--- By Channel ---',
|
|
700
|
+
...(r.channelBreakdown?.length
|
|
701
|
+
? r.channelBreakdown.map(c => ` ${c.channel}: ${c.count} open, ${c.queued} queued`)
|
|
702
|
+
: [' (no data)']),
|
|
703
|
+
'',
|
|
704
|
+
'--- By Team ---',
|
|
705
|
+
...(r.teams?.length
|
|
706
|
+
? r.teams.map(t =>
|
|
707
|
+
` ${t.teamName}: ${t.agentsAvailable}/${t.agentsOnline} available | ${t.openConversations} open | ${t.unassignedConversations} unassigned | wait avg ${t.avgWaitMinutes}m | ${t.coverageStatus}`
|
|
708
|
+
)
|
|
709
|
+
: [' (no teams)']),
|
|
710
|
+
];
|
|
711
|
+
return lines.join('\n');
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// ── CSAT ──────────────────────────────────────────────────────────────
|
|
715
|
+
case 'csat_summary': {
|
|
716
|
+
const days = args.days ?? 30;
|
|
717
|
+
const r = await api('GET', `/api/admin/Csat/Summary?days=${days}`);
|
|
718
|
+
if (!r.totalResponses) return `No CSAT responses in the last ${days} days.`;
|
|
719
|
+
const lines = [
|
|
720
|
+
`=== CSAT Summary (last ${days} days) ===`,
|
|
721
|
+
`Responses: ${r.totalResponses} | Avg Rating: ${r.averageRating}/5 | Satisfaction Rate: ${r.satisfactionRate}%`,
|
|
722
|
+
'',
|
|
723
|
+
'--- Rating Breakdown ---',
|
|
724
|
+
...(r.breakdown?.map(b => ` ${b.rating}★: ${b.count}`) ?? []),
|
|
725
|
+
'',
|
|
726
|
+
'--- Recent Comments ---',
|
|
727
|
+
...(r.recentComments?.length
|
|
728
|
+
? r.recentComments.map(c => ` [${c.Rating}★] ${c.Channel ?? 'web'} — "${c.Comment}"`)
|
|
729
|
+
: [' (no comments)']),
|
|
730
|
+
];
|
|
731
|
+
return lines.join('\n');
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// ── Bot Diagnostics ───────────────────────────────────────────────────
|
|
735
|
+
case 'bot_analytics_summary': {
|
|
736
|
+
const days = args.days ?? 30;
|
|
737
|
+
const r = await api('GET', `/BotAnalytics/Summary?days=${days}`);
|
|
738
|
+
return [
|
|
739
|
+
`=== Bot Analytics (last ${days} days) ===`,
|
|
740
|
+
`AI Skill Calls: ${r.totalSkillCalls} | Errors: ${r.errorCount} | Success Rate: ${r.successRate}%`,
|
|
741
|
+
`Top Skill: ${r.topSkill} (${r.topSkillCount} calls)`,
|
|
742
|
+
`Conversations Trained: ${r.conversationsTrainedCount}`,
|
|
743
|
+
].join('\n');
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
case 'bot_skill_usage': {
|
|
747
|
+
const days = args.days ?? 30;
|
|
748
|
+
const r = await api('GET', `/BotAnalytics/SkillUsage?days=${days}`);
|
|
749
|
+
const lines = [
|
|
750
|
+
`=== Bot Skill Usage (last ${days} days) ===`,
|
|
751
|
+
`Total Successful Calls: ${r.totalSuccessfulCalls} | Total Errors: ${r.totalErrors}`,
|
|
752
|
+
'',
|
|
753
|
+
'--- Skill Calls ---',
|
|
754
|
+
...(r.skillUsage?.length
|
|
755
|
+
? r.skillUsage.slice(0, 30).map(s => ` [${s.integration}] ${s.toolName}: ${s.count}`)
|
|
756
|
+
: [' (no data)']),
|
|
757
|
+
'',
|
|
758
|
+
'--- Errors by Integration ---',
|
|
759
|
+
...(r.errorsByIntegration?.length
|
|
760
|
+
? r.errorsByIntegration.map(e => ` ${e.integration}: ${e.errorCount} errors`)
|
|
761
|
+
: [' (none)']),
|
|
762
|
+
];
|
|
763
|
+
return lines.join('\n');
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
case 'bot_recent_errors': {
|
|
767
|
+
const days = args.days ?? 7;
|
|
768
|
+
const limit = args.limit ?? 20;
|
|
769
|
+
const errors = await api('GET', `/BotAnalytics/RecentErrors?days=${days}&limit=${limit}`);
|
|
770
|
+
if (!Array.isArray(errors) || !errors.length) return `No AI skill errors in the last ${days} days.`;
|
|
771
|
+
const lines = [
|
|
772
|
+
`=== Recent Bot Errors (last ${days} days, up to ${limit}) ===`,
|
|
773
|
+
...errors.map(e => ` [${e.integration ?? 'unknown'}] ${new Date(e.createdAt).toISOString().slice(0, 16)} — ${e.message ?? e.errorMessage ?? JSON.stringify(e)}`),
|
|
774
|
+
];
|
|
775
|
+
return lines.join('\n');
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// ── Service Level ─────────────────────────────────────────────────────
|
|
779
|
+
case 'service_level_report': {
|
|
780
|
+
const qs = [
|
|
781
|
+
args.startDate ? `startDate=${args.startDate}` : '',
|
|
782
|
+
args.endDate ? `endDate=${args.endDate}` : '',
|
|
783
|
+
args.granularity ? `granularity=${args.granularity}` : '',
|
|
784
|
+
].filter(Boolean).join('&');
|
|
785
|
+
const r = await api('GET', `/ServiceLevel${qs ? '?' + qs : ''}`);
|
|
786
|
+
const rows = r.rows ?? r;
|
|
787
|
+
if (!Array.isArray(rows) || !rows.length) return 'No service level data for this period.';
|
|
788
|
+
const lines = [
|
|
789
|
+
`=== Service Level Report ===`,
|
|
790
|
+
...rows.map(row =>
|
|
791
|
+
` ${row.period ?? row.label ?? row.date}: ${row.totalConversations ?? row.total ?? 0} convos | SLA ${row.slaPercent ?? row.withinSla ?? 0}% | avg response ${row.avgResponseSecs ?? row.avgFirstResponseSecs ?? 0}s | avg handle ${row.avgHandleTimeSecs ?? row.avgHandleSecs ?? 0}s`
|
|
792
|
+
),
|
|
793
|
+
];
|
|
794
|
+
return lines.join('\n');
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// ── Reporting ─────────────────────────────────────────────────────────
|
|
798
|
+
case 'report_roi_summary': {
|
|
799
|
+
const days = args.days ?? 30;
|
|
800
|
+
const cost = args.agentCostPerHour ?? 25;
|
|
801
|
+
const handle = args.avgHandleMinutes ?? 8;
|
|
802
|
+
const r = await api('GET', `/RoiReport/Summary?days=${days}&agentCostPerHour=${cost}&avgHandleMinutes=${handle}`);
|
|
803
|
+
const h = r.headline;
|
|
804
|
+
const t = r.trends;
|
|
805
|
+
const lines = [
|
|
806
|
+
`=== ROI Summary (last ${days} days) ===`,
|
|
807
|
+
`Total Conversations: ${h.totalConversations} (${t.conversationVolumeChange > 0 ? '+' : ''}${t.conversationVolumeChange}% vs prior period)`,
|
|
808
|
+
`Bot Deflections: ${h.botDeflections} (${h.deflectionRate}% ${t.deflectionRateChange > 0 ? '▲' : '▼'}${Math.abs(t.deflectionRateChange)}% vs prior)`,
|
|
809
|
+
`Human-Handled: ${h.humanHandledConversations}`,
|
|
810
|
+
`Hours Saved: ${h.hoursSaved} | Estimated Savings: $${h.estimatedSavings}`,
|
|
811
|
+
`AI Skill Executions: ${h.aiSkillExecutions}`,
|
|
812
|
+
`Resolution Rate: ${h.resolutionRate}% (${t.resolutionRateChange > 0 ? '+' : ''}${t.resolutionRateChange}% vs prior)`,
|
|
813
|
+
`Avg First Response: ${Math.round(h.avgFirstResponseSecs)}s`,
|
|
814
|
+
'',
|
|
815
|
+
'--- Top AI Skills ---',
|
|
816
|
+
...(r.topSkills?.length ? r.topSkills.map(s => ` ${s.skill}: ${s.calls} calls`) : [' (no data)']),
|
|
817
|
+
'',
|
|
818
|
+
'--- By Channel ---',
|
|
819
|
+
...(r.channelBreakdown?.length ? r.channelBreakdown.map(c => ` ${c.channel}: ${c.conversations} convos, ${c.deflectionPct}% bot-deflected`) : [' (no data)']),
|
|
820
|
+
'',
|
|
821
|
+
'--- Monthly Trend (last 6 months) ---',
|
|
822
|
+
...(r.monthlyBreakdown?.length ? r.monthlyBreakdown.map(m =>
|
|
823
|
+
` ${m.label}: ${m.total} total, ${m.botHandled} bot-handled (${m.deflectionRate}%), ${m.resolved} resolved, avg response ${m.avgResponseSecs}s`
|
|
824
|
+
) : [' (no data)']),
|
|
825
|
+
];
|
|
826
|
+
return lines.join('\n');
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
case 'report_agent_utilization': {
|
|
830
|
+
const qs = [
|
|
831
|
+
args.startDate ? `startDate=${args.startDate}` : '',
|
|
832
|
+
args.endDate ? `endDate=${args.endDate}` : '',
|
|
833
|
+
].filter(Boolean).join('&');
|
|
834
|
+
const rows = await api('GET', `/Reports/AgentUtilization${qs ? '?' + qs : ''}`);
|
|
835
|
+
if (!Array.isArray(rows) || !rows.length) return 'No agent utilization data for this period.';
|
|
836
|
+
const lines = [
|
|
837
|
+
`=== Agent Utilization (${rows.length} agents) ===`,
|
|
838
|
+
...rows.map(a =>
|
|
839
|
+
` ${a.agentName} — ${a.totalConversations} convos | avg handle: ${a.avgHandleTimeSecs}s | active: ${a.activePercent}% | away: ${a.awayPercent}%`
|
|
840
|
+
),
|
|
841
|
+
];
|
|
842
|
+
return lines.join('\n');
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
case 'report_campaign_performance': {
|
|
846
|
+
const days = args.days ?? 90;
|
|
847
|
+
const r = await api('GET', `/CampaignReports/Performance?days=${days}`);
|
|
848
|
+
const s = r.summary ?? {};
|
|
849
|
+
const lines = [
|
|
850
|
+
`=== Campaign Performance (last ${days} days, ${s.campaignCount ?? 0} campaigns) ===`,
|
|
851
|
+
`Total Sent: ${s.totalSent ?? 0} | Opens: ${s.totalOpened ?? 0} (${s.avgOpenRate ?? 0}%) | Clicks: ${s.totalClicked ?? 0} (${s.avgClickRate ?? 0}%)`,
|
|
852
|
+
'',
|
|
853
|
+
'--- Per Campaign ---',
|
|
854
|
+
...(Array.isArray(r.campaigns) && r.campaigns.length
|
|
855
|
+
? r.campaigns.map(c =>
|
|
856
|
+
` [${c.Id}] "${c.Name}" (${c.Channel}) ${c.SentAt?.slice(0, 10)} — sent ${c.SentCount}, open ${c.openRate}%, click ${c.clickRate}%, bounce ${c.bounceRate ?? 0}%`
|
|
857
|
+
)
|
|
858
|
+
: [' (no campaigns sent in this window)']),
|
|
859
|
+
];
|
|
860
|
+
return lines.join('\n');
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
case 'report_campaign_deliverability': {
|
|
864
|
+
const days = args.days ?? 30;
|
|
865
|
+
const r = await api('GET', `/CampaignReports/Deliverability?days=${days}`);
|
|
866
|
+
const lines = [
|
|
867
|
+
`=== Deliverability Health (${r.period}) ===`,
|
|
868
|
+
`Total Sent: ${r.totalSent}`,
|
|
869
|
+
`Delivery Rate: ${r.deliveryRate}% [${r.deliveryStatus?.toUpperCase()}]`,
|
|
870
|
+
`Bounce Rate: ${r.bounceRate}% [${r.bounceStatus?.toUpperCase()}] (hard: ${r.totalHardBounce}, soft: ${r.totalSoftBounce})`,
|
|
871
|
+
`Complaint Rate: ${r.complaintRate}% [${r.complaintStatus?.toUpperCase()}]`,
|
|
872
|
+
`Unsubscribe Rate: ${r.unsubRate}% [${r.unsubStatus?.toUpperCase()}] (${r.unsubCount} unsubs)`,
|
|
873
|
+
`Open Rate: ${r.openRate}% [${r.openStatus?.toUpperCase()}]`,
|
|
874
|
+
'',
|
|
875
|
+
'--- Recommendations ---',
|
|
876
|
+
...(r.recommendations?.length ? r.recommendations.map(rec => ` • ${rec}`) : [' All metrics healthy.']),
|
|
877
|
+
];
|
|
878
|
+
return lines.join('\n');
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
case 'report_campaign_monthly': {
|
|
882
|
+
const rows = await api('GET', '/CampaignReports/Monthly');
|
|
883
|
+
if (!Array.isArray(rows) || !rows.length) return 'No monthly campaign data (no sent campaigns in the last 12 months).';
|
|
884
|
+
const lines = [
|
|
885
|
+
'=== Monthly Email Volume (last 12 months) ===',
|
|
886
|
+
...rows.map(m => ` ${m.month}: sent ${m.sent}, opened ${m.opened}, clicked ${m.clicked}`),
|
|
887
|
+
];
|
|
888
|
+
return lines.join('\n');
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
case 'report_audience_growth': {
|
|
892
|
+
const r = await api('GET', '/CampaignReports/AudienceGrowth');
|
|
893
|
+
const lines = [
|
|
894
|
+
`=== Audience Growth ===`,
|
|
895
|
+
`Total Active Contacts: ${r.totalContacts} | Unsubscribed: ${r.unsubTotal} | Net: ${r.netContacts}`,
|
|
896
|
+
'',
|
|
897
|
+
'--- Monthly Additions (last 12 months) ---',
|
|
898
|
+
...(r.monthly?.length
|
|
899
|
+
? r.monthly.map(m => ` ${m.month}: +${m.added} contacts`)
|
|
900
|
+
: [' (no data)']),
|
|
901
|
+
];
|
|
902
|
+
return lines.join('\n');
|
|
903
|
+
}
|
|
904
|
+
|
|
240
905
|
default:
|
|
241
906
|
throw new Error(`Unknown tool: ${name}`);
|
|
242
907
|
}
|
|
@@ -245,7 +910,7 @@ async function handleTool(name, args) {
|
|
|
245
910
|
// ── Server setup ──────────────────────────────────────────────────────────────
|
|
246
911
|
|
|
247
912
|
const server = new Server(
|
|
248
|
-
{ name: 'velaro', version: '0.
|
|
913
|
+
{ name: 'velaro', version: '0.4.0' },
|
|
249
914
|
{ capabilities: { tools: {} } }
|
|
250
915
|
);
|
|
251
916
|
|