@@ -43,6 +43,14 @@ export class GreetingTool {
4343 } ) ,
4444 } )
4545 async sayHello ( { name } , context : Context ) {
46+ // Validate that mcpServer and mcpRequest properties exist
47+ if ( ! context . mcpServer ) {
48+ throw new Error ( 'mcpServer is not defined in the context' ) ;
49+ }
50+ if ( ! context . mcpRequest ) {
51+ throw new Error ( 'mcpRequest is not defined in the context' ) ;
52+ }
53+
4654 const user = await this . userRepository . findByName ( name ) ;
4755 for ( let i = 0 ; i < 5 ; i ++ ) {
4856 await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
@@ -125,12 +133,15 @@ export class ToolRequestScoped {
125133
126134describe ( 'E2E: MCP ToolServer' , ( ) => {
127135 let app : INestApplication ;
128- let testPort : number ;
136+ let statelessApp : INestApplication ;
137+ let statefulServerPort : number ;
138+ let statelessServerPort : number ;
129139
130140 // Set timeout for all tests in this describe block to 15000ms
131141 jest . setTimeout ( 15000 ) ;
132142
133143 beforeAll ( async ( ) => {
144+ // Create stateful server (original)
134145 const moduleFixture : TestingModule = await Test . createTestingModule ( {
135146 imports : [
136147 McpModule . forRoot ( {
@@ -159,21 +170,63 @@ describe('E2E: MCP ToolServer', () => {
159170 if ( ! server . address ( ) ) {
160171 throw new Error ( 'Server address not found after listen' ) ;
161172 }
162- testPort = ( server . address ( ) as import ( 'net' ) . AddressInfo ) . port ;
173+ statefulServerPort = ( server . address ( ) as import ( 'net' ) . AddressInfo ) . port ;
174+
175+ // Create stateless server
176+ const statelessModuleFixture : TestingModule =
177+ await Test . createTestingModule ( {
178+ imports : [
179+ McpModule . forRoot ( {
180+ name : 'test-stateless-mcp-server' ,
181+ version : '0.0.1' ,
182+ guards : [ ] ,
183+ transport : McpTransportType . STREAMABLE_HTTP ,
184+ streamableHttp : {
185+ enableJsonResponse : true ,
186+ sessionIdGenerator : undefined ,
187+ statelessMode : true ,
188+ } ,
189+ } ) ,
190+ ] ,
191+ providers : [
192+ GreetingTool ,
193+ GreetingToolRequestScoped ,
194+ MockUserRepository ,
195+ ToolRequestScoped ,
196+ ] ,
197+ } ) . compile ( ) ;
198+
199+ statelessApp = statelessModuleFixture . createNestApplication ( ) ;
200+ await statelessApp . listen ( 0 ) ;
201+
202+ const statelessServer = statelessApp . getHttpServer ( ) ;
203+ if ( ! statelessServer . address ( ) ) {
204+ throw new Error ( 'Stateless server address not found after listen' ) ;
205+ }
206+ statelessServerPort = (
207+ statelessServer . address ( ) as import ( 'net' ) . AddressInfo
208+ ) . port ;
163209 } ) ;
164210
165211 afterAll ( async ( ) => {
166212 await app . close ( ) ;
213+ await statelessApp . close ( ) ;
167214 } ) ;
168215
169216 const runClientTests = (
170217 clientType : 'http+sse' | 'streamable http' | 'stdio' ,
171218 clientCreator : ( port : number , options ?: any ) => Promise < Client > ,
172219 requestScopedHeaderValue : string ,
220+ stateless = false ,
173221 ) => {
174222 describe ( `using ${ clientType } client (${ clientCreator . name } )` , ( ) => {
223+ let port : number ;
224+
225+ beforeAll ( async ( ) => {
226+ port = stateless ? statelessServerPort : statefulServerPort ;
227+ } ) ;
175228 it ( 'should list tools' , async ( ) => {
176- const client = await clientCreator ( testPort ) ;
229+ const client = await clientCreator ( port ) ;
177230 try {
178231 const tools = await client . listTools ( ) ;
179232 expect ( tools . tools . length ) . toBeGreaterThan ( 0 ) ;
@@ -194,7 +247,7 @@ describe('E2E: MCP ToolServer', () => {
194247 it . each ( [ { tool : 'hello-world' } , { tool : 'hello-world-scoped' } ] ) (
195248 'should call the tool $tool and receive results' ,
196249 async ( { tool } ) => {
197- const client = await clientCreator ( testPort ) ;
250+ const client = await clientCreator ( port ) ;
198251 try {
199252 let progressCount = 1 ;
200253 const result : any = await client . callTool (
@@ -209,7 +262,7 @@ describe('E2E: MCP ToolServer', () => {
209262 } ,
210263 ) ;
211264
212- if ( clientType != 'stdio' ) {
265+ if ( clientType != 'stdio' && ! stateless ) {
213266 // stdio has no support for progress
214267 expect ( progressCount ) . toBe ( 5 ) ;
215268 }
@@ -224,7 +277,7 @@ describe('E2E: MCP ToolServer', () => {
224277 ) ;
225278
226279 it ( 'should call the tool get-request-scoped and receive header' , async ( ) => {
227- const client = await clientCreator ( testPort , {
280+ const client = await clientCreator ( port , {
228281 requestInit : {
229282 headers : { 'any-header' : requestScopedHeaderValue } ,
230283 } ,
@@ -243,7 +296,7 @@ describe('E2E: MCP ToolServer', () => {
243296 } ) ;
244297
245298 it ( 'should reject invalid arguments for hello-world' , async ( ) => {
246- const client = await clientCreator ( testPort ) ;
299+ const client = await clientCreator ( port ) ;
247300
248301 try {
249302 await client . callTool ( {
@@ -259,7 +312,7 @@ describe('E2E: MCP ToolServer', () => {
259312 } ) ;
260313
261314 it ( 'should reject missing arguments for hello-world' , async ( ) => {
262- const client = await clientCreator ( testPort ) ;
315+ const client = await clientCreator ( port ) ;
263316
264317 try {
265318 await client . callTool ( {
@@ -275,7 +328,7 @@ describe('E2E: MCP ToolServer', () => {
275328 } ) ;
276329
277330 it ( 'should call the tool and receive an error' , async ( ) => {
278- const client = await clientCreator ( testPort ) ;
331+ const client = await clientCreator ( port ) ;
279332 try {
280333 const result : any = await client . callTool ( {
281334 name : 'hello-world-error' ,
@@ -297,9 +350,17 @@ describe('E2E: MCP ToolServer', () => {
297350 // Run tests using the HTTP+SSE MCP client
298351 runClientTests ( 'http+sse' , createSseClient , 'any-value' ) ;
299352
300- // Run tests using the Streamable HTTP MCP client
353+ // Run tests using the [Stateful] Streamable HTTP MCP client
301354 runClientTests ( 'streamable http' , createStreamableClient , 'streamable-value' ) ;
302355
356+ // Run tests using the [Stateless] Streamable HTTP MCP client
357+ runClientTests (
358+ 'streamable http' ,
359+ createStreamableClient ,
360+ 'stateless-streamable-value' ,
361+ true ,
362+ ) ;
363+
303364 runClientTests (
304365 'stdio' ,
305366 ( ) =>
0 commit comments