fix: gate MCP server actions behind isMcpEnabled - #6889
Open
aroh3006 wants to merge 1 commit into
Open
Conversation
Every function in app/mcp/actions.ts is exported from a "use server" module, which makes each one callable directly over HTTP as a Next.js Server Action, unauthenticated, using the action id embedded in the client bundle. isMcpEnabled() already existed for this but nothing called it, so every action ran regardless of the ENABLE_MCP setting. addMcpServer takes a command and args and spawns a child process with them (via StdioClientTransport), so this was remote code execution reachable on any deployment, whether or not MCP was ever turned on. Every exported action now calls isMcpEnabled() first and rejects if it's off, matching how the frontend already gates on it before ever calling these. Added a test that calls each action with ENABLE_MCP unset and checks it rejects instead of running. Confirmed it fails against the current code: addMcpServer actually wrote the malicious config to disk and spawned the process (a real /tmp file got created running the test against the unfixed code), and passes with the fix.
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.
Fixes #6757.
app/mcp/actions.tshas"use server"at the top, which makes everyexported function in the file callable directly over HTTP as a Next.js
Server Action, unauthenticated, using the action id embedded in the
client bundle.
isMcpEnabled()already existed as a guard for this,but nothing called it, so every action ran regardless of the
ENABLE_MCPsetting.addMcpServerwrites a caller-supplied{command, args}tomcp_config.jsonand spawns a child process with them viaStdioClientTransport. With no auth check and noisMcpEnabled()check, that's remote code execution reachable on any deployment,
whether or not MCP was ever turned on — exactly as laid out in #6757.
Every exported action in the file now calls
isMcpEnabled()first andrejects if it's off. This matches what the frontend already does —
chat.tsx,home.tsx,mcp-market.tsx,sidebar.tsxandstore/chat.tsall checkisMcpEnabled()before ever calling these,so legitimate MCP usage (
ENABLE_MCP=true) is unaffected.Test. Added
test/mcp-actions-require-enabled.test.ts, which callsevery exported action with
ENABLE_MCPunset and asserts each onerejects with "MCP is not enabled" instead of running, plus a couple of
sanity checks for the enabled case. Ran it against the current code
first:
addMcpServeractually wrote the malicious config to disk andspawned the process — a real file was created on my machine by the
touchpayload in the test, running against unpatchedmain. Itfails there and passes with this change.
This only closes the primary path in #6757 (no auth check at all). The
report's other recommendations — command allowlisting, not inheriting
the full
process.envinto the spawned process, and moving MCPmanagement to an explicit authenticated route — are broader hardening
for deployments that do have
ENABLE_MCP=true, and probably deservetheir own separate PR(s) and maintainer input on the intended design.