@typeship-ax/mcp 0.22.0 → 0.23.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 +1 -1
- package/README.md +1 -1
- package/api.md +1 -1
- package/dist/arguments.d.ts +9 -2
- package/dist/arguments.d.ts.map +1 -1
- package/dist/arguments.js +17 -6
- package/dist/fields.d.ts +8 -1
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +91 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/mcp-protocol.d.ts +21 -0
- package/dist/mcp-protocol.d.ts.map +1 -1
- package/dist/mcp-protocol.js +280 -58
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +4 -3
- package/dist/ops.d.ts +5 -0
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +66 -6
- package/dist/resources/deliveries.d.ts +1 -1
- package/dist/resources/deliveries.d.ts.map +1 -1
- package/dist/resources/deliveries.js +1 -1
- package/dist/resources/drafts.d.ts +1 -1
- package/dist/resources/drafts.d.ts.map +1 -1
- package/dist/resources/drafts.js +1 -1
- package/dist/resources/generations.d.ts +2 -2
- package/dist/resources/generations.d.ts.map +1 -1
- package/dist/resources/generations.js +2 -2
- package/dist/resources/releases.d.ts +1 -1
- package/dist/resources/releases.d.ts.map +1 -1
- package/dist/resources/releases.js +1 -1
- package/dist/resources/spec-revisions.d.ts +1 -1
- package/dist/resources/spec-revisions.d.ts.map +1 -1
- package/dist/resources/spec-revisions.js +1 -1
- package/dist/resources/targets.d.ts +1 -1
- package/dist/resources/targets.d.ts.map +1 -1
- package/dist/resources/targets.js +1 -1
- package/dist/type-docs.d.ts +61 -0
- package/dist/type-docs.d.ts.map +1 -0
- package/dist/type-docs.js +174 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/arguments.ts +19 -7
- package/src/fields.ts +81 -5
- package/src/index.ts +3 -3
- package/src/mcp-protocol.ts +279 -58
- package/src/mcp.ts +4 -3
- package/src/ops.ts +69 -6
- package/src/resources/deliveries.ts +2 -2
- package/src/resources/drafts.ts +2 -2
- package/src/resources/generations.ts +4 -4
- package/src/resources/releases.ts +2 -2
- package/src/resources/spec-revisions.ts +2 -2
- package/src/resources/targets.ts +2 -2
- package/src/type-docs.ts +205 -0
|
@@ -90,7 +90,7 @@ export class DeliveriesResource {
|
|
|
90
90
|
query: {
|
|
91
91
|
limit: params?.limit,
|
|
92
92
|
cursor: params?.cursor,
|
|
93
|
-
target_id: params?.
|
|
93
|
+
target_id: params?.target_id,
|
|
94
94
|
},
|
|
95
95
|
idempotent: true,
|
|
96
96
|
schemaKey: "deliveries.list",
|
|
@@ -239,7 +239,7 @@ export interface DeliveriesListParams {
|
|
|
239
239
|
*/
|
|
240
240
|
cursor?: string;
|
|
241
241
|
/** Only Deliveries of this Target. */
|
|
242
|
-
|
|
242
|
+
target_id?: TargetId;
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
/** Typed errors `list` can throw. */
|
package/src/resources/drafts.ts
CHANGED
|
@@ -58,7 +58,7 @@ export class DraftsResource {
|
|
|
58
58
|
query: {
|
|
59
59
|
limit: params?.limit,
|
|
60
60
|
cursor: params?.cursor,
|
|
61
|
-
target_id: params?.
|
|
61
|
+
target_id: params?.target_id,
|
|
62
62
|
status: params?.status,
|
|
63
63
|
},
|
|
64
64
|
idempotent: true,
|
|
@@ -249,7 +249,7 @@ export interface DraftsListParams {
|
|
|
249
249
|
*/
|
|
250
250
|
cursor?: string;
|
|
251
251
|
/** Only Drafts of this Target. */
|
|
252
|
-
|
|
252
|
+
target_id?: TargetId;
|
|
253
253
|
/** Only Drafts with this status. */
|
|
254
254
|
status?: DraftStatus;
|
|
255
255
|
}
|
|
@@ -72,8 +72,8 @@ export class GenerationsResource {
|
|
|
72
72
|
query: {
|
|
73
73
|
limit: params?.limit,
|
|
74
74
|
cursor: params?.cursor,
|
|
75
|
-
project_id: params?.
|
|
76
|
-
target_id: params?.
|
|
75
|
+
project_id: params?.project_id,
|
|
76
|
+
target_id: params?.target_id,
|
|
77
77
|
status: params?.status,
|
|
78
78
|
},
|
|
79
79
|
idempotent: true,
|
|
@@ -173,9 +173,9 @@ export interface GenerationsListParams {
|
|
|
173
173
|
*/
|
|
174
174
|
cursor?: string;
|
|
175
175
|
/** Only Generations in this Project. */
|
|
176
|
-
|
|
176
|
+
project_id?: ProjectId;
|
|
177
177
|
/** Only Generations of this Target. */
|
|
178
|
-
|
|
178
|
+
target_id?: TargetId;
|
|
179
179
|
/** Only Generations with this status. */
|
|
180
180
|
status?: GenerationStatus;
|
|
181
181
|
}
|
|
@@ -45,7 +45,7 @@ export class ReleasesResource {
|
|
|
45
45
|
query: {
|
|
46
46
|
limit: params?.limit,
|
|
47
47
|
cursor: params?.cursor,
|
|
48
|
-
target_id: params?.
|
|
48
|
+
target_id: params?.target_id,
|
|
49
49
|
},
|
|
50
50
|
idempotent: true,
|
|
51
51
|
schemaKey: "releases.list",
|
|
@@ -128,7 +128,7 @@ export interface ReleasesListParams {
|
|
|
128
128
|
*/
|
|
129
129
|
cursor?: string;
|
|
130
130
|
/** Only releases of this Target. */
|
|
131
|
-
|
|
131
|
+
target_id?: TargetId;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/** Typed errors `list` can throw. */
|
|
@@ -54,7 +54,7 @@ export class SpecRevisionsResource {
|
|
|
54
54
|
query: {
|
|
55
55
|
limit: params?.limit,
|
|
56
56
|
cursor: params?.cursor,
|
|
57
|
-
spec_id: params?.
|
|
57
|
+
spec_id: params?.spec_id,
|
|
58
58
|
},
|
|
59
59
|
idempotent: true,
|
|
60
60
|
schemaKey: "specRevisions.list",
|
|
@@ -151,7 +151,7 @@ export interface SpecRevisionsListParams {
|
|
|
151
151
|
*/
|
|
152
152
|
cursor?: string;
|
|
153
153
|
/** Only revisions of this Spec. */
|
|
154
|
-
|
|
154
|
+
spec_id?: SpecId;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
/** Typed errors `list` can throw. */
|
package/src/resources/targets.ts
CHANGED
|
@@ -84,7 +84,7 @@ export class TargetsResource {
|
|
|
84
84
|
query: {
|
|
85
85
|
limit: params?.limit,
|
|
86
86
|
cursor: params?.cursor,
|
|
87
|
-
project_id: params?.
|
|
87
|
+
project_id: params?.project_id,
|
|
88
88
|
},
|
|
89
89
|
idempotent: true,
|
|
90
90
|
schemaKey: "targets.list",
|
|
@@ -260,7 +260,7 @@ export interface TargetsListParams {
|
|
|
260
260
|
*/
|
|
261
261
|
cursor?: string;
|
|
262
262
|
/** Only Targets in this Project. */
|
|
263
|
-
|
|
263
|
+
project_id?: ProjectId;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
/** Typed errors `list` can throw. */
|
package/src/type-docs.ts
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named input types for the docs surfaces (MCP read_docs, the CLI's docs
|
|
3
|
+
* command): a type's reference by name, and an argument path such as
|
|
4
|
+
* "filter.team.key" resolved to its type. Each type is described once with
|
|
5
|
+
* its fields typed by name, so a lookup is bounded and every type it
|
|
6
|
+
* mentions can be looked up the same way.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface InputTypeField {
|
|
10
|
+
/** As an agent writes it: a type name, string[], "a"|"b", object. */
|
|
11
|
+
type: string;
|
|
12
|
+
required?: true;
|
|
13
|
+
description?: string;
|
|
14
|
+
default?: unknown;
|
|
15
|
+
deprecated?: true;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface InputTypeDoc {
|
|
19
|
+
description?: string;
|
|
20
|
+
/** An input object's fields. */
|
|
21
|
+
fields?: Record<string, InputTypeField>;
|
|
22
|
+
/** An enum's values. */
|
|
23
|
+
values?: unknown[];
|
|
24
|
+
/** A union's member types. */
|
|
25
|
+
variants?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface InputTypes {
|
|
29
|
+
types: Record<string, InputTypeDoc>;
|
|
30
|
+
/** Per tool, each argument whose type is (or contains) a named type. */
|
|
31
|
+
args: Record<string, Record<string, string>>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Enum values a type page lists before a count. */
|
|
35
|
+
const TYPE_ENUM_VALUES = 200;
|
|
36
|
+
|
|
37
|
+
/** One line of prose: links reduced to their text. */
|
|
38
|
+
function prose(text: string | undefined): string {
|
|
39
|
+
return (text ?? "").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\s+/g, " ").trim();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The table's type names an expression mentions, in order. */
|
|
43
|
+
export function namedTypesIn(table: InputTypes | undefined, expression: string | undefined): string[] {
|
|
44
|
+
if (!table || !expression) return [];
|
|
45
|
+
return [...new Set(expression.split(/[|()[\]\s]+/).filter((part) => Object.hasOwn(table.types, part)))];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The one input object an expression names (IssueFilter, IssueFilter[]),
|
|
49
|
+
* whose fields a path continues into. */
|
|
50
|
+
function objectTypeOf(table: InputTypes, expression: string | undefined): string | undefined {
|
|
51
|
+
const objects = namedTypesIn(table, expression).filter((name) => table.types[name]!.fields);
|
|
52
|
+
return objects.length === 1 ? objects[0] : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** A type by exact name, else by a case-insensitive match. */
|
|
56
|
+
export function findInputType(table: InputTypes | undefined, name: string): string | undefined {
|
|
57
|
+
if (!table) return undefined;
|
|
58
|
+
if (Object.hasOwn(table.types, name)) return name;
|
|
59
|
+
const lower = name.toLowerCase();
|
|
60
|
+
return Object.keys(table.types).find((candidate) => candidate.toLowerCase() === lower);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function fieldLine(name: string, field: InputTypeField): string {
|
|
64
|
+
const extras = [
|
|
65
|
+
...(field.required ? ["required"] : []),
|
|
66
|
+
...(field.default !== undefined ? ["default " + JSON.stringify(field.default)] : []),
|
|
67
|
+
...(field.deprecated ? ["deprecated"] : []),
|
|
68
|
+
];
|
|
69
|
+
const description = prose(field.description);
|
|
70
|
+
return " " + name + " (" + field.type + (extras.length ? ", " + extras.join(", ") : "") + ")" + (description ? ": " + description : "");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** How to read another type, in the caller's own syntax. */
|
|
74
|
+
export type TypeLookupHint = (name: string) => string;
|
|
75
|
+
|
|
76
|
+
/** A named type's reference: description, then its fields, values or
|
|
77
|
+
* members, then how to read the named types it mentions. */
|
|
78
|
+
export function inputTypeText(table: InputTypes, name: string, lookup: TypeLookupHint): string {
|
|
79
|
+
const doc = table.types[name]!;
|
|
80
|
+
const kind = doc.fields ? "input object" : doc.values ? "enum" : "union";
|
|
81
|
+
const lines = [name + " (" + kind + ")"];
|
|
82
|
+
if (doc.description) lines.push(prose(doc.description));
|
|
83
|
+
const mentioned: string[] = [];
|
|
84
|
+
if (doc.fields) {
|
|
85
|
+
const entries = Object.entries(doc.fields);
|
|
86
|
+
lines.push("", entries.length === 0 ? "No fields." : "Fields (" + entries.length + "):");
|
|
87
|
+
for (const [field, spec] of entries) {
|
|
88
|
+
lines.push(fieldLine(field, spec));
|
|
89
|
+
mentioned.push(...namedTypesIn(table, spec.type));
|
|
90
|
+
}
|
|
91
|
+
} else if (doc.values) {
|
|
92
|
+
const values = doc.values.slice(0, TYPE_ENUM_VALUES).map((value) => JSON.stringify(value));
|
|
93
|
+
lines.push("", "Values: " + values.join(", ") + (doc.values.length > TYPE_ENUM_VALUES ? ", … " + (doc.values.length - TYPE_ENUM_VALUES) + " more" : ""));
|
|
94
|
+
} else if (doc.variants) {
|
|
95
|
+
lines.push("", "One of: " + doc.variants.join(" | "));
|
|
96
|
+
for (const variant of doc.variants) mentioned.push(...namedTypesIn(table, variant));
|
|
97
|
+
}
|
|
98
|
+
const others = [...new Set(mentioned)].filter((other) => other !== name);
|
|
99
|
+
if (others.length > 0) {
|
|
100
|
+
lines.push("", "Types used above (" + others.length + "): " + others.join(", ") + ". Read one with " + lookup("<type>") + ".");
|
|
101
|
+
}
|
|
102
|
+
return lines.join("\n");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** A field of an anonymous object in an inline JSON Schema, one level. */
|
|
106
|
+
interface InlineSchema { type?: unknown; properties?: Record<string, InlineSchema>; required?: string[]; items?: InlineSchema; anyOf?: InlineSchema[]; oneOf?: InlineSchema[]; enum?: unknown[]; description?: string }
|
|
107
|
+
|
|
108
|
+
function inlineObject(schema: InlineSchema | undefined): InlineSchema | undefined {
|
|
109
|
+
if (!schema || typeof schema !== "object") return undefined;
|
|
110
|
+
if (schema.properties && typeof schema.properties === "object") return schema;
|
|
111
|
+
if (schema.items) return inlineObject(schema.items);
|
|
112
|
+
const variants = schema.anyOf ?? schema.oneOf;
|
|
113
|
+
if (Array.isArray(variants)) {
|
|
114
|
+
const objects = variants.map(inlineObject).filter((variant) => variant !== undefined);
|
|
115
|
+
if (objects.length === 1) return objects[0];
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function inlineType(schema: InlineSchema): string {
|
|
121
|
+
if (Array.isArray(schema.enum)) return schema.enum.filter((value) => value !== null).map((value) => JSON.stringify(value)).join("|");
|
|
122
|
+
const variants = schema.anyOf ?? schema.oneOf;
|
|
123
|
+
if (Array.isArray(variants)) return [...new Set(variants.map(inlineType))].filter((t) => t !== "null").join("|") || "null";
|
|
124
|
+
const types = (Array.isArray(schema.type) ? schema.type : typeof schema.type === "string" ? [schema.type] : []).filter((t) => t !== "null") as string[];
|
|
125
|
+
if (types.includes("array")) {
|
|
126
|
+
const inner = schema.items ? inlineType(schema.items) : "any";
|
|
127
|
+
return (/[| ]/.test(inner) ? "(" + inner + ")" : inner) + "[]";
|
|
128
|
+
}
|
|
129
|
+
return types.join("|") || (schema.properties ? "object" : "any");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type PathLookup =
|
|
133
|
+
| { ok: true; text: string }
|
|
134
|
+
| { ok: false; message: string; available: string[] };
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* What one argument path means: the field's type, whether it is required,
|
|
138
|
+
* its description, and, when its type is named, that type's reference in
|
|
139
|
+
* full (a comparator's operators, an enum's values). `root` is an
|
|
140
|
+
* operation (its tool name, inline input schema, and how the caller names
|
|
141
|
+
* it) or a named type.
|
|
142
|
+
*/
|
|
143
|
+
export function argumentPathText(
|
|
144
|
+
table: InputTypes | undefined,
|
|
145
|
+
root: { tool: string; inputSchema: Record<string, unknown>; label?: string } | { type: string },
|
|
146
|
+
path: string,
|
|
147
|
+
lookup: TypeLookupHint,
|
|
148
|
+
): PathLookup {
|
|
149
|
+
const segments = path.split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
|
|
150
|
+
if (segments.length === 0) return { ok: false, message: "The path is empty.", available: [] };
|
|
151
|
+
// Walk the named-type table where the path has a type name, else the
|
|
152
|
+
// operation's inline schema.
|
|
153
|
+
let typeName: string | undefined = "type" in root ? root.type : undefined;
|
|
154
|
+
let inline: InlineSchema | undefined = "type" in root ? undefined : root.inputSchema as InlineSchema;
|
|
155
|
+
let expression: string | undefined;
|
|
156
|
+
let field: InputTypeField | undefined;
|
|
157
|
+
const label = "type" in root ? root.type : root.label ?? root.tool;
|
|
158
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
159
|
+
const segment = segments[index]!;
|
|
160
|
+
const at = index === 0 ? label : label + " " + segments.slice(0, index).join(".");
|
|
161
|
+
if (typeName && table) {
|
|
162
|
+
const fields = table.types[typeName]!.fields ?? {};
|
|
163
|
+
if (!Object.hasOwn(fields, segment)) return { ok: false, message: at + " (" + typeName + ") has no field \"" + segment + "\".", available: Object.keys(fields) };
|
|
164
|
+
field = fields[segment]!;
|
|
165
|
+
expression = field.type;
|
|
166
|
+
inline = undefined;
|
|
167
|
+
} else {
|
|
168
|
+
const object = inlineObject(inline);
|
|
169
|
+
const properties = object?.properties ?? {};
|
|
170
|
+
if (!object || !Object.hasOwn(properties, segment)) {
|
|
171
|
+
return { ok: false, message: object ? at + " has no field \"" + segment + "\"." : at + " is not an object; the path cannot continue past it.", available: Object.keys(properties) };
|
|
172
|
+
}
|
|
173
|
+
const child = properties[segment]!;
|
|
174
|
+
const topLevel = index === 0 && !("type" in root) ? table?.args[root.tool]?.[segment] : undefined;
|
|
175
|
+
expression = topLevel ?? inlineType(child);
|
|
176
|
+
field = {
|
|
177
|
+
type: expression,
|
|
178
|
+
...(Array.isArray(object.required) && object.required.includes(segment) ? { required: true as const } : {}),
|
|
179
|
+
...(typeof child.description === "string" ? { description: child.description } : {}),
|
|
180
|
+
};
|
|
181
|
+
inline = child;
|
|
182
|
+
}
|
|
183
|
+
typeName = table ? objectTypeOf(table, expression) : undefined;
|
|
184
|
+
if (!typeName && index < segments.length - 1 && !inlineObject(inline)) {
|
|
185
|
+
return { ok: false, message: label + " " + segments.slice(0, index + 1).join(".") + " is " + expression + ", not an object; the path cannot continue past it.", available: [] };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const lines = [label + " " + segments.join(".") + ": " + field!.type + (field!.required ? " (required)" : "")];
|
|
189
|
+
const description = prose(field!.description);
|
|
190
|
+
if (description) lines.push(description);
|
|
191
|
+
const named = namedTypesIn(table, expression);
|
|
192
|
+
if (named.length > 0 && table) {
|
|
193
|
+
for (const name of named) lines.push("", inputTypeText(table, name, lookup));
|
|
194
|
+
} else {
|
|
195
|
+
const object = inlineObject(inline);
|
|
196
|
+
if (object) {
|
|
197
|
+
const required = new Set(object.required ?? []);
|
|
198
|
+
lines.push("", "Fields:");
|
|
199
|
+
for (const [name, child] of Object.entries(object.properties ?? {})) {
|
|
200
|
+
lines.push(fieldLine(name, { type: inlineType(child), ...(required.has(name) ? { required: true } : {}), ...(child.description ? { description: child.description } : {}) }));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return { ok: true, text: lines.join("\n") };
|
|
205
|
+
}
|