@adrata/adrata-mcp 1.0.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 +548 -0
- package/access/auth.js +289 -0
- package/access/oauth.js +1059 -0
- package/access/resource-metadata.js +167 -0
- package/access/tiers.js +422 -0
- package/analytics.js +634 -0
- package/api-bridge.js +499 -0
- package/governance/money.js +141 -0
- package/output-formatter.js +589 -0
- package/package.json +68 -0
- package/resources.js +246 -0
- package/security.js +690 -0
- package/server.js +2139 -0
- package/server.json +55 -0
- package/skills/backlog-triage/SKILL.md +115 -0
- package/skills/board-review/SKILL.md +96 -0
- package/skills/incident-to-card/SKILL.md +126 -0
- package/skills/log-outreach.md +62 -0
- package/skills/ship-the-card/SKILL.md +155 -0
- package/tool-annotations.js +269 -0
- package/tools/billing.js +149 -0
- package/tools/email-tools.js +652 -0
- package/tools/enterprise-tools.js +651 -0
- package/tools/free-search.js +160 -0
- package/tools/memory.js +440 -0
- package/tools/morning-brief.js +551 -0
- package/tools/paper-tools.js +563 -0
- package/tools/scheduling.js +322 -0
- package/tools/work-board-tools.js +758 -0
- package/toolsets/communications.js +276 -0
- package/toolsets/crm.js +495 -0
- package/toolsets/extensibility.js +1131 -0
- package/toolsets/infrastructure.js +757 -0
- package/toolsets/intelligence.js +232 -0
- package/toolsets/knowledge.js +154 -0
- package/toolsets/matrix.js +217 -0
- package/toolsets/outreach.js +432 -0
- package/toolsets/prospecting.js +314 -0
- package/toolsets/revenue/always-loaded.js +341 -0
- package/toolsets/revenue/sloan-tools.js +81 -0
- package/transport-http.js +505 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Communications Toolset (5 tools, Enterprise tier)
|
|
3
|
+
*
|
|
4
|
+
* make_call, send_sms, get_call_transcript,
|
|
5
|
+
* check_calendar, schedule_meeting
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { md, mdError, table, formatDate } from '../output-formatter.js';
|
|
10
|
+
|
|
11
|
+
export function register(server, api, AUTH) {
|
|
12
|
+
|
|
13
|
+
// -----------------------------------------------------------------------
|
|
14
|
+
// make_call
|
|
15
|
+
// -----------------------------------------------------------------------
|
|
16
|
+
server.tool(
|
|
17
|
+
'make_call',
|
|
18
|
+
'Dial a number via connected telephony provider. Initiates the call and returns a call ID for tracking. Use get_call_transcript after the call to get the AI summary.',
|
|
19
|
+
{
|
|
20
|
+
phoneNumber: z.string().describe('Phone number to dial'),
|
|
21
|
+
personId: z.string().optional().describe('Person ID for CRM linking'),
|
|
22
|
+
companyId: z.string().optional().describe('Company ID for CRM linking'),
|
|
23
|
+
notes: z.string().optional().describe('Pre-call notes or talking points'),
|
|
24
|
+
},
|
|
25
|
+
async (args) => {
|
|
26
|
+
try {
|
|
27
|
+
const result = await api('POST', '/api/v1/calls', {
|
|
28
|
+
body: {
|
|
29
|
+
phoneNumber: args.phoneNumber,
|
|
30
|
+
personId: args.personId,
|
|
31
|
+
companyId: args.companyId,
|
|
32
|
+
notes: args.notes,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const c = result?.data || result || {};
|
|
37
|
+
let text = `## Call Initiated\n\n`;
|
|
38
|
+
text += `- **To:** ${args.phoneNumber}\n`;
|
|
39
|
+
text += `- **Call ID:** ${c.id || c.callId || '\u2014'}\n`;
|
|
40
|
+
text += `- **Status:** ${c.status || 'Connecting...'}\n`;
|
|
41
|
+
if (args.personId) text += `- **Linked to person:** ${args.personId}\n`;
|
|
42
|
+
if (args.notes) text += `\n### Talking Points\n${args.notes}\n`;
|
|
43
|
+
text += '\nAfter the call, use `get_call_transcript` to get the AI summary.\n';
|
|
44
|
+
|
|
45
|
+
// Log action
|
|
46
|
+
if (args.personId) {
|
|
47
|
+
await api('POST', '/api/v1/actions', {
|
|
48
|
+
body: {
|
|
49
|
+
title: `Call to ${args.phoneNumber}`,
|
|
50
|
+
type: 'call',
|
|
51
|
+
personId: args.personId,
|
|
52
|
+
companyId: args.companyId,
|
|
53
|
+
status: 'IN_PROGRESS',
|
|
54
|
+
metadata: { callId: c.id || c.callId, phoneNumber: args.phoneNumber },
|
|
55
|
+
},
|
|
56
|
+
}).catch(() => {});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return md(text);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
return mdError('Call failed', err.message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// -----------------------------------------------------------------------
|
|
67
|
+
// send_sms
|
|
68
|
+
// -----------------------------------------------------------------------
|
|
69
|
+
server.tool(
|
|
70
|
+
'send_sms',
|
|
71
|
+
'Send a text message via connected telephony provider (Twilio/Vonage). Links to CRM for tracking.',
|
|
72
|
+
{
|
|
73
|
+
to: z.string().describe('Recipient phone number'),
|
|
74
|
+
message: z.string().describe('SMS message text'),
|
|
75
|
+
personId: z.string().optional().describe('Person ID for CRM linking'),
|
|
76
|
+
},
|
|
77
|
+
async (args) => {
|
|
78
|
+
try {
|
|
79
|
+
const result = await api('POST', '/api/v1/sms', {
|
|
80
|
+
body: {
|
|
81
|
+
to: args.to,
|
|
82
|
+
message: args.message,
|
|
83
|
+
personId: args.personId,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const r = result?.data || result || {};
|
|
88
|
+
let text = `## SMS Sent\n\n`;
|
|
89
|
+
text += `- **To:** ${args.to}\n`;
|
|
90
|
+
text += `- **Message:** ${args.message}\n`;
|
|
91
|
+
text += `- **Status:** ${r.status || 'Sent'}\n`;
|
|
92
|
+
if (r.id) text += `- **ID:** ${r.id}\n`;
|
|
93
|
+
|
|
94
|
+
return md(text);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
return mdError('SMS failed', err.message);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// -----------------------------------------------------------------------
|
|
102
|
+
// get_call_transcript
|
|
103
|
+
// -----------------------------------------------------------------------
|
|
104
|
+
server.tool(
|
|
105
|
+
'get_call_transcript',
|
|
106
|
+
'Retrieve call recording transcript with AI summary, key topics, action items, and sentiment analysis.',
|
|
107
|
+
{
|
|
108
|
+
callId: z.string().describe('Call ID (from make_call or meeting)'),
|
|
109
|
+
},
|
|
110
|
+
async (args) => {
|
|
111
|
+
try {
|
|
112
|
+
const result = await api('GET', `/api/v1/calls/${args.callId}/transcript`);
|
|
113
|
+
const t = result?.data || result || {};
|
|
114
|
+
|
|
115
|
+
let text = `## Call Transcript\n\n`;
|
|
116
|
+
text += `- **Call ID:** ${args.callId}\n`;
|
|
117
|
+
text += `- **Duration:** ${t.duration || '\u2014'}\n`;
|
|
118
|
+
text += `- **Date:** ${formatDate(t.date || t.createdAt)}\n`;
|
|
119
|
+
if (t.sentiment) text += `- **Sentiment:** ${t.sentiment}\n`;
|
|
120
|
+
text += '\n';
|
|
121
|
+
|
|
122
|
+
if (t.summary) {
|
|
123
|
+
text += `### AI Summary\n${t.summary}\n\n`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (t.keyTopics?.length) {
|
|
127
|
+
text += `### Key Topics\n`;
|
|
128
|
+
t.keyTopics.forEach(topic => { text += `- ${topic}\n`; });
|
|
129
|
+
text += '\n';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (t.actionItems?.length) {
|
|
133
|
+
text += `### Action Items\n`;
|
|
134
|
+
t.actionItems.forEach(item => { text += `- [ ] ${item}\n`; });
|
|
135
|
+
text += '\n';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (t.transcript) {
|
|
139
|
+
text += `### Full Transcript\n${t.transcript}\n`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return md(text);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
return mdError('Transcript retrieval failed', err.message);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// -----------------------------------------------------------------------
|
|
150
|
+
// check_calendar
|
|
151
|
+
// -----------------------------------------------------------------------
|
|
152
|
+
server.tool(
|
|
153
|
+
'check_calendar',
|
|
154
|
+
'View your calendar: upcoming meetings, free/busy times, and scheduling conflicts. Helps find open slots for meetings.',
|
|
155
|
+
{
|
|
156
|
+
date: z.string().optional().describe('Date to check (ISO, default: today)'),
|
|
157
|
+
days: z.number().optional().describe('Number of days to look ahead (default 7)'),
|
|
158
|
+
},
|
|
159
|
+
async (args) => {
|
|
160
|
+
try {
|
|
161
|
+
// Read the calendar `events` store (real meetings with startTime and
|
|
162
|
+
// attendee rosters), not the AI meeting-copilot `meetings` table which
|
|
163
|
+
// is empty until a call is recorded. `args.date` anchors the window;
|
|
164
|
+
// `args.days` extends the lookahead (default 7).
|
|
165
|
+
const start = args.date ? new Date(args.date) : new Date();
|
|
166
|
+
const days = Number.isFinite(args.days) ? args.days : 7;
|
|
167
|
+
const end = new Date(start.getTime() + days * 24 * 60 * 60 * 1000);
|
|
168
|
+
const params = {
|
|
169
|
+
start_date: start.toISOString(),
|
|
170
|
+
end_date: end.toISOString(),
|
|
171
|
+
limit: 25,
|
|
172
|
+
page: 1,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const meetings = await api('GET', '/api/v1/events', { params });
|
|
176
|
+
const list = meetings?.data || [];
|
|
177
|
+
|
|
178
|
+
if (list.length === 0) {
|
|
179
|
+
return md('## Calendar\n\nNo upcoming meetings. Your calendar is wide open.\n');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let text = `## Calendar \u2014 ${list.length} Upcoming Meetings\n\n`;
|
|
183
|
+
const rows = list.slice(0, 15).map(m => [
|
|
184
|
+
formatDate(m.startTime || m.date),
|
|
185
|
+
m.startTime ? new Date(m.startTime).toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }) : '\u2014',
|
|
186
|
+
m.title || m.subject || '\u2014',
|
|
187
|
+
m.attendees?.length ? `${m.attendees.length} attendees` : '\u2014',
|
|
188
|
+
m.duration || '\u2014',
|
|
189
|
+
]);
|
|
190
|
+
text += table(['Date', 'Time', 'Meeting', 'Attendees', 'Duration'], rows);
|
|
191
|
+
|
|
192
|
+
return md(text);
|
|
193
|
+
} catch (err) {
|
|
194
|
+
return mdError('Calendar check failed', err.message);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// -----------------------------------------------------------------------
|
|
200
|
+
// schedule_meeting
|
|
201
|
+
// -----------------------------------------------------------------------
|
|
202
|
+
server.tool(
|
|
203
|
+
'schedule_meeting',
|
|
204
|
+
'Preview or create a meeting through the governed Adrata calendar path. Never claims invite delivery before provider synchronization.',
|
|
205
|
+
{
|
|
206
|
+
title: z.string().describe('Meeting title'),
|
|
207
|
+
eventId: z.string().optional().describe('Existing event ID to update instead of creating a duplicate'),
|
|
208
|
+
startTime: z.string().describe('Start time (ISO datetime)'),
|
|
209
|
+
duration: z.number().optional().describe('Duration in minutes (default 30)'),
|
|
210
|
+
attendeeEmails: z.array(z.string()).optional().describe('Attendee email addresses'),
|
|
211
|
+
personIds: z.array(z.string()).optional().describe('Person IDs from CRM'),
|
|
212
|
+
companyId: z.string().optional().describe('Company ID for CRM linking'),
|
|
213
|
+
description: z.string().optional().describe('Meeting description/agenda'),
|
|
214
|
+
location: z.string().optional().describe('Location or video link'),
|
|
215
|
+
endTime: z.string().optional().describe('End time (ISO datetime); defaults from duration'),
|
|
216
|
+
dryRun: z.boolean().optional().describe('Defaults true. Set false only after explicit approval.'),
|
|
217
|
+
reason: z.string().optional().describe('Required reason for requesting live-write confirmation.'),
|
|
218
|
+
confirmationToken: z.string().optional().describe('Single-use confirmation token returned by the server.'),
|
|
219
|
+
},
|
|
220
|
+
async (args) => {
|
|
221
|
+
try {
|
|
222
|
+
const start = new Date(args.startTime);
|
|
223
|
+
const endTime = args.endTime || new Date(start.getTime() + (args.duration || 30) * 60000).toISOString();
|
|
224
|
+
const result = await api('POST', '/api/v1/ai-crm-tools/execute', {
|
|
225
|
+
body: {
|
|
226
|
+
toolName: 'schedule_meeting',
|
|
227
|
+
arguments: {
|
|
228
|
+
title: args.title,
|
|
229
|
+
eventId: args.eventId,
|
|
230
|
+
startTime: args.startTime,
|
|
231
|
+
endTime,
|
|
232
|
+
attendees: args.attendeeEmails,
|
|
233
|
+
personIds: args.personIds,
|
|
234
|
+
companyId: args.companyId,
|
|
235
|
+
description: args.description,
|
|
236
|
+
location: args.location,
|
|
237
|
+
},
|
|
238
|
+
dryRun: args.dryRun ?? true,
|
|
239
|
+
reason: args.reason,
|
|
240
|
+
confirmationToken: args.confirmationToken,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const envelope = result?.data || result || {};
|
|
245
|
+
const m = envelope?.data || envelope;
|
|
246
|
+
let text = `## Meeting ${m.id ? 'Queued' : m.confirmationToken ? 'Awaiting Confirmation' : 'Preview'}\n\n`;
|
|
247
|
+
text += `- **Title:** ${args.title}\n`;
|
|
248
|
+
text += `- **When:** ${formatDate(args.startTime)} at ${new Date(args.startTime).toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}\n`;
|
|
249
|
+
text += `- **Duration:** ${args.duration || 30} minutes\n`;
|
|
250
|
+
if (args.attendeeEmails?.length) text += `- **Attendees:** ${args.attendeeEmails.join(', ')}\n`;
|
|
251
|
+
if (args.location) text += `- **Location:** ${args.location}\n`;
|
|
252
|
+
if (m.id) text += `- **ID:** ${m.id}\n`;
|
|
253
|
+
text += `\n${m.message || 'No invite delivery has been confirmed.'}\n`;
|
|
254
|
+
|
|
255
|
+
// Log action
|
|
256
|
+
if (m.id && (args.companyId || args.personIds?.length)) {
|
|
257
|
+
await api('POST', '/api/v1/actions', {
|
|
258
|
+
body: {
|
|
259
|
+
title: `Meeting scheduled: ${args.title}`,
|
|
260
|
+
type: 'meeting',
|
|
261
|
+
companyId: args.companyId,
|
|
262
|
+
personId: args.personIds?.[0],
|
|
263
|
+
status: 'PLANNED',
|
|
264
|
+
dueDate: args.startTime,
|
|
265
|
+
metadata: { meetingId: m.id, attendees: args.attendeeEmails },
|
|
266
|
+
},
|
|
267
|
+
}).catch(() => {});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return md(text);
|
|
271
|
+
} catch (err) {
|
|
272
|
+
return mdError('Scheduling failed', err.message);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
}
|