salesflare-mcp-server 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.
Files changed (73) hide show
  1. package/API.md +691 -0
  2. package/CHANGELOG.md +49 -0
  3. package/CLAUDE.md +117 -0
  4. package/CONTRIBUTING.md +399 -0
  5. package/FIX_PLAN.md +70 -0
  6. package/INSPECTOR.md +191 -0
  7. package/LICENSE +21 -0
  8. package/PUBLISH.md +73 -0
  9. package/README.md +383 -0
  10. package/dist/auth/api-key-auth.d.ts +75 -0
  11. package/dist/auth/api-key-auth.d.ts.map +1 -0
  12. package/dist/auth/api-key-auth.js +103 -0
  13. package/dist/auth/oauth-auth.d.ts +81 -0
  14. package/dist/auth/oauth-auth.d.ts.map +1 -0
  15. package/dist/auth/oauth-auth.js +123 -0
  16. package/dist/auth/token-manager.d.ts +105 -0
  17. package/dist/auth/token-manager.d.ts.map +1 -0
  18. package/dist/auth/token-manager.js +87 -0
  19. package/dist/client/salesflare-client.d.ts +219 -0
  20. package/dist/client/salesflare-client.d.ts.map +1 -0
  21. package/dist/client/salesflare-client.js +484 -0
  22. package/dist/index.d.ts +15 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +82 -0
  25. package/dist/server.d.ts +39 -0
  26. package/dist/server.d.ts.map +1 -0
  27. package/dist/server.js +140 -0
  28. package/dist/tools/companies.d.ts +45 -0
  29. package/dist/tools/companies.d.ts.map +1 -0
  30. package/dist/tools/companies.js +392 -0
  31. package/dist/tools/contacts.d.ts +45 -0
  32. package/dist/tools/contacts.d.ts.map +1 -0
  33. package/dist/tools/contacts.js +290 -0
  34. package/dist/tools/deals.d.ts +46 -0
  35. package/dist/tools/deals.d.ts.map +1 -0
  36. package/dist/tools/deals.js +442 -0
  37. package/dist/tools/pipeline.d.ts +43 -0
  38. package/dist/tools/pipeline.d.ts.map +1 -0
  39. package/dist/tools/pipeline.js +328 -0
  40. package/dist/tools/tasks.d.ts +44 -0
  41. package/dist/tools/tasks.d.ts.map +1 -0
  42. package/dist/tools/tasks.js +406 -0
  43. package/dist/transport/http-transport.d.ts +36 -0
  44. package/dist/transport/http-transport.d.ts.map +1 -0
  45. package/dist/transport/http-transport.js +173 -0
  46. package/dist/transport/stdio-transport.d.ts +37 -0
  47. package/dist/transport/stdio-transport.d.ts.map +1 -0
  48. package/dist/transport/stdio-transport.js +129 -0
  49. package/dist/types/company.d.ts +223 -0
  50. package/dist/types/company.d.ts.map +1 -0
  51. package/dist/types/company.js +8 -0
  52. package/dist/types/contact.d.ts +166 -0
  53. package/dist/types/contact.d.ts.map +1 -0
  54. package/dist/types/contact.js +8 -0
  55. package/dist/types/deal.d.ts +203 -0
  56. package/dist/types/deal.d.ts.map +1 -0
  57. package/dist/types/deal.js +8 -0
  58. package/dist/types/pipeline.d.ts +116 -0
  59. package/dist/types/pipeline.d.ts.map +1 -0
  60. package/dist/types/pipeline.js +8 -0
  61. package/dist/types/task.d.ts +154 -0
  62. package/dist/types/task.d.ts.map +1 -0
  63. package/dist/types/task.js +8 -0
  64. package/dist/utils/errors.d.ts +128 -0
  65. package/dist/utils/errors.d.ts.map +1 -0
  66. package/dist/utils/errors.js +205 -0
  67. package/dist/utils/validation.d.ts +354 -0
  68. package/dist/utils/validation.d.ts.map +1 -0
  69. package/dist/utils/validation.js +716 -0
  70. package/package.json +49 -0
  71. package/test-tasks-debug.js +21 -0
  72. package/test-tasks-params.js +52 -0
  73. package/test-tools.js +171 -0
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Pipeline tools for Salesflare MCP Server
3
+ *
4
+ * Implements pipeline operations:
5
+ * - salesflare_pipeline_list_stages: List pipeline stages
6
+ * - salesflare_pipeline_get_overview: Get pipeline statistics
7
+ *
8
+ * @module tools/pipeline
9
+ */
10
+ import { SalesflareError, ErrorCode } from '../utils/errors.js';
11
+ /**
12
+ * Pipeline tool definitions with JSON schemas
13
+ */
14
+ const pipelineTools = [
15
+ {
16
+ name: 'salesflare_pipeline_list_stages',
17
+ description: 'List pipeline stages from Salesflare CRM. ' +
18
+ 'Supports filtering by pipeline_id to get stages for a specific pipeline. ' +
19
+ 'Returns all stages across all pipelines by default, sorted by pipeline and position.',
20
+ inputSchema: {
21
+ type: 'object',
22
+ properties: {
23
+ pipeline_id: { type: 'string', description: 'Filter by pipeline ID (UUID, optional)' },
24
+ },
25
+ },
26
+ },
27
+ {
28
+ name: 'salesflare_pipeline_get_overview',
29
+ description: 'Get pipeline overview and statistics from Salesflare CRM. ' +
30
+ 'Shows deals per stage, total values, and win/loss counts. ' +
31
+ 'Supports filtering by pipeline_id for single pipeline view. ' +
32
+ 'Values are displayed in human-readable format (e.g., "$10,000.00").',
33
+ inputSchema: {
34
+ type: 'object',
35
+ properties: {
36
+ pipeline_id: { type: 'string', description: 'Filter by pipeline ID (UUID, optional)' },
37
+ },
38
+ },
39
+ },
40
+ ];
41
+ /**
42
+ * Exported pipeline tools array for unified registration
43
+ */
44
+ export { pipelineTools };
45
+ /**
46
+ * Handle pipeline tool calls
47
+ *
48
+ * @param client - Salesflare API client
49
+ * @param name - Tool name
50
+ * @param args - Tool arguments
51
+ * @returns Tool response
52
+ */
53
+ export async function handlePipelineTool(client, name, args) {
54
+ switch (name) {
55
+ case 'salesflare_pipeline_list_stages':
56
+ return await handleListStages(client, args);
57
+ case 'salesflare_pipeline_get_overview':
58
+ return await handleGetOverview(client, args);
59
+ default:
60
+ throw new SalesflareError({
61
+ code: ErrorCode.INVALID_INPUT,
62
+ message: `Unknown pipeline tool: ${name}`,
63
+ retryable: false,
64
+ });
65
+ }
66
+ }
67
+ /**
68
+ * Register pipeline-related tools with the MCP server
69
+ *
70
+ * @deprecated Use handlePipelineTool instead for unified registration
71
+ * @param server - MCP Server instance
72
+ * @param client - Salesflare API client
73
+ */
74
+ export function registerPipelineTools(server, client) {
75
+ // This function is deprecated - use unified tool registration in server.ts
76
+ // Kept for backward compatibility
77
+ }
78
+ /**
79
+ * Convert cents to dollars
80
+ */
81
+ function centsToDollars(cents) {
82
+ return cents / 100;
83
+ }
84
+ /**
85
+ * Format currency value for human-readable display
86
+ * Example: 1000000 cents → '$10,000.00' (per D-24)
87
+ */
88
+ function formatCurrency(valueInCents, currency = 'USD') {
89
+ const dollars = centsToDollars(valueInCents);
90
+ // Format with appropriate locale based on currency
91
+ let locale = 'en-US';
92
+ switch (currency.toUpperCase()) {
93
+ case 'EUR':
94
+ locale = 'de-DE';
95
+ break;
96
+ case 'GBP':
97
+ locale = 'en-GB';
98
+ break;
99
+ case 'JPY':
100
+ locale = 'ja-JP';
101
+ break;
102
+ default:
103
+ locale = 'en-US';
104
+ }
105
+ try {
106
+ return new Intl.NumberFormat(locale, {
107
+ style: 'currency',
108
+ currency: currency.toUpperCase(),
109
+ }).format(dollars);
110
+ }
111
+ catch {
112
+ // Fallback for unsupported currency codes
113
+ return `$${dollars.toFixed(2)}`;
114
+ }
115
+ }
116
+ /**
117
+ * Handle salesflare_pipeline_list_stages tool call
118
+ * Supports filtering by pipeline_id per D-21
119
+ */
120
+ async function handleListStages(client, params) {
121
+ let stages = [];
122
+ let total = 0;
123
+ if (params.pipeline_id) {
124
+ // Get stages for specific pipeline
125
+ const response = await client.get(`/pipelines/${params.pipeline_id}/stages`);
126
+ const items = response.items || [];
127
+ stages = items.map((item) => ({
128
+ id: item.id,
129
+ name: item.name,
130
+ pipeline_id: params.pipeline_id,
131
+ position: item.position,
132
+ created_at: item.created_at,
133
+ }));
134
+ total = response.total ?? items.length;
135
+ }
136
+ else {
137
+ // Get all pipelines first, then their stages
138
+ const pipelinesResponse = await client.get('/pipelines');
139
+ // Handle different response formats
140
+ const pipelines = pipelinesResponse.items || [];
141
+ // Fetch stages for each pipeline
142
+ const stagesPromises = pipelines.map(async (pipeline) => {
143
+ const pipelineId = pipeline.id;
144
+ try {
145
+ const stagesResponse = await client.get(`/pipelines/${pipelineId}/stages`);
146
+ const stagesItems = stagesResponse.items || [];
147
+ return stagesItems.map((item) => ({
148
+ id: item.id,
149
+ name: item.name,
150
+ pipeline_id: pipelineId,
151
+ position: item.position,
152
+ created_at: item.created_at,
153
+ }));
154
+ }
155
+ catch {
156
+ // If can't fetch stages for a pipeline, return empty array
157
+ return [];
158
+ }
159
+ });
160
+ const stagesArrays = await Promise.all(stagesPromises);
161
+ stages = stagesArrays.flat();
162
+ // Sort by pipeline_id, then position (per D-22)
163
+ stages.sort((a, b) => {
164
+ if (a.pipeline_id !== b.pipeline_id) {
165
+ return a.pipeline_id.localeCompare(b.pipeline_id);
166
+ }
167
+ return a.position - b.position;
168
+ });
169
+ total = stages.length;
170
+ }
171
+ const listResponse = {
172
+ stages,
173
+ pipeline_id: params.pipeline_id,
174
+ total,
175
+ };
176
+ // Generate human-readable summary
177
+ let summaryText;
178
+ if (stages.length === 0) {
179
+ summaryText = params.pipeline_id
180
+ ? 'No stages found for the specified pipeline.'
181
+ : 'No pipeline stages found.';
182
+ }
183
+ else {
184
+ summaryText = `Found ${total} pipeline stage(s)`;
185
+ if (params.pipeline_id) {
186
+ summaryText += ` for pipeline ${params.pipeline_id}`;
187
+ }
188
+ summaryText += '.';
189
+ // List stages
190
+ const stageList = stages.slice(0, 10).map((stage) => {
191
+ return ` ${stage.position}. ${stage.name}`;
192
+ });
193
+ if (stageList.length > 0) {
194
+ summaryText += '\n\nStages:\n' + stageList.join('\n');
195
+ }
196
+ if (stages.length > 10) {
197
+ summaryText += `\n ... and ${stages.length - 10} more`;
198
+ }
199
+ }
200
+ return {
201
+ content: [
202
+ { type: 'text', text: summaryText },
203
+ { type: 'text', text: JSON.stringify(listResponse, null, 2) },
204
+ ],
205
+ };
206
+ }
207
+ /**
208
+ * Handle salesflare_pipeline_get_overview tool call
209
+ * Returns pipeline statistics per D-18, D-23, D-24
210
+ */
211
+ async function handleGetOverview(client, params) {
212
+ const overviews = [];
213
+ // Get pipelines to process
214
+ let pipelines = [];
215
+ if (params.pipeline_id) {
216
+ // Get specific pipeline
217
+ try {
218
+ const pipelineResponse = await client.get(`/pipelines/${params.pipeline_id}`);
219
+ pipelines = [{
220
+ id: pipelineResponse.id,
221
+ name: pipelineResponse.name,
222
+ currency: pipelineResponse.currency || 'USD',
223
+ }];
224
+ }
225
+ catch (error) {
226
+ if (error instanceof SalesflareError && error.code === ErrorCode.NOT_FOUND) {
227
+ throw new SalesflareError({
228
+ code: ErrorCode.NOT_FOUND,
229
+ message: `Pipeline not found: ${params.pipeline_id}`,
230
+ retryable: false,
231
+ });
232
+ }
233
+ throw error;
234
+ }
235
+ }
236
+ else {
237
+ // Get all pipelines
238
+ const pipelinesResponse = await client.get('/pipelines');
239
+ const pipelinesItems = pipelinesResponse.items || [];
240
+ pipelines = pipelinesItems.map((p) => ({
241
+ id: p.id,
242
+ name: p.name,
243
+ currency: p.currency || 'USD',
244
+ }));
245
+ }
246
+ // Gather statistics for each pipeline
247
+ for (const pipeline of pipelines) {
248
+ try {
249
+ // Get stages for this pipeline
250
+ const stagesResponse = await client.get(`/pipelines/${pipeline.id}/stages`);
251
+ // Get deals for this pipeline
252
+ const dealsResponse = await client.get('/opportunities', {
253
+ params: { pipeline_id: pipeline.id, limit: 1000 },
254
+ });
255
+ const stagesItems = stagesResponse.items || [];
256
+ const dealsItems = dealsResponse.items || [];
257
+ // Calculate statistics per stage
258
+ const stageStatistics = stagesItems.map((stage) => {
259
+ const stageDeals = dealsItems.filter((d) => d.stage_id === stage.id);
260
+ const stageValueCents = stageDeals.reduce((sum, d) => sum + (d.value || 0), 0);
261
+ return {
262
+ stage_id: stage.id,
263
+ stage_name: stage.name,
264
+ position: stage.position,
265
+ deal_count: stageDeals.length,
266
+ total_value_cents: stageValueCents,
267
+ total_value_display: formatCurrency(stageValueCents, pipeline.currency),
268
+ };
269
+ });
270
+ // Calculate overall totals
271
+ const totalValueCents = dealsItems.reduce((sum, d) => sum + (d.value || 0), 0);
272
+ const openDeals = dealsItems.filter((d) => d.status === 'open').length;
273
+ const wonDeals = dealsItems.filter((d) => d.status === 'won').length;
274
+ const lostDeals = dealsItems.filter((d) => d.status === 'lost').length;
275
+ overviews.push({
276
+ pipeline_id: pipeline.id,
277
+ pipeline_name: pipeline.name,
278
+ total_deals: dealsItems.length,
279
+ total_value_cents: totalValueCents,
280
+ total_value_display: formatCurrency(totalValueCents, pipeline.currency),
281
+ stages: stageStatistics,
282
+ open_deals: openDeals,
283
+ won_deals: wonDeals,
284
+ lost_deals: lostDeals,
285
+ });
286
+ }
287
+ catch {
288
+ // Skip pipelines we can't fetch data for
289
+ continue;
290
+ }
291
+ }
292
+ const response = {
293
+ pipelines: overviews,
294
+ total_pipelines: overviews.length,
295
+ };
296
+ // Generate human-readable summary
297
+ let summaryText;
298
+ if (overviews.length === 0) {
299
+ summaryText = params.pipeline_id
300
+ ? 'No data found for the specified pipeline.'
301
+ : 'No pipeline overview data available.';
302
+ }
303
+ else if (overviews.length === 1) {
304
+ const overview = overviews[0];
305
+ summaryText = `Pipeline Overview: ${overview.pipeline_name}\n` +
306
+ `Total: ${overview.total_deals} deals worth ${overview.total_value_display}\n` +
307
+ `Status: ${overview.open_deals} open, ${overview.won_deals} won, ${overview.lost_deals} lost`;
308
+ if (overview.stages.length > 0) {
309
+ summaryText += '\n\nStages:';
310
+ overview.stages.forEach((stage) => {
311
+ summaryText += `\n ${stage.stage_name}: ${stage.deal_count} deals (${stage.total_value_display})`;
312
+ });
313
+ }
314
+ }
315
+ else {
316
+ summaryText = `Pipeline Overview for ${overviews.length} pipelines:\n`;
317
+ overviews.forEach((overview) => {
318
+ summaryText += `\n${overview.pipeline_name}: ${overview.total_deals} deals, ${overview.total_value_display} ` +
319
+ `(${overview.open_deals} open, ${overview.won_deals} won, ${overview.lost_deals} lost)`;
320
+ });
321
+ }
322
+ return {
323
+ content: [
324
+ { type: 'text', text: summaryText },
325
+ { type: 'text', text: JSON.stringify(response, null, 2) },
326
+ ],
327
+ };
328
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Task tools for Salesflare MCP Server
3
+ *
4
+ * Implements CRUD operations for tasks:
5
+ * - salesflare_tasks_list: List tasks with filtering and pagination
6
+ * - salesflare_tasks_create: Create new tasks
7
+ * - salesflare_tasks_update: Update existing tasks (mark complete/incomplete)
8
+ *
9
+ * @module tools/tasks
10
+ */
11
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
12
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
13
+ import { SalesflareClient } from '../client/salesflare-client.js';
14
+ /**
15
+ * Task tool definitions with JSON schemas
16
+ */
17
+ declare const taskTools: Tool[];
18
+ /**
19
+ * Exported task tools array for unified registration
20
+ */
21
+ export { taskTools };
22
+ /**
23
+ * Handle task tool calls
24
+ *
25
+ * @param client - Salesflare API client
26
+ * @param name - Tool name
27
+ * @param args - Tool arguments
28
+ * @returns Tool response
29
+ */
30
+ export declare function handleTasksTool(client: SalesflareClient, name: string, args: unknown): Promise<{
31
+ content: Array<{
32
+ type: string;
33
+ text: string;
34
+ }>;
35
+ }>;
36
+ /**
37
+ * Register task-related tools with the MCP server
38
+ *
39
+ * @deprecated Use handleTasksTool instead for unified registration
40
+ * @param server - MCP Server instance
41
+ * @param client - Salesflare API client
42
+ */
43
+ export declare function registerTasksTools(server: Server, client: SalesflareClient): void;
44
+ //# sourceMappingURL=tasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAGL,IAAI,EACL,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AASlE;;GAEG;AACH,QAAA,MAAM,SAAS,EAAE,IAAI,EA2DpB,CAAC;AAEF;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAkB7D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAGjF"}