src/vs/platform/agentHost/node/codex/codexMcpServers.ts

358 LOC · 356 covered · 2 uncovered · 60 ranges · 62 concepts · 24 introducers · 35 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filecodexAgent.ts ×13 · 83 introduced LOCcodexAgent.ts ×13codexSessionConfigKeys.test|title=codexSessionConfigKeys resolveSessionConfig preserves legacy read-only permissions on restore|occurrence=1 · 0 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.test|title=codexSessionConfigKeys resolveSessionConfig exposes a single permissions-preset chip|occurrence=1 · 0 introduced LOCcodexSessionConfigKeys.t…codexAgent.ts ×1 · 27 introduced LOCcodexAgent.ts ×1codexAgent.ts ×24 · 89 introduced LOCcodexAgent.ts ×24codexSessionConfigKeys.ts ×1 · 7 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.ts ×1 · 2 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.ts ×7 · 49 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.ts ×1 · 11 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.ts ×1 · 2 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.ts ×2 · 4 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.ts ×3 · 18 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.ts ×3 · 19 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.ts ×2 · 3 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.ts ×2 · 2 introduced LOCcodexSessionConfigKeys.t…codexSessionConfigKeys.ts ×6 · 23 introduced LOCcodexSessionConfigKeys.t…codexAgent.ts ×1 · 2 introduced LOCcodexAgent.ts ×1codexAgent.ts ×1 · 2 introduced LOCcodexAgent.ts ×1codexAgent.ts ×3 · 7 introduced LOCcodexAgent.ts ×3codexAgent.ts ×1 · 2 introduced LOCcodexAgent.ts ×1codexAgent.ts ×1 · 2 introduced LOCcodexAgent.ts ×1codexAgent.ts ×1 · 2 introduced LOCcodexAgent.ts ×1codexAgent.ts ×1 · 10 introduced LOCcodexAgent.ts ×1codexAgent.ts ×8 · 19 introduced LOCcodexAgent.ts ×8codexAgent.ts ×158 · 1640 introduced LOCcodexAgent.ts ×158codexClientCustomizations.ts ×1 · 10 introduced LOCcodexClientCustomization…codexClientCustomizations.test|title=codexClientCustomizations codexMcpServersFromPlugins maps stdio + http, stringifies env, and maps headers|occurrence=1 · 0 introduced LOCcodexClientCustomization…codexClientCustomizations.ts ×1 · 10 introduced LOCcodexClientCustomization…codexClientCustomizations.ts ×2 · 4 introduced LOCcodexClientCustomization…codexClientCustomizations.ts ×2 · 20 introduced LOCcodexClientCustomization…codexClientCustomizations.ts ×1 · 2 introduced LOCcodexClientCustomization…codexClientCustomizations.ts ×4 · 8 introduced LOCcodexClientCustomization…codexClientCustomizations.ts ×7 · 26 introduced LOCcodexClientCustomization…codexClientCustomizations.ts ×13 · 98 introduced LOCcodexClientCustomization…codexMcpServers.test|title=codexMcpServers codexMcpServersFromConfig maps stdio + http servers, stringifies env, and maps headers to http_headers|occurrence=1 · 0 introduced LOCcodexMcpServers.test|tit…cwd from untrusted config|occurrence=1 · 0 introduced LOCcwd from untrusted confi…headers and command-only stdio|occurrence=1 · 0 introduced LOCheaders and command-only…codexMcpServers.ts ×1 · 2 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×2 · 4 introduced LOCcodexMcpServers.ts ×2codexMcpServers.ts ×4 · 10 introduced LOCcodexMcpServers.ts ×4spaces (per-thread JSON keys, not `-c` override keys)|occurrence=1 · 0 introduced LOCspaces (per-thread JSON …codexMcpServers.ts ×1 · 2 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×1 · 2 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×3 · 12 introduced LOCcodexMcpServers.ts ×3codexMcpServers.ts ×3 · 5 introduced LOCcodexMcpServers.ts ×3codexMcpServers.ts ×2 · 5 introduced LOCcodexMcpServers.ts ×2codexMcpServers.ts ×1 · 1 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×3 · 20 introduced LOCcodexMcpServers.ts ×3codexMcpServers.ts ×6 · 14 introduced LOCcodexMcpServers.ts ×6codexMcpServers.ts ×2 · 12 introduced LOCcodexMcpServers.ts ×2codexMcpServers.ts ×1 · 7 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×1 · 11 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×1 · 2 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×1 · 2 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×1 · 7 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×2 · 6 introduced LOCcodexMcpServers.ts ×2codexMcpServers.ts ×2 · 15 introduced LOCcodexMcpServers.ts ×2codexMcpServers.ts ×2 · 4 introduced LOCcodexMcpServers.ts ×2codexMcpServers.ts ×1 · 5 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×1 · 10 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×1 · 4 introduced LOCcodexMcpServers.ts ×1codexMcpServers.ts ×17 · 194 introduced LOCcodexMcpServers.ts ×17codexClientCustomizations.test|title=codexClientCustomizations codexMcpServersFromPlugins de-duplicates server names (first wins) and omits empties|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexClientCustomizations.test|title=codexClientCustomizations codexMcpServersFromPlugins de-duplicates server names (first wins) and omits empties|occurrence=1codexClientCustomization…codexClientCustomizations.test|title=codexClientCustomizations codexMcpServersFromPlugins maps stdio + http, stringifies env, and maps headers|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexClientCustomizations.test|title=codexClientCustomizations codexMcpServersFromPlugins maps stdio + http, stringifies env, and maps headers|occurrence=1codexClientCustomization…codexClientCustomizations.test|title=codexClientCustomizations codexSkillRootsFromPlugins returns the skills root (dirname twice), deduped and sorted|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexClientCustomizations.test|title=codexClientCustomizations codexSkillRootsFromPlugins returns the skills root (dirname twice), deduped and sorted|occurrence=1codexClientCustomization…codexClientCustomizations.test|title=codexClientCustomizations enabledPlugins excludes disabled and unparsed plugins; merge dedupes by id (first client wins)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexClientCustomizations.test|title=codexClientCustomizations enabledPlugins excludes disabled and unparsed plugins; merge dedupes by id (first client wins)|occurrence=1codexClientCustomization…codexClientCustomizations.test|title=codexClientCustomizations removeClient drops a client and setEnabled reports whether it changed|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexClientCustomizations.test|title=codexClientCustomizations removeClient drops a client and setEnabled reports whether it changed|occurrence=1codexClientCustomization…codexClientCustomizations.test|title=codexClientCustomizations toCustomizations folds parsed children and applies the enablement overlay|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexClientCustomizations.test|title=codexClientCustomizations toCustomizations folds parsed children and applies the enablement overlay|occurrence=1codexClientCustomization…auth phrasing, not generic failures|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers MCP authentication helpers codexStartupErrorNeedsAuth matches login/auth phrasing, not generic failures|occurrence=1auth phrasing, not gener…codexMcpServers.test|title=codexMcpServers MCP authentication helpers injectCodexMcpAuthTokens adds a bearer header for http servers with a token, leaving others intact|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers MCP authentication helpers injectCodexMcpAuthTokens adds a bearer header for http servers with a token, leaving others intact|occurrence=1codexMcpServers.test|tit…codexMcpServers.test|title=codexMcpServers MCP authentication helpers injectCodexMcpAuthTokens returns the input unchanged when there are no tokens|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers MCP authentication helpers injectCodexMcpAuthTokens returns the input unchanged when there are no tokens|occurrence=1codexMcpServers.test|tit…codexMcpServers.test|title=codexMcpServers MCP authentication helpers injectCodexMcpAuthTokens strips a pre-existing case-insensitive authorization header|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers MCP authentication helpers injectCodexMcpAuthTokens strips a pre-existing case-insensitive authorization header|occurrence=1codexMcpServers.test|tit…codexMcpServers.test|title=codexMcpServers MCP authentication helpers normalizeCodexMcpResourceUrl strips fragment + trailing slashes; undefined for non-URL|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers MCP authentication helpers normalizeCodexMcpResourceUrl strips fragment + trailing slashes; undefined for non-URL|occurrence=1codexMcpServers.test|tit…codexMcpServers.test|title=codexMcpServers buildCodexMcpReadResult answers read methods from cache and defers the rest|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers buildCodexMcpReadResult answers read methods from cache and defers the rest|occurrence=1codexMcpServers.test|tit…codexMcpServers.test|title=codexMcpServers codexMcpListToInventory + inventoryToSdkServers build a Ready snapshot|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers codexMcpListToInventory + inventoryToSdkServers build a Ready snapshot|occurrence=1codexMcpServers.test|tit…spaces (per-thread JSON keys, not `-c` override keys)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers codexMcpServersFromConfig keeps server names with dots/spaces (per-thread JSON keys, not `-c` override keys)|occurrence=1spaces (per-thread JSON …codexMcpServers.test|title=codexMcpServers codexMcpServersFromConfig maps stdio + http servers, stringifies env, and maps headers to http_headers|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers codexMcpServersFromConfig maps stdio + http servers, stringifies env, and maps headers to http_headers|occurrence=1codexMcpServers.test|tit…headers and command-only stdio|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers codexMcpServersFromConfig omits empty args/env/headers and command-only stdio|occurrence=1headers and command-only… empty config|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers codexMcpServersFromConfig returns empty for undefined / empty config|occurrence=1 empty config|occurrence…cwd from untrusted config|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers codexMcpServersFromConfig sanitizes non-string args/env/headers/cwd from untrusted config|occurrence=1cwd from untrusted confi… unsupported entries|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers codexMcpServersFromConfig skips malformed / unsupported entries|occurrence=1 unsupported entries|occ…codexMcpServers.test|title=codexMcpServers codexMcpToolsChanged detects tool-set changes by name|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers codexMcpToolsChanged detects tool-set changes by name|occurrence=1codexMcpServers.test|tit…codexMcpServers.test|title=codexMcpServers codexToolMapToArray flattens and name-sorts, dropping holes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers codexToolMapToArray flattens and name-sorts, dropping holes|occurrence=1codexMcpServers.test|tit…codexMcpServers.test|title=codexMcpServers translateCodexMcpStartupState maps every lifecycle state|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexMcpServers.test|title=codexMcpServers translateCodexMcpStartupState maps every lifecycle state|occurrence=1codexMcpServers.test|tit…codexModelRefresh.test|title=CodexAgent model refresh keeps the last known-good models when a periodic refresh fails|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexModelRefresh.test|title=CodexAgent model refresh keeps the last known-good models when a periodic refresh fails|occurrence=1codexModelRefresh.test|t…codexPackagePaths.test|title=codex package paths codexBinaryTriple every suffix produced by codexPackageSuffix maps to a rust target triple|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexPackagePaths.test|title=codex package paths codexBinaryTriple every suffix produced by codexPackageSuffix maps to a rust target triple|occurrence=1codexPackagePaths.test|t…codexPackagePaths.test|title=codex package paths codexBinaryTriple returns undefined for unknown suffixes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexPackagePaths.test|title=codex package paths codexBinaryTriple returns undefined for unknown suffixes|occurrence=1codexPackagePaths.test|t…codexPackagePaths.test|title=codex package paths codexPackageSuffix every supported (platform, arch) returns the npm optionalDependencies suffix|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexPackagePaths.test|title=codex package paths codexPackageSuffix every supported (platform, arch) returns the npm optionalDependencies suffix|occurrence=1codexPackagePaths.test|t…codexPackagePaths.test|title=codex package paths codexPackageSuffix never returns a -musl suffix on Linux (Codex is statically musl-linked)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexPackagePaths.test|title=codex package paths codexPackageSuffix never returns a -musl suffix on Linux (Codex is statically musl-linked)|occurrence=1codexPackagePaths.test|t…codexPackagePaths.test|title=codex package paths codexPackageSuffix returns undefined for unsupported platforms and architectures|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexPackagePaths.test|title=codex package paths codexPackageSuffix returns undefined for unsupported platforms and architectures|occurrence=1codexPackagePaths.test|t…codex resolves|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexPackagePaths.test|title=codex package paths resolveCodexDevSdkRoot returns the directory containing node_modules when @openai/codex resolves|occurrence=1codex resolves|occurrenc…codexPackagePaths.test|title=codex package paths resolveCodexDevSdkRoot returns undefined when resolution throws (e.g. built product without the devDependency)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexPackagePaths.test|title=codex package paths resolveCodexDevSdkRoot returns undefined when resolution throws (e.g. built product without the devDependency)|occurrence=1codexPackagePaths.test|t…codexSessionConfigKeys.test|title=codexSessionConfigKeys expands permissions presets and falls back to legacy axes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexSessionConfigKeys.test|title=codexSessionConfigKeys expands permissions presets and falls back to legacy axes|occurrence=1codexSessionConfigKeys.t…codexSessionConfigKeys.test|title=codexSessionConfigKeys inverts presets and migrates legacy axes without escalating|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexSessionConfigKeys.test|title=codexSessionConfigKeys inverts presets and migrates legacy axes without escalating|occurrence=1codexSessionConfigKeys.t…codexSessionConfigKeys.test|title=codexSessionConfigKeys narrows valid values and rejects invalid values|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexSessionConfigKeys.test|title=codexSessionConfigKeys narrows valid values and rejects invalid values|occurrence=1codexSessionConfigKeys.t…codexSessionConfigKeys.test|title=codexSessionConfigKeys resolveSessionConfig exposes a single permissions-preset chip|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexSessionConfigKeys.test|title=codexSessionConfigKeys resolveSessionConfig exposes a single permissions-preset chip|occurrence=1codexSessionConfigKeys.t…codexSessionConfigKeys.test|title=codexSessionConfigKeys resolveSessionConfig preserves legacy read-only permissions on restore|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/node/codex/codexSessionConfigKeys.test|title=codexSessionConfigKeys resolveSessionConfig preserves legacy read-only permissions on restore|occurrence=1codexSessionConfigKeys.t…Focused file · src/vs/platform/agentHost/node/codex/codexMcpServers.ts · 358 LOCcodex/codexMcpServers.ts

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- codexMcpServers.ts ×17
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { McpServerType, type IMcpServerConfiguration } from '../../../mcp/common/mcpPlatformTypes.js';
7 > import { McpServerStatus, type McpServerState } from '../../common/state/protocol/channels-session/state.js';
8 > import type { ISdkMcpServer } from '../shared/mcpCustomizationController.js';
9 > import type { McpServerStartupState } from './protocol/generated/v2/McpServerStartupState.js';
10 > import type { McpServerStatus as CodexMcpServerStatus } from './protocol/generated/v2/McpServerStatus.js';
11 > import type { Resource } from './protocol/generated/Resource.js';
12 > import type { ResourceTemplate } from './protocol/generated/ResourceTemplate.js';
13 > import type { Tool } from './protocol/generated/Tool.js';
14 >
15 > /**
16 > * Cached inventory entry for a single MCP server reported by the codex
17 > * app-server. {@link state} drives the AHP customization surface while
18 > * {@link tools} / {@link resources} / {@link resourceTemplates} back the
19 > * read-only `tools/list`, `resources/list` and `resources/templates/list`
20 > * MCP methods so the host can answer them from cache without
21 > * round-tripping to codex.
22 > */
23 > export interface ICodexMcpServerEntry {
24 > readonly state: McpServerState;
25 > readonly tools: readonly Tool[];
26 > readonly resources: readonly Resource[];
27 > readonly resourceTemplates: readonly ResourceTemplate[];
28 > }
29 >
30 > /**
31 > * Translates a codex `mcpServer/startupStatus/updated` lifecycle state
32 > * into the AHP {@link McpServerState} union.
33 > *
34 > * V1 scope: codex's auth states are not surfaced as
35 > * {@link McpServerStatus.AuthRequired}; a connected server is reported as
36 > * {@link McpServerStatus.Ready} regardless of `authStatus`.
37 > */
38 > export function translateCodexMcpStartupState(status: McpServerStartupState, error: string | null | undefined): McpServerState {
39 > switch (status) { codexMcpServers.ts ×2
40 > case 'ready':
41 > return { kind: McpServerStatus.Ready };
42 > case 'starting':
43 > return { kind: McpServerStatus.Starting };
44 > case 'failed':
45 > return {
46 > kind: McpServerStatus.Error,
47 > error: { errorType: 'mcp-server-failed', message: error ?? 'MCP server failed to start' },
48 > };
49 > case 'cancelled':
50 > return { kind: McpServerStatus.Stopped };
51 > default:
52 return { kind: McpServerStatus.Stopped };
54 > }
56 > /**
57 > * Flattens the codex `McpServerStatus.tools` map (`{ [name]: Tool }`)
58 > * into a name-sorted array, dropping any holes the map type allows.
59 > */
60 > export function codexToolMapToArray(tools: CodexMcpServerStatus['tools']): Tool[] {
61 > const out: Tool[] = []; codexMcpServers.ts ×1
62 > for (const key of Object.keys(tools)) {
63 > const tool = tools[key];
64 > if (tool) {
65 > out.push(tool);
66 > }
67 > }
68 > out.sort((a, b) => a.name.localeCompare(b.name));
69 > return out;
70 > }
72 > /**
73 > * Builds an {@link ICodexMcpServerEntry} from a codex `mcpServerStatus/list`
74 > * entry. Servers returned by `mcpServerStatus/list` are connected and
75 > * serving, so they map to {@link McpServerStatus.Ready}.
76 > */
77 > export function codexMcpStatusToEntry(status: CodexMcpServerStatus): ICodexMcpServerEntry {
78 > return { codexMcpServers.ts ×1
79 > state: { kind: McpServerStatus.Ready },
80 > tools: codexToolMapToArray(status.tools),
81 > resources: status.resources,
82 > resourceTemplates: status.resourceTemplates,
83 > };
84 > }
86 > /**
87 > * Builds a name-keyed inventory snapshot from a codex `mcpServerStatus/list`
88 > * response page (or the concatenation of all paginated pages).
89 > */
90 > export function codexMcpListToInventory(data: readonly CodexMcpServerStatus[]): Map<string, ICodexMcpServerEntry> {
91 > const inventory = new Map<string, ICodexMcpServerEntry>(); codexMcpServers.ts ×2
92 > for (const status of data) {
93 > inventory.set(status.name, codexMcpStatusToEntry(status));
94 > }
95 > return inventory;
96 > }
98 > /**
99 > * Projects an inventory snapshot to the SDK-neutral
100 > * {@link ISdkMcpServer} list the {@link McpCustomizationController}
101 > * consumes (name + state only — tool/resource payloads stay in the
102 > * inventory and back {@link buildCodexMcpReadResult}).
103 > */
104 > export function inventoryToSdkServers(inventory: ReadonlyMap<string, ICodexMcpServerEntry>): ISdkMcpServer[] {
105 > const out: ISdkMcpServer[] = []; codexMcpServers.ts ×2
106 > for (const [name, entry] of inventory) {
107 > out.push({ name, state: entry.state });
108 > }
109 > return out;
110 > }
112 > /**
113 > * Answers the read-only MCP methods (`tools/list`, `resources/list`,
114 > * `resources/templates/list`) from a cached inventory entry without a
115 > * round-trip to codex. Returns `{ handled: false }` for any other method
116 > * so the caller can forward it as an RPC (`tools/call`, `resources/read`)
117 > * or reject it.
118 > */
119 > export function buildCodexMcpReadResult(method: string, entry: ICodexMcpServerEntry): { readonly handled: true; readonly result: unknown } | { readonly handled: false } {
120 > switch (method) { codexMcpServers.ts ×1
121 > case 'tools/list':
122 > return { handled: true, result: { tools: entry.tools } };
123 > case 'resources/list':
124 > return { handled: true, result: { resources: entry.resources } };
125 > case 'resources/templates/list':
126 > return { handled: true, result: { resourceTemplates: entry.resourceTemplates } };
127 > default:
128 > return { handled: false };
129 > }
130 > }
132 > /**
133 > * Whether two inventory entries expose a different tool set (compared by
134 > * name). Drives the decision to fire `notifications/tools/list_changed`.
135 > */
136 > export function codexMcpToolsChanged(previous: ICodexMcpServerEntry | undefined, next: ICodexMcpServerEntry | undefined): boolean {
137 > const a = (previous?.tools ?? []).map(t => t.name).sort(); codexMcpServers.ts ×1
138 > const b = (next?.tools ?? []).map(t => t.name).sort();
139 > if (a.length !== b.length) {
140 > return true;
141 > }
142 > return a.some((name, i) => name !== b[i]);
143 > }
145 > // #region MCP server config → codex per-thread `config.mcp_servers`
146 > //
147 > // Codex's `thread/start.config` dict is applied as per-thread config overrides
148 > // that *merge* with (rather than replace) the user's global
149 > // `~/.codex/config.toml` (verified against the real app-server). We inject the
150 > // workbench's configured MCP servers (the root `mcpServers` config, keyed by
151 > // server name) via `config.mcp_servers` so codex launches them for that
152 > // thread — the same set Copilot passes to its SDK via
153 > // `toSdkMcpServersFromConfigMap`. Feeding them per-thread (rather than as
154 > // process-global `-c` spawn overrides) means each new session picks up the
155 > // current config without restarting the shared app-server.
156 > //
157 > // The codex MCP config schema (`codex-rs/config/src/mcp_types.rs`,
158 > // `RawMcpServerConfig`) infers the transport from the presence of `command`
159 > // (stdio) vs `url` (streamable http) and has no `type` field, so we drop the
160 > // workbench `type` discriminator and map `headers` → `http_headers`.
161 >
162 > /**
163 > * The codex JSON shape for one MCP server inside `thread/start.config.mcp_servers`.
164 > */
165 > export interface ICodexMcpServerConfigJson {
166 > command?: string;
167 > args?: readonly string[];
168 > env?: Record<string, string>;
169 > cwd?: string;
170 > url?: string;
171 > http_headers?: Record<string, string>;
172 > }
173 >
174 > /**
175 > * Narrows an untrusted root-config value to a supported
176 > * {@link IMcpServerConfiguration}: a `stdio` server with a string `command`,
177 > * or an `http` server with a string `url`. Mirrors Copilot's
178 > * `isSupportedMcpServerConfiguration` so a malformed entry can't surface as a
179 > * `command`/`url: undefined` server.
180 > */
181 > export function isSupportedMcpServerConfiguration(value: unknown): value is IMcpServerConfiguration {
182 > if (!value || typeof value !== 'object') { codexMcpServers.ts ×3
183 > return false; codexMcpServers.ts ×2
184 > }
185 > const candidate = value as { type?: unknown; command?: unknown; url?: unknown }; codexMcpServers.ts ×3
186 > if (candidate.type === McpServerType.LOCAL) {
187 > return typeof candidate.command === 'string';
188 > }
189 > if (candidate.type === McpServerType.REMOTE) {
190 > return typeof candidate.url === 'string';
191 > }
192 > return false; codexMcpServers.ts ×2
193 > }
195 > /**
196 > * Coerces a record's values to strings (codex's `env`/`http_headers` are
197 > * `Map<string, string>`), dropping `null`/`undefined`. The root `mcpServers`
198 > * config is user-authored and only loosely schema-validated, so a stray
199 > * non-string (e.g. a numeric header value) is coerced here rather than passed
200 > * through — an un-coerced value can make codex reject the whole per-thread
201 > * config, disabling every server for the session.
202 > */
203 > function toCodexStringRecord(record: Record<string, unknown> | undefined): Record<string, string> { codexMcpServers.ts ×6
204 > const result: Record<string, string> = {};
205 > if (!record) {
206 > return result; codexMcpServers.ts ×1
207 > }
208 > for (const [key, value] of Object.entries(record)) { codexMcpServers.ts ×3
209 > if (value !== null && value !== undefined) { codexMcpServers.ts ×4
210 > result[key] = String(value);
211 > }
212 > }
213 > return result; codexMcpServers.ts ×3
214 > }
216 > /** Coerces command args to a string array (codex's `args` is `Vec<String>`), dropping `null`/`undefined`. */
217 > function toCodexStringArray(values: readonly unknown[] | undefined): string[] { codexMcpServers.ts ×6
218 > if (!Array.isArray(values)) {
219 > return []; codexMcpServers.ts ×1
220 > }
221 > return values.filter(v => v !== null && v !== undefined).map(v => String(v)); codexMcpServers.ts ×3
222 > }
224 > /**
225 > * Converts one supported MCP server configuration into codex's JSON shape.
226 > * Optional fields (`args`, `env`, `cwd`, `headers`) come from user-authored
227 > * config that the root schema does not deeply validate, so each is sanitized
228 > * (coerced to the string shapes codex requires, dropping holes) rather than
229 > * trusted, so a single malformed entry can't make codex reject the config.
230 > */
231 > export function toCodexMcpServerJson(config: IMcpServerConfiguration): ICodexMcpServerConfigJson {
232 > if (config.type === McpServerType.LOCAL) { codexMcpServers.ts ×6
233 > const out: ICodexMcpServerConfigJson = { command: config.command };
234 > const args = toCodexStringArray(config.args);
235 > if (args.length > 0) {
236 > out.args = args; codexMcpServers.ts ×4
237 > }
238 > const env = toCodexStringRecord(config.env); codexMcpServers.ts ×6
239 > if (Object.keys(env).length > 0) {
240 > out.env = env; codexMcpServers.ts ×4
241 > }
242 > if (typeof config.cwd === 'string') { codexMcpServers.ts ×6
243 > out.cwd = config.cwd; codexMcpServers.ts ×1
244 > }
245 > return out; codexMcpServers.ts ×6
246 > }
247 > const out: ICodexMcpServerConfigJson = { url: config.url }; codexMcpServers.ts ×2
248 > const headers = toCodexStringRecord(config.headers);
249 > if (Object.keys(headers).length > 0) {
250 > out.http_headers = headers; codexMcpServers.ts ×4
251 > }
252 > return out; codexMcpServers.ts ×2
253 > }
255 > /**
256 > * Converts the workbench root `mcpServers` config (server name →
257 > * {@link IMcpServerConfiguration}) into the `mcp_servers` object codex accepts
258 > * in `thread/start.config`. Unsupported/malformed entries are skipped so a bad
259 > * entry can't surface as a `command`/`url: undefined` server. Returns an empty
260 > * object when nothing is configured.
261 > */
262 > export function codexMcpServersFromConfig(servers: Record<string, unknown> | undefined): Record<string, ICodexMcpServerConfigJson> {
263 > const out: Record<string, ICodexMcpServerConfigJson> = {}; codexMcpServers.ts ×2
264 > for (const [name, config] of Object.entries(servers ?? {})) {
265 > if (isSupportedMcpServerConfiguration(config)) { codexMcpServers.ts ×3
266 > out[name] = toCodexMcpServerJson(config);
267 > }
268 > }
269 > return out; codexMcpServers.ts ×2
270 > }
272 > // #endregion
273 >
274 > // #region MCP server authentication (reuse the workbench OAuth path)
275 > //
276 > // codex won't expose an OAuth-gated http MCP server's tools until it is
277 > // authenticated (it reports a `failed` startup with a "not logged in" error).
278 > // Rather than drive codex's own `mcpServer/oauth/login` browser flow, we reuse
279 > // the *same* mechanism the Copilot agent uses: report the server as
280 > // `McpServerStatus.AuthRequired` so the workbench acquires an OAuth bearer
281 > // token (VS Code dynamic client registration), then inject that token into the
282 > // server's per-thread `http_headers.Authorization`. Verified against the real
283 > // codex binary: it forwards `http_headers` on every MCP HTTP request, so a
284 > // workbench-acquired bearer authenticates the connection.
285 >
286 > /**
287 > * Canonicalizes an MCP server URL for matching a workbench-acquired token
288 > * (keyed by the OAuth `resource`) against a configured server. Mirrors the
289 > * Copilot agent's normalization: strips the fragment and any trailing slashes
290 > * from the path. Returns `undefined` for a non-URL value (e.g. a stdio server).
291 > */
292 > export function normalizeCodexMcpResourceUrl(value: string): string | undefined {
293 > if (!URL.canParse(value)) { codexMcpServers.ts ×2
294 > return undefined; codexMcpServers.ts ×1
295 > }
296 > const url = new URL(value); codexMcpServers.ts ×2
297 > url.hash = '';
298 > url.pathname = url.pathname.replace(/\/+$/, '');
299 > return url.href;
300 > }
302 > /**
303 > * Whether a codex `mcpServer/startupStatus/updated` `failed` error indicates
304 > * the server needs authentication (rather than a generic crash), so it should
305 > * surface as {@link McpServerStatus.AuthRequired} (workbench "Authenticate"
306 > * affordance) instead of a fatal error. codex has no structured auth state on
307 > * this notification, so this matches its human-readable "not logged in" /
308 > * "run `codex mcp login`" phrasing and the standard OAuth challenge vocabulary.
309 > */
310 > export function codexStartupErrorNeedsAuth(error: string | null | undefined): boolean {
311 > if (!error) { codexMcpServers.ts ×1
312 > return false;
313 > }
314 > return /not logged in|mcp login|log in to|unauthori[sz]ed|requires? (?:authentication|authorization|login)|\b401\b/i.test(error);
315 > }
317 > /**
318 > * Returns a copy of `servers` with `Authorization: Bearer <token>` injected
319 > * into the `http_headers` of every http server whose (normalized) URL has a
320 > * token in `tokensByNormalizedUrl`. stdio servers and servers without a token
321 > * are passed through unchanged. Any existing authorization header is removed
322 > * first -- case-insensitively, since HTTP header names are case-insensitive and
323 > * a configured lowercase `authorization` would otherwise coexist with the
324 > * injected value and leave a stale credential in the payload.
325 > */
326 > export function injectCodexMcpAuthTokens(
327 > servers: Record<string, ICodexMcpServerConfigJson>, codexMcpServers.ts ×1
328 > tokensByNormalizedUrl: ReadonlyMap<string, string>,
329 > ): Record<string, ICodexMcpServerConfigJson> {
330 > if (tokensByNormalizedUrl.size === 0) {
331 > return servers; codexMcpServers.ts ×1
332 > }
333 > const out: Record<string, ICodexMcpServerConfigJson> = {}; codexMcpServers.ts ×3
334 > for (const [name, server] of Object.entries(servers)) {
335 > const normalized = server.url !== undefined ? normalizeCodexMcpResourceUrl(server.url) : undefined;
336 > const token = normalized !== undefined ? tokensByNormalizedUrl.get(normalized) : undefined;
337 > out[name] = token !== undefined
338 > ? { ...server, http_headers: { ...withoutAuthorizationHeaders(server.http_headers), Authorization: `Bearer ${token}` } }
339 > : server; codexMcpServers.ts ×1
341 > return out;
342 > }
344 > /** Drops any header whose name is `authorization` (case-insensitive). */
345 > function withoutAuthorizationHeaders(headers: Record<string, string> | undefined): Record<string, string> { codexMcpServers.ts ×3
346 > const out: Record<string, string> = {};
347 > if (headers) {
348 > for (const [key, value] of Object.entries(headers)) {
349 > if (key.toLowerCase() !== 'authorization') {
350 > out[key] = value;
351 > }
352 > }
353 > }
354 > return out;
355 > }
357 > // #endregion
358