@krak-stack/registry 0.1.11 → 0.1.14
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/README.md +31 -2
- package/dist/components/ui/app-brand.d.ts +18 -13
- package/dist/components/ui/app-brand.js +48 -15
- package/dist/components/ui/code-block.js +10 -6
- package/dist/components/ui/copy-button.d.ts +1 -1
- package/dist/components/ui/copy-button.js +1 -1
- package/dist/components/ui/data-table.d.ts +1 -1
- package/dist/components/ui/data-table.js +21 -20
- package/dist/components/ui/effect-form.js +7 -6
- package/dist/components/ui/file-picker.js +2 -1
- package/dist/components/ui/form.js +17 -15
- package/dist/components/ui/google-map.d.ts +6 -10
- package/dist/components/ui/google-map.js +19 -14
- package/dist/components/ui/locale-switcher.js +7 -1
- package/dist/components/ui/sidebar-layout.js +19 -21
- package/dist/components/ui/theme-switcher.js +1 -1
- package/dist/lib/docs-core.js +165 -109
- package/dist/lib/documentation-toolkit.d.ts +140 -0
- package/dist/lib/{docs-ai.js → documentation-toolkit.js} +54 -63
- package/dist/lib/httpapi-cli.d.ts +6 -6
- package/dist/lib/httpapi-cli.js +75 -48
- package/dist/lib/httpapi-client.d.ts +8 -2
- package/dist/lib/httpapi-client.js +21 -9
- package/dist/lib/httpapi-helpers.d.ts +15 -21
- package/dist/lib/httpapi-helpers.js +38 -26
- package/dist/lib/httpapi-mcp.js +73 -50
- package/dist/lib/httpapi-toolkit.d.ts +20 -0
- package/dist/lib/{httpapi-ai.js → httpapi-toolkit.js} +123 -100
- package/dist/lib/seo.js +29 -17
- package/dist/lib/webfetch-toolkit.d.ts +45 -0
- package/dist/lib/webfetch-toolkit.js +276 -0
- package/dist/oxlint/anti-slop/index.js +1592 -0
- package/dist/services/agent/client/atom.d.ts +7 -7
- package/dist/services/agent/client/index.js +55 -54
- package/dist/services/agent/index.d.ts +71 -71
- package/dist/services/agent/index.js +38 -19
- package/dist/services/notification/channels/index.d.ts +11 -9
- package/dist/services/notification/channels/ses/index.d.ts +6 -6
- package/dist/services/notification/channels/ses/index.js +15 -11
- package/dist/services/notification/client/index.js +3 -2
- package/dist/services/notification/client/notification-menu.d.ts +9 -9
- package/dist/services/notification/index.d.ts +7 -6
- package/dist/services/notification/persistence/drizzle.js +214 -0
- package/dist/services/notification/persistence/index.js +227 -0
- package/dist/services/notification/persistence/schema.js +118 -0
- package/dist/services/notification/public.js +3 -2
- package/dist/services/s3/index.d.ts +2 -16
- package/dist/services/s3/index.js +12 -9
- package/package.json +33 -7
- package/dist/lib/docs-ai.d.ts +0 -142
- package/dist/lib/httpapi-ai.d.ts +0 -54
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Context, Effect, JsonSchema, Layer, Schema } from "effect";
|
|
2
|
+
import type { Json } from "effect/Schema";
|
|
2
3
|
import { HttpApi, OpenApi } from "effect/unstable/httpapi";
|
|
3
|
-
export declare const JsonObjectSchema: Schema.$Record<Schema.String, Schema.
|
|
4
|
+
export declare const JsonObjectSchema: Schema.$Record<Schema.String, Schema.Codec<Json, Json, never, never>>;
|
|
4
5
|
export type JsonObject = typeof JsonObjectSchema.Type;
|
|
6
|
+
type HttpBoundaryError = ErrorOptions["cause"];
|
|
5
7
|
export type HttpApiDocument = ReturnType<typeof OpenApi.fromApi>;
|
|
6
8
|
export type HttpApiMethod = "get" | "post" | "put" | "patch" | "delete";
|
|
7
9
|
export declare const HttpApiMethods: ReadonlyArray<HttpApiMethod>;
|
|
@@ -31,7 +33,7 @@ export type HttpApiOperationEntry = {
|
|
|
31
33
|
readonly operation: HttpApiOperation;
|
|
32
34
|
};
|
|
33
35
|
export type HttpApiOperationInput = {
|
|
34
|
-
readonly body?:
|
|
36
|
+
readonly body?: Json;
|
|
35
37
|
readonly headers: JsonObject;
|
|
36
38
|
readonly params: JsonObject;
|
|
37
39
|
readonly query: JsonObject;
|
|
@@ -41,7 +43,7 @@ export type HttpApiSpecConfig = {
|
|
|
41
43
|
readonly methods?: ReadonlyArray<HttpApiMethod>;
|
|
42
44
|
readonly include?: (operation: HttpApiOperationEntry) => boolean;
|
|
43
45
|
};
|
|
44
|
-
export declare const toHttpError: (message: string, error:
|
|
46
|
+
export declare const toHttpError: (message: string, error: HttpBoundaryError) => Error;
|
|
45
47
|
export declare const sanitizeHttpName: (name: string) => string;
|
|
46
48
|
export declare const httpApiToolName: (method: string, path: string, operation: HttpApiOperation) => string;
|
|
47
49
|
export declare const httpApiOperations: ({ spec, methods, }: {
|
|
@@ -55,46 +57,38 @@ export declare const httpApiToolEntries: (operations: readonly HttpApiOperationE
|
|
|
55
57
|
name: string;
|
|
56
58
|
}[], Error, never>;
|
|
57
59
|
export declare const httpApiOperationInputSchema: (operation: HttpApiOperation) => JsonSchema.JsonSchema;
|
|
58
|
-
export declare const decodeHttpApiOperationInput: (input:
|
|
59
|
-
body?:
|
|
60
|
+
export declare const decodeHttpApiOperationInput: (input: Json, operation: HttpApiOperation) => Effect.Effect<{
|
|
61
|
+
body?: Json | undefined;
|
|
60
62
|
headers: any;
|
|
61
63
|
params: any;
|
|
62
64
|
query: any;
|
|
63
65
|
}, Error, never>;
|
|
64
66
|
export declare const parseJsonObject: (value: string | undefined, label: string) => Effect.Effect<{
|
|
65
|
-
readonly [x: string]:
|
|
67
|
+
readonly [x: string]: Json;
|
|
66
68
|
}, Error, never>;
|
|
67
|
-
export declare const parseJsonValue: (value: string | undefined, label: string) => Effect.Effect<
|
|
69
|
+
export declare const parseJsonValue: (value: string | undefined, label: string) => Effect.Effect<Json | undefined, Error, never>;
|
|
68
70
|
declare const HttpApiSpec_base: Context.ServiceClass<HttpApiSpec, "HttpApiSpec", {
|
|
69
71
|
info: OpenApi.OpenAPISpecInfo;
|
|
70
72
|
operations: HttpApiOperationEntry[];
|
|
71
|
-
decodeOperationInput: (input:
|
|
72
|
-
body?:
|
|
73
|
+
decodeOperationInput: (input: Json, operation: HttpApiOperation) => Effect.Effect<{
|
|
74
|
+
body?: Json | undefined;
|
|
73
75
|
headers: any;
|
|
74
76
|
params: any;
|
|
75
77
|
query: any;
|
|
76
78
|
}, Error, never>;
|
|
77
|
-
operationJsonSchema: (operation: HttpApiOperation) =>
|
|
78
|
-
$defs?: {
|
|
79
|
-
[k: string]: JsonSchema.JsonSchema;
|
|
80
|
-
} | undefined;
|
|
81
|
-
};
|
|
79
|
+
operationJsonSchema: (operation: HttpApiOperation) => JsonSchema.JsonSchema;
|
|
82
80
|
operationSchema: (operation: HttpApiOperation) => Schema.Top;
|
|
83
81
|
}> & {
|
|
84
82
|
readonly make: (config: HttpApiSpecConfig) => Effect.Effect<{
|
|
85
83
|
info: OpenApi.OpenAPISpecInfo;
|
|
86
84
|
operations: HttpApiOperationEntry[];
|
|
87
|
-
decodeOperationInput: (input:
|
|
88
|
-
body?:
|
|
85
|
+
decodeOperationInput: (input: Json, operation: HttpApiOperation) => Effect.Effect<{
|
|
86
|
+
body?: Json | undefined;
|
|
89
87
|
headers: any;
|
|
90
88
|
params: any;
|
|
91
89
|
query: any;
|
|
92
90
|
}, Error, never>;
|
|
93
|
-
operationJsonSchema: (operation: HttpApiOperation) =>
|
|
94
|
-
$defs?: {
|
|
95
|
-
[k: string]: JsonSchema.JsonSchema;
|
|
96
|
-
} | undefined;
|
|
97
|
-
};
|
|
91
|
+
operationJsonSchema: (operation: HttpApiOperation) => JsonSchema.JsonSchema;
|
|
98
92
|
operationSchema: (operation: HttpApiOperation) => Schema.Top;
|
|
99
93
|
}, Error, never>;
|
|
100
94
|
};
|
|
@@ -9,25 +9,25 @@ import {
|
|
|
9
9
|
SchemaRepresentation
|
|
10
10
|
} from "effect";
|
|
11
11
|
import { HttpApi, OpenApi } from "effect/unstable/httpapi";
|
|
12
|
-
var JsonObjectSchema = Schema.Record(Schema.String, Schema.
|
|
12
|
+
var JsonObjectSchema = Schema.Record(Schema.String, Schema.Json).annotate({
|
|
13
13
|
identifier: "HttpJsonObject",
|
|
14
14
|
title: "HTTP JSON object",
|
|
15
15
|
description: "A JSON object passed to an HTTP API operation.",
|
|
16
16
|
examples: [{ id: "example-id" }]
|
|
17
17
|
});
|
|
18
18
|
var JsonObjectFromString = Schema.fromJsonString(JsonObjectSchema);
|
|
19
|
-
var JsonValueFromString = Schema.fromJsonString(Schema.
|
|
19
|
+
var JsonValueFromString = Schema.fromJsonString(Schema.Json);
|
|
20
20
|
var JsonSchemaAnnotations = Schema.Struct({
|
|
21
21
|
title: Schema.optional(Schema.String),
|
|
22
22
|
description: Schema.optional(Schema.String),
|
|
23
|
-
examples: Schema.optional(Schema.Array(Schema.
|
|
23
|
+
examples: Schema.optional(Schema.Array(Schema.Json))
|
|
24
24
|
}).annotate({
|
|
25
25
|
identifier: "HttpJsonSchemaAnnotations",
|
|
26
26
|
title: "HTTP JSON Schema annotations",
|
|
27
27
|
description: "JSON Schema annotations surfaced on generated tool inputs."
|
|
28
28
|
});
|
|
29
29
|
var HttpApiToolInputSchema = Schema.Struct({
|
|
30
|
-
body: Schema.optional(Schema.
|
|
30
|
+
body: Schema.optional(Schema.Json)
|
|
31
31
|
}).annotate({
|
|
32
32
|
identifier: "HttpApiToolInput",
|
|
33
33
|
title: "HTTP API tool input",
|
|
@@ -41,6 +41,8 @@ var HttpApiMethods = [
|
|
|
41
41
|
"patch",
|
|
42
42
|
"delete"
|
|
43
43
|
];
|
|
44
|
+
var HttpApiOperationSchema = Schema.declare((value) => Schema.is(JsonObjectSchema)(value)).annotate({ identifier: "HttpApiOperation" });
|
|
45
|
+
var decodeHttpApiOperation = Schema.decodeUnknownOption(HttpApiOperationSchema);
|
|
44
46
|
var toHttpError = (message, error) => new Error(message, { cause: error });
|
|
45
47
|
var sanitizeHttpName = (name) => name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
46
48
|
var httpApiToolName = (method, path, operation) => {
|
|
@@ -54,9 +56,10 @@ var httpApiOperations = ({
|
|
|
54
56
|
const operations = [];
|
|
55
57
|
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
56
58
|
for (const method of methods) {
|
|
57
|
-
const operation = pathItem[method];
|
|
58
|
-
if (operation)
|
|
59
|
-
operations.push({ method, path, operation });
|
|
59
|
+
const operation = decodeHttpApiOperation(pathItem[method]);
|
|
60
|
+
if (Option.isSome(operation)) {
|
|
61
|
+
operations.push({ method, path, operation: operation.value });
|
|
62
|
+
}
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
return operations;
|
|
@@ -85,30 +88,36 @@ var schemaWithVisibleAnnotations = (schema) => {
|
|
|
85
88
|
...acc,
|
|
86
89
|
...decodeAnnotations(item).pipe(Option.getOrElse(() => ({})))
|
|
87
90
|
}), directAnnotations);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
const visible = { ...schema };
|
|
92
|
+
if ("title" in annotations && !("title" in schema)) {
|
|
93
|
+
visible.title = annotations.title;
|
|
94
|
+
}
|
|
95
|
+
if ("description" in annotations && !("description" in schema)) {
|
|
96
|
+
visible.description = annotations.description;
|
|
97
|
+
}
|
|
98
|
+
if ("examples" in annotations && !("examples" in schema)) {
|
|
99
|
+
visible.examples = annotations.examples;
|
|
100
|
+
}
|
|
101
|
+
return visible;
|
|
94
102
|
};
|
|
95
103
|
var referencedDefinitions = (schema, definitions) => {
|
|
96
104
|
const names = new Set;
|
|
97
|
-
const pending = [schema];
|
|
105
|
+
const pending = [Schema.decodeUnknownSync(Schema.Json)(schema)];
|
|
98
106
|
while (pending.length > 0) {
|
|
99
107
|
const current = pending.pop();
|
|
100
|
-
if (!current || typeof current !== "object")
|
|
101
|
-
continue;
|
|
102
108
|
if (Array.isArray(current)) {
|
|
103
109
|
pending.push(...current);
|
|
104
110
|
continue;
|
|
105
111
|
}
|
|
106
|
-
|
|
107
|
-
|
|
112
|
+
const record = Schema.decodeUnknownOption(Schema.Record(Schema.String, Schema.Json))(current);
|
|
113
|
+
if (Option.isNone(record))
|
|
114
|
+
continue;
|
|
115
|
+
for (const [key, value] of Object.entries(record.value)) {
|
|
116
|
+
if (key === "$ref" && Schema.is(Schema.String)(value)) {
|
|
108
117
|
const name = value.match(/^#\/(?:\$defs|components\/schemas)\/(.+)$/)?.[1];
|
|
109
118
|
if (name && !names.has(name) && definitions[name]) {
|
|
110
119
|
names.add(name);
|
|
111
|
-
pending.push(definitions[name]);
|
|
120
|
+
pending.push(Schema.decodeUnknownSync(Schema.Json)(definitions[name]));
|
|
112
121
|
}
|
|
113
122
|
} else if (key !== "$defs") {
|
|
114
123
|
pending.push(value);
|
|
@@ -131,10 +140,12 @@ var httpApiOperationInputSchema = (operation) => {
|
|
|
131
140
|
...queryParameters,
|
|
132
141
|
...headerParameters
|
|
133
142
|
]) {
|
|
134
|
-
|
|
135
|
-
...parameter.schema ? schemaWithVisibleAnnotations(parameter.schema) : { type: "string" }
|
|
136
|
-
...parameter.description ? { description: parameter.description } : {}
|
|
143
|
+
const property = {
|
|
144
|
+
...parameter.schema ? schemaWithVisibleAnnotations(parameter.schema) : { type: "string" }
|
|
137
145
|
};
|
|
146
|
+
if (parameter.description)
|
|
147
|
+
property.description = parameter.description;
|
|
148
|
+
properties[parameter.name] = property;
|
|
138
149
|
if (parameter.required)
|
|
139
150
|
required.push(parameter.name);
|
|
140
151
|
}
|
|
@@ -200,10 +211,11 @@ class HttpApiSpec extends Context.Service()("HttpApiSpec", {
|
|
|
200
211
|
});
|
|
201
212
|
const definitions = document.definitions ?? {};
|
|
202
213
|
const usedDefinitions = referencedDefinitions(document.schema, definitions);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
214
|
+
const result = { ...document.schema };
|
|
215
|
+
if (Object.keys(usedDefinitions).length > 0) {
|
|
216
|
+
result.$defs = usedDefinitions;
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
207
219
|
};
|
|
208
220
|
return {
|
|
209
221
|
info: spec.info,
|
package/dist/lib/httpapi-mcp.js
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
// ../../src/lib/httpapi-mcp.ts
|
|
2
|
-
import { Context as Context3, Effect as Effect3, Layer as Layer3, Option as Option2, Schema as
|
|
2
|
+
import { Context as Context3, Effect as Effect3, Layer as Layer3, Option as Option2, Schema as Schema3 } from "effect";
|
|
3
3
|
import { McpSchema, McpServer } from "effect/unstable/ai";
|
|
4
4
|
|
|
5
5
|
// ../../src/lib/httpapi-client.ts
|
|
6
|
-
import { Context, Effect, Layer } from "effect";
|
|
6
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
7
7
|
import { HttpClient } from "effect/unstable/http";
|
|
8
8
|
import {
|
|
9
9
|
HttpApiClient as EffectHttpApiClient
|
|
10
10
|
} from "effect/unstable/httpapi";
|
|
11
|
+
var HttpApiOperationResult = Schema.Union([
|
|
12
|
+
Schema.Json,
|
|
13
|
+
Schema.DateFromString,
|
|
14
|
+
Schema.Uint8ArrayFromBase64,
|
|
15
|
+
Schema.Undefined
|
|
16
|
+
]).annotate({ identifier: "HttpApiOperationResult" });
|
|
17
|
+
var encodeHttpApiOperationResult = Effect.fn("HttpApiClient.encodeOperationResult")((result) => Schema.encodeUnknownEffect(HttpApiOperationResult)(result).pipe(Effect.map((encoded) => encoded ?? null), Effect.mapError((cause) => new Error("HTTP API result is not serializable", { cause }))));
|
|
18
|
+
var GeneratedOperation = Schema.declare((value) => value instanceof Function).annotate({ identifier: "GeneratedOperation" });
|
|
19
|
+
var GeneratedOperationEffect = Schema.declare((value) => Effect.isEffect(value)).annotate({ identifier: "GeneratedOperationEffect" });
|
|
11
20
|
var makeGeneratedClient = (api, http, baseUrl) => EffectHttpApiClient.makeWith(api, {
|
|
12
21
|
baseUrl,
|
|
13
22
|
httpClient: http
|
|
14
23
|
});
|
|
15
|
-
var isClientEffect = (value) => Effect.isEffect(value);
|
|
16
24
|
var executeGeneratedOperation = Effect.fn("HttpApiClient.executeGeneratedOperation")(function* (client, { input, operation: entry }) {
|
|
17
25
|
const operationId = entry.operation.operationId;
|
|
18
26
|
if (!operationId) {
|
|
@@ -20,22 +28,23 @@ var executeGeneratedOperation = Effect.fn("HttpApiClient.executeGeneratedOperati
|
|
|
20
28
|
}
|
|
21
29
|
const target = Object(client);
|
|
22
30
|
const groupName = Object.keys(target).filter((name) => operationId.startsWith(`${name}.`)).sort((a, b) => b.length - a.length)[0];
|
|
23
|
-
const group = groupName ?
|
|
31
|
+
const group = groupName ? Object.entries(target).find(([name]) => name === groupName)?.[1] : target;
|
|
24
32
|
const endpointName = groupName ? operationId.slice(groupName.length + 1) : operationId;
|
|
25
|
-
const
|
|
26
|
-
|
|
33
|
+
const endpointCandidate = group instanceof Function ? undefined : Object.entries(Object(group)).find(([name]) => name === endpointName)?.[1];
|
|
34
|
+
const endpoint = Schema.decodeUnknownOption(GeneratedOperation)(endpointCandidate);
|
|
35
|
+
if (endpoint._tag === "None") {
|
|
27
36
|
return yield* Effect.fail(new Error(`No generated API client operation for ${operationId}`));
|
|
28
37
|
}
|
|
29
|
-
const result = endpoint({
|
|
38
|
+
const result = Schema.decodeUnknownOption(GeneratedOperationEffect)(endpoint.value({
|
|
30
39
|
headers: input.headers,
|
|
31
40
|
params: input.params,
|
|
32
41
|
query: input.query,
|
|
33
42
|
payload: input.body
|
|
34
|
-
});
|
|
35
|
-
if (
|
|
43
|
+
}));
|
|
44
|
+
if (result._tag === "None") {
|
|
36
45
|
return yield* Effect.fail(new Error(`Generated API client operation ${operationId} is invalid`));
|
|
37
46
|
}
|
|
38
|
-
return yield* result;
|
|
47
|
+
return yield* result.value;
|
|
39
48
|
});
|
|
40
49
|
|
|
41
50
|
class ApiClient extends Context.Service()("ApiClient") {
|
|
@@ -43,6 +52,7 @@ class ApiClient extends Context.Service()("ApiClient") {
|
|
|
43
52
|
const http = yield* HttpClient.HttpClient;
|
|
44
53
|
const client = yield* makeGeneratedClient(config.api, http, config.baseUrl);
|
|
45
54
|
return {
|
|
55
|
+
encodeResult: config.encodeResult ?? ((result) => encodeHttpApiOperationResult(result)),
|
|
46
56
|
execute: Effect.fn("ApiClient.execute")(function* (options) {
|
|
47
57
|
return yield* executeGeneratedOperation(client, options);
|
|
48
58
|
})
|
|
@@ -57,29 +67,29 @@ import {
|
|
|
57
67
|
JsonSchema,
|
|
58
68
|
Layer as Layer2,
|
|
59
69
|
Option,
|
|
60
|
-
Schema,
|
|
70
|
+
Schema as Schema2,
|
|
61
71
|
SchemaRepresentation
|
|
62
72
|
} from "effect";
|
|
63
73
|
import { HttpApi as HttpApi2, OpenApi } from "effect/unstable/httpapi";
|
|
64
|
-
var JsonObjectSchema =
|
|
74
|
+
var JsonObjectSchema = Schema2.Record(Schema2.String, Schema2.Json).annotate({
|
|
65
75
|
identifier: "HttpJsonObject",
|
|
66
76
|
title: "HTTP JSON object",
|
|
67
77
|
description: "A JSON object passed to an HTTP API operation.",
|
|
68
78
|
examples: [{ id: "example-id" }]
|
|
69
79
|
});
|
|
70
|
-
var JsonObjectFromString =
|
|
71
|
-
var JsonValueFromString =
|
|
72
|
-
var JsonSchemaAnnotations =
|
|
73
|
-
title:
|
|
74
|
-
description:
|
|
75
|
-
examples:
|
|
80
|
+
var JsonObjectFromString = Schema2.fromJsonString(JsonObjectSchema);
|
|
81
|
+
var JsonValueFromString = Schema2.fromJsonString(Schema2.Json);
|
|
82
|
+
var JsonSchemaAnnotations = Schema2.Struct({
|
|
83
|
+
title: Schema2.optional(Schema2.String),
|
|
84
|
+
description: Schema2.optional(Schema2.String),
|
|
85
|
+
examples: Schema2.optional(Schema2.Array(Schema2.Json))
|
|
76
86
|
}).annotate({
|
|
77
87
|
identifier: "HttpJsonSchemaAnnotations",
|
|
78
88
|
title: "HTTP JSON Schema annotations",
|
|
79
89
|
description: "JSON Schema annotations surfaced on generated tool inputs."
|
|
80
90
|
});
|
|
81
|
-
var HttpApiToolInputSchema =
|
|
82
|
-
body:
|
|
91
|
+
var HttpApiToolInputSchema = Schema2.Struct({
|
|
92
|
+
body: Schema2.optional(Schema2.Json)
|
|
83
93
|
}).annotate({
|
|
84
94
|
identifier: "HttpApiToolInput",
|
|
85
95
|
title: "HTTP API tool input",
|
|
@@ -93,6 +103,8 @@ var HttpApiMethods = [
|
|
|
93
103
|
"patch",
|
|
94
104
|
"delete"
|
|
95
105
|
];
|
|
106
|
+
var HttpApiOperationSchema = Schema2.declare((value) => Schema2.is(JsonObjectSchema)(value)).annotate({ identifier: "HttpApiOperation" });
|
|
107
|
+
var decodeHttpApiOperation = Schema2.decodeUnknownOption(HttpApiOperationSchema);
|
|
96
108
|
var toHttpError = (message, error) => new Error(message, { cause: error });
|
|
97
109
|
var httpApiToolName = (method, path, operation) => {
|
|
98
110
|
const fallback = `${method}_${path.replace(/^\/api\//, "").replace(/[/:{}]/g, "_")}`;
|
|
@@ -105,9 +117,10 @@ var httpApiOperations = ({
|
|
|
105
117
|
const operations = [];
|
|
106
118
|
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
107
119
|
for (const method of methods) {
|
|
108
|
-
const operation = pathItem[method];
|
|
109
|
-
if (operation)
|
|
110
|
-
operations.push({ method, path, operation });
|
|
120
|
+
const operation = decodeHttpApiOperation(pathItem[method]);
|
|
121
|
+
if (Option.isSome(operation)) {
|
|
122
|
+
operations.push({ method, path, operation: operation.value });
|
|
123
|
+
}
|
|
111
124
|
}
|
|
112
125
|
}
|
|
113
126
|
return operations;
|
|
@@ -128,7 +141,7 @@ var httpApiToolEntries = Effect2.fn("Http.toolEntries")(function* (operations) {
|
|
|
128
141
|
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
129
142
|
});
|
|
130
143
|
});
|
|
131
|
-
var decodeAnnotations =
|
|
144
|
+
var decodeAnnotations = Schema2.decodeUnknownOption(JsonSchemaAnnotations);
|
|
132
145
|
var schemaWithVisibleAnnotations = (schema) => {
|
|
133
146
|
const directAnnotations = decodeAnnotations(schema).pipe(Option.getOrElse(() => ({})));
|
|
134
147
|
const allOf = Array.isArray(schema.allOf) ? schema.allOf : [];
|
|
@@ -136,30 +149,36 @@ var schemaWithVisibleAnnotations = (schema) => {
|
|
|
136
149
|
...acc,
|
|
137
150
|
...decodeAnnotations(item).pipe(Option.getOrElse(() => ({})))
|
|
138
151
|
}), directAnnotations);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
const visible = { ...schema };
|
|
153
|
+
if ("title" in annotations && !("title" in schema)) {
|
|
154
|
+
visible.title = annotations.title;
|
|
155
|
+
}
|
|
156
|
+
if ("description" in annotations && !("description" in schema)) {
|
|
157
|
+
visible.description = annotations.description;
|
|
158
|
+
}
|
|
159
|
+
if ("examples" in annotations && !("examples" in schema)) {
|
|
160
|
+
visible.examples = annotations.examples;
|
|
161
|
+
}
|
|
162
|
+
return visible;
|
|
145
163
|
};
|
|
146
164
|
var referencedDefinitions = (schema, definitions) => {
|
|
147
165
|
const names = new Set;
|
|
148
|
-
const pending = [schema];
|
|
166
|
+
const pending = [Schema2.decodeUnknownSync(Schema2.Json)(schema)];
|
|
149
167
|
while (pending.length > 0) {
|
|
150
168
|
const current = pending.pop();
|
|
151
|
-
if (!current || typeof current !== "object")
|
|
152
|
-
continue;
|
|
153
169
|
if (Array.isArray(current)) {
|
|
154
170
|
pending.push(...current);
|
|
155
171
|
continue;
|
|
156
172
|
}
|
|
157
|
-
|
|
158
|
-
|
|
173
|
+
const record = Schema2.decodeUnknownOption(Schema2.Record(Schema2.String, Schema2.Json))(current);
|
|
174
|
+
if (Option.isNone(record))
|
|
175
|
+
continue;
|
|
176
|
+
for (const [key, value] of Object.entries(record.value)) {
|
|
177
|
+
if (key === "$ref" && Schema2.is(Schema2.String)(value)) {
|
|
159
178
|
const name = value.match(/^#\/(?:\$defs|components\/schemas)\/(.+)$/)?.[1];
|
|
160
179
|
if (name && !names.has(name) && definitions[name]) {
|
|
161
180
|
names.add(name);
|
|
162
|
-
pending.push(definitions[name]);
|
|
181
|
+
pending.push(Schema2.decodeUnknownSync(Schema2.Json)(definitions[name]));
|
|
163
182
|
}
|
|
164
183
|
} else if (key !== "$defs") {
|
|
165
184
|
pending.push(value);
|
|
@@ -182,10 +201,12 @@ var httpApiOperationInputSchema = (operation) => {
|
|
|
182
201
|
...queryParameters,
|
|
183
202
|
...headerParameters
|
|
184
203
|
]) {
|
|
185
|
-
|
|
186
|
-
...parameter.schema ? schemaWithVisibleAnnotations(parameter.schema) : { type: "string" }
|
|
187
|
-
...parameter.description ? { description: parameter.description } : {}
|
|
204
|
+
const property = {
|
|
205
|
+
...parameter.schema ? schemaWithVisibleAnnotations(parameter.schema) : { type: "string" }
|
|
188
206
|
};
|
|
207
|
+
if (parameter.description)
|
|
208
|
+
property.description = parameter.description;
|
|
209
|
+
properties[parameter.name] = property;
|
|
189
210
|
if (parameter.required)
|
|
190
211
|
required.push(parameter.name);
|
|
191
212
|
}
|
|
@@ -201,10 +222,10 @@ var httpApiOperationInputSchema = (operation) => {
|
|
|
201
222
|
additionalProperties: false
|
|
202
223
|
};
|
|
203
224
|
};
|
|
204
|
-
var decodeJsonObject =
|
|
225
|
+
var decodeJsonObject = Schema2.decodeUnknownOption(JsonObjectSchema);
|
|
205
226
|
var decodeHttpApiOperationInput = Effect2.fn("Http.decodeApiOperationInput")(function* (input, operation) {
|
|
206
|
-
const payload = yield*
|
|
207
|
-
const decoded = yield*
|
|
227
|
+
const payload = yield* Schema2.decodeUnknownEffect(JsonObjectSchema)(input ?? {}).pipe(Effect2.mapError(() => new Error("Tool input must be a JSON object")));
|
|
228
|
+
const decoded = yield* Schema2.decodeUnknownEffect(HttpApiToolInputSchema)(payload).pipe(Effect2.mapError(() => new Error("Tool input must be a JSON object")));
|
|
208
229
|
const parameters = operation.parameters ?? [];
|
|
209
230
|
const pickParameters = (location) => {
|
|
210
231
|
const nestedKey = location === "path" ? "params" : location;
|
|
@@ -224,12 +245,12 @@ var decodeHttpApiOperationInput = Effect2.fn("Http.decodeApiOperationInput")(fun
|
|
|
224
245
|
var parseJsonObject = Effect2.fn("Http.parseJsonObject")(function* (value, label) {
|
|
225
246
|
if (!value)
|
|
226
247
|
return {};
|
|
227
|
-
return yield*
|
|
248
|
+
return yield* Schema2.decodeUnknownEffect(JsonObjectFromString)(value).pipe(Effect2.mapError(() => new Error(`${label} must be a JSON object`)));
|
|
228
249
|
});
|
|
229
250
|
var parseJsonValue = Effect2.fn("Http.parseJsonValue")(function* (value, label) {
|
|
230
251
|
if (!value)
|
|
231
252
|
return;
|
|
232
|
-
return yield*
|
|
253
|
+
return yield* Schema2.decodeUnknownEffect(JsonValueFromString)(value).pipe(Effect2.mapError(() => new Error(`${label} must be valid JSON`)));
|
|
233
254
|
});
|
|
234
255
|
|
|
235
256
|
class HttpApiSpec extends Context2.Service()("HttpApiSpec", {
|
|
@@ -251,10 +272,11 @@ class HttpApiSpec extends Context2.Service()("HttpApiSpec", {
|
|
|
251
272
|
});
|
|
252
273
|
const definitions = document.definitions ?? {};
|
|
253
274
|
const usedDefinitions = referencedDefinitions(document.schema, definitions);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
275
|
+
const result = { ...document.schema };
|
|
276
|
+
if (Object.keys(usedDefinitions).length > 0) {
|
|
277
|
+
result.$defs = usedDefinitions;
|
|
278
|
+
}
|
|
279
|
+
return result;
|
|
258
280
|
};
|
|
259
281
|
return {
|
|
260
282
|
info: spec.info,
|
|
@@ -285,7 +307,7 @@ class HttpApiMcp extends Context3.Service()("HttpApiMcp", {
|
|
|
285
307
|
}) {
|
|
286
308
|
static layer = (config) => Layer3.effect(this, this.make(config));
|
|
287
309
|
}
|
|
288
|
-
var decodeStructuredContent =
|
|
310
|
+
var decodeStructuredContent = Schema3.decodeUnknownOption(JsonObjectSchema);
|
|
289
311
|
var structuredContent = (value) => decodeStructuredContent(value).pipe(Option2.getOrElse(() => ({ result: value ?? null })));
|
|
290
312
|
var toolResult = (value) => new McpSchema.CallToolResult({
|
|
291
313
|
isError: false,
|
|
@@ -303,7 +325,7 @@ var toolError = (error) => new McpSchema.CallToolResult({
|
|
|
303
325
|
});
|
|
304
326
|
var executeOperation = Effect3.fn("HttpApiMcp.executeOperation")(function* (method, path, operation, input, spec, client) {
|
|
305
327
|
const payload = yield* spec.decodeOperationInput(input, operation);
|
|
306
|
-
|
|
328
|
+
const result = yield* client.execute({
|
|
307
329
|
operation: { method, path, operation },
|
|
308
330
|
input: {
|
|
309
331
|
body: payload.body,
|
|
@@ -312,6 +334,7 @@ var executeOperation = Effect3.fn("HttpApiMcp.executeOperation")(function* (meth
|
|
|
312
334
|
query: payload.query
|
|
313
335
|
}
|
|
314
336
|
});
|
|
337
|
+
return yield* client.encodeResult(result, { method, path, operation });
|
|
315
338
|
});
|
|
316
339
|
var registerOperation = (method, path, operation, name, config, spec, client) => Effect3.gen(function* () {
|
|
317
340
|
const server = yield* McpServer.McpServer;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect, JsonSchema, Layer, Schema } from "effect";
|
|
2
|
+
import type { Json } from "effect/Schema";
|
|
3
|
+
import { Tool, Toolkit } from "effect/unstable/ai";
|
|
4
|
+
import { ApiClient } from "./httpapi-client.js";
|
|
5
|
+
import { HttpApiSpec, type HttpApiOperationEntry } from "./httpapi-helpers.js";
|
|
6
|
+
export type HttpApiToolkitConfig = {
|
|
7
|
+
readonly needsApproval?: (operation: HttpApiOperationEntry) => boolean;
|
|
8
|
+
readonly strict?: (operation: HttpApiOperationEntry) => boolean;
|
|
9
|
+
readonly transformResult?: (operation: HttpApiOperationEntry, result: Json) => Json;
|
|
10
|
+
};
|
|
11
|
+
export declare const makeOpenAiStrictJsonSchema: (schema: JsonSchema.JsonSchema) => JsonSchema.JsonSchema;
|
|
12
|
+
export declare const HttpApiToolkit: (config: HttpApiToolkitConfig) => Effect.Effect<Toolkit.Toolkit<{
|
|
13
|
+
readonly [x: string]: Tool.Tool<string, {
|
|
14
|
+
readonly parameters: Schema.Struct<{}>;
|
|
15
|
+
readonly success: Schema.Codec<Json, Json, never, never>;
|
|
16
|
+
readonly failure: Schema.String;
|
|
17
|
+
readonly failureMode: "return";
|
|
18
|
+
}, never>;
|
|
19
|
+
}>, Error, HttpApiSpec>;
|
|
20
|
+
export declare const HttpApiToolkitLayer: (config: HttpApiToolkitConfig) => Layer.Layer<Tool.Handler<string>, Error, ApiClient | HttpApiSpec>;
|