Skip to content
GlxymeshDocs
glxymesh.com
Select theme
Open console
Reading time 3 min

Write an MCP tool in Go

Build an MCP tool in Go with the official MCP Go SDK: the template, typed input schemas, logging, the host API, dependencies and custom builds.

Glxymesh tools are ordinary MCP servers written with the official MCP Go SDK. The only Glxymesh-specific part is the small host package, which provides what WebAssembly can’t provide by itself: outbound HTTP, storage, logs, the clock and randomness.

Terminal window
gxmesh new weather
File What it is
manifest.json The tool’s name and the hosts it’s allowed to reach
main.go The MCP server
go.mod The module and its pinned dependencies, for your editor and for local builds
README.md Notes on publishing and egress

The name has to be 1–64 characters of a-z, 0-9 and -. It identifies the tool within the project and also names its KV storage. If you change the name in manifest.json, the next push publishes a new tool alongside the old one, which you then remove with gxmesh rm.

package main
import (
"context"
"fmt"
"log"
"github.com/modelcontextprotocol/go-sdk/mcp"
"glxymesh.com/mcp-core/host"
)
type args struct {
City string `json:"city" jsonschema:"the city to report on"`
}
func main() {
server := mcp.NewServer(&mcp.Implementation{Name: "weather", Version: "0.1.0"}, nil)
mcp.AddTool(server, &mcp.Tool{Name: "weather", Description: "Current weather for a city"},
func(_ context.Context, _ *mcp.CallToolRequest, a args) (*mcp.CallToolResult, any, error) {
host.Logf(host.LevelInfo, "weather for %q", a.City)
return &mcp.CallToolResult{
Content: []mcp.Content{&mcp.TextContent{Text: fmt.Sprintf("It is sunny in %s", a.City)}},
}, nil, nil
})
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
log.Fatal(err)
}
}
  • The SDK generates the tool’s input schema from the args struct, and the jsonschema tag becomes each field’s description. Clients show these descriptions to the model, so write them for the model.
  • The server runs over stdio. The platform handles HTTP, authorization and routing, so your code never deals with them.
  • Write the tool’s Description carefully. It’s what the model reads when deciding whether to call the tool.
  • One module can register several MCP tools. Every name must be unique within the project; see one endpoint per project.

Everything in glxymesh.com/mcp-core/host:

Call Purpose
host.HTTPClient() A standard *http.Client whose requests go through the platform’s egress allowlist. Vendor SDKs that accept a custom client work with it unchanged.
host.KV.Get / Put / Delete / List Key/value storage that persists between calls.
host.Log(level, msg), host.Logf(level, format, ...) Logs shown in the console under this tool. Levels: LevelDebug, LevelInfo, LevelWarn, LevelError.
host.Now() The current time.
host.Rand(n) n cryptographically random bytes.

Secrets are read with os.Getenv; see Secrets.

A build from gxmesh push or the console compiles offline against a pinned set of modules: the Go standard library, github.com/modelcontextprotocol/go-sdk and glxymesh.com/mcp-core. Importing anything else fails with no required module provides package. Your go.mod is not used for these builds; the platform supplies its own.

The build takes every .go file in the tool’s directory and nothing in subdirectories, so keep the tool in one main package.

To use other modules, build the WebAssembly module yourself and push that. gxmesh push looks for a .wasm file next to the manifest with the same base name, manifest.wasm. If it finds one, it uploads that file instead of the source:

Terminal window
GOOS=wasip1 GOARCH=wasm go build -o manifest.wasm .
gxmesh push

The module must be wasip1 and must serve MCP over stdio, the same as the template. Any .go files in the directory are sent along with it, and are used only to generate the tool’s description in the console.

Terminal window
gxmesh push # build and publish to the active project
gxmesh tools # list what the project has
gxmesh rm weather # remove it

Pushing a tool that already exists replaces it. The runtime picks up new versions within about 15 seconds.