* docs: compare Go Micro with Google ADK in the comparison guide Adds a 'vs Agent Frameworks (Google ADK)' section: ADK builds an agent, Go Micro builds the distributed system the agent lives in (agents are services in the mesh). Covers the category difference, a feature table, when to choose each, and MCP/A2A interoperability. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL * docs: replace ADK comparison slogan with concrete explanation State plainly what each tool provides (ADK builds an agent process; Go Micro builds the surrounding service mesh) instead of marketing phrasing. * lint: apply golangci-lint autofixes; exclude ST1003 and demo errcheck Mechanical, behaviour-preserving fixes applied by 'golangci-lint run --fix': gofmt, misspell (US spelling), usestdlibvars (http.Method*/Status*), unconvert, and the auto-fixable staticcheck simplifications (QF*, S1017/S1019/S1023/S1039). Config: exclude ST1003 (remaining offenders are exported API renames, e.g. web.Id, which would break compatibility) and skip errcheck for examples/ and internal/harness/ (demo code where fire-and-forget is intentional). Build and test compilation verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL * lint: WIP cleanup checkpoint (errcheck config + partial fixes) Checkpoint of an in-progress golangci-lint cleanup (background pass). Builds cleanly; lint is not yet zero. Follow-up commit will complete the cleanup and switch CI to a blocking full-tree lint. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL --------- Co-authored-by: Claude <noreply@anthropic.com>
Agent Tool Wrappers
Middleware around an agent's tool execution, the same way
client.CallWrapper and server.HandlerWrapper wrap RPCs.
Every tool call an agent makes runs through ai.ToolHandler:
type ToolHandler func(ctx context.Context, call ai.ToolCall) ai.ToolResult
type ToolWrapper func(ai.ToolHandler) ai.ToolHandler
WrapTool (exposed as micro.AgentWrapTool) registers a wrapper: it
takes the next handler and returns a new one. Code before next(...)
runs before the tool, code after runs after. That single seam covers
the whole lifecycle — before/after hooks, timing, metrics, retries,
inspecting results.
What this example does
One flaky weather service and one agent with two wrappers:
- observe — times every call and records a per-tool count, logging
the correlation ID (
call.ID) carried through from the provider. It observes; it changes nothing. - retry — re-runs a call whose result is an error, up to three attempts. The weather service fails the first time it's hit and succeeds after, so retry turns a transient failure into a success the model never sees.
Wrappers compose outermost-first: observe is registered first, so
it wraps retry and sees one logical call even when retry runs the tool
twice.
micro.NewAgent("forecaster",
micro.AgentServices("weather"),
micro.AgentProvider(provider),
micro.AgentAPIKey(apiKey),
micro.AgentWrapTool(m.observe, retry(3)),
)
Wrappers vs. guardrails
Developer wrappers run outside the built-in guardrails (MaxSteps,
LoopLimit, ApproveTool), so they see every call and its result —
including a guardrail's refusal. The flip side: a retry wrapper's
next is the full guardrail stack, so each retry is also counted by
loop detection. Keep LoopLimit at or above your retry count, or set
AgentLoopLimit(0) when a wrapper owns the repetition.
See the Agent Guardrails guide for the full picture.
Run
Needs an LLM provider key:
export ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY, GEMINI_API_KEY, ...
go run main.go