suarify-mcp-server 0.1.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 +73 -0
- package/index.js +395 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Suarify MCP Server
|
|
2
|
+
|
|
3
|
+
This is a Model Context Protocol (MCP) server for Suarify, providing tools for AI agents to interact with Suarify's voice calling and lead management platform.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Voice Calls**: Initiate outbound calls and configure inbound settings.
|
|
8
|
+
- **Lead Management**: CRUD operations for leads and bulk uploads.
|
|
9
|
+
- **Agent Configuration**: Manage AI voice agents and phone configurations.
|
|
10
|
+
- **Modern MCP Design**: Returns `structuredContent` for LLM efficiency and uses standardized naming.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- Node.js 20+
|
|
15
|
+
- [Suarify](https://suarify.my) API Key
|
|
16
|
+
|
|
17
|
+
## Configuration
|
|
18
|
+
|
|
19
|
+
Set the following environment variables:
|
|
20
|
+
|
|
21
|
+
- `SUARIFY_API_KEY`: (Required) Your API key for authentication.
|
|
22
|
+
- `SUARIFY_BASE_URL`: (Optional) Defaults to `https://suarify1.my`.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Local Development
|
|
27
|
+
|
|
28
|
+
1. Install dependencies:
|
|
29
|
+
```bash
|
|
30
|
+
npm install
|
|
31
|
+
```
|
|
32
|
+
2. Run tests:
|
|
33
|
+
```bash
|
|
34
|
+
npm test
|
|
35
|
+
```
|
|
36
|
+
3. Start the server (stdio transport):
|
|
37
|
+
```bash
|
|
38
|
+
export SUARIFY_API_KEY=your_key_here
|
|
39
|
+
node index.js
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Running with Docker
|
|
43
|
+
|
|
44
|
+
1. **Build the image**:
|
|
45
|
+
```bash
|
|
46
|
+
docker build -t suarify-mcp .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
2. **Run the container**:
|
|
50
|
+
```bash
|
|
51
|
+
docker run -e SUARIFY_API_KEY=your_key_here suarify-mcp
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Tools Overview
|
|
55
|
+
|
|
56
|
+
All tools are prefixed with `suarify_` and use `snake_case`.
|
|
57
|
+
|
|
58
|
+
| Tool Name | Description |
|
|
59
|
+
|-----------|-------------|
|
|
60
|
+
| `suarify_initiate_call` | Start a simple voice call. |
|
|
61
|
+
| `suarify_do_outbound_call` | Detailed outbound call with validation. |
|
|
62
|
+
| `suarify_setup_inbound_settings` | Configure AI behavior for inbound calls. |
|
|
63
|
+
| `suarify_list_leads` / `suarify_create_lead` | Manage your lead database. |
|
|
64
|
+
| `suarify_list_user_agents` | Retrieve AI voice agents. |
|
|
65
|
+
| `suarify_get_outbound_call_logs` | Fetch historical call data. |
|
|
66
|
+
|
|
67
|
+
## Development
|
|
68
|
+
|
|
69
|
+
The project uses Jest for unit testing. Tool logic is extracted into a `handlers` object for easy isolation.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm test
|
|
73
|
+
```
|
package/index.js
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { FastMCP } from "fastmcp";
|
|
3
|
+
import axios, { AxiosError } from "axios";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import url from "url";
|
|
6
|
+
|
|
7
|
+
// --- SILENCE STDOUT ---
|
|
8
|
+
// MCP uses stdout for communication. Any other output will break the protocol.
|
|
9
|
+
const originalError = console.error;
|
|
10
|
+
|
|
11
|
+
// Redirect all standard console methods to stderr
|
|
12
|
+
console.log = (...args) => originalError(...args);
|
|
13
|
+
console.info = (...args) => originalError(...args);
|
|
14
|
+
console.warn = (...args) => originalError(...args);
|
|
15
|
+
|
|
16
|
+
// Patch process.stdout.write to ensure ONLY JSON messages (likely from FastMCP) go to stdout
|
|
17
|
+
const stdoutWrite = process.stdout.write;
|
|
18
|
+
process.stdout.write = function (chunk) {
|
|
19
|
+
const str = chunk.toString();
|
|
20
|
+
if (str.trim().startsWith('{') || str.trim().startsWith('[')) {
|
|
21
|
+
return stdoutWrite.apply(process.stdout, arguments);
|
|
22
|
+
}
|
|
23
|
+
return process.stderr.write.apply(process.stderr, arguments);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const BASE_URL = process.env.SUARIFY_BASE_URL || "https://suarify.my";
|
|
27
|
+
const API_KEY = process.env.SUARIFY_API_KEY;
|
|
28
|
+
|
|
29
|
+
if (!API_KEY) {
|
|
30
|
+
originalError("CRITICAL: SUARIFY_API_KEY environment variable is not set. Please sign up at https://suarify.my/register-new-user to get your API key.");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// --- Server Definition ---
|
|
34
|
+
const mcp = new FastMCP("suarify-mcp-server", {
|
|
35
|
+
version: "0.1.0"
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const apiClient = axios.create({
|
|
39
|
+
baseURL: BASE_URL,
|
|
40
|
+
headers: {
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
"x-api-key": API_KEY,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
apiClient.interceptors.response.use(
|
|
47
|
+
(response) => response,
|
|
48
|
+
(error) => {
|
|
49
|
+
if (error.response && (error.response.status === 401 || error.response.status === 403)) {
|
|
50
|
+
const signupMsg = " (API authentication failed. Please ensure your SUARIFY_API_KEY is valid or sign up at https://suarify.my/register-new-user)";
|
|
51
|
+
error.message += signupMsg;
|
|
52
|
+
}
|
|
53
|
+
return Promise.reject(error);
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// --- Helpers ---
|
|
58
|
+
function formatError(error) {
|
|
59
|
+
if (error instanceof AxiosError) {
|
|
60
|
+
const status = error.response?.status;
|
|
61
|
+
const data = error.response?.data;
|
|
62
|
+
let msg = `API Error (${status || 'Network'}): ${error.message}`;
|
|
63
|
+
if (data && typeof data === 'object') {
|
|
64
|
+
msg += ` - ${JSON.stringify(data)}`;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
isError: true,
|
|
68
|
+
content: [{ type: "text", text: msg }],
|
|
69
|
+
structuredContent: { error: msg, status, data }
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const msg = `Unexpected Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
73
|
+
return {
|
|
74
|
+
isError: true,
|
|
75
|
+
content: [{ type: "text", text: msg }],
|
|
76
|
+
structuredContent: { error: msg }
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// --- Handlers ---
|
|
81
|
+
export const handlers = {
|
|
82
|
+
setupInboundSettings: async (args) => {
|
|
83
|
+
try {
|
|
84
|
+
const response = await apiClient.post("/inbound-phone-settings", args);
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: "text", text: `Inbound settings configured for ${args.phonenumber}` }],
|
|
87
|
+
structuredContent: response.data
|
|
88
|
+
};
|
|
89
|
+
} catch (e) { return formatError(e); }
|
|
90
|
+
},
|
|
91
|
+
getInboundSettings: async (args) => {
|
|
92
|
+
try {
|
|
93
|
+
const response = await apiClient.get("/inbound-phone-settings", { params: args });
|
|
94
|
+
return {
|
|
95
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
96
|
+
structuredContent: response.data
|
|
97
|
+
};
|
|
98
|
+
} catch (e) { return formatError(e); }
|
|
99
|
+
},
|
|
100
|
+
setupPhoneConfiguration: async (args) => {
|
|
101
|
+
try {
|
|
102
|
+
const response = await apiClient.post("/api/phone-configuration", args);
|
|
103
|
+
return {
|
|
104
|
+
content: [{ type: "text", text: `Phone configuration upserted for token: ${args.tokenid}` }],
|
|
105
|
+
structuredContent: response.data
|
|
106
|
+
};
|
|
107
|
+
} catch (e) { return formatError(e); }
|
|
108
|
+
},
|
|
109
|
+
getPhoneConfiguration: async (args) => {
|
|
110
|
+
try {
|
|
111
|
+
const response = await apiClient.get("/api/phone-configuration", { params: args });
|
|
112
|
+
return {
|
|
113
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
114
|
+
structuredContent: response.data
|
|
115
|
+
};
|
|
116
|
+
} catch (e) { return formatError(e); }
|
|
117
|
+
},
|
|
118
|
+
initiateCall: async (args) => {
|
|
119
|
+
try {
|
|
120
|
+
const response = await apiClient.post("/api/call", args);
|
|
121
|
+
return {
|
|
122
|
+
content: [{ type: "text", text: `Call initiated: ${JSON.stringify(response.data)}` }],
|
|
123
|
+
structuredContent: response.data
|
|
124
|
+
};
|
|
125
|
+
} catch (e) { return formatError(e); }
|
|
126
|
+
},
|
|
127
|
+
doOutboundCall: async (args) => {
|
|
128
|
+
try {
|
|
129
|
+
const response = await apiClient.post("/do-outbound-phone-call", args);
|
|
130
|
+
return {
|
|
131
|
+
content: [{ type: "text", text: `Outbound call executed: ${JSON.stringify(response.data)}` }],
|
|
132
|
+
structuredContent: response.data
|
|
133
|
+
};
|
|
134
|
+
} catch (e) { return formatError(e); }
|
|
135
|
+
},
|
|
136
|
+
getOutboundCallLogs: async (args) => {
|
|
137
|
+
try {
|
|
138
|
+
const response = await apiClient.get("/api/outbound-call-logs", { params: args });
|
|
139
|
+
return {
|
|
140
|
+
content: [{ type: "text", text: `Retrieved ${response.data?.length || 0} outbound logs.` }],
|
|
141
|
+
structuredContent: response.data
|
|
142
|
+
};
|
|
143
|
+
} catch (e) { return formatError(e); }
|
|
144
|
+
},
|
|
145
|
+
getInboundCallLogs: async (args) => {
|
|
146
|
+
try {
|
|
147
|
+
const response = await apiClient.get("/api/inbound-call-logs", { params: args });
|
|
148
|
+
return {
|
|
149
|
+
content: [{ type: "text", text: `Retrieved ${response.data?.length || 0} inbound logs.` }],
|
|
150
|
+
structuredContent: response.data
|
|
151
|
+
};
|
|
152
|
+
} catch (e) { return formatError(e); }
|
|
153
|
+
},
|
|
154
|
+
listUserAgents: async (args) => {
|
|
155
|
+
try {
|
|
156
|
+
const response = await apiClient.get("/api/user-agents", { params: args });
|
|
157
|
+
return {
|
|
158
|
+
content: [{ type: "text", text: `Retrieved ${response.data?.length || 0} user agents.` }],
|
|
159
|
+
structuredContent: response.data
|
|
160
|
+
};
|
|
161
|
+
} catch (e) { return formatError(e); }
|
|
162
|
+
},
|
|
163
|
+
getUserAgent: async (args) => {
|
|
164
|
+
try {
|
|
165
|
+
const { id, ...params } = args;
|
|
166
|
+
const response = await apiClient.get(`/api/user-agents/${id}`, { params });
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
169
|
+
structuredContent: response.data
|
|
170
|
+
};
|
|
171
|
+
} catch (e) { return formatError(e); }
|
|
172
|
+
},
|
|
173
|
+
deleteUserAgent: async (args) => {
|
|
174
|
+
try {
|
|
175
|
+
const { id, ...params } = args;
|
|
176
|
+
const response = await apiClient.delete(`/api/user-agents/${id}`, { params });
|
|
177
|
+
return {
|
|
178
|
+
content: [{ type: "text", text: `User agent ${id} deleted.` }],
|
|
179
|
+
structuredContent: response.data
|
|
180
|
+
};
|
|
181
|
+
} catch (e) { return formatError(e); }
|
|
182
|
+
},
|
|
183
|
+
createLead: async (args) => {
|
|
184
|
+
try {
|
|
185
|
+
const response = await apiClient.post("/api/user-leads", args);
|
|
186
|
+
return {
|
|
187
|
+
content: [{ type: "text", text: `Lead created for ${args.receipient_name}` }],
|
|
188
|
+
structuredContent: response.data
|
|
189
|
+
};
|
|
190
|
+
} catch (e) { return formatError(e); }
|
|
191
|
+
},
|
|
192
|
+
bulkUploadLeads: async (args) => {
|
|
193
|
+
try {
|
|
194
|
+
const response = await apiClient.post("/api/user-leads/bulk", args);
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: "text", text: `Bulk upload processed: ${JSON.stringify(response.data)}` }],
|
|
197
|
+
structuredContent: response.data
|
|
198
|
+
};
|
|
199
|
+
} catch (e) { return formatError(e); }
|
|
200
|
+
},
|
|
201
|
+
listLeads: async (args) => {
|
|
202
|
+
try {
|
|
203
|
+
const response = await apiClient.get("/api/user-leads", { params: args });
|
|
204
|
+
return {
|
|
205
|
+
content: [{ type: "text", text: `Retrieved ${response.data?.length || 0} leads.` }],
|
|
206
|
+
structuredContent: response.data
|
|
207
|
+
};
|
|
208
|
+
} catch (e) { return formatError(e); }
|
|
209
|
+
},
|
|
210
|
+
getLead: async (args) => {
|
|
211
|
+
try {
|
|
212
|
+
const response = await apiClient.get(`/api/user-leads/${args.id}`);
|
|
213
|
+
return {
|
|
214
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
215
|
+
structuredContent: response.data
|
|
216
|
+
};
|
|
217
|
+
} catch (e) { return formatError(e); }
|
|
218
|
+
},
|
|
219
|
+
updateLead: async (args) => {
|
|
220
|
+
try {
|
|
221
|
+
const { id, ...data } = args;
|
|
222
|
+
const response = await apiClient.patch(`/api/user-leads/${id}`, data);
|
|
223
|
+
return {
|
|
224
|
+
content: [{ type: "text", text: `Lead ${id} updated.` }],
|
|
225
|
+
structuredContent: response.data
|
|
226
|
+
};
|
|
227
|
+
} catch (e) { return formatError(e); }
|
|
228
|
+
},
|
|
229
|
+
deleteLead: async (args) => {
|
|
230
|
+
try {
|
|
231
|
+
const response = await apiClient.delete(`/api/user-leads/${args.id}`);
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: "text", text: `Lead ${args.id} deleted.` }],
|
|
234
|
+
structuredContent: response.data
|
|
235
|
+
};
|
|
236
|
+
} catch (e) { return formatError(e); }
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// --- Inbound Phone Settings ---
|
|
241
|
+
|
|
242
|
+
mcp.addTool({
|
|
243
|
+
name: "suarify_setup_inbound_settings",
|
|
244
|
+
description: "Configure the AI agent behavior, voice, and prompts for inbound calls on a specific phone number. This setup is stored as 'inbound-<phonenumber>'. Requires Suarify API Key.",
|
|
245
|
+
parameters: z.object({
|
|
246
|
+
phonenumber: z.string().describe("The phone number to configure (e.g., 015487666768)"),
|
|
247
|
+
params: z.string().describe("JSON string containing main_voice, start_time, owner_email, start_message, system_prompt, planned_call_id, transfer_message"),
|
|
248
|
+
openai_key: z.string().optional().describe("Optional OpenAI API key override"),
|
|
249
|
+
}),
|
|
250
|
+
execute: handlers.setupInboundSettings,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
mcp.addTool({
|
|
254
|
+
name: "suarify_get_inbound_settings",
|
|
255
|
+
description: "Get the current inbound call settings for the authenticated user's default phone number.",
|
|
256
|
+
parameters: z.object({
|
|
257
|
+
owner_email: z.string().optional().describe("Owner email (required if using system API key)"),
|
|
258
|
+
}),
|
|
259
|
+
execute: handlers.getInboundSettings,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// --- Generic Phone Configuration ---
|
|
263
|
+
|
|
264
|
+
mcp.addTool({
|
|
265
|
+
name: "suarify_setup_phone_configuration",
|
|
266
|
+
description: "Upsert general phone configuration in the call_params table. Use this for custom tokenized configurations.",
|
|
267
|
+
parameters: z.object({
|
|
268
|
+
tokenid: z.string().describe("Unique token identifier for this configuration"),
|
|
269
|
+
params: z.any().describe("Configuration parameters object or JSON string"),
|
|
270
|
+
openai_key: z.string().optional().describe("Optional OpenAI API key override"),
|
|
271
|
+
}),
|
|
272
|
+
execute: handlers.setupPhoneConfiguration,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
mcp.addTool({
|
|
276
|
+
name: "suarify_get_phone_configuration",
|
|
277
|
+
description: "Retrieve a specific phone configuration by tokenid, or list all configurations.",
|
|
278
|
+
parameters: z.object({
|
|
279
|
+
tokenid: z.string().optional().describe("Specific token ID to retrieve. If omitted, returns all (max 100)."),
|
|
280
|
+
}),
|
|
281
|
+
execute: handlers.getPhoneConfiguration,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// --- Outbound Calls ---
|
|
285
|
+
|
|
286
|
+
mcp.addTool({
|
|
287
|
+
name: "suarify_initiate_call",
|
|
288
|
+
description: "Initiate an outbound AI voice call using simple or enhanced parameters.",
|
|
289
|
+
parameters: z.object({
|
|
290
|
+
phone_number: z.string().optional().describe("Target phone number (simple format)"),
|
|
291
|
+
system_prompt: z.string().optional().describe("System prompt for AI (simple format)"),
|
|
292
|
+
start_message: z.string().optional().describe("Initial greeting message (simple format)"),
|
|
293
|
+
voice: z.string().optional().describe("Voice selection (simple format)"),
|
|
294
|
+
receipient_phone: z.string().optional().describe("Target phone number (enhanced format)"),
|
|
295
|
+
agent_prompt: z.string().optional().describe("AI instructions (enhanced format)"),
|
|
296
|
+
}),
|
|
297
|
+
execute: handlers.initiateCall,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
mcp.addTool({
|
|
301
|
+
name: "suarify_do_outbound_call",
|
|
302
|
+
description: "Make an outbound phone call with full validation (balance check, user profile). Required for production calls.",
|
|
303
|
+
parameters: z.object({
|
|
304
|
+
owner_email: z.string().describe("Owner's email address"),
|
|
305
|
+
password: z.string().describe("Must be 'LIVE' to execute the call"),
|
|
306
|
+
receipient_phone: z.string().describe("Target phone number (international format)"),
|
|
307
|
+
agent_voice: z.string().optional().describe("Voice selection"),
|
|
308
|
+
agent_prompt: z.string().optional().describe("System prompt"),
|
|
309
|
+
agent_start_message: z.string().optional().describe("First message agent speaks"),
|
|
310
|
+
}),
|
|
311
|
+
execute: handlers.doOutboundCall,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// --- Call Logs ---
|
|
315
|
+
|
|
316
|
+
mcp.addTool({
|
|
317
|
+
name: "suarify_get_outbound_call_logs",
|
|
318
|
+
description: "Get all outbound call logs with optional filters and record limit.",
|
|
319
|
+
parameters: z.object({
|
|
320
|
+
current_phone_number: z.string().describe("Filter by the outbound phone number used"),
|
|
321
|
+
totalNumberOfRecords: z.number().optional().default(50).describe("Number of records to return"),
|
|
322
|
+
}),
|
|
323
|
+
execute: handlers.getOutboundCallLogs,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
mcp.addTool({
|
|
327
|
+
name: "suarify_get_inbound_call_logs",
|
|
328
|
+
description: "Get all inbound call logs with optional filters and record limit.",
|
|
329
|
+
parameters: z.object({
|
|
330
|
+
current_phone_number: z.string().describe("Filter by the inbound phone number"),
|
|
331
|
+
totalNumberOfRecords: z.number().optional().default(50).describe("Number of records to return"),
|
|
332
|
+
}),
|
|
333
|
+
execute: handlers.getInboundCallLogs,
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// --- User Agents ---
|
|
337
|
+
|
|
338
|
+
mcp.addTool({
|
|
339
|
+
name: "suarify_list_user_agents",
|
|
340
|
+
description: "Retrieve all AI agents for the authenticated user.",
|
|
341
|
+
parameters: z.object({
|
|
342
|
+
owner_email: z.string().optional().describe("Owner email filter"),
|
|
343
|
+
limit: z.number().optional().default(100),
|
|
344
|
+
}),
|
|
345
|
+
execute: handlers.listUserAgents,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
mcp.addTool({
|
|
349
|
+
name: "suarify_get_user_agent",
|
|
350
|
+
description: "Retrieve a single AI agent by ID.",
|
|
351
|
+
parameters: z.object({
|
|
352
|
+
id: z.string().describe("The ID of the user agent"),
|
|
353
|
+
}),
|
|
354
|
+
execute: handlers.getUserAgent,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// --- Leads Management ---
|
|
358
|
+
|
|
359
|
+
mcp.addTool({
|
|
360
|
+
name: "suarify_create_lead",
|
|
361
|
+
description: "Create a single lead record with detailed recipient information.",
|
|
362
|
+
parameters: z.object({
|
|
363
|
+
owner_email: z.string().describe("Owner's email address"),
|
|
364
|
+
receipient_name: z.string().describe("Name of the recipient"),
|
|
365
|
+
receipient_phone: z.string().describe("Phone number of the recipient"),
|
|
366
|
+
}),
|
|
367
|
+
execute: handlers.createLead,
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
mcp.addTool({
|
|
371
|
+
name: "suarify_list_leads",
|
|
372
|
+
description: "Retrieve a list of leads with optional filtering and pagination.",
|
|
373
|
+
parameters: z.object({
|
|
374
|
+
owner_email: z.string().optional().describe("Filter by owner email"),
|
|
375
|
+
limit: z.number().optional().default(100),
|
|
376
|
+
}),
|
|
377
|
+
execute: handlers.listLeads,
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
mcp.addTool({
|
|
381
|
+
name: "suarify_delete_lead",
|
|
382
|
+
description: "Remove a lead record from the database.",
|
|
383
|
+
parameters: z.object({
|
|
384
|
+
id: z.string().describe("The ID of the lead record"),
|
|
385
|
+
}),
|
|
386
|
+
execute: handlers.deleteLead,
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
export { mcp, apiClient };
|
|
390
|
+
|
|
391
|
+
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
|
392
|
+
mcp.start({
|
|
393
|
+
transportType: "stdio",
|
|
394
|
+
});
|
|
395
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "suarify-mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Suarify MCP Server for voice calling and lead management",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"suarify-mcp-server": "index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
12
|
+
"start": "node index.js"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/suarifymy/mcp-suarify-server.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"model-context-protocol",
|
|
21
|
+
"suarify",
|
|
22
|
+
"voice-ai",
|
|
23
|
+
"telephony"
|
|
24
|
+
],
|
|
25
|
+
"author": "Suarify",
|
|
26
|
+
"license": "ISC",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"axios": "^1.13.5",
|
|
29
|
+
"dotenv": "^17.2.4",
|
|
30
|
+
"fastmcp": "^3.32.0",
|
|
31
|
+
"zod": "^4.3.6"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"axios-mock-adapter": "^2.1.0",
|
|
35
|
+
"jest": "^30.2.0"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"index.js",
|
|
39
|
+
"README.md",
|
|
40
|
+
"package.json"
|
|
41
|
+
]
|
|
42
|
+
}
|