n8n-nodes-clientify 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/LICENSE +21 -0
- package/README.md +378 -0
- package/dist/credentials/ClientifyMcpApi.credentials.d.ts +7 -0
- package/dist/credentials/ClientifyMcpApi.credentials.js +25 -0
- package/dist/nodes/ClientifyMcp/ClientifyMcpDynamic.node.d.ts +5 -0
- package/dist/nodes/ClientifyMcp/ClientifyMcpDynamic.node.js +211 -0
- package/dist/nodes/ClientifyMcp/ClientifyMcpFields.d.ts +15 -0
- package/dist/nodes/ClientifyMcp/ClientifyMcpFields.js +894 -0
- package/dist/nodes/ClientifyMcp/clientify.png +0 -0
- package/dist/nodes/ClientifyMcp/clientify.svg +5 -0
- package/dist/package.json +58 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Clientify
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# n8n-nodes-clientify
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/n8n-nodes-clientify)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
N8N community node for integrating with Clientify CRM via Model Context Protocol (MCP). This node provides seamless access to all Clientify CRM operations including contacts, companies, deals, tasks, and more.
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Prerequisites](#prerequisites)
|
|
12
|
+
- [Configuration](#configuration)
|
|
13
|
+
- [Available Operations](#available-operations)
|
|
14
|
+
- [Usage Examples](#usage-examples)
|
|
15
|
+
- [Troubleshooting](#troubleshooting)
|
|
16
|
+
- [Support](#support)
|
|
17
|
+
- [License](#license)
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
### In n8n (GUI)
|
|
22
|
+
|
|
23
|
+
1. Go to **Settings** → **Community Nodes**
|
|
24
|
+
2. Click **Install**
|
|
25
|
+
3. Enter: `n8n-nodes-clientify`
|
|
26
|
+
4. Click **I understand the risks** and then **Install**
|
|
27
|
+
5. **Restart n8n** (important - the node won't appear until you restart)
|
|
28
|
+
|
|
29
|
+
### Via npm (Command Line)
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cd ~/.n8n/nodes
|
|
33
|
+
npm install n8n-nodes-clientify
|
|
34
|
+
# Restart n8n
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Docker
|
|
38
|
+
|
|
39
|
+
If running n8n in Docker, ensure you persist the nodes directory:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
volumes:
|
|
43
|
+
- n8n_data:/home/node/.n8n
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Or enable auto-reinstall on restart:
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
environment:
|
|
50
|
+
- N8N_REINSTALL_MISSING_PACKAGES=true
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Prerequisites
|
|
54
|
+
|
|
55
|
+
- **n8n version:** 0.180.0 or higher
|
|
56
|
+
- **Node.js version:** 20.15 or higher
|
|
57
|
+
- **Clientify Account:** Active Clientify CRM account with API access
|
|
58
|
+
- **Authentication Token:** JWT token from Clientify MCP server
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
### Step 1: Obtain Your Clientify MCP Token
|
|
63
|
+
|
|
64
|
+
1. Log into your Clientify account
|
|
65
|
+
2. Navigate to API settings or contact your Clientify administrator
|
|
66
|
+
3. Generate or copy your MCP authentication token (JWT format)
|
|
67
|
+
|
|
68
|
+
### Step 2: Add Credentials in n8n
|
|
69
|
+
|
|
70
|
+
1. In n8n, go to **Credentials** → **New**
|
|
71
|
+
2. Search for **"Clientify MCP API"**
|
|
72
|
+
3. Enter your **Authentication Token** (JWT)
|
|
73
|
+
4. Click **Save**
|
|
74
|
+
|
|
75
|
+
### Step 3: Use the Node
|
|
76
|
+
|
|
77
|
+
1. In your workflow, click **+** to add a node
|
|
78
|
+
2. Search for **"Clientify"**
|
|
79
|
+
3. Select the **Clientify** node
|
|
80
|
+
4. Choose your saved credentials
|
|
81
|
+
5. Select an operation from the dropdown
|
|
82
|
+
6. Configure the parameters for your operation
|
|
83
|
+
|
|
84
|
+
## Available Operations
|
|
85
|
+
|
|
86
|
+
This node dynamically supports **26 operations** from the Clientify MCP API:
|
|
87
|
+
|
|
88
|
+
### 📊 Companies
|
|
89
|
+
- **List Companies** - Retrieve all companies with pagination
|
|
90
|
+
- **Get Company** - Get detailed information for a specific company by ID
|
|
91
|
+
- **Create Company** - Create a new company in the CRM
|
|
92
|
+
- **Update Company** - Update an existing company
|
|
93
|
+
- **Delete Company** - Remove a company from the CRM
|
|
94
|
+
- **Search Companies** - Search for companies by criteria
|
|
95
|
+
|
|
96
|
+
### 👥 Contacts
|
|
97
|
+
- **List Contacts** - Retrieve all contacts or search for specific contacts
|
|
98
|
+
- **Get Contact** - Get detailed information for a specific contact by ID
|
|
99
|
+
- **Create Contact** - Create a new contact in the CRM
|
|
100
|
+
- **Update Contact** - Update an existing contact
|
|
101
|
+
- **Delete Contact** - Remove a contact from the CRM
|
|
102
|
+
|
|
103
|
+
### 💼 Deals
|
|
104
|
+
- **List Deals** - Retrieve all deals with filtering and pagination
|
|
105
|
+
- **List Deals By Stage** - Filter deals by pipeline stage
|
|
106
|
+
- **Get Deal** - Get detailed information for a specific deal by ID
|
|
107
|
+
- **Create Deal** - Create a new deal/opportunity
|
|
108
|
+
- **Update Deal** - Update an existing deal
|
|
109
|
+
- **Delete Deal** - Remove a deal from the CRM
|
|
110
|
+
- **Mark Deal Won** - Mark a deal as won/closed successfully
|
|
111
|
+
- **Mark Deal Lost** - Mark a deal as lost/closed unsuccessfully
|
|
112
|
+
|
|
113
|
+
### ✅ Tasks
|
|
114
|
+
- **List Tasks** - Retrieve all tasks with pagination
|
|
115
|
+
- **Get Task** - Get detailed information for a specific task by ID
|
|
116
|
+
- **Create Task** - Create a new task/activity
|
|
117
|
+
- **List Activity Types** - Get available activity types for tasks
|
|
118
|
+
|
|
119
|
+
### ⚙️ Configuration & Utilities
|
|
120
|
+
- **List Pipelines** - Retrieve all available sales pipelines
|
|
121
|
+
- **Get Current User** - Get information about the authenticated user
|
|
122
|
+
- **Get Current Time** - Get current date/time with timezone awareness
|
|
123
|
+
|
|
124
|
+
## Usage Examples
|
|
125
|
+
|
|
126
|
+
### Example 1: List All Contacts
|
|
127
|
+
|
|
128
|
+
Retrieve a paginated list of all contacts in your CRM.
|
|
129
|
+
|
|
130
|
+
**Configuration:**
|
|
131
|
+
- **Operation:** `List Contacts`
|
|
132
|
+
- **Parameters:** Leave empty for all contacts, or add filters
|
|
133
|
+
|
|
134
|
+
**Output:** Array of contact objects with names, emails, phone numbers, and IDs.
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
[
|
|
138
|
+
{
|
|
139
|
+
"id": 123,
|
|
140
|
+
"first_name": "John",
|
|
141
|
+
"last_name": "Doe",
|
|
142
|
+
"email": "john.doe@example.com",
|
|
143
|
+
"phone": "+1234567890",
|
|
144
|
+
"company_id": 456
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### Example 2: Create a New Company
|
|
152
|
+
|
|
153
|
+
Add a new company to your Clientify CRM.
|
|
154
|
+
|
|
155
|
+
**Configuration:**
|
|
156
|
+
- **Operation:** `Create Company`
|
|
157
|
+
- **Parameters:**
|
|
158
|
+
- **name** (required): `Acme Corporation`
|
|
159
|
+
- **domain**: `acme.com`
|
|
160
|
+
- **industry**: `Technology`
|
|
161
|
+
- **description**: `Leading software provider`
|
|
162
|
+
|
|
163
|
+
**Output:** Created company object with assigned ID.
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"id": 789,
|
|
168
|
+
"name": "Acme Corporation",
|
|
169
|
+
"domain": "acme.com",
|
|
170
|
+
"industry": "Technology",
|
|
171
|
+
"created_at": "2025-10-01T12:00:00Z"
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
### Example 3: Update a Deal and Mark as Won
|
|
178
|
+
|
|
179
|
+
Update deal details and mark it as successfully closed.
|
|
180
|
+
|
|
181
|
+
**Workflow:**
|
|
182
|
+
1. **First Node:** Get the deal you want to update
|
|
183
|
+
- **Operation:** `Get Deal`
|
|
184
|
+
- **deal_id:** `{{$json.deal_id}}`
|
|
185
|
+
|
|
186
|
+
2. **Second Node:** Update the deal
|
|
187
|
+
- **Operation:** `Update Deal`
|
|
188
|
+
- **deal_id:** `{{$json.id}}`
|
|
189
|
+
- **value:** `50000`
|
|
190
|
+
- **notes:** `Final negotiations completed`
|
|
191
|
+
|
|
192
|
+
3. **Third Node:** Mark as won
|
|
193
|
+
- **Operation:** `Mark Deal Won`
|
|
194
|
+
- **deal_id:** `{{$json.id}}`
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### Example 4: Search Companies and Create Contacts
|
|
199
|
+
|
|
200
|
+
Find a company by name and create a new contact associated with it.
|
|
201
|
+
|
|
202
|
+
**Workflow:**
|
|
203
|
+
1. **Search Companies:**
|
|
204
|
+
- **Operation:** `Search Companies`
|
|
205
|
+
- **query:** `Acme`
|
|
206
|
+
|
|
207
|
+
2. **Create Contact:**
|
|
208
|
+
- **Operation:** `Create Contact`
|
|
209
|
+
- **first_name:** `Jane`
|
|
210
|
+
- **last_name:** `Smith`
|
|
211
|
+
- **email:** `jane.smith@acme.com`
|
|
212
|
+
- **company_id:** `{{$json.id}}` (from previous node)
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
### Example 5: List All Deals in a Specific Pipeline Stage
|
|
217
|
+
|
|
218
|
+
Retrieve deals that are in the "Negotiation" stage.
|
|
219
|
+
|
|
220
|
+
**Configuration:**
|
|
221
|
+
- **Operation:** `List Deals By Stage`
|
|
222
|
+
- **Parameters:**
|
|
223
|
+
- **stage:** `Negotiation`
|
|
224
|
+
|
|
225
|
+
**Use Case:** Create automated alerts when deals reach certain stages, or generate reports on pipeline health.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Troubleshooting
|
|
230
|
+
|
|
231
|
+
### Node Doesn't Appear After Installation
|
|
232
|
+
|
|
233
|
+
**Problem:** The Clientify node doesn't show up in the node panel after installing.
|
|
234
|
+
|
|
235
|
+
**Solution:**
|
|
236
|
+
1. **Restart n8n completely** (required for community nodes)
|
|
237
|
+
2. Clear your browser cache (Ctrl+Shift+Delete or Cmd+Shift+Delete)
|
|
238
|
+
3. Search for "Clientify" (not the package name) in the node search
|
|
239
|
+
4. Verify installation in **Settings → Community Nodes**
|
|
240
|
+
|
|
241
|
+
### Authentication Errors
|
|
242
|
+
|
|
243
|
+
**Problem:** "Authentication failed" or 401/403 errors.
|
|
244
|
+
|
|
245
|
+
**Solution:**
|
|
246
|
+
1. Verify your JWT token is valid and not expired
|
|
247
|
+
2. Check that the token has the correct permissions in Clientify
|
|
248
|
+
3. Ensure you're using the Clientify MCP token (not a regular API key)
|
|
249
|
+
4. Try creating a new credential with a fresh token
|
|
250
|
+
|
|
251
|
+
### "Missing required parameter" Errors
|
|
252
|
+
|
|
253
|
+
**Problem:** Operation fails with missing parameter errors.
|
|
254
|
+
|
|
255
|
+
**Solution:**
|
|
256
|
+
1. Check which fields are marked as **required** (indicated by red asterisk)
|
|
257
|
+
2. Some operations require specific IDs (contact_id, company_id, etc.)
|
|
258
|
+
3. Use `Get` operations first to retrieve IDs, then pass them to `Update` or `Delete` operations
|
|
259
|
+
4. Review the parameter descriptions for expected format
|
|
260
|
+
|
|
261
|
+
### Operations Return Empty Results
|
|
262
|
+
|
|
263
|
+
**Problem:** List operations return no results even though data exists.
|
|
264
|
+
|
|
265
|
+
**Solution:**
|
|
266
|
+
1. Check pagination parameters (page, page_size)
|
|
267
|
+
2. Verify your token has permission to access the requested resources
|
|
268
|
+
3. Try without filters first to confirm data access
|
|
269
|
+
4. Check the Clientify web UI to confirm data exists
|
|
270
|
+
|
|
271
|
+
### Node Execution Fails in Docker
|
|
272
|
+
|
|
273
|
+
**Problem:** Node fails with "package not found" errors in Docker environments.
|
|
274
|
+
|
|
275
|
+
**Solution:**
|
|
276
|
+
1. Ensure community nodes directory is persisted in volumes:
|
|
277
|
+
```yaml
|
|
278
|
+
volumes:
|
|
279
|
+
- n8n_data:/home/node/.n8n
|
|
280
|
+
```
|
|
281
|
+
2. Or enable auto-reinstall:
|
|
282
|
+
```yaml
|
|
283
|
+
environment:
|
|
284
|
+
- N8N_REINSTALL_MISSING_PACKAGES=true
|
|
285
|
+
```
|
|
286
|
+
3. Restart the container after installing the node
|
|
287
|
+
|
|
288
|
+
### Rate Limiting Errors
|
|
289
|
+
|
|
290
|
+
**Problem:** "Too many requests" or 429 errors.
|
|
291
|
+
|
|
292
|
+
**Solution:**
|
|
293
|
+
1. Add delay between operations using n8n's **Wait** node
|
|
294
|
+
2. Reduce the frequency of scheduled workflows
|
|
295
|
+
3. Batch operations where possible
|
|
296
|
+
4. Contact Clientify support to increase rate limits if needed
|
|
297
|
+
|
|
298
|
+
## Parameter Reference
|
|
299
|
+
|
|
300
|
+
### Common Parameters
|
|
301
|
+
|
|
302
|
+
Most operations support these common parameters:
|
|
303
|
+
|
|
304
|
+
- **page** (number): Page number for pagination (default: 1)
|
|
305
|
+
- **page_size** (number): Number of results per page (default: 20, max: 100)
|
|
306
|
+
- **sort_by** (string): Field to sort by (e.g., "created_at", "name")
|
|
307
|
+
- **sort_order** (string): Sort direction ("asc" or "desc")
|
|
308
|
+
|
|
309
|
+
### ID Parameters
|
|
310
|
+
|
|
311
|
+
Operations that work with specific records require an ID parameter:
|
|
312
|
+
|
|
313
|
+
- **company_id** (number): Unique identifier for a company
|
|
314
|
+
- **contact_id** (number): Unique identifier for a contact
|
|
315
|
+
- **deal_id** (number): Unique identifier for a deal
|
|
316
|
+
- **task_id** (number): Unique identifier for a task
|
|
317
|
+
|
|
318
|
+
## Advanced Usage
|
|
319
|
+
|
|
320
|
+
### Combining Multiple Operations
|
|
321
|
+
|
|
322
|
+
Create powerful workflows by chaining operations:
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
Trigger → Search Companies → Create Contact → Create Deal → Create Task
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Error Handling
|
|
329
|
+
|
|
330
|
+
Use n8n's built-in error handling:
|
|
331
|
+
|
|
332
|
+
1. Enable **"Continue On Fail"** in node settings
|
|
333
|
+
2. Add an **IF** node to check for errors
|
|
334
|
+
3. Route errors to notification nodes (Email, Slack, etc.)
|
|
335
|
+
|
|
336
|
+
### Data Transformation
|
|
337
|
+
|
|
338
|
+
Use n8n's **Code** or **Function** nodes to transform data between operations:
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
List Contacts → Code Node (transform) → External API
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Development & Contribution
|
|
345
|
+
|
|
346
|
+
This is a community-maintained node. Contributions are welcome!
|
|
347
|
+
|
|
348
|
+
**Repository:** https://github.com/contacteitor/clientify_ai
|
|
349
|
+
|
|
350
|
+
**Report Issues:** https://github.com/contacteitor/clientify_ai/issues
|
|
351
|
+
|
|
352
|
+
**Documentation:** See the `/n8n` directory in the repository
|
|
353
|
+
|
|
354
|
+
## Support
|
|
355
|
+
|
|
356
|
+
- **Issues & Bugs:** https://github.com/contacteitor/clientify_ai/issues
|
|
357
|
+
- **Email:** sebastianonearth@gmail.com
|
|
358
|
+
- **Clientify Documentation:** https://mcp.clientify.com/docs
|
|
359
|
+
|
|
360
|
+
## Version History
|
|
361
|
+
|
|
362
|
+
- **v0.1.0** (2025-10-01): Initial release
|
|
363
|
+
- 26 MCP operations supported
|
|
364
|
+
- Dynamic field generation from MCP API
|
|
365
|
+
- Full CRUD operations for contacts, companies, deals, tasks
|
|
366
|
+
- Pipeline and user management
|
|
367
|
+
|
|
368
|
+
## License
|
|
369
|
+
|
|
370
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
371
|
+
|
|
372
|
+
Copyright (c) 2025 Clientify
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
**Made with ❤️ for the n8n community**
|
|
377
|
+
|
|
378
|
+
*This is an unofficial community node. Clientify and n8n are trademarks of their respective owners.*
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClientifyMcpApi = void 0;
|
|
4
|
+
class ClientifyMcpApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'clientifyMcpApi';
|
|
7
|
+
this.displayName = 'Clientify MCP API';
|
|
8
|
+
this.documentationUrl = 'https://mcp.clientify.com/docs';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'Authentication Token',
|
|
12
|
+
name: 'authToken',
|
|
13
|
+
type: 'string',
|
|
14
|
+
typeOptions: {
|
|
15
|
+
password: true,
|
|
16
|
+
},
|
|
17
|
+
default: '',
|
|
18
|
+
required: true,
|
|
19
|
+
placeholder: 'Enter your JWT token',
|
|
20
|
+
description: 'The x-end-user-token for authentication with Clientify MCP server',
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.ClientifyMcpApi = ClientifyMcpApi;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class ClientifyMcpDynamic implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClientifyMcpDynamic = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const ClientifyMcpFields_1 = require("./ClientifyMcpFields");
|
|
6
|
+
class ClientifyMcpDynamic {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.description = {
|
|
9
|
+
displayName: 'Clientify',
|
|
10
|
+
name: 'clientifyMcpDynamic',
|
|
11
|
+
icon: 'file:clientify.svg',
|
|
12
|
+
group: ['transform'],
|
|
13
|
+
version: 1,
|
|
14
|
+
subtitle: '={{$parameter["tool"]}}',
|
|
15
|
+
description: 'Connect to Clientify CRM via MCP',
|
|
16
|
+
defaults: {
|
|
17
|
+
name: 'Clientify',
|
|
18
|
+
},
|
|
19
|
+
inputs: ["main" /* NodeConnectionType.Main */],
|
|
20
|
+
outputs: ["main" /* NodeConnectionType.Main */],
|
|
21
|
+
credentials: [
|
|
22
|
+
{
|
|
23
|
+
name: 'clientifyMcpApi',
|
|
24
|
+
required: true,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
properties: (() => {
|
|
28
|
+
const props = [
|
|
29
|
+
{
|
|
30
|
+
displayName: 'Tool',
|
|
31
|
+
name: 'tool',
|
|
32
|
+
type: 'options',
|
|
33
|
+
options: ClientifyMcpFields_1.toolOptions,
|
|
34
|
+
default: 'listContacts',
|
|
35
|
+
required: true,
|
|
36
|
+
description: 'Select the MCP tool to execute',
|
|
37
|
+
},
|
|
38
|
+
...ClientifyMcpFields_1.toolFields,
|
|
39
|
+
];
|
|
40
|
+
// Add additional fields for each tool
|
|
41
|
+
for (const toolOption of ClientifyMcpFields_1.toolOptions) {
|
|
42
|
+
const additionalField = (0, ClientifyMcpFields_1.getAdditionalFieldsForTool)(toolOption.value);
|
|
43
|
+
if (additionalField) {
|
|
44
|
+
props.push(additionalField);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return props;
|
|
48
|
+
})(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async execute() {
|
|
52
|
+
var _a, _b, _c, _d, _e, _f;
|
|
53
|
+
const items = this.getInputData();
|
|
54
|
+
const returnData = [];
|
|
55
|
+
// Get credentials
|
|
56
|
+
const credentials = await this.getCredentials('clientifyMcpApi');
|
|
57
|
+
const authToken = credentials.authToken;
|
|
58
|
+
// MCP Server URL is fixed
|
|
59
|
+
const mcpUrl = 'https://mcp.clientify.com/mcp';
|
|
60
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
61
|
+
try {
|
|
62
|
+
const toolName = this.getNodeParameter('tool', itemIndex);
|
|
63
|
+
// Build tool arguments from individual fields
|
|
64
|
+
const toolArguments = {};
|
|
65
|
+
// Process regular fields
|
|
66
|
+
for (const field of ClientifyMcpFields_1.toolFields) {
|
|
67
|
+
const fieldName = field.name;
|
|
68
|
+
// Check if this field should be shown for the current tool
|
|
69
|
+
if ((_c = (_b = (_a = field.displayOptions) === null || _a === void 0 ? void 0 : _a.show) === null || _b === void 0 ? void 0 : _b.tool) === null || _c === void 0 ? void 0 : _c.includes(toolName)) {
|
|
70
|
+
try {
|
|
71
|
+
const value = this.getNodeParameter(fieldName, itemIndex);
|
|
72
|
+
if (value !== undefined && value !== '' && value !== null) {
|
|
73
|
+
toolArguments[fieldName] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
// Field might not exist for this tool
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Process additional fields
|
|
82
|
+
const additionalFieldsParam = `additionalFields`;
|
|
83
|
+
try {
|
|
84
|
+
const additionalFields = this.getNodeParameter(additionalFieldsParam, itemIndex);
|
|
85
|
+
if (additionalFields && typeof additionalFields === 'object') {
|
|
86
|
+
Object.assign(toolArguments, additionalFields);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
// Additional fields might not exist for this tool
|
|
91
|
+
}
|
|
92
|
+
// Step 1: Initialize session
|
|
93
|
+
const initRequest = {
|
|
94
|
+
jsonrpc: '2.0',
|
|
95
|
+
id: 1,
|
|
96
|
+
method: 'initialize',
|
|
97
|
+
params: {
|
|
98
|
+
protocolVersion: '2024-11-05',
|
|
99
|
+
capabilities: {}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const initOptions = {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
url: mcpUrl,
|
|
105
|
+
headers: {
|
|
106
|
+
'Content-Type': 'application/json',
|
|
107
|
+
'x-end-user-token': authToken,
|
|
108
|
+
},
|
|
109
|
+
body: initRequest,
|
|
110
|
+
json: true,
|
|
111
|
+
returnFullResponse: true,
|
|
112
|
+
};
|
|
113
|
+
const initResponse = await this.helpers.httpRequest(initOptions);
|
|
114
|
+
// Extract session ID from headers
|
|
115
|
+
const sessionId = initResponse.headers['mcp-session-id'] || initResponse.headers['Mcp-Session-Id'];
|
|
116
|
+
if (!sessionId) {
|
|
117
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to get session ID from MCP server');
|
|
118
|
+
}
|
|
119
|
+
// Step 2: Execute the selected tool
|
|
120
|
+
const requestBody = {
|
|
121
|
+
jsonrpc: '2.0',
|
|
122
|
+
id: 2,
|
|
123
|
+
method: 'tools/call',
|
|
124
|
+
params: {
|
|
125
|
+
name: toolName,
|
|
126
|
+
arguments: toolArguments,
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
// Make request with session ID
|
|
130
|
+
const options = {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
url: mcpUrl,
|
|
133
|
+
headers: {
|
|
134
|
+
'Content-Type': 'application/json',
|
|
135
|
+
'x-end-user-token': authToken,
|
|
136
|
+
'Mcp-Session-Id': sessionId,
|
|
137
|
+
},
|
|
138
|
+
body: requestBody,
|
|
139
|
+
json: true,
|
|
140
|
+
};
|
|
141
|
+
const response = await this.helpers.httpRequest(options);
|
|
142
|
+
// Process response
|
|
143
|
+
if (response.error) {
|
|
144
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `MCP Error: ${response.error.message || JSON.stringify(response.error)}`);
|
|
145
|
+
}
|
|
146
|
+
// Parse MCP response to make it user-friendly
|
|
147
|
+
let parsedResult = {};
|
|
148
|
+
try {
|
|
149
|
+
// Check if response has the MCP structure with JSON string
|
|
150
|
+
if ((_f = (_e = (_d = response.result) === null || _d === void 0 ? void 0 : _d.content) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.text) {
|
|
151
|
+
// Parse the JSON string from MCP response
|
|
152
|
+
const mcpData = JSON.parse(response.result.content[0].text);
|
|
153
|
+
// Return the parsed data directly - clean and accessible
|
|
154
|
+
parsedResult = mcpData;
|
|
155
|
+
// Add helpful boolean flags for common checks (generic, works for any response)
|
|
156
|
+
if (typeof mcpData === 'object' && mcpData !== null) {
|
|
157
|
+
// If there's a count field, add a boolean for it
|
|
158
|
+
if ('count' in mcpData) {
|
|
159
|
+
parsedResult._hasResults = mcpData.count > 0;
|
|
160
|
+
}
|
|
161
|
+
// If there's a results array, add a boolean for it
|
|
162
|
+
if (Array.isArray(mcpData.results)) {
|
|
163
|
+
parsedResult._hasResults = mcpData.results.length > 0;
|
|
164
|
+
parsedResult._resultCount = mcpData.results.length;
|
|
165
|
+
}
|
|
166
|
+
// If there's a success field, expose it clearly
|
|
167
|
+
if ('success' in mcpData) {
|
|
168
|
+
parsedResult._success = mcpData.success;
|
|
169
|
+
}
|
|
170
|
+
// If there's an error field, expose it clearly
|
|
171
|
+
if ('error' in mcpData) {
|
|
172
|
+
parsedResult._hasError = true;
|
|
173
|
+
parsedResult._error = mcpData.error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
// Not an MCP text response - return as is
|
|
179
|
+
parsedResult = response.result || response;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (parseError) {
|
|
183
|
+
// If parsing fails, return original result
|
|
184
|
+
parsedResult = response.result || response;
|
|
185
|
+
}
|
|
186
|
+
// Return formatted result with both parsed and raw data
|
|
187
|
+
returnData.push({
|
|
188
|
+
json: Object.assign(Object.assign({ success: true, tool: toolName, sessionId: sessionId }, parsedResult), {
|
|
189
|
+
// Keep raw response for advanced users
|
|
190
|
+
_raw: response.result || response }),
|
|
191
|
+
pairedItem: itemIndex,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
if (this.continueOnFail()) {
|
|
196
|
+
returnData.push({
|
|
197
|
+
json: {
|
|
198
|
+
success: false,
|
|
199
|
+
error: error instanceof Error ? error.message : String(error),
|
|
200
|
+
},
|
|
201
|
+
pairedItem: itemIndex,
|
|
202
|
+
});
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return [returnData];
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.ClientifyMcpDynamic = ClientifyMcpDynamic;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare const toolOptions: ({
|
|
3
|
+
name: string;
|
|
4
|
+
value: string;
|
|
5
|
+
description: string;
|
|
6
|
+
} | {
|
|
7
|
+
name: string;
|
|
8
|
+
value: string;
|
|
9
|
+
description?: undefined;
|
|
10
|
+
})[];
|
|
11
|
+
export declare const toolFields: INodeProperties[];
|
|
12
|
+
export declare const additionalFieldsDefinitions: {
|
|
13
|
+
[key: string]: INodeProperties[];
|
|
14
|
+
};
|
|
15
|
+
export declare function getAdditionalFieldsForTool(toolName: string): INodeProperties | null;
|