@nestia/sdk 11.2.0 → 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/executable/internal/NestiaConfigLoader.js +3 -2
- package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
- 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/executable/internal/NestiaConfigLoader.ts +2 -1
- 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
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import { NamingConvention } from "@typia/utils";
|
|
2
2
|
|
|
3
3
|
import { ITypedHttpRoute } from "../structures/ITypedHttpRoute";
|
|
4
|
+
import { ITypedMcpRoute } from "../structures/ITypedMcpRoute";
|
|
4
5
|
import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute";
|
|
5
6
|
|
|
7
|
+
type AnyRoute = ITypedHttpRoute | ITypedWebSocketRoute | ITypedMcpRoute;
|
|
8
|
+
|
|
6
9
|
export namespace AccessorAnalyzer {
|
|
7
|
-
export const analyze = (
|
|
8
|
-
routes: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
|
|
9
|
-
) => {
|
|
10
|
+
export const analyze = (routes: Array<AnyRoute>) => {
|
|
10
11
|
shrink(routes);
|
|
11
12
|
variable(routes);
|
|
12
13
|
shrink(routes);
|
|
13
14
|
for (const r of routes) r.name = r.accessor.at(-1) ?? r.name;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
|
-
const prepare = (
|
|
17
|
-
routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
|
|
18
|
-
): Map<string, number> => {
|
|
17
|
+
const prepare = (routeList: Array<AnyRoute>): Map<string, number> => {
|
|
19
18
|
const dict: Map<string, number> = new Map();
|
|
20
19
|
for (const route of routeList)
|
|
21
20
|
route.accessor.forEach((_a, i) => {
|
|
@@ -25,9 +24,7 @@ export namespace AccessorAnalyzer {
|
|
|
25
24
|
return dict;
|
|
26
25
|
};
|
|
27
26
|
|
|
28
|
-
const variable = (
|
|
29
|
-
routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
|
|
30
|
-
) => {
|
|
27
|
+
const variable = (routeList: Array<AnyRoute>) => {
|
|
31
28
|
const dict: Map<string, number> = prepare(routeList);
|
|
32
29
|
for (const route of routeList) {
|
|
33
30
|
const emended: string[] = route.accessor.slice();
|
|
@@ -49,7 +46,7 @@ export namespace AccessorAnalyzer {
|
|
|
49
46
|
}
|
|
50
47
|
};
|
|
51
48
|
|
|
52
|
-
const shrink = (routeList: Array<
|
|
49
|
+
const shrink = (routeList: Array<AnyRoute>) => {
|
|
53
50
|
const dict: Map<string, number> = prepare(routeList);
|
|
54
51
|
for (const route of routeList) {
|
|
55
52
|
if (
|
|
@@ -8,10 +8,12 @@ import { INestiaProject } from "../structures/INestiaProject";
|
|
|
8
8
|
import { INestiaSdkInput } from "../structures/INestiaSdkInput";
|
|
9
9
|
import { IReflectController } from "../structures/IReflectController";
|
|
10
10
|
import { IReflectHttpOperation } from "../structures/IReflectHttpOperation";
|
|
11
|
+
import { IReflectMcpOperation } from "../structures/IReflectMcpOperation";
|
|
11
12
|
import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOperation";
|
|
12
13
|
import { IOperationMetadata } from "../transformers/IOperationMetadata";
|
|
13
14
|
import { ArrayUtil } from "../utils/ArrayUtil";
|
|
14
15
|
import { ReflectHttpOperationAnalyzer } from "./ReflectHttpOperationAnalyzer";
|
|
16
|
+
import { ReflectMcpOperationAnalyzer } from "./ReflectMcpOperationAnalyzer";
|
|
15
17
|
import { ReflectMetadataAnalyzer } from "./ReflectMetadataAnalyzer";
|
|
16
18
|
import { ReflectWebSocketOperationAnalyzer } from "./ReflectWebSocketOperationAnalyzer";
|
|
17
19
|
|
|
@@ -81,7 +83,12 @@ export namespace ReflectControllerAnalyzer {
|
|
|
81
83
|
function: value,
|
|
82
84
|
metadata,
|
|
83
85
|
};
|
|
84
|
-
const child:
|
|
86
|
+
const child:
|
|
87
|
+
| IReflectHttpOperation
|
|
88
|
+
| IReflectWebSocketOperation
|
|
89
|
+
| IReflectMcpOperation
|
|
90
|
+
| null =
|
|
91
|
+
ReflectMcpOperationAnalyzer.analyze(next) ??
|
|
85
92
|
ReflectWebSocketOperationAnalyzer.analyze(next) ??
|
|
86
93
|
ReflectHttpOperationAnalyzer.analyze(next);
|
|
87
94
|
if (child !== null) controller.operations.push(child);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { INestiaProject } from "../structures/INestiaProject";
|
|
2
|
+
import { IReflectController } from "../structures/IReflectController";
|
|
3
|
+
import { IReflectImport } from "../structures/IReflectImport";
|
|
4
|
+
import { IReflectMcpOperation } from "../structures/IReflectMcpOperation";
|
|
5
|
+
import { IReflectMcpOperationParameter } from "../structures/IReflectMcpOperationParameter";
|
|
6
|
+
import { IOperationMetadata } from "../transformers/IOperationMetadata";
|
|
7
|
+
import { ImportAnalyzer } from "./ImportAnalyzer";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Reflects controller methods carrying `"nestia/McpRoute"` metadata into
|
|
11
|
+
* {@link IReflectMcpOperation} structures consumed by the SDK generator.
|
|
12
|
+
*
|
|
13
|
+
* @author wildduck - https://github.com/wildduck2
|
|
14
|
+
*/
|
|
15
|
+
export namespace ReflectMcpOperationAnalyzer {
|
|
16
|
+
export interface IProps {
|
|
17
|
+
project: Omit<INestiaProject, "config">;
|
|
18
|
+
controller: IReflectController;
|
|
19
|
+
function: Function;
|
|
20
|
+
name: string;
|
|
21
|
+
metadata: IOperationMetadata;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const analyze = (ctx: IProps): IReflectMcpOperation | null => {
|
|
25
|
+
const route:
|
|
26
|
+
| {
|
|
27
|
+
name: string;
|
|
28
|
+
title?: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
inputSchema: object;
|
|
31
|
+
outputSchema?: object;
|
|
32
|
+
annotations?: IReflectMcpOperation.IAnnotations;
|
|
33
|
+
}
|
|
34
|
+
| undefined = Reflect.getMetadata("nestia/McpRoute", ctx.function);
|
|
35
|
+
if (route === undefined) return null;
|
|
36
|
+
|
|
37
|
+
const errors: string[] = [];
|
|
38
|
+
|
|
39
|
+
const preconfigured: IReflectMcpOperationParameter.IPreconfigured[] = (
|
|
40
|
+
(Reflect.getMetadata(
|
|
41
|
+
"nestia/McpRoute/Parameters",
|
|
42
|
+
ctx.controller.class.prototype,
|
|
43
|
+
ctx.name,
|
|
44
|
+
) ?? []) as IReflectMcpOperationParameter.IPreconfigured[]
|
|
45
|
+
).sort((a, b) => a.index - b.index);
|
|
46
|
+
|
|
47
|
+
if (preconfigured.length !== 1)
|
|
48
|
+
errors.push(
|
|
49
|
+
"@McpRoute tools must declare exactly one @McpRoute.Params() parameter.",
|
|
50
|
+
);
|
|
51
|
+
if (ctx.metadata.parameters.length !== 1)
|
|
52
|
+
errors.push(
|
|
53
|
+
"@McpRoute tools must have exactly one parameter (the MCP arguments object).",
|
|
54
|
+
);
|
|
55
|
+
else if (preconfigured[0]?.index !== 0)
|
|
56
|
+
errors.push(
|
|
57
|
+
"@McpRoute tools must decorate their only parameter with @McpRoute.Params().",
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const imports: IReflectImport[] = [];
|
|
61
|
+
const parameters: IReflectMcpOperationParameter[] = preconfigured
|
|
62
|
+
.map((p) => {
|
|
63
|
+
const matched: IOperationMetadata.IParameter | undefined =
|
|
64
|
+
ctx.metadata.parameters.find((m) => p.index === m.index);
|
|
65
|
+
if (matched === undefined) {
|
|
66
|
+
errors.push(
|
|
67
|
+
`Unable to find parameter type of the ${p.index} (th) argument.`,
|
|
68
|
+
);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (matched.type === null) {
|
|
72
|
+
errors.push(
|
|
73
|
+
`Failed to analyze the parameter type of ${JSON.stringify(matched.name)}.`,
|
|
74
|
+
);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
imports.push(...matched.imports);
|
|
78
|
+
return {
|
|
79
|
+
category: "params" as const,
|
|
80
|
+
name: matched.name,
|
|
81
|
+
index: p.index,
|
|
82
|
+
type: matched.type,
|
|
83
|
+
imports: matched.imports,
|
|
84
|
+
description: matched.description,
|
|
85
|
+
jsDocTags: matched.jsDocTags,
|
|
86
|
+
};
|
|
87
|
+
})
|
|
88
|
+
.filter((p): p is IReflectMcpOperationParameter => !!p);
|
|
89
|
+
|
|
90
|
+
if (ctx.metadata.success?.imports?.length)
|
|
91
|
+
imports.push(...ctx.metadata.success.imports);
|
|
92
|
+
|
|
93
|
+
if (errors.length) {
|
|
94
|
+
ctx.project.errors.push({
|
|
95
|
+
file: ctx.controller.file,
|
|
96
|
+
class: ctx.controller.class.name,
|
|
97
|
+
function: ctx.function.name,
|
|
98
|
+
from: ctx.name,
|
|
99
|
+
contents: errors,
|
|
100
|
+
});
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
// Prefer the decorator's explicit config. Fall back to the JSDoc-derived
|
|
104
|
+
// description captured by SdkOperationTransformer, so users who only
|
|
105
|
+
// document their method with a JSDoc comment still get it surfaced on
|
|
106
|
+
// the `tools/list` wire + the generated SDK metadata.
|
|
107
|
+
return {
|
|
108
|
+
protocol: "mcp",
|
|
109
|
+
name: ctx.name,
|
|
110
|
+
toolName: route.name,
|
|
111
|
+
title: route.title ?? null,
|
|
112
|
+
toolDescription: route.description ?? ctx.metadata.description ?? null,
|
|
113
|
+
inputSchema: route.inputSchema,
|
|
114
|
+
outputSchema: route.outputSchema ?? null,
|
|
115
|
+
annotations: route.annotations ?? null,
|
|
116
|
+
function: ctx.function,
|
|
117
|
+
parameters,
|
|
118
|
+
returnType: ctx.metadata.success?.type ?? null,
|
|
119
|
+
imports: ImportAnalyzer.merge(imports),
|
|
120
|
+
description: ctx.metadata.description ?? null,
|
|
121
|
+
jsDocTags: ctx.metadata.jsDocTags,
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { IReflectController } from "../structures/IReflectController";
|
|
2
|
+
import { IReflectMcpOperation } from "../structures/IReflectMcpOperation";
|
|
3
|
+
import { ITypedMcpRoute } from "../structures/ITypedMcpRoute";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds {@link ITypedMcpRoute} entries from a reflected MCP operation,
|
|
7
|
+
* assigning a stable `["mcp", toolName]` accessor for SDK file emission.
|
|
8
|
+
*
|
|
9
|
+
* @author wildduck - https://github.com/wildduck2
|
|
10
|
+
*/
|
|
11
|
+
export namespace TypedMcpRouteAnalyzer {
|
|
12
|
+
export const analyze = (props: {
|
|
13
|
+
controller: IReflectController;
|
|
14
|
+
operation: IReflectMcpOperation;
|
|
15
|
+
}): ITypedMcpRoute[] => [
|
|
16
|
+
{
|
|
17
|
+
protocol: "mcp",
|
|
18
|
+
controller: props.controller,
|
|
19
|
+
name: props.operation.name,
|
|
20
|
+
toolName: props.operation.toolName,
|
|
21
|
+
title: props.operation.title,
|
|
22
|
+
toolDescription: props.operation.toolDescription,
|
|
23
|
+
accessor: accessor(props.operation.toolName),
|
|
24
|
+
function: props.operation.function,
|
|
25
|
+
input: props.operation.parameters[0] ?? null,
|
|
26
|
+
returnType: props.operation.returnType,
|
|
27
|
+
inputSchema: props.operation.inputSchema,
|
|
28
|
+
outputSchema: props.operation.outputSchema,
|
|
29
|
+
annotations: props.operation.annotations,
|
|
30
|
+
imports: props.operation.imports,
|
|
31
|
+
description: props.operation.description,
|
|
32
|
+
jsDocTags: props.operation.jsDocTags,
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const accessor = (toolName: string): string[] => {
|
|
37
|
+
const safe = toolName.replace(/[^A-Za-z0-9_$]/g, "_");
|
|
38
|
+
return ["mcp", safe];
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -47,6 +47,7 @@ export namespace NestiaConfigLoader {
|
|
|
47
47
|
...(compilerOptions.plugins ?? []),
|
|
48
48
|
...(setup ? [] : [{ transform: "@nestia/sdk/lib/transform" }]),
|
|
49
49
|
];
|
|
50
|
+
const hasPathAliases = Object.keys(compilerOptions.paths ?? {}).length > 0;
|
|
50
51
|
if (!(process as any)[Symbol.for("ts-node.register.instance")])
|
|
51
52
|
register({
|
|
52
53
|
emit: false,
|
|
@@ -54,7 +55,7 @@ export namespace NestiaConfigLoader {
|
|
|
54
55
|
...compilerOptions,
|
|
55
56
|
plugins,
|
|
56
57
|
},
|
|
57
|
-
require: compilerOptions.baseUrl
|
|
58
|
+
require: (compilerOptions.baseUrl || hasPathAliases)
|
|
58
59
|
? ["tsconfig-paths/register"]
|
|
59
60
|
: undefined,
|
|
60
61
|
});
|
|
@@ -7,6 +7,7 @@ import { IReflectOperationError } from "../structures/IReflectOperationError";
|
|
|
7
7
|
import { IReflectType } from "../structures/IReflectType";
|
|
8
8
|
import { ITypedApplication } from "../structures/ITypedApplication";
|
|
9
9
|
import { ITypedHttpRoute } from "../structures/ITypedHttpRoute";
|
|
10
|
+
import { ITypedMcpRoute } from "../structures/ITypedMcpRoute";
|
|
10
11
|
import { CloneGenerator } from "./CloneGenerator";
|
|
11
12
|
import { SdkDistributionComposer } from "./internal/SdkDistributionComposer";
|
|
12
13
|
import { SdkFileProgrammer } from "./internal/SdkFileProgrammer";
|
|
@@ -55,6 +56,7 @@ export namespace SdkGenerator {
|
|
|
55
56
|
if (app.project.config.distribute !== undefined)
|
|
56
57
|
await SdkDistributionComposer.compose({
|
|
57
58
|
config: app.project.config,
|
|
59
|
+
mcp: app.routes.some((r) => r.protocol === "mcp"),
|
|
58
60
|
websocket: app.routes.some((r) => r.protocol === "websocket"),
|
|
59
61
|
});
|
|
60
62
|
};
|
|
@@ -63,6 +65,8 @@ export namespace SdkGenerator {
|
|
|
63
65
|
app: ITypedApplication,
|
|
64
66
|
): IReflectOperationError[] => {
|
|
65
67
|
const errors: IReflectOperationError[] = [];
|
|
68
|
+
validateMcpDuplicates(errors)(app.routes);
|
|
69
|
+
validateMcpAccessors(errors)(app.routes);
|
|
66
70
|
if (app.project.config.clone === true) return errors;
|
|
67
71
|
for (const route of app.routes)
|
|
68
72
|
if (route.protocol === "http")
|
|
@@ -74,6 +78,57 @@ export namespace SdkGenerator {
|
|
|
74
78
|
return errors;
|
|
75
79
|
};
|
|
76
80
|
|
|
81
|
+
const validateMcpDuplicates =
|
|
82
|
+
(errors: IReflectOperationError[]) =>
|
|
83
|
+
(routes: ITypedApplication["routes"]): void => {
|
|
84
|
+
const dict: Map<string, ITypedMcpRoute[]> = new Map();
|
|
85
|
+
for (const route of routes)
|
|
86
|
+
if (route.protocol === "mcp") {
|
|
87
|
+
const array = dict.get(route.toolName) ?? [];
|
|
88
|
+
array.push(route);
|
|
89
|
+
dict.set(route.toolName, array);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const [toolName, list] of dict)
|
|
93
|
+
if (list.length > 1)
|
|
94
|
+
for (const route of list)
|
|
95
|
+
errors.push({
|
|
96
|
+
file: route.controller.file,
|
|
97
|
+
class: route.controller.class.name,
|
|
98
|
+
function: route.function.name || route.name,
|
|
99
|
+
from: `@McpRoute(${JSON.stringify(toolName)})`,
|
|
100
|
+
contents: [
|
|
101
|
+
`Duplicate MCP tool name ${JSON.stringify(toolName)} is not allowed.`,
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const validateMcpAccessors =
|
|
107
|
+
(errors: IReflectOperationError[]) =>
|
|
108
|
+
(routes: ITypedApplication["routes"]): void => {
|
|
109
|
+
const dict: Map<string, ITypedMcpRoute[]> = new Map();
|
|
110
|
+
for (const route of routes)
|
|
111
|
+
if (route.protocol === "mcp") {
|
|
112
|
+
const accessor = route.accessor.join(".");
|
|
113
|
+
const array = dict.get(accessor) ?? [];
|
|
114
|
+
array.push(route);
|
|
115
|
+
dict.set(accessor, array);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const [accessor, list] of dict)
|
|
119
|
+
if (list.length > 1)
|
|
120
|
+
for (const route of list)
|
|
121
|
+
errors.push({
|
|
122
|
+
file: route.controller.file,
|
|
123
|
+
class: route.controller.class.name,
|
|
124
|
+
function: route.function.name || route.name,
|
|
125
|
+
from: `@McpRoute(${JSON.stringify(route.toolName)})`,
|
|
126
|
+
contents: [
|
|
127
|
+
`MCP tool name ${JSON.stringify(route.toolName)} conflicts on generated SDK accessor "api.functional.${accessor}".`,
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
|
|
77
132
|
const validateImplicit = (props: {
|
|
78
133
|
config: INestiaConfig;
|
|
79
134
|
errors: IReflectOperationError[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import { HashMap,
|
|
2
|
+
import { HashMap, TreeMap, hash } from "tstl";
|
|
3
3
|
import ts from "typescript";
|
|
4
4
|
|
|
5
5
|
import { ImportAnalyzer } from "../../analyses/ImportAnalyzer";
|
|
@@ -78,10 +78,11 @@ export class ImportDictionary {
|
|
|
78
78
|
};
|
|
79
79
|
const value: ICompositeValue = this.components_.take(key, () => ({
|
|
80
80
|
...key,
|
|
81
|
-
elements: new
|
|
81
|
+
elements: new TreeMap<string, string | null>(),
|
|
82
82
|
}));
|
|
83
|
-
if (props.type === "element")
|
|
84
|
-
|
|
83
|
+
if (props.type === "element")
|
|
84
|
+
value.elements.set(props.name, props.alias ?? null);
|
|
85
|
+
return props.type === "element" ? (props.alias ?? props.name) : props.name;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
public toStatements(outDir: string): ts.Statement[] {
|
|
@@ -149,11 +150,11 @@ export class ImportDictionary {
|
|
|
149
150
|
c.default !== null ? ts.factory.createIdentifier(c.default) : undefined,
|
|
150
151
|
c.elements.size() !== 0
|
|
151
152
|
? ts.factory.createNamedImports(
|
|
152
|
-
Array.from(c.elements).map((
|
|
153
|
+
Array.from(c.elements).map(({ first: name, second: alias }) =>
|
|
153
154
|
ts.factory.createImportSpecifier(
|
|
154
155
|
false,
|
|
155
|
-
undefined,
|
|
156
|
-
ts.factory.createIdentifier(
|
|
156
|
+
alias !== null ? ts.factory.createIdentifier(name) : undefined,
|
|
157
|
+
ts.factory.createIdentifier(alias ?? name),
|
|
157
158
|
),
|
|
158
159
|
),
|
|
159
160
|
)
|
|
@@ -166,6 +167,7 @@ export namespace ImportDictionary {
|
|
|
166
167
|
type: "default" | "element" | "asterisk";
|
|
167
168
|
file: string;
|
|
168
169
|
name: string;
|
|
170
|
+
alias?: string;
|
|
169
171
|
declaration: boolean;
|
|
170
172
|
}
|
|
171
173
|
}
|
|
@@ -177,7 +179,7 @@ interface ICompositeKey {
|
|
|
177
179
|
default: string | null;
|
|
178
180
|
}
|
|
179
181
|
interface ICompositeValue extends ICompositeKey {
|
|
180
|
-
elements:
|
|
182
|
+
elements: TreeMap<string, string | null>;
|
|
181
183
|
}
|
|
182
184
|
|
|
183
185
|
const NODE_MODULES = "node_modules/";
|
|
@@ -8,6 +8,7 @@ import { INestiaConfig } from "../../INestiaConfig";
|
|
|
8
8
|
export namespace SdkDistributionComposer {
|
|
9
9
|
export const compose = async (props: {
|
|
10
10
|
config: INestiaConfig;
|
|
11
|
+
mcp: boolean;
|
|
11
12
|
websocket: boolean;
|
|
12
13
|
}) => {
|
|
13
14
|
if (!fs.existsSync(props.config.distribute!))
|
|
@@ -34,6 +35,10 @@ export namespace SdkDistributionComposer {
|
|
|
34
35
|
execute("npm install --save-dev rimraf");
|
|
35
36
|
execute(`npm install --save @nestia/fetcher@${v.version}`);
|
|
36
37
|
execute(`npm install --save typia@${v.typia}`);
|
|
38
|
+
if (props.mcp)
|
|
39
|
+
execute(
|
|
40
|
+
`npm install --save @modelcontextprotocol/sdk@${v["@modelcontextprotocol/sdk"]}`,
|
|
41
|
+
);
|
|
37
42
|
if (props.websocket) execute(`npm install --save tgrid@${v.tgrid}`);
|
|
38
43
|
execute("npx typia setup --manager npm");
|
|
39
44
|
|
|
@@ -96,6 +101,7 @@ export namespace SdkDistributionComposer {
|
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
interface IDependencies {
|
|
104
|
+
"@modelcontextprotocol/sdk": string;
|
|
99
105
|
version: string;
|
|
100
106
|
typia: string;
|
|
101
107
|
tgrid: string;
|
|
@@ -4,11 +4,13 @@ import ts from "typescript";
|
|
|
4
4
|
import { INestiaProject } from "../../structures/INestiaProject";
|
|
5
5
|
import { ITypedApplication } from "../../structures/ITypedApplication";
|
|
6
6
|
import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute";
|
|
7
|
+
import { ITypedMcpRoute } from "../../structures/ITypedMcpRoute";
|
|
7
8
|
import { ITypedWebSocketRoute } from "../../structures/ITypedWebSocketRoute";
|
|
8
9
|
import { MapUtil } from "../../utils/MapUtil";
|
|
9
10
|
import { FilePrinter } from "./FilePrinter";
|
|
10
11
|
import { ImportDictionary } from "./ImportDictionary";
|
|
11
12
|
import { SdkHttpRouteProgrammer } from "./SdkHttpRouteProgrammer";
|
|
13
|
+
import { SdkMcpRouteProgrammer } from "./SdkMcpRouteProgrammer";
|
|
12
14
|
import { SdkRouteDirectory } from "./SdkRouteDirectory";
|
|
13
15
|
import { SdkWebSocketRouteProgrammer } from "./SdkWebSocketRouteProgrammer";
|
|
14
16
|
|
|
@@ -27,7 +29,7 @@ export namespace SdkFileProgrammer {
|
|
|
27
29
|
|
|
28
30
|
const emplace =
|
|
29
31
|
(directory: SdkRouteDirectory) =>
|
|
30
|
-
(route: ITypedHttpRoute | ITypedWebSocketRoute): void => {
|
|
32
|
+
(route: ITypedHttpRoute | ITypedWebSocketRoute | ITypedMcpRoute): void => {
|
|
31
33
|
// OPEN DIRECTORIES
|
|
32
34
|
for (const key of route.accessor.slice(0, -1)) {
|
|
33
35
|
directory = MapUtil.take(
|
|
@@ -80,7 +82,9 @@ export namespace SdkFileProgrammer {
|
|
|
80
82
|
statements.push(
|
|
81
83
|
...(route.protocol === "http"
|
|
82
84
|
? SdkHttpRouteProgrammer.write(project)(importer)(route)
|
|
83
|
-
:
|
|
85
|
+
: route.protocol === "websocket"
|
|
86
|
+
? SdkWebSocketRouteProgrammer.write(project)(importer)(route)
|
|
87
|
+
: SdkMcpRouteProgrammer.write(project)(importer)(route)),
|
|
84
88
|
);
|
|
85
89
|
if (i !== directory.routes.length - 1)
|
|
86
90
|
statements.push(FilePrinter.enter());
|