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:160 — watchRoots callback (currently only does file watching)
gopls/internal/cmd/cmd.go:314 — os.Getwd() hardcodes the workspace root at session init
gopls/internal/mcp/workspace.go:25 — h.session.Views() — shows only views from the initial root
gopls/internal/lsprpc/lsprpc.go — DidChangeWorkspaceFolders forwarding in the LSP forwarder
Background
gopls mcpinitializes its LSP session by callingos.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=autoin #75270.Proposal
Support a single gopls MCP daemon per machine (keyed on binary only, like the existing LSP
-remote=autodaemon) that can serve MCP clients across multiple projects simultaneously.This is a parity gap: the LSP daemon already does this today.
gopls -remote=autostarts a single machine-wide process that manages multiple workspace views across projects. This proposal bringsgopls mcpto 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 thewatchRootscallback (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
watchRootsfires with a root that isn't covered by any existing view, gopls should call the equivalent ofDidChangeWorkspaceFolderson 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=autowith 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:160—watchRootscallback (currently only does file watching)gopls/internal/cmd/cmd.go:314—os.Getwd()hardcodes the workspace root at session initgopls/internal/mcp/workspace.go:25—h.session.Views()— shows only views from the initial rootgopls/internal/lsprpc/lsprpc.go—DidChangeWorkspaceFoldersforwarding in the LSP forwarder