notifyme-csm-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/dist/index.js +541 -0
- package/package.json +41 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* NotifyMe CSM — MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes CSM data and actions to LLMs via the Model Context Protocol (stdio).
|
|
6
|
+
*
|
|
7
|
+
* Configuration (env vars):
|
|
8
|
+
* CSM_API_TOKEN — personal bearer token from Settings → API Tokens
|
|
9
|
+
* CSM_BASE_URL — defaults to prod Cloud Run URL
|
|
10
|
+
*
|
|
11
|
+
* Tools (full feature set):
|
|
12
|
+
*
|
|
13
|
+
* Companies
|
|
14
|
+
* list_companies — browse/filter companies
|
|
15
|
+
* search_companies — search by name / domain / email
|
|
16
|
+
* get_company — full profile + recent notes/reminders/meetings/emails
|
|
17
|
+
* update_company — update priority / state / tags
|
|
18
|
+
*
|
|
19
|
+
* Per-company sub-resources
|
|
20
|
+
* list_notes — all notes for a company
|
|
21
|
+
* list_meetings — all meetings for a company
|
|
22
|
+
* list_emails — all sent emails for a company
|
|
23
|
+
* list_feature_requests — feature requests from a company
|
|
24
|
+
* get_activity — activity log for a company
|
|
25
|
+
*
|
|
26
|
+
* Meetings
|
|
27
|
+
* upcoming_meetings — upcoming calendar meetings
|
|
28
|
+
* log_meeting — record a meeting on a company
|
|
29
|
+
*
|
|
30
|
+
* Emails
|
|
31
|
+
* draft_email — AI-generate an email draft
|
|
32
|
+
* send_email — send email via Gmail and log it
|
|
33
|
+
*
|
|
34
|
+
* Feature Requests
|
|
35
|
+
* create_feature_request — log a feature request
|
|
36
|
+
*
|
|
37
|
+
* Reminders
|
|
38
|
+
* list_reminders — overdue + due-today follow-ups
|
|
39
|
+
* create_reminder — set a follow-up with due date
|
|
40
|
+
* complete_reminder — mark a reminder done
|
|
41
|
+
*
|
|
42
|
+
* Dashboard
|
|
43
|
+
* get_dashboard — at-risk merchants
|
|
44
|
+
* get_stats — full dashboard stats
|
|
45
|
+
*
|
|
46
|
+
* Kanban
|
|
47
|
+
* list_kanban_columns — list pipeline columns
|
|
48
|
+
* move_to_column — move a company to a pipeline column
|
|
49
|
+
*/
|
|
50
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
51
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
52
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
53
|
+
const BASE_URL = process.env.CSM_BASE_URL ??
|
|
54
|
+
"https://notify-me-csm-44700630095.us-central1.run.app";
|
|
55
|
+
const TOKEN = process.env.CSM_API_TOKEN ?? "";
|
|
56
|
+
if (!TOKEN) {
|
|
57
|
+
console.error("[notifyme-mcp] CSM_API_TOKEN is not set. Exiting.");
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
// ── HTTP helper ────────────────────────────────────────────────────────────
|
|
61
|
+
async function api(method, path, body) {
|
|
62
|
+
const url = `${BASE_URL}/api/mcp${path}`;
|
|
63
|
+
const res = await fetch(url, {
|
|
64
|
+
method,
|
|
65
|
+
headers: {
|
|
66
|
+
Authorization: `Bearer ${TOKEN}`,
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
},
|
|
69
|
+
body: body != null ? JSON.stringify(body) : undefined,
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
const text = await res.text().catch(() => res.statusText);
|
|
73
|
+
throw new Error(`CSM API error ${res.status}: ${text}`);
|
|
74
|
+
}
|
|
75
|
+
return res.json();
|
|
76
|
+
}
|
|
77
|
+
// ── Tool definitions ───────────────────────────────────────────────────────
|
|
78
|
+
const TOOLS = [
|
|
79
|
+
// ── Companies ────────────────────────────────────────────────────────────
|
|
80
|
+
{
|
|
81
|
+
name: "list_companies",
|
|
82
|
+
description: "Browse or filter all companies in the CSM. Supports optional filters for type and stage/state. Use search_companies for name/domain lookups.",
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: "object",
|
|
85
|
+
properties: {
|
|
86
|
+
type: {
|
|
87
|
+
type: "string",
|
|
88
|
+
enum: ["agency", "merchant", "app_partner", "technology_partner", "media", "investor", "other"],
|
|
89
|
+
description: "Filter by company type (optional)",
|
|
90
|
+
},
|
|
91
|
+
stage: { type: "string", description: "Filter by state/stage (optional, e.g. 'active', 'churned')" },
|
|
92
|
+
limit: { type: "number", description: "Max results (default 25, max 100)" },
|
|
93
|
+
},
|
|
94
|
+
required: [],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "search_companies",
|
|
99
|
+
description: "Search NotifyMe CSM companies (agencies, merchants, partners) by name, domain, or email. Always call this first to find the company id before calling get_company.",
|
|
100
|
+
inputSchema: {
|
|
101
|
+
type: "object",
|
|
102
|
+
properties: {
|
|
103
|
+
q: { type: "string", description: "Search query (name, domain, or email)" },
|
|
104
|
+
type: {
|
|
105
|
+
type: "string",
|
|
106
|
+
enum: ["agency", "merchant", "app_partner", "technology_partner", "media", "investor", "other"],
|
|
107
|
+
description: "Filter by company type (optional)",
|
|
108
|
+
},
|
|
109
|
+
limit: { type: "number", description: "Max results (default 10, max 50)" },
|
|
110
|
+
},
|
|
111
|
+
required: ["q"],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "get_company",
|
|
116
|
+
description: "Get the full profile for a company by its CSM id, including recent notes, open reminders, recent meetings, and recent emails. Use search_companies first to find the id.",
|
|
117
|
+
inputSchema: {
|
|
118
|
+
type: "object",
|
|
119
|
+
properties: {
|
|
120
|
+
id: { type: "number", description: "Company id (from search_companies or list_companies)" },
|
|
121
|
+
},
|
|
122
|
+
required: ["id"],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "update_company",
|
|
127
|
+
description: "Update a company's priority, state, or tags. All fields are optional — only pass what you want to change.",
|
|
128
|
+
inputSchema: {
|
|
129
|
+
type: "object",
|
|
130
|
+
properties: {
|
|
131
|
+
id: { type: "number", description: "Company id" },
|
|
132
|
+
priority: { type: "string", enum: ["p1", "p2", "p3"], description: "Priority tier" },
|
|
133
|
+
state: { type: "string", description: "State/stage (e.g. 'active', 'churned')" },
|
|
134
|
+
tags: { type: "array", items: { type: "string" }, description: "Tags array (replaces existing)" },
|
|
135
|
+
},
|
|
136
|
+
required: ["id"],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
// ── Per-company sub-resources ─────────────────────────────────────────────
|
|
140
|
+
{
|
|
141
|
+
name: "list_notes",
|
|
142
|
+
description: "List all notes for a company. Use get_company for a quick snapshot; use this for deeper history.",
|
|
143
|
+
inputSchema: {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: {
|
|
146
|
+
companyId: { type: "number", description: "Company id" },
|
|
147
|
+
limit: { type: "number", description: "Max results (default 20, max 100)" },
|
|
148
|
+
},
|
|
149
|
+
required: ["companyId"],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: "list_meetings",
|
|
154
|
+
description: "List meetings for a specific company, most recent first.",
|
|
155
|
+
inputSchema: {
|
|
156
|
+
type: "object",
|
|
157
|
+
properties: {
|
|
158
|
+
companyId: { type: "number", description: "Company id" },
|
|
159
|
+
limit: { type: "number", description: "Max results (default 10, max 50)" },
|
|
160
|
+
},
|
|
161
|
+
required: ["companyId"],
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "list_emails",
|
|
166
|
+
description: "List emails sent to a company, most recent first.",
|
|
167
|
+
inputSchema: {
|
|
168
|
+
type: "object",
|
|
169
|
+
properties: {
|
|
170
|
+
companyId: { type: "number", description: "Company id" },
|
|
171
|
+
limit: { type: "number", description: "Max results (default 20, max 100)" },
|
|
172
|
+
},
|
|
173
|
+
required: ["companyId"],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: "list_feature_requests",
|
|
178
|
+
description: "List feature requests submitted for a company.",
|
|
179
|
+
inputSchema: {
|
|
180
|
+
type: "object",
|
|
181
|
+
properties: {
|
|
182
|
+
companyId: { type: "number", description: "Company id" },
|
|
183
|
+
},
|
|
184
|
+
required: ["companyId"],
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: "get_activity",
|
|
189
|
+
description: "Get the activity log for a company (notes, meetings, emails, reminders).",
|
|
190
|
+
inputSchema: {
|
|
191
|
+
type: "object",
|
|
192
|
+
properties: {
|
|
193
|
+
companyId: { type: "number", description: "Company id" },
|
|
194
|
+
limit: { type: "number", description: "Max results (default 20, max 100)" },
|
|
195
|
+
},
|
|
196
|
+
required: ["companyId"],
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
// ── Notes ─────────────────────────────────────────────────────────────────
|
|
200
|
+
{
|
|
201
|
+
name: "add_note",
|
|
202
|
+
description: "Add a note to a company in the CSM. Use this after a meeting, call, or any interaction worth logging.",
|
|
203
|
+
inputSchema: {
|
|
204
|
+
type: "object",
|
|
205
|
+
properties: {
|
|
206
|
+
companyId: { type: "number", description: "Company id (from search_companies)" },
|
|
207
|
+
content: { type: "string", description: "Note content (plain text, up to 10,000 chars)" },
|
|
208
|
+
},
|
|
209
|
+
required: ["companyId", "content"],
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
// ── Meetings ──────────────────────────────────────────────────────────────
|
|
213
|
+
{
|
|
214
|
+
name: "upcoming_meetings",
|
|
215
|
+
description: "List upcoming calendar meetings (future scheduled meetings across all companies).",
|
|
216
|
+
inputSchema: {
|
|
217
|
+
type: "object",
|
|
218
|
+
properties: {
|
|
219
|
+
limit: { type: "number", description: "Max results (default 10, max 50)" },
|
|
220
|
+
},
|
|
221
|
+
required: [],
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "log_meeting",
|
|
226
|
+
description: "Log a meeting on a company (past or future). Use this to record meetings that happened.",
|
|
227
|
+
inputSchema: {
|
|
228
|
+
type: "object",
|
|
229
|
+
properties: {
|
|
230
|
+
companyId: { type: "number", description: "Company id" },
|
|
231
|
+
title: { type: "string", description: "Meeting title/subject" },
|
|
232
|
+
scheduledAt: { type: "string", description: "ISO 8601 datetime e.g. 2026-06-01T14:00:00Z" },
|
|
233
|
+
summary: { type: "string", description: "Meeting summary or notes (optional)" },
|
|
234
|
+
duration: { type: "number", description: "Duration in minutes (optional)" },
|
|
235
|
+
},
|
|
236
|
+
required: ["companyId", "title", "scheduledAt"],
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
// ── Emails ────────────────────────────────────────────────────────────────
|
|
240
|
+
{
|
|
241
|
+
name: "draft_email",
|
|
242
|
+
description: "AI-generate a personalized email draft for a company. Returns subject + body. Use send_email to send it afterward.",
|
|
243
|
+
inputSchema: {
|
|
244
|
+
type: "object",
|
|
245
|
+
properties: {
|
|
246
|
+
companyId: { type: "number", description: "Company id" },
|
|
247
|
+
emailType: {
|
|
248
|
+
type: "string",
|
|
249
|
+
enum: ["follow_up", "onboarding", "roi_improvement", "expansion", "general"],
|
|
250
|
+
description: "Type of email to generate",
|
|
251
|
+
},
|
|
252
|
+
userPrompt: { type: "string", description: "Additional context or instructions for the AI (optional)" },
|
|
253
|
+
},
|
|
254
|
+
required: ["companyId", "emailType"],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: "send_email",
|
|
259
|
+
description: "Send an email to a company via Gmail and log it in the CSM. Uses the company's contact email unless recipients is provided.",
|
|
260
|
+
inputSchema: {
|
|
261
|
+
type: "object",
|
|
262
|
+
properties: {
|
|
263
|
+
companyId: { type: "number", description: "Company id" },
|
|
264
|
+
subject: { type: "string", description: "Email subject" },
|
|
265
|
+
body: { type: "string", description: "Email body (plain text or HTML)" },
|
|
266
|
+
emailType: {
|
|
267
|
+
type: "string",
|
|
268
|
+
enum: ["follow_up", "onboarding", "roi_improvement", "expansion", "general"],
|
|
269
|
+
description: "Email category for logging (default: general)",
|
|
270
|
+
},
|
|
271
|
+
recipients: { type: "array", items: { type: "string" }, description: "Override recipient emails (optional)" },
|
|
272
|
+
},
|
|
273
|
+
required: ["companyId", "subject", "body"],
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
// ── Feature Requests ─────────────────────────────────────────────────────
|
|
277
|
+
{
|
|
278
|
+
name: "create_feature_request",
|
|
279
|
+
description: "Log a feature request from a company.",
|
|
280
|
+
inputSchema: {
|
|
281
|
+
type: "object",
|
|
282
|
+
properties: {
|
|
283
|
+
companyId: { type: "number", description: "Company id" },
|
|
284
|
+
title: { type: "string", description: "Short feature request title" },
|
|
285
|
+
description: { type: "string", description: "Detailed description (up to 5,000 chars)" },
|
|
286
|
+
priority: { type: "string", enum: ["low", "medium", "high"], description: "Priority (default: medium)" },
|
|
287
|
+
},
|
|
288
|
+
required: ["companyId", "title", "description"],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
// ── Reminders ─────────────────────────────────────────────────────────────
|
|
292
|
+
{
|
|
293
|
+
name: "list_reminders",
|
|
294
|
+
description: "List the current user's overdue and due-today follow-up reminders. Useful for a morning briefing or checking what needs attention.",
|
|
295
|
+
inputSchema: {
|
|
296
|
+
type: "object",
|
|
297
|
+
properties: {},
|
|
298
|
+
required: [],
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: "create_reminder",
|
|
303
|
+
description: "Create a follow-up reminder for a company. The reminder will appear in the Priority Follow-ups widget on the dashboard.",
|
|
304
|
+
inputSchema: {
|
|
305
|
+
type: "object",
|
|
306
|
+
properties: {
|
|
307
|
+
companyId: { type: "number", description: "Company id" },
|
|
308
|
+
message: { type: "string", description: "Reminder message (what to do)" },
|
|
309
|
+
dueAt: { type: "string", description: "ISO 8601 datetime e.g. 2026-06-01T09:00:00Z" },
|
|
310
|
+
},
|
|
311
|
+
required: ["companyId", "message", "dueAt"],
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: "complete_reminder",
|
|
316
|
+
description: "Mark a reminder as completed. Use after you've handled a follow-up.",
|
|
317
|
+
inputSchema: {
|
|
318
|
+
type: "object",
|
|
319
|
+
properties: {
|
|
320
|
+
id: { type: "number", description: "Reminder id (from list_reminders)" },
|
|
321
|
+
},
|
|
322
|
+
required: ["id"],
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
// ── Dashboard ─────────────────────────────────────────────────────────────
|
|
326
|
+
{
|
|
327
|
+
name: "get_dashboard",
|
|
328
|
+
description: "Get at-risk merchants from the CSM dashboard — merchants whose activity or spend signals they may churn.",
|
|
329
|
+
inputSchema: {
|
|
330
|
+
type: "object",
|
|
331
|
+
properties: {},
|
|
332
|
+
required: [],
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: "get_stats",
|
|
337
|
+
description: "Get full dashboard stats: total companies, active merchants, at-risk count, open reminders, upcoming meetings, recent activity, and more.",
|
|
338
|
+
inputSchema: {
|
|
339
|
+
type: "object",
|
|
340
|
+
properties: {},
|
|
341
|
+
required: [],
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
// ── Kanban ────────────────────────────────────────────────────────────────
|
|
345
|
+
{
|
|
346
|
+
name: "list_kanban_columns",
|
|
347
|
+
description: "List all pipeline (Kanban) columns and their positions. Use move_to_column to move companies.",
|
|
348
|
+
inputSchema: {
|
|
349
|
+
type: "object",
|
|
350
|
+
properties: {},
|
|
351
|
+
required: [],
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
name: "move_to_column",
|
|
356
|
+
description: "Move a company to a specific pipeline (Kanban) column. Creates a card if none exists.",
|
|
357
|
+
inputSchema: {
|
|
358
|
+
type: "object",
|
|
359
|
+
properties: {
|
|
360
|
+
companyId: { type: "number", description: "Company id" },
|
|
361
|
+
columnId: { type: "number", description: "Target column id (from list_kanban_columns)" },
|
|
362
|
+
},
|
|
363
|
+
required: ["companyId", "columnId"],
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
];
|
|
367
|
+
// ── Server setup ───────────────────────────────────────────────────────────
|
|
368
|
+
const server = new Server({ name: "notifyme-csm", version: "2.0.0" }, { capabilities: { tools: {} } });
|
|
369
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
370
|
+
tools: TOOLS.map((t) => ({
|
|
371
|
+
name: t.name,
|
|
372
|
+
description: t.description,
|
|
373
|
+
inputSchema: t.inputSchema,
|
|
374
|
+
})),
|
|
375
|
+
}));
|
|
376
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
377
|
+
const { name, arguments: args } = request.params;
|
|
378
|
+
try {
|
|
379
|
+
let data;
|
|
380
|
+
switch (name) {
|
|
381
|
+
// ── Companies ──────────────────────────────────────────────────────────
|
|
382
|
+
case "list_companies": {
|
|
383
|
+
const { type, stage, limit } = args;
|
|
384
|
+
const params = new URLSearchParams();
|
|
385
|
+
if (type)
|
|
386
|
+
params.set("type", type);
|
|
387
|
+
if (stage)
|
|
388
|
+
params.set("stage", stage);
|
|
389
|
+
if (limit)
|
|
390
|
+
params.set("limit", String(limit));
|
|
391
|
+
data = await api("GET", `/companies?${params}`);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
case "search_companies": {
|
|
395
|
+
const { q, type, limit } = args;
|
|
396
|
+
const params = new URLSearchParams({ q });
|
|
397
|
+
if (type)
|
|
398
|
+
params.set("type", type);
|
|
399
|
+
if (limit)
|
|
400
|
+
params.set("limit", String(limit));
|
|
401
|
+
data = await api("GET", `/companies/search?${params}`);
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
case "get_company": {
|
|
405
|
+
const { id } = args;
|
|
406
|
+
data = await api("GET", `/companies/${id}`);
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
case "update_company": {
|
|
410
|
+
const { id, ...updates } = args;
|
|
411
|
+
data = await api("PATCH", `/companies/${id}`, updates);
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
// ── Per-company sub-resources ──────────────────────────────────────────
|
|
415
|
+
case "list_notes": {
|
|
416
|
+
const { companyId, limit } = args;
|
|
417
|
+
const params = new URLSearchParams();
|
|
418
|
+
if (limit)
|
|
419
|
+
params.set("limit", String(limit));
|
|
420
|
+
data = await api("GET", `/companies/${companyId}/notes?${params}`);
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
case "list_meetings": {
|
|
424
|
+
const { companyId, limit } = args;
|
|
425
|
+
const params = new URLSearchParams();
|
|
426
|
+
if (limit)
|
|
427
|
+
params.set("limit", String(limit));
|
|
428
|
+
data = await api("GET", `/companies/${companyId}/meetings?${params}`);
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
case "list_emails": {
|
|
432
|
+
const { companyId, limit } = args;
|
|
433
|
+
const params = new URLSearchParams();
|
|
434
|
+
if (limit)
|
|
435
|
+
params.set("limit", String(limit));
|
|
436
|
+
data = await api("GET", `/companies/${companyId}/emails?${params}`);
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
case "list_feature_requests": {
|
|
440
|
+
const { companyId } = args;
|
|
441
|
+
data = await api("GET", `/companies/${companyId}/feature-requests`);
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
case "get_activity": {
|
|
445
|
+
const { companyId, limit } = args;
|
|
446
|
+
const params = new URLSearchParams();
|
|
447
|
+
if (limit)
|
|
448
|
+
params.set("limit", String(limit));
|
|
449
|
+
data = await api("GET", `/companies/${companyId}/activity?${params}`);
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
// ── Notes ──────────────────────────────────────────────────────────────
|
|
453
|
+
case "add_note": {
|
|
454
|
+
const { companyId, content } = args;
|
|
455
|
+
data = await api("POST", "/notes", { companyId, content });
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
// ── Meetings ───────────────────────────────────────────────────────────
|
|
459
|
+
case "upcoming_meetings": {
|
|
460
|
+
const { limit } = args;
|
|
461
|
+
const params = new URLSearchParams();
|
|
462
|
+
if (limit)
|
|
463
|
+
params.set("limit", String(limit));
|
|
464
|
+
data = await api("GET", `/meetings/upcoming?${params}`);
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
case "log_meeting": {
|
|
468
|
+
const { companyId, title, scheduledAt, summary, duration } = args;
|
|
469
|
+
data = await api("POST", "/meetings", { companyId, title, scheduledAt, summary, duration });
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
// ── Emails ─────────────────────────────────────────────────────────────
|
|
473
|
+
case "draft_email": {
|
|
474
|
+
const { companyId, emailType, userPrompt } = args;
|
|
475
|
+
data = await api("POST", "/emails/draft", { companyId, emailType, userPrompt });
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
case "send_email": {
|
|
479
|
+
const { companyId, subject, body, emailType, recipients } = args;
|
|
480
|
+
data = await api("POST", "/emails/send", { companyId, subject, body, emailType, recipients });
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
// ── Feature Requests ───────────────────────────────────────────────────
|
|
484
|
+
case "create_feature_request": {
|
|
485
|
+
const { companyId, title, description, priority } = args;
|
|
486
|
+
data = await api("POST", "/feature-requests", { companyId, title, description, priority });
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
// ── Reminders ──────────────────────────────────────────────────────────
|
|
490
|
+
case "list_reminders": {
|
|
491
|
+
data = await api("GET", "/reminders");
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
case "create_reminder": {
|
|
495
|
+
const { companyId, message, dueAt } = args;
|
|
496
|
+
data = await api("POST", "/reminders", { companyId, message, dueAt });
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
case "complete_reminder": {
|
|
500
|
+
const { id } = args;
|
|
501
|
+
data = await api("POST", `/reminders/${id}/complete`, {});
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
// ── Dashboard ──────────────────────────────────────────────────────────
|
|
505
|
+
case "get_dashboard": {
|
|
506
|
+
data = await api("GET", "/dashboard");
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
case "get_stats": {
|
|
510
|
+
data = await api("GET", "/stats");
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
// ── Kanban ─────────────────────────────────────────────────────────────
|
|
514
|
+
case "list_kanban_columns": {
|
|
515
|
+
data = await api("GET", "/kanban/columns");
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
case "move_to_column": {
|
|
519
|
+
const { companyId, columnId } = args;
|
|
520
|
+
data = await api("POST", "/kanban/move", { companyId, columnId });
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
default:
|
|
524
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
catch (err) {
|
|
531
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
532
|
+
return {
|
|
533
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
534
|
+
isError: true,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
// ── Start ──────────────────────────────────────────────────────────────────
|
|
539
|
+
const transport = new StdioServerTransport();
|
|
540
|
+
await server.connect(transport);
|
|
541
|
+
console.error("[notifyme-mcp] MCP server running on stdio");
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "notifyme-csm-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "NotifyMe CSM — MCP server for LLM integrations (Claude Code, Cursor, etc.)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"notifyme-csm-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsx src/index.ts",
|
|
19
|
+
"start": "node dist/index.js",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
24
|
+
"zod": "^3.25.76"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^22.19.19",
|
|
28
|
+
"tsx": "^4.22.3",
|
|
29
|
+
"typescript": "^5.9.3"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"mcp",
|
|
36
|
+
"claude",
|
|
37
|
+
"notifyme",
|
|
38
|
+
"csm"
|
|
39
|
+
],
|
|
40
|
+
"license": "UNLICENSED"
|
|
41
|
+
}
|