@sendinel/mcp-server 1.0.123 → 1.0.124

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.
@@ -309,7 +309,9 @@ export async function requestApproval(action, params, apiKeyId, channelContext,
309
309
  projectName = project.name;
310
310
  }
311
311
  catch { /* Missing display data omits the project line. */ }
312
- return elicitApproval({
312
+ const approveUrl = approvalPageUrl(data.id);
313
+ const previewText = buildApprovalPreview({ action, params: frozenParams, summary, projectName }).plain_summary;
314
+ const result = await elicitApproval({
313
315
  requires_approval: true,
314
316
  ...(await offerApprovalApp({ id: data.id, action, params: frozenParams, summary, tier, expires_at: expiresAt,
315
317
  payload_hash: approvalPayloadHash(action, frozenParams) })),
@@ -319,10 +321,16 @@ export async function requestApproval(action, params, apiKeyId, channelContext,
319
321
  summary,
320
322
  expires_at: expiresAt,
321
323
  approve_via: 'Open approve_url (works on a phone) or Dashboard → Settings → Pending Approvals',
322
- approve_url: approvalPageUrl(data.id),
323
- preview_text: buildApprovalPreview({ action, params: frozenParams, summary, projectName }).plain_summary,
324
- message: `This action requires human approval. A notification has been sent to your dashboard. Approval ID: ${data.id}. Check status with: poll this approval_id, or wait for the webhook callback (event: approval.decided). Token expires in 24 hours.`,
324
+ approve_url: approveUrl,
325
+ preview_text: previewText,
326
+ message: `Tap to approve: ${approveUrl}\n${previewText}\nIt stays open for 24 hours. In a terminal with prompts you can also answer the on-screen Accept/Decline.`,
325
327
  }, { action, params: frozenParams, summary, projectName }, getProjectId());
328
+ if (result.decision_status === 'approved') {
329
+ return { ...result, message: 'Approved. Call this tool again with the same arguments plus approval_id to run it.' };
330
+ }
331
+ if (result.decision_status === 'rejected')
332
+ return { ...result, message: 'Rejected.' };
333
+ return result;
326
334
  }
327
335
  const registeredApproval = new AsyncLocalStorage();
328
336
  /** Read by the hosted bridge only; no scope or token is changed. */
@@ -1,5 +1,6 @@
1
1
  import type { ElicitRequest, JSONRPCRequest } from '@modelcontextprotocol/sdk/types.js';
2
2
  import { type HumanDecisionIdentity } from './approval-human-eligibility.js';
3
+ export declare const ELICITATION_ANSWER_WINDOW_MS = 600000;
3
4
  export declare function isElicitationResponse(body: unknown): boolean;
4
5
  /** Auth has already run. Unknown and malformed responses are deliberately indistinguishable.
5
6
  * No decision can enter this path via a tools/call, model text, or nested tool arguments.
@@ -3,6 +3,7 @@ import { randomUUID } from 'node:crypto';
3
3
  import { approvalStore } from './tester-requests-store.js';
4
4
  import { humanApprovalEligible } from './approval-human-eligibility.js';
5
5
  const TABLE = 'mcp_elicitation_prompts';
6
+ export const ELICITATION_ANSWER_WINDOW_MS = 600_000;
6
7
  const object = (value) => value !== null && typeof value === 'object' && !Array.isArray(value) ? value : null;
7
8
  function email() {
8
9
  const source = approvalStore();
@@ -107,7 +108,7 @@ export async function relayElicitation(options) {
107
108
  const { error } = await client.from(TABLE).insert({ id: promptId, approval_id: options.approvalId,
108
109
  project_id: identity.projectId, oauth_token_id: identity.oauthTokenId, user_id: identity.actorId,
109
110
  jsonrpc_request_id: requestId, channel: 'mcp_elicitation', status: 'pending',
110
- created_at: new Date(now).toISOString(), expires_at: new Date(Math.min(now + 120_000, Date.parse(approval.expires_at))).toISOString() });
111
+ created_at: new Date(now).toISOString(), expires_at: new Date(Math.min(now + ELICITATION_ANSWER_WINDOW_MS, Date.parse(approval.expires_at))).toISOString() });
111
112
  if (signal.aborted)
112
113
  return exit('request_aborted');
113
114
  if (error)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendinel/mcp-server",
3
- "version": "1.0.123",
3
+ "version": "1.0.124",
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",