@knitli/openapi-mcp 0.0.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/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/README.md +362 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +249 -0
- package/dist/compile.d.ts +14 -0
- package/dist/compile.js +132 -0
- package/dist/compiler.d.ts +13 -0
- package/dist/compiler.js +7 -0
- package/dist/conformance/fixtures.d.ts +24 -0
- package/dist/conformance/fixtures.js +119 -0
- package/dist/conformance/index.d.ts +2 -0
- package/dist/conformance/index.js +2 -0
- package/dist/conformance/runtime-suite.d.ts +31 -0
- package/dist/conformance/runtime-suite.js +343 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/load.d.ts +31 -0
- package/dist/load.js +20 -0
- package/dist/operations.d.ts +27 -0
- package/dist/operations.js +231 -0
- package/dist/permissions.d.ts +31 -0
- package/dist/permissions.js +89 -0
- package/dist/release/compile-release.d.ts +33 -0
- package/dist/release/compile-release.js +1620 -0
- package/dist/release/load-v4.d.ts +29 -0
- package/dist/release/load-v4.js +297 -0
- package/dist/release/manifest-builder.d.ts +19 -0
- package/dist/release/manifest-builder.js +28 -0
- package/dist/release/publish.d.ts +45 -0
- package/dist/release/publish.js +346 -0
- package/dist/release/reference-map.d.ts +53 -0
- package/dist/release/reference-map.js +347 -0
- package/dist/release/schema-v4.d.ts +5 -0
- package/dist/release/schema-v4.js +91 -0
- package/dist/runtime/action-permit.d.ts +11 -0
- package/dist/runtime/action-permit.js +1 -0
- package/dist/runtime/classify.d.ts +16 -0
- package/dist/runtime/classify.js +279 -0
- package/dist/runtime/credential-binding.d.ts +3 -0
- package/dist/runtime/credential-binding.js +101 -0
- package/dist/runtime/digest.d.ts +8 -0
- package/dist/runtime/digest.js +80 -0
- package/dist/runtime/errors.d.ts +14 -0
- package/dist/runtime/errors.js +15 -0
- package/dist/runtime/index.d.ts +20 -0
- package/dist/runtime/index.js +14 -0
- package/dist/runtime/manifest.d.ts +33 -0
- package/dist/runtime/manifest.js +489 -0
- package/dist/runtime/prepared-call.d.ts +39 -0
- package/dist/runtime/prepared-call.js +566 -0
- package/dist/runtime/references.d.ts +7 -0
- package/dist/runtime/references.js +197 -0
- package/dist/runtime/runtime.d.ts +25 -0
- package/dist/runtime/runtime.js +1272 -0
- package/dist/runtime/schema-keywords.d.ts +2 -0
- package/dist/runtime/schema-keywords.js +30 -0
- package/dist/runtime/schema-resolver.d.ts +10 -0
- package/dist/runtime/schema-resolver.js +237 -0
- package/dist/runtime/serialize.d.ts +12 -0
- package/dist/runtime/serialize.js +877 -0
- package/dist/runtime/store.d.ts +52 -0
- package/dist/runtime/store.js +582 -0
- package/dist/runtime/strict-json.d.ts +19 -0
- package/dist/runtime/strict-json.js +539 -0
- package/dist/runtime/types.d.ts +457 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/verify-record.d.ts +6 -0
- package/dist/runtime/verify-record.js +492 -0
- package/dist/runtime/versions.d.ts +63 -0
- package/dist/runtime/versions.js +86 -0
- package/dist/safety.d.ts +28 -0
- package/dist/safety.js +60 -0
- package/dist/schema.d.ts +5 -0
- package/dist/schema.js +52 -0
- package/dist/schemas.d.ts +9 -0
- package/dist/schemas.js +16 -0
- package/dist/sign.d.ts +20 -0
- package/dist/sign.js +55 -0
- package/dist/sqlite/auth.d.ts +33 -0
- package/dist/sqlite/auth.js +973 -0
- package/dist/sqlite/catalog-store.d.ts +20 -0
- package/dist/sqlite/catalog-store.js +370 -0
- package/dist/sqlite/destination-guard.d.ts +24 -0
- package/dist/sqlite/destination-guard.js +140 -0
- package/dist/sqlite/generation-store.d.ts +12 -0
- package/dist/sqlite/generation-store.js +721 -0
- package/dist/sqlite/guarded-fetch.d.ts +24 -0
- package/dist/sqlite/guarded-fetch.js +626 -0
- package/dist/sqlite/index.d.ts +5 -0
- package/dist/sqlite/index.js +5 -0
- package/dist/sqlite/oauth-resource.d.ts +2 -0
- package/dist/sqlite/oauth-resource.js +15 -0
- package/dist/sqlite/release-completion.d.ts +5 -0
- package/dist/sqlite/release-completion.js +80 -0
- package/dist/stdio/action-broker.d.ts +15 -0
- package/dist/stdio/action-broker.js +136 -0
- package/dist/stdio/authorizer.d.ts +30 -0
- package/dist/stdio/authorizer.js +484 -0
- package/dist/stdio/config.d.ts +77 -0
- package/dist/stdio/config.js +144 -0
- package/dist/stdio/exact-policy.d.ts +41 -0
- package/dist/stdio/exact-policy.js +361 -0
- package/dist/stdio/index.d.ts +6 -0
- package/dist/stdio/index.js +148 -0
- package/dist/stdio/render.d.ts +8 -0
- package/dist/stdio/render.js +253 -0
- package/dist/stdio/request-state.d.ts +18 -0
- package/dist/stdio/request-state.js +79 -0
- package/dist/stdio/search.d.ts +10 -0
- package/dist/stdio/search.js +76 -0
- package/dist/stdio/server.d.ts +29 -0
- package/dist/stdio/server.js +309 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.js +1 -0
- package/package.json +131 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const singular = new Set([
|
|
2
|
+
"additionalProperties",
|
|
3
|
+
"contains",
|
|
4
|
+
"contentSchema",
|
|
5
|
+
"else",
|
|
6
|
+
"if",
|
|
7
|
+
"items",
|
|
8
|
+
"not",
|
|
9
|
+
"propertyNames",
|
|
10
|
+
"then",
|
|
11
|
+
"unevaluatedItems",
|
|
12
|
+
"unevaluatedProperties",
|
|
13
|
+
]);
|
|
14
|
+
const maps = new Set([
|
|
15
|
+
"$defs",
|
|
16
|
+
"definitions",
|
|
17
|
+
"dependentSchemas",
|
|
18
|
+
"patternProperties",
|
|
19
|
+
"properties",
|
|
20
|
+
]);
|
|
21
|
+
const arrays = new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
|
|
22
|
+
export function schemaChildKind(key) {
|
|
23
|
+
if (singular.has(key))
|
|
24
|
+
return "single";
|
|
25
|
+
if (maps.has(key))
|
|
26
|
+
return "map";
|
|
27
|
+
if (arrays.has(key))
|
|
28
|
+
return "array";
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AdmittedManifest } from "./manifest.ts";
|
|
2
|
+
import type { CatalogStore, JsonSchemaV4, SchemaRecordV4, TypedSchemaId } from "./types.ts";
|
|
3
|
+
import { type RuntimeLimits } from "./versions.ts";
|
|
4
|
+
/** Collect references only from positions that JSON Schema defines as schemas. */
|
|
5
|
+
export declare function collectReferences(schema: JsonSchemaV4): readonly TypedSchemaId[];
|
|
6
|
+
/**
|
|
7
|
+
* Load and verify a schema graph one breadth-first frontier at a time.
|
|
8
|
+
* No result escapes until the complete requested closure has passed all checks.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveSchemaClosure(store: CatalogStore, admitted: AdmittedManifest, roots: TypedSchemaId | readonly TypedSchemaId[], limitOverrides?: RuntimeLimits): Promise<ReadonlyMap<TypedSchemaId, Readonly<SchemaRecordV4>>>;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { OpenApiMcpError } from "./errors.js";
|
|
2
|
+
import { parseTypedRecordId } from "./references.js";
|
|
3
|
+
import { schemaChildKind } from "./schema-keywords.js";
|
|
4
|
+
import { canonicalJson } from "./strict-json.js";
|
|
5
|
+
import { verifyStoredRecord } from "./verify-record.js";
|
|
6
|
+
import { DEFAULT_RUNTIME_LIMITS, resolveRuntimeLimits, } from "./versions.js";
|
|
7
|
+
function resolutionLimit(message) {
|
|
8
|
+
return new OpenApiMcpError("SCHEMA_RESOLUTION_LIMIT", message);
|
|
9
|
+
}
|
|
10
|
+
function schemaLookupUnavailable() {
|
|
11
|
+
return new OpenApiMcpError("UPSTREAM_ERROR", "Schema lookup is unavailable", {
|
|
12
|
+
retryable: true,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function schemaReference(value) {
|
|
16
|
+
try {
|
|
17
|
+
if (typeof value !== "string")
|
|
18
|
+
throw new Error();
|
|
19
|
+
const parsed = parseTypedRecordId(value);
|
|
20
|
+
if (!parsed.startsWith("schema:"))
|
|
21
|
+
throw new Error();
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new OpenApiMcpError("RECORD_DIGEST_MISMATCH", "Verified schema contains an invalid local reference");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Collect references only from positions that JSON Schema defines as schemas. */
|
|
29
|
+
export function collectReferences(schema) {
|
|
30
|
+
if (typeof schema === "boolean")
|
|
31
|
+
return [];
|
|
32
|
+
const found = new Set();
|
|
33
|
+
const stack = [schema];
|
|
34
|
+
while (stack.length > 0) {
|
|
35
|
+
const current = stack.pop();
|
|
36
|
+
if (typeof current === "boolean" || current === undefined)
|
|
37
|
+
continue;
|
|
38
|
+
if (Object.hasOwn(current, "$ref")) {
|
|
39
|
+
found.add(schemaReference(current.$ref));
|
|
40
|
+
}
|
|
41
|
+
for (const [key, child] of Object.entries(current)) {
|
|
42
|
+
const kind = schemaChildKind(key);
|
|
43
|
+
if (kind === "single") {
|
|
44
|
+
stack.push(child);
|
|
45
|
+
}
|
|
46
|
+
else if (kind === "array") {
|
|
47
|
+
for (const entry of child)
|
|
48
|
+
stack.push(entry);
|
|
49
|
+
}
|
|
50
|
+
else if (kind === "map") {
|
|
51
|
+
for (const entry of Object.values(child)) {
|
|
52
|
+
stack.push(entry);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return [...found].sort();
|
|
58
|
+
}
|
|
59
|
+
function snapshotRows(value, maximum) {
|
|
60
|
+
try {
|
|
61
|
+
if (!Array.isArray(value))
|
|
62
|
+
throw new Error();
|
|
63
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
64
|
+
if (lengthDescriptor === undefined ||
|
|
65
|
+
!("value" in lengthDescriptor) ||
|
|
66
|
+
!Number.isSafeInteger(lengthDescriptor.value) ||
|
|
67
|
+
lengthDescriptor.value < 0 ||
|
|
68
|
+
lengthDescriptor.value > maximum)
|
|
69
|
+
throw new Error();
|
|
70
|
+
const length = lengthDescriptor.value;
|
|
71
|
+
const keys = Reflect.ownKeys(value);
|
|
72
|
+
if (keys.length !== length + 1 ||
|
|
73
|
+
keys.some((key) => typeof key !== "string" ||
|
|
74
|
+
(key !== "length" && !/^(0|[1-9][0-9]*)$/.test(key))))
|
|
75
|
+
throw new Error();
|
|
76
|
+
const snapshot = [];
|
|
77
|
+
for (let index = 0; index < length; index += 1) {
|
|
78
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
79
|
+
if (descriptor === undefined ||
|
|
80
|
+
!("value" in descriptor) ||
|
|
81
|
+
!descriptor.enumerable)
|
|
82
|
+
throw new Error();
|
|
83
|
+
snapshot.push(descriptor.value);
|
|
84
|
+
}
|
|
85
|
+
return snapshot;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
throw new OpenApiMcpError("RECORD_NOT_ADMITTED", "Schema lookup result is invalid");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
class ImmutableSchemaMap {
|
|
92
|
+
#records;
|
|
93
|
+
constructor(records) {
|
|
94
|
+
this.#records = new Map(records);
|
|
95
|
+
Object.freeze(this);
|
|
96
|
+
}
|
|
97
|
+
get size() {
|
|
98
|
+
return this.#records.size;
|
|
99
|
+
}
|
|
100
|
+
get(key) {
|
|
101
|
+
return this.#records.get(key);
|
|
102
|
+
}
|
|
103
|
+
has(key) {
|
|
104
|
+
return this.#records.has(key);
|
|
105
|
+
}
|
|
106
|
+
entries() {
|
|
107
|
+
return this.#records.entries();
|
|
108
|
+
}
|
|
109
|
+
keys() {
|
|
110
|
+
return this.#records.keys();
|
|
111
|
+
}
|
|
112
|
+
values() {
|
|
113
|
+
return this.#records.values();
|
|
114
|
+
}
|
|
115
|
+
forEach(callbackfn, thisArg) {
|
|
116
|
+
for (const [key, value] of this.#records) {
|
|
117
|
+
callbackfn.call(thisArg, value, key, this);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
[Symbol.iterator]() {
|
|
121
|
+
return this.entries();
|
|
122
|
+
}
|
|
123
|
+
get [Symbol.toStringTag]() {
|
|
124
|
+
return "ImmutableSchemaMap";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function snapshotRoots(value, maximum) {
|
|
128
|
+
try {
|
|
129
|
+
if (typeof value === "string")
|
|
130
|
+
return [value];
|
|
131
|
+
if (!Array.isArray(value) ||
|
|
132
|
+
Object.getPrototypeOf(value) !== Array.prototype)
|
|
133
|
+
throw new Error();
|
|
134
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
135
|
+
if (lengthDescriptor === undefined ||
|
|
136
|
+
!("value" in lengthDescriptor) ||
|
|
137
|
+
!Number.isSafeInteger(lengthDescriptor.value) ||
|
|
138
|
+
lengthDescriptor.value < 0 ||
|
|
139
|
+
lengthDescriptor.value > maximum)
|
|
140
|
+
throw new Error();
|
|
141
|
+
const length = lengthDescriptor.value;
|
|
142
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
143
|
+
if (ownKeys.length !== length + 1 ||
|
|
144
|
+
ownKeys.some((key) => typeof key !== "string" ||
|
|
145
|
+
(key !== "length" && !/^(0|[1-9][0-9]*)$/.test(key))))
|
|
146
|
+
throw new Error();
|
|
147
|
+
const snapshot = [];
|
|
148
|
+
for (let index = 0; index < length; index += 1) {
|
|
149
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
150
|
+
if (descriptor === undefined ||
|
|
151
|
+
!("value" in descriptor) ||
|
|
152
|
+
typeof descriptor.value !== "string")
|
|
153
|
+
throw new Error();
|
|
154
|
+
snapshot.push(descriptor.value);
|
|
155
|
+
}
|
|
156
|
+
return snapshot;
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
throw resolutionLimit("Schema root list is invalid or exceeds its limit");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Load and verify a schema graph one breadth-first frontier at a time.
|
|
164
|
+
* No result escapes until the complete requested closure has passed all checks.
|
|
165
|
+
*/
|
|
166
|
+
export async function resolveSchemaClosure(store, admitted, roots, limitOverrides = DEFAULT_RUNTIME_LIMITS) {
|
|
167
|
+
const limits = resolveRuntimeLimits(limitOverrides);
|
|
168
|
+
const rootLimit = Math.min(limits.maxManifestRecords, limits.maxSchemaClosureBytes);
|
|
169
|
+
let frontier = [...new Set(snapshotRoots(roots, rootLimit))].sort();
|
|
170
|
+
const visited = new Set();
|
|
171
|
+
const resolved = new Map();
|
|
172
|
+
let aggregateBytes = 0;
|
|
173
|
+
let hop = 0;
|
|
174
|
+
while (frontier.length > 0) {
|
|
175
|
+
if (hop > limits.maxSchemaRefHops) {
|
|
176
|
+
throw resolutionLimit("Schema reference hop limit exceeded");
|
|
177
|
+
}
|
|
178
|
+
for (const id of frontier) {
|
|
179
|
+
let parsed;
|
|
180
|
+
try {
|
|
181
|
+
parsed = parseTypedRecordId(id);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
throw new OpenApiMcpError("RECORD_NOT_ADMITTED", "Schema reference is not admitted");
|
|
185
|
+
}
|
|
186
|
+
if (!parsed.startsWith("schema:") ||
|
|
187
|
+
!Object.hasOwn(admitted.manifest.records, id)) {
|
|
188
|
+
throw new OpenApiMcpError("RECORD_NOT_ADMITTED", "Schema reference is not admitted");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const next = new Set();
|
|
192
|
+
let offset = 0;
|
|
193
|
+
while (offset < frontier.length) {
|
|
194
|
+
const remainingBytes = limits.maxSchemaClosureBytes - aggregateBytes;
|
|
195
|
+
if (remainingBytes <= 0) {
|
|
196
|
+
throw resolutionLimit("Schema closure byte limit exceeded");
|
|
197
|
+
}
|
|
198
|
+
const chunkSize = Math.max(1, Math.min(frontier.length - offset, Math.floor(remainingBytes / limits.maxRecordBytes)));
|
|
199
|
+
const chunk = frontier.slice(offset, offset + chunkSize);
|
|
200
|
+
let rowResult;
|
|
201
|
+
try {
|
|
202
|
+
rowResult = await store.getSchemas(admitted.manifest.catalogId, admitted.manifest.releaseId, chunk);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
throw schemaLookupUnavailable();
|
|
206
|
+
}
|
|
207
|
+
const rows = snapshotRows(rowResult, chunk.length);
|
|
208
|
+
const verifiedRows = await Promise.all(rows.map((row) => verifyStoredRecord(admitted, row, limits)));
|
|
209
|
+
const byId = new Map(verifiedRows.map((record) => [record.id, record]));
|
|
210
|
+
if (byId.size !== chunk.length ||
|
|
211
|
+
verifiedRows.length !== chunk.length ||
|
|
212
|
+
chunk.some((id) => !byId.has(id))) {
|
|
213
|
+
throw new OpenApiMcpError("RECORD_NOT_ADMITTED", "A required schema record is missing or ambiguous");
|
|
214
|
+
}
|
|
215
|
+
for (const id of chunk) {
|
|
216
|
+
const verified = byId.get(id);
|
|
217
|
+
if (!verified.id.startsWith("schema:")) {
|
|
218
|
+
throw new OpenApiMcpError("RECORD_DIGEST_MISMATCH", "Schema lookup returned a non-schema record");
|
|
219
|
+
}
|
|
220
|
+
aggregateBytes += new TextEncoder().encode(canonicalJson(verified)).length;
|
|
221
|
+
if (aggregateBytes > limits.maxSchemaClosureBytes) {
|
|
222
|
+
throw resolutionLimit("Schema closure byte limit exceeded");
|
|
223
|
+
}
|
|
224
|
+
resolved.set(id, verified);
|
|
225
|
+
visited.add(id);
|
|
226
|
+
for (const reference of collectReferences(verified.schema)) {
|
|
227
|
+
if (!visited.has(reference))
|
|
228
|
+
next.add(reference);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
offset += chunk.length;
|
|
232
|
+
}
|
|
233
|
+
frontier = [...next].filter((id) => !visited.has(id)).sort();
|
|
234
|
+
hop += 1;
|
|
235
|
+
}
|
|
236
|
+
return new ImmutableSchemaMap(resolved);
|
|
237
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { JsonObject, OperationRecordV4, SchemaRecordV4, SerializeArgumentsOptions, TypedSchemaId } from "./types.ts";
|
|
2
|
+
export interface SerializedArguments {
|
|
3
|
+
readonly normalizedArguments: JsonObject;
|
|
4
|
+
readonly relativeUrl: string;
|
|
5
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
6
|
+
readonly body: Uint8Array | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Detach, validate, and serialize one exact public argument envelope.
|
|
10
|
+
* The result is authority-free and contains no caller-selected credentials.
|
|
11
|
+
*/
|
|
12
|
+
export declare function serializeArguments(operation: Readonly<OperationRecordV4>, schemas: ReadonlyMap<TypedSchemaId, Readonly<SchemaRecordV4>>, arguments_: unknown, optionsValue?: SerializeArgumentsOptions): SerializedArguments;
|