@@ -6,6 +6,7 @@ import type * as acp from "@agentclientprotocol/sdk";
66import {
77 createCodexMockTestFixture ,
88 createTestFixture ,
9+ createTestModel ,
910 createTestSessionState ,
1011 type TestFixture
1112} from "../acp-test-utils" ;
@@ -269,6 +270,66 @@ describe('ACP server test', { timeout: 40_000 }, () => {
269270 expect ( listSkillsSpy . mock . invocationCallOrder [ 0 ] ! ) . toBeLessThan ( threadStartSpy . mock . invocationCallOrder [ 0 ] ! ) ;
270271 } ) ;
271272
273+ it ( 'sanitizes whitespace in ACP MCP server names before adding them to Codex config' , async ( ) => {
274+ const mockFixture = createCodexMockTestFixture ( ) ;
275+ const codexAcpClient = mockFixture . getCodexAcpClient ( ) ;
276+ const codexAppServerClient = mockFixture . getCodexAppServerClient ( ) ;
277+
278+ vi . spyOn ( codexAppServerClient , "listSkills" ) . mockResolvedValue ( { data : [ ] } ) ;
279+ vi . spyOn ( codexAppServerClient , "configRead" ) . mockResolvedValue ( {
280+ config : {
281+ mcp_servers : {
282+ shared_mcp : {
283+ url : "https://example.com/mcp" ,
284+ } ,
285+ } ,
286+ } ,
287+ } as any ) ;
288+ const threadStartSpy = vi . spyOn ( codexAppServerClient , "threadStart" ) . mockResolvedValue ( {
289+ thread : { id : "thread-id" } as any ,
290+ model : "gpt-5" ,
291+ reasoningEffort : "medium" ,
292+ serviceTier : null ,
293+ } as any ) ;
294+ vi . spyOn ( codexAppServerClient , "listModels" ) . mockResolvedValue ( {
295+ data : [ createTestModel ( { id : "gpt-5" } ) ] ,
296+ nextCursor : null ,
297+ } ) ;
298+
299+ await codexAcpClient . newSession ( {
300+ cwd : "/workspace" ,
301+ mcpServers : [ {
302+ name : "shared mcp" ,
303+ command : "npx" ,
304+ args : [ "shared" ] ,
305+ env : [ ] ,
306+ } , {
307+ name : "stdio server\tone" ,
308+ command : "npx" ,
309+ args : [ "stdio" ] ,
310+ env : [ { name : "EXAMPLE" , value : "1" } ] ,
311+ } , {
312+ type : "http" ,
313+ name : "http\nserver\u00a0two" ,
314+ url : "https://example.com/http" ,
315+ headers : [ { name : "Authorization" , value : "Bearer token" } ] ,
316+ } ] ,
317+ } ) ;
318+
319+ const threadStartRequest = threadStartSpy . mock . calls [ 0 ] ! [ 0 ] ;
320+ expect ( threadStartRequest . config ?. [ "mcp_servers" ] ) . toEqual ( {
321+ stdio_server_one : {
322+ command : "npx" ,
323+ args : [ "stdio" ] ,
324+ env : { EXAMPLE : "1" } ,
325+ } ,
326+ http_server_two : {
327+ url : "https://example.com/http" ,
328+ http_headers : { Authorization : "Bearer token" } ,
329+ } ,
330+ } ) ;
331+ } ) ;
332+
272333 it ( 'waits for typed mcp startup status updates and returns terminal states' , async ( ) => {
273334 const mockFixture = createCodexMockTestFixture ( ) ;
274335 const codexAcpClient = mockFixture . getCodexAcpClient ( ) ;
0 commit comments