rshade

Go's ecosystem is going agent-readable

pkg.go.dev shipped an API built for LLMs, Go 1.27 moved the toolchain, and ax-go v0.7.0 cut one CLI's MCP tools from 37 to 14. Hand agents structure that's true.

by
#go#ai#agents

For years, the only way for a program to learn about a Go package was to scrape the HTML off pkg.go.dev and hope the markup didn’t shift under it.

That changed in June, when Google shipped a pkg.go.dev API: eight stateless JSON endpoints for package and module metadata, versions, symbols, who-imports-what, search, and vulnerabilities. There’s an OpenAPI spec and a reference CLI, pkgsite-cli. It has since graduated from v1beta to v1. “Structured API access has been one of the most highly requested features for pkg.go.dev for a while now,” the announcement says.

An API for a package registry isn’t news on its own. What it’s for is. Google names the first-class consumer directly:

LLMs and agents need precise context. This API provides the data required for agents and models to reason deterministically about Go packages.

Google Open Source Blog, “A new pkg.go.dev API for Go”

The registry didn’t just get an API. It got one designed for machines that read.

the same move, one level down

That’s the exact thing I’ve been building at the tool level with ax-go.

An agent meeting an ordinary CLI has to scrape all over again: parse --help, guess at the flags, run the command, and pray the output is stable. ax-go kills that. It ships a __schema command the agent runs to get the tool’s commands, flags, and types as JSON up front, and it keeps that output deterministic enough to pin: same inputs, same bytes. The same command even speaks MCP: __schema --as=mcp hands back that surface in the shape agent runtimes already expect.

Two levels, one shift:

scrape-and-guess (before)ask-and-get (now)
ecosystemparse pkg.go.dev HTMLpkg.go.dev JSON API
toolparse --help textax-go __schema

Same problem, same answer: stop making the machine reverse-engineer structure you could have just handed it.

dog-fooding the thing

The nice part is you can watch the two levels meet. Point the new API at ax-go’s own page:

curl https://pkg.go.dev/v1/package/github.com/rshade/ax-go

You get JSON back: module path, latest version, and the synopsis (“Package ax provides the Agentic Experience foundation for Go CLI tools.”). Symbols and versions are their own endpoints. A library whose whole job is making Go CLIs legible to agents, now legible at the registry level too, through an endpoint built for exactly that.

And look at what Google shipped as the reference client: pkgsite-cli, a Go command-line tool that consumes a JSON API. That’s not a coincidence, it’s the shape. A CLI over a structured backend, meant to be driven by a human, a script, or an agent without changing what it returns. It’s the same shape ax-go standardizes, shipped by the Go team as the front door to their own API.

what does Go 1.27 change for agents?

I drafted most of this post in July. Then Go 1.27 shipped in August and kept making the argument for me.

No flagship feature, just defaults moving:

The ecosystem answered in kind. The same month, JetBrains shipped Modern Go Guidelines, a plugin with a CLI an agent runs directly: list returns guidelines for your Go version, explain gives the worked case, both scoped to what’s pinned in go.mod. An IDE company, shipping a CLI whose intended reader is an agent.

Google says outright that the API is for agents. For the toolchain, that’s my read: the 1.27 release notes never say “agent” once. It’s showing up in the defaults, one JSON field and one vet check at a time. That’s what it looks like when an ecosystem stops debating a direction and starts assuming it.

why did ax-go add mcp.Exclude?

ax-go v0.7.0 landed September 24, and its best feature started as a bug report from finfocus, my FinOps CLI and an ax-go adopter.

On v0.6.0, finfocus __schema --as=mcp advertised 37 tools. Four kinds of them were traps:

v0.6.0 “tool”Why it’s a trap
the runnable rootopens a TUI
Cobra’s auto-added helpreturns prose
six group commandsonly print usage
analyzer servea gRPC handshake that never returns

Dispatch is serialized, so one call to a blocker stalls every call behind it. An agent reading that list had no way to tell the tools from the traps.

Hidden was the only lever, and it was the wrong one: it prunes the whole subtree and pulls the command out of --help for humans too. So v0.7.0 does two things. It skips groups and help on its own. And mcp.Exclude(cmd) marks one command as not-a-tool while leaving it in --help and leaving its subcommands alone.

The static __schema --as=mcp and the live server honor it the same way, so the list an agent reads up front is the list it can call. finfocus went from 37 tools to 14 (ax-go#253, finfocus#1509).

Structure only helps if it’s true. A machine-readable list that includes the things that hang the machine is worse than no list.

why it matters

When the platform underneath you decides agents are a first-class reader, the tools you build on top can stop pretending otherwise.

The cost of scraping was always paid in flakiness: the markup shifts, the --help reflows, the agent breaks. Structured endpoints are the ecosystem paying that bill down. Go is deciding, at the registry and in the toolchain defaults, that the next reader is a machine. I’ve been betting the same thing at the CLI. v0.7.0 is what the bet costs: the list has to be right.

related