@suveren/gateway 0.11.2 → 0.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.
Files changed (117) hide show
  1. package/README.md +1 -1
  2. package/bin/suveren-gateway.js +9 -0
  3. package/content/integrations/calendar.json +1 -0
  4. package/content/integrations/crm.json +1 -67
  5. package/content/integrations/deploy-github.json +3 -5
  6. package/content/integrations/erp.json +1 -80
  7. package/content/integrations/gmail.json +1 -0
  8. package/content/integrations/linkedin.json +1 -0
  9. package/content/integrations/mail.json +1 -48
  10. package/content/integrations/mollie.json +1 -0
  11. package/content/integrations/records.json +1 -0
  12. package/dist/mcp-server/http.mjs +159 -14
  13. package/lib/node-version.d.mts +7 -0
  14. package/lib/node-version.mjs +28 -0
  15. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/auth.d.ts.map +1 -1
  16. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/auth.js +9 -7
  17. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/auth.js.map +1 -1
  18. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/sse.d.ts +13 -0
  19. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/sse.d.ts.map +1 -1
  20. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/sse.js +17 -4
  21. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/sse.js.map +1 -1
  22. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/streamableHttp.d.ts +13 -0
  23. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/streamableHttp.d.ts.map +1 -1
  24. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/streamableHttp.js +23 -10
  25. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/streamableHttp.js.map +1 -1
  26. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/elicitationFormExample.js +63 -20
  27. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/elicitationFormExample.js.map +1 -1
  28. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/elicitationUrlExample.js +71 -29
  29. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/elicitationUrlExample.js.map +1 -1
  30. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/jsonResponseStreamableHttp.js +45 -6
  31. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/jsonResponseStreamableHttp.js.map +1 -1
  32. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/simpleStreamableHttp.js +71 -31
  33. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/simpleStreamableHttp.js.map +1 -1
  34. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/simpleTaskInteractive.js +61 -18
  35. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/simpleTaskInteractive.js.map +1 -1
  36. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js +57 -19
  37. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js.map +1 -1
  38. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/ssePollingExample.js +64 -17
  39. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/ssePollingExample.js.map +1 -1
  40. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.js +61 -13
  41. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.js.map +1 -1
  42. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/experimental/tasks/interfaces.d.ts +4 -0
  43. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/experimental/tasks/interfaces.d.ts.map +1 -1
  44. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/experimental/tasks/interfaces.js.map +1 -1
  45. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/experimental/tasks/stores/in-memory.d.ts +17 -8
  46. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/experimental/tasks/stores/in-memory.d.ts.map +1 -1
  47. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/experimental/tasks/stores/in-memory.js +31 -14
  48. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/experimental/tasks/stores/in-memory.js.map +1 -1
  49. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/auth/middleware/bearerAuth.d.ts +9 -1
  50. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/auth/middleware/bearerAuth.d.ts.map +1 -1
  51. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/auth/middleware/bearerAuth.js +17 -1
  52. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/auth/middleware/bearerAuth.js.map +1 -1
  53. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp.d.ts +12 -1
  54. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp.d.ts.map +1 -1
  55. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp.js +46 -2
  56. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp.js.map +1 -1
  57. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/shared/protocol.d.ts +1 -0
  58. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/shared/protocol.d.ts.map +1 -1
  59. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/shared/protocol.js +39 -9
  60. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/shared/protocol.js.map +1 -1
  61. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/shared/transport.d.ts +10 -0
  62. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/shared/transport.d.ts.map +1 -1
  63. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/shared/transport.js +77 -0
  64. package/node_modules/@modelcontextprotocol/sdk/dist/cjs/shared/transport.js.map +1 -1
  65. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.d.ts.map +1 -1
  66. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.js +9 -7
  67. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.js.map +1 -1
  68. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/sse.d.ts +13 -0
  69. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/sse.d.ts.map +1 -1
  70. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/sse.js +18 -5
  71. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/sse.js.map +1 -1
  72. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/streamableHttp.d.ts +13 -0
  73. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/streamableHttp.d.ts.map +1 -1
  74. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/streamableHttp.js +24 -11
  75. package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/streamableHttp.js.map +1 -1
  76. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/elicitationFormExample.js +63 -20
  77. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/elicitationFormExample.js.map +1 -1
  78. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/elicitationUrlExample.js +71 -29
  79. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/elicitationUrlExample.js.map +1 -1
  80. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/jsonResponseStreamableHttp.js +45 -6
  81. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/jsonResponseStreamableHttp.js.map +1 -1
  82. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/simpleStreamableHttp.js +71 -31
  83. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/simpleStreamableHttp.js.map +1 -1
  84. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/simpleTaskInteractive.js +61 -18
  85. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/simpleTaskInteractive.js.map +1 -1
  86. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js +57 -19
  87. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js.map +1 -1
  88. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/ssePollingExample.js +64 -17
  89. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/ssePollingExample.js.map +1 -1
  90. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.js +61 -13
  91. package/node_modules/@modelcontextprotocol/sdk/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.js.map +1 -1
  92. package/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.d.ts +4 -0
  93. package/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.d.ts.map +1 -1
  94. package/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js.map +1 -1
  95. package/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/stores/in-memory.d.ts +17 -8
  96. package/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/stores/in-memory.d.ts.map +1 -1
  97. package/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/stores/in-memory.js +31 -14
  98. package/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/stores/in-memory.js.map +1 -1
  99. package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/auth/middleware/bearerAuth.d.ts +9 -1
  100. package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/auth/middleware/bearerAuth.d.ts.map +1 -1
  101. package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/auth/middleware/bearerAuth.js +17 -1
  102. package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/auth/middleware/bearerAuth.js.map +1 -1
  103. package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.d.ts +12 -1
  104. package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.d.ts.map +1 -1
  105. package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js +46 -2
  106. package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js.map +1 -1
  107. package/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.d.ts +1 -0
  108. package/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.d.ts.map +1 -1
  109. package/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js +39 -9
  110. package/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js.map +1 -1
  111. package/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/transport.d.ts +10 -0
  112. package/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/transport.d.ts.map +1 -1
  113. package/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/transport.js +72 -0
  114. package/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/transport.js.map +1 -1
  115. package/node_modules/@modelcontextprotocol/sdk/package.json +1 -1
  116. package/package.json +2 -2
  117. package/server.js +9 -0
package/README.md CHANGED
@@ -106,7 +106,7 @@ Open `http://localhost:7400`. The MCP server is at `http://localhost:7430`.
106
106
 
107
107
  ### Option B — npm
108
108
 
109
- Requires [Node.js 20.18.1+](https://nodejs.org/).
109
+ Requires [Node.js 22+](https://nodejs.org/).
110
110
 
111
111
  ```bash
112
112
  npm install -g @suveren/gateway
@@ -20,6 +20,15 @@ import { buildLaunchAgentPlist, buildMacLauncher, buildSystemdUnit, buildWindows
20
20
  import { DEFAULT_AS_URL, readConfig, writeConfig, validateAsUrl, validateCaFile, validatePinTls, resolveAsUrl, resolvePinTls, resolvePinTlsExpectedFingerprint, resolveSimulation } from '../lib/config.mjs';
21
21
  import { createInterface } from 'node:readline/promises';
22
22
  import { readPairing as readAsPairing, recordTlsPin, formatFingerprint, normalizeFingerprint } from '../lib/as-pairing.mjs';
23
+ import { unsupportedNodeReason } from '../lib/node-version.mjs';
24
+
25
+ // Before any command: on an unsupported Node the connectors cannot run (see
26
+ // lib/node-version.mjs), so refuse with the reason instead of starting.
27
+ const nodeReason = unsupportedNodeReason(process.versions.node);
28
+ if (nodeReason) {
29
+ console.error(nodeReason);
30
+ process.exit(1);
31
+ }
23
32
 
24
33
  const __dirname = dirname(fileURLToPath(import.meta.url));
25
34
  const PKG_ROOT = resolve(__dirname, '..');
@@ -9,6 +9,7 @@
9
9
  "defaultAgeDays": 30
10
10
  },
11
11
  "npmPackage": "@humanagencyp/google-calendar-mcp",
12
+ "npmVersion": "0.1.2",
12
13
  "mcp": {
13
14
  "command": "google-calendar-mcp",
14
15
  "args": []
@@ -6,6 +6,7 @@
6
6
  "icon": "users",
7
7
  "profile": "customers",
8
8
  "npmPackage": "@humanagencyp/crm-mcp",
9
+ "npmVersion": "1.1.2",
9
10
  "mcp": {
10
11
  "command": "crm-mcp",
11
12
  "args": []
@@ -142,73 +143,6 @@
142
143
  }
143
144
  }
144
145
  },
145
- "templates": [
146
- {
147
- "name": "Browse & Log",
148
- "description": "Search contacts, view pipeline and timeline, log activities. No creates or deletes.",
149
- "risk": "low",
150
- "mode": "automatic",
151
- "bounds": {
152
- "read_access": "unlimited",
153
- "write_daily_max": "50",
154
- "delete_daily_max": "0",
155
- "setup_daily_max": "0"
156
- },
157
- "context": {
158
- "contact_type": "customer,lead,partner,vendor"
159
- },
160
- "intent": "Allow the agent to browse CRM data and log activities. Read and log only — no creating, editing, or deleting records.",
161
- "ttl": 2592000,
162
- "tags": [
163
- "read + log",
164
- "no deletes"
165
- ]
166
- },
167
- {
168
- "name": "Full CRM with Review",
169
- "description": "Create contacts, deals, and tasks. Update and delete records. Every write requires approval.",
170
- "risk": "high",
171
- "mode": "review",
172
- "bounds": {
173
- "read_access": "unlimited",
174
- "write_daily_max": "50",
175
- "delete_daily_max": "10",
176
- "setup_daily_max": "0"
177
- },
178
- "context": {
179
- "contact_type": "customer,lead,partner,vendor"
180
- },
181
- "intent": "Full CRM management — create, update, and delete contacts, deals, and tasks. Every write action requires my review before execution.",
182
- "ttl": 2592000,
183
- "tags": [
184
- "50 writes/day",
185
- "10 deletes/day",
186
- "review mode"
187
- ]
188
- },
189
- {
190
- "name": "Load test data (with approval)",
191
- "description": "Load a simulation package (customers and optional contacts) into the simulated CRM, for setting up a three-week test. No browsing, writing, or deleting — this template grants nothing but the one-time test-data load, and it still requires your approval.",
192
- "risk": "high",
193
- "mode": "review",
194
- "bounds": {
195
- "read_access": "none",
196
- "write_daily_max": "0",
197
- "delete_daily_max": "0",
198
- "setup_daily_max": "1"
199
- },
200
- "context": {
201
- "contact_type": "customer"
202
- },
203
- "intent": "Allow the agent to load one simulation package of test data into the simulated CRM, for my review before it happens. No other CRM action is granted by this mandate — set up test data separately from the mandate the agent uses for its actual work.",
204
- "ttl": 2592000,
205
- "tags": [
206
- "setup only",
207
- "review mode",
208
- "1 load"
209
- ]
210
- }
211
- ],
212
146
  "setupHint": "No configuration needed — works out of the box.",
213
147
  "setupGuide": [
214
148
  {
@@ -6,12 +6,10 @@
6
6
  "icon": "wrench",
7
7
  "profile": "deploy",
8
8
  "npmPackage": "@humanagencyp/deploy-mcp",
9
+ "npmVersion": "0.4.2",
9
10
  "mcp": {
10
- "command": "npx",
11
- "args": [
12
- "-y",
13
- "@humanagencyp/deploy-mcp@latest"
14
- ]
11
+ "command": "deploy-mcp",
12
+ "args": []
15
13
  },
16
14
  "credentials": {
17
15
  "fields": [
@@ -6,6 +6,7 @@
6
6
  "icon": "briefcase",
7
7
  "profile": "sales",
8
8
  "npmPackage": "@humanagencyp/erp-mcp",
9
+ "npmVersion": "0.3.3",
9
10
  "mcp": {
10
11
  "command": "erp-mcp",
11
12
  "args": []
@@ -138,86 +139,6 @@
138
139
  }
139
140
  }
140
141
  },
141
- "templates": [
142
- {
143
- "name": "Draft quotes",
144
- "description": "Look up items and customers, create and edit draft quotes. Nothing is sent to a customer or converted to an order.",
145
- "risk": "low",
146
- "mode": "automatic",
147
- "bounds": {
148
- "read_access": "unlimited",
149
- "value_max": "5000",
150
- "discount_max": "15",
151
- "order_value_daily_max": "0",
152
- "quote_daily_max": "20",
153
- "send_daily_max": "0",
154
- "order_daily_max": "0",
155
- "setup_daily_max": "0"
156
- },
157
- "context": {
158
- "currency": "EUR"
159
- },
160
- "intent": "Allow the agent to browse items and customers and prepare draft quotes for my review. It cannot send a quote to a customer or convert one into an order.",
161
- "ttl": 2592000,
162
- "tags": [
163
- "drafts only",
164
- "no sends",
165
- "no orders"
166
- ]
167
- },
168
- {
169
- "name": "Send and confirm with review above limit",
170
- "description": "Draft, send, and convert quotes into orders. Every send and every order requires your approval.",
171
- "risk": "high",
172
- "mode": "review",
173
- "bounds": {
174
- "read_access": "unlimited",
175
- "value_max": "20000",
176
- "discount_max": "20",
177
- "order_value_daily_max": "50000",
178
- "quote_daily_max": "50",
179
- "send_daily_max": "20",
180
- "order_daily_max": "10",
181
- "setup_daily_max": "0"
182
- },
183
- "context": {
184
- "currency": "EUR"
185
- },
186
- "intent": "Allow the agent to draft, send, and convert quotes into orders. Every quote sent to a customer and every order requires my review before it happens.",
187
- "ttl": 2592000,
188
- "tags": [
189
- "20k EUR/document",
190
- "review mode",
191
- "10 orders/day"
192
- ]
193
- },
194
- {
195
- "name": "Load test data (with approval)",
196
- "description": "Load a simulation package (customers and products) into the simulated ERP, for setting up a three-week test. No quoting, sending, or ordering — this template grants nothing but the one-time test-data load, and it still requires your approval.",
197
- "risk": "high",
198
- "mode": "review",
199
- "bounds": {
200
- "read_access": "none",
201
- "value_max": "0",
202
- "discount_max": "0",
203
- "order_value_daily_max": "0",
204
- "quote_daily_max": "0",
205
- "send_daily_max": "0",
206
- "order_daily_max": "0",
207
- "setup_daily_max": "1"
208
- },
209
- "context": {
210
- "currency": "EUR"
211
- },
212
- "intent": "Allow the agent to load one simulation package of test data into the simulated ERP, for my review before it happens. No other ERP action is granted by this mandate — set up test data separately from the mandate the agent uses for its actual work.",
213
- "ttl": 2592000,
214
- "tags": [
215
- "setup only",
216
- "review mode",
217
- "1 load"
218
- ]
219
- }
220
- ],
221
142
  "setupHint": "No configuration needed — works out of the box.",
222
143
  "setupGuide": [
223
144
  {
@@ -6,6 +6,7 @@
6
6
  "icon": "mail",
7
7
  "profile": "email",
8
8
  "npmPackage": "@shinzolabs/gmail-mcp",
9
+ "npmVersion": "1.7.4",
9
10
  "readPolicy": {
10
11
  "defaultAgeDays": 30
11
12
  },
@@ -6,6 +6,7 @@
6
6
  "icon": "share",
7
7
  "profile": "publish",
8
8
  "npmPackage": "@humanagencyp/linkedin-mcp",
9
+ "npmVersion": "1.0.7",
9
10
  "mcp": {
10
11
  "command": "linkedin-mcp",
11
12
  "args": []
@@ -6,6 +6,7 @@
6
6
  "icon": "mail",
7
7
  "profile": "email",
8
8
  "npmPackage": "@humanagencyp/email-mcp",
9
+ "npmVersion": "0.1.3",
9
10
  "mcp": {
10
11
  "command": "email-mcp",
11
12
  "args": []
@@ -86,54 +87,6 @@
86
87
  }
87
88
  }
88
89
  },
89
- "templates": [
90
- {
91
- "name": "Work the inbox",
92
- "description": "Read the simulated inbox and send replies. Cannot load or replace test data.",
93
- "risk": "low",
94
- "mode": "automatic",
95
- "bounds": {
96
- "read_access": "unlimited",
97
- "recipient_max": "1",
98
- "send_daily_max": "50",
99
- "setup_daily_max": "0"
100
- },
101
- "context": {
102
- "allowed_recipients": "",
103
- "allowed_domains": ""
104
- },
105
- "intent": "Allow the agent to read the simulated inbox and reply to messages on its own. It cannot load or replace the test data the inbox was seeded with.",
106
- "ttl": 2592000,
107
- "tags": [
108
- "read + reply",
109
- "automatic",
110
- "no setup"
111
- ]
112
- },
113
- {
114
- "name": "Load test data (with approval)",
115
- "description": "Load a simulation package into an empty inbox. No reading or sending — a separate, one-time setup mandate.",
116
- "risk": "high",
117
- "mode": "review",
118
- "bounds": {
119
- "read_access": "none",
120
- "recipient_max": "0",
121
- "send_daily_max": "0",
122
- "setup_daily_max": "1"
123
- },
124
- "context": {
125
- "allowed_recipients": "",
126
- "allowed_domains": ""
127
- },
128
- "intent": "Allow the agent to load one simulation package into an empty inbox, with my review before it happens. No reading or sending under this mandate — grant it separately from the mandate the agent uses for its daily work.",
129
- "ttl": 2592000,
130
- "tags": [
131
- "setup only",
132
- "review mode",
133
- "1 load/day"
134
- ]
135
- }
136
- ],
137
90
  "setupHint": "No configuration needed — works out of the box, in simulation mode.",
138
91
  "setupGuide": [
139
92
  {
@@ -6,6 +6,7 @@
6
6
  "icon": "card",
7
7
  "profile": "charge",
8
8
  "npmPackage": "mcp-remote",
9
+ "npmVersion": "0.14.3",
9
10
  "mcp": {
10
11
  "command": "mcp-remote",
11
12
  "args": [
@@ -6,6 +6,7 @@
6
6
  "icon": "file-text",
7
7
  "profile": "records",
8
8
  "npmPackage": "@humanagencyp/records-mcp",
9
+ "npmVersion": "1.0.4",
9
10
  "personalDefault": true,
10
11
  "mcp": {
11
12
  "command": "records-mcp",
@@ -2607,6 +2607,20 @@ import { randomUUID } from "crypto";
2607
2607
  // src/lib/manifest-loader.ts
2608
2608
  import { readFileSync as readFileSync12, existsSync as existsSync11 } from "fs";
2609
2609
  import { join as join11, resolve } from "path";
2610
+ var EXACT_SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
2611
+ function isExactSemver(value) {
2612
+ return typeof value === "string" && EXACT_SEMVER_RE.test(value);
2613
+ }
2614
+ function invalidNpmPinReason(manifest) {
2615
+ if (!manifest.npmPackage) return null;
2616
+ if (manifest.npmVersion === void 0) {
2617
+ return `npmPackage "${manifest.npmPackage}" is set but npmVersion is missing \u2014 every connector manifest must pin an exact tested version`;
2618
+ }
2619
+ if (!isExactSemver(manifest.npmVersion)) {
2620
+ return `npmVersion ${JSON.stringify(manifest.npmVersion)} for "${manifest.npmPackage}" is not an exact semver (e.g. "1.2.3") \u2014 ranges, dist-tags like "latest", git specs, URLs, and file paths are not allowed`;
2621
+ }
2622
+ return null;
2623
+ }
2610
2624
  var manifests = /* @__PURE__ */ new Map();
2611
2625
  function loadManifests(integrationsDir) {
2612
2626
  const dir = resolve(
@@ -2629,6 +2643,11 @@ function loadManifests(integrationsDir) {
2629
2643
  const manifestPath = join11(dir, relativePath);
2630
2644
  try {
2631
2645
  const manifest = JSON.parse(readFileSync12(manifestPath, "utf-8"));
2646
+ const pinReason = invalidNpmPinReason(manifest);
2647
+ if (pinReason) {
2648
+ console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${pinReason}`);
2649
+ continue;
2650
+ }
2632
2651
  manifests.set(id, manifest);
2633
2652
  loaded++;
2634
2653
  } catch (err) {
@@ -4383,6 +4402,17 @@ var IntegrationManager = class {
4383
4402
  }
4384
4403
  running = /* @__PURE__ */ new Map();
4385
4404
  onToolsChanged = null;
4405
+ /**
4406
+ * Called when a persisted config was migrated to the manifest's command/args
4407
+ * (see `migrateNpxConfig`) so the caller can write the fix back to
4408
+ * `integrations.json` — the manager itself has no registry/persistence
4409
+ * handle, by design (mirrors `onToolsChanged`). Without a subscriber the
4410
+ * migration still takes effect for THIS start (the in-memory config is
4411
+ * still corrected before spawn); only the one-time persistence is skipped,
4412
+ * so every later start keeps re-migrating in memory, which is safe, if
4413
+ * redundant.
4414
+ */
4415
+ onConfigMigrated = null;
4386
4416
  /**
4387
4417
  * Per-integration operation queue. Start/stop for one id are serialized
4388
4418
  * through here; without it, a second start issued while the first is still
@@ -4408,6 +4438,16 @@ var IntegrationManager = class {
4408
4438
  setOnToolsChanged(cb) {
4409
4439
  this.onToolsChanged = cb;
4410
4440
  }
4441
+ /**
4442
+ * Register a callback invoked when a persisted config is migrated off a
4443
+ * stale `npx` command to the current manifest's command/args (see
4444
+ * `migrateNpxConfig`). The caller is expected to persist `updates` against
4445
+ * `id` in `integrations.json` so the fix survives the next boot instead of
4446
+ * silently reapplying on every start.
4447
+ */
4448
+ setOnConfigMigrated(cb) {
4449
+ this.onConfigMigrated = cb;
4450
+ }
4411
4451
  /**
4412
4452
  * Is `npmPackage` present AND usable?
4413
4453
  *
@@ -4438,6 +4478,23 @@ var IntegrationManager = class {
4438
4478
  return false;
4439
4479
  }
4440
4480
  }
4481
+ /**
4482
+ * The installed version of `npmPackage`, or null if it isn't usably
4483
+ * installed at all (see `isUsableInstall`) or its package.json has no
4484
+ * parseable `version`. Never throws — an unreadable version is treated the
4485
+ * same as "not installed", which is the safe direction: it drives a
4486
+ * (re)install rather than a false "already pinned".
4487
+ */
4488
+ readInstalledVersion(npmPackage) {
4489
+ if (!this.isUsableInstall(npmPackage)) return null;
4490
+ const pkgJson = join13(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"), "package.json");
4491
+ try {
4492
+ const pkg = JSON.parse(readFileSync14(pkgJson, "utf8"));
4493
+ return typeof pkg.version === "string" ? pkg.version : null;
4494
+ } catch {
4495
+ return null;
4496
+ }
4497
+ }
4441
4498
  /**
4442
4499
  * Does the node_modules/.bin shim for `binName` exist? On Windows npm writes
4443
4500
  * `<name>`, `<name>.cmd`, and `<name>.ps1`; the .cmd variant is what actually
@@ -4448,9 +4505,32 @@ var IntegrationManager = class {
4448
4505
  const candidates = process.platform === "win32" ? [binName, `${binName}.cmd`, `${binName}.CMD`, `${binName}.ps1`] : [binName];
4449
4506
  return candidates.some((c) => existsSync13(join13(INTEGRATIONS_BIN, c)));
4450
4507
  }
4508
+ /**
4509
+ * The exact version THIS gateway's CURRENTLY LOADED manifest pins
4510
+ * `config.npmPackage` to — or undefined when there's no npmPackage, no
4511
+ * manifest for this id, or the manifest's `npmPackage` no longer names the
4512
+ * same package as `config` (a manually re-pointed integration: nothing in
4513
+ * the manifest vouches for a version of config's actual package).
4514
+ *
4515
+ * Read fresh from `getManifest(config.id)` on every call, never from
4516
+ * anything persisted on `config` itself — manifests are reloaded fresh on
4517
+ * every boot, `integrations.json` is not, so this is what keeps the pin
4518
+ * from going stale the day after an integration was first activated. Used
4519
+ * both to decide the npm install/update (`ensureInstalled`) and to decide
4520
+ * whether a persisted `npx …` command must be refused/migrated
4521
+ * (`migrateNpxConfig`) — one definition of "pinned", so the two can't
4522
+ * disagree about which connectors it applies to.
4523
+ */
4524
+ pinnedVersionFor(config) {
4525
+ if (!config.npmPackage) return void 0;
4526
+ const manifest = getManifest(config.id);
4527
+ return manifest?.npmPackage === config.npmPackage ? manifest.npmVersion : void 0;
4528
+ }
4451
4529
  /**
4452
4530
  * Install an npm package into the managed integrations directory if not
4453
- * already present. Called automatically before spawning when
4531
+ * already present, and — when `config`'s manifest pins an exact
4532
+ * `npmVersion` for this same package — bring the installed copy to EXACTLY
4533
+ * that version, older or newer. Called automatically before spawning when
4454
4534
  * config.npmPackage is set.
4455
4535
  *
4456
4536
  * Asynchronous ON PURPOSE. This used to be execSync, which blocked the MCP
@@ -4460,19 +4540,49 @@ var IntegrationManager = class {
4460
4540
  * CI runners: ~3.6s (Linux), ~5.4s (macOS), ~14s (Windows), and far worse
4461
4541
  * on machines with real-time antivirus.
4462
4542
  */
4463
- async ensureInstalled(npmPackage) {
4543
+ async ensureInstalled(config) {
4544
+ const npmPackage = config.npmPackage;
4545
+ if (!npmPackage) return;
4464
4546
  ensureIntegrationsDir();
4547
+ const pinnedVersion = this.pinnedVersionFor(config);
4548
+ if (pinnedVersion) {
4549
+ if (this.readInstalledVersion(npmPackage) === pinnedVersion) return;
4550
+ return withInstallLock(async () => {
4551
+ const current = this.readInstalledVersion(npmPackage);
4552
+ if (current === pinnedVersion) return;
4553
+ console.error(
4554
+ `[IntegrationManager] ${npmPackage} ${current ?? "(not installed)"} \u2192 ${pinnedVersion} (pinned by manifest)`
4555
+ );
4556
+ const cleanFirst = !this.isUsableInstall(npmPackage);
4557
+ await this.installNow(npmPackage, pinnedVersion, cleanFirst);
4558
+ });
4559
+ }
4465
4560
  if (this.isUsableInstall(npmPackage)) return;
4466
4561
  return withInstallLock(async () => {
4467
4562
  if (this.isUsableInstall(npmPackage)) return;
4468
4563
  await this.installNow(npmPackage);
4469
4564
  });
4470
4565
  }
4471
- /** The actual install. Callers MUST hold the install lock. */
4472
- async installNow(npmPackage) {
4566
+ /**
4567
+ * The actual install. Callers MUST hold the install lock.
4568
+ *
4569
+ * `exactVersion`, when given, is a pin already validated as an exact semver
4570
+ * by the manifest loader (`isExactSemver`) — re-validated here anyway
4571
+ * because this method does not trust its caller's caller, and because the
4572
+ * version reaches a shell on Windows (see the package-name check below).
4573
+ *
4574
+ * `cleanFirst` (default true, i.e. today's behaviour for every caller
4575
+ * except a version-only pin correction): wipe any existing package
4576
+ * directory before installing. Needed for a genuinely broken/partial
4577
+ * install, where stale files can make the reinstall itself fail; skipped
4578
+ * for a USABLE install being corrected to a different pinned version, so a
4579
+ * failed update leaves the previously-working version on disk instead of
4580
+ * nothing at all.
4581
+ */
4582
+ async installNow(npmPackage, exactVersion, cleanFirst = true) {
4473
4583
  const pkgDir = join13(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
4474
- if (existsSync13(pkgDir)) {
4475
- console.error(`[IntegrationManager] Removing unusable install of ${npmPackage}`);
4584
+ if (cleanFirst && existsSync13(pkgDir)) {
4585
+ console.error(`[IntegrationManager] Removing ${exactVersion ? "outdated" : "unusable"} install of ${npmPackage}`);
4476
4586
  try {
4477
4587
  rmSync(pkgDir, { recursive: true, force: true });
4478
4588
  } catch (err) {
@@ -4482,18 +4592,22 @@ var IntegrationManager = class {
4482
4592
  if (!/^(@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*$/i.test(npmPackage)) {
4483
4593
  throw new Error(`Refusing to install unsafe package name: ${JSON.stringify(npmPackage)}`);
4484
4594
  }
4485
- console.error(`[IntegrationManager] Installing ${npmPackage}...`);
4595
+ if (exactVersion !== void 0 && !isExactSemver(exactVersion)) {
4596
+ throw new Error(`Refusing to install ${npmPackage} at unsafe/non-exact version spec: ${JSON.stringify(exactVersion)}`);
4597
+ }
4598
+ const spec = exactVersion ? `${npmPackage}@${exactVersion}` : npmPackage;
4599
+ console.error(`[IntegrationManager] Installing ${spec}...`);
4486
4600
  const startedAt = Date.now();
4487
4601
  const isWin = process.platform === "win32";
4488
4602
  await new Promise((resolve3, reject) => {
4489
4603
  const child = execFile(
4490
4604
  "npm",
4491
- ["install", "--no-fund", "--no-audit", npmPackage],
4605
+ ["install", "--no-fund", "--no-audit", spec],
4492
4606
  {
4493
4607
  cwd: INTEGRATIONS_DIR,
4494
4608
  // Windows: a shell so cmd.exe finds npm.cmd (see EINVAL note above).
4495
4609
  // POSIX: no shell — npm is a normal executable and keeping shell off
4496
- // means the validated package name is still passed as a bare argv.
4610
+ // means the validated package spec is still passed as a bare argv.
4497
4611
  shell: isWin,
4498
4612
  // No timeout: a slow install on a machine with antivirus is not an
4499
4613
  // error, and killing it midway is what created broken installs in
@@ -4503,20 +4617,28 @@ var IntegrationManager = class {
4503
4617
  },
4504
4618
  (err, _stdout, stderr) => {
4505
4619
  if (err) {
4506
- reject(new Error(`Failed to install ${npmPackage}: ${err.message}${stderr ? ` \u2014 ${stderr.trim()}` : ""}`));
4620
+ reject(new Error(`Failed to install ${spec}: ${err.message}${stderr ? ` \u2014 ${stderr.trim()}` : ""}`));
4507
4621
  return;
4508
4622
  }
4509
4623
  resolve3();
4510
4624
  }
4511
4625
  );
4512
- child.on("error", (err) => reject(new Error(`Failed to run npm for ${npmPackage}: ${err.message}`)));
4626
+ child.on("error", (err) => reject(new Error(`Failed to run npm for ${spec}: ${err.message}`)));
4513
4627
  });
4514
4628
  if (!this.isUsableInstall(npmPackage)) {
4515
4629
  throw new Error(
4516
- `Installed ${npmPackage} but it is not usable \u2014 package.json or its bin target is missing. Check ${INTEGRATIONS_DIR} for a partial install.`
4630
+ `Installed ${spec} but it is not usable \u2014 package.json or its bin target is missing. Check ${INTEGRATIONS_DIR} for a partial install.`
4517
4631
  );
4518
4632
  }
4519
- console.error(`[IntegrationManager] Installed ${npmPackage} (${((Date.now() - startedAt) / 1e3).toFixed(1)}s)`);
4633
+ if (exactVersion !== void 0) {
4634
+ const installed = this.readInstalledVersion(npmPackage);
4635
+ if (installed !== exactVersion) {
4636
+ throw new Error(
4637
+ `Installed ${npmPackage} but got version ${installed ?? "unknown"}, expected the pinned ${exactVersion}.`
4638
+ );
4639
+ }
4640
+ }
4641
+ console.error(`[IntegrationManager] Installed ${spec} (${((Date.now() - startedAt) / 1e3).toFixed(1)}s)`);
4520
4642
  }
4521
4643
  /**
4522
4644
  * Connect a client to its transport, but never wait indefinitely. Resolves
@@ -4575,7 +4697,26 @@ var IntegrationManager = class {
4575
4697
  throw new Error(`${config.id}: ${SIMULATION_BLOCK_REASON}`);
4576
4698
  }
4577
4699
  if (config.npmPackage) {
4578
- await this.ensureInstalled(config.npmPackage);
4700
+ await this.ensureInstalled(config);
4701
+ }
4702
+ const pinnedVersion = this.pinnedVersionFor(config);
4703
+ if (pinnedVersion && config.command === "npx") {
4704
+ const manifest = getManifest(config.id);
4705
+ const migrated = {
4706
+ command: manifest.mcp.command,
4707
+ args: manifest.mcp.args,
4708
+ env: manifest.mcp.env ?? config.env
4709
+ };
4710
+ console.error(
4711
+ `[IntegrationManager] ${config.id}: command migrated to manifest (was npx ${config.args.join(" ")})`
4712
+ );
4713
+ config = { ...config, ...migrated };
4714
+ this.onConfigMigrated?.(config.id, migrated);
4715
+ }
4716
+ if (pinnedVersion && config.command === "npx") {
4717
+ throw new Error(
4718
+ `${config.id}: refusing to spawn a pinned connector ("${config.npmPackage}"@${pinnedVersion}) via npx \u2014 this would ignore the pinned local install and fetch the package fresh on every start. The manifest's mcp.command must name the installed binary, not "npx".`
4719
+ );
4579
4720
  }
4580
4721
  const env = this.resolveEnvKeys(config);
4581
4722
  if (isSimulationMode()) {
@@ -5129,6 +5270,10 @@ function refreshAllSessions() {
5129
5270
  integrationManager.setOnToolsChanged(() => {
5130
5271
  refreshAllSessions();
5131
5272
  });
5273
+ integrationManager.setOnConfigMigrated((id, updates) => {
5274
+ integrationRegistry.update(id, updates);
5275
+ console.error(`[Suveren MCP] ${id}: persisted config migrated (${Object.keys(updates).join(", ")})`);
5276
+ });
5132
5277
  var app = express();
5133
5278
  app.use(express.json());
5134
5279
  app.use((_req, res, next) => {
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Types for the plain-ESM Node version check (see config.d.mts for why the
3
+ * .mjs modules ship declarations).
4
+ */
5
+
6
+ export declare const MIN_NODE_MAJOR: number;
7
+ export declare function unsupportedNodeReason(version: string): string | null;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Minimum Node.js version for the gateway — checked at start by
3
+ * bundle/bin/suveren-gateway.js and bundle/server.js.
4
+ *
5
+ * The connectors the gateway installs (crm, erp, email, records — all built on
6
+ * better-sqlite3 13) need Node 22. `engines` in package.json is not enough:
7
+ * npm only WARNS on a mismatch, and for an unpinned install it silently picks
8
+ * an older connector that still runs on the old Node. With pinned connector
9
+ * versions the pinned connector is installed anyway and crashes on its first
10
+ * database call. Refusing at start, with the reason, is the only visible
11
+ * failure. Keep MIN_NODE_MAJOR in step with `engines` in package.json.tpl.
12
+ */
13
+
14
+ export const MIN_NODE_MAJOR = 22;
15
+
16
+ /**
17
+ * Returns null when `version` (e.g. process.versions.node, "22.11.0") is
18
+ * supported, otherwise a message naming both versions. An unparseable version
19
+ * is refused — the safe direction.
20
+ */
21
+ export function unsupportedNodeReason(version) {
22
+ const major = Number.parseInt(String(version).split('.')[0], 10);
23
+ if (Number.isInteger(major) && major >= MIN_NODE_MAJOR) return null;
24
+ return (
25
+ `Suveren gateway needs Node.js ${MIN_NODE_MAJOR} or newer — this is Node.js ${version}. ` +
26
+ `Install a current Node.js (https://nodejs.org) and start the gateway again.`
27
+ );
28
+ }