@krak-stack/registry 0.1.37 → 0.1.39
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 +131 -7
- package/dist/components/ui/app-brand.js +17 -17
- package/dist/components/ui/code-block.js +4 -4
- package/dist/components/ui/copy-button.js +2 -2
- package/dist/components/ui/data-table.js +56 -56
- package/dist/components/ui/editing-locale-switcher.js +8 -8
- package/dist/components/ui/effect-form.js +12 -12
- package/dist/components/ui/error-component.js +6 -6
- package/dist/components/ui/file-picker.js +2 -2
- package/dist/components/ui/form.js +24 -24
- package/dist/components/ui/icon-input.js +5 -5
- package/dist/components/ui/locale-switcher.js +2 -2
- package/dist/components/ui/search-menu.js +2 -2
- package/dist/components/ui/sidebar-layout.js +1 -1
- package/dist/components/ui/theme-switcher.js +3 -3
- package/dist/lib/docs.js +52 -111
- package/dist/lib/docs.server.js +1 -29
- package/dist/lib/documentation-toolkit.js +0 -28
- package/dist/lib/httpapi-adapter.d.ts +73 -0
- package/dist/lib/httpapi-adapter.js +189 -0
- package/dist/lib/httpapi-cli.d.ts +4 -22
- package/dist/lib/httpapi-cli.js +227 -533
- package/dist/lib/httpapi-toolkit.d.ts +15 -12
- package/dist/lib/httpapi-toolkit.js +175 -381
- package/dist/lib/query.d.ts +2 -2
- package/dist/lib/query.js +18 -5
- package/dist/lib/webfetch-toolkit.d.ts +1 -1
- package/dist/lib/webfetch-toolkit.js +48 -50
- package/dist/oxlint/anti-slop/index.js +2 -2
- package/dist/services/agent/client/index.js +6 -6
- package/dist/services/agent/index.js +3 -3
- package/package.json +5 -13
- package/dist/lib/httpapi-client.d.ts +0 -30
- package/dist/lib/httpapi-client.js +0 -112
- package/dist/lib/httpapi-helpers.d.ts +0 -101
- package/dist/lib/httpapi-helpers.js +0 -286
- package/dist/lib/httpapi-mcp.d.ts +0 -20
- package/dist/lib/httpapi-mcp.js +0 -477
package/dist/lib/httpapi-mcp.js
DELETED
|
@@ -1,477 +0,0 @@
|
|
|
1
|
-
// ../../src/lib/httpapi-mcp.ts
|
|
2
|
-
import { Context as Context3, Effect as Effect3, Layer as Layer3, Option as Option2, Schema as Schema3 } from "effect";
|
|
3
|
-
import { McpProtocol, McpSchema, McpServer } from "effect/ai";
|
|
4
|
-
|
|
5
|
-
// ../../src/lib/httpapi-client.ts
|
|
6
|
-
import { Context, Effect, Layer, Schema, SchemaTransformation } from "effect";
|
|
7
|
-
import { HttpClient } from "effect/http";
|
|
8
|
-
import {
|
|
9
|
-
HttpApi,
|
|
10
|
-
HttpApiClient as EffectHttpApiClient,
|
|
11
|
-
OpenApi
|
|
12
|
-
} from "effect/http-api";
|
|
13
|
-
var UndefinedFromNull = Schema.Null.pipe(Schema.decodeTo(Schema.Undefined, SchemaTransformation.transform({
|
|
14
|
-
decode: () => {
|
|
15
|
-
return;
|
|
16
|
-
},
|
|
17
|
-
encode: () => null
|
|
18
|
-
})));
|
|
19
|
-
var HttpApiOperationResultValue = Schema.suspend(() => Schema.Union([
|
|
20
|
-
Schema.Null,
|
|
21
|
-
UndefinedFromNull,
|
|
22
|
-
Schema.String,
|
|
23
|
-
Schema.Number,
|
|
24
|
-
Schema.Boolean,
|
|
25
|
-
Schema.DateFromString,
|
|
26
|
-
Schema.Uint8ArrayFromBase64,
|
|
27
|
-
Schema.Array(HttpApiOperationResultValue),
|
|
28
|
-
Schema.Record(Schema.String, HttpApiOperationResultValue)
|
|
29
|
-
]));
|
|
30
|
-
var HttpApiOperationResult = Schema.Union([
|
|
31
|
-
HttpApiOperationResultValue,
|
|
32
|
-
Schema.Undefined
|
|
33
|
-
]).annotate({ identifier: "HttpApiOperationResult" });
|
|
34
|
-
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 }))));
|
|
35
|
-
var reflectedSchema = (schema) => Schema.make(schema.ast);
|
|
36
|
-
var operationResultSchemas = (api) => {
|
|
37
|
-
const schemas = new Map;
|
|
38
|
-
HttpApi.reflect(api, {
|
|
39
|
-
onGroup: () => {
|
|
40
|
-
return;
|
|
41
|
-
},
|
|
42
|
-
onEndpoint: ({ endpoint, group, successes }) => {
|
|
43
|
-
const operationId = Context.getOrElse(endpoint.annotations, OpenApi.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
44
|
-
const operationSchemas = Array.from(successes.values()).flat().map(reflectedSchema);
|
|
45
|
-
if (operationSchemas.length === 1 && operationSchemas[0]) {
|
|
46
|
-
schemas.set(operationId, operationSchemas[0]);
|
|
47
|
-
} else if (operationSchemas.length > 1) {
|
|
48
|
-
schemas.set(operationId, Schema.Union(operationSchemas));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
return schemas;
|
|
53
|
-
};
|
|
54
|
-
var makeHttpApiOperationResultEncoder = (api) => {
|
|
55
|
-
const schemas = operationResultSchemas(api);
|
|
56
|
-
return Effect.fn("HttpApiClient.encodeOperationResultWithSchema")((result, operation) => {
|
|
57
|
-
const operationId = operation.operation.operationId;
|
|
58
|
-
const schema = operationId ? schemas.get(operationId) : undefined;
|
|
59
|
-
if (!schema)
|
|
60
|
-
return encodeHttpApiOperationResult(result);
|
|
61
|
-
return Schema.encodeUnknownEffect(schema)(result).pipe(Effect.flatMap(encodeHttpApiOperationResult), Effect.mapError((cause) => new Error("HTTP API result does not match its success schema", {
|
|
62
|
-
cause
|
|
63
|
-
})));
|
|
64
|
-
});
|
|
65
|
-
};
|
|
66
|
-
var GeneratedOperation = Schema.declare((value) => value instanceof Function).annotate({ identifier: "GeneratedOperation" });
|
|
67
|
-
var GeneratedOperationEffect = Schema.declare((value) => Effect.isEffect(value)).annotate({ identifier: "GeneratedOperationEffect" });
|
|
68
|
-
var makeGeneratedClient = (api, http, baseUrl) => EffectHttpApiClient.makeWith(api, {
|
|
69
|
-
baseUrl,
|
|
70
|
-
httpClient: http
|
|
71
|
-
});
|
|
72
|
-
var executeGeneratedOperation = Effect.fn("HttpApiClient.executeGeneratedOperation")(function* (client, { input, operation: entry }) {
|
|
73
|
-
const operationId = entry.operation.operationId;
|
|
74
|
-
if (!operationId) {
|
|
75
|
-
return yield* Effect.fail(new Error(`No generated API client operation for ${entry.method} ${entry.path}`));
|
|
76
|
-
}
|
|
77
|
-
const target = Object(client);
|
|
78
|
-
const groupName = Object.keys(target).filter((name) => operationId.startsWith(`${name}.`)).sort((a, b) => b.length - a.length)[0];
|
|
79
|
-
const group = groupName ? Object.entries(target).find(([name]) => name === groupName)?.[1] : target;
|
|
80
|
-
const endpointName = groupName ? operationId.slice(groupName.length + 1) : operationId;
|
|
81
|
-
const endpointCandidate = group instanceof Function ? undefined : Object.entries(Object(group)).find(([name]) => name === endpointName)?.[1];
|
|
82
|
-
const endpoint = Schema.decodeUnknownOption(GeneratedOperation)(endpointCandidate);
|
|
83
|
-
if (endpoint._tag === "None") {
|
|
84
|
-
return yield* Effect.fail(new Error(`No generated API client operation for ${operationId}`));
|
|
85
|
-
}
|
|
86
|
-
const result = Schema.decodeUnknownOption(GeneratedOperationEffect)(endpoint.value({
|
|
87
|
-
headers: input.headers,
|
|
88
|
-
params: input.params,
|
|
89
|
-
query: input.query,
|
|
90
|
-
payload: input.body
|
|
91
|
-
}));
|
|
92
|
-
if (result._tag === "None") {
|
|
93
|
-
return yield* Effect.fail(new Error(`Generated API client operation ${operationId} is invalid`));
|
|
94
|
-
}
|
|
95
|
-
return yield* result.value;
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
class ApiClient extends Context.Service()("ApiClient") {
|
|
99
|
-
static layer = (config) => Layer.effect(this, Effect.gen(function* () {
|
|
100
|
-
const http = yield* HttpClient.HttpClient;
|
|
101
|
-
const encodeResult = makeHttpApiOperationResultEncoder(config.api);
|
|
102
|
-
const client = yield* makeGeneratedClient(config.api, http, config.baseUrl);
|
|
103
|
-
return {
|
|
104
|
-
encodeResult: config.encodeResult ?? ((result, operation) => encodeResult(result, operation)),
|
|
105
|
-
execute: Effect.fn("ApiClient.execute")(function* (options) {
|
|
106
|
-
return yield* executeGeneratedOperation(client, options);
|
|
107
|
-
})
|
|
108
|
-
};
|
|
109
|
-
}));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ../../src/lib/httpapi-helpers.tsx
|
|
113
|
-
import {
|
|
114
|
-
Context as Context2,
|
|
115
|
-
Effect as Effect2,
|
|
116
|
-
JsonSchema,
|
|
117
|
-
Layer as Layer2,
|
|
118
|
-
Option,
|
|
119
|
-
Schema as Schema2,
|
|
120
|
-
SchemaRepresentation
|
|
121
|
-
} from "effect";
|
|
122
|
-
import { HttpApi as HttpApi2, OpenApi as OpenApi2 } from "effect/http-api";
|
|
123
|
-
var JsonObjectSchema = Schema2.Record(Schema2.String, Schema2.Json).annotate({
|
|
124
|
-
identifier: "HttpJsonObject",
|
|
125
|
-
title: "HTTP JSON object",
|
|
126
|
-
description: "A JSON object passed to an HTTP API operation.",
|
|
127
|
-
examples: [{ id: "example-id" }]
|
|
128
|
-
});
|
|
129
|
-
var JsonObjectFromString = Schema2.fromJsonString(JsonObjectSchema);
|
|
130
|
-
var JsonValueFromString = Schema2.fromJsonString(Schema2.Json);
|
|
131
|
-
var JsonSchemaAnnotations = Schema2.Struct({
|
|
132
|
-
title: Schema2.optional(Schema2.String),
|
|
133
|
-
description: Schema2.optional(Schema2.String),
|
|
134
|
-
examples: Schema2.optional(Schema2.Array(Schema2.Json))
|
|
135
|
-
}).annotate({
|
|
136
|
-
identifier: "HttpJsonSchemaAnnotations",
|
|
137
|
-
title: "HTTP JSON Schema annotations",
|
|
138
|
-
description: "JSON Schema annotations surfaced on generated tool inputs."
|
|
139
|
-
});
|
|
140
|
-
var HttpApiToolInputSchema = Schema2.Struct({
|
|
141
|
-
body: Schema2.optional(Schema2.Json)
|
|
142
|
-
}).annotate({
|
|
143
|
-
identifier: "HttpApiToolInput",
|
|
144
|
-
title: "HTTP API tool input",
|
|
145
|
-
description: "Input accepted by an HTTP API-backed tool.",
|
|
146
|
-
examples: [{ body: { id: "example-id" } }]
|
|
147
|
-
});
|
|
148
|
-
var HttpApiMethods = [
|
|
149
|
-
"get",
|
|
150
|
-
"post",
|
|
151
|
-
"put",
|
|
152
|
-
"patch",
|
|
153
|
-
"delete"
|
|
154
|
-
];
|
|
155
|
-
var HttpApiOperationSchema = Schema2.declare((value) => Schema2.is(JsonObjectSchema)(value)).annotate({ identifier: "HttpApiOperation" });
|
|
156
|
-
var decodeHttpApiOperation = Schema2.decodeUnknownOption(HttpApiOperationSchema);
|
|
157
|
-
var toHttpError = (message, error) => new Error(message, { cause: error });
|
|
158
|
-
var sanitizeHttpName = (name) => name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
159
|
-
var httpApiToolName = (method, path, operation) => {
|
|
160
|
-
const fallback = `${method}_${path.replace(/^\/api\//, "").replace(/[/:{}]/g, "_")}`;
|
|
161
|
-
return (operation.operationId || fallback).replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").slice(0, 64);
|
|
162
|
-
};
|
|
163
|
-
var httpApiOperations = ({
|
|
164
|
-
spec,
|
|
165
|
-
methods = HttpApiMethods
|
|
166
|
-
}) => {
|
|
167
|
-
const operations = [];
|
|
168
|
-
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
169
|
-
for (const method of methods) {
|
|
170
|
-
const operation = decodeHttpApiOperation(pathItem[method]);
|
|
171
|
-
if (Option.isSome(operation)) {
|
|
172
|
-
operations.push({ method, path, operation: operation.value });
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return operations;
|
|
177
|
-
};
|
|
178
|
-
var httpApiToolEntries = Effect2.fn("Http.toolEntries")(function* (operations) {
|
|
179
|
-
return yield* Effect2.try({
|
|
180
|
-
try: () => {
|
|
181
|
-
const names = new Set;
|
|
182
|
-
return operations.map((entry) => {
|
|
183
|
-
const name = httpApiToolName(entry.method, entry.path, entry.operation);
|
|
184
|
-
if (!name || names.has(name)) {
|
|
185
|
-
throw new Error(`Duplicate or empty HTTP API tool name: ${name}`);
|
|
186
|
-
}
|
|
187
|
-
names.add(name);
|
|
188
|
-
return { ...entry, name };
|
|
189
|
-
});
|
|
190
|
-
},
|
|
191
|
-
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
var decodeAnnotations = Schema2.decodeUnknownOption(JsonSchemaAnnotations);
|
|
195
|
-
var schemaWithVisibleAnnotations = (schema) => {
|
|
196
|
-
const directAnnotations = decodeAnnotations(schema).pipe(Option.getOrElse(() => ({})));
|
|
197
|
-
const allOf = Array.isArray(schema.allOf) ? schema.allOf : [];
|
|
198
|
-
const annotations = allOf.reduce((acc, item) => ({
|
|
199
|
-
...acc,
|
|
200
|
-
...decodeAnnotations(item).pipe(Option.getOrElse(() => ({})))
|
|
201
|
-
}), directAnnotations);
|
|
202
|
-
const visible = { ...schema };
|
|
203
|
-
if ("title" in annotations && !("title" in schema)) {
|
|
204
|
-
visible.title = annotations.title;
|
|
205
|
-
}
|
|
206
|
-
if ("description" in annotations && !("description" in schema)) {
|
|
207
|
-
visible.description = annotations.description;
|
|
208
|
-
}
|
|
209
|
-
if ("examples" in annotations && !("examples" in schema)) {
|
|
210
|
-
visible.examples = annotations.examples;
|
|
211
|
-
}
|
|
212
|
-
return visible;
|
|
213
|
-
};
|
|
214
|
-
var referencedDefinitions = (schema, definitions) => {
|
|
215
|
-
const names = new Set;
|
|
216
|
-
const pending = [Schema2.decodeUnknownSync(Schema2.Json)(schema)];
|
|
217
|
-
while (pending.length > 0) {
|
|
218
|
-
const current = pending.pop();
|
|
219
|
-
if (Array.isArray(current)) {
|
|
220
|
-
pending.push(...current);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
const record = Schema2.decodeUnknownOption(Schema2.Record(Schema2.String, Schema2.Json))(current);
|
|
224
|
-
if (Option.isNone(record))
|
|
225
|
-
continue;
|
|
226
|
-
for (const [key, value] of Object.entries(record.value)) {
|
|
227
|
-
if (key === "$ref" && Schema2.is(Schema2.String)(value)) {
|
|
228
|
-
const name = value.match(/^#\/(?:\$defs|components\/schemas)\/(.+)$/)?.[1];
|
|
229
|
-
if (name && !names.has(name) && definitions[name]) {
|
|
230
|
-
names.add(name);
|
|
231
|
-
pending.push(Schema2.decodeUnknownSync(Schema2.Json)(definitions[name]));
|
|
232
|
-
}
|
|
233
|
-
} else if (key !== "$defs") {
|
|
234
|
-
pending.push(value);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return Object.fromEntries([...names].map((name) => [name, definitions[name]]));
|
|
239
|
-
};
|
|
240
|
-
var operationParameters = (parameters, location) => parameters.filter((parameter) => parameter.in === location);
|
|
241
|
-
var httpApiOperationInputSchema = (operation) => {
|
|
242
|
-
const parameters = operation.parameters ?? [];
|
|
243
|
-
const pathParameters = operationParameters(parameters, "path");
|
|
244
|
-
const queryParameters = operationParameters(parameters, "query");
|
|
245
|
-
const headerParameters = operationParameters(parameters, "header");
|
|
246
|
-
const properties = {};
|
|
247
|
-
const required = [];
|
|
248
|
-
const body = operation.requestBody?.content?.["application/json"]?.schema;
|
|
249
|
-
for (const parameter of [
|
|
250
|
-
...pathParameters,
|
|
251
|
-
...queryParameters,
|
|
252
|
-
...headerParameters
|
|
253
|
-
]) {
|
|
254
|
-
const property = {
|
|
255
|
-
...parameter.schema ? schemaWithVisibleAnnotations(parameter.schema) : { type: "string" }
|
|
256
|
-
};
|
|
257
|
-
if (parameter.description)
|
|
258
|
-
property.description = parameter.description;
|
|
259
|
-
properties[parameter.name] = property;
|
|
260
|
-
if (parameter.required)
|
|
261
|
-
required.push(parameter.name);
|
|
262
|
-
}
|
|
263
|
-
if (body) {
|
|
264
|
-
properties.body = body;
|
|
265
|
-
if (operation.requestBody?.required)
|
|
266
|
-
required.push("body");
|
|
267
|
-
}
|
|
268
|
-
return {
|
|
269
|
-
type: "object",
|
|
270
|
-
properties,
|
|
271
|
-
required,
|
|
272
|
-
additionalProperties: false
|
|
273
|
-
};
|
|
274
|
-
};
|
|
275
|
-
var decodeJsonObject = Schema2.decodeUnknownOption(JsonObjectSchema);
|
|
276
|
-
var decodeHttpApiOperationInput = Effect2.fn("Http.decodeApiOperationInput")(function* (input, operation) {
|
|
277
|
-
const payload = yield* Schema2.decodeUnknownEffect(JsonObjectSchema)(input ?? {}).pipe(Effect2.mapError(() => new Error("Tool input must be a JSON object")));
|
|
278
|
-
const decoded = yield* Schema2.decodeUnknownEffect(HttpApiToolInputSchema)(payload).pipe(Effect2.mapError(() => new Error("Tool input must be a JSON object")));
|
|
279
|
-
const parameters = operation.parameters ?? [];
|
|
280
|
-
const pickParameters = (location) => {
|
|
281
|
-
const nestedKey = location === "path" ? "params" : location;
|
|
282
|
-
const nested = decodeJsonObject(payload[nestedKey]).pipe(Option.getOrElse(() => ({})));
|
|
283
|
-
return Object.fromEntries(operationParameters(parameters, location).map((parameter) => [
|
|
284
|
-
parameter.name,
|
|
285
|
-
nested[parameter.name] ?? payload[parameter.name]
|
|
286
|
-
]).filter(([, value]) => value !== undefined));
|
|
287
|
-
};
|
|
288
|
-
return {
|
|
289
|
-
...decoded,
|
|
290
|
-
headers: pickParameters("header"),
|
|
291
|
-
params: pickParameters("path"),
|
|
292
|
-
query: pickParameters("query")
|
|
293
|
-
};
|
|
294
|
-
});
|
|
295
|
-
var reflectedSchema2 = (schema) => Schema2.make(schema.ast);
|
|
296
|
-
var reflectedOperationInputSchemas = (api) => {
|
|
297
|
-
const schemas = new Map;
|
|
298
|
-
HttpApi2.reflect(api, {
|
|
299
|
-
onGroup: () => {
|
|
300
|
-
return;
|
|
301
|
-
},
|
|
302
|
-
onEndpoint: ({ endpoint, group }) => {
|
|
303
|
-
const operationId = Context2.getOrElse(endpoint.annotations, OpenApi2.Identifier, () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`);
|
|
304
|
-
const fields = {};
|
|
305
|
-
if (endpoint.params)
|
|
306
|
-
fields.params = reflectedSchema2(endpoint.params);
|
|
307
|
-
if (endpoint.query) {
|
|
308
|
-
fields.query = Schema2.optionalKey(reflectedSchema2(endpoint.query));
|
|
309
|
-
}
|
|
310
|
-
if (endpoint.headers) {
|
|
311
|
-
fields.headers = Schema2.optionalKey(reflectedSchema2(endpoint.headers));
|
|
312
|
-
}
|
|
313
|
-
const payloadSchemas = Array.from(endpoint.payload.values()).flatMap(({ schemas: schemas2 }) => schemas2.map(reflectedSchema2));
|
|
314
|
-
if (payloadSchemas.length === 1 && payloadSchemas[0]) {
|
|
315
|
-
fields.body = payloadSchemas[0];
|
|
316
|
-
} else if (payloadSchemas.length > 1) {
|
|
317
|
-
fields.body = Schema2.Union(payloadSchemas);
|
|
318
|
-
}
|
|
319
|
-
const inputSchema = Object.keys(fields).length === 0 ? Schema2.Record(Schema2.String, Schema2.Never) : Schema2.Struct(fields);
|
|
320
|
-
schemas.set(operationId, inputSchema.annotate({
|
|
321
|
-
identifier: `${sanitizeHttpName(operationId)}ToolInput`
|
|
322
|
-
}));
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
return schemas;
|
|
326
|
-
};
|
|
327
|
-
var parseJsonObject = Effect2.fn("Http.parseJsonObject")(function* (value, label) {
|
|
328
|
-
if (!value)
|
|
329
|
-
return {};
|
|
330
|
-
return yield* Schema2.decodeUnknownEffect(JsonObjectFromString)(value).pipe(Effect2.mapError(() => new Error(`${label} must be a JSON object`)));
|
|
331
|
-
});
|
|
332
|
-
var parseJsonValue = Effect2.fn("Http.parseJsonValue")(function* (value, label) {
|
|
333
|
-
if (!value)
|
|
334
|
-
return;
|
|
335
|
-
return yield* Schema2.decodeUnknownEffect(JsonValueFromString)(value).pipe(Effect2.mapError(() => new Error(`${label} must be valid JSON`)));
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
class HttpApiSpec extends Context2.Service()("HttpApiSpec", {
|
|
339
|
-
make: (config) => Effect2.try({
|
|
340
|
-
try: () => {
|
|
341
|
-
const api = config.api;
|
|
342
|
-
if (!HttpApi2.isHttpApi(api)) {
|
|
343
|
-
throw new Error("HttpApiSpec requires a valid HttpApi");
|
|
344
|
-
}
|
|
345
|
-
const spec = OpenApi2.fromApi(api);
|
|
346
|
-
const reflectedSchemas = reflectedOperationInputSchemas(api);
|
|
347
|
-
const operations = httpApiOperations({
|
|
348
|
-
spec,
|
|
349
|
-
methods: config.methods
|
|
350
|
-
}).filter((operation) => config.include?.(operation) ?? true);
|
|
351
|
-
const operationJsonSchema = (operation) => {
|
|
352
|
-
const schema = httpApiOperationInputSchema(operation);
|
|
353
|
-
const document = JsonSchema.fromSchemaOpenApi3_1({
|
|
354
|
-
...schema,
|
|
355
|
-
$defs: spec.components?.schemas
|
|
356
|
-
});
|
|
357
|
-
const definitions = document.definitions ?? {};
|
|
358
|
-
const usedDefinitions = referencedDefinitions(document.schema, definitions);
|
|
359
|
-
const result = { ...document.schema };
|
|
360
|
-
if (Object.keys(usedDefinitions).length > 0) {
|
|
361
|
-
result.$defs = usedDefinitions;
|
|
362
|
-
}
|
|
363
|
-
return result;
|
|
364
|
-
};
|
|
365
|
-
return {
|
|
366
|
-
info: spec.info,
|
|
367
|
-
operations,
|
|
368
|
-
decodeOperationInput: decodeHttpApiOperationInput,
|
|
369
|
-
operationJsonSchema,
|
|
370
|
-
operationSchema: (operation) => {
|
|
371
|
-
const reflected = operation.operationId ? reflectedSchemas.get(operation.operationId) : undefined;
|
|
372
|
-
if (reflected)
|
|
373
|
-
return reflected;
|
|
374
|
-
const document = JsonSchema.fromSchemaOpenApi3_1(operationJsonSchema(operation));
|
|
375
|
-
return reflectedSchema2(SchemaRepresentation.fromJsonSchemaDocument(document));
|
|
376
|
-
}
|
|
377
|
-
};
|
|
378
|
-
},
|
|
379
|
-
catch: (error) => toHttpError("Failed to build HTTP API spec", error)
|
|
380
|
-
})
|
|
381
|
-
}) {
|
|
382
|
-
static layer = (config) => Layer2.effect(this, this.make(config));
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// ../../src/lib/httpapi-mcp.ts
|
|
386
|
-
class HttpApiMcp extends Context3.Service()("HttpApiMcp", {
|
|
387
|
-
make: (config) => Effect3.gen(function* () {
|
|
388
|
-
const spec = yield* HttpApiSpec;
|
|
389
|
-
const client = yield* ApiClient;
|
|
390
|
-
return {
|
|
391
|
-
registerTools: registerHttpApiTools(spec, client, config)
|
|
392
|
-
};
|
|
393
|
-
})
|
|
394
|
-
}) {
|
|
395
|
-
static layer = (config) => Layer3.effect(this, this.make(config));
|
|
396
|
-
}
|
|
397
|
-
var decodeStructuredContent = Schema3.decodeUnknownOption(JsonObjectSchema);
|
|
398
|
-
var structuredContent = (value) => decodeStructuredContent(value).pipe(Option2.getOrElse(() => ({ result: value ?? null })));
|
|
399
|
-
var toolResult = (value) => new McpSchema.CallToolResult({
|
|
400
|
-
isError: false,
|
|
401
|
-
structuredContent: structuredContent(value),
|
|
402
|
-
content: [{ type: "text", text: JSON.stringify(value, null, 2) ?? "null" }]
|
|
403
|
-
});
|
|
404
|
-
var toolError = (error) => new McpSchema.CallToolResult({
|
|
405
|
-
isError: true,
|
|
406
|
-
content: [
|
|
407
|
-
{
|
|
408
|
-
type: "text",
|
|
409
|
-
text: error instanceof Error ? error.message : String(error)
|
|
410
|
-
}
|
|
411
|
-
]
|
|
412
|
-
});
|
|
413
|
-
var executeOperation = Effect3.fn("HttpApiMcp.executeOperation")(function* (method, path, operation, input, spec, client) {
|
|
414
|
-
const payload = yield* spec.decodeOperationInput(input, operation);
|
|
415
|
-
const result = yield* client.execute({
|
|
416
|
-
operation: { method, path, operation },
|
|
417
|
-
input: {
|
|
418
|
-
body: payload.body,
|
|
419
|
-
headers: payload.headers,
|
|
420
|
-
params: payload.params,
|
|
421
|
-
query: payload.query
|
|
422
|
-
}
|
|
423
|
-
});
|
|
424
|
-
return yield* client.encodeResult(result, { method, path, operation });
|
|
425
|
-
});
|
|
426
|
-
var registerOperation = (method, path, operation, name, config, spec, client) => Effect3.gen(function* () {
|
|
427
|
-
const server = yield* McpServer.McpServer;
|
|
428
|
-
yield* server.addTool({
|
|
429
|
-
tool: new McpSchema.Tool({
|
|
430
|
-
name,
|
|
431
|
-
title: operation.summary,
|
|
432
|
-
description: operation.description ?? operation.summary,
|
|
433
|
-
inputSchema: spec.operationJsonSchema(operation),
|
|
434
|
-
annotations: {
|
|
435
|
-
readOnlyHint: method.toUpperCase() === "GET",
|
|
436
|
-
destructiveHint: method === "delete",
|
|
437
|
-
idempotentHint: method === "get" || method === "put" || method === "delete",
|
|
438
|
-
openWorldHint: false
|
|
439
|
-
},
|
|
440
|
-
_meta: {
|
|
441
|
-
[config.toolMetaKey ?? "api/operation"]: {
|
|
442
|
-
method: method.toUpperCase(),
|
|
443
|
-
path
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}),
|
|
447
|
-
annotations: Context3.empty(),
|
|
448
|
-
handle: (payload) => executeOperation(method, path, operation, payload, spec, client).pipe(Effect3.match({
|
|
449
|
-
onFailure: toolError,
|
|
450
|
-
onSuccess: toolResult
|
|
451
|
-
}))
|
|
452
|
-
});
|
|
453
|
-
});
|
|
454
|
-
var registerHttpApiTools = (spec, client, config) => Effect3.gen(function* () {
|
|
455
|
-
const entries = yield* httpApiToolEntries(spec.operations);
|
|
456
|
-
for (const { method, name, operation, path } of entries) {
|
|
457
|
-
yield* registerOperation(method, path, operation, name, config, spec, client);
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
var httpApiMcpToolsLayer = Layer3.effectDiscard(Effect3.gen(function* () {
|
|
461
|
-
const mcp = yield* HttpApiMcp;
|
|
462
|
-
yield* mcp.registerTools;
|
|
463
|
-
}));
|
|
464
|
-
var httpApiMcpServerLayer = (path) => Layer3.unwrap(Effect3.gen(function* () {
|
|
465
|
-
const spec = yield* HttpApiSpec;
|
|
466
|
-
return McpServer.layerHttp({
|
|
467
|
-
name: spec.info.title,
|
|
468
|
-
version: spec.info.version,
|
|
469
|
-
path,
|
|
470
|
-
protocols: [McpProtocol.v2025_11_25]
|
|
471
|
-
});
|
|
472
|
-
}));
|
|
473
|
-
export {
|
|
474
|
-
HttpApiMcp,
|
|
475
|
-
httpApiMcpServerLayer,
|
|
476
|
-
httpApiMcpToolsLayer
|
|
477
|
-
};
|