@daeda/mcp-pro 0.1.29 → 0.1.30

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 (2) hide show
  1. package/dist/index.js +117 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1712,6 +1712,20 @@ async function openDatabaseConnection(portalId, _encryptionKey, options2) {
1712
1712
  if (mode === "read_only" && !fs3.existsSync(sourcePath)) {
1713
1713
  throw new Error(`Replica database not found for portal ${portalId}: ${sourcePath}`);
1714
1714
  }
1715
+ if (mode === "read_only") {
1716
+ const instance2 = await DuckDBInstance.create(":memory:");
1717
+ const connection2 = await instance2.connect();
1718
+ const escapedPath = escapeSqlString(sourcePath);
1719
+ await connection2.run(`ATTACH '${escapedPath}' AS portal_replica (READ_ONLY)`);
1720
+ await connection2.run("USE portal_replica");
1721
+ return {
1722
+ instance: instance2,
1723
+ connection: connection2,
1724
+ mode,
1725
+ sourcePath,
1726
+ replicaFingerprint: getReplicaFingerprint(portalId)
1727
+ };
1728
+ }
1715
1729
  const instance = await DuckDBInstance.create(sourcePath);
1716
1730
  const connection = await instance.connect();
1717
1731
  return {
@@ -9303,7 +9317,7 @@ var BUILT_IN_ACTION_TYPES = {
9303
9317
  { name: "date", type: "object", required: true, description: "Date spec \u2014 MUST include type: { type: 'STATIC_VALUE', staticValue: '<unix ms>' } or { type: 'OBJECT_PROPERTY', propertyName: '<name>' }. Omitting type causes HTTP 500." },
9304
9318
  { name: "delta", type: "string", required: true, description: "Offset from the date (e.g. '0')" },
9305
9319
  { name: "time_unit", type: "string", required: true, description: "Unit for delta: DAYS, HOURS, MINUTES" },
9306
- { name: "time_of_day", type: "object", required: true, description: "Required time of day: { hour: number, minute: number }. HubSpot UI rejects delay-until-date actions without it." }
9320
+ { name: "time_of_day", type: "object", required: true, description: "Required time of day: { hour: number, minute: number }. HubSpot UI rejects delay-until-date actions without it. If the workflow continues after this delay, the next action should be a STATIC_BRANCH on hs_delay_status rather than a direct business-action connection." }
9307
9321
  ]
9308
9322
  },
9309
9323
  "0-63809083": {
@@ -9623,7 +9637,7 @@ var WORKFLOW_ACTION_CREATE_METADATA_BY_ID = {
9623
9637
  },
9624
9638
  "0-35": {
9625
9639
  resourceRequirements: [],
9626
- createGuidanceText: "When using STATIC_VALUE dates, send a future Unix-milliseconds timestamp. time_of_day is required for reliable creation."
9640
+ createGuidanceText: "When using STATIC_VALUE dates, send a future Unix-milliseconds timestamp. time_of_day is required for reliable creation. If the delay action continues to another step, route it into a STATIC_BRANCH that reads inputValue { actionId: '<delay_action_id>', dataKey: 'hs_delay_status', type: 'FIELD_DATA' } and continues from the DATE_MET_AS_PLANNED branch instead of connecting directly to the next business action."
9627
9641
  },
9628
9642
  "0-14": {
9629
9643
  resourceRequirements: [],
@@ -11774,12 +11788,12 @@ var PROPERTY_VALUE_CHANGED_PARAMETER_SCHEMA = [
11774
11788
  {
11775
11789
  name: "property_value_operator",
11776
11790
  required: false,
11777
- description: "Defaults to IS_KNOWN. Supported values: IS_KNOWN, IS_UNKNOWN, IS_EQUAL_TO, IS_NOT_EQUAL_TO."
11791
+ description: "Defaults to IS_KNOWN. Supported values: IS_KNOWN, IS_UNKNOWN, IS_EQUAL_TO, IS_NOT_EQUAL_TO. This only controls the event refinement filter on hs_value inside the 4-655002 property-change event branch."
11778
11792
  },
11779
11793
  {
11780
11794
  name: "property_value",
11781
11795
  required: false,
11782
- description: "Required when property_value_operator is IS_EQUAL_TO or IS_NOT_EQUAL_TO."
11796
+ description: "Required when property_value_operator is IS_EQUAL_TO or IS_NOT_EQUAL_TO. This does not add generic object-property filters like pipeline or dealstage to the event branch."
11783
11797
  }
11784
11798
  ];
11785
11799
  var WORKFLOW_REFERENCE_PARAMETER_SCHEMA = [
@@ -11797,7 +11811,7 @@ var PARAMETER_SCHEMA_BY_TEMPLATE = {
11797
11811
  var EVENT_TYPE_METADATA_BY_EVENT_TYPE_ID = {
11798
11812
  "4-655002": {
11799
11813
  requiresRefinementFilters: true,
11800
- refinementNote: "Requires event-specific refinement filters (e.g. hs_property_name, hs_property_value) embedded in the event filter branch to specify which property and value to trigger on. Standard PROPERTY filters are NOT sufficient. Prefer LIST_BASED enrollment with re-enrollment triggers for 'property changed to X' scenarios."
11814
+ refinementNote: "Requires event-specific refinement filters embedded in the event branch. For MCP these are hs_name and hs_value. Standard object-property filters like pipeline, dealstage, amount, or closedate are not valid inside the 4-655002 branch. Prefer LIST_BASED enrollment with re-enrollment triggers for 'property changed to X' scenarios that also need record eligibility filters."
11801
11815
  }
11802
11816
  };
11803
11817
  var normalizedRows = normalized_triggers_default;
@@ -12235,6 +12249,13 @@ var buildEnrollmentCriteriaFromTrigger = (trigger) => {
12235
12249
  };
12236
12250
 
12237
12251
  // ../shared/pure/workflow-operation-schema.ts
12252
+ var ENUMERATION_DISALLOWED_OPERATORS = /* @__PURE__ */ new Set([
12253
+ "IS_EQUAL_TO",
12254
+ "IS_NOT_EQUAL_TO"
12255
+ ]);
12256
+ var REFINEMENT_PROPERTIES_BY_EVENT_TYPE_ID = {
12257
+ "4-655002": /* @__PURE__ */ new Set(["hs_name", "hs_value"])
12258
+ };
12238
12259
  var WorkflowConnectionSchema2 = z62.object({
12239
12260
  edgeType: z62.string().min(1),
12240
12261
  nextActionId: z62.string().min(1)
@@ -12262,6 +12283,14 @@ var WorkflowFilterSchema = z62.object({
12262
12283
  message: "property filters require operation",
12263
12284
  path: ["operation"]
12264
12285
  });
12286
+ return;
12287
+ }
12288
+ if (value.operation.operationType === "ENUMERATION" && ENUMERATION_DISALLOWED_OPERATORS.has(value.operation.operator)) {
12289
+ ctx.addIssue({
12290
+ code: z62.ZodIssueCode.custom,
12291
+ message: `operator '${value.operation.operator}' is not valid for ENUMERATION workflow filters. Use operators like IS_ANY_OF, IS_NONE_OF, IS_EXACTLY, or IS_NOT_EXACTLY.`,
12292
+ path: ["operation", "operator"]
12293
+ });
12265
12294
  }
12266
12295
  });
12267
12296
  var WorkflowBranchSchema = z62.object({
@@ -12302,6 +12331,19 @@ var WorkflowEnrollmentBranchSchema = z62.object({
12302
12331
  path: ["eventTypeId"]
12303
12332
  });
12304
12333
  }
12334
+ const allowedRefinementProperties = REFINEMENT_PROPERTIES_BY_EVENT_TYPE_ID[value.eventTypeId];
12335
+ if (!allowedRefinementProperties) return;
12336
+ for (const [index, filter] of value.filters.entries()) {
12337
+ if (!filter || typeof filter !== "object") continue;
12338
+ if (filter.filterType !== "PROPERTY") continue;
12339
+ if (typeof filter.property !== "string") continue;
12340
+ if (allowedRefinementProperties.has(filter.property)) continue;
12341
+ ctx.addIssue({
12342
+ code: z62.ZodIssueCode.custom,
12343
+ message: `eventTypeId '${value.eventTypeId}' only supports refinement filters on ${Array.from(allowedRefinementProperties).join(", ")}. Move generic properties like '${filter.property}' into LIST_BASED criteria or use enrollment_trigger.`,
12344
+ path: ["filters", index, "property"]
12345
+ });
12346
+ }
12305
12347
  });
12306
12348
  var EventBasedEnrollmentCriteriaSchema = z62.object({
12307
12349
  type: z62.literal("EVENT_BASED"),
@@ -12432,7 +12474,7 @@ var fields44 = [
12432
12474
  name: "enrollment_criteria",
12433
12475
  type: "object",
12434
12476
  required: false,
12435
- description: "Enrollment trigger config. Set type to 'EVENT_BASED', 'LIST_BASED', or 'MANUAL'. Prefer enrollment_trigger for supported single-trigger EVENT_BASED shortcuts. For EVENT_BASED, each eventFilterBranch needs a valid eventTypeId from the workflow_enrollment_triggers table (e.g. '4-655002' for property change, '4-1639801' for form submission). Do NOT use object type IDs like '0-3' as eventTypeId \u2014 those are CRM object types, not events. For LIST_BASED and MANUAL, query workflow_enrollment_triggers rows where shortcut_kind = 'CRITERIA_EXAMPLE' and reuse official_criteria_example_json, criteria_shape_json, usage_guidance, preferred_for_json, and avoid_for_json. IMPORTANT: Some event types (notably '4-655002' property value changed) require event-specific refinement filters (e.g. hs_property_name, hs_property_value) embedded in the event filter branch \u2014 standard PROPERTY filters are NOT sufficient. For 'when property X changes to Y' scenarios, prefer LIST_BASED enrollment with re-enrollment triggers on that property instead. PROPERTY filters must include operation.operationType. For enum properties use 'ENUMERATION' not 'MULTISTRING'. Query synced workflows for structural examples.",
12477
+ description: "Enrollment trigger config. Set type to 'EVENT_BASED', 'LIST_BASED', or 'MANUAL'. Prefer enrollment_trigger for supported single-trigger EVENT_BASED shortcuts. For EVENT_BASED, each eventFilterBranch needs a valid eventTypeId from the workflow_enrollment_triggers table (e.g. '4-655002' for property change, '4-1639801' for form submission). Do NOT use object type IDs like '0-3' as eventTypeId \u2014 those are CRM object types, not events. For LIST_BASED and MANUAL, query workflow_enrollment_triggers rows where shortcut_kind = 'CRITERIA_EXAMPLE' and reuse official_criteria_example_json, criteria_shape_json, usage_guidance, preferred_for_json, and avoid_for_json. IMPORTANT: Some event types (notably '4-655002' property value changed) require event-specific refinement filters embedded in the event branch. For '4-655002', only event refinement properties like hs_name and hs_value belong in that branch; generic record properties like pipeline, dealstage, amount, and closedate do NOT belong there. For 'when property X changes to Y' scenarios that also need record eligibility checks, prefer LIST_BASED enrollment with re-enrollment triggers on that property instead. PROPERTY filters must include operation.operationType. For enum properties use 'ENUMERATION' not 'MULTISTRING'. For LIST_BASED property existence filters like IS_KNOWN / IS_UNKNOWN, prefer operationType 'ALL_PROPERTY' instead of 'TIME_POINT', even for date properties. ENUMERATION filters do not support operators like 'IS_EQUAL_TO' or 'IS_NOT_EQUAL_TO'; use enum operators like 'IS_ANY_OF', 'IS_NONE_OF', 'IS_EXACTLY', or 'IS_NOT_EXACTLY'. Query synced workflows for structural examples.",
12436
12478
  shape: "EVENT_BASED: { shouldReEnroll: boolean, eventFilterBranches: [{ filterBranchType: 'UNIFIED_EVENTS', filterBranchOperator?, eventTypeId: string (REQUIRED, from workflow_enrollment_triggers.event_type_id), operator?, filters: [{ filterType: 'PROPERTY', property: string, operation: { operationType: string, operator: string } }], filterBranches: [...] }], listMembershipFilterBranches?: [...] } | LIST_BASED: { shouldReEnroll: boolean, listFilterBranch: { filterBranchType, filters: [...], filterBranches: [...] }, reEnrollmentTriggersFilterBranches?: [...], unEnrollObjectsNotMeetingCriteria?: boolean } | MANUAL: { shouldReEnroll: boolean }"
12437
12479
  },
12438
12480
  { name: "suppression_list_ids", type: "array", required: false, description: "Array of list IDs to suppress from enrollment" },
@@ -12539,7 +12581,7 @@ var fields45 = [
12539
12581
  description: "Replacement single-trigger shortcut. Query workflow_enrollment_triggers first and only use rows where shortcut_kind = 'EVENT_TRIGGER_SHORTCUT'. Then use catalog_id plus parameter_schema_json, official_filter_template, official_filters_example_json, and required_object_type_ids_json.",
12540
12582
  shape: "{ catalog_id: string, should_re_enroll?: boolean, ...template-specific parameters from parameter_schema_json such as property_name/property_value_operator/property_value or target_workflow_id }"
12541
12583
  },
12542
- { name: "enrollment_criteria", type: "object", required: false, description: "Replacement enrollment criteria (replaces existing). Set type to EVENT_BASED, LIST_BASED, or MANUAL. Prefer enrollment_trigger for supported single-trigger EVENT_BASED shortcuts. For EVENT_BASED, each UNIFIED_EVENTS branch requires a valid eventTypeId from workflow_enrollment_triggers.event_type_id (e.g. '4-655002' for property change). Do NOT use object type IDs like '0-3' as eventTypeId. For LIST_BASED and MANUAL, query workflow_enrollment_triggers rows where shortcut_kind = 'CRITERIA_EXAMPLE' and reuse official_criteria_example_json, criteria_shape_json, and usage_guidance. IMPORTANT: Event types like '4-655002' (property changed) require event-specific refinement filters (e.g. hs_property_name) \u2014 standard PROPERTY filters are NOT sufficient. Prefer LIST_BASED enrollment with re-enrollment triggers for property-change scenarios." },
12584
+ { name: "enrollment_criteria", type: "object", required: false, description: "Replacement enrollment criteria (replaces existing). Set type to EVENT_BASED, LIST_BASED, or MANUAL. Prefer enrollment_trigger for supported single-trigger EVENT_BASED shortcuts. For EVENT_BASED, each UNIFIED_EVENTS branch requires a valid eventTypeId from workflow_enrollment_triggers.event_type_id (e.g. '4-655002' for property change). Do NOT use object type IDs like '0-3' as eventTypeId. For LIST_BASED and MANUAL, query workflow_enrollment_triggers rows where shortcut_kind = 'CRITERIA_EXAMPLE' and reuse official_criteria_example_json, criteria_shape_json, and usage_guidance. IMPORTANT: Event types like '4-655002' require event-specific refinement filters only. In MCP this means hs_name and hs_value inside the event branch; generic properties like pipeline, dealstage, amount, or closedate do not belong there. ENUMERATION filters also do not support operators like IS_EQUAL_TO or IS_NOT_EQUAL_TO; use IS_ANY_OF, IS_NONE_OF, IS_EXACTLY, or IS_NOT_EXACTLY instead. Prefer LIST_BASED enrollment with re-enrollment triggers for property-change scenarios that also need record eligibility filters." },
12543
12585
  { name: "suppression_list_ids", type: "array", required: false, description: "Replacement suppression list IDs" },
12544
12586
  { name: "time_windows", type: "array", required: false, description: "Replacement execution time windows" },
12545
12587
  { name: "blocked_dates", type: "array", required: false, description: "Replacement blocked date ranges" }
@@ -16949,6 +16991,10 @@ var OPERATION_TYPE_FOR_PROPERTY_TYPE2 = {
16949
16991
  date: "TIME_POINT",
16950
16992
  datetime: "TIME_POINT"
16951
16993
  };
16994
+ var ENUMERATION_INVALID_OPERATORS = /* @__PURE__ */ new Set([
16995
+ "IS_EQUAL_TO",
16996
+ "IS_NOT_EQUAL_TO"
16997
+ ]);
16952
16998
  var isCompatibleEnrollmentOperationType = (operationType, expectedOperationType, propertyType) => operationType === expectedOperationType || operationType === "ALL_PROPERTY" || operationType === "TIME_RANGED" && (propertyType === "date" || propertyType === "datetime");
16953
16999
  var collectPropertyFiltersFromBranches = (branches, parentObjectTypeId, parentEventTypeId) => {
16954
17000
  const refs = [];
@@ -16963,6 +17009,7 @@ var collectPropertyFiltersFromBranches = (branches, parentObjectTypeId, parentEv
16963
17009
  refs.push({
16964
17010
  property: filter.property,
16965
17011
  operationType: operation.operationType,
17012
+ operator: typeof operation.operator === "string" ? operation.operator : void 0,
16966
17013
  objectTypeId: contextObjectTypeId,
16967
17014
  eventTypeId: branchEventTypeId
16968
17015
  });
@@ -17029,6 +17076,15 @@ var validateWorkflowEnrollmentMetadata = (params) => {
17029
17076
  });
17030
17077
  continue;
17031
17078
  }
17079
+ if (filterRef.eventTypeId === "4-655002") {
17080
+ add({
17081
+ severity: "error",
17082
+ operation_index: operationIndex,
17083
+ code: "INVALID_ENROLLMENT_REFINEMENT_FILTER",
17084
+ message: `eventTypeId '4-655002' only supports event refinement filters like hs_name and hs_value. Property '${filterRef.property}' should not appear in that event branch; move it into LIST_BASED criteria or use enrollment_trigger.`
17085
+ });
17086
+ continue;
17087
+ }
17032
17088
  if (!enabledObjectTypeIds.has(filterRef.objectTypeId)) continue;
17033
17089
  const propertyDefinitions = propertyDefinitionsByObjectType.get(filterRef.objectTypeId);
17034
17090
  const propertyDefinition = propertyDefinitions?.get(filterRef.property);
@@ -17043,7 +17099,24 @@ var validateWorkflowEnrollmentMetadata = (params) => {
17043
17099
  }
17044
17100
  const expectedOperationType = OPERATION_TYPE_FOR_PROPERTY_TYPE2[propertyDefinition.type];
17045
17101
  if (!expectedOperationType) continue;
17102
+ if (filterRef.eventTypeId === void 0 && (filterRef.operator === "IS_KNOWN" || filterRef.operator === "IS_UNKNOWN") && filterRef.operationType === "TIME_POINT" && (propertyDefinition.type === "date" || propertyDefinition.type === "datetime")) {
17103
+ add({
17104
+ severity: "error",
17105
+ operation_index: operationIndex,
17106
+ code: "INVALID_ENROLLMENT_FILTER_OPERATION_TYPE",
17107
+ message: `Workflow enrollment filter property '${filterRef.property}' uses operator '${filterRef.operator}' with operationType 'TIME_POINT'. For LIST_BASED property existence checks, use operationType 'ALL_PROPERTY' instead.`
17108
+ });
17109
+ continue;
17110
+ }
17046
17111
  if (isCompatibleEnrollmentOperationType(filterRef.operationType, expectedOperationType, propertyDefinition.type)) {
17112
+ if (propertyDefinition.type === "enumeration" && filterRef.operator !== void 0 && ENUMERATION_INVALID_OPERATORS.has(filterRef.operator)) {
17113
+ add({
17114
+ severity: "error",
17115
+ operation_index: operationIndex,
17116
+ code: "INVALID_ENROLLMENT_FILTER_OPERATOR",
17117
+ message: `Workflow enrollment filter property '${filterRef.property}' has type 'enumeration' but uses operator '${filterRef.operator}'. Use enumeration operators like IS_ANY_OF, IS_NONE_OF, IS_EXACTLY, or IS_NOT_EXACTLY.`
17118
+ });
17119
+ }
17047
17120
  continue;
17048
17121
  }
17049
17122
  add({
@@ -17166,6 +17239,42 @@ var validateDelayUntilDateAction = (params) => {
17166
17239
  }
17167
17240
  return issues;
17168
17241
  };
17242
+ var validateDelayUntilDateBranching = (params) => {
17243
+ const { actions, operationIndex } = params;
17244
+ const issues = [];
17245
+ const actionsById = new Map(actions.flatMap(
17246
+ (action) => typeof action.actionId === "string" && action.actionId.length > 0 ? [[action.actionId, action]] : []
17247
+ ));
17248
+ for (const action of actions) {
17249
+ if (action.actionTypeId !== "0-35") continue;
17250
+ const delayActionId = action.actionId ?? "<missing>";
17251
+ const branchActionId = action.connection?.nextActionId;
17252
+ if (!branchActionId) continue;
17253
+ const branchAction = actionsById.get(branchActionId);
17254
+ if (!branchAction || branchAction.type !== "STATIC_BRANCH") {
17255
+ issues.push({
17256
+ severity: "error",
17257
+ operation_index: operationIndex,
17258
+ code: "WORKFLOW_DELAY_UNTIL_DATE_BRANCH_REQUIRED",
17259
+ message: `Action '${delayActionId}' (0-35 delay until date) must flow into a STATIC_BRANCH that reads hs_delay_status before continuing. Do not connect the delay action directly to the next business action.`
17260
+ });
17261
+ continue;
17262
+ }
17263
+ const inputValue = branchAction.inputValue;
17264
+ const hasExpectedInputValue = inputValue?.actionId === action.actionId && inputValue?.dataKey === "hs_delay_status" && inputValue?.type === "FIELD_DATA";
17265
+ const hasDateMetAsPlannedBranch = Array.isArray(branchAction.staticBranches) && branchAction.staticBranches.some(
17266
+ (branch) => isRecord(branch) && branch.branchValue === "DATE_MET_AS_PLANNED" && typeof getNestedNextActionId(branch) === "string"
17267
+ );
17268
+ if (hasExpectedInputValue && hasDateMetAsPlannedBranch) continue;
17269
+ issues.push({
17270
+ severity: "error",
17271
+ operation_index: operationIndex,
17272
+ code: "WORKFLOW_DELAY_UNTIL_DATE_BRANCH_REQUIRED",
17273
+ message: `Action '${delayActionId}' (0-35 delay until date) must be followed by a STATIC_BRANCH with inputValue { actionId: '${delayActionId}', dataKey: 'hs_delay_status', type: 'FIELD_DATA' } and a DATE_MET_AS_PLANNED branch connection.`
17274
+ });
17275
+ }
17276
+ return issues;
17277
+ };
17169
17278
  var validateRotateToOwnerAction = (params) => {
17170
17279
  const { action, actionId, operationIndex } = params;
17171
17280
  if (action.actionTypeId !== "0-11") return [];
@@ -17312,6 +17421,7 @@ var validateWorkflowActions = (params) => {
17312
17421
  issues.push(...validateRotateToOwnerAction({ action, actionId, operationIndex }));
17313
17422
  issues.push(...validateCreateTaskAssociations({ action, actionId, operationIndex, context }));
17314
17423
  }
17424
+ issues.push(...validateDelayUntilDateBranching({ actions, operationIndex }));
17315
17425
  for (const action of actions) {
17316
17426
  for (const nextActionId of collectActionNextActionIds(action)) {
17317
17427
  if (actionIds.has(nextActionId)) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daeda/mcp-pro",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "description": "MCP server for HubSpot CRM — sync, query, and manage your portal data",
5
5
  "type": "module",
6
6
  "bin": {