I’ve been writing MCP servers for fun and profit lately, and noticed that with the right tools, it’s not just easy — it’s actually pretty fun!
A few people have asked for a guide on how I set things up. So here it is: my day-to-day stack, a working philosophy, and an example that you can reuse. This isn’t just a tech guide — it’s how to vibe while building real stuff.
🐝 The Stack I Use
🔧 ToolHive (core runtime)
Disclaimer: I am a maintainer in the ToolHive project. That being said, I do use the tool in my day-to-day, so I consider it a foundational piece of my workflow. ToolHive is a lightweight, developer-friendly MCP runtime I use to run, test, and deploy MCP servers — whether they’re containerized or not. It handles authentication, transports, and common runtime concerns so I can focus on building servers, not plumbing.
Servers I use ToolHive to run every day:
fetch: Lets the AI client fetch URLs and embed markdown into context.
github: Lets the AI client read GitHub repositories and create PRs.
ToolHive is open source and flexible — it can run your server locally or as a container without requiring a Dockerfile. Just point it to a Go binary or MCP server image and go.
💻 Golang + mcp-go
Go makes writing MCP servers minimal and straightforward. I’ve been using mcp-go
and mcp-go/server
to implement the protocol, and it’s been incredibly smooth.
🐳 ko (build tool)
I use ko
to build container images for my Go projects without needing Dockerfiles. It produces minimal, secure, and production-ready containers — great for MCP servers.
🔍 Testing & Linting
testify
– for expressive Go unit testsgolangci-lint
– for enforcing style and catching issues early
🌐 Anthropic’s Claude Sonnet 4
I’ve been using Claude Sonnet 4 with a lot of success lately. It’s great for coding!
🦘 Roo Code
Roo Code provides just what I need in terms of an LLM client. I don’t have a very complex setup, just MCP servers with the LLM I mentioned above, plus some custom prompts to take my Fedora Silverblue + toolbox Linux setup into account.
🌱 Philosophy: Vibe Coding Principles
Here are the core values I follow when writing MCP servers:
Principle | Why it matters |
---|---|
Minimalism | Don’t over-engineer. Use the fewest tools that do the job well. |
Observability | Understand every layer: protocols, requests, and failures. |
Security | Prefer non-root containers, minimal base images, and validated input. |
KISS + DRY | Simplicity and reuse always win over clever hacks. |
Rapid feedback | Run tests and linters constantly to stay in flow. |
Dev-First UX | Use tools like ToolHive that reduce friction and empower iteration. |
💡 Sample Prompt
Here's the kind of prompt I give myself when starting a new MCP server:
I'd like to start a new golang project. It'll be an MCP server that queries an OCI registry and image references. It'll be an SSE MCP server. We'll be using https://pkg.go.dev/github.com/mark3labs/mcp-go/mcp and https://pkg.go.dev/github.com/mark3labs/mcp-go/server so query those before going forward. Also, query https://pkg.go.dev/github.com/google/go-containerregistry and its subpackages since I'd like to use that library to query the registry. Let's gather all the information and plan ahead first. I'd like to have a very minimal and handy set of tools only. Let's also write tests using testify and run those often. Finally, in the container use golangci-lint to lint.
Run linting tools and unit tests often to ensure changes are correct.
Please think step by step about whether there exists a less over-engineered and yet simpler, more elegant, and more robust solution to the problem that accords with KISS and DRY principles.
You can copy this and adjust it for your own project — it's like setting your mindset before you even write the first main()
.
🏗️ Vibe Coding Dev Loop
I wish I could tell you that I just press enter and forget; But this is not the case. While I do allow the client to read files in the repository (I never have secrets in the repository anyways), I don’t allow any execute or write operations. So, I tend to read all diffs, and verify what the LLM is trying to execute. I look at this as more of a pair-programming exercise, where I have a conversation with the LLM and ensure it’s going the right way and following best practices. Sometimes this may be very fast, sometimes, not so much.
🧪 Dev Loop with ToolHive
ToolHive makes it easy to go from idea → tested server → deployed runtime:
1. Write your MCP server in Go using mcp-go
2. Add tests with testify
3. Add linting with golangci-lint
4. Build with ko (or just run it directly via ToolHive!)
5. Test locally with ToolHive:
$ thv run go://./cmd/my-server
6. Iterate fast, deploy when ready
You can run SSE servers, HTTP-based ones, or even non-containerized ones locally using just a single thv run
command.
🔒Lock it down!
Finally, don't forget that the regular software engineering best practices apply:
- Do automatic updates
- Use minimal container images
- Run vulnerability scanning constantly
- Set up vulnerability disclosure
- Publish signatures and provenance
- Publish an SBOM
All of these things are easy to do and you can even leverage an LLM to help you implement these!
Don't worry! In ToolHive we'll soon publish a list of heuristics for you to follow to publish a quality MCP server... Stay tuned!
🧭 TL;DR
Leverage MCP servers to enhance your workflow.
fetch
andgithub
are great!Write servers in Go with
mcp-go
andgo-containerregistry
Build minimal, secure images with ko
Keep dev feedback tight: testify + golangci-lint
Start with a clear prompt and let simplicity guide the implementation
Use ToolHive to simplify running and testing MCP servers
Top comments (0)