@suveren/gateway 0.34.0 → 0.35.1

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.
@@ -6,7 +6,7 @@
6
6
  "icon": "users",
7
7
  "profile": "customers",
8
8
  "npmPackage": "@humanagencyp/crm-mcp",
9
- "npmVersion": "1.5.0",
9
+ "npmVersion": "1.6.0",
10
10
  "mcp": {
11
11
  "command": "crm-mcp",
12
12
  "args": []
@@ -26,17 +26,33 @@
26
26
  "type": "text",
27
27
  "placeholder": "simulation (default) — live needs an adapter for your CRM",
28
28
  "optional": true
29
+ },
30
+ {
31
+ "key": "liveAdapter",
32
+ "label": "Live adapter (optional)",
33
+ "type": "text",
34
+ "placeholder": "local — your own company's CRM data. Leave empty unless Mode is \"live\"",
35
+ "optional": true
29
36
  }
30
37
  ],
31
38
  "envMapping": {
32
39
  "DATABASE_URL": "databaseUrl",
33
- "CRM_MODE": "mode"
40
+ "CRM_MODE": "mode",
41
+ "CRM_LIVE_ADAPTER": "liveAdapter"
34
42
  }
35
43
  },
36
44
  "simulation": {
37
45
  "field": "mode",
38
46
  "default": "simulation"
39
47
  },
48
+ "modeLabels": {
49
+ "simulation": "Test data",
50
+ "live": { "local": "Local" }
51
+ },
52
+ "modeNotes": {
53
+ "live": { "local": "On this computer — no shared access" }
54
+ },
55
+ "adapterField": "liveAdapter",
40
56
  "personalDefault": true,
41
57
  "oauth": null,
42
58
  "context": {
@@ -6,7 +6,7 @@
6
6
  "icon": "briefcase",
7
7
  "profile": "sales",
8
8
  "npmPackage": "@humanagencyp/erp-mcp",
9
- "npmVersion": "0.7.0",
9
+ "npmVersion": "0.9.1",
10
10
  "mcp": {
11
11
  "command": "erp-mcp",
12
12
  "args": [],
@@ -27,19 +27,63 @@
27
27
  "type": "text",
28
28
  "placeholder": "simulation (default) — live needs an adapter for your ERP",
29
29
  "optional": true
30
+ },
31
+ {
32
+ "key": "liveAdapter",
33
+ "label": "Live adapter (optional)",
34
+ "type": "text",
35
+ "placeholder": "local — your own company's invoicing. Leave empty unless Mode is \"live\"",
36
+ "optional": true
37
+ },
38
+ {
39
+ "key": "companyFile",
40
+ "label": "Company data file (optional)",
41
+ "type": "text",
42
+ "placeholder": "Path to your company file (name, address, UID, bank, VAT mode) — required when the live adapter is \"local\"",
43
+ "optional": true
44
+ },
45
+ {
46
+ "key": "invoiceTemplate",
47
+ "label": "Invoice template (optional)",
48
+ "type": "text",
49
+ "placeholder": "Path to an invoice template file — leave empty for the bundled German template",
50
+ "optional": true
30
51
  }
31
52
  ],
32
53
  "envMapping": {
33
54
  "DATABASE_URL": "databaseUrl",
34
- "ERP_MODE": "mode"
55
+ "ERP_MODE": "mode",
56
+ "ERP_LIVE_ADAPTER": "liveAdapter",
57
+ "ERP_LIVE_COMPANY_FILE": "companyFile",
58
+ "ERP_INVOICE_TEMPLATE": "invoiceTemplate"
35
59
  }
36
60
  },
37
61
  "simulation": {
38
62
  "field": "mode",
39
63
  "default": "simulation"
40
64
  },
65
+ "modeLabels": {
66
+ "simulation": "Test data",
67
+ "live": { "local": "Local" }
68
+ },
69
+ "modeNotes": {
70
+ "live": { "local": "On this computer — no shared access" }
71
+ },
72
+ "adapterField": "liveAdapter",
41
73
  "personalDefault": true,
42
74
  "oauth": null,
75
+ "documents": {
76
+ "invoice-pdf": {
77
+ "tool": "get_document",
78
+ "args": { "kind": "invoice-pdf", "id": "$id" },
79
+ "label": "issued invoice {id}"
80
+ },
81
+ "credit-note-pdf": {
82
+ "tool": "get_document",
83
+ "args": { "kind": "credit-note-pdf", "id": "$id" },
84
+ "label": "issued credit note {id}"
85
+ }
86
+ },
43
87
  "context": {
44
88
  "recordKeys": {
45
89
  "customer": {
@@ -202,7 +246,7 @@
202
246
  },
203
247
  "create_invoice": {
204
248
  "profile": "invoicing",
205
- "summary": "Invoice of {net_total|money} net to {customer_id}[ for order {order_id}]",
249
+ "summary": "Invoice[ {number}] of {net_total|money} net to {customer_id}[ for order {order_id}]",
206
250
  "executionMapping": {
207
251
  "net_total": "net_total",
208
252
  "currency": "currency"
@@ -213,15 +257,33 @@
213
257
  "actionLabel": "Invoice issued",
214
258
  "contextKeys": { "customer": "customer_id" },
215
259
  "preview": [
216
- { "tool": "get_customer", "args": { "id": "customer_id" }, "attach": "customer_id" }
260
+ { "tool": "get_customer", "args": { "id": "customer_id" }, "attach": "customer_id" },
261
+ {
262
+ "tool": "preview_invoice",
263
+ "args": {
264
+ "customer_id": "customer_id",
265
+ "number": "number",
266
+ "document_date": "document_date",
267
+ "service_period": "service_period",
268
+ "lines": "lines",
269
+ "net_total": "net_total",
270
+ "currency": "currency",
271
+ "payment_term_days": "payment_term_days",
272
+ "memo": "memo"
273
+ },
274
+ "kind": "document"
275
+ }
217
276
  ],
218
277
  "display": {
219
- "order": ["customer_id", "order_id", "lines", "net_total", "currency", "payment_term_days", "memo"],
278
+ "order": ["customer_id", "order_id", "number", "document_date", "service_period", "lines", "net_total", "currency", "payment_term_days", "memo"],
220
279
  "money": ["net_total", "customer_id.credit_limit", "customer_id.open_balance", "customer_id.available_credit"],
221
280
  "currencyField": "currency",
222
281
  "labels": {
223
282
  "customer_id": "Customer",
224
283
  "order_id": "Order",
284
+ "number": "Invoice number",
285
+ "document_date": "Invoice date",
286
+ "service_period": "Service period",
225
287
  "net_total": "Total (net)",
226
288
  "payment_term_days": "Payment terms (days)",
227
289
  "memo": "Memo",
@@ -233,7 +295,7 @@
233
295
  },
234
296
  "create_credit_note": {
235
297
  "profile": "invoicing",
236
- "summary": "Credit note of {credit_total|money} net for invoice {invoice_id}: {reason|short}",
298
+ "summary": "Credit note[ {number}] of {credit_total|money} net for invoice {invoice_id}: {reason|short}",
237
299
  "executionMapping": {
238
300
  "credit_total": "credit_total",
239
301
  "currency": "currency"
@@ -248,16 +310,29 @@
248
310
  "fields": ["number", "customer_id", "net_total", "currency", "status"]
249
311
  },
250
312
  "display": {
251
- "order": ["invoice_id", "credit_total", "currency", "reason", "lines"],
313
+ "order": ["invoice_id", "number", "document_date", "credit_total", "currency", "reason", "service_period", "lines"],
252
314
  "money": ["credit_total"],
253
315
  "currencyField": "currency",
254
316
  "labels": {
255
317
  "invoice_id": "Invoice",
318
+ "number": "Credit note number",
319
+ "document_date": "Credit note date",
256
320
  "credit_total": "Total (net)",
257
- "reason": "Reason"
321
+ "reason": "Reason",
322
+ "service_period": "Service period"
258
323
  }
259
324
  }
260
325
  },
326
+ "preview_invoice": {
327
+ "profile": "invoicing",
328
+ "category": "read",
329
+ "internal": true
330
+ },
331
+ "get_document": {
332
+ "profile": "invoicing",
333
+ "category": "read",
334
+ "internal": true
335
+ },
261
336
  "list_invoices": {
262
337
  "profile": "invoicing",
263
338
  "category": "read",
@@ -38,6 +38,10 @@
38
38
  "REFRESH_TOKEN": "refreshToken"
39
39
  }
40
40
  },
41
+ "attachments": {
42
+ "transport": "mime-raw",
43
+ "arg": "raw"
44
+ },
41
45
  "context": {
42
46
  "sources": {
43
47
  "message": {
@@ -106,7 +110,10 @@
106
110
  "argEncoding": {
107
111
  "subject": "rfc2047"
108
112
  },
109
- "actionLabel": "Email sent"
113
+ "actionLabel": "Email sent",
114
+ "attachmentsArg": "attachments",
115
+ "insertFields": ["body"],
116
+ "display": { "aiText": "body" }
110
117
  },
111
118
  "create_draft": {
112
119
  "summary": "Draft email to {to|list}[: {subject|short}]",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "id": "mail",
3
- "name": "Email (simulation)",
3
+ "name": "Email",
4
4
  "version": "1.0.0",
5
5
  "description": "Simulated inbox for testing an agent's email replies — nothing leaves the laptop",
6
6
  "icon": "mail",
7
7
  "profile": "email",
8
8
  "npmPackage": "@humanagencyp/email-mcp",
9
- "npmVersion": "0.3.0",
9
+ "npmVersion": "0.4.0",
10
10
  "readPolicy": {
11
11
  "defaultAgeDays": 30
12
12
  },
@@ -40,8 +40,15 @@
40
40
  "field": "mode",
41
41
  "default": "simulation"
42
42
  },
43
+ "modeLabels": {
44
+ "simulation": "Test mailbox"
45
+ },
43
46
  "personalDefault": false,
44
47
  "oauth": null,
48
+ "attachments": {
49
+ "transport": "field",
50
+ "arg": "attachments"
51
+ },
45
52
  "context": {
46
53
  "sources": {
47
54
  "message": {
@@ -101,7 +108,10 @@
101
108
  },
102
109
  "actionLabel": "Email sent",
103
110
  "sourceFromArg": { "kind": "message", "arg": "in_reply_to" },
104
- "contextKeys": { "email": "to" }
111
+ "contextKeys": { "email": "to" },
112
+ "attachmentsArg": "attachments",
113
+ "insertFields": ["body"],
114
+ "display": { "aiText": "body" }
105
115
  },
106
116
  "load_simulation": {
107
117
  "executionMapping": {},
@@ -29,6 +29,14 @@
29
29
  }
30
30
  },
31
31
  "oauth": null,
32
+ "values": {
33
+ "payment-link": {
34
+ "tool": "GetPaymentLink",
35
+ "args": { "paymentLinkId": "$id" },
36
+ "path": "_links.paymentLink.href",
37
+ "allowedHosts": ["payment-links.mollie.com"]
38
+ }
39
+ },
32
40
  "toolGating": {
33
41
  "default": {
34
42
  "executionMapping": {},
@@ -759,6 +759,25 @@ async function getToolRequestContext(data) {
759
759
  clearTimeout(timeout);
760
760
  }
761
761
  }
762
+ async function getAttachmentPreview(ref) {
763
+ const controller = new AbortController();
764
+ const timeout = setTimeout(() => controller.abort(), 12e3);
765
+ try {
766
+ const res = await fetch(`${MCP_BASE}/internal/document-preview`, {
767
+ method: "POST",
768
+ headers: internalHeaders(),
769
+ body: JSON.stringify(ref),
770
+ signal: controller.signal
771
+ });
772
+ if (!res.ok) {
773
+ const err = await res.json().catch(() => ({ error: "Unknown error" }));
774
+ throw new Error(`MCP document-preview failed: ${err.error ?? res.status}`);
775
+ }
776
+ return res.json();
777
+ } finally {
778
+ clearTimeout(timeout);
779
+ }
780
+ }
762
781
  async function getAgentContact() {
763
782
  const res = await fetch(`${MCP_BASE}/internal/agent-contact`, {
764
783
  headers: internalHeaders()
@@ -3713,6 +3732,24 @@ function createEvidenceExportRouter(spUrl, vault2, dataDir, lockAsTlsMismatch2)
3713
3732
  // src/routes/proposal-preview.ts
3714
3733
  import { Router as Router10 } from "express";
3715
3734
 
3735
+ // src/lib/attachment-preview-types.ts
3736
+ var ATTACHMENTS_SEALED_META_KEY = "_attachmentsSealed";
3737
+ function findSealedAttachment(toolArgs, sha256) {
3738
+ if (!toolArgs) return null;
3739
+ const raw = toolArgs[ATTACHMENTS_SEALED_META_KEY];
3740
+ if (raw === null || typeof raw !== "object") return null;
3741
+ const attachments = raw.attachments;
3742
+ if (!Array.isArray(attachments)) return null;
3743
+ for (const a of attachments) {
3744
+ if (a === null || typeof a !== "object") continue;
3745
+ const r = a;
3746
+ if (r.sha256 === sha256 && typeof r.connector === "string" && typeof r.kind === "string" && typeof r.id === "string") {
3747
+ return { connector: r.connector, kind: r.kind, id: r.id, sha256 };
3748
+ }
3749
+ }
3750
+ return null;
3751
+ }
3752
+
3716
3753
  // src/lib/content-crypto.ts
3717
3754
  import { createHash as createHash5 } from "crypto";
3718
3755
  import { canonicalize as canonicalize3 } from "@hap/core";
@@ -3911,6 +3948,23 @@ function createProposalStatusRouter(spUrl, vault2, dataDir, lockAsTlsMismatch2,
3911
3948
  res.status(502).json({ error: `MCP server unavailable: ${err instanceof Error ? err.message : String(err)}` });
3912
3949
  }
3913
3950
  });
3951
+ router.get("/:id/attachment-preview/:sha256", async (req, res) => {
3952
+ const proposalId = req.params.id;
3953
+ const sha256 = req.params.sha256;
3954
+ const resolved = await resolveProposalToolArgsForRoute(spUrl, vault2, dataDir, lockAsTlsMismatch2, proposalId, res);
3955
+ if (!resolved) return;
3956
+ const ref = findSealedAttachment(resolved.toolArgs, sha256);
3957
+ if (!ref) {
3958
+ res.status(404).json({ status: "error", message: "No sealed attachment with this fingerprint on this proposal." });
3959
+ return;
3960
+ }
3961
+ try {
3962
+ const result = await getAttachmentPreview(ref);
3963
+ res.json(result);
3964
+ } catch (err) {
3965
+ res.status(502).json({ status: "error", message: `MCP server unavailable: ${err instanceof Error ? err.message : String(err)}` });
3966
+ }
3967
+ });
3914
3968
  router.get("/:id/request-context", async (req, res) => {
3915
3969
  const proposalId = req.params.id;
3916
3970
  const resolved = await resolveProposalToolArgsForRoute(spUrl, vault2, dataDir, lockAsTlsMismatch2, proposalId, res);
@@ -4138,6 +4192,31 @@ function createEvidenceRouter() {
4138
4192
  });
4139
4193
  }
4140
4194
  });
4195
+ router.get("/receipt/:id/attachment-preview/:sha256", async (req, res) => {
4196
+ const id = String(req.params.id);
4197
+ const sha256 = String(req.params.sha256);
4198
+ try {
4199
+ const evidence = await getLocalEvidence();
4200
+ const entry = (evidence.receipts ?? []).find(
4201
+ (r) => r.receipt?.id === id
4202
+ );
4203
+ if (!entry) {
4204
+ res.status(404).json({ status: "error", message: "No local copy of this receipt" });
4205
+ return;
4206
+ }
4207
+ const e = entry;
4208
+ const candidate = e.sentArgs ?? e.proposal?.toolArgs ?? (e.boundContent && typeof e.boundContent === "object" && !Array.isArray(e.boundContent) ? e.boundContent : void 0);
4209
+ const ref = findSealedAttachment(candidate, sha256);
4210
+ if (!ref) {
4211
+ res.status(404).json({ status: "error", message: "No sealed attachment with this fingerprint on this ticket." });
4212
+ return;
4213
+ }
4214
+ const result = await getAttachmentPreview(ref);
4215
+ res.json(result);
4216
+ } catch (err) {
4217
+ res.status(502).json({ status: "error", message: `MCP server unavailable: ${err instanceof Error ? err.message : String(err)}` });
4218
+ }
4219
+ });
4141
4220
  router.get("/headlines", async (_req, res) => {
4142
4221
  try {
4143
4222
  res.json({ headlines: await getTicketHeadlines() });