@@ -45,6 +45,7 @@ export class SessionStartOptionsResolver {
4545 const { accountId } = defaults ;
4646 const account = accountId === undefined ? { } : { accountId } ;
4747 const { translator } = this ;
48+ const providerMcpServerNames = this . providerMcpServerNames . bind ( this ) ;
4849 const withCustomMcpServers = this . withCustomMcpServers . bind ( this ) ;
4950 const withSimulatorMcp = this . withSimulatorMcp . bind ( this ) ;
5051 const withPluginMcpServers = this . withPluginMcpServers . bind ( this ) ;
@@ -69,8 +70,9 @@ export class SessionStartOptionsResolver {
6970 } ) ,
7071 ) ;
7172 }
72- const custom = yield * withCustomMcpServers ( defaults . options ) ;
73- const pluginInjected = yield * withPluginMcpServers ( custom . options , custom . warnings ) ;
73+ const nativeMcpNames = yield * providerMcpServerNames ( defaults . options ) ;
74+ const custom = withCustomMcpServers ( defaults . options , nativeMcpNames ) ;
75+ const pluginInjected = withPluginMcpServers ( custom . options , custom . warnings , nativeMcpNames ) ;
7476 const resolved = withSimulatorMcp ( pluginInjected . options , sessionId ) ;
7577 const upstream = translationUpstream ( resolved ) ;
7678 if ( ! upstream ) return { options : resolved , ...account , warnings : pluginInjected . warnings } ;
@@ -118,64 +120,71 @@ export class SessionStartOptionsResolver {
118120 return names ;
119121 }
120122
123+ /**
124+ * Enabled provider-native MCP names for Codex's shared, un-namespaced space — resolved once per
125+ * session start and shared by the custom-MCP and LinkCode-plugin folds, since each resolution is
126+ * a full discovery round trip (`plugin/list` plus per-plugin detail reads). `null` means the
127+ * preflight failed: an override cannot be ruled out, so consumers skip instead of injecting.
128+ */
129+ private providerMcpServerNames ( options : StartOptions ) : Effect . Effect < ReadonlySet < string > | null > {
130+ if ( options . kind === 'codex' && this . plugins ) {
131+ return this . plugins
132+ . enabledMcpServerNames ( 'codex' , { cwd : options . cwd } )
133+ . pipe ( Effect . match ( { onSuccess : ( names ) => names , onFailure : ( ) => null } ) ) ;
134+ }
135+ return Effect . succeed ( new Set < string > ( ) ) ;
136+ }
137+
121138 /** Fold enabled custom MCP servers into the session's server list, warning instead of
122139 * silently dropping: unsupported agent kinds and name collisions are user-visible facts. */
123140 private withCustomMcpServers (
124141 options : StartOptions ,
125- ) : Effect . Effect < { options : StartOptions ; warnings : McpWarning [ ] } > {
142+ nativeMcpNames : ReadonlySet < string > | null ,
143+ ) : { options : StartOptions ; warnings : McpWarning [ ] } {
126144 const warnings : McpWarning [ ] = [ ] ;
127145 const enabled = this . customMcp ?. listEnabled ( ) ?? [ ] ;
128- if ( enabled . length === 0 ) return Effect . succeed ( { options, warnings } ) ;
146+ if ( enabled . length === 0 ) return { options, warnings } ;
129147 if ( ! MCP_CAPABLE_AGENT_KINDS . has ( options . kind ) ) {
130148 for ( const entry of enabled ) {
131149 warnings . push ( { serverName : entry . server . name , reason : 'agent-unsupported' } ) ;
132150 }
133- return Effect . succeed ( { options, warnings } ) ;
151+ return { options, warnings } ;
134152 }
135- const pluginNames =
136- options . kind === 'codex' && this . plugins
137- ? this . plugins
138- . enabledMcpServerNames ( 'codex' , { cwd : options . cwd } )
139- . pipe ( Effect . match ( { onSuccess : ( names ) => names , onFailure : ( ) => null } ) )
140- : Effect . succeed ( new Set < string > ( ) ) ;
141- return pluginNames . pipe (
142- Effect . map ( ( names ) => {
143- const servers = [ ...( options . mcpServers ?? [ ] ) ] ;
144- for ( const entry of enabled ) {
145- if ( names === null ) {
146- warnings . push ( {
147- serverName : entry . server . name ,
148- reason : 'provider-preflight-failed' ,
149- } ) ;
150- continue ;
151- }
152- if (
153- options . kind === 'codex' &&
154- entry . server . type === 'http' &&
155- entry . server . headers !== undefined &&
156- ! isObjectEmpty ( entry . server . headers )
157- ) {
158- warnings . push ( { serverName : entry . server . name , reason : 'provider-unsupported' } ) ;
159- continue ;
160- }
161- if (
162- names . has ( entry . server . name ) ||
163- servers . some ( ( server ) => server . name === entry . server . name )
164- ) {
165- warnings . push ( { serverName : entry . server . name , reason : 'name-conflict' } ) ;
166- continue ;
167- }
168- servers . push ( entry . server ) ;
169- }
170- return {
171- options :
172- servers . length === 0 && options . mcpServers === undefined
173- ? options
174- : { ...options , mcpServers : servers } ,
175- warnings,
176- } ;
177- } ) ,
178- ) ;
153+ const servers = [ ...( options . mcpServers ?? [ ] ) ] ;
154+ for ( const entry of enabled ) {
155+ const names = nativeMcpNames ;
156+ if ( names === null ) {
157+ warnings . push ( {
158+ serverName : entry . server . name ,
159+ reason : 'provider-preflight-failed' ,
160+ } ) ;
161+ continue ;
162+ }
163+ if (
164+ options . kind === 'codex' &&
165+ entry . server . type === 'http' &&
166+ entry . server . headers !== undefined &&
167+ ! isObjectEmpty ( entry . server . headers )
168+ ) {
169+ warnings . push ( { serverName : entry . server . name , reason : 'provider-unsupported' } ) ;
170+ continue ;
171+ }
172+ if (
173+ names . has ( entry . server . name ) ||
174+ servers . some ( ( server ) => server . name === entry . server . name )
175+ ) {
176+ warnings . push ( { serverName : entry . server . name , reason : 'name-conflict' } ) ;
177+ continue ;
178+ }
179+ servers . push ( entry . server ) ;
180+ }
181+ return {
182+ options :
183+ servers . length === 0 && options . mcpServers === undefined
184+ ? options
185+ : { ...options , mcpServers : servers } ,
186+ warnings,
187+ } ;
179188 }
180189
181190 /** Fold enabled LinkCode plugin mcp-server components into the session's server list, resolving
@@ -186,11 +195,12 @@ export class SessionStartOptionsResolver {
186195 private withPluginMcpServers (
187196 options : StartOptions ,
188197 warnings : McpWarning [ ] ,
189- ) : Effect . Effect < { options : StartOptions ; warnings : McpWarning [ ] } > {
198+ nativeMcpNames : ReadonlySet < string > | null ,
199+ ) : { options : StartOptions ; warnings : McpWarning [ ] } {
190200 const store = this . linkCodePluginStore ;
191- if ( store === undefined ) return Effect . succeed ( { options, warnings } ) ;
201+ if ( store === undefined ) return { options, warnings } ;
192202 const entries = store . list ( ) . filter ( ( entry ) => entry . installed . enabled ) ;
193- if ( entries . length === 0 ) return Effect . succeed ( { options, warnings } ) ;
203+ if ( entries . length === 0 ) return { options, warnings } ;
194204 if ( ! MCP_CAPABLE_AGENT_KINDS . has ( options . kind ) ) {
195205 for ( const entry of entries ) {
196206 for ( const component of entry . manifest . components ) {
@@ -199,61 +209,51 @@ export class SessionStartOptionsResolver {
199209 }
200210 }
201211 }
202- return Effect . succeed ( { options, warnings } ) ;
212+ return { options, warnings } ;
203213 }
204- const pluginNames =
205- options . kind === 'codex' && this . plugins
206- ? this . plugins
207- . enabledMcpServerNames ( 'codex' , { cwd : options . cwd } )
208- . pipe ( Effect . match ( { onSuccess : ( names ) => names , onFailure : ( ) => null } ) )
209- : Effect . succeed ( new Set < string > ( ) ) ;
210- return Effect . map ( pluginNames , ( names ) => {
211- const servers = [ ...( options . mcpServers ?? [ ] ) ] ;
212- for ( const entry of entries ) {
213- const settings = store . getSettings ( entry . installed . id ) ;
214- for ( const component of entry . manifest . components ) {
215- if ( component . kind !== 'mcp-server' ) continue ;
216- if ( names === null ) {
217- // Without the native name set an override cannot be ruled out — skip, don't inject.
218- warnings . push ( { serverName : component . name , reason : 'provider-preflight-failed' } ) ;
219- continue ;
220- }
221- if (
222- names . has ( component . name ) ||
223- servers . some ( ( server ) => server . name === component . name )
224- ) {
225- warnings . push ( { serverName : component . name , reason : 'name-conflict' } ) ;
226- continue ;
227- }
228- const env : Record < string , string > = { } ;
229- if ( component . env ) {
230- for ( const [ envVar , settingId ] of Object . entries ( component . env ) ) {
231- if ( settingId in settings ) env [ envVar ] = String ( settings [ settingId ] ) ;
232- }
214+ const names = nativeMcpNames ;
215+ const servers = [ ...( options . mcpServers ?? [ ] ) ] ;
216+ for ( const entry of entries ) {
217+ const settings = store . getSettings ( entry . installed . id ) ;
218+ for ( const component of entry . manifest . components ) {
219+ if ( component . kind !== 'mcp-server' ) continue ;
220+ if ( names === null ) {
221+ // Without the native name set an override cannot be ruled out — skip, don't inject.
222+ warnings . push ( { serverName : component . name , reason : 'provider-preflight-failed' } ) ;
223+ continue ;
224+ }
225+ if ( names . has ( component . name ) || servers . some ( ( server ) => server . name === component . name ) ) {
226+ warnings . push ( { serverName : component . name , reason : 'name-conflict' } ) ;
227+ continue ;
228+ }
229+ const env : Record < string , string > = { } ;
230+ if ( component . env ) {
231+ for ( const [ envVar , settingId ] of Object . entries ( component . env ) ) {
232+ if ( settingId in settings ) env [ envVar ] = String ( settings [ settingId ] ) ;
233233 }
234- // No missing-config advisory yet: shipped clients validate `reason` against the old enum
235- // and would drop the whole session.started frame, so emission waits for a tolerant floor.
236- const server : McpServer = {
237- type : 'stdio' ,
238- name : component . name ,
239- command : component . command ,
240- ...( component . entry && {
241- args : [ resolvePath ( entry . installed . path , component . entry ) , ...( component . args ?? [ ] ) ] ,
242- } ) ,
243- ...( ! component . entry && component . args && { args : component . args } ) ,
244- ...( ! isObjectEmpty ( env ) && { env } ) ,
245- } ;
246- servers . push ( server ) ;
247234 }
235+ // No missing-config advisory yet: shipped clients validate `reason` against the old enum
236+ // and would drop the whole session.started frame, so emission waits for a tolerant floor.
237+ const server : McpServer = {
238+ type : 'stdio' ,
239+ name : component . name ,
240+ command : component . command ,
241+ ...( component . entry && {
242+ args : [ resolvePath ( entry . installed . path , component . entry ) , ...( component . args ?? [ ] ) ] ,
243+ } ) ,
244+ ...( ! component . entry && component . args && { args : component . args } ) ,
245+ ...( ! isObjectEmpty ( env ) && { env } ) ,
246+ } ;
247+ servers . push ( server ) ;
248248 }
249- return {
250- options :
251- servers . length === 0 && options . mcpServers === undefined
252- ? options
253- : { ... options , mcpServers : servers } ,
254- warnings ,
255- } ;
256- } ) ;
249+ }
250+ return {
251+ options :
252+ servers . length === 0 && options . mcpServers === undefined
253+ ? options
254+ : { ... options , mcpServers : servers } ,
255+ warnings ,
256+ } ;
257257 }
258258
259259 /** Append the session's simulator MCP endpoint for agents whose SDK can consume it. */
0 commit comments