Skip to content

[Bug]: MCP stdio server always runs with cwd=homedir() and returns home as roots; workspace context is dropped, breaking per-project tools like codegraph #76

@jianga0801-ui

Description

@jianga0801-ui

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:

  1. The stdio transport spawns the child with cwd = homedir() regardless of which session/agent triggered the call.
  2. 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

  1. 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.
  2. Start an agent session in MiniMax Code with that project as the workspace.
  3. From the agent, call any codegraph tool (e.g. codegraph_search) without passing projectPath.
  4. Observe the server reports "No CodeGraph project is loaded … starting from: C:\Users<you>".
  5. 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:

  1. 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.

  2. 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.

  3. 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.

  4. 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 --version3.0.43
  • CodeGraph server's own daemon log (<project>/.codegraph/daemon.log) shows the server starts fine, just at the wrong root.

Before submitting

  • I have searched existing issues. (Searched: "MCP workspace", "roots/list", "stdio cwd" — no matches in open issues)
  • I am using the latest version available to me. (3.0.43)
  • I have removed sensitive information from logs/screenshots. (paths and tool names only)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions