@nestjs-mcp/server 0.1.0-alpha.4
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/.copilotignore +38 -0
- package/.devcontainer/Dockerfile.dev +28 -0
- package/.devcontainer/devcontainer.json +56 -0
- package/.devcontainer/docker-compose.yml +15 -0
- package/.dockerignore +37 -0
- package/.github/codeql-config.yml +4 -0
- package/.github/copilot-instructions.md +138 -0
- package/.github/prompts/memory.prompt.md +120 -0
- package/.github/workflows/auto-tag-release.yml +84 -0
- package/.github/workflows/codeql-analysis.yml +56 -0
- package/.github/workflows/npm-publish.yml +58 -0
- package/.github/workflows/pr-branch-validation.yml +78 -0
- package/.github/workflows/run-tests.yml +41 -0
- package/.github/workflows/sync-main-to-develop.yml +53 -0
- package/.handbook/GIT_GUIDELINES.md +250 -0
- package/.handbook/PACKAGE_VERSIONING.md +140 -0
- package/.handbook/STACK.md +75 -0
- package/.prettierrc +4 -0
- package/.vscode/extensions.json +44 -0
- package/.vscode/settings.json +40 -0
- package/CONTRIBUTING.md +261 -0
- package/LICENSE +21 -0
- package/README.md +490 -0
- package/dist/examples/async-import/app.module.d.ts +2 -0
- package/dist/examples/async-import/app.module.js +33 -0
- package/dist/examples/async-import/app.module.js.map +1 -0
- package/dist/examples/async-import/main.d.ts +1 -0
- package/dist/examples/async-import/main.js +17 -0
- package/dist/examples/async-import/main.js.map +1 -0
- package/dist/examples/guards/app.module.d.ts +6 -0
- package/dist/examples/guards/app.module.js +48 -0
- package/dist/examples/guards/app.module.js.map +1 -0
- package/dist/examples/guards/guards.resolver.d.ts +13 -0
- package/dist/examples/guards/guards.resolver.js +61 -0
- package/dist/examples/guards/guards.resolver.js.map +1 -0
- package/dist/examples/guards/main.d.ts +1 -0
- package/dist/examples/guards/main.js +11 -0
- package/dist/examples/guards/main.js.map +1 -0
- package/dist/examples/mixed/app.module.d.ts +2 -0
- package/dist/examples/mixed/app.module.js +31 -0
- package/dist/examples/mixed/app.module.js.map +1 -0
- package/dist/examples/mixed/main.d.ts +1 -0
- package/dist/examples/mixed/main.js +11 -0
- package/dist/examples/mixed/main.js.map +1 -0
- package/dist/examples/mixed/mixed.resolver.d.ts +6 -0
- package/dist/examples/mixed/mixed.resolver.js +78 -0
- package/dist/examples/mixed/mixed.resolver.js.map +1 -0
- package/dist/examples/prompts/app.module.d.ts +2 -0
- package/dist/examples/prompts/app.module.js +31 -0
- package/dist/examples/prompts/app.module.js.map +1 -0
- package/dist/examples/prompts/main.d.ts +1 -0
- package/dist/examples/prompts/main.js +11 -0
- package/dist/examples/prompts/main.js.map +1 -0
- package/dist/examples/prompts/prompts.resolver.d.ts +14 -0
- package/dist/examples/prompts/prompts.resolver.js +165 -0
- package/dist/examples/prompts/prompts.resolver.js.map +1 -0
- package/dist/examples/resources/app.module.d.ts +2 -0
- package/dist/examples/resources/app.module.js +31 -0
- package/dist/examples/resources/app.module.js.map +1 -0
- package/dist/examples/resources/main.d.ts +1 -0
- package/dist/examples/resources/main.js +11 -0
- package/dist/examples/resources/main.js.map +1 -0
- package/dist/examples/resources/resources.resolver.d.ts +12 -0
- package/dist/examples/resources/resources.resolver.js +114 -0
- package/dist/examples/resources/resources.resolver.js.map +1 -0
- package/dist/examples/tools/app.module.d.ts +2 -0
- package/dist/examples/tools/app.module.js +31 -0
- package/dist/examples/tools/app.module.js.map +1 -0
- package/dist/examples/tools/main.d.ts +1 -0
- package/dist/examples/tools/main.js +11 -0
- package/dist/examples/tools/main.js.map +1 -0
- package/dist/examples/tools/tools.resolver.d.ts +23 -0
- package/dist/examples/tools/tools.resolver.js +175 -0
- package/dist/examples/tools/tools.resolver.js.map +1 -0
- package/dist/src/controllers/sse/index.d.ts +2 -0
- package/dist/src/controllers/sse/index.js +19 -0
- package/dist/src/controllers/sse/index.js.map +1 -0
- package/dist/src/controllers/sse/sse.controller.d.ts +8 -0
- package/dist/src/controllers/sse/sse.controller.js +51 -0
- package/dist/src/controllers/sse/sse.controller.js.map +1 -0
- package/dist/src/controllers/sse/sse.service.d.ts +16 -0
- package/dist/src/controllers/sse/sse.service.js +78 -0
- package/dist/src/controllers/sse/sse.service.js.map +1 -0
- package/dist/src/controllers/streamable/index.d.ts +2 -0
- package/dist/src/controllers/streamable/index.js +19 -0
- package/dist/src/controllers/streamable/index.js.map +1 -0
- package/dist/src/controllers/streamable/streamable.controller.d.ts +9 -0
- package/dist/src/controllers/streamable/streamable.controller.js +62 -0
- package/dist/src/controllers/streamable/streamable.controller.js.map +1 -0
- package/dist/src/controllers/streamable/streamable.service.d.ts +24 -0
- package/dist/src/controllers/streamable/streamable.service.js +117 -0
- package/dist/src/controllers/streamable/streamable.service.js.map +1 -0
- package/dist/src/decorators/capabilities.constants.d.ts +4 -0
- package/dist/src/decorators/capabilities.constants.js +8 -0
- package/dist/src/decorators/capabilities.constants.js.map +1 -0
- package/dist/src/decorators/capabilities.decorators.d.ts +8 -0
- package/dist/src/decorators/capabilities.decorators.js +49 -0
- package/dist/src/decorators/capabilities.decorators.js.map +1 -0
- package/dist/src/decorators/index.d.ts +2 -0
- package/dist/src/decorators/index.js +19 -0
- package/dist/src/decorators/index.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +21 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/interfaces/capabilities.interface.d.ts +52 -0
- package/dist/src/interfaces/capabilities.interface.js +3 -0
- package/dist/src/interfaces/capabilities.interface.js.map +1 -0
- package/dist/src/interfaces/guards.interface.d.ts +4 -0
- package/dist/src/interfaces/guards.interface.js +3 -0
- package/dist/src/interfaces/guards.interface.js.map +1 -0
- package/dist/src/interfaces/index.d.ts +2 -0
- package/dist/src/interfaces/index.js +19 -0
- package/dist/src/interfaces/index.js.map +1 -0
- package/dist/src/interfaces/mcp-server-options.interface.d.ts +42 -0
- package/dist/src/interfaces/mcp-server-options.interface.js +3 -0
- package/dist/src/interfaces/mcp-server-options.interface.js.map +1 -0
- package/dist/src/mcp.module.d.ts +13 -0
- package/dist/src/mcp.module.js +176 -0
- package/dist/src/mcp.module.js.map +1 -0
- package/dist/src/registry/discovery.service.d.ts +16 -0
- package/dist/src/registry/discovery.service.js +85 -0
- package/dist/src/registry/discovery.service.js.map +1 -0
- package/dist/src/registry/index.d.ts +2 -0
- package/dist/src/registry/index.js +19 -0
- package/dist/src/registry/index.js.map +1 -0
- package/dist/src/registry/logger.service.d.ts +16 -0
- package/dist/src/registry/logger.service.js +97 -0
- package/dist/src/registry/logger.service.js.map +1 -0
- package/dist/src/registry/registry.service.d.ts +14 -0
- package/dist/src/registry/registry.service.js +165 -0
- package/dist/src/registry/registry.service.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/eslint.config.mjs +40 -0
- package/examples/README.md +56 -0
- package/examples/async-import/app.module.ts +22 -0
- package/examples/async-import/main.ts +15 -0
- package/examples/guards/app.module.ts +44 -0
- package/examples/guards/guards.resolver.ts +52 -0
- package/examples/guards/main.ts +11 -0
- package/examples/mixed/app.module.ts +20 -0
- package/examples/mixed/main.ts +11 -0
- package/examples/mixed/mixed.resolver.ts +56 -0
- package/examples/prompts/app.module.ts +20 -0
- package/examples/prompts/main.ts +11 -0
- package/examples/prompts/prompts.resolver.ts +184 -0
- package/examples/resources/app.module.ts +19 -0
- package/examples/resources/main.ts +11 -0
- package/examples/resources/resources.resolver.ts +123 -0
- package/examples/tools/app.module.ts +20 -0
- package/examples/tools/main.ts +11 -0
- package/examples/tools/tools.resolver.ts +205 -0
- package/nest-cli.json +8 -0
- package/package.json +106 -0
- package/scripts/npm-publish.js +301 -0
- package/src/controllers/sse/index.ts +2 -0
- package/src/controllers/sse/sse.controller.ts +19 -0
- package/src/controllers/sse/sse.service.ts +90 -0
- package/src/controllers/streamable/index.ts +2 -0
- package/src/controllers/streamable/streamable.controller.ts +24 -0
- package/src/controllers/streamable/streamable.service.ts +168 -0
- package/src/decorators/capabilities.constants.ts +7 -0
- package/src/decorators/capabilities.decorators.ts +150 -0
- package/src/decorators/index.ts +2 -0
- package/src/index.ts +11 -0
- package/src/interfaces/capabilities.interface.ts +95 -0
- package/src/interfaces/guards.interface.ts +13 -0
- package/src/interfaces/index.ts +2 -0
- package/src/interfaces/mcp-server-options.interface.ts +105 -0
- package/src/mcp.module.ts +233 -0
- package/src/mcp.service.spec.ts +28 -0
- package/src/registry/discovery.service.ts +116 -0
- package/src/registry/index.ts +2 -0
- package/src/registry/logger.service.ts +143 -0
- package/src/registry/registry.service.ts +281 -0
- package/test/base.e2e-spec.ts +74 -0
- package/test/jest-e2e.json +9 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import {
|
|
2
|
+
McpServer,
|
|
3
|
+
ResourceTemplate,
|
|
4
|
+
} from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import type { CanActivate, Type } from '@nestjs/common';
|
|
6
|
+
import { Injectable } from '@nestjs/common';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
MCP_PROMPT,
|
|
10
|
+
MCP_RESOURCE,
|
|
11
|
+
MCP_TOOL,
|
|
12
|
+
} from '../decorators/capabilities.constants';
|
|
13
|
+
import { MCP_GUARDS } from '../decorators/capabilities.decorators';
|
|
14
|
+
import {
|
|
15
|
+
PromptOptions,
|
|
16
|
+
ResourceOptions,
|
|
17
|
+
ToolOptions,
|
|
18
|
+
} from '../interfaces/capabilities.interface';
|
|
19
|
+
|
|
20
|
+
import { MCP_RESOLVER } from '../decorators';
|
|
21
|
+
import { McpExecutionContext } from '../interfaces/guards.interface';
|
|
22
|
+
import { DiscoveryService } from './discovery.service';
|
|
23
|
+
import { McpLoggerService } from './logger.service';
|
|
24
|
+
|
|
25
|
+
@Injectable()
|
|
26
|
+
export class RegistryService {
|
|
27
|
+
constructor(
|
|
28
|
+
private readonly discoveryService: DiscoveryService,
|
|
29
|
+
private readonly logger: McpLoggerService,
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
async registerAll(server: McpServer): Promise<void> {
|
|
33
|
+
this.logger.log(
|
|
34
|
+
'Starting registration of all MCP capabilities...',
|
|
35
|
+
'registry',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
await Promise.all([
|
|
39
|
+
this.registerResources(server),
|
|
40
|
+
this.registerPrompts(server),
|
|
41
|
+
this.registerTools(server),
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Executes all guards attached to the resolver class and method.
|
|
47
|
+
* Throws an error if any guard denies access.
|
|
48
|
+
*
|
|
49
|
+
* @param instance The resolver instance
|
|
50
|
+
* @param methodName The method name being invoked
|
|
51
|
+
* @param args The arguments passed to the method
|
|
52
|
+
* @throws Error if any guard denies access
|
|
53
|
+
*/
|
|
54
|
+
private runGuards(
|
|
55
|
+
instance: object,
|
|
56
|
+
methodName: string,
|
|
57
|
+
args: unknown[],
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
// Retrieve class-level guards
|
|
60
|
+
const classConstructor = instance.constructor;
|
|
61
|
+
const classGuards: (CanActivate | { new (): CanActivate })[] =
|
|
62
|
+
(Reflect.getMetadata(MCP_GUARDS, classConstructor) as (
|
|
63
|
+
| CanActivate
|
|
64
|
+
| { new (): CanActivate }
|
|
65
|
+
)[]) || [];
|
|
66
|
+
|
|
67
|
+
// Retrieve method-level guards
|
|
68
|
+
const prototype = Object.getPrototypeOf(instance) as Record<
|
|
69
|
+
string,
|
|
70
|
+
unknown
|
|
71
|
+
>;
|
|
72
|
+
const methodKey = prototype[methodName] as object | undefined;
|
|
73
|
+
|
|
74
|
+
const methodGuards: (CanActivate | { new (): CanActivate })[] =
|
|
75
|
+
(methodKey &&
|
|
76
|
+
(Reflect.getMetadata(MCP_GUARDS, methodKey) as (
|
|
77
|
+
| CanActivate
|
|
78
|
+
| { new (): CanActivate }
|
|
79
|
+
)[])) ||
|
|
80
|
+
[];
|
|
81
|
+
|
|
82
|
+
// Combine guards: class-level first, then method-level
|
|
83
|
+
const allGuards = [...classGuards, ...methodGuards];
|
|
84
|
+
|
|
85
|
+
if (!allGuards.length) return Promise.resolve();
|
|
86
|
+
// Build a minimal context (customize as needed)
|
|
87
|
+
const context: McpExecutionContext = {
|
|
88
|
+
args,
|
|
89
|
+
|
|
90
|
+
// @ts-expect-error: Default types are 'http' | 'ws' | 'rpc' but in our case
|
|
91
|
+
// we are using 'mcp'
|
|
92
|
+
getType: () => 'mcp',
|
|
93
|
+
getClass: () => instance.constructor as Type<any>,
|
|
94
|
+
getArgs: <T extends Array<any>>() => args as T,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return (async () => {
|
|
98
|
+
for (const Guard of allGuards) {
|
|
99
|
+
const guardInstance: CanActivate =
|
|
100
|
+
typeof Guard === 'function' ? new Guard() : Guard;
|
|
101
|
+
const allowed = await guardInstance.canActivate(context);
|
|
102
|
+
|
|
103
|
+
if (!allowed)
|
|
104
|
+
throw new Error(`Access denied by guard on ${methodName}`);
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private async wrappedHandler<TArgs extends unknown[], TResult>(
|
|
110
|
+
instance: object,
|
|
111
|
+
handler: (...args: TArgs) => TResult,
|
|
112
|
+
args: unknown[],
|
|
113
|
+
) {
|
|
114
|
+
const isResolver = Reflect.hasMetadata(MCP_RESOLVER, instance.constructor);
|
|
115
|
+
|
|
116
|
+
if (!isResolver) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Class "${instance.constructor.name}" must be decorated with @Resolver to use @Prompt, @Tool, or @Resource.`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const methodName = handler.name;
|
|
123
|
+
|
|
124
|
+
await this.runGuards(instance, methodName, args);
|
|
125
|
+
|
|
126
|
+
return handler(...(args as TArgs));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private registerResources(server: McpServer) {
|
|
130
|
+
const resourceMethods =
|
|
131
|
+
this.discoveryService.getAllMethodsWithMetadata<ResourceOptions>(
|
|
132
|
+
MCP_RESOURCE,
|
|
133
|
+
);
|
|
134
|
+
for (const method of resourceMethods) {
|
|
135
|
+
const { metadata, handler, instance } = method;
|
|
136
|
+
|
|
137
|
+
this.logger.log(
|
|
138
|
+
`Resource "${metadata?.name || 'unnamed'}" found.`,
|
|
139
|
+
'resources',
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const wrappedHandler = (...args: unknown[]) =>
|
|
143
|
+
this.wrappedHandler(instance, handler, args);
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
if ('template' in metadata) {
|
|
147
|
+
if ('metadata' in metadata) {
|
|
148
|
+
server.resource(
|
|
149
|
+
metadata.name,
|
|
150
|
+
new ResourceTemplate(metadata.template, { list: undefined }),
|
|
151
|
+
metadata.metadata,
|
|
152
|
+
wrappedHandler,
|
|
153
|
+
);
|
|
154
|
+
} else {
|
|
155
|
+
server.resource(
|
|
156
|
+
metadata.name,
|
|
157
|
+
new ResourceTemplate(metadata.template, { list: undefined }),
|
|
158
|
+
wrappedHandler,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
} else if ('uri' in metadata) {
|
|
162
|
+
if ('metadata' in metadata) {
|
|
163
|
+
server.resource(
|
|
164
|
+
metadata.name,
|
|
165
|
+
metadata.uri,
|
|
166
|
+
metadata.metadata,
|
|
167
|
+
wrappedHandler,
|
|
168
|
+
);
|
|
169
|
+
} else {
|
|
170
|
+
server.resource(metadata.name, metadata.uri, wrappedHandler);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
this.logger.error(
|
|
175
|
+
`Error registering resource ${metadata.name}: ${error}`,
|
|
176
|
+
undefined,
|
|
177
|
+
'resources',
|
|
178
|
+
);
|
|
179
|
+
if (error && typeof error === 'object' && 'stack' in error) {
|
|
180
|
+
this.logger.error(
|
|
181
|
+
`Error stack: ${(error as Error).stack}`,
|
|
182
|
+
undefined,
|
|
183
|
+
'resources',
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private registerPrompts(server: McpServer) {
|
|
191
|
+
const promptMethods =
|
|
192
|
+
this.discoveryService.getAllMethodsWithMetadata<PromptOptions>(
|
|
193
|
+
MCP_PROMPT,
|
|
194
|
+
);
|
|
195
|
+
for (const method of promptMethods) {
|
|
196
|
+
const { metadata, handler, instance } = method;
|
|
197
|
+
|
|
198
|
+
this.logger.log(
|
|
199
|
+
`Prompt "${metadata?.name || 'unnamed'}" found.`,
|
|
200
|
+
'prompts',
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const wrappedHandler = (...args: unknown[]) =>
|
|
204
|
+
this.wrappedHandler(instance, handler, args);
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
if ('description' in metadata && 'argsSchema' in metadata) {
|
|
208
|
+
server.prompt(
|
|
209
|
+
metadata.name,
|
|
210
|
+
metadata.description,
|
|
211
|
+
metadata.argsSchema,
|
|
212
|
+
wrappedHandler,
|
|
213
|
+
);
|
|
214
|
+
} else if ('argsSchema' in metadata) {
|
|
215
|
+
server.prompt(metadata.name, metadata.argsSchema, wrappedHandler);
|
|
216
|
+
} else if ('description' in metadata) {
|
|
217
|
+
server.prompt(metadata.name, metadata.description, wrappedHandler);
|
|
218
|
+
} else {
|
|
219
|
+
server.prompt(metadata.name, wrappedHandler);
|
|
220
|
+
}
|
|
221
|
+
} catch (error) {
|
|
222
|
+
this.logger.error(
|
|
223
|
+
`Error registering prompt ${metadata.name}: ${error}`,
|
|
224
|
+
undefined,
|
|
225
|
+
'prompts',
|
|
226
|
+
);
|
|
227
|
+
if (error && typeof error === 'object' && 'stack' in error) {
|
|
228
|
+
this.logger.error(
|
|
229
|
+
`Error stack: ${(error as Error).stack}`,
|
|
230
|
+
undefined,
|
|
231
|
+
'prompts',
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private registerTools(server: McpServer) {
|
|
239
|
+
const toolMethods =
|
|
240
|
+
this.discoveryService.getAllMethodsWithMetadata<ToolOptions>(MCP_TOOL);
|
|
241
|
+
|
|
242
|
+
for (const method of toolMethods) {
|
|
243
|
+
const { metadata, handler, instance } = method;
|
|
244
|
+
|
|
245
|
+
this.logger.log(`Tool "${metadata?.name || 'unnamed'}" found.`, 'tools');
|
|
246
|
+
|
|
247
|
+
const wrappedHandler = (...args: unknown[]) =>
|
|
248
|
+
this.wrappedHandler(instance, handler, args);
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
if ('description' in metadata && 'paramSchema' in metadata) {
|
|
252
|
+
server.tool(
|
|
253
|
+
metadata.name,
|
|
254
|
+
metadata.description,
|
|
255
|
+
metadata.paramSchema,
|
|
256
|
+
wrappedHandler,
|
|
257
|
+
);
|
|
258
|
+
} else if ('paramSchema' in metadata) {
|
|
259
|
+
server.tool(metadata.name, metadata.paramSchema, wrappedHandler);
|
|
260
|
+
} else if ('description' in metadata) {
|
|
261
|
+
server.tool(metadata.name, metadata.description, wrappedHandler);
|
|
262
|
+
} else {
|
|
263
|
+
server.tool(metadata.name, wrappedHandler);
|
|
264
|
+
}
|
|
265
|
+
} catch (error) {
|
|
266
|
+
this.logger.error(
|
|
267
|
+
`Error registering tool ${metadata.name}: ${error}`,
|
|
268
|
+
undefined,
|
|
269
|
+
'tools',
|
|
270
|
+
);
|
|
271
|
+
if (error && typeof error === 'object' && 'stack' in error) {
|
|
272
|
+
this.logger.error(
|
|
273
|
+
`Stack trace: ${(error as Error).stack}`,
|
|
274
|
+
undefined,
|
|
275
|
+
'tools',
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
3
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
4
|
+
import { INestApplication } from '@nestjs/common';
|
|
5
|
+
import { Test, TestingModule } from '@nestjs/testing';
|
|
6
|
+
// import * as request from 'supertest';
|
|
7
|
+
|
|
8
|
+
import { App } from 'supertest/types';
|
|
9
|
+
|
|
10
|
+
import { AppModule } from '../examples/tools/app.module';
|
|
11
|
+
|
|
12
|
+
describe('AppController (e2e)', () => {
|
|
13
|
+
let app: INestApplication<App>;
|
|
14
|
+
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
console.log('Starting test suite...');
|
|
17
|
+
const moduleFixture: TestingModule = await Test.createTestingModule({
|
|
18
|
+
imports: [AppModule],
|
|
19
|
+
}).compile();
|
|
20
|
+
|
|
21
|
+
app = moduleFixture.createNestApplication();
|
|
22
|
+
|
|
23
|
+
await app
|
|
24
|
+
.init()
|
|
25
|
+
.then(() => {
|
|
26
|
+
console.log('App initialized');
|
|
27
|
+
})
|
|
28
|
+
.catch((error) => {
|
|
29
|
+
console.error('Error initializing app:', error);
|
|
30
|
+
throw error;
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should stremeable client works', async () => {
|
|
35
|
+
let client: Client | undefined = undefined;
|
|
36
|
+
|
|
37
|
+
const baseUrl = new URL('http://localhost:3000');
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
client = new Client({
|
|
41
|
+
name: 'streamable-http-client',
|
|
42
|
+
version: '1.0.0',
|
|
43
|
+
});
|
|
44
|
+
const transport = new StreamableHTTPClientTransport(new URL(baseUrl));
|
|
45
|
+
await client.connect(transport);
|
|
46
|
+
console.log('Connected using Streamable HTTP transport');
|
|
47
|
+
await transport.close();
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.log('Error connecting using Streamable HTTP transport');
|
|
50
|
+
console.log(error);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should sse client works', async () => {
|
|
56
|
+
let client: Client | undefined = undefined;
|
|
57
|
+
|
|
58
|
+
const baseUrl = new URL('http://localhost:3000');
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
client = new Client({
|
|
62
|
+
name: 'sse-client',
|
|
63
|
+
version: '1.0.0',
|
|
64
|
+
});
|
|
65
|
+
const sseTransport = new SSEClientTransport(baseUrl);
|
|
66
|
+
await client.connect(sseTransport);
|
|
67
|
+
console.log('Connected using SSE transport');
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.log('Error connecting using SSE transport');
|
|
70
|
+
console.log(error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"removeComments": true,
|
|
6
|
+
"emitDecoratorMetadata": true,
|
|
7
|
+
"experimentalDecorators": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"target": "ES2023",
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"outDir": "./dist",
|
|
12
|
+
"baseUrl": "./",
|
|
13
|
+
"incremental": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"strictNullChecks": true,
|
|
16
|
+
"forceConsistentCasingInFileNames": true,
|
|
17
|
+
"noImplicitAny": false,
|
|
18
|
+
"strictBindCallApply": false,
|
|
19
|
+
"noFallthroughCasesInSwitch": false
|
|
20
|
+
},
|
|
21
|
+
"include": ["src/**/*.ts", "test/**/*.ts", "examples/**/*.ts"],
|
|
22
|
+
"exclude": ["node_modules", "scripts"]
|
|
23
|
+
}
|