[tools] internal/mcp: add simd mapper tool to gopls mcp server

4 views
Skip to first unread message

Junyang Shao (Gerrit)

unread,
Jan 13, 2026, 5:56:50 PM (4 days ago) Jan 13
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Junyang Shao has uploaded the change for review

Commit message

internal/mcp: add simd mapper tool to gopls mcp server

This CL adds a custom tool to gopls mcp server that maps simd
instructions to archsimd intrinsic calls.
Change-Id: Iaaee061ce2017293821f80e09d949c35cbf77568

Change diff

diff --git a/gopls/internal/mcp/instructions.md b/gopls/internal/mcp/instructions.md
index 449ea8c..eeb921e 100644
--- a/gopls/internal/mcp/instructions.md
+++ b/gopls/internal/mcp/instructions.md
@@ -27,6 +27,9 @@
4. **Understand a package's public API**: When you need to understand what a package provides to external code (i.e., its public API), use `go_package_api`. This is especially useful for understanding third-party dependencies or other packages in the same monorepo.
EXAMPLE: to see the API of the `storage` package: `go_package_api({"packagePaths":["example.com/internal/storage"]})`

+5. **Translate SIMD assemblies**: When you need to understand SIMD instructions about their intrinsics representations (in package archsimd), use `go_simdmapper`. This is critical for your workflow if the user asked you to translate amd64 assemblies that contains SIMD instructions to Go code.
+ EXAMPLE: to know the API of "VPADDD X2, X9, X2": `go_simdmapper({"asm": "VPADDD X2, X9, X2"})`.
+
### Editing workflow

The editing workflow is iterative. You should cycle through these steps until the task is complete.
@@ -45,5 +48,4 @@

6. **Check for vulnerabilities**: If your edits involved adding or updating dependencies in the go.mod file, you MUST run a vulnerability check on the entire workspace. This ensures that the new dependencies do not introduce any security risks. This step should be performed after all build errors are resolved. EXAMPLE: `go_vulncheck({"pattern":"./..."})`

-7. **Run tests**: Once `go_diagnostics` reports no errors (and ONLY once there are no errors), run the tests for the packages you have changed. You can do this with `go test [packagePath...]`. Don't run `go test ./...` unless the user explicitly requests it, as doing so may slow down the iteration loop.
-
+7. **Run tests**: Once `go_diagnostics` reports no errors (and ONLY once there are no errors), run the tests for the packages you have changed. You can do this with `go test [packagePath...]`. Don't run `go test ./...` unless the user explicitly requests it, as doing so may slow down the iteration loop.
\ No newline at end of file
diff --git a/gopls/internal/mcp/mcp.go b/gopls/internal/mcp/mcp.go
index 6f4a0b4..ff151a7 100644
--- a/gopls/internal/mcp/mcp.go
+++ b/gopls/internal/mcp/mcp.go
@@ -171,7 +171,8 @@
"go_symbol_references",
"go_search",
"go_file_context",
- "go_vulncheck"}
+ "go_vulncheck",
+ "go_simdmapper"}
disabledTools := append(defaultTools,
// The fileMetadata tool is redundant with fileContext.
[]string{"go_file_metadata",
@@ -309,6 +310,19 @@
If no directory is provided, it defaults to the workspace root.
If no pattern is provided, it defaults to "./...".`,
}, h.vulncheckHandler)
+ case "go_simdmapper":
+ mcp.AddTool(mcpServer, &mcp.Tool{
+ Name: "go_simdmapper",
+ Description: `Given a Go assembler flavor simd instruction, this
+tool will return the archsimd API form of it and its CPU feature requirement.
+For example, given argument {"asm": "VPADDD X2, X9, X2"}, the tool will return
+"""
+ if archsimd.X86.AVX() {
+ X2 = X2.Add(X9)
+ }
+"""
+This tool only supports amd64 instructions at this moment.`,
+ }, h.simdMapperHandler)
}
}

diff --git a/gopls/internal/mcp/simdmapper.go b/gopls/internal/mcp/simdmapper.go
new file mode 100644
index 0000000..e3701ea
--- /dev/null
+++ b/gopls/internal/mcp/simdmapper.go
@@ -0,0 +1,92 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mcp
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+ "strings"
+
+ "github.com/modelcontextprotocol/go-sdk/mcp"
+)
+
+type simdMapperParams struct {
+ Asm string `json:"asm" jsonschema:"the assembly instruction to be mapped to simd intrinsics."`
+}
+
+type simdAsm struct {
+ name string
+ operands []string
+}
+
+// TODO: generate the full list of opOrder and opShape from simdgen.
+
+// If an entry here, the final [simdAsm.operands] field
+// would be reordered by this map.
+// e.g. intially VPINSRD $7, DX, X9, X11 => name: VPINSRD, operands: [7, DX, X9, X11]
+// would be reordered to [X9, 7, DX, X11] by doing operand_new[i] = operand_old[opOrder["VPINSRD"][i]]
+var opOrder = map[string][]int{
+ "VPINSRD": {2, 0, 1, 3},
+}
+
+// Returns the shape of the operands by index, after reordering if applicable.
+var opShape = map[string][]string{
+ "VPADDD": {"32x4", "32x4", "32x4"},
+ "VPINSRD": {"32x4", "immediate", "float32", "32x4"},
+}
+
+func (h *handler) simdMapperHandler(ctx context.Context, req *mcp.CallToolRequest, params simdMapperParams) (*mcp.CallToolResult, any, error) {
+ query := params.Asm
+ if len(query) == 0 {
+ return nil, nil, fmt.Errorf("empty query")
+ }
+ var b strings.Builder
+
+ // parse the assembly instruction into simdAsm
+ // Example: VPADDD X2, X9, X2 => name: VPADDD, operands: [X2, X9, X2]
+ // Example: VPADDD (R11), X9, X2 => name: VPADDD, operands: [archsimd.Load32x4(r11), X9, X2]
+ // Example: VPADDD -7(DI)(R8*8), X2, K1, X2 => name: VPADDD, operands: [archsimd.Load32x4(di+r8*8-7), X2, K1, X2]
+ // Example: VPINSRD $7, DX, X9, X11 => name: VPINSRD, operands: [X9, 7, DX, X11]
+ var asm simdAsm
+ parts := strings.Fields(query)
+ if len(parts) == 0 {
+ return nil, nil, fmt.Errorf("empty query")
+ }
+ asm.name = parts[0]
+ asm.operands = parts[1:]
+ if _, ok := opOrder[asm.name]; ok {
+ newOps := make([]string, len(opOrder[asm.name]))
+ for i, op := range opOrder[asm.name] {
+ newOps[i] = asm.operands[op]
+ }
+ asm.operands = newOps
+ }
+ for i, op := range asm.operands {
+ if strings.HasPrefix(op, "$") {
+ asm.operands[i] = op[1:]
+ } else if strings.Contains(op, "(") {
+ addrParts := regexp.MustCompile(`(-?\d+)?(\(\w+\))?\(\w+\)`).FindStringSubmatch(op)
+ if len(addrParts) != 4 {
+ return nil, nil, fmt.Errorf("failed to parse address register: %s", op)
+ }
+ addr := ""
+ if addrParts[2] != "" {
+ addr += strings.Trim(strings.Trim(addrParts[2], "("), ")")
+ }
+ if addrParts[3] != "" {
+ addr += "+" + strings.Trim(strings.Trim(addrParts[3], "("), ")")
+ }
+ if addrParts[1] != "" {
+ addr += addrParts[1]
+ }
+ asm.operands[i] = fmt.Sprintf("archsimd.Load%s(%s)", opShape[asm.name][i], addr)
+ }
+ }
+ // Write the final result.
+ fmt.Fprintf(&b, "%s.%s(%s)\n", asm.operands[0], asm.name, strings.Join(asm.operands[1:], ", "))
+
+ return textResult(b.String()), nil, nil
+}

Change information

Files:
  • M gopls/internal/mcp/instructions.md
  • M gopls/internal/mcp/mcp.go
  • A gopls/internal/mcp/simdmapper.go
Change size: M
Delta: 3 files changed, 111 insertions(+), 3 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: Iaaee061ce2017293821f80e09d949c35cbf77568
Gerrit-Change-Number: 736123
Gerrit-PatchSet: 1
Gerrit-Owner: Junyang Shao <shaoj...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Junyang Shao (Gerrit)

unread,
Jan 16, 2026, 2:26:53 PM (yesterday) Jan 16
to goph...@pubsubhelper.golang.org, David Chase, Alan Donovan, Hongxiang Jiang, golang-co...@googlegroups.com
Attention needed from Alan Donovan, David Chase and Hongxiang Jiang

New activity on the change

Open in Gerrit

Related details

Attention is currently required from:
  • Alan Donovan
  • David Chase
  • Hongxiang Jiang
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: Iaaee061ce2017293821f80e09d949c35cbf77568
Gerrit-Change-Number: 736123
Gerrit-PatchSet: 8
Gerrit-Owner: Junyang Shao <shaoj...@google.com>
Gerrit-Reviewer: Alan Donovan <adon...@google.com>
Gerrit-Reviewer: David Chase <drc...@google.com>
Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
Gerrit-Attention: David Chase <drc...@google.com>
Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
Gerrit-Attention: Alan Donovan <adon...@google.com>
Gerrit-Comment-Date: Fri, 16 Jan 2026 19:26:49 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: No
unsatisfied_requirement
satisfied_requirement
open
diffy

Alan Donovan (Gerrit)

unread,
Jan 16, 2026, 3:08:30 PM (yesterday) Jan 16
to Junyang Shao, goph...@pubsubhelper.golang.org, David Chase, Hongxiang Jiang, golang-co...@googlegroups.com
Attention needed from David Chase, Hongxiang Jiang and Junyang Shao

Alan Donovan voted and added 1 comment

Votes added by Alan Donovan

Hold+1

1 comment

Patchset-level comments
File-level comment, Patchset 8 (Latest):
Alan Donovan . resolved

Thanks, this is an interesting idea. But it would be good to discuss it on the issue tracker before we commit to code. My main concerns are:

(a) that it is very obscure and likely to be useful to less than 1% of users;

(b) that gopls' MCP server should really be a set of thin wrappers around existing debugged and tested packages (e.g. LSP operations, analyzers) rather than large amounts of new logic; and

(c) it adds more than 300KB to the executable, and the big map may have a noticeable cost on allocation and init time. (Its costs are doubled because gopls has a second process for telemetry.) A lazily decompressed rodata section would be better.

Open in Gerrit

Related details

Attention is currently required from:
  • David Chase
  • Hongxiang Jiang
  • Junyang Shao
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Holds
    • requirement satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: tools
    Gerrit-Branch: master
    Gerrit-Change-Id: Iaaee061ce2017293821f80e09d949c35cbf77568
    Gerrit-Change-Number: 736123
    Gerrit-PatchSet: 8
    Gerrit-Owner: Junyang Shao <shaoj...@google.com>
    Gerrit-Reviewer: Alan Donovan <adon...@google.com>
    Gerrit-Reviewer: David Chase <drc...@google.com>
    Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
    Gerrit-Attention: David Chase <drc...@google.com>
    Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
    Gerrit-Attention: Junyang Shao <shaoj...@google.com>
    Gerrit-Comment-Date: Fri, 16 Jan 2026 20:08:27 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: Yes
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy
    Reply all
    Reply to author
    Forward
    0 new messages