Skip to content

Commit fc8208a

Browse files
committed
refactor(env): extract MCP env vars into per-var modules under src/env
cmd-mcp was reading 7 environment variables directly via process env lookups. Move each to its own module under src/env, exposing a getter that reads at call time so tests that mutate process env between invocations see the latest value. New modules: - mcp-http-mode.mts → getMcpHttpMode() - mcp-port.mts → getMcpPort() - socket-oauth-issuer.mts → getSocketOauthIssuer() - socket-oauth-introspection-client-id.mts → getSocketOauthIntrospectionClientId() - socket-oauth-introspection-client-secret.mts → getSocketOauthIntrospectionClientSecret() - socket-oauth-required-scopes.mts → getSocketOauthRequiredScopes() - trust-proxy.mts → getTrustProxy() cmd-mcp now imports the typed getters instead of poking process env directly. All 269 existing mcp unit tests still pass; the load-time constant pattern would have broken the runtime mutation tests rely on.
1 parent 0c0f371 commit fc8208a

8 files changed

Lines changed: 121 additions & 14 deletions

packages/cli/src/commands/mcp/cmd-mcp.mts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
import { handleMcp } from './handle-mcp.mts'
2+
import { getMcpHttpMode } from '../../env/mcp-http-mode.mts'
3+
import { getMcpPort } from '../../env/mcp-port.mts'
4+
import { getSocketOauthIntrospectionClientId } from '../../env/socket-oauth-introspection-client-id.mts'
5+
import { getSocketOauthIntrospectionClientSecret } from '../../env/socket-oauth-introspection-client-secret.mts'
6+
import { getSocketOauthIssuer } from '../../env/socket-oauth-issuer.mts'
7+
import { getSocketOauthRequiredScopes } from '../../env/socket-oauth-required-scopes.mts'
8+
import { getTrustProxy } from '../../env/trust-proxy.mts'
29
import { commonFlags } from '../../flags.mts'
310
import { defineFlags } from '../../meow.mts'
411
import { meowOrExit } from '../../utils/cli/with-subcommands.mjs'
@@ -130,38 +137,31 @@ export async function run(
130137
parentName,
131138
})
132139

133-
const envHttp = process.env['MCP_HTTP_MODE'] === 'true'
134-
const http = cli.flags.http || envHttp
140+
const http = cli.flags.http || getMcpHttpMode()
135141

136142
const portFlag = cli.flags.port
137143
const portRaw =
138144
portFlag && portFlag > 0
139145
? portFlag
140-
: Number.parseInt(process.env['MCP_PORT'] || `${DEFAULT_PORT}`, 10)
146+
: Number.parseInt(getMcpPort() || `${DEFAULT_PORT}`, 10)
141147
const port = Number.isFinite(portRaw) && portRaw > 0 ? portRaw : DEFAULT_PORT
142148

143-
const oauthIssuer =
144-
cli.flags['oauth-issuer'] ||
145-
process.env['SOCKET_OAUTH_ISSUER'] ||
146-
''
149+
const oauthIssuer = cli.flags['oauth-issuer'] || getSocketOauthIssuer() || ''
147150
const oauthClientId =
148151
cli.flags['oauth-client-id'] ||
149-
process.env['SOCKET_OAUTH_INTROSPECTION_CLIENT_ID'] ||
152+
getSocketOauthIntrospectionClientId() ||
150153
''
151154
const oauthClientSecret =
152155
cli.flags['oauth-client-secret'] ||
153-
process.env['SOCKET_OAUTH_INTROSPECTION_CLIENT_SECRET'] ||
156+
getSocketOauthIntrospectionClientSecret() ||
154157
''
155158
const oauthRequiredScopesRaw =
156-
cli.flags['oauth-required-scopes'] ||
157-
process.env['SOCKET_OAUTH_REQUIRED_SCOPES'] ||
158-
''
159+
cli.flags['oauth-required-scopes'] || getSocketOauthRequiredScopes() || ''
159160
const oauthRequiredScopes = oauthRequiredScopesRaw
160161
? parseRequiredScopes(oauthRequiredScopesRaw)
161162
: undefined
162163

163-
const trustProxy =
164-
cli.flags['trust-proxy'] || process.env['TRUST_PROXY'] === 'true'
164+
const trustProxy = cli.flags['trust-proxy'] || getTrustProxy()
165165

166166
await handleMcp({
167167
http,
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* MCP_HTTP_MODE environment variable.
3+
*
4+
* When set to the literal string "true", forces the `socket mcp` command to
5+
* serve over HTTP instead of stdio. Useful in container / CI setups where
6+
* stdio binding isn't available.
7+
*
8+
* Read lazily so tests that mutate process.env after module load see the
9+
* latest value.
10+
*/
11+
12+
import process from 'node:process'
13+
14+
export function getMcpHttpMode(): boolean {
15+
return process.env['MCP_HTTP_MODE'] === 'true'
16+
}

packages/cli/src/env/mcp-port.mts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* MCP_PORT environment variable.
3+
*
4+
* Port the `socket mcp` HTTP server should listen on when running in HTTP
5+
* mode. Empty string when unset; the caller decides the default.
6+
*
7+
* Read lazily so tests that mutate process.env after module load see the
8+
* latest value.
9+
*/
10+
11+
import process from 'node:process'
12+
13+
export function getMcpPort(): string {
14+
return process.env['MCP_PORT'] ?? ''
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* SOCKET_OAUTH_INTROSPECTION_CLIENT_ID environment variable.
3+
*
4+
* Client ID used by the `socket mcp` HTTP server when calling the OAuth
5+
* introspection endpoint. Empty string when unset.
6+
*
7+
* Read lazily so tests that mutate process.env after module load see the
8+
* latest value.
9+
*/
10+
11+
import process from 'node:process'
12+
13+
export function getSocketOauthIntrospectionClientId(): string {
14+
return process.env['SOCKET_OAUTH_INTROSPECTION_CLIENT_ID'] ?? ''
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* SOCKET_OAUTH_INTROSPECTION_CLIENT_SECRET environment variable.
3+
*
4+
* Client secret paired with SOCKET_OAUTH_INTROSPECTION_CLIENT_ID for the
5+
* `socket mcp` OAuth introspection call. Empty string when unset.
6+
*
7+
* Read lazily so tests that mutate process.env after module load see the
8+
* latest value.
9+
*/
10+
11+
import process from 'node:process'
12+
13+
export function getSocketOauthIntrospectionClientSecret(): string {
14+
return process.env['SOCKET_OAUTH_INTROSPECTION_CLIENT_SECRET'] ?? ''
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* SOCKET_OAUTH_ISSUER environment variable.
3+
*
4+
* Issuer URL the `socket mcp` HTTP server uses to validate inbound OAuth
5+
* tokens. Empty string when unset.
6+
*
7+
* Read lazily so tests that mutate process.env after module load see the
8+
* latest value.
9+
*/
10+
11+
import process from 'node:process'
12+
13+
export function getSocketOauthIssuer(): string {
14+
return process.env['SOCKET_OAUTH_ISSUER'] ?? ''
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* SOCKET_OAUTH_REQUIRED_SCOPES environment variable.
3+
*
4+
* Whitespace-separated list of OAuth scopes the `socket mcp` HTTP server
5+
* requires on every inbound token. Empty string when unset (no scope check).
6+
*
7+
* Read lazily so tests that mutate process.env after module load see the
8+
* latest value.
9+
*/
10+
11+
import process from 'node:process'
12+
13+
export function getSocketOauthRequiredScopes(): string {
14+
return process.env['SOCKET_OAUTH_REQUIRED_SCOPES'] ?? ''
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* TRUST_PROXY environment variable.
3+
*
4+
* When set to the literal string "true", the `socket mcp` HTTP server trusts
5+
* X-Forwarded-* headers from upstream proxies (e.g., when running behind a
6+
* load balancer that terminates TLS).
7+
*
8+
* Read lazily so tests that mutate process.env after module load see the
9+
* latest value.
10+
*/
11+
12+
import process from 'node:process'
13+
14+
export function getTrustProxy(): boolean {
15+
return process.env['TRUST_PROXY'] === 'true'
16+
}

0 commit comments

Comments
 (0)