Repository navigation
fix(cli): point packaged Linux installs at the system service (--system) - #1800
Merged
Merged
Conversation
On a deb/rpm install the service runs with /etc/mcpproxy + /var/lib/mcpproxy as the mcpproxy user, but a plain CLI command silently created ~/.mcpproxy/mcp_config.json and reported an empty, unrelated instance (discussion #1678). - Linux-only detection of the packaged layout (service config + shipped systemd unit; an unreadable /etc/mcpproxy still counts). With no --config, --data-dir or user config, management commands no longer create a home config: they exit non-zero naming the service config and the exact `sudo mcpproxy --system ...` command. serve, macOS and Windows unchanged. - Global --system flag: shorthand for the service's --config/--data-dir. The service socket refuses every uid but its own (root included), so in --system mode the socket is used only when it answers; otherwise the CLI uses HTTP with the config's listen + api_key (works with enable_socket:false). Unreadable config -> error naming sudo and the mcpproxy group. Refused with --config/--data-dir and for serve. - doctor and status warn on stderr when mcpproxy.service is active and the CLI reads a different config, with the --system command to run. - status reports and resets the key in the config file actually loaded (it used <data-dir>/mcp_config.json even under --config). - Docs: "Manage the packaged service from the CLI" + --system in the CLI references. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ONFIG Cross-review round 1 (opencode gpt-6.1-sol): - SaveConfig rewrote every config 0600. On a packaged install the service does that on first start (persisting its generated API key), which locked the mcpproxy group out of /etc/mcpproxy/mcp_config.json, and a save under `sudo mcpproxy --system ...` left a root-owned 0600 file the service could not read. A file that already grants group read and nothing else (0640) now keeps that mode; everything else is still written 0600. postinstall restores g+r on the shipped config for upgraded hosts. - The packaged-install guard ignored MCPP_CONFIG, which config.Load honors. - search-servers now reads the config chosen by -c/--config/--system (it always used defaults). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cross-review round 2 (opencode gpt-6.1-sol): - A rename-based save creates a new inode whose group comes from the directory (setgid/BSD) or the process, not from the replaced file, so keeping 0640 could open a config to a group nobody chose. The temp file's group is now checked before the rename; on a mismatch the save writes 0600 as before. Regular files only. - Drop the postinstall chmod/chgrp repair: /etc/mcpproxy is group-writable, so a root-run repair would follow a planted symlink. Docs and the --system permission error give the one-time `sudo chmod 0640` instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…check Cross-review round 3: with exactMode the temp file was created with the final mode (0640) and written before its group was checked, so a member of an unintended group could open it in that window. Create it owner-only and apply the final mode with the post-check chmod. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nstance warning Cross-review round 4: - --system now refuses to run unless the service answers (socket or HTTP). The CLI's standalone/offline fallbacks would otherwise run as the caller: launching the service's stdio upstreams as root, or writing root-owned config/database files the service cannot open (e.g. db compact). The error names systemctl start and the sudo -u mcpproxy form for offline maintenance or an HTTPS (tls.enabled) listener. - The different-instance warning reads MCPP_CONFIG like the loader does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploying mcpproxy-docs with
|
| Latest commit: |
ea8cdcc
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://28827191.mcpproxy-docs.pages.dev |
| Branch Preview URL: | https://fix-linux-packaged-cli-syste.mcpproxy-docs.pages.dev |
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
os.UserHomeDir reads USERPROFILE on Windows, so tests that only set HOME resolved the runner's real home and failed on the Windows build jobs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
📦 Build ArtifactsWorkflow Run: View Run Available Artifacts
How to DownloadOption 1: GitHub Web UI (easiest)
Option 2: GitHub CLI gh run download 41039890419 --repo smart-mcp-proxy/mcpproxy-go
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the CLI confusion on packaged Linux installs reported in discussion #1678. The deb/rpm service runs as
mcpproxywith/etc/mcpproxy/mcp_config.json+/var/lib/mcpproxy. A plainmcpproxy upstream listsilently created~/.mcpproxy/mcp_config.jsonand reported an empty, unrelated instance, and nothing in the product pointed at the service./etc/mcpproxy, which is2770 root:mcpproxy, still counts) and the shippedmcpproxy.serviceunit is present. When a management command has no--config/--data-dirand the user has no config of their own, it exits non-zero with the service config path and the exactsudo mcpproxy --system <same command>to run, plus the group alternative andmcpproxy servefor a separate per-user instance.serve, macOS, Windows, and users who already have~/.mcpproxybehave as before.registrydiscovery keeps working on defaults but no longer creates the file.--systemflag. Shorthand for--config /etc/mcpproxy/mcp_config.json --data-dir /var/lib/mcpproxy. The service socket rejects every uid butmcpproxy's (SO_PEERCRED, root included), so in--systemmode the socket is used only when it actually answers; otherwise the CLI goes over HTTP with the config'slisten+api_key(soenable_socket: falseworks too).--systemrequires the running service: it never falls back to the CLI's standalone/offline paths, which would launch stdio upstreams or write the service's config/DB as root. The error namessudo systemctl start mcpproxyand thesudo -u mcpproxy mcpproxy --config … --data-dir …form for offline maintenance (e.g.db compact) or an HTTPS listener. An unreadable config gives a permission error namingsudoandusermod -aG mcpproxy. Refused together with--config/--data-dir, forserve, and off Linux.0640(internal/config).SaveConfigalways wrote0600; the service does that on first start when it persists its generated API key, which silently locked themcpproxygroup out of/etc/mcpproxy/mcp_config.json. A regular file that already grants group read and nothing else keeps0640, but only when the replacement inode lands in the same group (checked on the temp file, which is created owner-only before the check). Everything else is still written0600. Existing hosts restore group read once withsudo chmod 0640(documented, and named in the--systempermission error); no root-run postinstall repair, since/etc/mcpproxyis group-writable.search-serversreads the config chosen by-c/--config/--system(it always used defaults).MCPP_CONFIGis honoured by the packaged-install guard and the instance warning.doctor/statuswarn (stderr, so-o jsonstays clean) whenmcpproxy.serviceis active and the CLI reads a different config — the case for anyone who already got a~/.mcpproxyfrom older versions — and print the--systemcommand.statusnow reports (and--reset-keywrites) the config file actually loaded, not<data-dir>/mcp_config.json, which was wrong under--config/--system.docs/getting-started/installation.md;--systemindocs/cli/command-reference.mdanddocs/cli-management-commands.md; migration-guidedoctorinvocations now use--system.All host paths are injectable (
packagedPaths,systemServiceActive); tests never touch/etc.Test plan
cmd/mcpproxy/packaged_install_test.go(written first, failing): detection matrix incl. unreadable config dir, every management loader refuses and creates nothing, user config still used, non-Linux unchanged,--systempath wiring / conflicts / permission / serve refusal, hint command rewriting, different-instance warning, socket-refuses → HTTP fallback.internal/config/save_mode_test.go: mode matrix, group-change fallback to 0600, temp file owner-only before the check (each seen failing first).cli_config_resolve_test.go(TestLoadersHonorGlobalConfigFlagand friends) green.go test -race./cmd/mcpproxy/ ./internal/config/...bare and-tags server;./internal/runtime/...race green with the SaveConfig change..github/.golangci.yml) bare and--build-tags server: 0 issues on./cmd/...;./internal/config/...only the known main baseline (reflect.Ptrinzero_value_preservation_test.go, untouched)../scripts/test-api-e2e.sh(with./mcpproxy-serverbuilt): 70 passed, 0 failed.github-copilot/gpt-6.1-sol, 5 rounds → CLEAN. Fixed along the way: root--reset-keymaking the config unreadable to the service,MCPP_CONFIGignored,search-serversignoring--system, symlink-following postinstall repair (dropped), 0640 kept across a group change, temp-file exposure window, standalone fallback as root, instance warning vsMCPP_CONFIG.Follow-up (not in this PR): the CLI's TCP fallback always uses
http://, so a service withtls.enabledis only reachable via its socket (sudo -u mcpproxy …). Pre-existing for every CLI command.Draft reply for discussion #1678 (not posted)
🤖 Generated with Claude Code