Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ Stripe product filter examples:
{ "field": "stripeProduct", "operator": "is_not", "value": "prod_pro" }
{ "field": "stripeProduct", "operator": "at_least", "value": "prod_pro:3" }
{ "field": "stripeProduct", "operator": "less_than_count", "value": "prod_pro:3" }
{ "field": "stripeCurrentProduct", "operator": "is", "value": "prod_pro" }
{ "field": "stripeTrialProduct", "operator": "is", "value": "prod_pro" }
{ "field": "stripeTrialProduct", "operator": "is", "value": "prod_pro:is_canceled" }
{ "field": "stripeTrialProduct", "operator": "gte", "value": "prod_pro:start_at:7 days ago" }
{ "field": "stripeTrialProduct", "operator": "is", "value": "prod_pro:end_at:2026-05-26" }
```

Engagement fields such as `emailSent`, `emailOpened`, `emailClicked`, `emailBounced`, and `emailComplained` accept rolling windows like `7d`, `30d`, `90d`, `180d`, `all`, or a campaign scope like `campaign:cmp_123`.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sequenzy/mcp",
"version": "0.0.44",
"version": "0.0.45",
"mcpName": "io.github.sequenzy/mcp",
"description": "Sequenzy MCP server for AI-powered email marketing automation",
"type": "module",
Expand Down
4 changes: 2 additions & 2 deletions server.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
"url": "https://github.com/Sequenzy/mcp",
"source": "github"
},
"version": "0.0.44",
"version": "0.0.45",
"packages": [
{
"registryType": "npm",
"identifier": "@sequenzy/mcp",
"version": "0.0.44",
"version": "0.0.45",
"runtime": "node",
"transport": {
"type": "stdio"
Expand Down
4 changes: 4 additions & 0 deletions src/tools/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1697,6 +1697,10 @@ describe("create_segment tool", () => {
"stripeTrialProduct",
])
);
const fieldEnum =
inputSchema?.properties?.filters?.items?.properties?.field?.enum;
expect(fieldEnum).not.toContain("stripeTrialStarted");
expect(fieldEnum).not.toContain("stripeTrialEnds");
});

it("rejects create_segment calls without filters or root before hitting the API", async () => {
Expand Down
8 changes: 4 additions & 4 deletions src/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ const segmentFilterItemSchema = {
"lt",
],
description:
"Filter operator. Use `is_empty` or `is_not_empty` for fields that can be blank, including custom attributes. For `stripeProduct`, use `is` or `is_not` with a raw product ID, and `at_least` or `less_than_count` with `productId:count`. For `event`, use `is`, `is_not`, `at_least`, or `less_than_count`. For engagement fields, use `is` (event happened) or `is_not` (event did not happen).",
"Filter operator. Use `is_empty` or `is_not_empty` for fields that can be blank, including custom attributes. For `stripeProduct`, use `is` or `is_not` with a raw product ID, and `at_least` or `less_than_count` with `productId:count`. For `stripeCurrentProduct` and `stripeTrialProduct`, use a raw product ID for current/trialing checks, or product-scoped values like `productId:is_canceled`, `productId:cancels_at:2026-05-26`, `productId:end_at:2026-05-26`, and `productId:start_at:7 days ago` with `is`, `is_not`, `gte`, `lte`, `gt`, or `lt` as appropriate. For `event`, use `is`, `is_not`, `at_least`, or `less_than_count`. For engagement fields, use `is` (event happened) or `is_not` (event did not happen).",
},
value: {
type: "string",
description:
"Filter value. For custom attribute empty checks, use `attributeName:` such as `last_logged_in:`. Event examples: `saas.purchase:30d`, `saas.purchase:all`, or `saas.purchase:5:30d` for thresholds. Segment values are segment IDs. Stripe product examples: `prod_123` for bought/didn't buy, `prod_123:3` for payment thresholds. Engagement examples: `7d`, `30d`, `90d`, `180d`, `all` for rolling time windows, or `campaign:<campaign_id>` to scope to a specific sent campaign (use `list_campaigns` to find IDs).",
"Filter value. For custom attribute empty checks, use `attributeName:` such as `last_logged_in:`. Event examples: `saas.purchase:30d`, `saas.purchase:all`, or `saas.purchase:5:30d` for thresholds. Segment values are segment IDs. Stripe product examples: `prod_123` for bought/didn't buy/current/trialing, `prod_123:3` for payment thresholds, `prod_123:is_canceled` for products set to cancel, `prod_123:cancels_at:2026-05-26`, `prod_123:end_at:2026-05-26`, or `prod_123:start_at:7 days ago` for product-scoped dates. Engagement examples: `7d`, `30d`, `90d`, `180d`, `all` for rolling time windows, or `campaign:<campaign_id>` to scope to a specific sent campaign (use `list_campaigns` to find IDs).",
},
},
required: ["field", "operator", "value"],
Expand Down Expand Up @@ -1572,7 +1572,7 @@ Before implementing, use create_api_key to generate an API key and save it to .e
{
name: "create_segment",
description:
'Create a new segment from explicit filter rules. Use `filters` plus `filterJoinOperator` for flat legacy rules, or `root` for nested AND/OR groups such as `{ "kind": "group", "joinOperator": "and", "children": [{ "kind": "filter", "field": "attribute", "operator": "gte", "value": "mrr:50" }, { "kind": "group", "joinOperator": "or", "children": [{ "kind": "filter", "field": "tag", "operator": "contains", "value": "vip" }, { "kind": "filter", "field": "event", "operator": "is_not", "value": "saas.purchase:30d" }] }] }`. Supports `event` and `segment` fields, Stripe product purchase filters, and campaign-specific engagement filters.',
'Create a new segment from explicit filter rules. Use `filters` plus `filterJoinOperator` for flat legacy rules, or `root` for nested AND/OR groups such as `{ "kind": "group", "joinOperator": "and", "children": [{ "kind": "filter", "field": "attribute", "operator": "gte", "value": "mrr:50" }, { "kind": "group", "joinOperator": "or", "children": [{ "kind": "filter", "field": "tag", "operator": "contains", "value": "vip" }, { "kind": "filter", "field": "event", "operator": "is_not", "value": "saas.purchase:30d" }] }] }`. Supports `event` and `segment` fields, Stripe product purchase/current/trial/date filters, and campaign-specific engagement filters.',
inputSchema: {
type: "object",
properties: {
Expand All @@ -1596,7 +1596,7 @@ Before implementing, use create_api_key to generate an API key and save it to .e
items: segmentFilterItemSchema,
minItems: 1,
description:
'Array of segment filters. Example custom attribute empty check: [{"id":"filter-1","field":"attribute","operator":"is_empty","value":"last_logged_in:"}]. Example Stripe purchase filter: [{"id":"filter-1","field":"stripeProduct","operator":"is","value":"prod_123"}]. Example threshold filter: [{"id":"filter-1","field":"stripeProduct","operator":"at_least","value":"prod_123:3"}]. Example campaign-specific engagement combo: [{"id":"filter-1","field":"emailBounced","operator":"is","value":"campaign:cmp_abc"},{"id":"filter-2","field":"emailBounced","operator":"is_not","value":"campaign:cmp_xyz"}]. Combine them with `filterJoinOperator: "or"` to match any filter.',
'Array of segment filters. Example custom attribute empty check: [{"id":"filter-1","field":"attribute","operator":"is_empty","value":"last_logged_in:"}]. Example Stripe purchase filter: [{"id":"filter-1","field":"stripeProduct","operator":"is","value":"prod_123"}]. Example threshold filter: [{"id":"filter-1","field":"stripeProduct","operator":"at_least","value":"prod_123:3"}]. Example trial cancellation filter: [{"id":"filter-1","field":"stripeTrialProduct","operator":"is","value":"prod_123:is_canceled"}]. Example trial end filter: [{"id":"filter-1","field":"stripeTrialProduct","operator":"is","value":"prod_123:end_at:2026-05-26"}]. Example campaign-specific engagement combo: [{"id":"filter-1","field":"emailBounced","operator":"is","value":"campaign:cmp_abc"},{"id":"filter-2","field":"emailBounced","operator":"is_not","value":"campaign:cmp_xyz"}]. Combine them with `filterJoinOperator: "or"` to match any filter.',
},
root: {
...segmentFilterGroupSchema,
Expand Down