@lantos1618/better-ui 0.5.0 → 0.6.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.
@@ -1,146 +1,11 @@
1
- import "../chunk-Y6FXYEAI.mjs";
2
-
3
- // src/mcp/schema.ts
4
- function zodToJsonSchema(schema) {
5
- return convert(schema);
6
- }
7
- function convert(schema) {
8
- const def = schema._def;
9
- const typeName = def?.typeName;
10
- switch (typeName) {
11
- case "ZodString":
12
- return convertString(def);
13
- case "ZodNumber":
14
- return convertNumber(def);
15
- case "ZodBoolean":
16
- return { type: "boolean" };
17
- case "ZodNull":
18
- return { type: "null" };
19
- case "ZodLiteral":
20
- return { enum: [def.value] };
21
- case "ZodEnum":
22
- return { type: "string", enum: def.values };
23
- case "ZodNativeEnum":
24
- return { enum: Object.values(def.values) };
25
- case "ZodObject":
26
- return convertObject(def);
27
- case "ZodArray":
28
- return convertArray(def);
29
- case "ZodOptional":
30
- return convert(def.innerType);
31
- case "ZodNullable": {
32
- const inner = convert(def.innerType);
33
- return { anyOf: [inner, { type: "null" }] };
34
- }
35
- case "ZodDefault":
36
- return { ...convert(def.innerType), default: def.defaultValue() };
37
- case "ZodUnion":
38
- return { anyOf: def.options.map((o) => convert(o)) };
39
- case "ZodDiscriminatedUnion":
40
- return { oneOf: [...def.options.values()].map((o) => convert(o)) };
41
- case "ZodRecord":
42
- return {
43
- type: "object",
44
- additionalProperties: convert(def.valueType)
45
- };
46
- case "ZodTuple": {
47
- const items = def.items.map((item) => convert(item));
48
- return { type: "array", items: items.length === 1 ? items[0] : void 0, prefixItems: items };
49
- }
50
- case "ZodEffects":
51
- return convert(def.schema);
52
- case "ZodPipeline":
53
- return convert(def.in);
54
- case "ZodLazy":
55
- return convert(def.getter());
56
- case "ZodAny":
57
- return {};
58
- case "ZodUnknown":
59
- return {};
60
- default:
61
- return {};
62
- }
63
- }
64
- function convertString(def) {
65
- const schema = { type: "string" };
66
- if (def.checks) {
67
- for (const check of def.checks) {
68
- switch (check.kind) {
69
- case "min":
70
- schema.minLength = check.value;
71
- break;
72
- case "max":
73
- schema.maxLength = check.value;
74
- break;
75
- case "email":
76
- schema.format = "email";
77
- break;
78
- case "url":
79
- schema.format = "uri";
80
- break;
81
- case "uuid":
82
- schema.format = "uuid";
83
- break;
84
- case "regex":
85
- schema.pattern = check.regex.source;
86
- break;
87
- }
88
- }
89
- }
90
- if (def.description) schema.description = def.description;
91
- return schema;
92
- }
93
- function convertNumber(def) {
94
- const schema = { type: "number" };
95
- if (def.checks) {
96
- for (const check of def.checks) {
97
- switch (check.kind) {
98
- case "min":
99
- schema.minimum = check.value;
100
- if (check.inclusive === false) schema.exclusiveMinimum = check.value;
101
- break;
102
- case "max":
103
- schema.maximum = check.value;
104
- if (check.inclusive === false) schema.exclusiveMaximum = check.value;
105
- break;
106
- case "int":
107
- schema.type = "integer";
108
- break;
109
- }
110
- }
111
- }
112
- if (def.description) schema.description = def.description;
113
- return schema;
114
- }
115
- function convertObject(def) {
116
- const shape = def.shape();
117
- const properties = {};
118
- const required = [];
119
- for (const [key, value] of Object.entries(shape)) {
120
- properties[key] = convert(value);
121
- const fieldDef = value._def;
122
- const isOptional = fieldDef?.typeName === "ZodOptional" || fieldDef?.typeName === "ZodDefault";
123
- if (!isOptional) {
124
- required.push(key);
125
- }
126
- }
127
- const schema = { type: "object", properties };
128
- if (required.length > 0) schema.required = required;
129
- if (def.description) schema.description = def.description;
130
- return schema;
131
- }
132
- function convertArray(def) {
133
- const schema = {
134
- type: "array",
135
- items: convert(def.type)
136
- };
137
- if (def.minLength) schema.minItems = def.minLength.value;
138
- if (def.maxLength) schema.maxItems = def.maxLength.value;
139
- if (def.description) schema.description = def.description;
140
- return schema;
141
- }
1
+ import {
2
+ init_schema,
3
+ zodToJsonSchema
4
+ } from "../chunk-XDWHMRBN.mjs";
5
+ import "../chunk-CIESM3BP.mjs";
142
6
 
143
7
  // src/mcp/server.ts
8
+ init_schema();
144
9
  var PARSE_ERROR = -32700;
145
10
  var INVALID_REQUEST = -32600;
146
11
  var METHOD_NOT_FOUND = -32601;
@@ -368,6 +233,84 @@ var MCPServer = class {
368
233
  return Response.json(response);
369
234
  };
370
235
  }
236
+ /**
237
+ * Create a Streamable HTTP handler (MCP spec 2025-03-26).
238
+ * Supports both single JSON-RPC requests and SSE streaming for long-running operations.
239
+ * Compatible with Next.js route handlers, Deno, Bun, Cloudflare Workers.
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * // Next.js route: app/api/mcp/route.ts
244
+ * import { server } from '@/lib/mcp';
245
+ * export const POST = server.streamableHttpHandler();
246
+ * ```
247
+ */
248
+ streamableHttpHandler() {
249
+ return async (req) => {
250
+ const accept = req.headers.get("accept") || "";
251
+ const contentType = req.headers.get("content-type") || "";
252
+ if (!contentType.includes("application/json")) {
253
+ return Response.json(
254
+ { jsonrpc: "2.0", id: null, error: { code: PARSE_ERROR, message: "Content-Type must be application/json" } },
255
+ { status: 400 }
256
+ );
257
+ }
258
+ let message;
259
+ try {
260
+ message = await req.json();
261
+ } catch {
262
+ return Response.json(
263
+ { jsonrpc: "2.0", id: null, error: { code: PARSE_ERROR, message: "Parse error" } },
264
+ { status: 400 }
265
+ );
266
+ }
267
+ if (!message.jsonrpc || message.jsonrpc !== "2.0") {
268
+ return Response.json(
269
+ { jsonrpc: "2.0", id: message.id ?? null, error: { code: INVALID_REQUEST, message: "Invalid JSON-RPC version" } },
270
+ { status: 400 }
271
+ );
272
+ }
273
+ if (accept.includes("text/event-stream")) {
274
+ const encoder = new TextEncoder();
275
+ const self = this;
276
+ const sseStream = new ReadableStream({
277
+ async start(controller) {
278
+ try {
279
+ const response2 = await self.handleMessage(message);
280
+ if (response2) {
281
+ controller.enqueue(encoder.encode(`event: message
282
+ data: ${JSON.stringify(response2)}
283
+
284
+ `));
285
+ }
286
+ } catch (err) {
287
+ const errorResponse = {
288
+ jsonrpc: "2.0",
289
+ id: message.id ?? null,
290
+ error: { code: INTERNAL_ERROR, message: err instanceof Error ? err.message : "Internal error" }
291
+ };
292
+ controller.enqueue(encoder.encode(`event: message
293
+ data: ${JSON.stringify(errorResponse)}
294
+
295
+ `));
296
+ }
297
+ controller.close();
298
+ }
299
+ });
300
+ return new Response(sseStream, {
301
+ headers: {
302
+ "Content-Type": "text/event-stream",
303
+ "Cache-Control": "no-cache"
304
+ }
305
+ });
306
+ }
307
+ const response = await this.handleMessage(message);
308
+ if (!response) {
309
+ return new Response(null, { status: 204 });
310
+ }
311
+ return Response.json(response);
312
+ };
313
+ }
371
314
  };
372
315
  var McpError = class extends Error {
373
316
  constructor(code, message) {
@@ -379,6 +322,9 @@ var McpError = class extends Error {
379
322
  function createMCPServer(config) {
380
323
  return new MCPServer(config);
381
324
  }
325
+
326
+ // src/mcp/index.ts
327
+ init_schema();
382
328
  export {
383
329
  MCPServer,
384
330
  createMCPServer,
@@ -1,4 +1,4 @@
1
- import "../chunk-Y6FXYEAI.mjs";
1
+ import "../chunk-CIESM3BP.mjs";
2
2
 
3
3
  // src/persistence/memory.ts
4
4
  function createMemoryAdapter() {
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import "../chunk-Y6FXYEAI.mjs";
2
+ import "../chunk-CIESM3BP.mjs";
3
3
 
4
4
  // src/react/useTool.ts
5
5
  import { useState, useCallback, useEffect, useRef } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lantos1618/better-ui",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A minimal, type-safe AI-first UI framework for building tools",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -36,6 +36,11 @@
36
36
  "import": "./dist/mcp/index.mjs",
37
37
  "require": "./dist/mcp/index.js"
38
38
  },
39
+ "./agui": {
40
+ "types": "./dist/agui/index.d.ts",
41
+ "import": "./dist/agui/index.mjs",
42
+ "require": "./dist/agui/index.js"
43
+ },
39
44
  "./theme.css": "./src/theme.css"
40
45
  },
41
46
  "files": [
@@ -50,7 +55,11 @@
50
55
  "ai",
51
56
  "react",
52
57
  "tools",
53
- "typescript"
58
+ "typescript",
59
+ "mcp",
60
+ "ag-ui",
61
+ "model-context-protocol",
62
+ "ai-tools"
54
63
  ],
55
64
  "author": "Lyndon Leong",
56
65
  "license": "MIT",
@@ -60,7 +69,7 @@
60
69
  },
61
70
  "scripts": {
62
71
  "build": "npm run build:lib",
63
- "build:lib": "tsup src/index.ts src/react/index.ts src/components/index.ts src/auth/index.ts src/persistence/index.ts src/mcp/index.ts --format cjs,esm --dts --clean --tsconfig tsconfig.lib.json",
72
+ "build:lib": "tsup src/index.ts src/react/index.ts src/components/index.ts src/auth/index.ts src/persistence/index.ts src/mcp/index.ts src/agui/index.ts --format cjs,esm --dts --clean --tsconfig tsconfig.lib.json",
64
73
  "test": "jest",
65
74
  "type-check": "tsc --noEmit",
66
75
  "prepublishOnly": "npm run build:lib"
@@ -1,10 +0,0 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
- export {
9
- __require
10
- };