@codespar/mcp-rd-station 0.1.2 → 0.2.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 +238 -4
- package/package.json +2 -2
- package/server.json +3 -3
- package/src/index.ts +220 -4
package/dist/index.js
CHANGED
|
@@ -2,15 +2,25 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* MCP Server for RD Station — Brazilian CRM and marketing automation.
|
|
4
4
|
*
|
|
5
|
-
* Tools:
|
|
5
|
+
* Tools (18):
|
|
6
6
|
* - create_contact: Create a contact in RD Station
|
|
7
7
|
* - update_contact: Update a contact by UUID
|
|
8
|
+
* - upsert_contact: Upsert contact by email (Marketing API)
|
|
8
9
|
* - get_contact: Get contact details by UUID or email
|
|
9
10
|
* - list_contacts: List contacts with pagination
|
|
11
|
+
* - delete_contact: Delete a contact by UUID
|
|
10
12
|
* - create_event: Create a conversion event
|
|
11
13
|
* - list_funnels: List sales funnels
|
|
12
14
|
* - get_funnel: Get funnel details with stages
|
|
15
|
+
* - list_deal_stages: List deal stages of a pipeline
|
|
13
16
|
* - create_opportunity: Create a sales opportunity
|
|
17
|
+
* - update_deal: Update a deal/opportunity by ID
|
|
18
|
+
* - get_deal: Get a deal/opportunity by ID
|
|
19
|
+
* - list_deals: List deals with filters
|
|
20
|
+
* - list_segmentations: List contact segmentations
|
|
21
|
+
* - get_segmentation_contacts: List contacts of a segmentation
|
|
22
|
+
* - update_lead_scoring: Mark a contact as lead/opportunity (lead scoring)
|
|
23
|
+
* - create_webhook: Subscribe a webhook to RD Station events
|
|
14
24
|
*
|
|
15
25
|
* Environment:
|
|
16
26
|
* RD_STATION_TOKEN — Bearer token from https://app.rdstation.com/
|
|
@@ -35,9 +45,20 @@ async function rdStationRequest(method, path, body) {
|
|
|
35
45
|
const err = await res.text();
|
|
36
46
|
throw new Error(`RD Station API ${res.status}: ${err}`);
|
|
37
47
|
}
|
|
38
|
-
return
|
|
48
|
+
// Some DELETE endpoints return 204 No Content
|
|
49
|
+
if (res.status === 204)
|
|
50
|
+
return { ok: true };
|
|
51
|
+
const text = await res.text();
|
|
52
|
+
if (!text)
|
|
53
|
+
return { ok: true };
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(text);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return { raw: text };
|
|
59
|
+
}
|
|
39
60
|
}
|
|
40
|
-
const server = new Server({ name: "mcp-rd-station", version: "0.
|
|
61
|
+
const server = new Server({ name: "mcp-rd-station", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
41
62
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
42
63
|
tools: [
|
|
43
64
|
{
|
|
@@ -78,6 +99,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
78
99
|
required: ["uuid"],
|
|
79
100
|
},
|
|
80
101
|
},
|
|
102
|
+
{
|
|
103
|
+
name: "upsert_contact",
|
|
104
|
+
description: "Upsert (create or update) a contact identified by email (Marketing API)",
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: "object",
|
|
107
|
+
properties: {
|
|
108
|
+
email: { type: "string", description: "Identifier email (used in path)" },
|
|
109
|
+
name: { type: "string", description: "Contact name" },
|
|
110
|
+
job_title: { type: "string", description: "Job title" },
|
|
111
|
+
mobile_phone: { type: "string", description: "Mobile phone" },
|
|
112
|
+
tags: { type: "array", items: { type: "string" }, description: "Tags" },
|
|
113
|
+
cf_custom_fields: { type: "object", description: "Custom fields (key-value)" },
|
|
114
|
+
},
|
|
115
|
+
required: ["email"],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
81
118
|
{
|
|
82
119
|
name: "get_contact",
|
|
83
120
|
description: "Get contact details by UUID or email",
|
|
@@ -101,6 +138,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
101
138
|
},
|
|
102
139
|
},
|
|
103
140
|
},
|
|
141
|
+
{
|
|
142
|
+
name: "delete_contact",
|
|
143
|
+
description: "Delete a contact by UUID",
|
|
144
|
+
inputSchema: {
|
|
145
|
+
type: "object",
|
|
146
|
+
properties: {
|
|
147
|
+
uuid: { type: "string", description: "Contact UUID" },
|
|
148
|
+
},
|
|
149
|
+
required: ["uuid"],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
104
152
|
{
|
|
105
153
|
name: "create_event",
|
|
106
154
|
description: "Create a conversion event for a contact",
|
|
@@ -140,6 +188,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
140
188
|
required: ["id"],
|
|
141
189
|
},
|
|
142
190
|
},
|
|
191
|
+
{
|
|
192
|
+
name: "list_deal_stages",
|
|
193
|
+
description: "List deal stages of a pipeline (funnel)",
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: "object",
|
|
196
|
+
properties: {
|
|
197
|
+
deal_pipeline_id: { type: "string", description: "Pipeline (funnel) ID — optional filter" },
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
143
201
|
{
|
|
144
202
|
name: "create_opportunity",
|
|
145
203
|
description: "Create a sales opportunity in a funnel",
|
|
@@ -155,6 +213,117 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
155
213
|
required: ["deal_stage_id", "name"],
|
|
156
214
|
},
|
|
157
215
|
},
|
|
216
|
+
{
|
|
217
|
+
name: "update_deal",
|
|
218
|
+
description: "Update a deal/opportunity by ID",
|
|
219
|
+
inputSchema: {
|
|
220
|
+
type: "object",
|
|
221
|
+
properties: {
|
|
222
|
+
id: { type: "string", description: "Deal ID" },
|
|
223
|
+
name: { type: "string", description: "Updated name" },
|
|
224
|
+
amount_total: { type: "number", description: "Total amount" },
|
|
225
|
+
deal_stage_id: { type: "string", description: "Move to this stage" },
|
|
226
|
+
win: { type: "boolean", description: "Mark as won" },
|
|
227
|
+
prediction_date: { type: "string", description: "Updated prediction date (YYYY-MM-DD)" },
|
|
228
|
+
},
|
|
229
|
+
required: ["id"],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "get_deal",
|
|
234
|
+
description: "Get a deal/opportunity by ID",
|
|
235
|
+
inputSchema: {
|
|
236
|
+
type: "object",
|
|
237
|
+
properties: {
|
|
238
|
+
id: { type: "string", description: "Deal ID" },
|
|
239
|
+
},
|
|
240
|
+
required: ["id"],
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: "list_deals",
|
|
245
|
+
description: "List deals with optional filters and pagination",
|
|
246
|
+
inputSchema: {
|
|
247
|
+
type: "object",
|
|
248
|
+
properties: {
|
|
249
|
+
page: { type: "number", description: "Page number (default 1)" },
|
|
250
|
+
limit: { type: "number", description: "Results per page (default 20)" },
|
|
251
|
+
deal_stage_id: { type: "string", description: "Filter by stage ID" },
|
|
252
|
+
deal_pipeline_id: { type: "string", description: "Filter by pipeline ID" },
|
|
253
|
+
user_id: { type: "string", description: "Filter by owner user ID" },
|
|
254
|
+
win: { type: "string", description: "Filter by win status: true | false | null" },
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: "list_segmentations",
|
|
260
|
+
description: "List contact segmentations",
|
|
261
|
+
inputSchema: {
|
|
262
|
+
type: "object",
|
|
263
|
+
properties: {
|
|
264
|
+
page: { type: "number", description: "Page number" },
|
|
265
|
+
page_size: { type: "number", description: "Page size" },
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "get_segmentation_contacts",
|
|
271
|
+
description: "List contacts inside a given segmentation",
|
|
272
|
+
inputSchema: {
|
|
273
|
+
type: "object",
|
|
274
|
+
properties: {
|
|
275
|
+
segmentation_id: { type: "string", description: "Segmentation ID" },
|
|
276
|
+
page: { type: "number", description: "Page number" },
|
|
277
|
+
page_size: { type: "number", description: "Page size" },
|
|
278
|
+
},
|
|
279
|
+
required: ["segmentation_id"],
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: "update_lead_scoring",
|
|
284
|
+
description: "Mark a contact as lead, qualified lead, or opportunity (lead scoring)",
|
|
285
|
+
inputSchema: {
|
|
286
|
+
type: "object",
|
|
287
|
+
properties: {
|
|
288
|
+
email: { type: "string", description: "Contact email" },
|
|
289
|
+
status: {
|
|
290
|
+
type: "string",
|
|
291
|
+
enum: ["opportunity", "qualified_lead", "lead", "client"],
|
|
292
|
+
description: "Lifecycle status to apply",
|
|
293
|
+
},
|
|
294
|
+
value: { type: "boolean", description: "Set/unset the status (default true)" },
|
|
295
|
+
},
|
|
296
|
+
required: ["email", "status"],
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: "create_webhook",
|
|
301
|
+
description: "Subscribe a webhook to RD Station events (WEBHOOK.CONVERTED / WEBHOOK.MARKED_OPPORTUNITY)",
|
|
302
|
+
inputSchema: {
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: {
|
|
305
|
+
entity_type: { type: "string", description: "Entity type (e.g. CONTACT)" },
|
|
306
|
+
event_type: {
|
|
307
|
+
type: "string",
|
|
308
|
+
enum: ["WEBHOOK.CONVERTED", "WEBHOOK.MARKED_OPPORTUNITY"],
|
|
309
|
+
description: "Event type to subscribe to",
|
|
310
|
+
},
|
|
311
|
+
event_identifiers: {
|
|
312
|
+
type: "array",
|
|
313
|
+
items: { type: "string" },
|
|
314
|
+
description: "Optional list of conversion identifiers",
|
|
315
|
+
},
|
|
316
|
+
url: { type: "string", description: "Destination URL" },
|
|
317
|
+
http_method: { type: "string", enum: ["POST", "GET"], description: "HTTP method (default POST)" },
|
|
318
|
+
include_relations: {
|
|
319
|
+
type: "array",
|
|
320
|
+
items: { type: "string" },
|
|
321
|
+
description: "Relations to include (e.g. COMPANY, CONTACT_FUNNEL)",
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
required: ["entity_type", "event_type", "url"],
|
|
325
|
+
},
|
|
326
|
+
},
|
|
158
327
|
],
|
|
159
328
|
}));
|
|
160
329
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -169,6 +338,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
169
338
|
delete body.uuid;
|
|
170
339
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("PATCH", `/platform/contacts/${uuid}`, body), null, 2) }] };
|
|
171
340
|
}
|
|
341
|
+
case "upsert_contact": {
|
|
342
|
+
const email = args?.email;
|
|
343
|
+
const body = { ...args };
|
|
344
|
+
delete body.email;
|
|
345
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("PATCH", `/platform/contacts/email:${email}`, body), null, 2) }] };
|
|
346
|
+
}
|
|
172
347
|
case "get_contact": {
|
|
173
348
|
if (args?.uuid) {
|
|
174
349
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/contacts/${args.uuid}`), null, 2) }] };
|
|
@@ -185,14 +360,73 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
185
360
|
params.set("query", String(args.query));
|
|
186
361
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/contacts?${params}`), null, 2) }] };
|
|
187
362
|
}
|
|
363
|
+
case "delete_contact":
|
|
364
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("DELETE", `/platform/contacts/${args?.uuid}`), null, 2) }] };
|
|
188
365
|
case "create_event":
|
|
189
366
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("POST", "/platform/events", args), null, 2) }] };
|
|
190
367
|
case "list_funnels":
|
|
191
368
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", "/platform/deal_pipelines"), null, 2) }] };
|
|
192
369
|
case "get_funnel":
|
|
193
370
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/deal_pipelines/${args?.id}`), null, 2) }] };
|
|
371
|
+
case "list_deal_stages": {
|
|
372
|
+
const params = new URLSearchParams();
|
|
373
|
+
if (args?.deal_pipeline_id)
|
|
374
|
+
params.set("deal_pipeline_id", String(args.deal_pipeline_id));
|
|
375
|
+
const qs = params.toString();
|
|
376
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/deal_stages${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
377
|
+
}
|
|
194
378
|
case "create_opportunity":
|
|
195
379
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("POST", "/platform/deals", args), null, 2) }] };
|
|
380
|
+
case "update_deal": {
|
|
381
|
+
const id = args?.id;
|
|
382
|
+
const body = { ...args };
|
|
383
|
+
delete body.id;
|
|
384
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("PUT", `/platform/deals/${id}`, body), null, 2) }] };
|
|
385
|
+
}
|
|
386
|
+
case "get_deal":
|
|
387
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/deals/${args?.id}`), null, 2) }] };
|
|
388
|
+
case "list_deals": {
|
|
389
|
+
const params = new URLSearchParams();
|
|
390
|
+
if (args?.page)
|
|
391
|
+
params.set("page", String(args.page));
|
|
392
|
+
if (args?.limit)
|
|
393
|
+
params.set("limit", String(args.limit));
|
|
394
|
+
if (args?.deal_stage_id)
|
|
395
|
+
params.set("deal_stage_id", String(args.deal_stage_id));
|
|
396
|
+
if (args?.deal_pipeline_id)
|
|
397
|
+
params.set("deal_pipeline_id", String(args.deal_pipeline_id));
|
|
398
|
+
if (args?.user_id)
|
|
399
|
+
params.set("user_id", String(args.user_id));
|
|
400
|
+
if (args?.win !== undefined)
|
|
401
|
+
params.set("win", String(args.win));
|
|
402
|
+
const qs = params.toString();
|
|
403
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/deals${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
404
|
+
}
|
|
405
|
+
case "list_segmentations": {
|
|
406
|
+
const params = new URLSearchParams();
|
|
407
|
+
if (args?.page)
|
|
408
|
+
params.set("page", String(args.page));
|
|
409
|
+
if (args?.page_size)
|
|
410
|
+
params.set("page_size", String(args.page_size));
|
|
411
|
+
const qs = params.toString();
|
|
412
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/segmentations${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
413
|
+
}
|
|
414
|
+
case "get_segmentation_contacts": {
|
|
415
|
+
const params = new URLSearchParams();
|
|
416
|
+
if (args?.page)
|
|
417
|
+
params.set("page", String(args.page));
|
|
418
|
+
if (args?.page_size)
|
|
419
|
+
params.set("page_size", String(args.page_size));
|
|
420
|
+
const qs = params.toString();
|
|
421
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/segmentations/${args?.segmentation_id}/contacts${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
422
|
+
}
|
|
423
|
+
case "update_lead_scoring": {
|
|
424
|
+
const status = String(args?.status ?? "lead");
|
|
425
|
+
const value = args?.value === undefined ? true : Boolean(args.value);
|
|
426
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("POST", `/platform/contacts/email:${args?.email}/funnels/default`, { lifecycle_stage: status, value }), null, 2) }] };
|
|
427
|
+
}
|
|
428
|
+
case "create_webhook":
|
|
429
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("POST", "/integrations/webhooks", args), null, 2) }] };
|
|
196
430
|
default:
|
|
197
431
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
198
432
|
}
|
|
@@ -219,7 +453,7 @@ async function main() {
|
|
|
219
453
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
220
454
|
t.onclose = () => { if (t.sessionId)
|
|
221
455
|
transports.delete(t.sessionId); };
|
|
222
|
-
const s = new Server({ name: "mcp-rd-station", version: "0.
|
|
456
|
+
const s = new Server({ name: "mcp-rd-station", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
223
457
|
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
224
458
|
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
225
459
|
await s.connect(t);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codespar/mcp-rd-station",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for RD Station — contacts, events, funnels,
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "MCP server for RD Station — contacts, events, funnels, deals, segmentations, lead scoring, webhooks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"bin": {
|
package/server.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.codespar/mcp-rd-station",
|
|
4
|
-
"description": "MCP server for RD Station — contacts, events, funnels,
|
|
4
|
+
"description": "MCP server for RD Station — contacts, events, funnels, deals, segmentations, lead scoring, webhooks",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/codespar/mcp-dev-brasil",
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/communication/rd-station"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.2.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@codespar/mcp-rd-station",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.2.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
package/src/index.ts
CHANGED
|
@@ -3,15 +3,25 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* MCP Server for RD Station — Brazilian CRM and marketing automation.
|
|
5
5
|
*
|
|
6
|
-
* Tools:
|
|
6
|
+
* Tools (18):
|
|
7
7
|
* - create_contact: Create a contact in RD Station
|
|
8
8
|
* - update_contact: Update a contact by UUID
|
|
9
|
+
* - upsert_contact: Upsert contact by email (Marketing API)
|
|
9
10
|
* - get_contact: Get contact details by UUID or email
|
|
10
11
|
* - list_contacts: List contacts with pagination
|
|
12
|
+
* - delete_contact: Delete a contact by UUID
|
|
11
13
|
* - create_event: Create a conversion event
|
|
12
14
|
* - list_funnels: List sales funnels
|
|
13
15
|
* - get_funnel: Get funnel details with stages
|
|
16
|
+
* - list_deal_stages: List deal stages of a pipeline
|
|
14
17
|
* - create_opportunity: Create a sales opportunity
|
|
18
|
+
* - update_deal: Update a deal/opportunity by ID
|
|
19
|
+
* - get_deal: Get a deal/opportunity by ID
|
|
20
|
+
* - list_deals: List deals with filters
|
|
21
|
+
* - list_segmentations: List contact segmentations
|
|
22
|
+
* - get_segmentation_contacts: List contacts of a segmentation
|
|
23
|
+
* - update_lead_scoring: Mark a contact as lead/opportunity (lead scoring)
|
|
24
|
+
* - create_webhook: Subscribe a webhook to RD Station events
|
|
15
25
|
*
|
|
16
26
|
* Environment:
|
|
17
27
|
* RD_STATION_TOKEN — Bearer token from https://app.rdstation.com/
|
|
@@ -42,11 +52,15 @@ async function rdStationRequest(method: string, path: string, body?: unknown): P
|
|
|
42
52
|
const err = await res.text();
|
|
43
53
|
throw new Error(`RD Station API ${res.status}: ${err}`);
|
|
44
54
|
}
|
|
45
|
-
return
|
|
55
|
+
// Some DELETE endpoints return 204 No Content
|
|
56
|
+
if (res.status === 204) return { ok: true };
|
|
57
|
+
const text = await res.text();
|
|
58
|
+
if (!text) return { ok: true };
|
|
59
|
+
try { return JSON.parse(text); } catch { return { raw: text }; }
|
|
46
60
|
}
|
|
47
61
|
|
|
48
62
|
const server = new Server(
|
|
49
|
-
{ name: "mcp-rd-station", version: "0.
|
|
63
|
+
{ name: "mcp-rd-station", version: "0.2.0" },
|
|
50
64
|
{ capabilities: { tools: {} } }
|
|
51
65
|
);
|
|
52
66
|
|
|
@@ -90,6 +104,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
90
104
|
required: ["uuid"],
|
|
91
105
|
},
|
|
92
106
|
},
|
|
107
|
+
{
|
|
108
|
+
name: "upsert_contact",
|
|
109
|
+
description: "Upsert (create or update) a contact identified by email (Marketing API)",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {
|
|
113
|
+
email: { type: "string", description: "Identifier email (used in path)" },
|
|
114
|
+
name: { type: "string", description: "Contact name" },
|
|
115
|
+
job_title: { type: "string", description: "Job title" },
|
|
116
|
+
mobile_phone: { type: "string", description: "Mobile phone" },
|
|
117
|
+
tags: { type: "array", items: { type: "string" }, description: "Tags" },
|
|
118
|
+
cf_custom_fields: { type: "object", description: "Custom fields (key-value)" },
|
|
119
|
+
},
|
|
120
|
+
required: ["email"],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
93
123
|
{
|
|
94
124
|
name: "get_contact",
|
|
95
125
|
description: "Get contact details by UUID or email",
|
|
@@ -113,6 +143,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
113
143
|
},
|
|
114
144
|
},
|
|
115
145
|
},
|
|
146
|
+
{
|
|
147
|
+
name: "delete_contact",
|
|
148
|
+
description: "Delete a contact by UUID",
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {
|
|
152
|
+
uuid: { type: "string", description: "Contact UUID" },
|
|
153
|
+
},
|
|
154
|
+
required: ["uuid"],
|
|
155
|
+
},
|
|
156
|
+
},
|
|
116
157
|
{
|
|
117
158
|
name: "create_event",
|
|
118
159
|
description: "Create a conversion event for a contact",
|
|
@@ -152,6 +193,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
152
193
|
required: ["id"],
|
|
153
194
|
},
|
|
154
195
|
},
|
|
196
|
+
{
|
|
197
|
+
name: "list_deal_stages",
|
|
198
|
+
description: "List deal stages of a pipeline (funnel)",
|
|
199
|
+
inputSchema: {
|
|
200
|
+
type: "object",
|
|
201
|
+
properties: {
|
|
202
|
+
deal_pipeline_id: { type: "string", description: "Pipeline (funnel) ID — optional filter" },
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
155
206
|
{
|
|
156
207
|
name: "create_opportunity",
|
|
157
208
|
description: "Create a sales opportunity in a funnel",
|
|
@@ -167,6 +218,117 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
167
218
|
required: ["deal_stage_id", "name"],
|
|
168
219
|
},
|
|
169
220
|
},
|
|
221
|
+
{
|
|
222
|
+
name: "update_deal",
|
|
223
|
+
description: "Update a deal/opportunity by ID",
|
|
224
|
+
inputSchema: {
|
|
225
|
+
type: "object",
|
|
226
|
+
properties: {
|
|
227
|
+
id: { type: "string", description: "Deal ID" },
|
|
228
|
+
name: { type: "string", description: "Updated name" },
|
|
229
|
+
amount_total: { type: "number", description: "Total amount" },
|
|
230
|
+
deal_stage_id: { type: "string", description: "Move to this stage" },
|
|
231
|
+
win: { type: "boolean", description: "Mark as won" },
|
|
232
|
+
prediction_date: { type: "string", description: "Updated prediction date (YYYY-MM-DD)" },
|
|
233
|
+
},
|
|
234
|
+
required: ["id"],
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: "get_deal",
|
|
239
|
+
description: "Get a deal/opportunity by ID",
|
|
240
|
+
inputSchema: {
|
|
241
|
+
type: "object",
|
|
242
|
+
properties: {
|
|
243
|
+
id: { type: "string", description: "Deal ID" },
|
|
244
|
+
},
|
|
245
|
+
required: ["id"],
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: "list_deals",
|
|
250
|
+
description: "List deals with optional filters and pagination",
|
|
251
|
+
inputSchema: {
|
|
252
|
+
type: "object",
|
|
253
|
+
properties: {
|
|
254
|
+
page: { type: "number", description: "Page number (default 1)" },
|
|
255
|
+
limit: { type: "number", description: "Results per page (default 20)" },
|
|
256
|
+
deal_stage_id: { type: "string", description: "Filter by stage ID" },
|
|
257
|
+
deal_pipeline_id: { type: "string", description: "Filter by pipeline ID" },
|
|
258
|
+
user_id: { type: "string", description: "Filter by owner user ID" },
|
|
259
|
+
win: { type: "string", description: "Filter by win status: true | false | null" },
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
name: "list_segmentations",
|
|
265
|
+
description: "List contact segmentations",
|
|
266
|
+
inputSchema: {
|
|
267
|
+
type: "object",
|
|
268
|
+
properties: {
|
|
269
|
+
page: { type: "number", description: "Page number" },
|
|
270
|
+
page_size: { type: "number", description: "Page size" },
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: "get_segmentation_contacts",
|
|
276
|
+
description: "List contacts inside a given segmentation",
|
|
277
|
+
inputSchema: {
|
|
278
|
+
type: "object",
|
|
279
|
+
properties: {
|
|
280
|
+
segmentation_id: { type: "string", description: "Segmentation ID" },
|
|
281
|
+
page: { type: "number", description: "Page number" },
|
|
282
|
+
page_size: { type: "number", description: "Page size" },
|
|
283
|
+
},
|
|
284
|
+
required: ["segmentation_id"],
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
name: "update_lead_scoring",
|
|
289
|
+
description: "Mark a contact as lead, qualified lead, or opportunity (lead scoring)",
|
|
290
|
+
inputSchema: {
|
|
291
|
+
type: "object",
|
|
292
|
+
properties: {
|
|
293
|
+
email: { type: "string", description: "Contact email" },
|
|
294
|
+
status: {
|
|
295
|
+
type: "string",
|
|
296
|
+
enum: ["opportunity", "qualified_lead", "lead", "client"],
|
|
297
|
+
description: "Lifecycle status to apply",
|
|
298
|
+
},
|
|
299
|
+
value: { type: "boolean", description: "Set/unset the status (default true)" },
|
|
300
|
+
},
|
|
301
|
+
required: ["email", "status"],
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: "create_webhook",
|
|
306
|
+
description: "Subscribe a webhook to RD Station events (WEBHOOK.CONVERTED / WEBHOOK.MARKED_OPPORTUNITY)",
|
|
307
|
+
inputSchema: {
|
|
308
|
+
type: "object",
|
|
309
|
+
properties: {
|
|
310
|
+
entity_type: { type: "string", description: "Entity type (e.g. CONTACT)" },
|
|
311
|
+
event_type: {
|
|
312
|
+
type: "string",
|
|
313
|
+
enum: ["WEBHOOK.CONVERTED", "WEBHOOK.MARKED_OPPORTUNITY"],
|
|
314
|
+
description: "Event type to subscribe to",
|
|
315
|
+
},
|
|
316
|
+
event_identifiers: {
|
|
317
|
+
type: "array",
|
|
318
|
+
items: { type: "string" },
|
|
319
|
+
description: "Optional list of conversion identifiers",
|
|
320
|
+
},
|
|
321
|
+
url: { type: "string", description: "Destination URL" },
|
|
322
|
+
http_method: { type: "string", enum: ["POST", "GET"], description: "HTTP method (default POST)" },
|
|
323
|
+
include_relations: {
|
|
324
|
+
type: "array",
|
|
325
|
+
items: { type: "string" },
|
|
326
|
+
description: "Relations to include (e.g. COMPANY, CONTACT_FUNNEL)",
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
required: ["entity_type", "event_type", "url"],
|
|
330
|
+
},
|
|
331
|
+
},
|
|
170
332
|
],
|
|
171
333
|
}));
|
|
172
334
|
|
|
@@ -183,6 +345,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
183
345
|
delete body.uuid;
|
|
184
346
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("PATCH", `/platform/contacts/${uuid}`, body), null, 2) }] };
|
|
185
347
|
}
|
|
348
|
+
case "upsert_contact": {
|
|
349
|
+
const email = args?.email;
|
|
350
|
+
const body = { ...args } as Record<string, unknown>;
|
|
351
|
+
delete body.email;
|
|
352
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("PATCH", `/platform/contacts/email:${email}`, body), null, 2) }] };
|
|
353
|
+
}
|
|
186
354
|
case "get_contact": {
|
|
187
355
|
if (args?.uuid) {
|
|
188
356
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/contacts/${args.uuid}`), null, 2) }] };
|
|
@@ -196,14 +364,62 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
196
364
|
if (args?.query) params.set("query", String(args.query));
|
|
197
365
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/contacts?${params}`), null, 2) }] };
|
|
198
366
|
}
|
|
367
|
+
case "delete_contact":
|
|
368
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("DELETE", `/platform/contacts/${args?.uuid}`), null, 2) }] };
|
|
199
369
|
case "create_event":
|
|
200
370
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("POST", "/platform/events", args), null, 2) }] };
|
|
201
371
|
case "list_funnels":
|
|
202
372
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", "/platform/deal_pipelines"), null, 2) }] };
|
|
203
373
|
case "get_funnel":
|
|
204
374
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/deal_pipelines/${args?.id}`), null, 2) }] };
|
|
375
|
+
case "list_deal_stages": {
|
|
376
|
+
const params = new URLSearchParams();
|
|
377
|
+
if (args?.deal_pipeline_id) params.set("deal_pipeline_id", String(args.deal_pipeline_id));
|
|
378
|
+
const qs = params.toString();
|
|
379
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/deal_stages${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
380
|
+
}
|
|
205
381
|
case "create_opportunity":
|
|
206
382
|
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("POST", "/platform/deals", args), null, 2) }] };
|
|
383
|
+
case "update_deal": {
|
|
384
|
+
const id = args?.id;
|
|
385
|
+
const body = { ...args } as Record<string, unknown>;
|
|
386
|
+
delete body.id;
|
|
387
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("PUT", `/platform/deals/${id}`, body), null, 2) }] };
|
|
388
|
+
}
|
|
389
|
+
case "get_deal":
|
|
390
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/deals/${args?.id}`), null, 2) }] };
|
|
391
|
+
case "list_deals": {
|
|
392
|
+
const params = new URLSearchParams();
|
|
393
|
+
if (args?.page) params.set("page", String(args.page));
|
|
394
|
+
if (args?.limit) params.set("limit", String(args.limit));
|
|
395
|
+
if (args?.deal_stage_id) params.set("deal_stage_id", String(args.deal_stage_id));
|
|
396
|
+
if (args?.deal_pipeline_id) params.set("deal_pipeline_id", String(args.deal_pipeline_id));
|
|
397
|
+
if (args?.user_id) params.set("user_id", String(args.user_id));
|
|
398
|
+
if (args?.win !== undefined) params.set("win", String(args.win));
|
|
399
|
+
const qs = params.toString();
|
|
400
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/deals${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
401
|
+
}
|
|
402
|
+
case "list_segmentations": {
|
|
403
|
+
const params = new URLSearchParams();
|
|
404
|
+
if (args?.page) params.set("page", String(args.page));
|
|
405
|
+
if (args?.page_size) params.set("page_size", String(args.page_size));
|
|
406
|
+
const qs = params.toString();
|
|
407
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/segmentations${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
408
|
+
}
|
|
409
|
+
case "get_segmentation_contacts": {
|
|
410
|
+
const params = new URLSearchParams();
|
|
411
|
+
if (args?.page) params.set("page", String(args.page));
|
|
412
|
+
if (args?.page_size) params.set("page_size", String(args.page_size));
|
|
413
|
+
const qs = params.toString();
|
|
414
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("GET", `/platform/segmentations/${args?.segmentation_id}/contacts${qs ? `?${qs}` : ""}`), null, 2) }] };
|
|
415
|
+
}
|
|
416
|
+
case "update_lead_scoring": {
|
|
417
|
+
const status = String(args?.status ?? "lead");
|
|
418
|
+
const value = args?.value === undefined ? true : Boolean(args.value);
|
|
419
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("POST", `/platform/contacts/email:${args?.email}/funnels/default`, { lifecycle_stage: status, value }), null, 2) }] };
|
|
420
|
+
}
|
|
421
|
+
case "create_webhook":
|
|
422
|
+
return { content: [{ type: "text", text: JSON.stringify(await rdStationRequest("POST", "/integrations/webhooks", args), null, 2) }] };
|
|
207
423
|
default:
|
|
208
424
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
209
425
|
}
|
|
@@ -226,7 +442,7 @@ async function main() {
|
|
|
226
442
|
if (!sid && isInitializeRequest(req.body)) {
|
|
227
443
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
228
444
|
t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
|
|
229
|
-
const s = new Server({ name: "mcp-rd-station", version: "0.
|
|
445
|
+
const s = new Server({ name: "mcp-rd-station", version: "0.2.0" }, { capabilities: { tools: {} } }); (server as any)._requestHandlers.forEach((v: any, k: any) => (s as any)._requestHandlers.set(k, v)); (server as any)._notificationHandlers?.forEach((v: any, k: any) => (s as any)._notificationHandlers.set(k, v)); await s.connect(t);
|
|
230
446
|
await t.handleRequest(req, res, req.body); return;
|
|
231
447
|
}
|
|
232
448
|
res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });
|