@@ -21,6 +21,7 @@ describe("acpx plugin config parsing", () => {
2121 expect ( resolved . allowPluginLocalInstall ) . toBe ( true ) ;
2222 expect ( resolved . cwd ) . toBe ( path . resolve ( "/tmp/workspace" ) ) ;
2323 expect ( resolved . strictWindowsCmdWrapper ) . toBe ( true ) ;
24+ expect ( resolved . mcpServers ) . toEqual ( { } ) ;
2425 } ) ;
2526
2627 it ( "accepts command override and disables plugin-local auto-install" , ( ) => {
@@ -133,3 +134,225 @@ describe("acpx plugin config parsing", () => {
133134 ) . toThrow ( "strictWindowsCmdWrapper must be a boolean" ) ;
134135 } ) ;
135136} ) ;
137+
138+ describe ( "acpx plugin mcpServers config parsing" , ( ) => {
139+ it ( "accepts mcpServers with command-only configuration" , ( ) => {
140+ const resolved = resolveAcpxPluginConfig ( {
141+ rawConfig : {
142+ mcpServers : {
143+ canva : {
144+ command : "npx" ,
145+ } ,
146+ } ,
147+ } ,
148+ workspaceDir : "/tmp/workspace" ,
149+ } ) ;
150+
151+ expect ( resolved . mcpServers ) . toEqual ( {
152+ canva : {
153+ command : "npx" ,
154+ } ,
155+ } ) ;
156+ } ) ;
157+
158+ it ( "accepts mcpServers with command and args" , ( ) => {
159+ const resolved = resolveAcpxPluginConfig ( {
160+ rawConfig : {
161+ mcpServers : {
162+ canva : {
163+ command : "npx" ,
164+ args : [ "-y" , "mcp-remote@latest" , "https://mcp.canva.com/mcp" ] ,
165+ } ,
166+ } ,
167+ } ,
168+ workspaceDir : "/tmp/workspace" ,
169+ } ) ;
170+
171+ expect ( resolved . mcpServers ) . toEqual ( {
172+ canva : {
173+ command : "npx" ,
174+ args : [ "-y" , "mcp-remote@latest" , "https://mcp.canva.com/mcp" ] ,
175+ } ,
176+ } ) ;
177+ } ) ;
178+
179+ it ( "accepts mcpServers with command, args, and env" , ( ) => {
180+ const resolved = resolveAcpxPluginConfig ( {
181+ rawConfig : {
182+ mcpServers : {
183+ canva : {
184+ command : "npx" ,
185+ args : [ "-y" , "mcp-remote@latest" ] ,
186+ env : {
187+ API_KEY : "secret123" ,
188+ } ,
189+ } ,
190+ } ,
191+ } ,
192+ workspaceDir : "/tmp/workspace" ,
193+ } ) ;
194+
195+ expect ( resolved . mcpServers ) . toEqual ( {
196+ canva : {
197+ command : "npx" ,
198+ args : [ "-y" , "mcp-remote@latest" ] ,
199+ env : {
200+ API_KEY : "secret123" ,
201+ } ,
202+ } ,
203+ } ) ;
204+ } ) ;
205+
206+ it ( "accepts multiple mcpServers" , ( ) => {
207+ const resolved = resolveAcpxPluginConfig ( {
208+ rawConfig : {
209+ mcpServers : {
210+ canva : {
211+ command : "npx" ,
212+ args : [ "mcp-remote@latest" , "https://mcp.canva.com/mcp" ] ,
213+ } ,
214+ github : {
215+ command : "npx" ,
216+ args : [ "-y" , "@github/mcp-server" ] ,
217+ env : {
218+ GITHUB_TOKEN : "token123" ,
219+ } ,
220+ } ,
221+ } ,
222+ } ,
223+ workspaceDir : "/tmp/workspace" ,
224+ } ) ;
225+
226+ expect ( Object . keys ( resolved . mcpServers ) ) . toHaveLength ( 2 ) ;
227+ expect ( resolved . mcpServers . canva ) . toBeDefined ( ) ;
228+ expect ( resolved . mcpServers . github ) . toBeDefined ( ) ;
229+ } ) ;
230+
231+ it ( "rejects mcpServers with missing command" , ( ) => {
232+ expect ( ( ) =>
233+ resolveAcpxPluginConfig ( {
234+ rawConfig : {
235+ mcpServers : {
236+ canva : {
237+ args : [ "-y" ] ,
238+ } ,
239+ } ,
240+ } ,
241+ workspaceDir : "/tmp/workspace" ,
242+ } ) ,
243+ ) . toThrow ( "mcpServers.canva must have a command string, optional args array, and optional env object" ) ;
244+ } ) ;
245+
246+ it ( "rejects mcpServers with non-string command" , ( ) => {
247+ expect ( ( ) =>
248+ resolveAcpxPluginConfig ( {
249+ rawConfig : {
250+ mcpServers : {
251+ canva : {
252+ command : 123 ,
253+ } ,
254+ } ,
255+ } ,
256+ workspaceDir : "/tmp/workspace" ,
257+ } ) ,
258+ ) . toThrow ( "mcpServers.canva must have a command string, optional args array, and optional env object" ) ;
259+ } ) ;
260+
261+ it ( "rejects mcpServers with non-array args" , ( ) => {
262+ expect ( ( ) =>
263+ resolveAcpxPluginConfig ( {
264+ rawConfig : {
265+ mcpServers : {
266+ canva : {
267+ command : "npx" ,
268+ args : "-y" ,
269+ } ,
270+ } ,
271+ } ,
272+ workspaceDir : "/tmp/workspace" ,
273+ } ) ,
274+ ) . toThrow ( "mcpServers.canva must have a command string, optional args array, and optional env object" ) ;
275+ } ) ;
276+
277+ it ( "rejects mcpServers with non-string args items" , ( ) => {
278+ expect ( ( ) =>
279+ resolveAcpxPluginConfig ( {
280+ rawConfig : {
281+ mcpServers : {
282+ canva : {
283+ command : "npx" ,
284+ args : [ "-y" , 123 ] ,
285+ } ,
286+ } ,
287+ } ,
288+ workspaceDir : "/tmp/workspace" ,
289+ } ) ,
290+ ) . toThrow ( "mcpServers.canva must have a command string, optional args array, and optional env object" ) ;
291+ } ) ;
292+
293+ it ( "rejects mcpServers with non-object env" , ( ) => {
294+ expect ( ( ) =>
295+ resolveAcpxPluginConfig ( {
296+ rawConfig : {
297+ mcpServers : {
298+ canva : {
299+ command : "npx" ,
300+ env : "API_KEY=secret" ,
301+ } ,
302+ } ,
303+ } ,
304+ workspaceDir : "/tmp/workspace" ,
305+ } ) ,
306+ ) . toThrow ( "mcpServers.canva must have a command string, optional args array, and optional env object" ) ;
307+ } ) ;
308+
309+ it ( "rejects mcpServers with non-string env values" , ( ) => {
310+ expect ( ( ) =>
311+ resolveAcpxPluginConfig ( {
312+ rawConfig : {
313+ mcpServers : {
314+ canva : {
315+ command : "npx" ,
316+ env : {
317+ API_KEY : 123 ,
318+ } ,
319+ } ,
320+ } ,
321+ } ,
322+ workspaceDir : "/tmp/workspace" ,
323+ } ) ,
324+ ) . toThrow ( "mcpServers.canva must have a command string, optional args array, and optional env object" ) ;
325+ } ) ;
326+
327+ it ( "schema accepts valid mcpServers config" , ( ) => {
328+ const schema = createAcpxPluginConfigSchema ( ) ;
329+ if ( ! schema . safeParse ) {
330+ throw new Error ( "acpx config schema missing safeParse" ) ;
331+ }
332+ const parsed = schema . safeParse ( {
333+ mcpServers : {
334+ canva : {
335+ command : "npx" ,
336+ args : [ "-y" , "mcp-remote@latest" ] ,
337+ env : {
338+ API_KEY : "secret" ,
339+ } ,
340+ } ,
341+ } ,
342+ } ) ;
343+
344+ expect ( parsed . success ) . toBe ( true ) ;
345+ } ) ;
346+
347+ it ( "schema rejects mcpServers with invalid structure" , ( ) => {
348+ const schema = createAcpxPluginConfigSchema ( ) ;
349+ if ( ! schema . safeParse ) {
350+ throw new Error ( "acpx config schema missing safeParse" ) ;
351+ }
352+ const parsed = schema . safeParse ( {
353+ mcpServers : "invalid" ,
354+ } ) ;
355+
356+ expect ( parsed . success ) . toBe ( false ) ;
357+ } ) ;
358+ } ) ;
0 commit comments