Issue 内容(MiniMax-AI/minimax-code · [Bug]: MCP stdio server 始终以 home 目录作为 cwd/roots,跨 workspace 不可用)
仓库: MiniMax-AI/minimax-code
模板: 01-bug-report.yml (Bug report / 问题反馈)
Issue area: MCP / OAuth / integration
Version: v3.0.43
Platform: Windows
OS version: Windows 11 23H2
Upload ID: cannot upload (no crash, behavior bug)
Title
[Bug]: MCP stdio server always runs with cwd=homedir() and returns home as roots; workspace context is dropped, breaking per-project tools like codegraph
What happened?
When the daemon starts an MCP server with transport.type === "stdio" and that server relies on the current working directory or the MCP roots/list request to discover the active workspace (e.g. the CodeGraph MCP server, which walks up from process.cwd() looking for .codegraph/), the server always sees the user's home directory instead of the agent's current workspace.
Symptom with the CodeGraph MCP server (configured in ~/.mavis/mcp/mcp.json):
> mavis mcp call codegraph codegraph_search '{"query":"Desktop"}'
No CodeGraph project is loaded for this session.
Searched for a .codegraph/ directory starting from: C:\Users\14378
The user has a valid .codegraph/ index at C:\Users\14378\Documents\Code\Claude\Windows-MCP\.codegraph\, but the server only ever walks up from $HOME, so it never finds it. Switching to a different workspace does not change behavior — every workspace reports the same "starting from home" message.
Workaround: passing projectPath explicitly in the tool args works (e.g. {"query":"Desktop","projectPath":"C:/Users/.../Windows-MCP"}), which confirms the server itself is healthy; the daemon is what is dropping the workspace context.
Root cause (from inspecting daemon.js)
The daemon bundles createStdioTransport and a hard-coded ListRootsRequestSchema handler that both ignore the calling session entirely:
// packages/daemon/src/mcp/runtime/transport/stdio.ts (in daemon.js ~line 493289)
function createStdioTransport(config, options) {
return new StdioClientTransport({
command: config.command,
args: config.args,
env: buildProcessEnv(config.env, options?.env),
cwd: homedir(), // ← always homedir, ignores session
stderr: "pipe"
});
}
// packages/daemon/src/mcp/runtime/connection-pool.ts (in daemon.js ~line 493515)
client.setRequestHandler(ListRootsRequestSchema, async () => ({
roots: [{ uri: pathToFileURL(homedir()).href, name: "home" }] // ← always home
}));
Two related issues:
- The stdio transport spawns the child with
cwd = homedir() regardless of which session/agent triggered the call.
- When the server requests
roots/list (the spec-blessed way to learn the workspace), the handler always returns the home directory.
McpConnectionPool.callTool(serverName, toolName, args, overrides, options) accepts an options object, but only options.timeout is read — there is no plumbing for workspace context from session → transport.
Additionally, McpConnectionPool.connections = new Map<serverName, connection>() caches one connection per server name globally, so even if transport were fixed naively, two sessions in different workspaces would share the same child process. CodeGraph supports per-call projectPath to multiplex, but other servers may not.
The session layer already has the data — session.workspaceDir is plumbed everywhere (e.g. agentProcessManager.ensureAgentProcess(ctx, agentName, workspaceDir), spawnSessionProcess(..., { cwd: workspaceDir }), ~235 hits in daemon.js), it just doesn't reach MCP.
Steps to reproduce
- Install the CodeGraph MCP server (or any stdio MCP server that resolves a project root from cwd/roots) and index a project with
codegraph init so .codegraph/ exists in the project root.
- Start an agent session in MiniMax Code with that project as the workspace.
- From the agent, call any codegraph tool (e.g.
codegraph_search) without passing projectPath.
- Observe the server reports "No CodeGraph project is loaded … starting from: C:\Users<you>".
- Switch to a different project workspace, repeat the call — the error message still says
C:\Users\<you>.
Reproduction from CLI:
mavis mcp call codegraph codegraph_search '{"query":"Desktop"}'
# → No CodeGraph project is loaded for this session.
# Searched for a .codegraph/ directory starting from: C:\Users\14378
echo '{"query":"Desktop","projectPath":"C:/Users/14378/Documents/Code/Claude/Windows-MCP"}' \
| mavis mcp call codegraph codegraph_search --stdin
# → works (10 results)
Expected behavior
The MCP stdio server should be able to discover the active workspace via one of:
process.cwd() reflecting the calling session's workspace, or
roots/list returning { uri: "file:///path/to/active/workspace", name: "workspace" } for the calling session's workspaceDir.
Switching to a different project workspace should change which .codegraph/ (or equivalent per-project state) the server finds.
Actual behavior
Both cwd and roots/list always resolve to the user's home directory. Per-project MCP servers are effectively unusable without manual projectPath workarounds. The user has to inject workspace context into every tool call.
Suggested fix (sketch, not exhaustive)
The minimum changes are in two places, both in daemon.js:
-
createStdioTransport — accept and forward an optional cwd from options (which already exists on createTransport's options), defaulting to homedir() only when nothing else is available.
-
ListRootsRequestSchema handler — pull the active workspace from a session-aware context (e.g. an AsyncLocalStorage populated by callTool from session.workspaceDir); fall back to homedir() if no session is in flight.
-
McpConnectionPool.callTool — extend the options argument to accept { timeout, workspaceDir, sessionId }, pass workspaceDir into the transport factory, and store it on the connection so roots/list can read it without global state.
-
Connection caching — consider keying connections by (serverName, workspaceDir) instead of just serverName, or at minimum guarantee that every call forwards the correct projectPath (CodeGraph already supports this; the daemon should inject it). For servers that don't support per-call project selection, per-workspace child processes are required.
The session layer already has the data (session.workspaceDir) and threads it into agent process spawning — it just stops at the MCP boundary.
Logs / evidence
From the user's machine:
mavis mcp get codegraph:
{
"command": "C:\\Program Files\\nodejs\\node.exe",
"args": [
"C:\\Users\\14378\\AppData\\Roaming\\npm\\node_modules\\@colbymchenry\\codegraph\\npm-shim.js",
"serve",
"--mcp"
],
"env": {},
"enabled": true,
"configured": true
}
mavis --version → 3.0.43
- CodeGraph server's own daemon log (
<project>/.codegraph/daemon.log) shows the server starts fine, just at the wrong root.
Before submitting
Issue 内容(MiniMax-AI/minimax-code · [Bug]: MCP stdio server 始终以 home 目录作为 cwd/roots,跨 workspace 不可用)
仓库: MiniMax-AI/minimax-code
模板: 01-bug-report.yml (Bug report / 问题反馈)
Issue area: MCP / OAuth / integration
Version: v3.0.43
Platform: Windows
OS version: Windows 11 23H2
Upload ID: cannot upload (no crash, behavior bug)
Title
[Bug]: MCP stdio server always runs with cwd=homedir() and returns home as roots; workspace context is dropped, breaking per-project tools like codegraphWhat happened?
When the daemon starts an MCP server with
transport.type === "stdio"and that server relies on the current working directory or the MCProots/listrequest to discover the active workspace (e.g. the CodeGraph MCP server, which walks up fromprocess.cwd()looking for.codegraph/), the server always sees the user's home directory instead of the agent's current workspace.Symptom with the CodeGraph MCP server (configured in
~/.mavis/mcp/mcp.json):The user has a valid
.codegraph/index atC:\Users\14378\Documents\Code\Claude\Windows-MCP\.codegraph\, but the server only ever walks up from$HOME, so it never finds it. Switching to a different workspace does not change behavior — every workspace reports the same "starting from home" message.Workaround: passing
projectPathexplicitly in the tool args works (e.g.{"query":"Desktop","projectPath":"C:/Users/.../Windows-MCP"}), which confirms the server itself is healthy; the daemon is what is dropping the workspace context.Root cause (from inspecting
daemon.js)The daemon bundles
createStdioTransportand a hard-codedListRootsRequestSchemahandler that both ignore the calling session entirely:Two related issues:
cwd = homedir()regardless of which session/agent triggered the call.roots/list(the spec-blessed way to learn the workspace), the handler always returns the home directory.McpConnectionPool.callTool(serverName, toolName, args, overrides, options)accepts anoptionsobject, but onlyoptions.timeoutis read — there is no plumbing for workspace context from session → transport.Additionally,
McpConnectionPool.connections = new Map<serverName, connection>()caches one connection per server name globally, so even if transport were fixed naively, two sessions in different workspaces would share the same child process. CodeGraph supports per-callprojectPathto multiplex, but other servers may not.The session layer already has the data —
session.workspaceDiris plumbed everywhere (e.g.agentProcessManager.ensureAgentProcess(ctx, agentName, workspaceDir),spawnSessionProcess(..., { cwd: workspaceDir }), ~235 hits indaemon.js), it just doesn't reach MCP.Steps to reproduce
codegraph initso.codegraph/exists in the project root.codegraph_search) without passingprojectPath.C:\Users\<you>.Reproduction from CLI:
Expected behavior
The MCP stdio server should be able to discover the active workspace via one of:
process.cwd()reflecting the calling session's workspace, orroots/listreturning{ uri: "file:///path/to/active/workspace", name: "workspace" }for the calling session'sworkspaceDir.Switching to a different project workspace should change which
.codegraph/(or equivalent per-project state) the server finds.Actual behavior
Both
cwdandroots/listalways resolve to the user's home directory. Per-project MCP servers are effectively unusable without manualprojectPathworkarounds. The user has to inject workspace context into every tool call.Suggested fix (sketch, not exhaustive)
The minimum changes are in two places, both in
daemon.js:createStdioTransport— accept and forward an optionalcwdfromoptions(which already exists oncreateTransport's options), defaulting tohomedir()only when nothing else is available.ListRootsRequestSchemahandler — pull the active workspace from a session-aware context (e.g. anAsyncLocalStoragepopulated bycallToolfromsession.workspaceDir); fall back tohomedir()if no session is in flight.McpConnectionPool.callTool— extend theoptionsargument to accept{ timeout, workspaceDir, sessionId }, passworkspaceDirinto the transport factory, and store it on the connection soroots/listcan read it without global state.Connection caching — consider keying connections by
(serverName, workspaceDir)instead of justserverName, or at minimum guarantee that every call forwards the correctprojectPath(CodeGraph already supports this; the daemon should inject it). For servers that don't support per-call project selection, per-workspace child processes are required.The session layer already has the data (
session.workspaceDir) and threads it into agent process spawning — it just stops at the MCP boundary.Logs / evidence
From the user's machine:
mavis mcp get codegraph:{ "command": "C:\\Program Files\\nodejs\\node.exe", "args": [ "C:\\Users\\14378\\AppData\\Roaming\\npm\\node_modules\\@colbymchenry\\codegraph\\npm-shim.js", "serve", "--mcp" ], "env": {}, "enabled": true, "configured": true }mavis --version→3.0.43<project>/.codegraph/daemon.log) shows the server starts fine, just at the wrong root.Before submitting