@typeship-ax/mcp 0.21.0 → 0.22.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/AGENTS.md +15 -11
- package/README.md +22 -53
- package/api.json +9998 -10118
- package/api.md +8983 -9120
- package/dist/arguments.d.ts +47 -0
- package/dist/arguments.d.ts.map +1 -0
- package/dist/arguments.js +254 -0
- package/dist/core/http.d.ts +162 -19
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +381 -48
- package/dist/core/pagination.d.ts +42 -6
- package/dist/core/pagination.d.ts.map +1 -1
- package/dist/core/pagination.js +111 -17
- package/dist/credential-storage.d.ts +10 -3
- package/dist/credential-storage.d.ts.map +1 -1
- package/dist/credential-storage.js +15 -6
- package/dist/dates.d.ts +1 -1
- package/dist/dates.js +1 -1
- package/dist/errors.d.ts +20 -84
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -108
- package/dist/fields.d.ts +29 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +101 -0
- package/dist/index.d.ts +28 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -25
- package/dist/mcp-authorization.d.ts.map +1 -1
- package/dist/mcp-authorization.js +34 -10
- package/dist/mcp-protocol.d.ts +87 -44
- package/dist/mcp-protocol.d.ts.map +1 -1
- package/dist/mcp-protocol.js +552 -478
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +127 -28
- package/dist/named-credentials.d.ts +19 -0
- package/dist/named-credentials.d.ts.map +1 -1
- package/dist/named-credentials.js +81 -1
- package/dist/oauth-request.d.ts +7 -1
- package/dist/oauth-request.d.ts.map +1 -1
- package/dist/oauth-request.js +26 -4
- package/dist/oauth-session.d.ts +13 -1
- package/dist/oauth-session.d.ts.map +1 -1
- package/dist/oauth-session.js +34 -18
- package/dist/ops.d.ts +53 -5
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +49 -40
- package/dist/resources/api-keys.d.ts +10 -7
- package/dist/resources/api-keys.d.ts.map +1 -1
- package/dist/resources/api-keys.js +10 -31
- package/dist/resources/deliveries.d.ts +88 -4
- package/dist/resources/deliveries.d.ts.map +1 -1
- package/dist/resources/deliveries.js +95 -18
- package/dist/resources/drafts.d.ts +15 -15
- package/dist/resources/drafts.d.ts.map +1 -1
- package/dist/resources/drafts.js +11 -64
- package/dist/resources/files.d.ts +4 -4
- package/dist/resources/files.d.ts.map +1 -1
- package/dist/resources/files.js +3 -12
- package/dist/resources/generations.d.ts +14 -14
- package/dist/resources/generations.d.ts.map +1 -1
- package/dist/resources/generations.js +21 -45
- package/dist/resources/organization.d.ts +4 -4
- package/dist/resources/organization.d.ts.map +1 -1
- package/dist/resources/organization.js +3 -10
- package/dist/resources/{generate.d.ts → packages.d.ts} +16 -16
- package/dist/resources/packages.d.ts.map +1 -0
- package/dist/resources/{generate.js → packages.js} +13 -29
- package/dist/resources/projects.d.ts +50 -50
- package/dist/resources/projects.d.ts.map +1 -1
- package/dist/resources/projects.js +60 -116
- package/dist/resources/releases.d.ts +21 -16
- package/dist/resources/releases.d.ts.map +1 -1
- package/dist/resources/releases.js +18 -39
- package/dist/resources/spec-revisions.d.ts +15 -6
- package/dist/resources/spec-revisions.d.ts.map +1 -1
- package/dist/resources/spec-revisions.js +6 -28
- package/dist/resources/specs.d.ts +7 -7
- package/dist/resources/specs.d.ts.map +1 -1
- package/dist/resources/specs.js +6 -34
- package/dist/resources/targets.d.ts +48 -48
- package/dist/resources/targets.d.ts.map +1 -1
- package/dist/resources/targets.js +58 -114
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +78 -76
- package/dist/search.d.ts +54 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +421 -0
- package/dist/types.d.ts +499 -339
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -18
- package/dist/worker.js +2 -2
- package/package.json +5 -2
- package/server.json +5 -5
- package/src/arguments.ts +242 -0
- package/src/core/http.ts +457 -58
- package/src/core/pagination.ts +129 -18
- package/src/credential-storage.ts +16 -6
- package/src/dates.ts +1 -1
- package/src/errors.ts +46 -115
- package/src/fields.ts +91 -0
- package/src/index.ts +45 -28
- package/src/mcp-authorization.ts +29 -9
- package/src/mcp-protocol.ts +580 -428
- package/src/mcp.ts +113 -26
- package/src/named-credentials.ts +66 -1
- package/src/oauth-request.ts +32 -6
- package/src/oauth-session.ts +37 -19
- package/src/ops.ts +82 -44
- package/src/resources/api-keys.ts +34 -48
- package/src/resources/deliveries.ts +211 -30
- package/src/resources/drafts.ts +60 -107
- package/src/resources/files.ts +19 -20
- package/src/resources/generations.ts +57 -75
- package/src/resources/organization.ts +11 -16
- package/src/resources/{generate.ts → packages.ts} +43 -51
- package/src/resources/projects.ts +145 -200
- package/src/resources/releases.ts +48 -65
- package/src/resources/spec-revisions.ts +38 -47
- package/src/resources/specs.ts +39 -59
- package/src/resources/targets.ts +143 -193
- package/src/schemas.ts +78 -76
- package/src/search.ts +434 -0
- package/src/types.ts +538 -357
- package/src/worker.ts +2 -2
- package/dist/resources/generate.d.ts.map +0 -1
- package/dist/resources/publications.d.ts +0 -47
- package/dist/resources/publications.d.ts.map +0 -1
- package/dist/resources/publications.js +0 -70
- package/src/resources/publications.ts +0 -140
package/src/arguments.ts
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Argument checks shared by the generated CLI and MCP server: coerce a value
|
|
3
|
+
* toward its schema where the intent is unambiguous, and report every
|
|
4
|
+
* problem, nested ones included, with the dotted path of the argument it
|
|
5
|
+
* belongs to. Generated by Typeship — https://typeship.dev
|
|
6
|
+
* No external dependencies.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { dateKindOf, relativeDate } from "./dates.js";
|
|
10
|
+
|
|
11
|
+
export interface ArgumentIssue {
|
|
12
|
+
code: "UNKNOWN_ARGUMENT" | "INVALID_ARGUMENT" | "MISSING_ARGUMENT";
|
|
13
|
+
argument: string;
|
|
14
|
+
message: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const normalizeName = (name: string): string => name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
18
|
+
|
|
19
|
+
function editDistance(a: string, b: string): number {
|
|
20
|
+
const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
21
|
+
for (let i = 1; i <= a.length; i++) {
|
|
22
|
+
let diag = prev[0]!;
|
|
23
|
+
prev[0] = i;
|
|
24
|
+
for (let j = 1; j <= b.length; j++) {
|
|
25
|
+
const tmp = prev[j]!;
|
|
26
|
+
prev[j] = Math.min(prev[j]! + 1, prev[j - 1]! + 1, diag + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
27
|
+
diag = tmp;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return prev[b.length]!;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The closest accepted name: same letters ignoring case/punctuation first,
|
|
34
|
+
* then a small edit distance. Undefined when nothing is close. */
|
|
35
|
+
export function closestName(name: string, known: string[]): string | undefined {
|
|
36
|
+
const exact = known.filter((k) => normalizeName(k) === normalizeName(name));
|
|
37
|
+
if (exact.length === 1) return exact[0];
|
|
38
|
+
if (exact.length > 1) return undefined;
|
|
39
|
+
let best: { name: string; d: number } | undefined;
|
|
40
|
+
for (const k of known) {
|
|
41
|
+
const d = editDistance(name.toLowerCase(), k.toLowerCase());
|
|
42
|
+
if (d <= Math.max(1, Math.floor(k.length / 4)) && (best === undefined || d < best.d)) best = { name: k, d };
|
|
43
|
+
}
|
|
44
|
+
return best?.name;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function schemaTypes(schema: Record<string, unknown>): string[] {
|
|
48
|
+
const t = schema.type;
|
|
49
|
+
if (typeof t === "string") return [t];
|
|
50
|
+
if (Array.isArray(t)) return t.filter((x): x is string => typeof x === "string");
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Coerce one value toward its schema when the intent is unambiguous: the
|
|
56
|
+
* strings agents produce for booleans and numbers, a JSON string for an
|
|
57
|
+
* object or array, a scalar for a one-element array, an enum member in the
|
|
58
|
+
* wrong case. Returns the value to send, or a message when it can't be made
|
|
59
|
+
* to fit. Untyped schemas (unions, anything) pass through. The first
|
|
60
|
+
* problem only; checkValue reports every nested one.
|
|
61
|
+
*/
|
|
62
|
+
export function coerceValue(value: unknown, schema: Record<string, unknown>): { value: unknown } | { error: string } {
|
|
63
|
+
const issues: ArgumentIssue[] = [];
|
|
64
|
+
const out = checkValue(value, schema, "", issues);
|
|
65
|
+
return issues.length > 0 ? { error: issues[0]!.message } : { value: out };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Coerce one scalar-level value (type and enum) without looking inside it. */
|
|
69
|
+
function coerceShallow(value: unknown, schema: Record<string, unknown>): { value: unknown } | { error: string } {
|
|
70
|
+
if (value === null || value === undefined) return { value };
|
|
71
|
+
// Date-shaped arguments take relative forms (-P7D, 7 days ago, today),
|
|
72
|
+
// resolved here so the API sees an absolute value.
|
|
73
|
+
const dateKind = dateKindOf(schema.format);
|
|
74
|
+
if (dateKind && typeof value === "string") {
|
|
75
|
+
const resolved = relativeDate(value, dateKind);
|
|
76
|
+
if (resolved && "error" in resolved) return { error: resolved.error };
|
|
77
|
+
if (resolved) value = resolved.value;
|
|
78
|
+
}
|
|
79
|
+
const types = schemaTypes(schema);
|
|
80
|
+
const enumValues = Array.isArray(schema.enum) ? schema.enum : undefined;
|
|
81
|
+
const accepts = (t: string) => types.length === 0 || types.includes(t);
|
|
82
|
+
const kind = Array.isArray(value) ? "array" : typeof value;
|
|
83
|
+
|
|
84
|
+
let out: unknown = value;
|
|
85
|
+
if (types.length > 0) {
|
|
86
|
+
if (kind === "boolean" && !accepts("boolean")) {
|
|
87
|
+
if (accepts("string")) out = String(value);
|
|
88
|
+
else return { error: "expected " + types.join(" or ") + ", got boolean" };
|
|
89
|
+
} else if (kind === "number" && !accepts("number") && !accepts("integer")) {
|
|
90
|
+
if (accepts("string")) out = String(value);
|
|
91
|
+
else if (accepts("array")) out = [value];
|
|
92
|
+
else return { error: "expected " + types.join(" or ") + ", got number" };
|
|
93
|
+
} else if (kind === "number" && accepts("integer") && !accepts("number") && !Number.isInteger(value)) {
|
|
94
|
+
return { error: "expected an integer, got " + String(value) };
|
|
95
|
+
} else if (kind === "string" && !accepts("string")) {
|
|
96
|
+
const s = (value as string).trim();
|
|
97
|
+
if (accepts("boolean") && /^(true|false|yes|no|1|0)$/i.test(s)) out = /^(true|yes|1)$/i.test(s);
|
|
98
|
+
else if ((accepts("integer") || accepts("number")) && s !== "" && !Number.isNaN(Number(s))) {
|
|
99
|
+
const n = Number(s);
|
|
100
|
+
if (accepts("integer") && !accepts("number") && !Number.isInteger(n)) return { error: "expected an integer, got \"" + s + "\"" };
|
|
101
|
+
out = n;
|
|
102
|
+
} else if ((accepts("object") || accepts("array")) && /^[[{]/.test(s)) {
|
|
103
|
+
try {
|
|
104
|
+
const parsed: unknown = JSON.parse(s);
|
|
105
|
+
const parsedKind = Array.isArray(parsed) ? "array" : parsed === null ? "null" : typeof parsed;
|
|
106
|
+
if (!accepts(parsedKind)) return { error: "expected " + types.join(" or ") + ", got a JSON " + parsedKind + " in a string" };
|
|
107
|
+
out = parsed;
|
|
108
|
+
} catch {
|
|
109
|
+
return { error: "expected " + types.join(" or ") + ", got a string that is not valid JSON" };
|
|
110
|
+
}
|
|
111
|
+
} else if (accepts("array")) {
|
|
112
|
+
out = [value];
|
|
113
|
+
} else {
|
|
114
|
+
return { error: "expected " + types.join(" or ") + ", got string" };
|
|
115
|
+
}
|
|
116
|
+
} else if (kind === "object" && !accepts("object")) {
|
|
117
|
+
if (accepts("array")) out = [value];
|
|
118
|
+
else return { error: "expected " + types.join(" or ") + ", got object" };
|
|
119
|
+
} else if (kind === "array" && !accepts("array")) {
|
|
120
|
+
return { error: "expected " + types.join(" or ") + ", got array" };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (enumValues && typeof out === "string" && !enumValues.includes(out)) {
|
|
125
|
+
const match = enumValues.filter((e) => typeof e === "string" && e.toLowerCase() === (out as string).toLowerCase());
|
|
126
|
+
if (match.length === 1) out = match[0];
|
|
127
|
+
else return { error: "must be one of " + enumValues.map((e) => JSON.stringify(e)).join(", ") + ", got " + JSON.stringify(out) };
|
|
128
|
+
}
|
|
129
|
+
return { value: out };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** A nullable single-variant union (`anyOf: [X, {type: "null"}]`, the shape
|
|
133
|
+
* nullable GraphQL enums and objects take) validates as X; any other union
|
|
134
|
+
* passes through unchecked. */
|
|
135
|
+
function nullableVariant(schema: Record<string, unknown>): Record<string, unknown> | undefined {
|
|
136
|
+
const variants = schema.anyOf ?? schema.oneOf;
|
|
137
|
+
if (!Array.isArray(variants) || variants.length !== 2 || schema.type !== undefined) return undefined;
|
|
138
|
+
const isNull = (v: unknown) => Boolean(v) && typeof v === "object" && (v as Record<string, unknown>).type === "null";
|
|
139
|
+
const others = variants.filter((v) => !isNull(v));
|
|
140
|
+
if (others.length !== 1 || !others[0] || typeof others[0] !== "object") return undefined;
|
|
141
|
+
return others[0] as Record<string, unknown>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const MAX_CHECK_DEPTH = 16;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Check one argument value against its schema, the way prepareCall checks
|
|
148
|
+
* top-level arguments, and return the value to send. Recurses into array
|
|
149
|
+
* items and object properties: types and enums are coerced where the intent
|
|
150
|
+
* is clear, required properties must be present, strings must match their
|
|
151
|
+
* pattern, and keys an object declares closed (`additionalProperties:
|
|
152
|
+
* false`, as every GraphQL input type is) must be known. Every problem is
|
|
153
|
+
* pushed to issues with its dotted path (input.priority, items[0].name).
|
|
154
|
+
*/
|
|
155
|
+
export function checkValue(
|
|
156
|
+
value: unknown,
|
|
157
|
+
schema: Record<string, unknown>,
|
|
158
|
+
path: string,
|
|
159
|
+
issues: ArgumentIssue[],
|
|
160
|
+
options: { skipPattern?: boolean; depth?: number } = {},
|
|
161
|
+
): unknown {
|
|
162
|
+
const depth = options.depth ?? 0;
|
|
163
|
+
const at = (suffix: string) => path === "" ? suffix : path + ": " + suffix;
|
|
164
|
+
if (value === null || value === undefined) return value;
|
|
165
|
+
const variant = nullableVariant(schema);
|
|
166
|
+
if (variant) return checkValue(value, variant, path, issues, options);
|
|
167
|
+
|
|
168
|
+
const shallow = coerceShallow(value, schema);
|
|
169
|
+
if ("error" in shallow) {
|
|
170
|
+
issues.push({ code: "INVALID_ARGUMENT", argument: path, message: at(shallow.error) });
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
let out = shallow.value;
|
|
174
|
+
if (depth >= MAX_CHECK_DEPTH) return out;
|
|
175
|
+
|
|
176
|
+
if (typeof out === "string" && typeof schema.pattern === "string" && !options.skipPattern) {
|
|
177
|
+
let pattern: RegExp | undefined;
|
|
178
|
+
try { pattern = new RegExp(schema.pattern, "u"); } catch {
|
|
179
|
+
try { pattern = new RegExp(schema.pattern); } catch { /* not an ECMAScript pattern: never false-alarm */ }
|
|
180
|
+
}
|
|
181
|
+
if (pattern && !pattern.test(out)) {
|
|
182
|
+
issues.push({ code: "INVALID_ARGUMENT", argument: path, message: at("must match the pattern " + schema.pattern + ", got " + JSON.stringify(out)) });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Array items: check each against the items schema when it has one.
|
|
187
|
+
if (Array.isArray(out) && schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
|
|
188
|
+
const itemSchema = schema.items as Record<string, unknown>;
|
|
189
|
+
out = out.map((item, i) => checkValue(item, itemSchema, path + "[" + i + "]", issues, { depth: depth + 1 }));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Object properties: the same checks one level down, keyed by dotted path.
|
|
193
|
+
if (out && typeof out === "object" && !Array.isArray(out) && schema.properties && typeof schema.properties === "object") {
|
|
194
|
+
const properties = schema.properties as Record<string, Record<string, unknown>>;
|
|
195
|
+
const known = Object.keys(properties);
|
|
196
|
+
const closed = schema.additionalProperties === false;
|
|
197
|
+
const input = out as Record<string, unknown>;
|
|
198
|
+
const next: Record<string, unknown> = {};
|
|
199
|
+
const child = (key: string) => path === "" ? key : path + "." + key;
|
|
200
|
+
for (const [key, entry] of Object.entries(input)) {
|
|
201
|
+
if (entry === undefined) continue;
|
|
202
|
+
let name = key;
|
|
203
|
+
if (properties[key] === undefined && closed) {
|
|
204
|
+
const close = closestName(key, known);
|
|
205
|
+
if (close !== undefined && normalizeName(close) === normalizeName(key) && input[close] === undefined) {
|
|
206
|
+
name = close;
|
|
207
|
+
} else {
|
|
208
|
+
const accepted = known.length <= 20 ? " Accepted: " + known.join(", ") + "." : "";
|
|
209
|
+
issues.push({
|
|
210
|
+
code: "UNKNOWN_ARGUMENT",
|
|
211
|
+
argument: child(key),
|
|
212
|
+
message: "Unknown property \"" + child(key) + "\"" + (close !== undefined ? "; did you mean \"" + child(close) + "\"?" : ".") + accepted,
|
|
213
|
+
});
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const propSchema = properties[name];
|
|
218
|
+
next[name] = propSchema && typeof propSchema === "object"
|
|
219
|
+
? checkValue(entry, propSchema, child(name), issues, { depth: depth + 1, skipPattern: typeof entry === "string" && isMeReference(name, entry) })
|
|
220
|
+
: entry;
|
|
221
|
+
}
|
|
222
|
+
for (const name of Array.isArray(schema.required) ? schema.required as string[] : []) {
|
|
223
|
+
if (next[name] === undefined && properties[name] !== undefined) {
|
|
224
|
+
const description = properties[name]!.description;
|
|
225
|
+
issues.push({ code: "MISSING_ARGUMENT", argument: child(name), message: "Missing required property \"" + child(name) + "\"" + (description ? ": " + String(description).split("\n")[0] : ".") });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
out = next;
|
|
229
|
+
}
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function userShapedReference(name: string): boolean {
|
|
234
|
+
const normalized = name.toLowerCase().replace(/[^a-z0-9]/g, "").replace(/ids?$/, "");
|
|
235
|
+
return ["user", "assignee", "owner", "member", "actor", "creator", "account", "profile", "subscriber"].includes(normalized);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** "me" given for a user-shaped argument or property, resolved to the
|
|
239
|
+
* caller's ID through the identity tool. */
|
|
240
|
+
export function isMeReference(name: string, value: string): boolean {
|
|
241
|
+
return value.trim().toLowerCase() === "me" && userShapedReference(name);
|
|
242
|
+
}
|