@sendora/mcp 1.11.0 → 1.12.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.
@@ -360,7 +360,7 @@ button:focus-visible {
360
360
  }
361
361
 
362
362
  // src/version.ts
363
- var MCP_VERSION = "1.11.0";
363
+ var MCP_VERSION = "1.12.0";
364
364
 
365
365
  // screens/host.ts
366
366
  var PROTOCOL_VERSION = "2026-01-26";
@@ -360,7 +360,7 @@ button:focus-visible {
360
360
  }
361
361
 
362
362
  // src/version.ts
363
- var MCP_VERSION = "1.11.0";
363
+ var MCP_VERSION = "1.12.0";
364
364
 
365
365
  // screens/host.ts
366
366
  var PROTOCOL_VERSION = "2026-01-26";
@@ -10,6 +10,7 @@ import { getServerTool } from './tools/server.js';
10
10
  import { deleteTestServerTool, listServersTool } from './tools/servers.js';
11
11
  import { archiveStreamTool, createStreamTool } from './tools/streams.js';
12
12
  import { listSuppressionsTool, removeSuppressionTool } from './tools/suppressions.js';
13
+ import { addTrackingOptOutTool, listTrackingOptOutsTool, removeTrackingOptOutTool, } from './tools/tracking-opt-outs.js';
13
14
  import { addTrackingDomainTool, checkTrackingDomainTool, listTrackingDomainsTool, } from './tools/tracking-domains.js';
14
15
  import { createWebhookTool, deleteWebhookTool, listWebhookDeliveriesTool, listWebhooksTool, replayWebhookDeliveryTool, } from './tools/webhooks.js';
15
16
  /**
@@ -26,6 +27,9 @@ export const toolTable = [
26
27
  getMessageTool,
27
28
  listSuppressionsTool,
28
29
  removeSuppressionTool,
30
+ listTrackingOptOutsTool,
31
+ addTrackingOptOutTool,
32
+ removeTrackingOptOutTool,
29
33
  createStreamTool,
30
34
  archiveStreamTool,
31
35
  sendBroadcastTool,
@@ -0,0 +1,135 @@
1
+ import { z } from 'zod';
2
+ import { toolError } from '../errors.js';
3
+ import { READING } from '../tool.js';
4
+ import { modesOf, registerServerTool } from '../target.js';
5
+ export const trackingOptOutOutput = z.object({
6
+ address: z
7
+ .string()
8
+ .nullable()
9
+ .describe('The recipient, lower-cased. Null once an erasure has left only a keyed hash.'),
10
+ source: z
11
+ .enum(['manual', 'recipient'])
12
+ .describe('manual: added through the API or the dashboard. recipient: the recipient asked for it.'),
13
+ messageId: z
14
+ .string()
15
+ .nullable()
16
+ .describe('The message that a recipient asked from. Null on a manual entry, and once that message or the address has gone.'),
17
+ createdAt: z.string().describe('When it was put on the list.'),
18
+ });
19
+ export const removedTrackingOptOutOutput = z.object({
20
+ address: z.string().describe('The address taken off the list.'),
21
+ });
22
+ const NOT_OPEN_YET = 'Tracking is not open to accounts yet, so the list changes nothing until it is.';
23
+ export const listTrackingOptOutsTool = {
24
+ name: 'list_tracking_opt_outs',
25
+ title: 'List the not-tracked addresses',
26
+ class: 'read',
27
+ reach: 'server',
28
+ operations: ['GET /v1/tracking-opt-outs'],
29
+ register: (server, { connection, targets }) => registerServerTool(server, targets, 'list_tracking_opt_outs', {
30
+ title: 'List the not-tracked addresses',
31
+ description: `Lists the addresses this server never tracks, on any of its streams, newest first. An entry whose address is null was erased, and only a keyed hash of it remains. ${NOT_OPEN_YET}`,
32
+ inputSchema: z.object({
33
+ limit: z
34
+ .number()
35
+ .int()
36
+ .min(1)
37
+ .max(100)
38
+ .default(20)
39
+ .describe('Addresses per page, 1 to 100.'),
40
+ after: z.uuid().optional().describe('The next value of the previous page.'),
41
+ }),
42
+ outputSchema: z.object({
43
+ trackingOptOuts: z.array(trackingOptOutOutput).describe('The page of the list.'),
44
+ next: z
45
+ .string()
46
+ .nullable()
47
+ .describe('Pass as after for the next page; null on the last.'),
48
+ }),
49
+ annotations: READING,
50
+ }, async (query, { sendora }) => {
51
+ try {
52
+ const page = await sendora.trackingOptOuts.list(query);
53
+ return {
54
+ content: [{ type: 'text', text: JSON.stringify(page) }],
55
+ structuredContent: page,
56
+ };
57
+ }
58
+ catch (error) {
59
+ return toolError(error, connection);
60
+ }
61
+ }),
62
+ };
63
+ export const addTrackingOptOutTool = {
64
+ name: 'add_tracking_opt_out',
65
+ title: 'Add a not-tracked address',
66
+ class: 'write',
67
+ reach: 'server',
68
+ operations: ['POST /v1/tracking-opt-outs'],
69
+ register: (server, { connection, targets }) => registerServerTool(server, targets, 'add_tracking_opt_out', {
70
+ title: 'Add a not-tracked address',
71
+ description: `Puts an address on this server's not-tracked list. New mail to it goes untracked, and its later opens and clicks are not recorded, from mail already sent too. An address already on the list changes nothing, and the answer is its entry as it stands. ${NOT_OPEN_YET}`,
72
+ inputSchema: z.object({
73
+ address: z.email().describe('The address never to track.'),
74
+ }),
75
+ outputSchema: trackingOptOutOutput,
76
+ annotations: {
77
+ readOnlyHint: false,
78
+ destructiveHint: false,
79
+ idempotentHint: true,
80
+ openWorldHint: false,
81
+ },
82
+ }, async ({ address }, { sendora }) => {
83
+ try {
84
+ const entry = await sendora.trackingOptOuts.add({ address });
85
+ return {
86
+ content: [{ type: 'text', text: JSON.stringify(entry) }],
87
+ structuredContent: entry,
88
+ };
89
+ }
90
+ catch (error) {
91
+ return toolError(error, connection);
92
+ }
93
+ }),
94
+ };
95
+ const REMOVE_DESCRIPTION = "Takes an address off this server's not-tracked list, so its mail can be tracked again. Remove an entry whose source is recipient only after the recipient has given new consent. An erased entry is found by its address too.";
96
+ function removeDescription(modes) {
97
+ if (!modes.includes('live')) {
98
+ return REMOVE_DESCRIPTION;
99
+ }
100
+ const server = modes.includes('test') ? 'a live server' : 'this live server';
101
+ return `${REMOVE_DESCRIPTION} On ${server} the address is a real person's: confirm with the user first.`;
102
+ }
103
+ export const removeTrackingOptOutTool = {
104
+ name: 'remove_tracking_opt_out',
105
+ title: 'Remove a not-tracked address',
106
+ class: 'write',
107
+ reach: 'server',
108
+ operations: ['POST /v1/tracking-opt-outs/delete'],
109
+ register: (server, { connection, targets }) => registerServerTool(server, targets, 'remove_tracking_opt_out', {
110
+ title: 'Remove a not-tracked address',
111
+ description: removeDescription(modesOf(targets)),
112
+ inputSchema: z.object({
113
+ address: z.email().describe('The address to track again.'),
114
+ }),
115
+ outputSchema: removedTrackingOptOutOutput,
116
+ annotations: {
117
+ readOnlyHint: false,
118
+ destructiveHint: false,
119
+ idempotentHint: true,
120
+ openWorldHint: false,
121
+ },
122
+ }, async ({ address }, { sendora }) => {
123
+ try {
124
+ await sendora.trackingOptOuts.delete({ address });
125
+ }
126
+ catch (error) {
127
+ return toolError(error, connection);
128
+ }
129
+ const output = { address };
130
+ return {
131
+ content: [{ type: 'text', text: JSON.stringify(output) }],
132
+ structuredContent: output,
133
+ };
134
+ }),
135
+ };
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** This package's version, held equal to its package.json by a test. */
2
- export const MCP_VERSION = '1.11.0';
2
+ export const MCP_VERSION = '1.12.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendora/mcp",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "The official MCP server for Sendora: its documentation and API for coding agents.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "dependencies": {
26
26
  "@modelcontextprotocol/server": "^2.1.0",
27
27
  "zod": "^4.6.2",
28
- "@sendora/sdk": "^5.3.0"
28
+ "@sendora/sdk": "^5.4.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@anthropic-ai/mcpb": "2.1.2",