@@ -2,6 +2,7 @@ import type { DispatchBody, InvokeRemoteToolArgs } from '../src';
22import type { Logger } from '@forestadmin/datasource-toolkit' ;
33
44import { AIModelNotSupportedError , Router } from '../src' ;
5+ import IntegrationClient from '../src/integration-client' ;
56import McpClient from '../src/mcp-client' ;
67import ProviderDispatcher from '../src/provider-dispatcher' ;
78
@@ -39,6 +40,18 @@ jest.mock('../src/mcp-client', () => {
3940
4041const MockedMcpClient = McpClient as jest . MockedClass < typeof McpClient > ;
4142
43+ const loadToolsMock = jest . fn ( ) . mockReturnValue ( [ ] ) ;
44+ jest . mock ( '../src/integration-client' , ( ) => {
45+ return {
46+ __esModule : true ,
47+ default : jest . fn ( ) . mockImplementation ( ( ) => ( {
48+ loadTools : loadToolsMock ,
49+ } ) ) ,
50+ } ;
51+ } ) ;
52+
53+ const MockedIntegrationClient = IntegrationClient as jest . MockedClass < typeof IntegrationClient > ;
54+
4255describe ( 'route' , ( ) => {
4356 beforeEach ( ( ) => {
4457 jest . clearAllMocks ( ) ;
@@ -60,6 +73,7 @@ describe('route', () => {
6073 await router . route ( {
6174 route : 'ai-query' ,
6275 body : { tools : [ ] , tool_choice : 'required' , messages : [ ] } as unknown as DispatchBody ,
76+ mcpConfigs : { configs : { } } ,
6377 } ) ;
6478
6579 expect ( dispatchMock ) . toHaveBeenCalledWith ( {
@@ -90,6 +104,7 @@ describe('route', () => {
90104 route : 'ai-query' ,
91105 query : { 'ai-name' : 'gpt4mini' } ,
92106 body : { tools : [ ] , tool_choice : 'required' , messages : [ ] } as unknown as DispatchBody ,
107+ mcpConfigs : { configs : { } } ,
93108 } ) ;
94109
95110 expect ( ProviderDispatcherMock ) . toHaveBeenCalledWith ( gpt4MiniConfig , expect . anything ( ) ) ;
@@ -115,6 +130,7 @@ describe('route', () => {
115130 await router . route ( {
116131 route : 'ai-query' ,
117132 body : { tools : [ ] , tool_choice : 'required' , messages : [ ] } as unknown as DispatchBody ,
133+ mcpConfigs : { configs : { } } ,
118134 } ) ;
119135
120136 expect ( ProviderDispatcherMock ) . toHaveBeenCalledWith ( gpt4Config , expect . anything ( ) ) ;
@@ -137,6 +153,7 @@ describe('route', () => {
137153 route : 'ai-query' ,
138154 query : { 'ai-name' : 'non-existent' } ,
139155 body : { tools : [ ] , tool_choice : 'required' , messages : [ ] } as unknown as DispatchBody ,
156+ mcpConfigs : { configs : { } } ,
140157 } ) ;
141158
142159 expect ( mockLogger ) . toHaveBeenCalledWith (
@@ -155,6 +172,7 @@ describe('route', () => {
155172 route : 'invoke-remote-tool' ,
156173 query : { 'tool-name' : 'tool-name' } ,
157174 body : { inputs : [ ] } ,
175+ mcpConfigs : { configs : { } } ,
158176 } ) ;
159177
160178 expect ( invokeToolMock ) . toHaveBeenCalledWith ( 'tool-name' , [ ] ) ;
@@ -168,6 +186,7 @@ describe('route', () => {
168186 route : 'invoke-remote-tool' ,
169187 query : { } ,
170188 body : { inputs : [ ] } ,
189+ mcpConfigs : { configs : { } } ,
171190 } as any ) ,
172191 ) . rejects . toThrow ( 'query.tool-name: Missing required query parameter: tool-name' ) ;
173192 } ) ;
@@ -180,6 +199,7 @@ describe('route', () => {
180199 route : 'invoke-remote-tool' ,
181200 query : { 'tool-name' : 'tool-name' } ,
182201 body : { } as InvokeRemoteToolArgs [ 'body' ] ,
202+ mcpConfigs : { configs : { } } ,
183203 } ) ,
184204 ) . rejects . toThrow ( 'body.inputs: Missing required body parameter: inputs' ) ;
185205 } ) ;
@@ -192,6 +212,7 @@ describe('route', () => {
192212 route : 'invoke-remote-tool' ,
193213 query : { } ,
194214 body : { } ,
215+ mcpConfigs : { configs : { } } ,
195216 } as any ) ,
196217 ) . rejects . toThrow ( / t o o l - n a m e .* ; .* i n p u t s | i n p u t s .* ; .* t o o l - n a m e / ) ;
197218 } ) ;
@@ -201,7 +222,7 @@ describe('route', () => {
201222 it ( 'returns the remote tools definitions' , async ( ) => {
202223 const router = new Router ( { } ) ;
203224
204- const result = await router . route ( { route : 'remote-tools' } ) ;
225+ const result = await router . route ( { route : 'remote-tools' , mcpConfigs : { configs : { } } } ) ;
205226
206227 expect ( result ) . toEqual ( toolDefinitionsForFrontend ) ;
207228 } ) ;
@@ -211,7 +232,9 @@ describe('route', () => {
211232 it ( 'throws a validation error with helpful message' , async ( ) => {
212233 const router = new Router ( { } ) ;
213234
214- await expect ( router . route ( { route : 'unknown' } as any ) ) . rejects . toThrow (
235+ await expect (
236+ router . route ( { route : 'unknown' , mcpConfigs : { configs : { } } } as any ) ,
237+ ) . rejects . toThrow (
215238 "Invalid route. Expected: 'ai-query', 'invoke-remote-tool', 'remote-tools'" ,
216239 ) ;
217240 } ) ;
@@ -264,11 +287,12 @@ describe('route', () => {
264287 expect ( mcpClientInstance . closeConnections ) . toHaveBeenCalledTimes ( 1 ) ;
265288 } ) ;
266289
267- it ( 'does not call closeConnections when no mcpConfigs provided' , async ( ) => {
290+ it ( 'does not call closeConnections when no mcp configs provided' , async ( ) => {
268291 const router = new Router ( { } ) ;
269292
270293 await router . route ( {
271294 route : 'remote-tools' ,
295+ mcpConfigs : { configs : { } } ,
272296 } ) ;
273297
274298 expect ( MockedMcpClient ) . not . toHaveBeenCalled ( ) ;
@@ -385,6 +409,72 @@ describe('route', () => {
385409 } ) ;
386410 } ) ;
387411
412+ describe ( 'config splitting between MCP and integration configs' , ( ) => {
413+ const zendeskIntegration = {
414+ isForestConnector : true ,
415+ integrationName : 'zendesk' ,
416+ config : { subdomain : 'test' , apiToken : 'token' , email : 'a@b.c' } ,
417+ } ;
418+
419+ it ( 'passes only non-forest configs to McpClient' , async ( ) => {
420+ const router = new Router ( { } ) ;
421+
422+ await router . route ( {
423+ route : 'remote-tools' ,
424+ mcpConfigs : {
425+ configs : {
426+ server1 : { command : 'test' , args : [ ] } ,
427+ zendesk : zendeskIntegration ,
428+ } as any ,
429+ } ,
430+ } ) ;
431+
432+ expect ( MockedMcpClient ) . toHaveBeenCalledWith (
433+ { configs : { server1 : { command : 'test' , args : [ ] } } } ,
434+ undefined ,
435+ ) ;
436+ } ) ;
437+
438+ it ( 'passes forest configs to IntegrationClient' , async ( ) => {
439+ const router = new Router ( { } ) ;
440+
441+ await router . route ( {
442+ route : 'remote-tools' ,
443+ mcpConfigs : {
444+ configs : { zendesk : zendeskIntegration } as any ,
445+ } ,
446+ } ) ;
447+
448+ expect ( MockedIntegrationClient ) . toHaveBeenCalledWith ( [ zendeskIntegration ] , undefined ) ;
449+ } ) ;
450+
451+ it ( 'does not create IntegrationClient when no forest configs' , async ( ) => {
452+ const router = new Router ( { } ) ;
453+
454+ await router . route ( {
455+ route : 'remote-tools' ,
456+ mcpConfigs : {
457+ configs : { server1 : { command : 'test' , args : [ ] } } ,
458+ } ,
459+ } ) ;
460+
461+ expect ( MockedIntegrationClient ) . not . toHaveBeenCalled ( ) ;
462+ } ) ;
463+
464+ it ( 'does not create McpClient when only forest configs' , async ( ) => {
465+ const router = new Router ( { } ) ;
466+
467+ await router . route ( {
468+ route : 'remote-tools' ,
469+ mcpConfigs : {
470+ configs : { zendesk : zendeskIntegration } as any ,
471+ } ,
472+ } ) ;
473+
474+ expect ( MockedMcpClient ) . not . toHaveBeenCalled ( ) ;
475+ } ) ;
476+ } ) ;
477+
388478 describe ( 'Model validation' , ( ) => {
389479 it ( 'throws AIModelNotSupportedError when model does not support tools' , ( ) => {
390480 expect (
0 commit comments