@sendinel/mcp-server 1.0.217 → 1.0.218

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.
@@ -63,10 +63,14 @@ export declare function formatZodError(error: z.ZodError | {
63
63
  [key: string]: any;
64
64
  }>;
65
65
  }): string;
66
- /** Machine-code classification for api_usage only. Never rewrite response codes.
66
+ /** Machine-code classification shared by tool results and api_usage. Never rewrite legacy codes.
67
67
  * Keys are case-insensitive; every supported legacy spelling is listed explicitly.
68
68
  * Existing usage-only policy classes remain supported alongside the six tool classes. */
69
69
  export declare const API_USAGE_ERROR_CODE_MAP: Readonly<Record<string, string>>;
70
+ /** One classifier for the additive response class and hosted usage tracking. */
71
+ export declare function classifyMachineCode(code: string): string | null;
72
+ /** Classify a failed body, preserving any explicitly supplied error_code. */
73
+ export declare function toolErrorClass(body: Record<string, unknown>): unknown;
70
74
  type ToolResult = {
71
75
  content: Array<{
72
76
  type: string;
@@ -99,7 +99,7 @@ export function formatZodError(error) {
99
99
  }
100
100
  return messages.join(' ');
101
101
  }
102
- /** Machine-code classification for api_usage only. Never rewrite response codes.
102
+ /** Machine-code classification shared by tool results and api_usage. Never rewrite legacy codes.
103
103
  * Keys are case-insensitive; every supported legacy spelling is listed explicitly.
104
104
  * Existing usage-only policy classes remain supported alongside the six tool classes. */
105
105
  export const API_USAGE_ERROR_CODE_MAP = {
@@ -111,6 +111,7 @@ export const API_USAGE_ERROR_CODE_MAP = {
111
111
  'bad_request': 'validation_failed',
112
112
  'campaigns_required': 'validation_failed',
113
113
  'content_required': 'validation_failed',
114
+ 'content_not_ready': 'validation_failed',
114
115
  'empty_audience': 'validation_failed',
115
116
  'empty_body': 'validation_failed',
116
117
  'empty_cohort': 'validation_failed',
@@ -146,6 +147,7 @@ export const API_USAGE_ERROR_CODE_MAP = {
146
147
  'validation_failed': 'validation_failed',
147
148
  'approval_not_cancellable': 'conflict',
148
149
  'approval_not_pending': 'conflict',
150
+ 'already_sent_to_audience': 'conflict',
149
151
  'human_decision_required': 'forbidden',
150
152
  '23505': 'conflict',
151
153
  'conflict': 'conflict',
@@ -209,10 +211,32 @@ export const API_USAGE_ERROR_CODE_MAP = {
209
211
  'step_create_failed': 'internal_error',
210
212
  'step_lookup_failed': 'internal_error',
211
213
  'survey_link_not_ready': 'internal_error',
214
+ 'project_connector_unavailable': 'internal_error',
212
215
  'sandbox_blocked': 'sandbox_blocked',
213
216
  'send_allowlist_refused': 'send_allowlist_refused',
214
217
  'rate_limited': 'rate_limited',
215
218
  };
219
+ /** One classifier for the additive response class and hosted usage tracking. */
220
+ export function classifyMachineCode(code) {
221
+ const lower = code.toLowerCase().trim();
222
+ if (Object.hasOwn(API_USAGE_ERROR_CODE_MAP, lower))
223
+ return API_USAGE_ERROR_CODE_MAP[lower];
224
+ if (lower.endsWith('_not_found'))
225
+ return 'not_found';
226
+ if (lower.endsWith('_conflict'))
227
+ return 'conflict';
228
+ if (lower.endsWith('_limit_reached') || lower.endsWith('_limit'))
229
+ return 'plan_limit';
230
+ return null;
231
+ }
232
+ /** Classify a failed body, preserving any explicitly supplied error_code. */
233
+ export function toolErrorClass(body) {
234
+ if ('error_code' in body)
235
+ return body.error_code;
236
+ if (typeof body.code !== 'string')
237
+ return 'internal_error';
238
+ return classifyMachineCode(body.code);
239
+ }
216
240
  /** Add a stable class to refusals without dropping existing fields or success data. */
217
241
  export function codedToolResult(result) {
218
242
  let failed = result.isError === true;
@@ -242,18 +266,21 @@ export function codedToolResult(result) {
242
266
  return block;
243
267
  failed = true;
244
268
  // An existing code is a frozen client surface, even when it is unfamiliar.
245
- if ('code' in body)
246
- return block;
269
+ if ('code' in body) {
270
+ if ('error_code' in body)
271
+ return block;
272
+ return { ...block, text: JSON.stringify({ ...body, error_code: toolErrorClass(body) }, null, block.text.includes('\n') ? 2 : undefined) };
273
+ }
247
274
  const nestedCode = body.error && typeof body.error === 'object' ? body.error.code : undefined;
248
275
  const nestedClass = typeof nestedCode === 'string' ? nestedCode.toLowerCase() : undefined;
249
276
  const code = textCode ?? (TOOL_ERROR_CODES.includes(nestedClass) ? nestedClass
250
277
  : (body.status === 404 ? 'not_found'
251
278
  : body.status === 409 ? 'conflict' : body.status === 422 ? 'validation_failed'
252
279
  : body.status === 401 || body.status === 403 ? 'forbidden' : 'internal_error'));
253
- return { ...block, text: JSON.stringify({ ...body, code }, null, block.text.includes('\n') ? 2 : undefined) };
280
+ return { ...block, text: JSON.stringify({ ...body, code, error_code: toolErrorClass({ ...body, code }) }, null, block.text.includes('\n') ? 2 : undefined) };
254
281
  });
255
282
  if (failed && !content.some(block => block.type === 'text')) {
256
- content.push({ type: 'text', text: JSON.stringify(mapDbError(undefined)) });
283
+ content.push({ type: 'text', text: JSON.stringify({ ...mapDbError(undefined), error_code: toolErrorClass(mapDbError(undefined)) }) });
257
284
  }
258
285
  return failed ? { ...result, isError: true, content } : result;
259
286
  }
@@ -266,7 +293,7 @@ export function withToolErrors(handler) {
266
293
  catch (error) {
267
294
  if (error instanceof McpError && error.code === ErrorCode.UrlElicitationRequired)
268
295
  throw error;
269
- return dbErrorResult(error);
296
+ return codedToolResult(dbErrorResult(error));
270
297
  }
271
298
  };
272
299
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendinel/mcp-server",
3
- "version": "1.0.217",
3
+ "version": "1.0.218",
4
4
  "description": "AI-controlled email operations control plane for campaigns, contacts, segments, analytics, and multi-provider sending via MCP.",
5
5
  "mcpName": "ai.sendinel/mcp-server",
6
6
  "type": "module",