go-surgeon
Deterministic Go code editing for LLM agents. AST-based CLI and MCP server that replaces Edit, Read, and Grep on .go files for Claude Code, Cursor, and any agent.
Install / Use
claude mcp add JLugagne -- npx -y github:JLugagne/go-surgeonIf the server publishes to npm under a different name, use that package instead — check the repo README.
MCP Server
Model Context Protocol server
Quality Score
Category
AI & Machine LearningSupported Platforms
Skill content
View source on GitHubgo-surgeon
Deterministic Go code editing for LLM agents. No text patching. No broken builds.
Your agent shouldn't edit Go with Edit, Read, Grep, or Bash.
Go isn't just text, it's a tree of declarations. go-surgeon gives your agent a real AST-based toolkit — precise symbol lookup, structural edits, automatic goimports — exposed as an MCP server it uses instead of generic file tools.
One tool call per edit. Valid Go every time.
Quick Start • Why • MCP • Highlights • Safety
</div>The problem nobody admits
Ask your agent to update a single function in a 200-line Go file. Watch what happens:
- It
Reads the file, finds the function, plans the edit - It calls
Editwith a string replacement — misses a trailing tab - The patch fails. It re-reads the file. Tries again with the whole function body
- This time it forgets the
context.Contextimport go buildfails. It edits the import block — badly. Curly brace drift.- Three turns later you have a working file and no idea what changed.
Every Go dev using an LLM agent has lived this. The problem isn't the model's reasoning — text-level patching is fundamentally wrong for a structured language. Indentation, imports, braces: these aren't content, they're grammar. And grammar breaks loudly.
The fix
update(object="func", file="internal/catalog/domain/book.go", identifier="NewBook", content="""
func NewBook(title, author string) (*Book, error) {
return &Book{Title: title, Author: author}, nil
}
""")
✅ SUCCESS (update func): Updated NewBook in internal/catalog/domain/book.go
Located by AST identifier. Replaced by structural edit. Imports handled by goimports automatically.
The agent stops counting tabs and starts shipping logic.
Why go-surgeon
1. It replaces generic file tools for Go — everywhere
The MCP server ships with instructions telling the agent: for any .go file, use these tools instead of Edit / Write / Read / Grep / Glob / Bash. No more sed on Go source. No more grep -r that misses method receivers. No more Edit that forgets imports.
2. Edits are atomic, not conversational
Every tool is a structured operation. Either it succeeds or you get a clear error like ERROR (update func): node 'Book.Validate' not found in .... No silent half-edits. No "it kind of worked".
3. Your agent never manages imports or formatting
Content is raw Go source — no package declaration, no imports, no indentation. goimports runs on every mutation. An entire category of agent mistakes, permanently eliminated.
4. Interfaces and mocks stay in sync
interface action=add and interface action=update regenerate a function-field mock atomically. The compile-time assertion (var _ Repo = (*MockRepo)(nil)) blocks drift. scaffold kind=interface_from_type pulls an interface out of an existing struct in one command.
5. Edits can be as granular as a single field or line
The unified patch tool makes scoped edits with a target selector: edit inside a function body, add/rename/retype a single struct field, add or remove a single interface method and regenerate the mock — all without re-emitting the whole declaration.
6. Type-aware references and renames across the module
find_definition, find_references, and rename_symbol resolve the target via go/packages so they only touch identifiers that bind to the same types.Object — not other symbols that happen to share a name. They also accept module= to resolve into a dependency.
When go-surgeon helps vs. when Edit is fine
Use go-surgeon for:
- Exploring unfamiliar Go code (
symbol body=true context=filegives you a function body + full file outline in one call) - Resolving a
file:linebuild/stack diagnostic to a declaration (symbol file=... at_line=...) - Structural edits: adding/renaming struct fields, interface methods, managing imports
- Batch edits across many functions or files in one atomic operation
- Multi-step sessions where AST validation prevents compounding errors
- Any edit where import management matters
Edit is fine or better when:
- Single-line tweak in a file you already have open and know well
- Files outside Go:
.yaml,.md,.sh,Dockerfile, etc. - One-off prototyping where you don't need AST guarantees
- 3-line changes where the 200–500 ms MCP overhead isn't amortized
Each go-surgeon MCP call adds ~200–500 ms overhead vs a direct Edit. The break-even is roughly 5+ structural edits, or any task requiring AST-level guarantees (import management, type-aware renames, struct/interface modifications). For a single 3-line tweak in a file you already know, Edit is faster.
Install
Linux / macOS
curl -fsSL https://raw.githubusercontent.com/JLugagne/go-surgeon/main/install.sh | sh
Installs the latest release binary to ~/.local/bin (no root required). Override with INSTALL_DIR:
INSTALL_DIR=/usr/local/bin curl -fsSL https://raw.githubusercontent.com/JLugagne/go-surgeon/main/install.sh | sh
Self-update — once installed, keep the binary current with:
go-surgeon upgrade
Homebrew / Scoop / Windows — coming soon.
Quick Start
# Verify installation
go-surgeon --version
# Run as MCP server (stdio)
go-surgeon mcp
# Or use the CLI directly
go-surgeon graph
go-surgeon symbol BookHandler.Handle --body
Configure your MCP client (example for Claude Code / Cursor):
{
"mcpServers": {
"go-surgeon": {
"command": "go-surgeon",
"args": ["mcp"]
}
}
}
The server auto-advertises instructions telling the agent to use go-surgeon tools for every operation on .go files — no prompt engineering required on your side.
🔌 MCP Server
go-surgeon mcp
Tools over stdio, grouped by purpose:
| Tools | Purpose |
|---|---|
| overview, symbol | Explore packages and look up symbols — replaces Read / Grep / Glob. symbol also resolves a file:at_line diagnostic directly to its declaration. |
| find_definition, find_references, rename_symbol | Type-aware cross-package symbol lookup and rename — powered by go/packages. All three accept module= to resolve into a dependency. |
| create, update, delete | Add, replace, or remove a file, function, or struct by AST identifier — replaces Edit / Write. object="auto" infers from the content; delete object="file" removes the file from disk. |
| patch | Unified surgical editor — one tool, five targets (function, struct, interface, file, decl). Scoped in-place edits without re-emitting whole declarations. Function ops include replace, insert_before/insert_after, delete, wrap, and set_signature (rewrite params/returns without touching the body). |
| patch with items: [{...}] | Apply many patch operations to many targets in a single atomic call — useful when one refactor touches dozens of functions or structs. |
| insert_call | Insert a single statement into a function body (before-return, end-of-body, or after:<marker>); auto-lifts out of nested scopes |
| interface (action=add\|update\|delete) | Manage interfaces with auto-generated (and auto-deleted) mocks |
| scaffold (kind=impl_from_interface\|mock_from_interface\|interface_from_type) | Generate stubs, standalone mocks, and extract interfaces from structs |
| test, tag | Generate test skeletons and struct field tags |
| build_check, test_run | Compile-verify and run tests in-loop. Both accept affected_by=<file> to narrow to the file's reverse-dep closure; test_run also accepts symbols=["pkg.MyFunc"] to auto-resolve owning packages and build a -run filter, plus verbosity=summary for compact output on large suites. |
| execute_plan | Run up to 15 edits atomically from a YAML/JSON plan — supports every action type including every patch target |
| batch_query | Run up to 10 read-only queries (symbol / overview / find_definition / find_references) in one round-trip |
| (none — discovery is CLI-only) | Run go-surgeon discovery for the grouped catalog, go-surgeon discovery <tool> for detail. Run go-surgeon skill --out .claude/skills/go-surgeon/ to install go-surgeon as a Claude skill. |
Every write tool supports preview=true to return a unified diff without writing. Errors carry a structured {code, message} in StructuredContent so agents can retry on CONFLICT, NOT_FOUND, PATCH_FAILED, PATCH_REPLACE_NOT_APPLIED, PATCH_DROPPED_CONTENT, PATCH_PRODUCES_INVALID_GO, etc. without string-matching.
See USAGE.md for the full parameter reference.
Highlighted features
symbol body=true context=file — explore a 1000-line file in 4 calls
symbol with body=true and context="file" returns the full body of the target function and an outline of every sibling declaration in the same file — in one call.
symbol(query="BookHandler.Create", body=true, context="file")
This replaces what used to be: read the file, grep for the function, read again with offset, grep for related symbols. Measured on a 1000-line file: 4 calls instead of 15.
Use this as your first move when entering any unfamiliar file — you get the implementation you care about plus a map of everything around it.
symbol file=… at_line=… — resolve a build error to a declaration
When build_check or a stack trace gives you internal/foo/bar.go:142, you don't need to look up the symbol name. Pass the line and symbol returns the outermost named declaration that spans it:
symbol(file="internal/foo/bar.go", at_line=142, body=true)
Mutually exclusive with query/pattern. Saves the "grep for the function around this line" step entirely.
execute_plan — atomic multi-step refactors
Refactoring a feature often means changing a struct, updating three methods, regenerating a mock, and wiring a new call. Doing this as 8 separate Edit operations is where agents drift the most.
actions:
- action: update_struct
file: internal/catalog/domain/book.go
identifier: Book
content: |
type Book struct {
ID BookID
Title string
Status BookStatus
CreatedAt time.Time
}
- action: update_func
file: internal/catalog/domain/book.go
identifier: NewBook
content: |
func NewBook(title string, status BookStatus) (*Book, error) {
return &Book{ID: NewBookID(), Title: title, Status: status}, nil
}
- action: update_interface
file: internal/catalog/domain/repositories/book/book.go
identifier: BookRepository
mock_file: internal/catalog/domain/repositories/book/booktest/mock.go
mock_name: MockBookRepository
content: |
type BookRepository interface {
Create(ctx context.Context, book domain.Book) error
UpdateStatus(ctx context.Context, id BookID, status BookStatus) error
}
- action: insert_call
file: internal/catalog/app/init.go
identifier: NewApp
content: handlers.RegisterBookStatusHandler(mux, repo)
position: before-return
One tool call. One success or one rollback. No drift between steps. Every individual patch_* action type is also accepted, so atomic multi-step plans can mix in-place patches with whole-declaration replacements.
patch — one tool, five targets, surgical edits
Classic AST edit tools make you resend the whole declaration to change one line. The unified patch tool applies scoped, text-match-or-line-target
Truncated for display — read the full file on GitHub.
Related Skills
claude-mem
92.9kPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode + More
Understand-Anything
81.3kGraphs that teach > graphs that impress. Turn any code into an interactive knowledge graph you can explore, search, and ask questions about. Works with Claude Code, Codex, Cursor, Copilot, Gemini CLI, and more.
Agent-Reach
77.4kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
ruflo
70.1k🌊 The original agent meta-harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, RAG integration, and native Claude Code / Codex / Hermes and many more Integrated
