Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e2cfeea

Browse files
committed
feat: Implement new API endpoints and MCP tools for funnels, A/B tests, and campaigns, alongside message and image proxy functionality.
1 parent 8f80e6b commit e2cfeea

41 files changed

Lines changed: 4459 additions & 20 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
99

1010
### Added
1111

12+
- **MCP Email Campaign & Automation:**
13+
14+
- **Campaign Management:** Added MCP tools and API endpoints for full email campaign lifecycle:
15+
- Create, update, delete campaigns (messages).
16+
- Manage recipient lists and exclusions.
17+
- Schedule and send campaigns.
18+
- View campaign statistics.
19+
- **A/B Testing:** Implemented comprehensive A/B testing capabilities via MCP:
20+
- Create and manage A/B tests.
21+
- Support for multiple variants (Subject, Content, Sender, etc.).
22+
- Tools for start, end, and retrieve test results.
23+
- **Automation Funnels:** Added tools for managing automation sequences:
24+
- Create funnels with triggers (List Signup, Tag Added, etc.).
25+
- Add steps (Email, Delay, Condition).
26+
- Activate and pause funnels.
27+
- **Extended Client:** Updated `@netsendo/mcp-client` with 25 new tools and corresponding API methods.
28+
1229
- **MCP Key Management:**
1330
- **Encrypted Storage:** Implemented secure storage for MCP-designated API keys using Laravel's encryption. Plain keys are encrypted and stored in the database, allowing retrieval for automated testing.
1431
- **Hybrid Connection Testing:** Updated `mcp:test-connection` command to support both standard HTTP testing and internal fallback verification.
@@ -27,12 +44,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
2744

2845
- **MCP Connection Test:** Fixed failure in Docker environments where internal networking prevented the test command from reaching the API endpoint. Added fallback mechanism to verify key validity directly against the database.
2946

47+
- **MCP Key for Existing API Keys:** Fixed issue where editing an existing API key to mark it as MCP would not allow connection testing because the plain key was not stored. Added an input field in the API Key edit modal to provide the plain key for encryption when marking an existing key as MCP.
48+
3049
- **Email Editor Image Editing:**
50+
3151
- Fixed an issue where images in full HTML documents (e.g., templates with imported footers/inserts) were not editable in preview mode as they are rendered inside an iframe.
3252
- Implemented double-click handling for images within the preview iframe to open the image editing modal.
3353
- Added synchronization between the image editing modal and the preview iframe for real-time updates of image properties (width, alignment, float, margin, border-radius).
3454
- Added visual hover effects to clearly indicate editable images in preview mode.
3555

56+
- **Template Builder - CORS Image Proxy:**
57+
- Fixed thumbnail generation failing silently when templates contain external images from domains without CORS headers.
58+
- Implemented server-side image proxy (`api.templates.proxy-image`) that fetches external images and returns them with proper CORS headers.
59+
- Updated `Builder.vue` to automatically route external images through the proxy during thumbnail generation.
60+
- Added security measures: MIME type validation, file size limits (5MB), blocked local/internal URLs, and response caching (1 hour).
61+
3662
## [1.7.5] – Short Description
3763

3864
**Release date:** 2026-01-17

docs/mcp-server.md

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,55 @@ You can connect to multiple NetSendo instances by using different server names:
182182

183183
## Available Tools
184184

185-
| Category | Tools |
186-
| ---------------- | --------------------------------------------------- |
187-
| **Subscribers** | List, get, create, update, delete, sync tags |
188-
| **Lists & Tags** | List contact lists, get list subscribers, list tags |
189-
| **Messaging** | Send email, send SMS, check delivery status |
190-
| **Account** | Test connection, get account info |
185+
| Category | Tools |
186+
| ---------------- | -------------------------------------------------------------------------------------- |
187+
| **Subscribers** | List, get, create, update, delete, sync tags |
188+
| **Lists & Tags** | List contact lists, get list subscribers, list tags |
189+
| **Messaging** | Send email, send SMS, check delivery status |
190+
| **Campaigns** | List, create, update, delete campaigns; set lists/exclusions; schedule/send; get stats |
191+
| **A/B Tests** | Create tests, add variants, start/end tests, get results |
192+
| **Funnels** | Create automation funnels, add steps, activate/pause, get stats |
193+
| **Account** | Test connection, get account info |
194+
195+
### Campaign Tools (NEW)
196+
197+
| Tool | Description |
198+
| ------------------------- | --------------------------------- |
199+
| `list_campaigns` | List all campaigns with filtering |
200+
| `get_campaign` | Get campaign details |
201+
| `create_campaign` | Create new email/SMS campaign |
202+
| `update_campaign` | Update campaign content/settings |
203+
| `set_campaign_lists` | Set recipient lists |
204+
| `set_campaign_exclusions` | Set exclusion lists |
205+
| `schedule_campaign` | Schedule for future sending |
206+
| `send_campaign` | Send immediately or activate |
207+
| `get_campaign_stats` | Get sending statistics |
208+
| `delete_campaign` | Delete a campaign |
209+
210+
### A/B Test Tools (NEW)
211+
212+
| Tool | Description |
213+
| --------------------- | -------------------------- |
214+
| `list_ab_tests` | List all A/B tests |
215+
| `create_ab_test` | Create test for a campaign |
216+
| `add_ab_variant` | Add test variant |
217+
| `start_ab_test` | Start running test |
218+
| `end_ab_test` | End test and pick winner |
219+
| `get_ab_test_results` | Get test results |
220+
| `delete_ab_test` | Delete a test |
221+
222+
### Funnel Tools (NEW)
223+
224+
| Tool | Description |
225+
| ------------------ | ---------------------------- |
226+
| `list_funnels` | List automation funnels |
227+
| `get_funnel` | Get funnel details and steps |
228+
| `create_funnel` | Create new funnel |
229+
| `add_funnel_step` | Add step to funnel |
230+
| `activate_funnel` | Activate funnel |
231+
| `pause_funnel` | Pause funnel |
232+
| `get_funnel_stats` | Get funnel statistics |
233+
| `delete_funnel` | Delete a funnel |
191234

192235
## Security
193236

mcp/dist/api-client.d.ts

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* HTTP client for communicating with NetSendo REST API v1
55
*/
66
import type { Config } from './config.js';
7-
import type { Subscriber, SubscriberCreateInput, SubscriberUpdateInput, ContactList, Tag, EmailSendInput, EmailStatus, Mailbox, SmsSendInput, SmsStatus, SmsProvider, CustomField, PaginatedResponse } from './types.js';
7+
import type { Subscriber, SubscriberCreateInput, SubscriberUpdateInput, ContactList, Tag, EmailSendInput, EmailStatus, Mailbox, SmsSendInput, SmsStatus, SmsProvider, CustomField, PaginatedResponse, Message, MessageCreateInput, MessageUpdateInput, MessageStats, AbTest, AbTestCreateInput, AbTestVariant, AbTestVariantInput, AbTestVariantResult, Funnel, FunnelCreateInput, FunnelStep, FunnelStepInput, FunnelStats } from './types.js';
88
export declare class NetSendoApiError extends Error {
99
statusCode: number;
1010
errors?: Record<string, string[]> | undefined;
@@ -51,6 +51,95 @@ export declare class NetSendoApiClient {
5151
}>;
5252
getSmsStatus(id: string): Promise<SmsStatus>;
5353
listSmsProviders(): Promise<SmsProvider[]>;
54+
listMessages(params?: {
55+
page?: number;
56+
per_page?: number;
57+
channel?: 'email' | 'sms';
58+
type?: 'broadcast' | 'autoresponder';
59+
status?: string;
60+
search?: string;
61+
}): Promise<PaginatedResponse<Message>>;
62+
getMessage(id: number): Promise<Message>;
63+
createMessage(data: MessageCreateInput): Promise<Message>;
64+
updateMessage(id: number, data: MessageUpdateInput): Promise<Message>;
65+
deleteMessage(id: number): Promise<void>;
66+
setMessageLists(id: number, contactListIds: number[]): Promise<{
67+
message: Message;
68+
planned_recipients: number;
69+
}>;
70+
setMessageExclusions(id: number, excludedListIds: number[]): Promise<{
71+
message: Message;
72+
planned_recipients: number;
73+
}>;
74+
scheduleMessage(id: number, scheduledAt: string): Promise<Message>;
75+
sendMessage(id: number): Promise<{
76+
message: Message;
77+
recipients_added?: number;
78+
}>;
79+
getMessageStats(id: number): Promise<MessageStats>;
80+
listAbTests(params?: {
81+
page?: number;
82+
per_page?: number;
83+
status?: string;
84+
message_id?: number;
85+
}): Promise<PaginatedResponse<AbTest>>;
86+
getAbTest(id: number): Promise<AbTest>;
87+
createAbTest(data: AbTestCreateInput): Promise<AbTest>;
88+
addAbTestVariant(testId: number, data: AbTestVariantInput): Promise<AbTestVariant>;
89+
startAbTest(id: number): Promise<{
90+
test: AbTest;
91+
ends_at: string;
92+
}>;
93+
endAbTest(id: number, winnerVariantId?: number): Promise<{
94+
test: AbTest;
95+
winner: {
96+
variant_letter: string;
97+
id: number;
98+
} | null;
99+
}>;
100+
getAbTestResults(id: number): Promise<{
101+
test_id: number;
102+
name: string;
103+
status: string;
104+
test_type: string;
105+
winning_metric: string;
106+
test_started_at: string | null;
107+
test_ended_at: string | null;
108+
winner: {
109+
variant_letter: string;
110+
id: number;
111+
} | null;
112+
results: Record<string, AbTestVariantResult>;
113+
}>;
114+
deleteAbTest(id: number): Promise<void>;
115+
listFunnels(params?: {
116+
page?: number;
117+
per_page?: number;
118+
status?: string;
119+
trigger_type?: string;
120+
search?: string;
121+
}): Promise<PaginatedResponse<Funnel>>;
122+
getFunnel(id: number): Promise<Funnel & {
123+
stats: FunnelStats;
124+
}>;
125+
createFunnel(data: FunnelCreateInput): Promise<Funnel>;
126+
updateFunnel(id: number, data: Partial<FunnelCreateInput>): Promise<Funnel>;
127+
addFunnelStep(funnelId: number, data: FunnelStepInput): Promise<FunnelStep>;
128+
activateFunnel(id: number): Promise<Funnel>;
129+
pauseFunnel(id: number): Promise<Funnel>;
130+
getFunnelStats(id: number): Promise<{
131+
id: number;
132+
name: string;
133+
status: string;
134+
stats: FunnelStats;
135+
trigger: {
136+
type: string;
137+
list?: string;
138+
form?: string;
139+
tag?: string;
140+
};
141+
}>;
142+
deleteFunnel(id: number): Promise<void>;
54143
getAccountInfo(): Promise<{
55144
name: string;
56145
email: string;

mcp/dist/api-client.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)