@nestia/sdk 11.2.1 → 11.3.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/assets/bundle/distribute/package.json +1 -1
- package/lib/NestiaSdkApplication.js +17 -2
- package/lib/NestiaSdkApplication.js.map +1 -1
- package/lib/NestiaSwaggerComposer.js +2 -0
- package/lib/NestiaSwaggerComposer.js.map +1 -1
- package/lib/analyses/AccessorAnalyzer.d.ts +4 -1
- package/lib/analyses/AccessorAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectControllerAnalyzer.js +3 -2
- package/lib/analyses/ReflectControllerAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectMcpOperationAnalyzer.d.ts +20 -0
- package/lib/analyses/ReflectMcpOperationAnalyzer.js +84 -0
- package/lib/analyses/ReflectMcpOperationAnalyzer.js.map +1 -0
- package/lib/analyses/TypedMcpRouteAnalyzer.d.ts +15 -0
- package/lib/analyses/TypedMcpRouteAnalyzer.js +40 -0
- package/lib/analyses/TypedMcpRouteAnalyzer.js.map +1 -0
- package/lib/generates/SdkGenerator.js +48 -0
- package/lib/generates/SdkGenerator.js.map +1 -1
- package/lib/generates/internal/ImportDictionary.d.ts +1 -0
- package/lib/generates/internal/ImportDictionary.js +5 -4
- package/lib/generates/internal/ImportDictionary.js.map +1 -1
- package/lib/generates/internal/SdkDistributionComposer.d.ts +1 -0
- package/lib/generates/internal/SdkDistributionComposer.js +8 -1
- package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
- package/lib/generates/internal/SdkFileProgrammer.js +4 -1
- package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkMcpRouteProgrammer.d.ts +25 -0
- package/lib/generates/internal/SdkMcpRouteProgrammer.js +192 -0
- package/lib/generates/internal/SdkMcpRouteProgrammer.js.map +1 -0
- package/lib/generates/internal/SdkRouteDirectory.d.ts +2 -1
- package/lib/generates/internal/SdkRouteDirectory.js.map +1 -1
- package/lib/structures/IReflectController.d.ts +2 -1
- package/lib/structures/IReflectMcpOperation.d.ts +37 -0
- package/lib/structures/IReflectMcpOperation.js +3 -0
- package/lib/structures/IReflectMcpOperation.js.map +1 -0
- package/lib/structures/IReflectMcpOperationParameter.d.ts +21 -0
- package/lib/structures/IReflectMcpOperationParameter.js +3 -0
- package/lib/structures/IReflectMcpOperationParameter.js.map +1 -0
- package/lib/structures/ITypedApplication.d.ts +2 -1
- package/lib/structures/ITypedMcpRoute.d.ts +33 -0
- package/lib/structures/ITypedMcpRoute.js +3 -0
- package/lib/structures/ITypedMcpRoute.js.map +1 -0
- package/package.json +5 -4
- package/src/NestiaSdkApplication.ts +23 -3
- package/src/NestiaSwaggerComposer.ts +1 -0
- package/src/analyses/AccessorAnalyzer.ts +7 -10
- package/src/analyses/ReflectControllerAnalyzer.ts +8 -1
- package/src/analyses/ReflectMcpOperationAnalyzer.ts +124 -0
- package/src/analyses/TypedMcpRouteAnalyzer.ts +40 -0
- package/src/generates/SdkGenerator.ts +55 -0
- package/src/generates/internal/ImportDictionary.ts +10 -8
- package/src/generates/internal/SdkDistributionComposer.ts +6 -0
- package/src/generates/internal/SdkFileProgrammer.ts +6 -2
- package/src/generates/internal/SdkMcpRouteProgrammer.ts +469 -0
- package/src/generates/internal/SdkRouteDirectory.ts +4 -1
- package/src/structures/IReflectController.ts +4 -1
- package/src/structures/IReflectMcpOperation.ts +40 -0
- package/src/structures/IReflectMcpOperationParameter.ts +29 -0
- package/src/structures/ITypedApplication.ts +2 -1
- package/src/structures/ITypedMcpRoute.ts +35 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
import { INestiaProject } from "../../structures/INestiaProject";
|
|
4
|
+
import { ITypedMcpRoute } from "../../structures/ITypedMcpRoute";
|
|
5
|
+
import { FilePrinter } from "./FilePrinter";
|
|
6
|
+
import { ImportDictionary } from "./ImportDictionary";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Emits a typed client wrapper for an MCP tool.
|
|
10
|
+
*
|
|
11
|
+
* Output mirrors `SdkHttpRouteProgrammer` conventions: a top-level async
|
|
12
|
+
* function paired with a namespace that exposes `Input`, `Output`, and
|
|
13
|
+
* `METADATA`. Object output types are wrapped in `Primitive<T>` from typia
|
|
14
|
+
* because MCP round-trips values through JSON, matching the semantics of
|
|
15
|
+
* `Primitive`. Void MCP tools return `Promise<void>`.
|
|
16
|
+
*
|
|
17
|
+
* The response is typed via `CallToolResult` from `@modelcontextprotocol/sdk`
|
|
18
|
+
* and narrowed structurally (`isError === true`, `type === "text"`) rather than
|
|
19
|
+
* asserted with `as any`. The single unavoidable cast is on the `arguments`
|
|
20
|
+
* field of `client.callTool(...)`, which the MCP SDK types as `Record<string,
|
|
21
|
+
* unknown> | undefined`; a user interface without an index signature is not
|
|
22
|
+
* structurally assignable to that record.
|
|
23
|
+
*
|
|
24
|
+
* @author wildduck - https://github.com/wildduck2
|
|
25
|
+
*/
|
|
26
|
+
export namespace SdkMcpRouteProgrammer {
|
|
27
|
+
export const write =
|
|
28
|
+
(project: INestiaProject) =>
|
|
29
|
+
(importer: ImportDictionary) =>
|
|
30
|
+
(route: ITypedMcpRoute): ts.Statement[] => [
|
|
31
|
+
FilePrinter.description(
|
|
32
|
+
writeFunction(project)(importer)(route),
|
|
33
|
+
writeDescription(route),
|
|
34
|
+
),
|
|
35
|
+
writeNamespace(project)(importer)(route),
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/* ---------------------------------------------------------
|
|
39
|
+
FUNCTION
|
|
40
|
+
--------------------------------------------------------- */
|
|
41
|
+
const writeFunction =
|
|
42
|
+
(_project: INestiaProject) =>
|
|
43
|
+
(importer: ImportDictionary) =>
|
|
44
|
+
(route: ITypedMcpRoute): ts.FunctionDeclaration => {
|
|
45
|
+
const clientType = ts.factory.createTypeReferenceNode(
|
|
46
|
+
importer.external({
|
|
47
|
+
declaration: true,
|
|
48
|
+
file: "@modelcontextprotocol/sdk/client/index.js",
|
|
49
|
+
type: "element",
|
|
50
|
+
name: "Client",
|
|
51
|
+
alias: "McpClient",
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
const callToolResultTypeName = importer.external({
|
|
55
|
+
declaration: true,
|
|
56
|
+
file: "@modelcontextprotocol/sdk/types.js",
|
|
57
|
+
type: "element",
|
|
58
|
+
name: "CallToolResult",
|
|
59
|
+
alias: "McpCallToolResult",
|
|
60
|
+
});
|
|
61
|
+
const isVoid = isVoidReturn(route);
|
|
62
|
+
// Register Primitive import so Output type resolves.
|
|
63
|
+
if (!isVoid)
|
|
64
|
+
importer.external({
|
|
65
|
+
declaration: true,
|
|
66
|
+
file: "typia",
|
|
67
|
+
type: "element",
|
|
68
|
+
name: "Primitive",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const inputRef: ts.TypeNode = route.input
|
|
72
|
+
? ts.factory.createTypeReferenceNode(`${route.name}.Input`)
|
|
73
|
+
: ts.factory.createTypeLiteralNode([]);
|
|
74
|
+
const outputRef: ts.TypeNode = ts.factory.createTypeReferenceNode(
|
|
75
|
+
`${route.name}.Output`,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const params: ts.ParameterDeclaration[] = [
|
|
79
|
+
ts.factory.createParameterDeclaration(
|
|
80
|
+
undefined,
|
|
81
|
+
undefined,
|
|
82
|
+
"client",
|
|
83
|
+
undefined,
|
|
84
|
+
clientType,
|
|
85
|
+
),
|
|
86
|
+
];
|
|
87
|
+
if (route.input) {
|
|
88
|
+
params.push(
|
|
89
|
+
ts.factory.createParameterDeclaration(
|
|
90
|
+
undefined,
|
|
91
|
+
undefined,
|
|
92
|
+
"args",
|
|
93
|
+
undefined,
|
|
94
|
+
inputRef,
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const toolNameExpr = ts.factory.createPropertyAccessExpression(
|
|
100
|
+
ts.factory.createPropertyAccessExpression(
|
|
101
|
+
ts.factory.createIdentifier(route.name),
|
|
102
|
+
"METADATA",
|
|
103
|
+
),
|
|
104
|
+
"tool",
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const callToolParams: ts.ObjectLiteralElementLike[] = [
|
|
108
|
+
ts.factory.createPropertyAssignment("name", toolNameExpr),
|
|
109
|
+
];
|
|
110
|
+
if (route.input) {
|
|
111
|
+
callToolParams.push(
|
|
112
|
+
ts.factory.createPropertyAssignment(
|
|
113
|
+
"arguments",
|
|
114
|
+
ts.factory.createAsExpression(
|
|
115
|
+
ts.factory.createAsExpression(
|
|
116
|
+
ts.factory.createIdentifier("args"),
|
|
117
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
|
|
118
|
+
),
|
|
119
|
+
ts.factory.createTypeReferenceNode("Record", [
|
|
120
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
121
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
|
|
122
|
+
]),
|
|
123
|
+
),
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const body = ts.factory.createBlock(
|
|
129
|
+
[
|
|
130
|
+
// const raw = await client.callTool({ ... });
|
|
131
|
+
// NOTE: callTool returns `CallToolResult | CompatibilityCallToolResult`
|
|
132
|
+
// (the pre-2024-11-05 compat variant has `toolResult`, no
|
|
133
|
+
// `content`). TypeScript cannot narrow the union structurally
|
|
134
|
+
// because both variants carry an `[x: string]: unknown` index
|
|
135
|
+
// signature. Branch at runtime, then cast to CallToolResult
|
|
136
|
+
// once for the modern path.
|
|
137
|
+
ts.factory.createVariableStatement(
|
|
138
|
+
undefined,
|
|
139
|
+
ts.factory.createVariableDeclarationList(
|
|
140
|
+
[
|
|
141
|
+
ts.factory.createVariableDeclaration(
|
|
142
|
+
"raw",
|
|
143
|
+
undefined,
|
|
144
|
+
undefined,
|
|
145
|
+
ts.factory.createAwaitExpression(
|
|
146
|
+
ts.factory.createCallExpression(
|
|
147
|
+
ts.factory.createPropertyAccessExpression(
|
|
148
|
+
ts.factory.createIdentifier("client"),
|
|
149
|
+
"callTool",
|
|
150
|
+
),
|
|
151
|
+
undefined,
|
|
152
|
+
[
|
|
153
|
+
ts.factory.createObjectLiteralExpression(
|
|
154
|
+
callToolParams,
|
|
155
|
+
true,
|
|
156
|
+
),
|
|
157
|
+
],
|
|
158
|
+
),
|
|
159
|
+
),
|
|
160
|
+
),
|
|
161
|
+
],
|
|
162
|
+
ts.NodeFlags.Const,
|
|
163
|
+
),
|
|
164
|
+
),
|
|
165
|
+
// if ("toolResult" in raw) throw ...;
|
|
166
|
+
ts.factory.createIfStatement(
|
|
167
|
+
ts.factory.createBinaryExpression(
|
|
168
|
+
ts.factory.createStringLiteral("toolResult"),
|
|
169
|
+
ts.factory.createToken(ts.SyntaxKind.InKeyword),
|
|
170
|
+
ts.factory.createIdentifier("raw"),
|
|
171
|
+
),
|
|
172
|
+
ts.factory.createThrowStatement(
|
|
173
|
+
ts.factory.createNewExpression(
|
|
174
|
+
ts.factory.createIdentifier("Error"),
|
|
175
|
+
undefined,
|
|
176
|
+
[
|
|
177
|
+
ts.factory.createTemplateExpression(
|
|
178
|
+
ts.factory.createTemplateHead('MCP tool "'),
|
|
179
|
+
[
|
|
180
|
+
ts.factory.createTemplateSpan(
|
|
181
|
+
toolNameExpr,
|
|
182
|
+
ts.factory.createTemplateTail(
|
|
183
|
+
'" returned a legacy (pre-2024-11-05) compatibility result',
|
|
184
|
+
),
|
|
185
|
+
),
|
|
186
|
+
],
|
|
187
|
+
),
|
|
188
|
+
],
|
|
189
|
+
),
|
|
190
|
+
),
|
|
191
|
+
),
|
|
192
|
+
// const result: CallToolResult = raw;
|
|
193
|
+
// Safe: preceding branch rejected the compat variant.
|
|
194
|
+
ts.factory.createVariableStatement(
|
|
195
|
+
undefined,
|
|
196
|
+
ts.factory.createVariableDeclarationList(
|
|
197
|
+
[
|
|
198
|
+
ts.factory.createVariableDeclaration(
|
|
199
|
+
"result",
|
|
200
|
+
undefined,
|
|
201
|
+
ts.factory.createTypeReferenceNode(callToolResultTypeName),
|
|
202
|
+
ts.factory.createAsExpression(
|
|
203
|
+
ts.factory.createIdentifier("raw"),
|
|
204
|
+
ts.factory.createTypeReferenceNode(callToolResultTypeName),
|
|
205
|
+
),
|
|
206
|
+
),
|
|
207
|
+
],
|
|
208
|
+
ts.NodeFlags.Const,
|
|
209
|
+
),
|
|
210
|
+
),
|
|
211
|
+
// if (result.isError === true) throw ...;
|
|
212
|
+
ts.factory.createIfStatement(
|
|
213
|
+
ts.factory.createBinaryExpression(
|
|
214
|
+
ts.factory.createPropertyAccessExpression(
|
|
215
|
+
ts.factory.createIdentifier("result"),
|
|
216
|
+
"isError",
|
|
217
|
+
),
|
|
218
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
|
|
219
|
+
ts.factory.createTrue(),
|
|
220
|
+
),
|
|
221
|
+
ts.factory.createThrowStatement(
|
|
222
|
+
ts.factory.createNewExpression(
|
|
223
|
+
ts.factory.createIdentifier("Error"),
|
|
224
|
+
undefined,
|
|
225
|
+
[
|
|
226
|
+
ts.factory.createTemplateExpression(
|
|
227
|
+
ts.factory.createTemplateHead('MCP tool "'),
|
|
228
|
+
[
|
|
229
|
+
ts.factory.createTemplateSpan(
|
|
230
|
+
toolNameExpr,
|
|
231
|
+
ts.factory.createTemplateTail('" returned isError'),
|
|
232
|
+
),
|
|
233
|
+
],
|
|
234
|
+
),
|
|
235
|
+
],
|
|
236
|
+
),
|
|
237
|
+
),
|
|
238
|
+
),
|
|
239
|
+
...(isVoid
|
|
240
|
+
? [ts.factory.createReturnStatement()]
|
|
241
|
+
: [
|
|
242
|
+
// const first = result.content[0];
|
|
243
|
+
ts.factory.createVariableStatement(
|
|
244
|
+
undefined,
|
|
245
|
+
ts.factory.createVariableDeclarationList(
|
|
246
|
+
[
|
|
247
|
+
ts.factory.createVariableDeclaration(
|
|
248
|
+
"first",
|
|
249
|
+
undefined,
|
|
250
|
+
undefined,
|
|
251
|
+
ts.factory.createElementAccessExpression(
|
|
252
|
+
ts.factory.createPropertyAccessExpression(
|
|
253
|
+
ts.factory.createIdentifier("result"),
|
|
254
|
+
"content",
|
|
255
|
+
),
|
|
256
|
+
ts.factory.createNumericLiteral(0),
|
|
257
|
+
),
|
|
258
|
+
),
|
|
259
|
+
],
|
|
260
|
+
ts.NodeFlags.Const,
|
|
261
|
+
),
|
|
262
|
+
),
|
|
263
|
+
ts.factory.createIfStatement(
|
|
264
|
+
ts.factory.createBinaryExpression(
|
|
265
|
+
ts.factory.createBinaryExpression(
|
|
266
|
+
ts.factory.createIdentifier("first"),
|
|
267
|
+
ts.factory.createToken(
|
|
268
|
+
ts.SyntaxKind.EqualsEqualsEqualsToken,
|
|
269
|
+
),
|
|
270
|
+
ts.factory.createIdentifier("undefined"),
|
|
271
|
+
),
|
|
272
|
+
ts.factory.createToken(ts.SyntaxKind.BarBarToken),
|
|
273
|
+
ts.factory.createBinaryExpression(
|
|
274
|
+
ts.factory.createPropertyAccessExpression(
|
|
275
|
+
ts.factory.createIdentifier("first"),
|
|
276
|
+
"type",
|
|
277
|
+
),
|
|
278
|
+
ts.factory.createToken(
|
|
279
|
+
ts.SyntaxKind.ExclamationEqualsEqualsToken,
|
|
280
|
+
),
|
|
281
|
+
ts.factory.createStringLiteral("text"),
|
|
282
|
+
),
|
|
283
|
+
),
|
|
284
|
+
ts.factory.createThrowStatement(
|
|
285
|
+
ts.factory.createNewExpression(
|
|
286
|
+
ts.factory.createIdentifier("Error"),
|
|
287
|
+
undefined,
|
|
288
|
+
[
|
|
289
|
+
ts.factory.createTemplateExpression(
|
|
290
|
+
ts.factory.createTemplateHead('MCP tool "'),
|
|
291
|
+
[
|
|
292
|
+
ts.factory.createTemplateSpan(
|
|
293
|
+
toolNameExpr,
|
|
294
|
+
ts.factory.createTemplateTail(
|
|
295
|
+
'" returned no text content',
|
|
296
|
+
),
|
|
297
|
+
),
|
|
298
|
+
],
|
|
299
|
+
),
|
|
300
|
+
],
|
|
301
|
+
),
|
|
302
|
+
),
|
|
303
|
+
),
|
|
304
|
+
ts.factory.createReturnStatement(
|
|
305
|
+
ts.factory.createAsExpression(
|
|
306
|
+
ts.factory.createCallExpression(
|
|
307
|
+
ts.factory.createPropertyAccessExpression(
|
|
308
|
+
ts.factory.createIdentifier("JSON"),
|
|
309
|
+
"parse",
|
|
310
|
+
),
|
|
311
|
+
undefined,
|
|
312
|
+
[
|
|
313
|
+
ts.factory.createPropertyAccessExpression(
|
|
314
|
+
ts.factory.createIdentifier("first"),
|
|
315
|
+
"text",
|
|
316
|
+
),
|
|
317
|
+
],
|
|
318
|
+
),
|
|
319
|
+
outputRef,
|
|
320
|
+
),
|
|
321
|
+
),
|
|
322
|
+
]),
|
|
323
|
+
],
|
|
324
|
+
true,
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
return ts.factory.createFunctionDeclaration(
|
|
328
|
+
[
|
|
329
|
+
ts.factory.createModifier(ts.SyntaxKind.ExportKeyword),
|
|
330
|
+
ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword),
|
|
331
|
+
],
|
|
332
|
+
undefined,
|
|
333
|
+
route.name,
|
|
334
|
+
undefined,
|
|
335
|
+
params,
|
|
336
|
+
ts.factory.createTypeReferenceNode("Promise", [outputRef]),
|
|
337
|
+
body,
|
|
338
|
+
);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
/* ---------------------------------------------------------
|
|
342
|
+
NAMESPACE
|
|
343
|
+
--------------------------------------------------------- */
|
|
344
|
+
const writeNamespace =
|
|
345
|
+
(_project: INestiaProject) =>
|
|
346
|
+
(importer: ImportDictionary) =>
|
|
347
|
+
(route: ITypedMcpRoute): ts.ModuleDeclaration => {
|
|
348
|
+
const statements: ts.Statement[] = [];
|
|
349
|
+
|
|
350
|
+
// Input type alias
|
|
351
|
+
if (route.input) {
|
|
352
|
+
statements.push(
|
|
353
|
+
ts.factory.createTypeAliasDeclaration(
|
|
354
|
+
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
|
355
|
+
"Input",
|
|
356
|
+
undefined,
|
|
357
|
+
ts.factory.createTypeReferenceNode(route.input.type.name),
|
|
358
|
+
),
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Output type alias
|
|
363
|
+
const isVoid = isVoidReturn(route);
|
|
364
|
+
const outputType: ts.TypeNode =
|
|
365
|
+
!isVoid && route.returnType !== null
|
|
366
|
+
? ts.factory.createTypeReferenceNode(
|
|
367
|
+
importer.external({
|
|
368
|
+
declaration: true,
|
|
369
|
+
file: "typia",
|
|
370
|
+
type: "element",
|
|
371
|
+
name: "Primitive",
|
|
372
|
+
}),
|
|
373
|
+
[
|
|
374
|
+
ts.factory.createTypeReferenceNode(
|
|
375
|
+
unwrapPromise(route.returnType.name),
|
|
376
|
+
),
|
|
377
|
+
],
|
|
378
|
+
)
|
|
379
|
+
: ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
|
|
380
|
+
statements.push(
|
|
381
|
+
ts.factory.createTypeAliasDeclaration(
|
|
382
|
+
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
|
383
|
+
"Output",
|
|
384
|
+
undefined,
|
|
385
|
+
outputType,
|
|
386
|
+
),
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
// METADATA const
|
|
390
|
+
const metadataProps: ts.ObjectLiteralElementLike[] = [
|
|
391
|
+
ts.factory.createPropertyAssignment(
|
|
392
|
+
"protocol",
|
|
393
|
+
ts.factory.createStringLiteral("mcp"),
|
|
394
|
+
),
|
|
395
|
+
ts.factory.createPropertyAssignment(
|
|
396
|
+
"tool",
|
|
397
|
+
ts.factory.createStringLiteral(route.toolName),
|
|
398
|
+
),
|
|
399
|
+
ts.factory.createPropertyAssignment(
|
|
400
|
+
"title",
|
|
401
|
+
route.title === null
|
|
402
|
+
? ts.factory.createNull()
|
|
403
|
+
: ts.factory.createStringLiteral(route.title),
|
|
404
|
+
),
|
|
405
|
+
ts.factory.createPropertyAssignment(
|
|
406
|
+
"description",
|
|
407
|
+
route.toolDescription === null
|
|
408
|
+
? ts.factory.createNull()
|
|
409
|
+
: ts.factory.createStringLiteral(route.toolDescription),
|
|
410
|
+
),
|
|
411
|
+
];
|
|
412
|
+
statements.push(
|
|
413
|
+
ts.factory.createVariableStatement(
|
|
414
|
+
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
|
415
|
+
ts.factory.createVariableDeclarationList(
|
|
416
|
+
[
|
|
417
|
+
ts.factory.createVariableDeclaration(
|
|
418
|
+
"METADATA",
|
|
419
|
+
undefined,
|
|
420
|
+
undefined,
|
|
421
|
+
ts.factory.createAsExpression(
|
|
422
|
+
ts.factory.createObjectLiteralExpression(metadataProps, true),
|
|
423
|
+
ts.factory.createTypeReferenceNode("const"),
|
|
424
|
+
),
|
|
425
|
+
),
|
|
426
|
+
],
|
|
427
|
+
ts.NodeFlags.Const,
|
|
428
|
+
),
|
|
429
|
+
),
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
return ts.factory.createModuleDeclaration(
|
|
433
|
+
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
|
434
|
+
ts.factory.createIdentifier(route.name),
|
|
435
|
+
ts.factory.createModuleBlock(statements),
|
|
436
|
+
ts.NodeFlags.Namespace,
|
|
437
|
+
);
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
/* ---------------------------------------------------------
|
|
441
|
+
DESCRIPTION
|
|
442
|
+
--------------------------------------------------------- */
|
|
443
|
+
const writeDescription = (route: ITypedMcpRoute): string => {
|
|
444
|
+
const lines: string[] = [];
|
|
445
|
+
if (route.toolDescription) lines.push(...route.toolDescription.split("\n"));
|
|
446
|
+
else if (route.description) lines.push(...route.description.split("\n"));
|
|
447
|
+
if (lines.length) lines.push("");
|
|
448
|
+
lines.push(
|
|
449
|
+
`@controller ${route.controller.class.name}.${route.function.name || route.name}`,
|
|
450
|
+
`@tool ${route.toolName}`,
|
|
451
|
+
`@accessor ${["api", "functional", ...route.accessor].join(".")}`,
|
|
452
|
+
`@protocol mcp`,
|
|
453
|
+
`@nestia Generated by Nestia - https://github.com/samchon/nestia`,
|
|
454
|
+
);
|
|
455
|
+
return lines.join("\n");
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const unwrapPromise = (name: string): string => {
|
|
459
|
+
const m = /^Promise<(.+)>$/.exec(name);
|
|
460
|
+
return m ? m[1]! : name;
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
const isVoidReturn = (route: ITypedMcpRoute): boolean =>
|
|
464
|
+
route.returnType === null ||
|
|
465
|
+
isVoidName(unwrapPromise(route.returnType.name));
|
|
466
|
+
|
|
467
|
+
const isVoidName = (name: string): boolean =>
|
|
468
|
+
name === "void" || name === "undefined";
|
|
469
|
+
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute";
|
|
2
|
+
import { ITypedMcpRoute } from "../../structures/ITypedMcpRoute";
|
|
2
3
|
import { ITypedWebSocketRoute } from "../../structures/ITypedWebSocketRoute";
|
|
3
4
|
|
|
4
5
|
export class SdkRouteDirectory {
|
|
5
6
|
public readonly module: string;
|
|
6
7
|
public readonly children: Map<string, SdkRouteDirectory>;
|
|
7
|
-
public readonly routes: Array<
|
|
8
|
+
public readonly routes: Array<
|
|
9
|
+
ITypedHttpRoute | ITypedWebSocketRoute | ITypedMcpRoute
|
|
10
|
+
>;
|
|
8
11
|
|
|
9
12
|
public constructor(
|
|
10
13
|
readonly parent: SdkRouteDirectory | null,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { VERSION_NEUTRAL } from "@nestjs/common/interfaces";
|
|
2
2
|
|
|
3
3
|
import { IReflectHttpOperation } from "./IReflectHttpOperation";
|
|
4
|
+
import { IReflectMcpOperation } from "./IReflectMcpOperation";
|
|
4
5
|
import { IReflectWebSocketOperation } from "./IReflectWebSocketOperation";
|
|
5
6
|
|
|
6
7
|
export interface IReflectController {
|
|
@@ -9,7 +10,9 @@ export interface IReflectController {
|
|
|
9
10
|
paths: string[];
|
|
10
11
|
file: string;
|
|
11
12
|
versions: Array<string | typeof VERSION_NEUTRAL> | undefined;
|
|
12
|
-
operations: Array<
|
|
13
|
+
operations: Array<
|
|
14
|
+
IReflectHttpOperation | IReflectWebSocketOperation | IReflectMcpOperation
|
|
15
|
+
>;
|
|
13
16
|
security: Record<string, string[]>[];
|
|
14
17
|
tags: string[];
|
|
15
18
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
import { IReflectImport } from "./IReflectImport";
|
|
4
|
+
import { IReflectMcpOperationParameter } from "./IReflectMcpOperationParameter";
|
|
5
|
+
import { IReflectType } from "./IReflectType";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Reflected operation metadata for a method decorated with `@McpRoute`.
|
|
9
|
+
*
|
|
10
|
+
* Produced by {@link ReflectMcpOperationAnalyzer.analyze}; consumed by
|
|
11
|
+
* {@link TypedMcpRouteAnalyzer.analyze} to produce the final
|
|
12
|
+
* {@link ITypedMcpRoute}.
|
|
13
|
+
*
|
|
14
|
+
* @author wildduck - https://github.com/wildduck2
|
|
15
|
+
*/
|
|
16
|
+
export interface IReflectMcpOperation {
|
|
17
|
+
protocol: "mcp";
|
|
18
|
+
name: string;
|
|
19
|
+
toolName: string;
|
|
20
|
+
title: string | null;
|
|
21
|
+
toolDescription: string | null;
|
|
22
|
+
inputSchema: object;
|
|
23
|
+
outputSchema: object | null;
|
|
24
|
+
annotations: IReflectMcpOperation.IAnnotations | null;
|
|
25
|
+
function: Function;
|
|
26
|
+
parameters: IReflectMcpOperationParameter[];
|
|
27
|
+
returnType: IReflectType | null;
|
|
28
|
+
imports: IReflectImport[];
|
|
29
|
+
description: string | null;
|
|
30
|
+
jsDocTags: ts.JSDocTagInfo[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export namespace IReflectMcpOperation {
|
|
34
|
+
export interface IAnnotations {
|
|
35
|
+
readOnlyHint?: boolean;
|
|
36
|
+
destructiveHint?: boolean;
|
|
37
|
+
idempotentHint?: boolean;
|
|
38
|
+
openWorldHint?: boolean;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { IJsDocTagInfo } from "typia";
|
|
2
|
+
|
|
3
|
+
import { IReflectImport } from "./IReflectImport";
|
|
4
|
+
import { IReflectType } from "./IReflectType";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parameter descriptor for an MCP tool method. Reflected from
|
|
8
|
+
* `"nestia/McpRoute/Parameters"` metadata plus compile-time type information
|
|
9
|
+
* collected by the nestia transformer.
|
|
10
|
+
*
|
|
11
|
+
* @author wildduck - https://github.com/wildduck2
|
|
12
|
+
*/
|
|
13
|
+
export interface IReflectMcpOperationParameter {
|
|
14
|
+
category: "params";
|
|
15
|
+
name: string;
|
|
16
|
+
index: number;
|
|
17
|
+
type: IReflectType;
|
|
18
|
+
imports: IReflectImport[];
|
|
19
|
+
description: string | null;
|
|
20
|
+
jsDocTags: IJsDocTagInfo[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export namespace IReflectMcpOperationParameter {
|
|
24
|
+
/** @internal */
|
|
25
|
+
export interface IPreconfigured {
|
|
26
|
+
category: "params";
|
|
27
|
+
index: number;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -2,10 +2,11 @@ import { IMetadataDictionary } from "@typia/core";
|
|
|
2
2
|
|
|
3
3
|
import { INestiaProject } from "./INestiaProject";
|
|
4
4
|
import { ITypedHttpRoute } from "./ITypedHttpRoute";
|
|
5
|
+
import { ITypedMcpRoute } from "./ITypedMcpRoute";
|
|
5
6
|
import { ITypedWebSocketRoute } from "./ITypedWebSocketRoute";
|
|
6
7
|
|
|
7
8
|
export interface ITypedApplication {
|
|
8
9
|
project: INestiaProject;
|
|
9
10
|
collection: IMetadataDictionary;
|
|
10
|
-
routes: Array<ITypedHttpRoute | ITypedWebSocketRoute>;
|
|
11
|
+
routes: Array<ITypedHttpRoute | ITypedWebSocketRoute | ITypedMcpRoute>;
|
|
11
12
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
import { IReflectController } from "./IReflectController";
|
|
4
|
+
import { IReflectImport } from "./IReflectImport";
|
|
5
|
+
import { IReflectMcpOperation } from "./IReflectMcpOperation";
|
|
6
|
+
import { IReflectMcpOperationParameter } from "./IReflectMcpOperationParameter";
|
|
7
|
+
import { IReflectType } from "./IReflectType";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Final typed representation of an MCP tool route.
|
|
11
|
+
*
|
|
12
|
+
* Carries everything the SDK generator needs to emit a typed client function:
|
|
13
|
+
* the controller reference, accessor path, typia-derived input schema, and
|
|
14
|
+
* input/output type info.
|
|
15
|
+
*
|
|
16
|
+
* @author wildduck - https://github.com/wildduck2
|
|
17
|
+
*/
|
|
18
|
+
export interface ITypedMcpRoute {
|
|
19
|
+
protocol: "mcp";
|
|
20
|
+
controller: IReflectController;
|
|
21
|
+
name: string;
|
|
22
|
+
toolName: string;
|
|
23
|
+
title: string | null;
|
|
24
|
+
toolDescription: string | null;
|
|
25
|
+
accessor: string[];
|
|
26
|
+
function: Function;
|
|
27
|
+
input: IReflectMcpOperationParameter | null;
|
|
28
|
+
returnType: IReflectType | null;
|
|
29
|
+
inputSchema: object;
|
|
30
|
+
outputSchema: object | null;
|
|
31
|
+
annotations: IReflectMcpOperation.IAnnotations | null;
|
|
32
|
+
imports: IReflectImport[];
|
|
33
|
+
description: string | null;
|
|
34
|
+
jsDocTags: ts.JSDocTagInfo[];
|
|
35
|
+
}
|