Skip to content

Commit 1c51f74

Browse files
fix(serve): read <data-dir>/mcp_config.json and implement MCPPROXY_DATA_DIR (#1694)
## Description Fixes #1693. `serve` now resolves its config the way the management commands already do (#854, #897, #908): an explicit `--config` wins, then `<data-dir>/mcp_config.json` when it exists, otherwise the legacy discovery (cwd, `$HOME/.mcpproxy`). On a first run with no config anywhere, the default (and its generated API key) is seeded inside the named data dir instead of under `$HOME`. `MCPPROXY_DATA_DIR`, which the docs list but the code never read, now seeds `--data-dir` for every command (an explicit flag still wins). The legacy `MCPPROXY_DATA` keeps its meaning (relocates the data directory only) and loses when both are set. `--help` is unchanged when the variable is set. Changes: `cmd/mcpproxy/main.go` (`implicitDataDirConfigPath`, used by `loadConfig`), `cmd/mcpproxy/path_flag.go`, `internal/config/loader.go` (`EnvDataDir`, `EnvDataDirLegacy`, `DataDirFromEnv`), docs (`installation.md` trap rewritten, env var tables, command reference) and a CHANGELOG entry. Behaviour change, one case: an instance started with `--data-dir X` that relied on the legacy config while a stale `X/mcp_config.json` also exists now reads the one in `X` (noted in the CHANGELOG as a migration). Verified with a built binary: a config with a fixed port and API key in the data dir is used through both `--data-dir` and `MCPPROXY_DATA_DIR`, nothing is created under a clean `$HOME`, and a first run seeds `<data-dir>/mcp_config.json`. ## Testing - [x] I have tested these changes locally - [x] I have added/updated tests that prove my fix is effective or my feature works (`cmd/mcpproxy/serve_data_dir_config_test.go`: data-dir and env read the config, flag beats env, `--config` beats data dir, first run seeds in the data dir, legacy config kept when the data dir has none, env name precedence) - [x] All existing tests pass (`go test ./cmd/mcpproxy ./internal/config ./cmd/release-gate`) --------- Co-authored-by: Algis Dumbris <a.dumbris@gmail.com>
1 parent 5e9c9cd commit 1c51f74

9 files changed

Lines changed: 243 additions & 15 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ Releases follow [Semantic Versioning](https://semver.org/).
178178

179179
### Bug Fixes
180180

181+
- **`serve --data-dir` (and `MCPPROXY_DATA_DIR`) now reads `<data-dir>/mcp_config.json`.** Until now `serve` looked for its config only in the working directory and `$HOME/.mcpproxy` and applied the data dir afterwards, so a config on a mounted volume was ignored and a fresh one (new listen address, new API key) was written under `$HOME`, while every management command already read the config from the data dir. `serve` now resolves it the same way: an explicit `--config` wins, then `<data-dir>/mcp_config.json` if it exists, and on a first run with no config anywhere the default is seeded inside the data dir. `MCPPROXY_DATA_DIR`, documented but never implemented, now works as an alias of `--data-dir` (the legacy `MCPPROXY_DATA` keeps relocating only the data directory, and loses when both are set). If you already export `MCPPROXY_DATA_DIR`, it takes effect for the first time on this upgrade: the data directory, and so the database, search index and socket, move to it. **Migration:** an instance started with `--data-dir X` that relied on the config under `$HOME/.mcpproxy` while a stale `X/mcp_config.json` also exists will now read the one in `X`.
181182
- **audit: a profile tool-policy refusal now names its rule (#1703).** The `authz` audit line for a call refused by a profile's tier cap, deny rule or unannotated-tool policy used to read `reason: other`; it now reads `profile_tier`, `profile_rule` or `profile_unannotated` (`disclosed: true`), matching the `block_reason` of the same call in the activity log. A refusal inside a `code_execution` script used to be written as `token_scope` with `disclosed: false` and now uses the same three reasons. `profile_scope` is unchanged, and `profile_code_execution` / `profile_management` are not audit reasons because those built-in tools are not audited per call. The `reason` enum in `docs/schemas/audit-line-v1.schema.json` gains the three members, so a consumer that validates audit lines against the enum must accept them. (refs #1703)
182183

183184
- **Upgrade no longer admits a server an older release held in quarantine (RC-UPG-001).** A v0.69 restart (including the baseline security scan shortly after startup) wrote a keyless server's un-gated config over its recorded quarantine, while v0.69 kept holding it in memory. On upgrade the config-load admission gate read that record as already admitted and auto-baseline approved all of its tools. The gate now also requires an approval baseline: a server recorded as unquarantined whose tools were never approved is quarantined again for review. Servers with approved tools are unaffected; vetted servers with no tool records (zero-tool servers, servers that never connected while live, servers last vetted before v0.21) are also held once — approve them, or set `"quarantined": false` before upgrading.

‎cmd/mcpproxy/main.go‎

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -793,23 +793,53 @@ func readConfigFile(path string) (*config.Config, error) {
793793
return config.DecodeConfigFile(path)
794794
}
795795

796+
// implicitDataDirConfigPath is the config file `serve` uses when no --config was
797+
// given but a data directory was: <data-dir>/mcp_config.json when it exists, and
798+
// on a first run (no config in the data dir or in the legacy discovery
799+
// locations) the same path so the default config is seeded inside the named
800+
// directory instead of under $HOME. "" keeps the legacy discovery (cwd, then
801+
// $HOME/.mcpproxy).
802+
func implicitDataDirConfigPath() string {
803+
if dataDir == "" {
804+
return ""
805+
}
806+
p := config.GetConfigPath(dataDir)
807+
if _, err := os.Stat(p); err == nil {
808+
return p
809+
}
810+
if !legacyConfigExists() {
811+
return p
812+
}
813+
return ""
814+
}
815+
796816
func loadConfig(cmd *cobra.Command) (*config.Config, *serveConfigSaver, error) {
797817
var cfg *config.Config
798818
var err error
799-
loadedPath := configFile
819+
820+
// A --data-dir (or MCPPROXY_DATA_DIR) names where this instance keeps its
821+
// state, config included: every management command already reads
822+
// <data-dir>/mcp_config.json (GH #854/#897/#908), so `serve` must too or
823+
// the two disagree about the listen address and API key. An explicit
824+
// --config still wins.
825+
configPath := configFile
826+
if configPath == "" {
827+
configPath = implicitDataDirConfigPath()
828+
}
829+
loadedPath := configPath
800830

801831
// Load configuration - use LoadFromFile if config file specified, otherwise use Load
802-
if configFile != "" {
832+
if configPath != "" {
803833
// A named config that has never been written is a FIRST RUN, not an
804834
// error: the relocated tray instance of GH #936 spawns its core with
805835
// --data-dir <root> --config <root>/mcp_config.json against a root
806836
// nothing has used before. The implicit path has always created its
807837
// default here; the explicit one used to exit with "no such file or
808838
// directory" instead, which made that whole flow unusable.
809-
if _, ensureErr := config.EnsureConfigFile(configFile, dataDir); ensureErr != nil {
839+
if _, ensureErr := config.EnsureConfigFile(configPath, dataDir); ensureErr != nil {
810840
return nil, nil, ensureErr
811841
}
812-
cfg, err = config.LoadFromFile(configFile)
842+
cfg, err = config.LoadFromFile(configPath)
813843
} else {
814844
cfg, loadedPath, err = config.LoadWithPath()
815845
}

‎cmd/mcpproxy/path_flag.go‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66

77
"github.com/spf13/cobra"
88
"github.com/spf13/pflag"
9+
10+
"github.com/smart-mcp-proxy/mcpproxy-go/internal/config"
911
)
1012

1113
// nonEmptyPathValue is the pflag.Value behind every --config / --data-dir flag.
@@ -49,5 +51,15 @@ func addConfigFlag(fs *pflag.FlagSet, p *string, usage string) {
4951
// -d/--data-dir flags.
5052
func registerRootPathFlags(root *cobra.Command) {
5153
root.PersistentFlags().VarP(newPathFlag(&configFile), "config", "c", "Configuration file path")
54+
// MCPPROXY_DATA_DIR seeds --data-dir so every command (serve and the
55+
// management commands that find the daemon through the data dir) agrees on
56+
// the directory; an explicit flag still wins because it is parsed later.
57+
if dataDir == "" {
58+
dataDir = config.DataDirFromEnv()
59+
}
5260
root.PersistentFlags().VarP(newPathFlag(&dataDir), "data-dir", "d", "Data directory path (default: ~/.mcpproxy)")
61+
// Keep --help identical whether or not the variable is set.
62+
if f := root.PersistentFlags().Lookup("data-dir"); f != nil {
63+
f.DefValue = ""
64+
}
5365
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/spf13/cobra"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
12+
"github.com/smart-mcp-proxy/mcpproxy-go/internal/config"
13+
)
14+
15+
// serveDataDirEnv isolates $HOME and cwd and clears every variable that could
16+
// relocate the data dir, so a test only sees what it sets itself.
17+
func serveDataDirEnv(t *testing.T) (home string) {
18+
t.Helper()
19+
saveServeGlobals(t)
20+
home = t.TempDir()
21+
t.Setenv("HOME", home)
22+
t.Setenv("USERPROFILE", home)
23+
t.Setenv(config.EnvDataDir, "")
24+
t.Setenv(config.EnvDataDirLegacy, "")
25+
t.Setenv("MCPPROXY_API_KEY", "")
26+
// Seeding a first-run config probes Docker with a `docker info` subprocess
27+
// (2 s timeout). On Windows a probe killed at the deadline can still hold
28+
// the test's working directory for a moment and make t.TempDir cleanup
29+
// fail; with no docker on PATH the probe returns without spawning anything.
30+
t.Setenv("PATH", t.TempDir())
31+
t.Chdir(t.TempDir())
32+
configFile, dataDir = "", ""
33+
return home
34+
}
35+
36+
// `serve --data-dir X` reads X/mcp_config.json (the file the management
37+
// commands already read), instead of $HOME/.mcpproxy.
38+
func TestServeReadsConfigFromDataDir(t *testing.T) {
39+
home := serveDataDirEnv(t)
40+
d := filepath.Join(t.TempDir(), "state")
41+
writeListenConfig(t, filepath.Join(d, "mcp_config.json"), "127.0.0.1:18997")
42+
dataDir = d
43+
44+
cfg, saver, err := loadConfig(newServeFlagTestCmd())
45+
require.NoError(t, err)
46+
assert.Equal(t, "127.0.0.1:18997", cfg.Listen, "config in the data dir was ignored")
47+
assert.Equal(t, d, cfg.DataDir)
48+
assert.Equal(t, filepath.Join(d, "mcp_config.json"), saver.path, "later saves must go to the file that was read")
49+
_, statErr := os.Stat(filepath.Join(home, ".mcpproxy", "mcp_config.json"))
50+
assert.True(t, os.IsNotExist(statErr), "a default config was created under $HOME")
51+
}
52+
53+
// The same through MCPPROXY_DATA_DIR, which registerRootPathFlags turns into
54+
// the --data-dir default.
55+
func TestServeReadsConfigFromDataDirEnv(t *testing.T) {
56+
serveDataDirEnv(t)
57+
d := filepath.Join(t.TempDir(), "state")
58+
writeListenConfig(t, filepath.Join(d, "mcp_config.json"), "127.0.0.1:18996")
59+
t.Setenv(config.EnvDataDir, d)
60+
61+
root := &cobra.Command{Use: "mcpproxy"}
62+
registerRootPathFlags(root)
63+
require.NoError(t, root.ParseFlags(nil))
64+
require.Equal(t, d, dataDir)
65+
assert.Equal(t, "", root.PersistentFlags().Lookup("data-dir").DefValue, "--help must not change when the variable is set")
66+
67+
cfg, _, err := loadConfig(newServeFlagTestCmd())
68+
require.NoError(t, err)
69+
assert.Equal(t, "127.0.0.1:18996", cfg.Listen)
70+
assert.Equal(t, d, cfg.DataDir)
71+
}
72+
73+
// An explicit --data-dir beats MCPPROXY_DATA_DIR.
74+
func TestServeDataDirFlagBeatsEnv(t *testing.T) {
75+
serveDataDirEnv(t)
76+
envDir := filepath.Join(t.TempDir(), "env")
77+
flagDir := filepath.Join(t.TempDir(), "flag")
78+
writeListenConfig(t, filepath.Join(envDir, "mcp_config.json"), "127.0.0.1:18001")
79+
writeListenConfig(t, filepath.Join(flagDir, "mcp_config.json"), "127.0.0.1:18002")
80+
t.Setenv(config.EnvDataDir, envDir)
81+
82+
root := &cobra.Command{Use: "mcpproxy"}
83+
registerRootPathFlags(root)
84+
require.NoError(t, root.ParseFlags([]string{"--data-dir", flagDir}))
85+
86+
cfg, _, err := loadConfig(newServeFlagTestCmd())
87+
require.NoError(t, err)
88+
assert.Equal(t, "127.0.0.1:18002", cfg.Listen)
89+
assert.Equal(t, flagDir, cfg.DataDir)
90+
}
91+
92+
// An explicit --config still wins over a config in the data dir.
93+
func TestServeConfigFlagBeatsDataDirConfig(t *testing.T) {
94+
serveDataDirEnv(t)
95+
d := filepath.Join(t.TempDir(), "state")
96+
writeListenConfig(t, filepath.Join(d, "mcp_config.json"), "127.0.0.1:18001")
97+
explicit := filepath.Join(t.TempDir(), "explicit.json")
98+
writeListenConfig(t, explicit, "127.0.0.1:18003")
99+
dataDir, configFile = d, explicit
100+
101+
cfg, _, err := loadConfig(newServeFlagTestCmd())
102+
require.NoError(t, err)
103+
assert.Equal(t, "127.0.0.1:18003", cfg.Listen)
104+
}
105+
106+
// First run: nothing anywhere, so the default config (and its generated API
107+
// key) is seeded inside the named data dir, not under $HOME.
108+
func TestServeFirstRunSeedsConfigInDataDir(t *testing.T) {
109+
home := serveDataDirEnv(t)
110+
d := filepath.Join(t.TempDir(), "fresh")
111+
dataDir = d
112+
113+
cfg, _, err := loadConfig(newServeFlagTestCmd())
114+
require.NoError(t, err)
115+
assert.Equal(t, d, cfg.DataDir)
116+
_, statErr := os.Stat(filepath.Join(d, "mcp_config.json"))
117+
assert.NoError(t, statErr, "default config was not seeded in the data dir")
118+
_, statErr = os.Stat(filepath.Join(home, ".mcpproxy", "mcp_config.json"))
119+
assert.True(t, os.IsNotExist(statErr), "a default config was created under $HOME")
120+
}
121+
122+
// Compatibility: with no config in the data dir but one in the legacy location,
123+
// serve keeps using the legacy file and only relocates the data dir.
124+
func TestServeKeepsLegacyConfigWhenDataDirHasNone(t *testing.T) {
125+
home := serveDataDirEnv(t)
126+
writeListenConfig(t, filepath.Join(home, ".mcpproxy", "mcp_config.json"), "127.0.0.1:18004")
127+
d := filepath.Join(t.TempDir(), "state")
128+
require.NoError(t, os.MkdirAll(d, 0o700))
129+
dataDir = d
130+
131+
cfg, _, err := loadConfig(newServeFlagTestCmd())
132+
require.NoError(t, err)
133+
assert.Equal(t, "127.0.0.1:18004", cfg.Listen)
134+
assert.Equal(t, d, cfg.DataDir)
135+
_, statErr := os.Stat(filepath.Join(d, "mcp_config.json"))
136+
assert.True(t, os.IsNotExist(statErr), "no config should be seeded when a legacy one exists")
137+
}
138+
139+
// MCPPROXY_DATA_DIR is the documented name and beats the legacy MCPPROXY_DATA
140+
// when the loader applies env overrides; the legacy name keeps working alone.
141+
func TestDataDirEnvNames(t *testing.T) {
142+
cases := []struct {
143+
name, newVar, legacy, want string
144+
}{
145+
{"new only", "/n", "", "/n"},
146+
{"legacy only", "", "/l", "/l"},
147+
{"both: new wins", "/n", "/l", "/n"},
148+
}
149+
for _, tc := range cases {
150+
t.Run(tc.name, func(t *testing.T) {
151+
t.Setenv(config.EnvDataDir, tc.newVar)
152+
t.Setenv(config.EnvDataDirLegacy, tc.legacy)
153+
cfg := config.DefaultConfig()
154+
config.ApplyEnvOverrides(cfg)
155+
assert.Equal(t, tc.want, cfg.DataDir)
156+
})
157+
}
158+
}

‎docs/cli/command-reference.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ These flags are available for all commands:
1919
|------|-------------|
2020
| `--config` | Path to configuration file |
2121
| `--log-level` | Log level (debug, info, warn, error) |
22-
| `--data-dir, -d` | Data directory path (default: ~/.mcpproxy) |
22+
| `--data-dir, -d` | Data directory path (default: ~/.mcpproxy). Also the place `serve` reads `mcp_config.json` from when `--config` is not given. Env: `MCPPROXY_DATA_DIR` |
2323
| `--log-to-file` | Enable logging to file in standard OS location |
2424
| `--log-dir` | Custom log directory path (overrides standard OS location) |
2525
| `--help` | Show help for command |

‎docs/configuration.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1867,7 +1867,8 @@ Many configuration options can be overridden via environment variables:
18671867
| `MCPPROXY_TLS_ENABLED` | `tls.enabled` | Enable HTTPS/TLS |
18681868
| `MCPPROXY_TLS_REQUIRE_CLIENT_CERT` | `tls.require_client_cert` | Enable mTLS |
18691869
| `MCPPROXY_CERTS_DIR` | `tls.certs_dir` | Custom certificates directory |
1870-
| `MCPPROXY_DATA` | `data_dir` | Override data directory |
1870+
| `MCPPROXY_DATA_DIR` | `data_dir` | Override data directory; also where `serve` looks for `mcp_config.json` (same as `--data-dir`) |
1871+
| `MCPPROXY_DATA` | `data_dir` | Override data directory only (legacy name; `MCPPROXY_DATA_DIR` wins) |
18711872
| `MCPPROXY_TOOL_RESPONSE_MODE` | `tool_response_mode` | `retrieve_tools` serialization: `full` (default) or `compact` |
18721873
| `MCPPROXY_DIRECT_TOOL_RESPONSE_MODE` | `direct_tool_response_mode` | Direct enumeration surface serialization: `full` (default) or `deferred` |
18731874
| `MCPPROXY_MAX_CONCURRENT_REQUESTS` | `max_concurrent_requests` | Global aggregate cap on concurrent upstream tool calls (`0` disables it). See [Concurrency Limits](#concurrency-limits--request-queueing) |

‎docs/configuration/environment-variables.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ Environment variables are useful for CI/CD environments or temporary overrides d
3131
| `MCPPROXY_LISTEN` | Override listen address | `127.0.0.1:8080` or `:8080` |
3232
| `MCPPROXY_API_KEY` | Set API key for authentication | `my-secret-key` |
3333
| `MCPPROXY_API_KEY_FILE` | Read the API key from a file (absolute path). Set it or `MCPPROXY_API_KEY`, not both; see [API key from a file](#api-key-from-a-file) | `/run/secrets/mcpproxy_api_key` |
34-
| `MCPPROXY_DATA_DIR` | Override data directory | `/var/lib/mcpproxy` |
35-
| `MCPPROXY_DATA` | Override data directory (backward compatibility, prefer `MCPPROXY_DATA_DIR`) | `/var/lib/mcpproxy` |
34+
| `MCPPROXY_DATA_DIR` | Override data directory. Like `--data-dir` (which wins when both are set), it also makes `serve` read `<data-dir>/mcp_config.json` | `/var/lib/mcpproxy` |
35+
| `MCPPROXY_DATA` | Override data directory only (backward compatibility; `MCPPROXY_DATA_DIR` wins when both are set, and the config file is still looked up in the working directory and `~/.mcpproxy`) | `/var/lib/mcpproxy` |
3636

3737
### API key from a file
3838

‎docs/getting-started/installation.md‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -383,11 +383,15 @@ docker run -d --name mcpproxy \
383383
- **State survives restarts, not replacement**: `docker restart` keeps everything. Removing and
384384
re-running the container, upgrading the tag, or rescheduling the pod destroys whatever is not
385385
on the volume — so mount one before you configure anything.
386-
- **Don't use `MCPPROXY_DATA` or a bare `--data-dir` to relocate state.** The data-dir override is
387-
applied after the config file is resolved, so the config in that directory is never read and is
388-
overwritten with a fresh default (rotating the API key) on every boot. Mount the volume at the
389-
default `/root/.mcpproxy` path instead. (`MCPPROXY_DATA_DIR`, referenced elsewhere in the docs,
390-
is not implemented at all.)
386+
- **Relocating state**: `--data-dir` and `MCPPROXY_DATA_DIR` move the data directory and also
387+
decide where `serve` looks for `mcp_config.json` — `<data-dir>/mcp_config.json` is read when it
388+
exists, and seeded there on a first run with no config anywhere — so the config and its API key
389+
live on the same volume as the database. If the data dir has no config but a legacy one exists in
390+
the working directory or `$HOME/.mcpproxy`, `serve` keeps using that legacy file and moves only the
391+
data. An explicit `--config` still wins. The legacy `MCPPROXY_DATA` only relocates the data
392+
directory: with it the config is still looked up in the working directory and `$HOME/.mcpproxy`
393+
(`/root/.mcpproxy` in this image). Releases before this change ignored `<data-dir>/mcp_config.json`
394+
under `serve`, so on those, mount the volume at the default `/root/.mcpproxy` path instead.
391395
- **Port**: the entrypoint is `mcpproxy serve --listen 0.0.0.0:8080`, so the container listens on
392396
`8080` inside the network namespace. The `-p 127.0.0.1:8080:8080` above keeps it reachable only
393397
from the host; drop the `127.0.0.1:` prefix only if you deliberately want it on the network, and

‎internal/config/loader.go‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,21 @@ func SaveConfigToDataDir(cfg *Config) error {
604604
return SaveConfig(cfg, configPath)
605605
}
606606

607+
// Environment variables that name the data directory. EnvDataDir is the
608+
// documented spelling and, like --data-dir, also decides where `serve` looks
609+
// for mcp_config.json (see cmd/mcpproxy loadConfig). EnvDataDirLegacy keeps its
610+
// historical meaning: it relocates the data directory only.
611+
const (
612+
EnvDataDir = "MCPPROXY_DATA_DIR"
613+
EnvDataDirLegacy = "MCPPROXY_DATA"
614+
)
615+
616+
// DataDirFromEnv returns the directory named by MCPPROXY_DATA_DIR, or "" when
617+
// it is unset or blank. It does not consult the legacy MCPPROXY_DATA.
618+
func DataDirFromEnv() string {
619+
return strings.TrimSpace(os.Getenv(EnvDataDir))
620+
}
621+
607622
// GetConfigPath returns the path to the configuration file in the data directory
608623
func GetConfigPath(dataDir string) string {
609624
if dataDir == "" {
@@ -844,8 +859,15 @@ func applyTLSEnvOverrides(cfg *Config) {
844859
value = os.Getenv("MCPPROXY_CERTS_DIR")
845860
envOverride(b, cfg, FieldTLSCertsDir, value != "", value)
846861

847-
// Override data directory from environment (for backward compatibility)
848-
value = os.Getenv("MCPPROXY_DATA")
862+
// Override data directory from environment. MCPPROXY_DATA_DIR is the
863+
// documented name; MCPPROXY_DATA is kept for backward compatibility and
864+
// loses when both are set.
865+
// DataDirFromEnv trims the value, so a whitespace-only MCPPROXY_DATA_DIR
866+
// neither relocates the data dir to " " nor shadows the legacy variable.
867+
value = DataDirFromEnv()
868+
if value == "" {
869+
value = strings.TrimSpace(os.Getenv(EnvDataDirLegacy))
870+
}
849871
envOverride(b, cfg, FieldDataDir, value != "", value)
850872

851873
// Spec 112 FR-003: MCPPROXY_FORWARD_CLIENT_HEADERS=false|0|off disables

0 commit comments

Comments
 (0)