@matthesketh/utopia-ai 0.5.0 → 0.7.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.
package/dist/index.cjs CHANGED
@@ -256,6 +256,7 @@ async function* parseSSEStream(response) {
256
256
  try {
257
257
  yield JSON.parse(data);
258
258
  } catch {
259
+ console.warn("[utopia-ai] Skipping malformed SSE data:", data);
259
260
  }
260
261
  }
261
262
  }
package/dist/index.js CHANGED
@@ -227,6 +227,7 @@ async function* parseSSEStream(response) {
227
227
  try {
228
228
  yield JSON.parse(data);
229
229
  } catch {
230
+ console.warn("[utopia-ai] Skipping malformed SSE data:", data);
230
231
  }
231
232
  }
232
233
  }
@@ -43,6 +43,16 @@ function createMCPServer(config) {
43
43
  (config.prompts ?? []).map((p) => [p.definition.name, p])
44
44
  );
45
45
  async function handleRequest(request) {
46
+ if (!request || request.jsonrpc !== "2.0" || typeof request.method !== "string") {
47
+ return {
48
+ jsonrpc: "2.0",
49
+ id: request?.id ?? null,
50
+ error: {
51
+ code: -32600,
52
+ message: 'Invalid Request: must include jsonrpc "2.0" and a string method'
53
+ }
54
+ };
55
+ }
46
56
  try {
47
57
  const result = await dispatch(request);
48
58
  return { jsonrpc: "2.0", id: request.id, result };
@@ -239,11 +249,13 @@ function createMCPClient(config) {
239
249
 
240
250
  // src/mcp/handler.ts
241
251
  function createMCPHandler(server, options) {
242
- const corsOrigin = options?.corsOrigin ?? "*";
252
+ const corsOrigin = options?.corsOrigin;
243
253
  return async (req, res) => {
244
- res.setHeader("Access-Control-Allow-Origin", corsOrigin);
245
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
246
- res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
254
+ if (corsOrigin) {
255
+ res.setHeader("Access-Control-Allow-Origin", corsOrigin);
256
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
257
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
258
+ }
247
259
  if (req.method === "OPTIONS") {
248
260
  res.writeHead(204);
249
261
  res.end();
@@ -270,6 +282,18 @@ async function handlePost(server, req, res) {
270
282
  res.writeHead(200, { "Content-Type": "application/json" });
271
283
  res.end(JSON.stringify(response));
272
284
  } catch (err) {
285
+ const errObj = err;
286
+ if (errObj.statusCode === 413) {
287
+ res.writeHead(413, { "Content-Type": "application/json" });
288
+ res.end(
289
+ JSON.stringify({
290
+ jsonrpc: "2.0",
291
+ id: null,
292
+ error: { code: -32600, message: "Payload too large" }
293
+ })
294
+ );
295
+ return;
296
+ }
273
297
  const data = err instanceof SyntaxError ? err.message : "Invalid request";
274
298
  res.writeHead(400, { "Content-Type": "application/json" });
275
299
  res.end(
@@ -303,10 +327,20 @@ data: ${endpointUrl}
303
327
  clearInterval(keepAlive);
304
328
  });
305
329
  }
306
- function readBody(req) {
330
+ var DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
331
+ function readBody(req, maxSize = DEFAULT_MAX_BODY_SIZE) {
307
332
  return new Promise((resolve, reject) => {
308
333
  const chunks = [];
309
- req.on("data", (chunk) => chunks.push(chunk));
334
+ let size = 0;
335
+ req.on("data", (chunk) => {
336
+ size += chunk.length;
337
+ if (size > maxSize) {
338
+ req.removeAllListeners("data");
339
+ reject(Object.assign(new Error("Payload too large"), { statusCode: 413 }));
340
+ return;
341
+ }
342
+ chunks.push(chunk);
343
+ });
310
344
  req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
311
345
  req.on("error", reject);
312
346
  });
package/dist/mcp/index.js CHANGED
@@ -15,6 +15,16 @@ function createMCPServer(config) {
15
15
  (config.prompts ?? []).map((p) => [p.definition.name, p])
16
16
  );
17
17
  async function handleRequest(request) {
18
+ if (!request || request.jsonrpc !== "2.0" || typeof request.method !== "string") {
19
+ return {
20
+ jsonrpc: "2.0",
21
+ id: request?.id ?? null,
22
+ error: {
23
+ code: -32600,
24
+ message: 'Invalid Request: must include jsonrpc "2.0" and a string method'
25
+ }
26
+ };
27
+ }
18
28
  try {
19
29
  const result = await dispatch(request);
20
30
  return { jsonrpc: "2.0", id: request.id, result };
@@ -211,11 +221,13 @@ function createMCPClient(config) {
211
221
 
212
222
  // src/mcp/handler.ts
213
223
  function createMCPHandler(server, options) {
214
- const corsOrigin = options?.corsOrigin ?? "*";
224
+ const corsOrigin = options?.corsOrigin;
215
225
  return async (req, res) => {
216
- res.setHeader("Access-Control-Allow-Origin", corsOrigin);
217
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
218
- res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
226
+ if (corsOrigin) {
227
+ res.setHeader("Access-Control-Allow-Origin", corsOrigin);
228
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
229
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
230
+ }
219
231
  if (req.method === "OPTIONS") {
220
232
  res.writeHead(204);
221
233
  res.end();
@@ -242,6 +254,18 @@ async function handlePost(server, req, res) {
242
254
  res.writeHead(200, { "Content-Type": "application/json" });
243
255
  res.end(JSON.stringify(response));
244
256
  } catch (err) {
257
+ const errObj = err;
258
+ if (errObj.statusCode === 413) {
259
+ res.writeHead(413, { "Content-Type": "application/json" });
260
+ res.end(
261
+ JSON.stringify({
262
+ jsonrpc: "2.0",
263
+ id: null,
264
+ error: { code: -32600, message: "Payload too large" }
265
+ })
266
+ );
267
+ return;
268
+ }
245
269
  const data = err instanceof SyntaxError ? err.message : "Invalid request";
246
270
  res.writeHead(400, { "Content-Type": "application/json" });
247
271
  res.end(
@@ -275,10 +299,20 @@ data: ${endpointUrl}
275
299
  clearInterval(keepAlive);
276
300
  });
277
301
  }
278
- function readBody(req) {
302
+ var DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
303
+ function readBody(req, maxSize = DEFAULT_MAX_BODY_SIZE) {
279
304
  return new Promise((resolve, reject) => {
280
305
  const chunks = [];
281
- req.on("data", (chunk) => chunks.push(chunk));
306
+ let size = 0;
307
+ req.on("data", (chunk) => {
308
+ size += chunk.length;
309
+ if (size > maxSize) {
310
+ req.removeAllListeners("data");
311
+ reject(Object.assign(new Error("Payload too large"), { statusCode: 413 }));
312
+ return;
313
+ }
314
+ chunks.push(chunk);
315
+ });
282
316
  req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
283
317
  req.on("error", reject);
284
318
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matthesketh/utopia-ai",
3
- "version": "0.5.0",
3
+ "version": "0.7.1",
4
4
  "description": "AI adapters and MCP support for UtopiaJS",
5
5
  "type": "module",
6
6
  "license": "MIT",