@lantos1618/better-ui 0.8.0 → 0.9.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.
- package/dist/mcp/index.d.mts +3 -31
- package/dist/mcp/index.d.ts +3 -31
- package/dist/openapi/index.d.mts +113 -0
- package/dist/openapi/index.d.ts +113 -0
- package/dist/openapi/index.js +260 -0
- package/dist/openapi/index.mjs +97 -0
- package/dist/schema-DdZf6066.d.mts +32 -0
- package/dist/schema-DdZf6066.d.ts +32 -0
- package/package.json +7 -2
package/dist/mcp/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { T as Tool, g as ToolContext } from '../tool-yZTixiN2.mjs';
|
|
2
|
-
|
|
2
|
+
export { z as zodToJsonSchema } from '../schema-DdZf6066.mjs';
|
|
3
|
+
import 'zod';
|
|
3
4
|
import 'react';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -127,33 +128,4 @@ declare class MCPServer {
|
|
|
127
128
|
*/
|
|
128
129
|
declare function createMCPServer(config: MCPServerConfig): MCPServer;
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
* Lightweight Zod-to-JSON-Schema converter.
|
|
132
|
-
* Handles common Zod types without requiring zod-to-json-schema dependency.
|
|
133
|
-
*/
|
|
134
|
-
|
|
135
|
-
interface JsonSchema {
|
|
136
|
-
type?: string;
|
|
137
|
-
properties?: Record<string, JsonSchema>;
|
|
138
|
-
required?: string[];
|
|
139
|
-
items?: JsonSchema;
|
|
140
|
-
enum?: unknown[];
|
|
141
|
-
description?: string;
|
|
142
|
-
default?: unknown;
|
|
143
|
-
minimum?: number;
|
|
144
|
-
maximum?: number;
|
|
145
|
-
minLength?: number;
|
|
146
|
-
maxLength?: number;
|
|
147
|
-
pattern?: string;
|
|
148
|
-
format?: string;
|
|
149
|
-
anyOf?: JsonSchema[];
|
|
150
|
-
oneOf?: JsonSchema[];
|
|
151
|
-
nullable?: boolean;
|
|
152
|
-
additionalProperties?: boolean | JsonSchema;
|
|
153
|
-
[key: string]: unknown;
|
|
154
|
-
}
|
|
155
|
-
declare function zodToJsonSchema(schema: z.ZodType | {
|
|
156
|
-
[key: string]: any;
|
|
157
|
-
}): JsonSchema;
|
|
158
|
-
|
|
159
|
-
export { MCPServer, type MCPServerConfig, createMCPServer, zodToJsonSchema };
|
|
131
|
+
export { MCPServer, type MCPServerConfig, createMCPServer };
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { T as Tool, g as ToolContext } from '../tool-yZTixiN2.js';
|
|
2
|
-
|
|
2
|
+
export { z as zodToJsonSchema } from '../schema-DdZf6066.js';
|
|
3
|
+
import 'zod';
|
|
3
4
|
import 'react';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -127,33 +128,4 @@ declare class MCPServer {
|
|
|
127
128
|
*/
|
|
128
129
|
declare function createMCPServer(config: MCPServerConfig): MCPServer;
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
* Lightweight Zod-to-JSON-Schema converter.
|
|
132
|
-
* Handles common Zod types without requiring zod-to-json-schema dependency.
|
|
133
|
-
*/
|
|
134
|
-
|
|
135
|
-
interface JsonSchema {
|
|
136
|
-
type?: string;
|
|
137
|
-
properties?: Record<string, JsonSchema>;
|
|
138
|
-
required?: string[];
|
|
139
|
-
items?: JsonSchema;
|
|
140
|
-
enum?: unknown[];
|
|
141
|
-
description?: string;
|
|
142
|
-
default?: unknown;
|
|
143
|
-
minimum?: number;
|
|
144
|
-
maximum?: number;
|
|
145
|
-
minLength?: number;
|
|
146
|
-
maxLength?: number;
|
|
147
|
-
pattern?: string;
|
|
148
|
-
format?: string;
|
|
149
|
-
anyOf?: JsonSchema[];
|
|
150
|
-
oneOf?: JsonSchema[];
|
|
151
|
-
nullable?: boolean;
|
|
152
|
-
additionalProperties?: boolean | JsonSchema;
|
|
153
|
-
[key: string]: unknown;
|
|
154
|
-
}
|
|
155
|
-
declare function zodToJsonSchema(schema: z.ZodType | {
|
|
156
|
-
[key: string]: any;
|
|
157
|
-
}): JsonSchema;
|
|
158
|
-
|
|
159
|
-
export { MCPServer, type MCPServerConfig, createMCPServer, zodToJsonSchema };
|
|
131
|
+
export { MCPServer, type MCPServerConfig, createMCPServer };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { T as Tool } from '../tool-yZTixiN2.mjs';
|
|
2
|
+
import { J as JsonSchema } from '../schema-DdZf6066.mjs';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* OpenAPI 3.1 spec generator for Better UI tools.
|
|
8
|
+
*
|
|
9
|
+
* Converts a tool registry into a valid OpenAPI document.
|
|
10
|
+
* Each tool becomes a POST endpoint at `/api/tools/{toolName}`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { generateOpenAPISpec } from '@lantos1618/better-ui/openapi';
|
|
15
|
+
* import { tools } from './tools';
|
|
16
|
+
*
|
|
17
|
+
* const spec = generateOpenAPISpec({
|
|
18
|
+
* title: 'My AI Tools API',
|
|
19
|
+
* version: '1.0.0',
|
|
20
|
+
* tools,
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Serve as JSON
|
|
24
|
+
* app.get('/openapi.json', (req, res) => res.json(spec));
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
interface OpenAPISpecConfig {
|
|
29
|
+
/** API title */
|
|
30
|
+
title: string;
|
|
31
|
+
/** API version */
|
|
32
|
+
version: string;
|
|
33
|
+
/** Tool registry */
|
|
34
|
+
tools: Record<string, Tool>;
|
|
35
|
+
/** API description */
|
|
36
|
+
description?: string;
|
|
37
|
+
/** Base URL for servers (default: '/') */
|
|
38
|
+
serverUrl?: string;
|
|
39
|
+
/** Base path prefix for tool endpoints (default: '/api/tools') */
|
|
40
|
+
basePath?: string;
|
|
41
|
+
/** Tags to group endpoints */
|
|
42
|
+
tags?: Array<{
|
|
43
|
+
name: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
interface OpenAPISpec {
|
|
48
|
+
openapi: '3.1.0';
|
|
49
|
+
info: {
|
|
50
|
+
title: string;
|
|
51
|
+
version: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
};
|
|
54
|
+
servers: Array<{
|
|
55
|
+
url: string;
|
|
56
|
+
}>;
|
|
57
|
+
paths: Record<string, PathItem>;
|
|
58
|
+
components: {
|
|
59
|
+
schemas: Record<string, JsonSchema>;
|
|
60
|
+
};
|
|
61
|
+
tags?: Array<{
|
|
62
|
+
name: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
interface PathItem {
|
|
67
|
+
post: {
|
|
68
|
+
operationId: string;
|
|
69
|
+
summary: string;
|
|
70
|
+
description?: string;
|
|
71
|
+
tags?: string[];
|
|
72
|
+
requestBody: {
|
|
73
|
+
required: true;
|
|
74
|
+
content: {
|
|
75
|
+
'application/json': {
|
|
76
|
+
schema: {
|
|
77
|
+
$ref: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
responses: Record<string, {
|
|
83
|
+
description: string;
|
|
84
|
+
content?: {
|
|
85
|
+
'application/json': {
|
|
86
|
+
schema: {
|
|
87
|
+
$ref: string;
|
|
88
|
+
} | JsonSchema;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
}>;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Generate an OpenAPI 3.1 spec from a Better UI tool registry.
|
|
96
|
+
*/
|
|
97
|
+
declare function generateOpenAPISpec(config: OpenAPISpecConfig): OpenAPISpec;
|
|
98
|
+
/**
|
|
99
|
+
* Create a request handler that serves the OpenAPI spec as JSON.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // Next.js route: app/api/openapi/route.ts
|
|
104
|
+
* export const GET = openAPIHandler({
|
|
105
|
+
* title: 'My Tools',
|
|
106
|
+
* version: '1.0.0',
|
|
107
|
+
* tools,
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function openAPIHandler(config: OpenAPISpecConfig): (req: Request) => Response;
|
|
112
|
+
|
|
113
|
+
export { type OpenAPISpec, type OpenAPISpecConfig, generateOpenAPISpec, openAPIHandler };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { T as Tool } from '../tool-yZTixiN2.js';
|
|
2
|
+
import { J as JsonSchema } from '../schema-DdZf6066.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* OpenAPI 3.1 spec generator for Better UI tools.
|
|
8
|
+
*
|
|
9
|
+
* Converts a tool registry into a valid OpenAPI document.
|
|
10
|
+
* Each tool becomes a POST endpoint at `/api/tools/{toolName}`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { generateOpenAPISpec } from '@lantos1618/better-ui/openapi';
|
|
15
|
+
* import { tools } from './tools';
|
|
16
|
+
*
|
|
17
|
+
* const spec = generateOpenAPISpec({
|
|
18
|
+
* title: 'My AI Tools API',
|
|
19
|
+
* version: '1.0.0',
|
|
20
|
+
* tools,
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Serve as JSON
|
|
24
|
+
* app.get('/openapi.json', (req, res) => res.json(spec));
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
interface OpenAPISpecConfig {
|
|
29
|
+
/** API title */
|
|
30
|
+
title: string;
|
|
31
|
+
/** API version */
|
|
32
|
+
version: string;
|
|
33
|
+
/** Tool registry */
|
|
34
|
+
tools: Record<string, Tool>;
|
|
35
|
+
/** API description */
|
|
36
|
+
description?: string;
|
|
37
|
+
/** Base URL for servers (default: '/') */
|
|
38
|
+
serverUrl?: string;
|
|
39
|
+
/** Base path prefix for tool endpoints (default: '/api/tools') */
|
|
40
|
+
basePath?: string;
|
|
41
|
+
/** Tags to group endpoints */
|
|
42
|
+
tags?: Array<{
|
|
43
|
+
name: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
interface OpenAPISpec {
|
|
48
|
+
openapi: '3.1.0';
|
|
49
|
+
info: {
|
|
50
|
+
title: string;
|
|
51
|
+
version: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
};
|
|
54
|
+
servers: Array<{
|
|
55
|
+
url: string;
|
|
56
|
+
}>;
|
|
57
|
+
paths: Record<string, PathItem>;
|
|
58
|
+
components: {
|
|
59
|
+
schemas: Record<string, JsonSchema>;
|
|
60
|
+
};
|
|
61
|
+
tags?: Array<{
|
|
62
|
+
name: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
interface PathItem {
|
|
67
|
+
post: {
|
|
68
|
+
operationId: string;
|
|
69
|
+
summary: string;
|
|
70
|
+
description?: string;
|
|
71
|
+
tags?: string[];
|
|
72
|
+
requestBody: {
|
|
73
|
+
required: true;
|
|
74
|
+
content: {
|
|
75
|
+
'application/json': {
|
|
76
|
+
schema: {
|
|
77
|
+
$ref: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
responses: Record<string, {
|
|
83
|
+
description: string;
|
|
84
|
+
content?: {
|
|
85
|
+
'application/json': {
|
|
86
|
+
schema: {
|
|
87
|
+
$ref: string;
|
|
88
|
+
} | JsonSchema;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
}>;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Generate an OpenAPI 3.1 spec from a Better UI tool registry.
|
|
96
|
+
*/
|
|
97
|
+
declare function generateOpenAPISpec(config: OpenAPISpecConfig): OpenAPISpec;
|
|
98
|
+
/**
|
|
99
|
+
* Create a request handler that serves the OpenAPI spec as JSON.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // Next.js route: app/api/openapi/route.ts
|
|
104
|
+
* export const GET = openAPIHandler({
|
|
105
|
+
* title: 'My Tools',
|
|
106
|
+
* version: '1.0.0',
|
|
107
|
+
* tools,
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function openAPIHandler(config: OpenAPISpecConfig): (req: Request) => Response;
|
|
112
|
+
|
|
113
|
+
export { type OpenAPISpec, type OpenAPISpecConfig, generateOpenAPISpec, openAPIHandler };
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/openapi/index.ts
|
|
21
|
+
var openapi_exports = {};
|
|
22
|
+
__export(openapi_exports, {
|
|
23
|
+
generateOpenAPISpec: () => generateOpenAPISpec,
|
|
24
|
+
openAPIHandler: () => openAPIHandler
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(openapi_exports);
|
|
27
|
+
|
|
28
|
+
// src/mcp/schema.ts
|
|
29
|
+
function zodToJsonSchema(schema) {
|
|
30
|
+
return convert(schema);
|
|
31
|
+
}
|
|
32
|
+
function convert(schema) {
|
|
33
|
+
const def = schema._def;
|
|
34
|
+
const typeName = def?.typeName;
|
|
35
|
+
switch (typeName) {
|
|
36
|
+
case "ZodString":
|
|
37
|
+
return convertString(def);
|
|
38
|
+
case "ZodNumber":
|
|
39
|
+
return convertNumber(def);
|
|
40
|
+
case "ZodBoolean":
|
|
41
|
+
return { type: "boolean" };
|
|
42
|
+
case "ZodNull":
|
|
43
|
+
return { type: "null" };
|
|
44
|
+
case "ZodLiteral":
|
|
45
|
+
return { enum: [def.value] };
|
|
46
|
+
case "ZodEnum":
|
|
47
|
+
return { type: "string", enum: def.values };
|
|
48
|
+
case "ZodNativeEnum":
|
|
49
|
+
return { enum: Object.values(def.values) };
|
|
50
|
+
case "ZodObject":
|
|
51
|
+
return convertObject(def);
|
|
52
|
+
case "ZodArray":
|
|
53
|
+
return convertArray(def);
|
|
54
|
+
case "ZodOptional":
|
|
55
|
+
return convert(def.innerType);
|
|
56
|
+
case "ZodNullable": {
|
|
57
|
+
const inner = convert(def.innerType);
|
|
58
|
+
return { anyOf: [inner, { type: "null" }] };
|
|
59
|
+
}
|
|
60
|
+
case "ZodDefault":
|
|
61
|
+
return { ...convert(def.innerType), default: def.defaultValue() };
|
|
62
|
+
case "ZodUnion":
|
|
63
|
+
return { anyOf: def.options.map((o) => convert(o)) };
|
|
64
|
+
case "ZodDiscriminatedUnion":
|
|
65
|
+
return { oneOf: [...def.options.values()].map((o) => convert(o)) };
|
|
66
|
+
case "ZodRecord":
|
|
67
|
+
return {
|
|
68
|
+
type: "object",
|
|
69
|
+
additionalProperties: convert(def.valueType)
|
|
70
|
+
};
|
|
71
|
+
case "ZodTuple": {
|
|
72
|
+
const items = def.items.map((item) => convert(item));
|
|
73
|
+
return { type: "array", items: items.length === 1 ? items[0] : void 0, prefixItems: items };
|
|
74
|
+
}
|
|
75
|
+
case "ZodEffects":
|
|
76
|
+
return convert(def.schema);
|
|
77
|
+
case "ZodPipeline":
|
|
78
|
+
return convert(def.in);
|
|
79
|
+
case "ZodLazy":
|
|
80
|
+
return convert(def.getter());
|
|
81
|
+
case "ZodAny":
|
|
82
|
+
return {};
|
|
83
|
+
case "ZodUnknown":
|
|
84
|
+
return {};
|
|
85
|
+
default:
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function convertString(def) {
|
|
90
|
+
const schema = { type: "string" };
|
|
91
|
+
if (def.checks) {
|
|
92
|
+
for (const check of def.checks) {
|
|
93
|
+
switch (check.kind) {
|
|
94
|
+
case "min":
|
|
95
|
+
schema.minLength = check.value;
|
|
96
|
+
break;
|
|
97
|
+
case "max":
|
|
98
|
+
schema.maxLength = check.value;
|
|
99
|
+
break;
|
|
100
|
+
case "email":
|
|
101
|
+
schema.format = "email";
|
|
102
|
+
break;
|
|
103
|
+
case "url":
|
|
104
|
+
schema.format = "uri";
|
|
105
|
+
break;
|
|
106
|
+
case "uuid":
|
|
107
|
+
schema.format = "uuid";
|
|
108
|
+
break;
|
|
109
|
+
case "regex":
|
|
110
|
+
schema.pattern = check.regex.source;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (def.description) schema.description = def.description;
|
|
116
|
+
return schema;
|
|
117
|
+
}
|
|
118
|
+
function convertNumber(def) {
|
|
119
|
+
const schema = { type: "number" };
|
|
120
|
+
if (def.checks) {
|
|
121
|
+
for (const check of def.checks) {
|
|
122
|
+
switch (check.kind) {
|
|
123
|
+
case "min":
|
|
124
|
+
schema.minimum = check.value;
|
|
125
|
+
if (check.inclusive === false) schema.exclusiveMinimum = check.value;
|
|
126
|
+
break;
|
|
127
|
+
case "max":
|
|
128
|
+
schema.maximum = check.value;
|
|
129
|
+
if (check.inclusive === false) schema.exclusiveMaximum = check.value;
|
|
130
|
+
break;
|
|
131
|
+
case "int":
|
|
132
|
+
schema.type = "integer";
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (def.description) schema.description = def.description;
|
|
138
|
+
return schema;
|
|
139
|
+
}
|
|
140
|
+
function convertObject(def) {
|
|
141
|
+
const shape = def.shape();
|
|
142
|
+
const properties = {};
|
|
143
|
+
const required = [];
|
|
144
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
145
|
+
properties[key] = convert(value);
|
|
146
|
+
const fieldDef = value._def;
|
|
147
|
+
const isOptional = fieldDef?.typeName === "ZodOptional" || fieldDef?.typeName === "ZodDefault";
|
|
148
|
+
if (!isOptional) {
|
|
149
|
+
required.push(key);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const schema = { type: "object", properties };
|
|
153
|
+
if (required.length > 0) schema.required = required;
|
|
154
|
+
if (def.description) schema.description = def.description;
|
|
155
|
+
return schema;
|
|
156
|
+
}
|
|
157
|
+
function convertArray(def) {
|
|
158
|
+
const schema = {
|
|
159
|
+
type: "array",
|
|
160
|
+
items: convert(def.type)
|
|
161
|
+
};
|
|
162
|
+
if (def.minLength) schema.minItems = def.minLength.value;
|
|
163
|
+
if (def.maxLength) schema.maxItems = def.maxLength.value;
|
|
164
|
+
if (def.description) schema.description = def.description;
|
|
165
|
+
return schema;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/openapi/index.ts
|
|
169
|
+
function generateOpenAPISpec(config) {
|
|
170
|
+
const basePath = config.basePath ?? "/api/tools";
|
|
171
|
+
const paths = {};
|
|
172
|
+
const schemas = {};
|
|
173
|
+
const autoTags = /* @__PURE__ */ new Set();
|
|
174
|
+
for (const [key, tool] of Object.entries(config.tools)) {
|
|
175
|
+
const name = tool.name || key;
|
|
176
|
+
const inputSchemaName = `${name}Input`;
|
|
177
|
+
const outputSchemaName = `${name}Output`;
|
|
178
|
+
schemas[inputSchemaName] = zodToJsonSchema(tool.inputSchema);
|
|
179
|
+
if (tool.outputSchema) {
|
|
180
|
+
schemas[outputSchemaName] = zodToJsonSchema(tool.outputSchema);
|
|
181
|
+
}
|
|
182
|
+
const toolTags = tool.tags.length > 0 ? tool.tags : [name];
|
|
183
|
+
for (const t of toolTags) autoTags.add(t);
|
|
184
|
+
const responseSchema = tool.outputSchema ? { $ref: `#/components/schemas/${outputSchemaName}` } : { type: "object" };
|
|
185
|
+
const hints = [];
|
|
186
|
+
if (tool.hints.destructive) hints.push("destructive");
|
|
187
|
+
if (tool.hints.readOnly) hints.push("read-only");
|
|
188
|
+
if (tool.hints.idempotent) hints.push("idempotent");
|
|
189
|
+
if (tool.requiresConfirmation) hints.push("requires confirmation");
|
|
190
|
+
const hintsStr = hints.length > 0 ? ` [${hints.join(", ")}]` : "";
|
|
191
|
+
paths[`${basePath}/${name}`] = {
|
|
192
|
+
post: {
|
|
193
|
+
operationId: name,
|
|
194
|
+
summary: tool.description || name,
|
|
195
|
+
description: (tool.description || "") + hintsStr,
|
|
196
|
+
tags: tool.tags.length > 0 ? tool.tags : void 0,
|
|
197
|
+
requestBody: {
|
|
198
|
+
required: true,
|
|
199
|
+
content: {
|
|
200
|
+
"application/json": {
|
|
201
|
+
schema: { $ref: `#/components/schemas/${inputSchemaName}` }
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
responses: {
|
|
206
|
+
"200": {
|
|
207
|
+
description: "Successful tool execution",
|
|
208
|
+
content: {
|
|
209
|
+
"application/json": {
|
|
210
|
+
schema: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
result: responseSchema
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
"400": {
|
|
220
|
+
description: "Invalid input (Zod validation error)"
|
|
221
|
+
},
|
|
222
|
+
"404": {
|
|
223
|
+
description: "Tool not found"
|
|
224
|
+
},
|
|
225
|
+
"500": {
|
|
226
|
+
description: "Tool execution failed"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const tags = config.tags ?? [...autoTags].map((t) => ({ name: t }));
|
|
233
|
+
return {
|
|
234
|
+
openapi: "3.1.0",
|
|
235
|
+
info: {
|
|
236
|
+
title: config.title,
|
|
237
|
+
version: config.version,
|
|
238
|
+
...config.description ? { description: config.description } : {}
|
|
239
|
+
},
|
|
240
|
+
servers: [{ url: config.serverUrl ?? "/" }],
|
|
241
|
+
paths,
|
|
242
|
+
components: { schemas },
|
|
243
|
+
...tags.length > 0 ? { tags } : {}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function openAPIHandler(config) {
|
|
247
|
+
const spec = generateOpenAPISpec(config);
|
|
248
|
+
const json = JSON.stringify(spec, null, 2);
|
|
249
|
+
return () => new Response(json, {
|
|
250
|
+
headers: {
|
|
251
|
+
"Content-Type": "application/json",
|
|
252
|
+
"Access-Control-Allow-Origin": "*"
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
257
|
+
0 && (module.exports = {
|
|
258
|
+
generateOpenAPISpec,
|
|
259
|
+
openAPIHandler
|
|
260
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
zodToJsonSchema
|
|
3
|
+
} from "../chunk-OH73K7I5.mjs";
|
|
4
|
+
import "../chunk-Y6FXYEAI.mjs";
|
|
5
|
+
|
|
6
|
+
// src/openapi/index.ts
|
|
7
|
+
function generateOpenAPISpec(config) {
|
|
8
|
+
const basePath = config.basePath ?? "/api/tools";
|
|
9
|
+
const paths = {};
|
|
10
|
+
const schemas = {};
|
|
11
|
+
const autoTags = /* @__PURE__ */ new Set();
|
|
12
|
+
for (const [key, tool] of Object.entries(config.tools)) {
|
|
13
|
+
const name = tool.name || key;
|
|
14
|
+
const inputSchemaName = `${name}Input`;
|
|
15
|
+
const outputSchemaName = `${name}Output`;
|
|
16
|
+
schemas[inputSchemaName] = zodToJsonSchema(tool.inputSchema);
|
|
17
|
+
if (tool.outputSchema) {
|
|
18
|
+
schemas[outputSchemaName] = zodToJsonSchema(tool.outputSchema);
|
|
19
|
+
}
|
|
20
|
+
const toolTags = tool.tags.length > 0 ? tool.tags : [name];
|
|
21
|
+
for (const t of toolTags) autoTags.add(t);
|
|
22
|
+
const responseSchema = tool.outputSchema ? { $ref: `#/components/schemas/${outputSchemaName}` } : { type: "object" };
|
|
23
|
+
const hints = [];
|
|
24
|
+
if (tool.hints.destructive) hints.push("destructive");
|
|
25
|
+
if (tool.hints.readOnly) hints.push("read-only");
|
|
26
|
+
if (tool.hints.idempotent) hints.push("idempotent");
|
|
27
|
+
if (tool.requiresConfirmation) hints.push("requires confirmation");
|
|
28
|
+
const hintsStr = hints.length > 0 ? ` [${hints.join(", ")}]` : "";
|
|
29
|
+
paths[`${basePath}/${name}`] = {
|
|
30
|
+
post: {
|
|
31
|
+
operationId: name,
|
|
32
|
+
summary: tool.description || name,
|
|
33
|
+
description: (tool.description || "") + hintsStr,
|
|
34
|
+
tags: tool.tags.length > 0 ? tool.tags : void 0,
|
|
35
|
+
requestBody: {
|
|
36
|
+
required: true,
|
|
37
|
+
content: {
|
|
38
|
+
"application/json": {
|
|
39
|
+
schema: { $ref: `#/components/schemas/${inputSchemaName}` }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
responses: {
|
|
44
|
+
"200": {
|
|
45
|
+
description: "Successful tool execution",
|
|
46
|
+
content: {
|
|
47
|
+
"application/json": {
|
|
48
|
+
schema: {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: {
|
|
51
|
+
result: responseSchema
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"400": {
|
|
58
|
+
description: "Invalid input (Zod validation error)"
|
|
59
|
+
},
|
|
60
|
+
"404": {
|
|
61
|
+
description: "Tool not found"
|
|
62
|
+
},
|
|
63
|
+
"500": {
|
|
64
|
+
description: "Tool execution failed"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const tags = config.tags ?? [...autoTags].map((t) => ({ name: t }));
|
|
71
|
+
return {
|
|
72
|
+
openapi: "3.1.0",
|
|
73
|
+
info: {
|
|
74
|
+
title: config.title,
|
|
75
|
+
version: config.version,
|
|
76
|
+
...config.description ? { description: config.description } : {}
|
|
77
|
+
},
|
|
78
|
+
servers: [{ url: config.serverUrl ?? "/" }],
|
|
79
|
+
paths,
|
|
80
|
+
components: { schemas },
|
|
81
|
+
...tags.length > 0 ? { tags } : {}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function openAPIHandler(config) {
|
|
85
|
+
const spec = generateOpenAPISpec(config);
|
|
86
|
+
const json = JSON.stringify(spec, null, 2);
|
|
87
|
+
return () => new Response(json, {
|
|
88
|
+
headers: {
|
|
89
|
+
"Content-Type": "application/json",
|
|
90
|
+
"Access-Control-Allow-Origin": "*"
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
generateOpenAPISpec,
|
|
96
|
+
openAPIHandler
|
|
97
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight Zod-to-JSON-Schema converter.
|
|
5
|
+
* Handles common Zod types without requiring zod-to-json-schema dependency.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface JsonSchema {
|
|
9
|
+
type?: string;
|
|
10
|
+
properties?: Record<string, JsonSchema>;
|
|
11
|
+
required?: string[];
|
|
12
|
+
items?: JsonSchema;
|
|
13
|
+
enum?: unknown[];
|
|
14
|
+
description?: string;
|
|
15
|
+
default?: unknown;
|
|
16
|
+
minimum?: number;
|
|
17
|
+
maximum?: number;
|
|
18
|
+
minLength?: number;
|
|
19
|
+
maxLength?: number;
|
|
20
|
+
pattern?: string;
|
|
21
|
+
format?: string;
|
|
22
|
+
anyOf?: JsonSchema[];
|
|
23
|
+
oneOf?: JsonSchema[];
|
|
24
|
+
nullable?: boolean;
|
|
25
|
+
additionalProperties?: boolean | JsonSchema;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
declare function zodToJsonSchema(schema: z.ZodType | {
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}): JsonSchema;
|
|
31
|
+
|
|
32
|
+
export { type JsonSchema as J, zodToJsonSchema as z };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight Zod-to-JSON-Schema converter.
|
|
5
|
+
* Handles common Zod types without requiring zod-to-json-schema dependency.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface JsonSchema {
|
|
9
|
+
type?: string;
|
|
10
|
+
properties?: Record<string, JsonSchema>;
|
|
11
|
+
required?: string[];
|
|
12
|
+
items?: JsonSchema;
|
|
13
|
+
enum?: unknown[];
|
|
14
|
+
description?: string;
|
|
15
|
+
default?: unknown;
|
|
16
|
+
minimum?: number;
|
|
17
|
+
maximum?: number;
|
|
18
|
+
minLength?: number;
|
|
19
|
+
maxLength?: number;
|
|
20
|
+
pattern?: string;
|
|
21
|
+
format?: string;
|
|
22
|
+
anyOf?: JsonSchema[];
|
|
23
|
+
oneOf?: JsonSchema[];
|
|
24
|
+
nullable?: boolean;
|
|
25
|
+
additionalProperties?: boolean | JsonSchema;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
declare function zodToJsonSchema(schema: z.ZodType | {
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}): JsonSchema;
|
|
31
|
+
|
|
32
|
+
export { type JsonSchema as J, zodToJsonSchema as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lantos1618/better-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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",
|
|
@@ -41,6 +41,11 @@
|
|
|
41
41
|
"import": "./dist/agui/index.mjs",
|
|
42
42
|
"require": "./dist/agui/index.js"
|
|
43
43
|
},
|
|
44
|
+
"./openapi": {
|
|
45
|
+
"types": "./dist/openapi/index.d.ts",
|
|
46
|
+
"import": "./dist/openapi/index.mjs",
|
|
47
|
+
"require": "./dist/openapi/index.js"
|
|
48
|
+
},
|
|
44
49
|
"./theme.css": "./src/theme.css"
|
|
45
50
|
},
|
|
46
51
|
"files": [
|
|
@@ -69,7 +74,7 @@
|
|
|
69
74
|
},
|
|
70
75
|
"scripts": {
|
|
71
76
|
"build": "npm run build:lib",
|
|
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",
|
|
77
|
+
"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 src/openapi/index.ts --format cjs,esm --dts --clean --tsconfig tsconfig.lib.json",
|
|
73
78
|
"test": "jest",
|
|
74
79
|
"type-check": "tsc --noEmit",
|
|
75
80
|
"prepublishOnly": "npm run build:lib"
|