-
Notifications
You must be signed in to change notification settings - Fork 47
🤖 feat: make MCP + OAuth global (repo overrides) #2135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ThomasK33
wants to merge
21
commits into
main
Choose a base branch
from
globa-mcp-config
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+3,023
−929
Conversation
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
2b0ed9d to
63818f3
Compare
- Read global MCP config from <muxHome>/mcp.jsonc with repo overrides - Add global secrets support + effective secret merge (project overrides) - Add top-level oRPC endpoints: mcp.* and secrets.* (legacy projects.* kept) - CLI uses real mux home for MCP config; --no-mcp-config ignores global+repo - Add/adjust unit tests for MCP config + secrets
- Add v2 mcp-oauth.json store keyed by normalized server URL\n- Migrate legacy v1 project-scoped store on read (dedupe by updatedAtMs)\n- Update MCPServerManager + ORPC legacy handlers to use serverUrl-scoped APIs\n- Update tests incl. v1→v2 migration coverage
Expose MCP OAuth flows + auth status at the top-level ORPC router (mcpOauth.*), while keeping projects.mcpOauth.* as legacy wrappers.
691d41d to
7790e20
Compare
Member
Author
|
@codex review |
|
Codex Review: Didn't find any major issues. Nice work! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
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
Mux MCP configuration is now global-first:
~/.mux/mcp.jsoncand managed via Settings → MCP../.mux/mcp.jsonc(merged over global by server name)../.mux/mcp.local.jsonc.This PR also globalizes MCP secrets + OAuth:
~/.mux/secrets.jsonusing a__global__sentinel plus per-project entries; project secrets override global.~/.mux/mcp-oauth.jsonusing a V2 schema keyed by normalized server URL, with automatic V1→V2 migration.Background
Previously MCP servers were configured per-project (
<repo>/.mux/mcp.jsonc), forcing users to reconfigure the same servers across repositories. OAuth tokens were also stored project-scoped.Implementation
~/.mux/mcp.jsonc(global) + optional repo overrides.secrets.*ORPC endpoints.mcpOauth.*ORPC endpoints.Follow-ups included
/mcpslash command (writes are global-only; docs updated accordingly).Validation
make fmtmake static-checkmake test📋 Implementation Plan
Plan: Make MCP + MCP OAuth global (with optional repo overrides)
Context / Why
Mux currently treats MCP server definitions as project-scoped (
<project>/.mux/mcp.jsonc) with workspace-local overrides (<workspace>/.mux/mcp.local.jsonc). This forces users to reconfigure MCP servers for every project.Goal: make MCP configuration global (JSONC-only):
~/.mux/mcp.jsoncand apply to the whole app../.mux/mcp.jsonc(no UI; checked into the repo)../.mux/mcp.local.jsonc(gitignored).Additionally, MCP OAuth was recently added and persists to
~/.mux/mcp-oauth.jsonbut its schema is still project-scoped (projectPath -> serverName -> creds). We want to make OAuth credentials global as well (shared across projects), and migrate the on-disk schema.Net result: configure MCP once, use everywhere; OAuth login once per server URL, use everywhere.
Evidence
.mux/mcp.jsoncviaMCPConfigService(src/node/services/mcpConfigService.ts)..mux/mcp.local.jsoncviaWorkspaceMcpOverridesService(src/node/services/workspaceMcpOverridesService.ts).MCPServerManager(src/node/services/mcpServerManager.ts).src/browser/components/Settings/sections/ProjectSettingsSection.tsxand callsapi.projects.mcp.*.~/.mux/mcp-oauth.jsonbut schema is V1 project-scoped and implemented insrc/node/services/mcpOauthService.ts.projects.mcpOauth(src/common/orpc/schemas/api.ts,src/node/orpc/router.ts).User decisions captured via
ask_user_question:.mux/mcp.jsoncfor repo overrides,~/.mux/mcp.jsoncfor global base).{secret:"KEY"}should resolve from project secrets first, then global secrets.Recommended approach (single approach)
Implement a global MCP config layer + global secrets + globalized MCP OAuth store.
Estimated net LoC (product code only): ~+650 / -450 (≈ +200 net)
Implementation details
1) MCP config files: define the new resolution order
Update MCP server resolution to (JSONC-only):
--mcp, constructorinlineServers) – overrides by name.<workspace>/.mux/mcp.local.jsonc(existing behavior; gitignored).<projectPath>/.mux/mcp.jsonc(optional; checked into the repo; no UI).~/.mux/mcp.jsonc.Merge semantics (per user):
effectiveServers = { ...globalServers, ...repoOverrideServers }(repo wins by name).2) Backend: refactor
MCPConfigServiceto support global + overridesFiles:
src/node/services/mcpConfigService.tssrc/node/services/serviceContainer.tssrc/node/services/mcpConfigService.test.tsKey changes:
Configso the service can locateconfig.rootDir.~/.mux/mcp.jsonc(usepath.join(config.rootDir, "mcp.jsonc"))..mux/mcp.jsonc(optional override; read-only)listServers(projectPath?: string): Promise<Record<string, MCPServerInfo>>projectPath⇒ global servers onlyprojectPath⇒ merged{ ...globalServers, ...repoOverrideServers }(repo wins by name)addServer(name, input)removeServer(name)setServerEnabled(name, enabled)setToolAllowlist(name, toolAllowlist)Suggested shape:
Defensive programming notes:
3) Backend: global secrets (project overrides global)
We need global secrets to support:
{secret:"KEY"}references in global MCP config.Files:
src/node/config.tssrc/node/services/aiService.tssrc/node/orpc/router.ts(MCP test endpoints)src/common/types/secrets.ts(doc comment updates only)Storage approach (minimal-change):
~/.mux/secrets.jsonasRecord<string, Secret[]>."__global__") for global secrets.Add new Config helpers:
getGlobalSecrets(): Secret[]updateGlobalSecrets(secrets: Secret[]): Promise<void>getEffectiveSecrets(projectPath: string): Secret[](merge; project wins by key)Merge algorithm:
Wire-up changes:
AIService(src/node/services/aiService.ts), replaceconfig.getProjectSecrets()usages that feed tool env injection and MCP header resolution withconfig.getEffectiveSecrets()(project overrides global).secrets.get/updatethat take an optionalprojectPath:projectPathomitted ⇒ global secretsprojectPathprovided ⇒ project-only secretsThis powers the new Settings → Secrets UI (and can replace
projects.secrets.*call sites over time).4) ORPC: single MCP list/test endpoint (optional
projectPath) + Secrets endpointsWe want:
projectPath) for Settings → MCPprojectPath) for workspace UI, returning merged global + repo override serversFiles:
src/common/orpc/schemas/api.tssrc/common/orpc/schemas/mcp.tssrc/node/orpc/router.tsProposed API shape:
projects.mcp.*with a top-levelmcpnamespace:mcp.list({ projectPath?: string })projectPathomitted ⇒ global servers onlyprojectPathprovided ⇒{ ...globalServers, ...repoOverrideServers }(repo wins by name)mcp.test({ projectPath?: string, name?: string })projectPathomitted ⇒ test inconfig.rootDir, secrets = global secrets onlyprojectPathprovided ⇒ test in project cwd, secrets = effective secrets (project overrides global)mcp.add/remove/setEnabled/setToolAllowlist⇒ operate on global MCP config only (noprojectPath).secretsnamespace:secrets.get({ projectPath?: string })⇒ global or project-only secretssecrets.update({ projectPath?: string, secrets })workspace.mcp.get/setas-is for workspace-local enable/disable + allowlist overrides.Defensive programming notes:
projectPathis provided but repo override file is unreadable/invalid, log + treat as empty override (still return global servers).5) UI: add Settings → Secrets + make MCP settings global
Files:
src/browser/components/Settings/SettingsModal.tsxsrc/browser/components/Settings/sections/SecretsSection.tsx(new)src/browser/components/Settings/sections/ProjectSettingsSection.tsx(replace withMCPSettingsSection.tsx)src/browser/components/WorkspaceMCPModal.tsxsrc/browser/stories/App.settings.stories.tsx(Chromatic)src/browser/stories/mocks/orpc.ts(story mocks)src/browser/utils/slashCommands/registry.tsSteps:
id: "secrets"/ label “Secrets” / icon: lucideKeyRound(or similar).SecretsSection(src/browser/components/Settings/sections/SecretsSection.tsx)api.secrets.get/update({ projectPath?: string }).SecretsModalUX (row editor, visibility toggle), preferably by extracting a sharedSecretsEditor.export const Secrets: AppStorytosrc/browser/stories/App.settings.stories.tsx.createMockORPCClient(src/browser/stories/mocks/orpc.ts) to implementsecrets.get/update(and seed global + project secrets).id: "projects"→id: "mcp".ProjectSettingsSectionwithMCPSettingsSection(global MCP config).api.mcp.list({})for global servers;api.mcp.add/remove/setEnabled/setToolAllowlistfor writes;api.mcp.test({})for global-only testing.api.secrets.get({})).api.mcp.list({ projectPath })(effective global+repo servers).api.mcp.test({ projectPath, name })(project cwd + effective secrets).api.workspace.mcp.get/set.6) Update system prompt + onboarding copy
Files:
src/node/services/systemMessage.tssrc/browser/components/splashScreens/OnboardingWizardSplash.tsxUpdate text from:
.mux/mcp.jsonc”To:
~/.mux/mcp.jsonc.”./.mux/mcp.jsonc.”Also update Settings pointer: “Settings → MCP”.
6b) CLI: keep
mux runconsistent with global MCP configFiles:
src/cli/run.tssrc/cli/run.test.ts(if assertions depend on help text)Changes:
--no-mcp-confighelp text to mean: “ignore~/.mux/mcp.jsonc(and repo overrides), use only--mcpservers”.MCPConfigServicewith the user’s realConfig()(not the ephemeral temp config).--dirasprojectPathintoMCPServerManagerso./.mux/mcp.jsoncoverrides apply.MCP OAuth: make store non-project-scoped + migrate schema
7) OAuth store schema V2 (global by server URL)
Files:
src/node/services/mcpOauthService.tssrc/common/types/mcpOauth.ts(update comments)src/node/services/mcpOauthService.test.tsCurrent store (V1):
New store (V2):
Migration strategy (on read, under file lock):
normalizeServerUrlForComparison(creds.serverUrl)as the V2 key.updatedAtMs.0o600).Pseudo:
8) Refactor
McpOauthServiceAPIs to remove project scopingFiles:
src/node/services/mcpOauthService.tssrc/node/services/mcpServerManager.tssrc/node/orpc/router.tssrc/common/orpc/schemas/api.tssrc/browser/components/Settings/sections/ProjectSettingsSection.tsx(moved into MCP settings)Key decisions:
projectPath.Concrete refactor:
getAuthStatus(projectPath, serverName)→getAuthStatus({ serverUrl })(or{ serverName, serverUrl }for nicer UI)logout(projectPath, serverName)→logout({ serverUrl })hasAuthTokens({ projectPath, serverName, serverUrl })→hasAuthTokens({ serverUrl })getAuthProviderForServer({ projectPath, serverName, serverUrl })→getAuthProviderForServer({ serverName, serverUrl })normalizeProjectPathKeyusage and allstore.entries[projectKey]logic.normalizeServerUrlForComparison) as-is.9) ORPC schema changes for OAuth
Move OAuth endpoints out of
projects.*to avoid implying project scoping.Proposed new ORPC section:
mcpOauth(top-level)mcpOauth.startDesktopFlow({ serverName, pendingServer? })mcpOauth.startServerFlow({ serverName, pendingServer? })mcpOauth.getAuthStatus({ serverUrl })mcpOauth.logout({ serverUrl })Where
pendingServeris still:10) UI: OAuth actions live in global MCP Settings
ProjectSettingsSectioninto the newMCPSettingsSection.Validation / Tests
11) Update + add tests
MCP config:
src/node/services/mcpConfigService.test.tsConfig(rootDir)and assert writes go to<rootDir>/mcp.jsonc.Secrets:
Config.getEffectiveSecrets()merge behavior.src/browser/stories/App.settings.stories.tsx→ newSecretsstory that opens Settings → Secrets with seeded global + project secrets.OAuth:
src/node/services/mcpOauthService.test.ts12) Manual smoke checklist
~/.mux/mcp.jsoncand appears in multiple projects../.mux/mcp.jsoncin a repo overriding a global server by name; verify effective server uses override..mux/mcp.local.jsonc).{secret:"KEY"}in global MCP headers; verify it resolves. Add a project secret with the same key and verify it overrides global when testing viamcp.test({ projectPath }).~/.mux/mcp-oauth.jsonis migrated to V2 (version bumped) on first run.Generated with
mux• Model:openai:gpt-5.2• Thinking:xhigh• Cost: $31.92