@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,197 @@
|
|
|
1
|
+
import { OpenApiMcpError } from "./errors.js";
|
|
2
|
+
import { canonicalJson, parseJsonStrict } from "./strict-json.js";
|
|
3
|
+
const base64urlAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
4
|
+
const operationRefPrefix = "opref.v1.";
|
|
5
|
+
const maxShortSegmentLength = 128;
|
|
6
|
+
const maxTailLength = 512;
|
|
7
|
+
const maxOperationRefLength = 2048;
|
|
8
|
+
const digestPattern = /^[0-9a-f]{64}$/;
|
|
9
|
+
const standardSegmentPattern = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
10
|
+
const schemaNamePattern = /^[A-Za-z0-9][A-Za-z0-9._~-]*$/;
|
|
11
|
+
const syntheticSchemaNamePattern = /^__openapi_mcp_v4_[0-9a-f]{64}$/;
|
|
12
|
+
function invalidReference(message) {
|
|
13
|
+
return new OpenApiMcpError("OPERATION_REF_INVALID", `OPERATION_REF_INVALID: ${message}`);
|
|
14
|
+
}
|
|
15
|
+
function assertSegment(value, maximumLength, pattern, label) {
|
|
16
|
+
if (value.length === 0 ||
|
|
17
|
+
value.length > maximumLength ||
|
|
18
|
+
value === "." ||
|
|
19
|
+
value === ".." ||
|
|
20
|
+
!pattern.test(value)) {
|
|
21
|
+
throw invalidReference(`${label} is invalid`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function parseCatalogId(value) {
|
|
25
|
+
assertSegment(value, maxShortSegmentLength, standardSegmentPattern, "catalog ID");
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function parseReleaseId(value) {
|
|
29
|
+
assertSegment(value, maxShortSegmentLength, standardSegmentPattern, "release ID");
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
function parseManifestDigest(value) {
|
|
33
|
+
if (!digestPattern.test(value))
|
|
34
|
+
throw invalidReference("manifest digest is invalid");
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
/** Validate a typed operation or schema record identifier before it is used as a key. */
|
|
38
|
+
export function parseTypedRecordId(value) {
|
|
39
|
+
if (value.startsWith("operation:")) {
|
|
40
|
+
const parts = value.slice("operation:".length).split(":");
|
|
41
|
+
if (parts.length !== 2)
|
|
42
|
+
throw invalidReference("operation ID is invalid");
|
|
43
|
+
const [api, operation] = parts;
|
|
44
|
+
assertSegment(api, maxShortSegmentLength, standardSegmentPattern, "API segment");
|
|
45
|
+
assertSegment(operation, maxTailLength, standardSegmentPattern, "operation segment");
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
if (value.startsWith("schema:")) {
|
|
49
|
+
const marker = ":#/components/schemas/";
|
|
50
|
+
const body = value.slice("schema:".length);
|
|
51
|
+
const markerIndex = body.indexOf(marker);
|
|
52
|
+
if (markerIndex < 1)
|
|
53
|
+
throw invalidReference("schema ID is invalid");
|
|
54
|
+
const api = body.slice(0, markerIndex);
|
|
55
|
+
const schemaName = body.slice(markerIndex + marker.length);
|
|
56
|
+
assertSegment(api, maxShortSegmentLength, standardSegmentPattern, "API segment");
|
|
57
|
+
if (!syntheticSchemaNamePattern.test(schemaName)) {
|
|
58
|
+
assertSegment(schemaName, maxTailLength, schemaNamePattern, "schema segment");
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
throw invalidReference("record ID type is invalid");
|
|
63
|
+
}
|
|
64
|
+
function encodeBase64Url(bytes) {
|
|
65
|
+
let accumulator = 0;
|
|
66
|
+
let bits = 0;
|
|
67
|
+
let encoded = "";
|
|
68
|
+
for (const byte of bytes) {
|
|
69
|
+
accumulator = (accumulator << 8) | byte;
|
|
70
|
+
bits += 8;
|
|
71
|
+
while (bits >= 6) {
|
|
72
|
+
bits -= 6;
|
|
73
|
+
encoded += base64urlAlphabet[(accumulator >> bits) & 0x3f];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (bits > 0)
|
|
77
|
+
encoded += base64urlAlphabet[(accumulator << (6 - bits)) & 0x3f];
|
|
78
|
+
return encoded;
|
|
79
|
+
}
|
|
80
|
+
function decodeBase64Url(value) {
|
|
81
|
+
if (!/^[A-Za-z0-9_-]*$/.test(value) || value.length % 4 === 1)
|
|
82
|
+
return null;
|
|
83
|
+
let accumulator = 0;
|
|
84
|
+
let bits = 0;
|
|
85
|
+
const bytes = [];
|
|
86
|
+
for (const character of value) {
|
|
87
|
+
const digit = base64urlAlphabet.indexOf(character);
|
|
88
|
+
if (digit < 0)
|
|
89
|
+
return null;
|
|
90
|
+
accumulator = (accumulator << 6) | digit;
|
|
91
|
+
bits += 6;
|
|
92
|
+
while (bits >= 8) {
|
|
93
|
+
bits -= 8;
|
|
94
|
+
bytes.push((accumulator >> bits) & 0xff);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (bits > 0 && (accumulator & ((1 << bits) - 1)) !== 0)
|
|
98
|
+
return null;
|
|
99
|
+
const decoded = Uint8Array.from(bytes);
|
|
100
|
+
return encodeBase64Url(decoded) === value ? decoded : null;
|
|
101
|
+
}
|
|
102
|
+
function isJsonObject(value) {
|
|
103
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
104
|
+
}
|
|
105
|
+
function normalizePayload(value) {
|
|
106
|
+
if (!isJsonObject(value))
|
|
107
|
+
throw invalidReference("payload must be an object");
|
|
108
|
+
const prototype = Object.getPrototypeOf(value);
|
|
109
|
+
if (prototype !== null && prototype !== Object.prototype) {
|
|
110
|
+
throw invalidReference("payload prototype is invalid");
|
|
111
|
+
}
|
|
112
|
+
const expectedKeys = [
|
|
113
|
+
"catalogId",
|
|
114
|
+
"manifestDigest",
|
|
115
|
+
"operationId",
|
|
116
|
+
"releaseId",
|
|
117
|
+
];
|
|
118
|
+
const keys = Object.keys(value).sort();
|
|
119
|
+
if (keys.length !== expectedKeys.length ||
|
|
120
|
+
keys.some((key, index) => key !== expectedKeys[index])) {
|
|
121
|
+
throw invalidReference("payload shape is invalid");
|
|
122
|
+
}
|
|
123
|
+
const fields = Object.create(null);
|
|
124
|
+
for (const key of expectedKeys) {
|
|
125
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
126
|
+
if (descriptor === undefined ||
|
|
127
|
+
!("value" in descriptor) ||
|
|
128
|
+
typeof descriptor.value !== "string") {
|
|
129
|
+
throw invalidReference("payload fields are invalid");
|
|
130
|
+
}
|
|
131
|
+
fields[key] = descriptor.value;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
catalogId: parseCatalogId(fields.catalogId),
|
|
135
|
+
releaseId: parseReleaseId(fields.releaseId),
|
|
136
|
+
operationId: parseOperationId(fields.operationId),
|
|
137
|
+
manifestDigest: parseManifestDigest(fields.manifestDigest),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function parseOperationId(value) {
|
|
141
|
+
const parsed = parseTypedRecordId(value);
|
|
142
|
+
if (!parsed.startsWith("operation:"))
|
|
143
|
+
throw invalidReference("record ID is not an operation");
|
|
144
|
+
return parsed;
|
|
145
|
+
}
|
|
146
|
+
/** Encode the exact, canonical, manifest-bound operation-reference wire format. */
|
|
147
|
+
export function encodeOperationRef(payload) {
|
|
148
|
+
const normalized = normalizePayload(payload);
|
|
149
|
+
const json = canonicalJson({
|
|
150
|
+
catalogId: normalized.catalogId,
|
|
151
|
+
releaseId: normalized.releaseId,
|
|
152
|
+
operationId: normalized.operationId,
|
|
153
|
+
manifestDigest: normalized.manifestDigest,
|
|
154
|
+
});
|
|
155
|
+
return `${operationRefPrefix}${encodeBase64Url(new TextEncoder().encode(json))}`;
|
|
156
|
+
}
|
|
157
|
+
/** Decode only canonical `opref.v1` data; authorization remains a later runtime concern. */
|
|
158
|
+
export function decodeOperationRef(reference) {
|
|
159
|
+
if (!reference.startsWith(operationRefPrefix) ||
|
|
160
|
+
reference.length > maxOperationRefLength) {
|
|
161
|
+
throw invalidReference("wire prefix or length is invalid");
|
|
162
|
+
}
|
|
163
|
+
const encoded = reference.slice(operationRefPrefix.length);
|
|
164
|
+
const bytes = decodeBase64Url(encoded);
|
|
165
|
+
if (bytes === null)
|
|
166
|
+
throw invalidReference("wire encoding is invalid");
|
|
167
|
+
let json;
|
|
168
|
+
try {
|
|
169
|
+
json = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
throw invalidReference("wire payload is not UTF-8");
|
|
173
|
+
}
|
|
174
|
+
let parsed;
|
|
175
|
+
try {
|
|
176
|
+
parsed = parseJsonStrict(json);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
throw invalidReference("wire payload is not strict JSON");
|
|
180
|
+
}
|
|
181
|
+
const payload = normalizePayload(parsed);
|
|
182
|
+
let canonical;
|
|
183
|
+
try {
|
|
184
|
+
canonical = canonicalJson({
|
|
185
|
+
catalogId: payload.catalogId,
|
|
186
|
+
releaseId: payload.releaseId,
|
|
187
|
+
operationId: payload.operationId,
|
|
188
|
+
manifestDigest: payload.manifestDigest,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
throw invalidReference("wire payload cannot be canonicalized");
|
|
193
|
+
}
|
|
194
|
+
if (json !== canonical)
|
|
195
|
+
throw invalidReference("wire payload is not canonical");
|
|
196
|
+
return payload;
|
|
197
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type AdmittedManifest, type AuthenticatedManifest, type ManifestTrust } from "./manifest.ts";
|
|
2
|
+
import type { CandidateRef, CatalogStore, CredentialBindingResolver, DestinationPolicy, GenerationStore, OpenApiRuntime, SearchQuery } from "./types.ts";
|
|
3
|
+
import { type RuntimeLimits } from "./versions.ts";
|
|
4
|
+
export interface OpenApiRuntimeOptions {
|
|
5
|
+
readonly paginationTokenCodec?: import("./types.ts").PaginationTokenCodec;
|
|
6
|
+
readonly store: CatalogStore;
|
|
7
|
+
readonly trust: ManifestTrust;
|
|
8
|
+
readonly generations: GenerationStore;
|
|
9
|
+
/** Required by preparation; search-only runtimes may omit this port. */
|
|
10
|
+
readonly destinationPolicy?: DestinationPolicy;
|
|
11
|
+
/** Required by preparation; search-only runtimes may omit this port. */
|
|
12
|
+
readonly credentialBinding?: CredentialBindingResolver;
|
|
13
|
+
readonly limits?: Partial<RuntimeLimits>;
|
|
14
|
+
}
|
|
15
|
+
/** Internal host preflight. Proves all members without admitting any generation. */
|
|
16
|
+
export declare function verifyExecutableRelease(options: OpenApiRuntimeOptions, catalogId: CandidateRef["catalogId"], releaseId: CandidateRef["releaseId"]): Promise<AuthenticatedManifest>;
|
|
17
|
+
/** Internal executable ingress; manifest-only admission retains its public contract. */
|
|
18
|
+
export declare function admitExecutableRelease(options: OpenApiRuntimeOptions, preflight: AuthenticatedManifest): Promise<AdmittedManifest>;
|
|
19
|
+
/** Construct the portable verified runtime. */
|
|
20
|
+
export declare function createOpenApiRuntime(options: OpenApiRuntimeOptions): OpenApiRuntime;
|
|
21
|
+
/** Internal adapter seam. Source calls draw from the same budget as record proof. */
|
|
22
|
+
export declare function createRuntimeWithCandidateLookup(options: OpenApiRuntimeOptions, lookup: (query: SearchQuery, tryChargeSource: () => boolean) => Promise<{
|
|
23
|
+
candidates: readonly CandidateRef[];
|
|
24
|
+
limited: boolean;
|
|
25
|
+
}>): OpenApiRuntime;
|