go icon indicating copy to clipboard operation
go copied to clipboard

x/tools/gopls: mcp: opens too many files

Open thenom opened this issue 1 month ago • 5 comments

Go version

go version go1.25.1 darwin/arm64

Output of go env in your module/workspace:

CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE='auto'
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/simon.thorley/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/simon.thorley/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/qd/k6bddt8s6156q_fft23bdly40000gr/T/go-build3764416828=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD=''
GOMODCACHE='/Users/simon.thorley/workspace/go/pkg/mod'
GONOPROXY='gitlab.com/private-subgroup/*'
GONOSUMDB='gitlab.com/private-subgroup/*'
GOOS='darwin'
GOPATH='/Users/simon.thorley/workspace/go'
GOPRIVATE='gitlab.com/private-subgroup/*'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.25.1/libexec'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/simon.thorley/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.25.1/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.25.1'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

Using the following MCP config within AmazonQ CLI:

		"gopls": {
			"command": "gopls",
			"args": [
				"mcp"
			]
		},

Or starting up directly by just running gopls mcp

What did you see happen?

When using Q CLI I always start getting too many open files errors and applications start to close/crash (e.g. chrome). I found that if i run the mcp server manually i can limit these open files by running it within an empty folder.

If i run it from my home folder i always hit the open file limit so it seems that potentially it is reading in all files and subfolders files on startup.

My open files went from ~12k before running it within my home folder to +71k at which point my laptop became unresponsive until i could finally cancel the server out:

 $ gopls mcp
2025/11/13 13:28:19 Listening for MCP messages on stdin...

2025/11/13 13:28:43 Error:2025/11/13 13:28:43 failed to get import resolver: err: fork/exec /opt/homebrew/bin/go: too many open files in system: stderr:
^C^C
^C
^C
^C

What did you expect to see?

Stable OS and low additional open files count

thenom avatar Nov 13 '25 13:11 thenom

CC @h9jiang

findleyr avatar Nov 13 '25 18:11 findleyr

Hi @thenom

Thanks for reporting this. I think this is issue is caused by our gopls file watcher. This file watcher go routine is being spinned up to watch your current directory changes so it try to watch recursively into your file system.

The reason for the file watcher is, gopls mcp server need to know what file have changed by the AI agent or by your editor. So we run this file watcher from your current working directory. (This is our best guess, the current working directory is your go project's workspace root).

// This is watching the entire ~
~/ gopls mcp 

// This is watching the tools repo
~/codebase/tools/ gopls mcp

As mentioned, the gopls mcp server is not aware of where is the root of your workspace so we made a guess. I think a better way of doing this is, instead of guessing, we can either ask ourselves or use Sampling or Elicitation to ask MCP client where is the root of the workspace.

The current approach of just listening to the current working directory is not ideal. I will experiment with other approaches.

But for now, ideally, you should run the gopls mcp from the root of your go repo and the gopls mcp server will only watching your go repo.

h9jiang avatar Nov 13 '25 20:11 h9jiang

@h9jiang Thats great, thanks for the update and looking into this.

With regards to running this in QCLI for example, how do you suggest i get round this for now? When this works it works well, especially with the exported context file. I did try with the attached mode (localhost:8092) but never really had much success with the session handling and the configuration itself but thinking about i am not sure how this gets around the issues with the file watcher you mentioned as i am guessing it offers up the same tools, i.e. doesnt give an option to control the working directory so this still needs to be launched manually before QCLI in the working folder.

Not sure i am up to go tools programming standard but let me know if you want me to take a look at anything, test or even get my hands dirty on it 👍

thenom avatar Nov 13 '25 22:11 thenom

Hi, sorry for the late reply, we had a quick discussion last week and we think a simple solution would be verify whether the current dir is a go module or not.

If the current dir is a go module, let's start watching the current dir. If not, we can walk up (visiting it's parent dir) until we reach a go module.

We should only start watching if we are inside of a go module. We should only watch the root of the go module.

Some of the mcp client have security feature like containerize mcp server so walking up is not an option. (Basically means you should not access file system beyond the current dir). In this case, the gopls mcp server should just fail.

Thank you.

Help wanted.

h9jiang avatar Dec 09 '25 06:12 h9jiang