@deployfoundation/foundation-deploy 0.2.19 → 0.2.20

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.
@@ -119,18 +119,132 @@ var GOOGLE_CALENDAR_EVENTS_SCOPE = [
119
119
  var GOOGLE_CALENDAR_JSON_MAX_BYTES = 2 * 1024 * 1024;
120
120
  // ../connectors/src/otter.ts
121
121
  var OTTER_MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
122
+ // ../connectors/src/remote-mcp.ts
123
+ var REMOTE_MCP_PROTOCOL_VERSIONS = ["2025-11-25", "2025-06-18", "2025-03-26"];
124
+ var REMOTE_MCP_MAX_RESPONSE_BYTES = 1024 * 1024;
125
+ var REMOTE_MCP_MAX_REQUEST_BYTES = 256 * 1024;
126
+ var MAX_URL_CHARS = 2048;
127
+ var MAX_TOKEN_TTL_SECONDS = 31 * 24 * 60 * 60;
128
+ class RemoteMcpResponseTooLargeError extends Error {
129
+ constructor() {
130
+ super(`remote MCP response exceeded ${REMOTE_MCP_MAX_RESPONSE_BYTES} bytes`);
131
+ this.name = "RemoteMcpResponseTooLargeError";
132
+ }
133
+ }
134
+ class RemoteMcpTimeoutError extends Error {
135
+ constructor() {
136
+ super("remote MCP did not answer in time");
137
+ this.name = "RemoteMcpTimeoutError";
138
+ }
139
+ }
140
+ function defineRemoteMcpProvider(input) {
141
+ const protocolVersions = [...input.protocolVersions ?? REMOTE_MCP_PROTOCOL_VERSIONS];
142
+ if (protocolVersions.length === 0 || new Set(protocolVersions).size !== protocolVersions.length || !protocolVersions.every((version) => /^\d{4}-\d{2}-\d{2}$/.test(version)))
143
+ throw new Error("remote MCP provider: invalid protocolVersions");
144
+ if (!/^[A-Za-z0-9_.-]{1,128}$/.test(input.clientName))
145
+ throw new Error("remote MCP provider: invalid clientName");
146
+ return Object.freeze({
147
+ endpoint: httpsUrl(input.endpoint, "remote MCP provider: endpoint"),
148
+ issuer: httpsUrl(input.issuer, "remote MCP provider: issuer"),
149
+ tokenUrl: httpsUrl(input.tokenUrl, "remote MCP provider: tokenUrl"),
150
+ clientName: input.clientName,
151
+ protocolVersions: Object.freeze(protocolVersions)
152
+ });
153
+ }
154
+ async function readBoundedRemoteMcpResponse(response, maxBytes = REMOTE_MCP_MAX_RESPONSE_BYTES, signal) {
155
+ const chunks = [];
156
+ let total = 0;
157
+ await consumeBody(response, maxBytes, signal, (chunk) => {
158
+ chunks.push(chunk);
159
+ total += chunk.byteLength;
160
+ return false;
161
+ });
162
+ const bytes = new Uint8Array(total);
163
+ let offset = 0;
164
+ for (const chunk of chunks) {
165
+ bytes.set(chunk, offset);
166
+ offset += chunk.byteLength;
167
+ }
168
+ return new TextDecoder().decode(bytes);
169
+ }
170
+ async function consumeBody(response, maxBytes, signal, onChunk) {
171
+ const declaredBytes = Number(response.headers.get("content-length") ?? "");
172
+ if (Number.isFinite(declaredBytes) && declaredBytes > maxBytes) {
173
+ await response.body?.cancel().catch(() => {
174
+ return;
175
+ });
176
+ throw new RemoteMcpResponseTooLargeError;
177
+ }
178
+ const reader = response.body?.getReader();
179
+ if (reader === undefined)
180
+ return 0;
181
+ const cancel = () => {
182
+ reader.cancel().catch(() => {
183
+ return;
184
+ });
185
+ };
186
+ const aborted = () => signal?.aborted === true;
187
+ if (aborted()) {
188
+ cancel();
189
+ throw new RemoteMcpTimeoutError;
190
+ }
191
+ signal?.addEventListener("abort", cancel, { once: true });
192
+ let total = 0;
193
+ try {
194
+ while (true) {
195
+ let next;
196
+ try {
197
+ next = await reader.read();
198
+ } catch {
199
+ if (aborted())
200
+ throw new RemoteMcpTimeoutError;
201
+ throw new Error("remote MCP response could not be read");
202
+ }
203
+ if (aborted())
204
+ throw new RemoteMcpTimeoutError;
205
+ if (next.done)
206
+ return total;
207
+ total += next.value.byteLength;
208
+ if (total > maxBytes) {
209
+ cancel();
210
+ throw new RemoteMcpResponseTooLargeError;
211
+ }
212
+ if (onChunk(next.value)) {
213
+ cancel();
214
+ return total;
215
+ }
216
+ }
217
+ } finally {
218
+ signal?.removeEventListener("abort", cancel);
219
+ }
220
+ }
221
+ function httpsUrl(value, label) {
222
+ let url;
223
+ try {
224
+ url = new URL(value);
225
+ } catch {
226
+ throw new Error(`${label} must be an HTTPS URL`);
227
+ }
228
+ if (value.length > MAX_URL_CHARS || url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.hash !== "" || url.search !== "")
229
+ throw new Error(`${label} must be an HTTPS URL`);
230
+ return value;
231
+ }
232
+
122
233
  // ../connectors/src/knock.ts
234
+ var KNOCK_MCP_ENDPOINT = "https://mcp.knock.app/mcp";
235
+ var KNOCK_OAUTH_ISSUER = "https://mcp.knock.app";
236
+ var KNOCK_TOKEN_URL = "https://mcp.knock.app/token";
123
237
  var KNOCK_REGISTER_URL = "https://mcp.knock.app/register";
124
- var KNOCK_MAX_RESPONSE_BYTES = 1024 * 1024;
125
- var KNOCK_MAX_REQUEST_BYTES = 256 * 1024;
238
+ var KNOCK_MCP_PROTOCOL_VERSION = "2025-06-18";
239
+ var KNOCK_MCP_PROVIDER = defineRemoteMcpProvider({
240
+ endpoint: KNOCK_MCP_ENDPOINT,
241
+ issuer: KNOCK_OAUTH_ISSUER,
242
+ tokenUrl: KNOCK_TOKEN_URL,
243
+ clientName: "foundation-knock-proxy",
244
+ protocolVersions: [KNOCK_MCP_PROTOCOL_VERSION]
245
+ });
126
246
  var MAX_CLIENT_ID_CHARS = 2048;
127
247
  var MAX_REDIRECT_URI_CHARS = 2048;
128
- class KnockResponseTooLargeError extends Error {
129
- constructor() {
130
- super(`Knock response exceeded ${KNOCK_MAX_RESPONSE_BYTES} bytes`);
131
- this.name = "KnockResponseTooLargeError";
132
- }
133
- }
134
248
  function parseKnockOAuthClient(raw) {
135
249
  const value = parseJsonObject(raw, "Knock OAuth client");
136
250
  const keys = Object.keys(value);
@@ -162,46 +276,12 @@ async function registerKnockPublicClient(args) {
162
276
  }
163
277
  if (!response.ok)
164
278
  throw new Error("Knock OAuth registration was rejected");
165
- const body = parseJsonObject(await readBoundedKnockResponse(response), "Knock OAuth registration");
279
+ const body = parseJsonObject(await readBoundedRemoteMcpResponse(response), "Knock OAuth registration");
166
280
  return {
167
281
  client_id: requiredOpaqueString(body.client_id, "Knock OAuth registration: missing client_id", MAX_CLIENT_ID_CHARS),
168
282
  redirect_uri: redirectUri
169
283
  };
170
284
  }
171
- async function readBoundedKnockResponse(response, maxBytes = KNOCK_MAX_RESPONSE_BYTES) {
172
- const declaredBytes = Number(response.headers.get("content-length") ?? "");
173
- if (Number.isFinite(declaredBytes) && declaredBytes > maxBytes) {
174
- await response.body?.cancel().catch(() => {
175
- return;
176
- });
177
- throw new KnockResponseTooLargeError;
178
- }
179
- const reader = response.body?.getReader();
180
- if (reader === undefined)
181
- return "";
182
- const chunks = [];
183
- let total = 0;
184
- while (true) {
185
- const { done, value } = await reader.read();
186
- if (done)
187
- break;
188
- total += value.byteLength;
189
- if (total > maxBytes) {
190
- await reader.cancel().catch(() => {
191
- return;
192
- });
193
- throw new KnockResponseTooLargeError;
194
- }
195
- chunks.push(value);
196
- }
197
- const bytes = new Uint8Array(total);
198
- let offset = 0;
199
- for (const chunk of chunks) {
200
- bytes.set(chunk, offset);
201
- offset += chunk.byteLength;
202
- }
203
- return new TextDecoder().decode(bytes);
204
- }
205
285
  function parseJsonObject(raw, label) {
206
286
  let value;
207
287
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deployfoundation/foundation-deploy",
3
- "version": "0.2.19",
3
+ "version": "0.2.20",
4
4
  "description": "Deploys one Foundation instance from a published, signed release: the CDK app and the deploy tool.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,