@revfleet/hscli 0.7.0 → 0.8.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.
@@ -6,7 +6,9 @@ import { HubSpotClient } from "../core/http.js";
6
6
  import { CliError, redactSensitive } from "../core/output.js";
7
7
  import { ENGAGEMENT_OBJECT_TYPES, OBJECT_COMMAND_TYPES, PIPELINE_OBJECT_TYPES, PROPERTY_OBJECT_TYPES, appendOptional, encodePathSegment, maybeWrite, parseBooleanFlag, parseNumberFlag, parseSupportedObjectType, } from "../commands/crm/shared.js";
8
8
  import { resolvePortalContext, enrichListResponse, enrichRecordUrl, enrichCustomListResponse, enrichCustomRecordUrl, } from "../core/urls.js";
9
- const baseArgsSchema = {
9
+ import { registerHubspotCompatTools } from "./compat-hubspot.js";
10
+ import { registerExtensionTools } from "./ext-tools.js";
11
+ export const baseArgsSchema = {
10
12
  profile: z.string().min(1).optional(),
11
13
  force: z.boolean().optional(),
12
14
  dryRun: z.boolean().optional(),
@@ -59,15 +61,7 @@ function textResult(data) {
59
61
  structuredContent: asStructuredContent(safe),
60
62
  };
61
63
  }
62
- async function executeTool(args, fn, toolName) {
63
- // Tag every request made during this tool call with the MCP tool name, so
64
- // trace files produced by `HUBCLI_TELEMETRY_FILE` show up in
65
- // `hubcli trace stats --byToolName`. We use an env var rather than
66
- // threading through the request options because fn() may construct
67
- // arbitrary client.request() chains and we want all of them tagged.
68
- const previousToolName = process.env.HUBCLI_MCP_TOOL_NAME;
69
- if (toolName)
70
- process.env.HUBCLI_MCP_TOOL_NAME = toolName;
64
+ export async function executeTool(args, fn) {
71
65
  try {
72
66
  const ctx = mcpContext(args);
73
67
  const client = new HubSpotClient(getToken(ctx.profile), {
@@ -92,14 +86,44 @@ async function executeTool(args, fn, toolName) {
92
86
  }),
93
87
  };
94
88
  }
95
- finally {
96
- if (toolName) {
97
- if (previousToolName === undefined)
89
+ }
90
+ /**
91
+ * Wrap `server.registerTool` so every MCP handler runs with
92
+ * HUBCLI_MCP_TOOL_NAME set to this tool's name. The HTTP layer reads this
93
+ * env var to tag trace/telemetry events, so `hscli trace stats` and
94
+ * `hscli audit by-tool` can break activity down by MCP tool.
95
+ *
96
+ * The SDK's registerTool signature is a heavily-overloaded generic; we
97
+ * stay loosely typed here because every callsite already builds its own
98
+ * config/handler with the right shape.
99
+ */
100
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
101
+ export function registerMcpTool(server, name, config, handler) {
102
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
103
+ const wrapped = (...handlerArgs) => {
104
+ const previous = process.env.HUBCLI_MCP_TOOL_NAME;
105
+ process.env.HUBCLI_MCP_TOOL_NAME = name;
106
+ const restore = () => {
107
+ if (previous === undefined)
98
108
  delete process.env.HUBCLI_MCP_TOOL_NAME;
99
109
  else
100
- process.env.HUBCLI_MCP_TOOL_NAME = previousToolName;
110
+ process.env.HUBCLI_MCP_TOOL_NAME = previous;
111
+ };
112
+ try {
113
+ const result = handler(...handlerArgs);
114
+ if (result && typeof result.then === "function") {
115
+ return Promise.resolve(result).finally(restore);
116
+ }
117
+ restore();
118
+ return result;
101
119
  }
102
- }
120
+ catch (err) {
121
+ restore();
122
+ throw err;
123
+ }
124
+ };
125
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
126
+ server.registerTool(name, config, wrapped);
103
127
  }
104
128
  function isEnvTrue(value) {
105
129
  if (!value)
@@ -108,7 +132,7 @@ function isEnvTrue(value) {
108
132
  return normalized === "1" || normalized === "true" || normalized === "yes";
109
133
  }
110
134
  function registerStandardObjectTools(server, objectType) {
111
- server.registerTool(`crm_${objectType}_list`, {
135
+ registerMcpTool(server, `crm_${objectType}_list`, {
112
136
  description: `List ${objectType}`,
113
137
  inputSchema: {
114
138
  ...baseArgsSchema,
@@ -130,7 +154,7 @@ function registerStandardObjectTools(server, objectType) {
130
154
  enrichListResponse(res, portal, objectType);
131
155
  return res;
132
156
  }));
133
- server.registerTool(`crm_${objectType}_get`, {
157
+ registerMcpTool(server, `crm_${objectType}_get`, {
134
158
  description: `Get one ${objectType.slice(0, -1)} by ID`,
135
159
  inputSchema: {
136
160
  ...baseArgsSchema,
@@ -146,7 +170,7 @@ function registerStandardObjectTools(server, objectType) {
146
170
  enrichRecordUrl(res, portal, objectType);
147
171
  return res;
148
172
  }));
149
- server.registerTool(`crm_${objectType}_search`, {
173
+ registerMcpTool(server, `crm_${objectType}_search`, {
150
174
  description: `Search ${objectType}`,
151
175
  inputSchema: {
152
176
  ...baseArgsSchema,
@@ -167,7 +191,7 @@ function registerStandardObjectTools(server, objectType) {
167
191
  enrichListResponse(res, portal, objectType);
168
192
  return res;
169
193
  }));
170
- server.registerTool(`crm_${objectType}_create`, {
194
+ registerMcpTool(server, `crm_${objectType}_create`, {
171
195
  description: `Create ${objectType.slice(0, -1)} (dry-run by default unless force=true)`,
172
196
  inputSchema: {
173
197
  ...baseArgsSchema,
@@ -180,7 +204,7 @@ function registerStandardObjectTools(server, objectType) {
180
204
  enrichRecordUrl(res, portal, objectType);
181
205
  return res;
182
206
  }));
183
- server.registerTool(`crm_${objectType}_update`, {
207
+ registerMcpTool(server, `crm_${objectType}_update`, {
184
208
  description: `Update ${objectType.slice(0, -1)} (dry-run by default unless force=true)`,
185
209
  inputSchema: {
186
210
  ...baseArgsSchema,
@@ -195,26 +219,26 @@ function registerStandardObjectTools(server, objectType) {
195
219
  enrichRecordUrl(res, portal, objectType);
196
220
  return res;
197
221
  }));
198
- server.registerTool(`crm_${objectType}_delete`, {
222
+ registerMcpTool(server, `crm_${objectType}_delete`, {
199
223
  description: `Delete/archive ${objectType.slice(0, -1)} by ID (dry-run by default unless force=true)`,
200
224
  inputSchema: { ...baseArgsSchema, id: z.string().min(1) },
201
225
  }, (args) => executeTool(args, (ctx, client) => {
202
226
  const idSegment = encodePathSegment(args.id, "id");
203
227
  return maybeWrite(ctx, client, "DELETE", `/crm/v3/objects/${objectType}/${idSegment}`);
204
228
  }));
205
- server.registerTool(`crm_${objectType}_merge`, {
229
+ registerMcpTool(server, `crm_${objectType}_merge`, {
206
230
  description: `Merge ${objectType} records (endpoint support varies by object)`,
207
231
  inputSchema: { ...baseArgsSchema, data: z.record(z.string(), z.unknown()) },
208
232
  }, (args) => executeTool(args, (ctx, client) => maybeWrite(ctx, client, "POST", `/crm/v3/objects/${objectType}/merge`, args.data)));
209
- server.registerTool(`crm_${objectType}_batch_read`, {
233
+ registerMcpTool(server, `crm_${objectType}_batch_read`, {
210
234
  description: `Batch read ${objectType}`,
211
235
  inputSchema: { ...baseArgsSchema, data: z.record(z.string(), z.unknown()) },
212
236
  }, (args) => executeTool(args, (_ctx, client) => client.request(`/crm/v3/objects/${objectType}/batch/read`, { method: "POST", body: args.data })));
213
- server.registerTool(`crm_${objectType}_batch_upsert`, {
237
+ registerMcpTool(server, `crm_${objectType}_batch_upsert`, {
214
238
  description: `Batch upsert ${objectType} (dry-run by default unless force=true)`,
215
239
  inputSchema: { ...baseArgsSchema, data: z.record(z.string(), z.unknown()) },
216
240
  }, (args) => executeTool(args, (ctx, client) => maybeWrite(ctx, client, "POST", `/crm/v3/objects/${objectType}/batch/upsert`, args.data)));
217
- server.registerTool(`crm_${objectType}_batch_archive`, {
241
+ registerMcpTool(server, `crm_${objectType}_batch_archive`, {
218
242
  description: `Batch archive ${objectType} (dry-run by default unless force=true)`,
219
243
  inputSchema: { ...baseArgsSchema, data: z.record(z.string(), z.unknown()) },
220
244
  }, (args) => executeTool(args, (ctx, client) => maybeWrite(ctx, client, "POST", `/crm/v3/objects/${objectType}/batch/archive`, args.data)));
@@ -226,7 +250,14 @@ export function registerHubSpotTools(server) {
226
250
  for (const objectType of ENGAGEMENT_OBJECT_TYPES) {
227
251
  registerStandardObjectTools(server, objectType);
228
252
  }
229
- server.registerTool("crm_properties_list", {
253
+ // HubSpot Remote MCP drop-in compatibility layer — agents built against
254
+ // https://mcp.hubspot.com can target `hscli mcp` unchanged.
255
+ registerHubspotCompatTools(server);
256
+ // Extension surface — MCP tools that HubSpot's hosted server doesn't
257
+ // expose (workflows, files, forms, webhooks, marketing-emails, CMS writes,
258
+ // conversations).
259
+ registerExtensionTools(server);
260
+ registerMcpTool(server, "crm_properties_list", {
230
261
  description: "List properties for an object type",
231
262
  inputSchema: { ...baseArgsSchema, objectType: z.enum(PROPERTY_OBJECT_TYPES) },
232
263
  }, (args) => executeTool(args, (_ctx, client) => {
@@ -234,7 +265,7 @@ export function registerHubSpotTools(server) {
234
265
  const objectTypeSegment = encodePathSegment(objectTypeValue, "objectType");
235
266
  return client.request(`/crm/v3/properties/${objectTypeSegment}`);
236
267
  }));
237
- server.registerTool("crm_properties_get", {
268
+ registerMcpTool(server, "crm_properties_get", {
238
269
  description: "Get one property definition",
239
270
  inputSchema: { ...baseArgsSchema, objectType: z.enum(PROPERTY_OBJECT_TYPES), propertyName: z.string().min(1) },
240
271
  }, (args) => executeTool(args, (_ctx, client) => {
@@ -243,7 +274,7 @@ export function registerHubSpotTools(server) {
243
274
  const propertyNameSegment = encodePathSegment(args.propertyName, "propertyName");
244
275
  return client.request(`/crm/v3/properties/${objectTypeSegment}/${propertyNameSegment}`);
245
276
  }));
246
- server.registerTool("crm_properties_create", {
277
+ registerMcpTool(server, "crm_properties_create", {
247
278
  description: "Create property (dry-run by default unless force=true)",
248
279
  inputSchema: { ...baseArgsSchema, objectType: z.enum(PROPERTY_OBJECT_TYPES), data: z.record(z.string(), z.unknown()) },
249
280
  }, (args) => executeTool(args, (ctx, client) => {
@@ -251,7 +282,7 @@ export function registerHubSpotTools(server) {
251
282
  const objectTypeSegment = encodePathSegment(objectTypeValue, "objectType");
252
283
  return maybeWrite(ctx, client, "POST", `/crm/v3/properties/${objectTypeSegment}`, args.data);
253
284
  }));
254
- server.registerTool("crm_properties_update", {
285
+ registerMcpTool(server, "crm_properties_update", {
255
286
  description: "Update property (dry-run by default unless force=true)",
256
287
  inputSchema: {
257
288
  ...baseArgsSchema,
@@ -265,7 +296,7 @@ export function registerHubSpotTools(server) {
265
296
  const propertyNameSegment = encodePathSegment(args.propertyName, "propertyName");
266
297
  return maybeWrite(ctx, client, "PATCH", `/crm/v3/properties/${objectTypeSegment}/${propertyNameSegment}`, args.data);
267
298
  }));
268
- server.registerTool("crm_associations_list", {
299
+ registerMcpTool(server, "crm_associations_list", {
269
300
  description: "List associations between CRM objects (supports standard types, engagements, and custom object type IDs like 2-199622513)",
270
301
  inputSchema: {
271
302
  ...baseArgsSchema,
@@ -284,7 +315,7 @@ export function registerHubSpotTools(server) {
284
315
  appendOptional(params, "after", args.after);
285
316
  return client.request(`/crm/v4/objects/${fromObjectTypeSegment}/${fromObjectIdSegment}/associations/${toObjectTypeSegment}?${params.toString()}`);
286
317
  }));
287
- server.registerTool("crm_associations_create", {
318
+ registerMcpTool(server, "crm_associations_create", {
288
319
  description: "Create default association (dry-run by default unless force=true). Supports standard types, engagements, and custom object type IDs.",
289
320
  inputSchema: {
290
321
  ...baseArgsSchema,
@@ -296,7 +327,7 @@ export function registerHubSpotTools(server) {
296
327
  }, (args) => executeTool(args, (ctx, client) => {
297
328
  return maybeWrite(ctx, client, "PUT", `/crm/v4/objects/${encodePathSegment(args.fromObjectType.trim().toLowerCase(), "fromObjectType")}/${encodePathSegment(args.fromObjectId, "fromObjectId")}/associations/default/${encodePathSegment(args.toObjectType.trim().toLowerCase(), "toObjectType")}/${encodePathSegment(args.toObjectId, "toObjectId")}`);
298
329
  }));
299
- server.registerTool("crm_associations_remove", {
330
+ registerMcpTool(server, "crm_associations_remove", {
300
331
  description: "Remove default association (dry-run by default unless force=true). Supports standard types, engagements, and custom object type IDs.",
301
332
  inputSchema: {
302
333
  ...baseArgsSchema,
@@ -308,11 +339,11 @@ export function registerHubSpotTools(server) {
308
339
  }, (args) => executeTool(args, (ctx, client) => {
309
340
  return maybeWrite(ctx, client, "DELETE", `/crm/v4/objects/${encodePathSegment(args.fromObjectType.trim().toLowerCase(), "fromObjectType")}/${encodePathSegment(args.fromObjectId, "fromObjectId")}/associations/default/${encodePathSegment(args.toObjectType.trim().toLowerCase(), "toObjectType")}/${encodePathSegment(args.toObjectId, "toObjectId")}`);
310
341
  }));
311
- server.registerTool("crm_imports_create", {
342
+ registerMcpTool(server, "crm_imports_create", {
312
343
  description: "Create import (dry-run by default unless force=true)",
313
344
  inputSchema: { ...baseArgsSchema, data: z.record(z.string(), z.unknown()) },
314
345
  }, (args) => executeTool(args, (ctx, client) => maybeWrite(ctx, client, "POST", "/crm/v3/imports", args.data)));
315
- server.registerTool("crm_imports_list", {
346
+ registerMcpTool(server, "crm_imports_list", {
316
347
  description: "List import jobs",
317
348
  inputSchema: { ...baseArgsSchema, limit: z.number().int().positive().default(100), after: z.string().optional() },
318
349
  }, (args) => executeTool(args, (_ctx, client) => {
@@ -321,21 +352,21 @@ export function registerHubSpotTools(server) {
321
352
  appendOptional(params, "after", args.after);
322
353
  return client.request(`/crm/v3/imports?${params.toString()}`);
323
354
  }));
324
- server.registerTool("crm_imports_get", {
355
+ registerMcpTool(server, "crm_imports_get", {
325
356
  description: "Get import by ID",
326
357
  inputSchema: { ...baseArgsSchema, importId: z.string().min(1) },
327
358
  }, (args) => executeTool(args, (_ctx, client) => {
328
359
  const importIdSegment = encodePathSegment(args.importId, "importId");
329
360
  return client.request(`/crm/v3/imports/${importIdSegment}`);
330
361
  }));
331
- server.registerTool("crm_imports_errors", {
362
+ registerMcpTool(server, "crm_imports_errors", {
332
363
  description: "Get import errors",
333
364
  inputSchema: { ...baseArgsSchema, importId: z.string().min(1) },
334
365
  }, (args) => executeTool(args, (_ctx, client) => {
335
366
  const importIdSegment = encodePathSegment(args.importId, "importId");
336
367
  return client.request(`/crm/v3/imports/${importIdSegment}/errors`);
337
368
  }));
338
- server.registerTool("crm_owners_list", {
369
+ registerMcpTool(server, "crm_owners_list", {
339
370
  description: "List HubSpot owners",
340
371
  inputSchema: {
341
372
  ...baseArgsSchema,
@@ -350,23 +381,23 @@ export function registerHubSpotTools(server) {
350
381
  appendOptional(params, "email", args.email);
351
382
  return client.request(`/crm/v3/owners/?${params.toString()}`);
352
383
  }));
353
- server.registerTool("crm_custom_schemas_list", {
384
+ registerMcpTool(server, "crm_custom_schemas_list", {
354
385
  description: "List custom object schemas",
355
386
  inputSchema: { ...baseArgsSchema },
356
387
  }, (args) => executeTool(args, (_ctx, client) => client.request("/crm/v3/schemas")));
357
- server.registerTool("crm_custom_schemas_get", {
388
+ registerMcpTool(server, "crm_custom_schemas_get", {
358
389
  description: "Get custom object schema by objectType",
359
390
  inputSchema: { ...baseArgsSchema, objectType: z.string().min(1) },
360
391
  }, (args) => executeTool(args, (_ctx, client) => client.request(`/crm/v3/schemas/${encodePathSegment(args.objectType, "objectType")}`)));
361
- server.registerTool("crm_custom_schemas_create", {
392
+ registerMcpTool(server, "crm_custom_schemas_create", {
362
393
  description: "Create custom object schema (dry-run by default unless force=true)",
363
394
  inputSchema: { ...baseArgsSchema, data: z.record(z.string(), z.unknown()) },
364
395
  }, (args) => executeTool(args, (ctx, client) => maybeWrite(ctx, client, "POST", "/crm/v3/schemas", args.data)));
365
- server.registerTool("crm_custom_schemas_update", {
396
+ registerMcpTool(server, "crm_custom_schemas_update", {
366
397
  description: "Update custom object schema (dry-run by default unless force=true)",
367
398
  inputSchema: { ...baseArgsSchema, objectType: z.string().min(1), data: z.record(z.string(), z.unknown()) },
368
399
  }, (args) => executeTool(args, (ctx, client) => maybeWrite(ctx, client, "PATCH", `/crm/v3/schemas/${encodePathSegment(args.objectType, "objectType")}`, args.data)));
369
- server.registerTool("crm_custom_records_list", {
400
+ registerMcpTool(server, "crm_custom_records_list", {
370
401
  description: "List custom object records by objectType",
371
402
  inputSchema: { ...baseArgsSchema, objectType: z.string().min(1), limit: z.number().int().positive().default(50), after: z.string().optional() },
372
403
  }, (args) => executeTool(args, async (ctx, client) => {
@@ -378,7 +409,7 @@ export function registerHubSpotTools(server) {
378
409
  enrichCustomListResponse(res, portal, args.objectType);
379
410
  return res;
380
411
  }));
381
- server.registerTool("crm_custom_records_get", {
412
+ registerMcpTool(server, "crm_custom_records_get", {
382
413
  description: "Get custom object record by objectType and id",
383
414
  inputSchema: { ...baseArgsSchema, objectType: z.string().min(1), id: z.string().min(1) },
384
415
  }, (args) => executeTool(args, async (ctx, client) => {
@@ -388,7 +419,7 @@ export function registerHubSpotTools(server) {
388
419
  enrichCustomRecordUrl(res, portal, args.objectType);
389
420
  return res;
390
421
  }));
391
- server.registerTool("crm_custom_records_search", {
422
+ registerMcpTool(server, "crm_custom_records_search", {
392
423
  description: "Search custom object records",
393
424
  inputSchema: { ...baseArgsSchema, objectType: z.string().min(1), data: z.record(z.string(), z.unknown()) },
394
425
  }, (args) => executeTool(args, async (ctx, client) => {
@@ -397,7 +428,7 @@ export function registerHubSpotTools(server) {
397
428
  enrichCustomListResponse(res, portal, args.objectType);
398
429
  return res;
399
430
  }));
400
- server.registerTool("crm_custom_records_create", {
431
+ registerMcpTool(server, "crm_custom_records_create", {
401
432
  description: "Create custom object record (dry-run by default unless force=true)",
402
433
  inputSchema: { ...baseArgsSchema, objectType: z.string().min(1), data: z.record(z.string(), z.unknown()) },
403
434
  }, (args) => executeTool(args, async (ctx, client) => {
@@ -407,7 +438,7 @@ export function registerHubSpotTools(server) {
407
438
  enrichCustomRecordUrl(res, portal, args.objectType);
408
439
  return res;
409
440
  }));
410
- server.registerTool("crm_custom_records_update", {
441
+ registerMcpTool(server, "crm_custom_records_update", {
411
442
  description: "Update custom object record (dry-run by default unless force=true)",
412
443
  inputSchema: { ...baseArgsSchema, objectType: z.string().min(1), id: z.string().min(1), data: z.record(z.string(), z.unknown()) },
413
444
  }, (args) => executeTool(args, async (ctx, client) => {
@@ -417,11 +448,11 @@ export function registerHubSpotTools(server) {
417
448
  enrichCustomRecordUrl(res, portal, args.objectType);
418
449
  return res;
419
450
  }));
420
- server.registerTool("crm_custom_records_delete", {
451
+ registerMcpTool(server, "crm_custom_records_delete", {
421
452
  description: "Delete custom object record (dry-run by default unless force=true)",
422
453
  inputSchema: { ...baseArgsSchema, objectType: z.string().min(1), id: z.string().min(1) },
423
454
  }, (args) => executeTool(args, (ctx, client) => maybeWrite(ctx, client, "DELETE", `/crm/v3/objects/${encodePathSegment(args.objectType, "objectType")}/${encodePathSegment(args.id, "id")}`)));
424
- server.registerTool("crm_pipelines_list", {
455
+ registerMcpTool(server, "crm_pipelines_list", {
425
456
  description: "List pipelines for object type",
426
457
  inputSchema: { ...baseArgsSchema, objectType: z.enum(PIPELINE_OBJECT_TYPES) },
427
458
  }, (args) => executeTool(args, (_ctx, client) => {
@@ -429,7 +460,7 @@ export function registerHubSpotTools(server) {
429
460
  const objectTypeSegment = encodePathSegment(objectTypeValue, "objectType");
430
461
  return client.request(`/crm/v3/pipelines/${objectTypeSegment}`);
431
462
  }));
432
- server.registerTool("crm_pipelines_get", {
463
+ registerMcpTool(server, "crm_pipelines_get", {
433
464
  description: "Get one pipeline",
434
465
  inputSchema: { ...baseArgsSchema, objectType: z.enum(PIPELINE_OBJECT_TYPES), pipelineId: z.string().min(1) },
435
466
  }, (args) => executeTool(args, (_ctx, client) => {
@@ -438,7 +469,7 @@ export function registerHubSpotTools(server) {
438
469
  const pipelineIdSegment = encodePathSegment(args.pipelineId, "pipelineId");
439
470
  return client.request(`/crm/v3/pipelines/${objectTypeSegment}/${pipelineIdSegment}`);
440
471
  }));
441
- server.registerTool("hub_api_request", {
472
+ registerMcpTool(server, "hub_api_request", {
442
473
  description: "Raw HubSpot API request with safety controls",
443
474
  inputSchema: {
444
475
  ...baseArgsSchema,
@@ -454,7 +485,7 @@ export function registerHubSpotTools(server) {
454
485
  return maybeWrite(ctx, client, method, args.path, args.data);
455
486
  }));
456
487
  // ── Lists ──────────────────────────────────────────────────────────────
457
- server.registerTool("crm_lists_list", {
488
+ registerMcpTool(server, "crm_lists_list", {
458
489
  description: "List CRM lists with pagination",
459
490
  inputSchema: {
460
491
  ...baseArgsSchema,
@@ -470,7 +501,7 @@ export function registerHubSpotTools(server) {
470
501
  const qs = params.toString();
471
502
  return await client.request(`/crm/v3/lists${qs ? `?${qs}` : ""}`);
472
503
  }));
473
- server.registerTool("crm_lists_get", {
504
+ registerMcpTool(server, "crm_lists_get", {
474
505
  description: "Get a CRM list by ID",
475
506
  inputSchema: {
476
507
  ...baseArgsSchema,
@@ -480,7 +511,7 @@ export function registerHubSpotTools(server) {
480
511
  const listIdSegment = encodePathSegment(args.listId, "listId");
481
512
  return await client.request(`/crm/v3/lists/${listIdSegment}`);
482
513
  }));
483
- server.registerTool("crm_lists_create", {
514
+ registerMcpTool(server, "crm_lists_create", {
484
515
  description: "Create a CRM list (dry-run by default unless force=true)",
485
516
  inputSchema: {
486
517
  ...baseArgsSchema,
@@ -489,7 +520,7 @@ export function registerHubSpotTools(server) {
489
520
  }, (args) => executeTool(args, (ctx, client) => {
490
521
  return maybeWrite(ctx, client, "POST", "/crm/v3/lists", args.data);
491
522
  }));
492
- server.registerTool("crm_lists_update", {
523
+ registerMcpTool(server, "crm_lists_update", {
493
524
  description: "Update a CRM list (dry-run by default unless force=true)",
494
525
  inputSchema: {
495
526
  ...baseArgsSchema,
@@ -500,7 +531,7 @@ export function registerHubSpotTools(server) {
500
531
  const listIdSegment = encodePathSegment(args.listId, "listId");
501
532
  return maybeWrite(ctx, client, "PATCH", `/crm/v3/lists/${listIdSegment}`, args.data);
502
533
  }));
503
- server.registerTool("crm_lists_delete", {
534
+ registerMcpTool(server, "crm_lists_delete", {
504
535
  description: "Delete a CRM list (dry-run by default unless force=true)",
505
536
  inputSchema: {
506
537
  ...baseArgsSchema,
@@ -510,7 +541,7 @@ export function registerHubSpotTools(server) {
510
541
  const listIdSegment = encodePathSegment(args.listId, "listId");
511
542
  return maybeWrite(ctx, client, "DELETE", `/crm/v3/lists/${listIdSegment}`);
512
543
  }));
513
- server.registerTool("crm_lists_memberships", {
544
+ registerMcpTool(server, "crm_lists_memberships", {
514
545
  description: "Get memberships of a CRM list",
515
546
  inputSchema: {
516
547
  ...baseArgsSchema,
@@ -521,7 +552,7 @@ export function registerHubSpotTools(server) {
521
552
  return await client.request(`/crm/v3/lists/${listIdSegment}/memberships`);
522
553
  }));
523
554
  // ── Sequences ──────────────────────────────────────────────────────────
524
- server.registerTool("sales_sequences_list", {
555
+ registerMcpTool(server, "sales_sequences_list", {
525
556
  description: "List sales sequences with pagination",
526
557
  inputSchema: {
527
558
  ...baseArgsSchema,
@@ -537,7 +568,7 @@ export function registerHubSpotTools(server) {
537
568
  const qs = params.toString();
538
569
  return await client.request(`/automation/v4/sequences${qs ? `?${qs}` : ""}`);
539
570
  }));
540
- server.registerTool("sales_sequences_get", {
571
+ registerMcpTool(server, "sales_sequences_get", {
541
572
  description: "Get a sales sequence by ID",
542
573
  inputSchema: {
543
574
  ...baseArgsSchema,
@@ -547,7 +578,7 @@ export function registerHubSpotTools(server) {
547
578
  const sequenceIdSegment = encodePathSegment(args.sequenceId, "sequenceId");
548
579
  return await client.request(`/automation/v4/sequences/${sequenceIdSegment}`);
549
580
  }));
550
- server.registerTool("sales_sequences_enrollments", {
581
+ registerMcpTool(server, "sales_sequences_enrollments", {
551
582
  description: "Get enrollments for a sales sequence",
552
583
  inputSchema: {
553
584
  ...baseArgsSchema,
@@ -558,7 +589,7 @@ export function registerHubSpotTools(server) {
558
589
  return await client.request(`/automation/v4/sequences/${sequenceIdSegment}/enrollments`);
559
590
  }));
560
591
  // ── Reporting ──────────────────────────────────────────────────────────
561
- server.registerTool("reporting_dashboards_list", {
592
+ registerMcpTool(server, "reporting_dashboards_list", {
562
593
  description: "List analytics reports/dashboards with pagination",
563
594
  inputSchema: {
564
595
  ...baseArgsSchema,
@@ -574,7 +605,7 @@ export function registerHubSpotTools(server) {
574
605
  const qs = params.toString();
575
606
  return await client.request(`/analytics/v2/reports${qs ? `?${qs}` : ""}`);
576
607
  }));
577
- server.registerTool("reporting_dashboards_get", {
608
+ registerMcpTool(server, "reporting_dashboards_get", {
578
609
  description: "Get an analytics report/dashboard by ID",
579
610
  inputSchema: {
580
611
  ...baseArgsSchema,
@@ -585,7 +616,7 @@ export function registerHubSpotTools(server) {
585
616
  return await client.request(`/analytics/v2/reports/${dashboardIdSegment}`);
586
617
  }));
587
618
  // ── Exports ────────────────────────────────────────────────────────────
588
- server.registerTool("crm_exports_create", {
619
+ registerMcpTool(server, "crm_exports_create", {
589
620
  description: "Create a CRM export (dry-run by default unless force=true)",
590
621
  inputSchema: {
591
622
  ...baseArgsSchema,
@@ -594,7 +625,7 @@ export function registerHubSpotTools(server) {
594
625
  }, (args) => executeTool(args, (ctx, client) => {
595
626
  return maybeWrite(ctx, client, "POST", "/crm/v3/exports", args.data);
596
627
  }));
597
- server.registerTool("crm_exports_list", {
628
+ registerMcpTool(server, "crm_exports_list", {
598
629
  description: "List CRM exports with pagination",
599
630
  inputSchema: {
600
631
  ...baseArgsSchema,
@@ -610,7 +641,7 @@ export function registerHubSpotTools(server) {
610
641
  const qs = params.toString();
611
642
  return await client.request(`/crm/v3/exports${qs ? `?${qs}` : ""}`);
612
643
  }));
613
- server.registerTool("crm_exports_get", {
644
+ registerMcpTool(server, "crm_exports_get", {
614
645
  description: "Get a CRM export by ID",
615
646
  inputSchema: {
616
647
  ...baseArgsSchema,
@@ -620,7 +651,7 @@ export function registerHubSpotTools(server) {
620
651
  const exportIdSegment = encodePathSegment(args.exportId, "exportId");
621
652
  return await client.request(`/crm/v3/exports/${exportIdSegment}`);
622
653
  }));
623
- server.registerTool("crm_exports_status", {
654
+ registerMcpTool(server, "crm_exports_status", {
624
655
  description: "Get the status of a CRM export",
625
656
  inputSchema: {
626
657
  ...baseArgsSchema,
@@ -631,7 +662,7 @@ export function registerHubSpotTools(server) {
631
662
  return await client.request(`/crm/v3/exports/${exportIdSegment}/status`);
632
663
  }));
633
664
  // ── Pipeline Stages ────────────────────────────────────────────────────
634
- server.registerTool("crm_pipelines_stages", {
665
+ registerMcpTool(server, "crm_pipelines_stages", {
635
666
  description: "List stages for a pipeline",
636
667
  inputSchema: {
637
668
  ...baseArgsSchema,
@@ -645,7 +676,7 @@ export function registerHubSpotTools(server) {
645
676
  return client.request(`/crm/v3/pipelines/${objectTypeSegment}/${pipelineIdSegment}/stages`);
646
677
  }));
647
678
  // ── Property Groups ────────────────────────────────────────────────────
648
- server.registerTool("crm_property_groups_list", {
679
+ registerMcpTool(server, "crm_property_groups_list", {
649
680
  description: "List property groups for an object type",
650
681
  inputSchema: {
651
682
  ...baseArgsSchema,
@@ -656,7 +687,7 @@ export function registerHubSpotTools(server) {
656
687
  const objectTypeSegment = encodePathSegment(objectTypeValue, "objectType");
657
688
  return client.request(`/crm/v3/properties/${objectTypeSegment}/groups`);
658
689
  }));
659
- server.registerTool("crm_property_groups_create", {
690
+ registerMcpTool(server, "crm_property_groups_create", {
660
691
  description: "Create a property group (dry-run by default unless force=true)",
661
692
  inputSchema: {
662
693
  ...baseArgsSchema,
@@ -668,7 +699,7 @@ export function registerHubSpotTools(server) {
668
699
  const objectTypeSegment = encodePathSegment(objectTypeValue, "objectType");
669
700
  return maybeWrite(ctx, client, "POST", `/crm/v3/properties/${objectTypeSegment}/groups`, args.data);
670
701
  }));
671
- server.registerTool("crm_property_groups_update", {
702
+ registerMcpTool(server, "crm_property_groups_update", {
672
703
  description: "Update a property group (dry-run by default unless force=true)",
673
704
  inputSchema: {
674
705
  ...baseArgsSchema,