From da2925ef19f0f8dacd400f5964cb6f422ee8aa4d Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 19 Jun 2025 17:57:11 -0400 Subject: [PATCH 1/7] Fix golang unit tests --- integrationtests/snapshots/go/hover/struct-type.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrationtests/snapshots/go/hover/struct-type.snap b/integrationtests/snapshots/go/hover/struct-type.snap index 08c7820..34aa914 100644 --- a/integrationtests/snapshots/go/hover/struct-type.snap +++ b/integrationtests/snapshots/go/hover/struct-type.snap @@ -1,5 +1,5 @@ ```go -type SharedStruct struct { // size=56 (0x38) +type SharedStruct struct { // size=56 (0x38), class=64 (0x40) ID int Name string Value float64 From cd409b32d16b5b31fb05e19a8c0b90fb34b66d3c Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 19 Jun 2025 18:09:13 -0400 Subject: [PATCH 2/7] Bump rust timeout --- integrationtests/tests/rust/internal/helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrationtests/tests/rust/internal/helpers.go b/integrationtests/tests/rust/internal/helpers.go index 19cf38f..ba227c1 100644 --- a/integrationtests/tests/rust/internal/helpers.go +++ b/integrationtests/tests/rust/internal/helpers.go @@ -21,7 +21,7 @@ func GetTestSuite(t *testing.T) *common.TestSuite { Command: "rust-analyzer", Args: []string{}, WorkspaceDir: filepath.Join(repoRoot, "integrationtests/workspaces/rust"), - InitializeTimeMs: 3000, + InitializeTimeMs: 5000, } // Create a test suite From f567a4d0e5a6e8aa7625f7fd135ef4b8cd5d0590 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 22:13:44 +0000 Subject: [PATCH 3/7] Bump golang.org/x/text from 0.25.0 to 0.26.0 Bumps [golang.org/x/text](https://github.com/golang/text) from 0.25.0 to 0.26.0. - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.25.0...v0.26.0) --- updated-dependencies: - dependency-name: golang.org/x/text dependency-version: 0.26.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index b875a3f..15b0a2e 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/mark3labs/mcp-go v0.25.0 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 github.com/stretchr/testify v1.10.0 - golang.org/x/text v0.25.0 + golang.org/x/text v0.26.0 ) require ( @@ -21,11 +21,11 @@ require ( github.com/spf13/cast v1.7.1 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/sys v0.31.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/sync v0.15.0 // indirect + golang.org/x/sys v0.33.0 // indirect golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 // indirect - golang.org/x/tools v0.31.0 // indirect + golang.org/x/tools v0.33.0 // indirect golang.org/x/vuln v1.1.4 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 9ae5405..9548c97 100644 --- a/go.sum +++ b/go.sum @@ -42,18 +42,18 @@ github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zI github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4= golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 h1:FemxDzfMUcK2f3YY4H+05K9CDzbSVr2+q/JKN45pey0= golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I= golang.org/x/vuln v1.1.4/go.mod h1:F+45wmU18ym/ca5PLTPLsSzr2KppzswxPP603ldA67s= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 8699cae9cd4a6133fa7b6dfecb47665e8a0e6d05 Mon Sep 17 00:00:00 2001 From: Micah Date: Thu, 19 Jun 2025 17:01:56 -0500 Subject: [PATCH 4/7] Don't send multiple 'initialized' notifications --- internal/lsp/client.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/internal/lsp/client.go b/internal/lsp/client.go index fc07059..59ab58d 100644 --- a/internal/lsp/client.go +++ b/internal/lsp/client.go @@ -196,8 +196,8 @@ func (c *Client) InitializeLSPClient(ctx context.Context, workspaceDir string) ( return nil, fmt.Errorf("initialize failed: %w", err) } - if err := c.Notify(ctx, "initialized", struct{}{}); err != nil { - return nil, fmt.Errorf("initialized notification failed: %w", err) + if err := c.Initialized(ctx, protocol.InitializedParams{}); err != nil { + return nil, fmt.Errorf("initialized failed: %w", err) } // Register handlers @@ -208,12 +208,6 @@ func (c *Client) InitializeLSPClient(ctx context.Context, workspaceDir string) ( c.RegisterNotificationHandler("textDocument/publishDiagnostics", func(params json.RawMessage) { HandleDiagnostics(c, params) }) - // Notify the LSP server - err := c.Initialized(ctx, protocol.InitializedParams{}) - if err != nil { - return nil, fmt.Errorf("initialization failed: %w", err) - } - // LSP sepecific Initialization path := strings.ToLower(c.Cmd.Path) switch { From 594ffd3e4d30f537fd7e1914c53b1bf72f41764e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 22:18:31 +0000 Subject: [PATCH 5/7] Bump github.com/mark3labs/mcp-go from 0.25.0 to 0.32.0 Bumps [github.com/mark3labs/mcp-go](https://github.com/mark3labs/mcp-go) from 0.25.0 to 0.32.0. - [Release notes](https://github.com/mark3labs/mcp-go/releases) - [Commits](https://github.com/mark3labs/mcp-go/compare/v0.25.0...v0.32.0) --- updated-dependencies: - dependency-name: github.com/mark3labs/mcp-go dependency-version: 0.32.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 15b0a2e..d10e493 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.24.0 require ( github.com/davecgh/go-spew v1.1.1 github.com/fsnotify/fsnotify v1.9.0 - github.com/mark3labs/mcp-go v0.25.0 + github.com/mark3labs/mcp-go v0.32.0 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 github.com/stretchr/testify v1.10.0 golang.org/x/text v0.26.0 diff --git a/go.sum b/go.sum index 9548c97..12ea2ca 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mark3labs/mcp-go v0.25.0 h1:UUpcMT3L5hIhuDy7aifj4Bphw4Pfx1Rf8mzMXDe8RQw= -github.com/mark3labs/mcp-go v0.25.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= +github.com/mark3labs/mcp-go v0.32.0 h1:fgwmbfL2gbd67obg57OfV2Dnrhs1HtSdlY/i5fn7MU8= +github.com/mark3labs/mcp-go v0.32.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= From e8c1c402d6b6e9336a4b5be27e31a09c7a9ce379 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 6 Jun 2025 01:07:42 -0400 Subject: [PATCH 6/7] Fix compilation errors in upgraded mcp-go --- tools.go | 99 +++++++++++++++++++++----------------------------------- 1 file changed, 36 insertions(+), 63 deletions(-) diff --git a/tools.go b/tools.go index 55898ca..4aeca7b 100644 --- a/tools.go +++ b/tools.go @@ -43,13 +43,13 @@ func (s *mcpServer) registerTools() error { s.mcpServer.AddTool(applyTextEditTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Extract arguments - filePath, ok := request.Params.Arguments["filePath"].(string) - if !ok { - return mcp.NewToolResultError("filePath must be a string"), nil + filePath, err := request.RequireString("filePath") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } // Extract edits array - editsArg, ok := request.Params.Arguments["edits"] + editsArg, ok := request.GetArguments()["edits"] if !ok { return mcp.NewToolResultError("edits is required"), nil } @@ -105,9 +105,9 @@ func (s *mcpServer) registerTools() error { s.mcpServer.AddTool(readDefinitionTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Extract arguments - symbolName, ok := request.Params.Arguments["symbolName"].(string) - if !ok { - return mcp.NewToolResultError("symbolName must be a string"), nil + symbolName, err := request.RequireString("symbolName") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } coreLogger.Debug("Executing definition for symbol: %s", symbolName) @@ -129,9 +129,9 @@ func (s *mcpServer) registerTools() error { s.mcpServer.AddTool(findReferencesTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Extract arguments - symbolName, ok := request.Params.Arguments["symbolName"].(string) - if !ok { - return mcp.NewToolResultError("symbolName must be a string"), nil + symbolName, err := request.RequireString("symbolName") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } coreLogger.Debug("Executing references for symbol: %s", symbolName) @@ -161,20 +161,13 @@ func (s *mcpServer) registerTools() error { s.mcpServer.AddTool(getDiagnosticsTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Extract arguments - filePath, ok := request.Params.Arguments["filePath"].(string) - if !ok { - return mcp.NewToolResultError("filePath must be a string"), nil - } - - contextLines := 5 // default value - if contextLinesArg, ok := request.Params.Arguments["contextLines"].(int); ok { - contextLines = contextLinesArg + filePath, err := request.RequireString("filePath") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } - showLineNumbers := true // default value - if showLineNumbersArg, ok := request.Params.Arguments["showLineNumbers"].(bool); ok { - showLineNumbers = showLineNumbersArg - } + contextLines := request.GetInt("contextLines", 5) + showLineNumbers := request.GetBool("showLineNumbers", true) coreLogger.Debug("Executing diagnostics for file: %s", filePath) text, err := tools.GetDiagnosticsForFile(s.ctx, s.lspClient, filePath, contextLines, showLineNumbers) @@ -268,29 +261,19 @@ func (s *mcpServer) registerTools() error { s.mcpServer.AddTool(hoverTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Extract arguments - filePath, ok := request.Params.Arguments["filePath"].(string) - if !ok { - return mcp.NewToolResultError("filePath must be a string"), nil + filePath, err := request.RequireString("filePath") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } - // Handle both float64 and int for line and column due to JSON parsing - var line, column int - switch v := request.Params.Arguments["line"].(type) { - case float64: - line = int(v) - case int: - line = v - default: - return mcp.NewToolResultError("line must be a number"), nil + line, err := request.RequireInt("line") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } - switch v := request.Params.Arguments["column"].(type) { - case float64: - column = int(v) - case int: - column = v - default: - return mcp.NewToolResultError("column must be a number"), nil + column, err := request.RequireInt("column") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } coreLogger.Debug("Executing hover for file: %s line: %d column: %d", filePath, line, column) @@ -324,34 +307,24 @@ func (s *mcpServer) registerTools() error { s.mcpServer.AddTool(renameSymbolTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Extract arguments - filePath, ok := request.Params.Arguments["filePath"].(string) - if !ok { - return mcp.NewToolResultError("filePath must be a string"), nil + filePath, err := request.RequireString("filePath") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } - newName, ok := request.Params.Arguments["newName"].(string) - if !ok { - return mcp.NewToolResultError("newName must be a string"), nil + newName, err := request.RequireString("newName") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } - // Handle both float64 and int for line and column due to JSON parsing - var line, column int - switch v := request.Params.Arguments["line"].(type) { - case float64: - line = int(v) - case int: - line = v - default: - return mcp.NewToolResultError("line must be a number"), nil + line, err := request.RequireInt("line") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } - switch v := request.Params.Arguments["column"].(type) { - case float64: - column = int(v) - case int: - column = v - default: - return mcp.NewToolResultError("column must be a number"), nil + column, err := request.RequireInt("column") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil } coreLogger.Debug("Executing rename_symbol for file: %s line: %d column: %d newName: %s", filePath, line, column, newName) From 36da7df85bc64ad129df8b6d2046f288a8db66da Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 5 Jun 2025 23:43:00 -0400 Subject: [PATCH 7/7] Add -open flag to trigger initial opening of workspace files --- README.md | 3 +++ go.mod | 1 + go.sum | 2 ++ main.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/README.md b/README.md index 0a79d0a..12ff045 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,7 @@ This is an [MCP](https://modelcontextprotocol.io/introduction) server that runs

Install clangd: Download prebuilt binaries from the official LLVM releases page or install via your system's package manager (e.g., apt install clangd, brew install clangd).

Configure your MCP client: This will be different but similar for each client. For Claude Desktop, add the following to ~/Library/Application\\ Support/Claude/claude_desktop_config.json

+

NOTE: clangd will not resolve symbols until the first file is opened. Use the `-open` argument to trigger indexing.

 {
@@ -140,6 +141,8 @@ This is an [MCP](https://modelcontextprotocol.io/introduction) server that runs
         "/Users/you/dev/yourproject/",
         "--lsp",
         "/path/to/your/clangd_binary",
+        "--open",
+        "/Users/you/dev/yourproject/main.cpp",
         "--",
         "--compile-commands-dir=/path/to/yourproject/build_or_compile_commands_dir"
       ]
diff --git a/go.mod b/go.mod
index d10e493..024ef87 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/isaacphi/mcp-language-server
 go 1.24.0
 
 require (
+	github.com/bmatcuk/doublestar/v4 v4.8.1
 	github.com/davecgh/go-spew v1.1.1
 	github.com/fsnotify/fsnotify v1.9.0
 	github.com/mark3labs/mcp-go v0.32.0
diff --git a/go.sum b/go.sum
index 12ea2ca..c0958d0 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
 github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
 github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
+github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
diff --git a/main.go b/main.go
index f6f3ed5..a6bf876 100644
--- a/main.go
+++ b/main.go
@@ -8,9 +8,11 @@ import (
 	"os/exec"
 	"os/signal"
 	"path/filepath"
+	"strings"
 	"syscall"
 	"time"
 
+	"github.com/bmatcuk/doublestar/v4"
 	"github.com/isaacphi/mcp-language-server/internal/logging"
 	"github.com/isaacphi/mcp-language-server/internal/lsp"
 	"github.com/isaacphi/mcp-language-server/internal/watcher"
@@ -23,6 +25,7 @@ var coreLogger = logging.NewLogger(logging.Core)
 type config struct {
 	workspaceDir string
 	lspCommand   string
+	openGlobs    StringArrayFlag
 	lspArgs      []string
 }
 
@@ -35,10 +38,25 @@ type mcpServer struct {
 	workspaceWatcher *watcher.WorkspaceWatcher
 }
 
+// StringArrayFlag is a custom flag type to handle an array of strings
+type StringArrayFlag []string
+
+// Set appends a new value to the custom flag value
+func (s *StringArrayFlag) Set(value string) error {
+	*s = append(*s, value)
+	return nil
+}
+
+// String returns the string representation of the custom flag value
+func (s *StringArrayFlag) String() string {
+	return strings.Join(*s, ",")
+}
+
 func parseConfig() (*config, error) {
 	cfg := &config{}
 	flag.StringVar(&cfg.workspaceDir, "workspace", "", "Path to workspace directory")
 	flag.StringVar(&cfg.lspCommand, "lsp", "", "LSP command to run (args should be passed after --)")
+	flag.Var(&cfg.openGlobs, "open", "Glob of files to open by default (can specify more than once)")
 	flag.Parse()
 
 	// Get remaining args after -- as LSP arguments
@@ -99,10 +117,41 @@ func (s *mcpServer) initializeLSP() error {
 
 	coreLogger.Debug("Server capabilities: %+v", initResult.Capabilities)
 
+	if len(s.config.openGlobs) > 0 {
+		s.openInitialFiles()
+	}
+
 	go s.workspaceWatcher.WatchWorkspace(s.ctx, s.config.workspaceDir)
 	return client.WaitForServerReady(s.ctx)
 }
 
+func (s *mcpServer) openInitialFiles() {
+
+	filepath.WalkDir(s.config.workspaceDir, func(path string, d os.DirEntry, err error) error {
+		if err != nil {
+			return err
+		}
+
+		if !d.IsDir() {
+			for _, pattern := range s.config.openGlobs {
+				match, err := doublestar.PathMatch(pattern, path)
+				if err != nil {
+					return err
+				}
+
+				if match {
+					if err := s.lspClient.OpenFile(s.ctx, path); err != nil {
+						coreLogger.Error("Failed to open file %s: %v", path, err)
+					}
+					break
+				}
+			}
+		}
+
+		return nil
+	})
+}
+
 func (s *mcpServer) start() error {
 	if err := s.initializeLSP(); err != nil {
 		return err