Skip to content

x/tools/gopls: mcp: support a single shared daemon across all projects (multi-workspace MCP) #78668

Description

@Afterous

Background

gopls mcp initializes its LSP session by calling os.Getwd() at startup and locking that directory as the workspace root for its entire lifetime. This is correct for single-project use, but means that a shared daemon cannot serve agents working in different projects — each would get analysis results rooted at the wrong module.

Today's workaround: per-project daemons (one per (binary, workdir) pair), as implemented in the community proxy gopls-mcp-proxy and proposed for -remote=auto in #75270.

Proposal

Support a single gopls MCP daemon per machine (keyed on binary only, like the existing LSP -remote=auto daemon) that can serve MCP clients across multiple projects simultaneously.

This is a parity gap: the LSP daemon already does this today. gopls -remote=auto starts a single machine-wide process that manages multiple workspace views across projects. This proposal brings gopls mcp to the same level.

This requires gopls to dynamically load workspace views when a new MCP client connects from a project not yet known to the daemon.

What's Missing

The MCP roots mechanism already exists: when a client connects and sends notifications/roots/list_changed, gopls receives the client's workspace roots via the watchRoots callback (cmd/mcp.go:160). Currently this only starts file watching (filewatcher.WatchDir) but does not open a new LSP workspace view for that root.

The missing piece: when watchRoots fires with a root that isn't covered by any existing view, gopls should call the equivalent of DidChangeWorkspaceFolders on its internal server to open a new view for that module. The LSP server already supports dynamic workspace folder changes — this is about wiring MCP client roots into that mechanism.

Relationship to #75270

#75270 proposes -remote=auto with a per-project daemon (one daemon per (binary, workdir) pair). That approach works without any new gopls features and should land first.

This issue proposes the follow-on: collapse all per-project daemons into one machine-wide daemon, saving additional memory through shared type-checking caches across projects.

Relevant Code

  • gopls/internal/cmd/mcp.go:160watchRoots callback (currently only does file watching)
  • gopls/internal/cmd/cmd.go:314os.Getwd() hardcodes the workspace root at session init
  • gopls/internal/mcp/workspace.go:25h.session.Views() — shows only views from the initial root
  • gopls/internal/lsprpc/lsprpc.goDidChangeWorkspaceFolders forwarding in the LSP forwarder

Metadata

Metadata

Assignees

No one assigned

    Labels

    FeatureRequestIssues asking for a new feature that does not need a proposal.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.ToolsThis label describes issues relating to any tools in the x/tools repository.goplsIssues related to the Go language server, gopls.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions