chore: import upstream snapshot with attribution
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:33 +08:00
commit e071084ebe
982 changed files with 160368 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
_site
+6
View File
@@ -0,0 +1,6 @@
# frozen_string_literal: true
source "https://rubygems.org"
# gem "rails"
gem 'github-pages', group: :jekyll_plugins
+282
View File
@@ -0,0 +1,282 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (7.2.2.1)
base64
benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
base64 (0.2.0)
benchmark (0.4.0)
bigdecimal (3.1.9)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.12.2)
colorator (1.1.0)
commonmarker (0.23.11)
concurrent-ruby (1.3.5)
connection_pool (2.5.3)
csv (3.3.4)
dnsruby (1.72.4)
base64 (~> 0.2.0)
logger (~> 1.6.5)
simpleidn (~> 0.2.1)
drb (2.2.3)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
ethon (0.16.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.10.0)
faraday (2.13.1)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.4.0)
net-http (>= 0.5.0)
ffi (1.17.2)
forwardable-extended (2.6.0)
gemoji (4.1.0)
github-pages (232)
github-pages-health-check (= 1.18.2)
jekyll (= 3.10.0)
jekyll-avatar (= 0.8.0)
jekyll-coffeescript (= 1.2.2)
jekyll-commonmark-ghpages (= 0.5.1)
jekyll-default-layout (= 0.1.5)
jekyll-feed (= 0.17.0)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.16.1)
jekyll-include-cache (= 0.2.1)
jekyll-mentions (= 1.6.0)
jekyll-optional-front-matter (= 0.3.2)
jekyll-paginate (= 1.1.0)
jekyll-readme-index (= 0.3.0)
jekyll-redirect-from (= 0.16.0)
jekyll-relative-links (= 0.6.1)
jekyll-remote-theme (= 0.4.3)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.8.0)
jekyll-sitemap (= 1.4.0)
jekyll-swiss (= 1.0.0)
jekyll-theme-architect (= 0.2.0)
jekyll-theme-cayman (= 0.2.0)
jekyll-theme-dinky (= 0.2.0)
jekyll-theme-hacker (= 0.2.0)
jekyll-theme-leap-day (= 0.2.0)
jekyll-theme-merlot (= 0.2.0)
jekyll-theme-midnight (= 0.2.0)
jekyll-theme-minimal (= 0.2.0)
jekyll-theme-modernist (= 0.2.0)
jekyll-theme-primer (= 0.6.0)
jekyll-theme-slate (= 0.2.0)
jekyll-theme-tactile (= 0.2.0)
jekyll-theme-time-machine (= 0.2.0)
jekyll-titles-from-headings (= 0.5.3)
jemoji (= 0.13.0)
kramdown (= 2.4.0)
kramdown-parser-gfm (= 1.1.0)
liquid (= 4.0.4)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.16.2, < 2.0)
rouge (= 3.30.0)
terminal-table (~> 1.4)
webrick (~> 1.8)
github-pages-health-check (1.18.2)
addressable (~> 2.3)
dnsruby (~> 1.60)
octokit (>= 4, < 8)
public_suffix (>= 3.0, < 6.0)
typhoeus (~> 1.3)
html-pipeline (2.14.3)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.8.0)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
jekyll (3.10.0)
addressable (~> 2.4)
colorator (~> 1.0)
csv (~> 3.0)
em-websocket (~> 0.5)
i18n (>= 0.7, < 2)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (>= 1.17, < 3)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
webrick (>= 1.0)
jekyll-avatar (0.8.0)
jekyll (>= 3.0, < 5.0)
jekyll-coffeescript (1.2.2)
coffee-script (~> 2.2)
coffee-script-source (~> 1.12)
jekyll-commonmark (1.4.0)
commonmarker (~> 0.22)
jekyll-commonmark-ghpages (0.5.1)
commonmarker (>= 0.23.7, < 1.1.0)
jekyll (>= 3.9, < 4.0)
jekyll-commonmark (~> 1.4.0)
rouge (>= 2.0, < 5.0)
jekyll-default-layout (0.1.5)
jekyll (>= 3.0, < 5.0)
jekyll-feed (0.17.0)
jekyll (>= 3.7, < 5.0)
jekyll-gist (1.5.0)
octokit (~> 4.2)
jekyll-github-metadata (2.16.1)
jekyll (>= 3.4, < 5.0)
octokit (>= 4, < 7, != 4.4.0)
jekyll-include-cache (0.2.1)
jekyll (>= 3.7, < 5.0)
jekyll-mentions (1.6.0)
html-pipeline (~> 2.3)
jekyll (>= 3.7, < 5.0)
jekyll-optional-front-matter (0.3.2)
jekyll (>= 3.0, < 5.0)
jekyll-paginate (1.1.0)
jekyll-readme-index (0.3.0)
jekyll (>= 3.0, < 5.0)
jekyll-redirect-from (0.16.0)
jekyll (>= 3.3, < 5.0)
jekyll-relative-links (0.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-remote-theme (0.4.3)
addressable (~> 2.0)
jekyll (>= 3.5, < 5.0)
jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
rubyzip (>= 1.3.0, < 3.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.8.0)
jekyll (>= 3.8, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-swiss (1.0.0)
jekyll-theme-architect (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-cayman (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-dinky (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-hacker (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-merlot (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-midnight (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.6.0)
jekyll (> 3.5, < 5.0)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.0)
jekyll-theme-slate (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-tactile (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-time-machine (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.3)
jekyll (>= 3.3, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
jemoji (0.13.0)
gemoji (>= 3, < 5)
html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0)
json (2.12.0)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.4)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.6.6)
mercenary (0.3.6)
mini_portile2 (2.8.9)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.25.5)
net-http (0.6.0)
uri
nokogiri (1.18.8)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
octokit (4.25.1)
faraday (>= 1, < 3)
sawyer (~> 0.9)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (5.1.1)
racc (1.8.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rexml (3.4.1)
rouge (3.30.0)
rubyzip (2.4.1)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
securerandom (0.4.1)
simpleidn (0.2.3)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
typhoeus (1.4.1)
ethon (>= 0.9.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (1.8.0)
uri (1.0.3)
webrick (1.9.1)
PLATFORMS
x86_64-linux
DEPENDENCIES
github-pages
BUNDLED WITH
2.3.18
+3
View File
@@ -0,0 +1,3 @@
# Website
The Go Micro website including docs
+14
View File
@@ -0,0 +1,14 @@
title: Docs
description: "A Go microservices framework"
baseurl: "" # served at root; docs under /docs/ paths
url: "https://go-micro.dev" # domain host
# Enable syntax highlighting
highlighter: rouge
markdown: kramdown
kramdown:
input: GFM
syntax_highlighter: rouge
syntax_highlighter_opts:
line_numbers: false
+111
View File
@@ -0,0 +1,111 @@
core:
- title: Overview
url: /docs/
- title: Getting Started
url: /docs/getting-started.html
- title: Install Troubleshooting
url: /docs/guides/install-troubleshooting.html
- title: AI Integration
url: /docs/ai-integration.html
- title: No-secret First Agent
url: /docs/guides/no-secret-first-agent.html
- title: Your First Agent
url: /docs/guides/your-first-agent.html
- title: 0→hero Reference
url: /docs/guides/zero-to-hero.html
- title: MCP & AI Agents
url: /docs/mcp.html
- title: Deployment
url: /docs/deployment.html
- title: Architecture
url: /docs/architecture.html
- title: Configuration
url: /docs/config.html
- title: Observability
url: /docs/observability.html
interfaces:
- title: Registry
url: /docs/registry.html
- title: Broker
url: /docs/broker.html
- title: Transport
url: /docs/transport.html
- title: Store
url: /docs/store.html
- title: Plugins
url: /docs/plugins.html
examples:
- title: Learn by Example
url: /docs/examples/
- title: Real-World Examples
url: /docs/examples/realworld/
guides:
- title: Debugging your agent
url: /docs/guides/debugging-agents.html
- title: micro loop quickstart
url: /docs/guides/micro-loop.html
- title: Plan & Delegate
url: /docs/guides/plan-delegate.html
- title: Agent Guardrails
url: /docs/guides/agent-guardrails.html
- title: Agents and Workflows
url: /docs/guides/agents-and-workflows.html
- title: Agent Integration Patterns
url: /docs/guides/agent-patterns.html
- title: The Agent Harness
url: /docs/guides/agent-harness.html
- title: Agent Loops
url: /docs/guides/agent-loops.html
- title: Agent2Agent (A2A)
url: /docs/guides/a2a-protocol.html
- title: AI Provider Guide
url: /docs/guides/ai-provider-guide.html
- title: Provider Conformance
url: /docs/guides/provider-conformance.html
- title: Atlas Cloud Integration
url: /docs/guides/atlascloud-integration.html
- title: Payments (x402)
url: /docs/guides/x402-payments.html
- title: Comparison
url: /docs/guides/comparison.html
- title: Migration Guides
url: /docs/guides/migration/
project:
- title: Commercial Support
url: /docs/support.html
- title: ADR Index
url: /docs/architecture/
- title: Contributing
url: /docs/contributing.html
- title: Roadmap
url: /docs/roadmap.html
- title: Get Badge
url: /badge.html
- title: Server (optional)
url: /docs/server.html
search_order:
- /docs/guides/install-troubleshooting.html
- /docs/guides/your-first-agent.html
- /docs/guides/zero-to-hero.html
- /docs/guides/debugging-agents.html
- /docs/guides/micro-loop.html
- /docs/getting-started.html
- /docs/mcp.html
- /docs/architecture.html
- /docs/config.html
- /docs/observability.html
- /docs/registry.html
- /docs/broker.html
- /docs/transport.html
- /docs/store.html
- /docs/plugins.html
- /docs/examples/
- /docs/examples/realworld/
- /docs/guides/provider-conformance.html
- /docs/guides/comparison.html
- /docs/guides/migration/
- /docs/architecture/
- /docs/contributing.html
- /docs/roadmap.html
- /docs/support.html
- /docs/server.html
@@ -0,0 +1,5 @@
<a href="/docs/">Docs</a> &middot;
<a href="/blog/">Blog</a> &middot;
<a href="https://github.com/micro/go-micro">GitHub</a> &middot;
<a href="https://discord.gg/G8Gk5j3uXr">Discord</a> &middot;
<a href="/support">Support</a>
@@ -0,0 +1,6 @@
<footer class="footer">
<div>&copy; {{ site.time | date: '%Y' }} Go Micro. Apache 2.0 Licensed.</div>
<div>
{% include footer-links.html %}
</div>
</footer>
@@ -0,0 +1,6 @@
<nav class="nav">
<a class="nav-brand" href="/">Go Micro</a>
<div class="nav-links">
{% include nav-links.html %}
</div>
</nav>
@@ -0,0 +1,5 @@
<a href="/docs/">Docs</a>
<a href="/blog/">Blog</a>
<a href="/support"{% if page.nav_active == 'support' %} class="active"{% endif %}>Support</a>
<a href="https://discord.gg/G8Gk5j3uXr">Discord</a>
<a href="https://github.com/micro/go-micro">GitHub</a>
+87
View File
@@ -0,0 +1,87 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% if page.title %}{{ page.title }} | {% endif %}Go Micro Blog</title>
<meta name="description" content="{{ page.description | default: 'Go Micro Blog - News, updates, and tutorials for the Go Micro framework' }}">
<style>
:root {
--bg: #ffffff;
--border: #e5e5e5;
--accent: #0366d6;
}
* { box-sizing: border-box; }
body { font-family: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif; margin:0; background: var(--bg); color:#1a1a1a; line-height: 1.7; }
a { color: var(--accent); text-decoration:none; }
a:hover { text-decoration:underline; }
/* Nav — matches landing page */
.site-nav { display:flex; align-items:center; justify-content:space-between; max-width: 1100px; margin:0 auto; padding:.75rem 1.5rem; border-bottom: 1px solid var(--border); position:sticky; top:0; z-index:20; background:#fff; }
.nav-brand { display:flex; align-items:center; gap:.5rem; font-weight:700; font-size:1.1rem; color:#1a1a1a; text-decoration:none; }
.nav-brand img { height:32px; }
.nav-brand:hover { text-decoration: none; }
.nav-links { display:flex; align-items:center; gap:1.25rem; font-size:.9rem; }
.nav-links a { color:#555; font-weight:500; }
.nav-links a:hover { color: var(--accent); text-decoration:none; }
/* Blog container */
.container { max-width: 760px; margin: 0 auto; padding: 2.5rem 1.5rem 4rem; }
.blog-header { margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 1px solid var(--border); }
.blog-header h1 { margin: 0 0 0.5rem; font-size: 2rem; color: #0d1117; }
.blog-header .meta { color: #888; font-size: 0.9rem; }
/* Article */
article h1 { font-size: 2.25rem; margin: 0 0 0.5rem; line-height: 1.3; color: #0d1117; }
article .meta { color: #888; font-size: 0.9rem; margin-bottom: 2rem; }
article h2 { font-size: 1.5rem; margin-top: 2.5rem; color: #0d1117; }
article h3 { font-size: 1.25rem; margin-top: 2rem; }
article p { margin: 1rem 0; }
article ul, article ol { margin: 1rem 0; padding-left: 1.5rem; }
article li { margin: 0.5rem 0; }
article img { border-radius: 8px; max-width: 100%; }
/* Code */
pre, code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
pre { background:#f6f8fa; border:1px solid #d0d7de; padding:1rem; border-radius:6px; overflow-x:auto; font-size: 0.88rem; }
code { background: #f6f8fa; padding: 0.1rem 0.3rem; border-radius: 3px; font-size: 0.9em; }
pre code { background: none; padding: 0; }
blockquote { margin: 1.5rem 0; padding: 0.5rem 1rem; border-left: 4px solid var(--accent); background: #f6f8fa; border-radius: 0 6px 6px 0; }
blockquote p { margin: 0; }
table { border-collapse:collapse; width:100%; }
th, td { border:1px solid #d0d7de; padding:.5rem .6rem; text-align:left; }
th { background:#f6f8fa; font-weight: 600; }
.post-nav { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--border); display: flex; justify-content: space-between; font-size: 0.9rem; }
/* Footer — matches landing page */
.site-footer { max-width: 760px; margin: 0 auto; padding: 2rem 1.5rem; border-top: 1px solid var(--border); font-size: .85rem; color: #666; display:flex; justify-content:space-between; flex-wrap:wrap; gap:1rem; }
.site-footer a { color: var(--accent); }
@media (max-width: 600px) {
.container { padding: 1.5rem 1rem; }
article h1 { font-size: 1.75rem; }
.site-nav { padding: 0.5rem 1rem; }
.nav-links { gap: 0.75rem; font-size: 0.8rem; }
.site-footer { flex-direction: column; text-align: center; }
}
</style>
</head>
<body>
<nav class="site-nav">
<a class="nav-brand" href="/">Go Micro</a>
<div class="nav-links">
{% include nav-links.html %}
</div>
</nav>
<div class="container">
{{ content }}
</div>
<footer class="site-footer">
<div>&copy; {{ site.time | date: '%Y' }} Go Micro. Apache 2.0 Licensed.</div>
<div>
{% include footer-links.html %}
</div>
</footer>
</body>
</html>
+198
View File
@@ -0,0 +1,198 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% if page.title %}{{ page.title }} | {% endif %}Go Micro Documentation</title>
<style>
:root {
--bg: #ffffff;
--border: #e5e5e5;
--sidebar-width: 240px;
--accent: #0366d6;
}
* { box-sizing: border-box; }
body { font-family: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif; margin:0; background: var(--bg); color:#1a1a1a; line-height: 1.6; }
a { color: var(--accent); text-decoration:none; }
a:hover { text-decoration:underline; }
/* Nav — matches landing page */
.site-nav { display:flex; align-items:center; justify-content:space-between; max-width: 1100px; margin:0 auto; padding:.75rem 1.5rem; border-bottom: 1px solid var(--border); position:sticky; top:0; z-index:20; background:#fff; }
.nav-brand { display:flex; align-items:center; gap:.5rem; font-weight:700; font-size:1.1rem; color:#1a1a1a; text-decoration:none; }
.nav-brand img { height:32px; }
.nav-brand:hover { text-decoration: none; }
.nav-links { display:flex; align-items:center; gap:1.25rem; font-size:.9rem; }
.nav-links a { color:#555; font-weight:500; }
.nav-links a:hover { color: var(--accent); text-decoration:none; }
.nav-left { display:flex; align-items:center; gap:.5rem; }
.menu-toggle { display:none; background:none; border:none; color: var(--accent); cursor:pointer; font-size:1.4rem; line-height:1; padding:0 .25rem; }
/* Layout */
.layout { display:flex; align-items:flex-start; max-width: 1100px; margin:0 auto; padding:0 1.25rem 4rem; }
.sidebar { width:var(--sidebar-width); padding:1.5rem .75rem 2rem; border-right:1px solid var(--border); position:sticky; top:55px; max-height:calc(100vh - 55px); overflow:auto; font-size:.88rem; flex-shrink: 0; }
.sidebar h4 { margin:1.4rem 0 .5rem; font-size:.7rem; text-transform:uppercase; letter-spacing:.06em; color:#888; font-weight: 600; }
.sidebar ul { list-style:none; margin:0; padding:0; }
.sidebar li { margin:.3rem 0; }
.sidebar a { color:#444; display:block; padding:.3rem .5rem; border-radius:5px; transition: background .15s; }
.sidebar a:hover { background:#f0f4ff; color: var(--accent); text-decoration: none; }
.content { flex:1; min-width:0; padding:2rem 2.5rem; }
.content .markdown-body { max-width: 100%; }
.content h1 { font-size: 2rem; margin-bottom: 0.5rem; color: #0d1117; }
.content h2 { font-size: 1.4rem; margin-top: 2.5rem; color: #0d1117; }
.content h3 { font-size: 1.15rem; margin-top: 2rem; }
.content p { margin: 1rem 0; }
.content img { border-radius: 8px; }
/* Code */
pre, code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; }
pre { background:#f6f8fa; border:1px solid #d0d7de; padding:.9rem 1rem; border-radius:6px; overflow-x:auto; max-width:100%; font-size: 0.88rem; }
code { word-wrap: break-word; background: #f6f8fa; padding: 0.1rem 0.3rem; border-radius: 3px; font-size: 0.9em; }
pre code { word-wrap: normal; background: none; padding: 0; }
/* Tables */
table { border-collapse:collapse; width:100%; overflow-x:auto; display:block; }
th, td { border:1px solid #d0d7de; padding:.5rem .6rem; text-align:left; }
th { background:#f6f8fa; font-weight: 600; }
img { max-width:100%; height:auto; }
/* Footer — matches landing page */
.site-footer { max-width:1100px; margin:0 auto; padding:2rem 1.5rem; border-top:1px solid var(--border); font-size:.85rem; color:#666; display:flex; justify-content:space-between; flex-wrap:wrap; gap:1rem; }
.site-footer a { color: var(--accent); }
/* Dark mode */
body.dark { --bg:#0d1117; --border:#30363d; color:#e6edf3; background:#0d1117; }
body.dark .site-nav, body.dark .sidebar, body.dark .content, body.dark .site-footer { background:#0d1117; }
body.dark .nav-brand, body.dark .sidebar a { color: #e6edf3; }
body.dark a { color:#58a6ff; }
body.dark pre { background:#161b22; border-color:#30363d; }
body.dark code { background:#161b22; }
body.dark .sidebar a:hover { background:#161b22; }
body.dark th { background:#161b22; }
.sidebar-overlay { display:none; position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:25; }
.sidebar-overlay.active { display:block; }
@media (max-width: 900px) {
.site-nav { padding:.5rem 1rem; }
.nav-links { gap:.75rem; font-size:.8rem; }
.menu-toggle { display:block; }
.layout { padding:0; }
.sidebar { position:fixed; left:-100%; top:55px; bottom:0; width:280px; max-width:85vw; background:var(--bg); border-right:1px solid var(--border); z-index:30; transition:left 0.3s ease; overflow-y:auto; padding:1rem; }
.sidebar.active { left:0; }
.content { width:100%; padding:1.5rem 1rem; }
.content h1 { font-size:1.75rem; margin-top:0; }
.content h2 { font-size:1.4rem; }
.content h3 { font-size:1.15rem; }
pre { padding:.6rem; font-size:.8rem; overflow-x:auto; white-space:pre; }
table { font-size:.85rem; }
.site-footer { flex-direction: column; text-align: center; padding:1.5rem 1rem; font-size:.75rem; }
}
@media (max-width: 480px) {
.nav-links { gap:.5rem; font-size:.75rem; }
.content { padding:1rem .75rem; }
.content h1 { font-size:1.5rem; }
.content h2 { font-size:1.25rem; }
pre { font-size:.75rem; padding:.5rem; }
.sidebar { width:100%; max-width:100%; }
}
</style>
</head>
<body>
<nav class="site-nav">
<div class="nav-left">
<button class="menu-toggle" id="menuToggle" aria-label="Toggle sidebar"></button>
<a class="nav-brand" href="/">Go Micro</a>
</div>
<div class="nav-links">
{% include nav-links.html %}
</div>
</nav>
<div class="sidebar-overlay" id="sidebarOverlay"></div>
<div class="layout">
<aside class="sidebar">
{% assign nav = site.data.navigation %}
{% for section in nav %}
{% unless section[0] == 'search_order' %}
<h4>{{ section[0] | capitalize }}</h4>
<ul>
{% for item in section[1] %}
<li><a href="{{ item.url }}" {% if item.url == page.url %}style="font-weight:600; color: var(--accent);"{% endif %}>{{ item.title }}</a></li>
{% endfor %}
</ul>
{% endunless %}
{% endfor %}
</aside>
<main class="content markdown-body">
{% assign crumbs = page.url | split:'/' %}
{% assign docs_root = site.baseurl | append: '/' %}
{% if page.url != docs_root and page.url contains docs_root %}
<nav style="font-size:.75rem; margin-bottom:1rem; color: #888;">
<a href="/docs/">Docs</a>
{% capture path_acc %}/docs{% endcapture %}
{% for c in crumbs %}
{% if forloop.index0 > 1 and c != '' %}
{% capture path_acc %}{{ path_acc }}/{{ c }}{% endcapture %}
<a href="{{ path_acc }}/">{{ c | replace:'.html','' | replace:'index','' | replace:'realworld','Real-World' | replace:'guides','Guides' | replace:'migration','Migration' | replace:'architecture','Architecture' | replace:'examples','Examples' | replace:'config','Configuration' | replace:'observability','Observability' | capitalize }}</a>
{% endif %}
{% endfor %}
</nav>
{% endif %}
{{ content }}
{% assign order = site.data.navigation.search_order %}
{% if page.url %}
{% assign current_index = -1 %}
{% for u in order %}
{% if u == page.url %}{% assign current_index = forloop.index0 %}{% endif %}
{% endfor %}
{% if current_index != -1 %}
<hr style="margin:2.5rem 0;" />
<div style="display:flex; justify-content:space-between; font-size:.85rem;">
<div>
{% if current_index > 0 %}
{% assign prev_url = order[current_index | minus: 1] %}
<a href="{{ prev_url }}">&larr; Previous</a>
{% endif %}
</div>
<div>
{% assign next_index = current_index | plus: 1 %}
{% if next_index < order.size %}
{% assign next_url = order[next_index] %}
<a href="{{ next_url }}">Next &rarr;</a>
{% endif %}
</div>
</div>
{% endif %}
{% endif %}
</main>
</div>
<footer class="site-footer">
<div>&copy; {{ site.time | date: '%Y' }} Go Micro. Apache 2.0 Licensed.</div>
<div>
{% include footer-links.html %}
</div>
</footer>
<script>
(function(){
const key='gm.dark';
function apply(){
if(localStorage.getItem(key)==='1') document.body.classList.add('dark');
else document.body.classList.remove('dark');
}
apply();
var btn=document.getElementById('dark-toggle');
if(btn) btn.addEventListener('click', function(){
localStorage.setItem(key, localStorage.getItem(key)==='1' ? '0' : '1');
apply();
});
var menuToggle = document.getElementById('menuToggle');
var sidebar = document.querySelector('.sidebar');
var overlay = document.getElementById('sidebarOverlay');
if(menuToggle && sidebar && overlay){
menuToggle.addEventListener('click', function(){ sidebar.classList.toggle('active'); overlay.classList.toggle('active'); });
overlay.addEventListener('click', function(){ sidebar.classList.remove('active'); overlay.classList.remove('active'); });
sidebar.querySelectorAll('a').forEach(function(link){ link.addEventListener('click', function(){ sidebar.classList.remove('active'); overlay.classList.remove('active'); }); });
}
})();
</script>
</body>
</html>
+202
View File
@@ -0,0 +1,202 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Powered by Go Micro Badge</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
line-height: 1.6;
color: #333;
background: #f6f8fa;
padding: 2rem 1rem;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 3rem 2.5rem;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}
h1 {
font-size: 2rem;
margin-bottom: 0.5rem;
color: #1a1a1a;
}
.subtitle {
color: #666;
font-size: 1.1rem;
margin-bottom: 2rem;
}
h2 {
font-size: 1.5rem;
margin: 2rem 0 1rem;
color: #0366d6;
}
h3 {
font-size: 1.15rem;
margin: 1.5rem 0 0.75rem;
color: #333;
}
.badge-preview {
background: #f8f9fa;
border: 1px solid #e5e5e5;
border-radius: 8px;
padding: 1.5rem;
margin: 1rem 0;
text-align: center;
}
.badge-preview img {
display: inline-block;
margin: 0.5rem 0;
}
pre {
background: #f6f8fa;
border: 1px solid #d0d7de;
border-radius: 6px;
padding: 1rem;
overflow-x: auto;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 0.9rem;
margin: 0.75rem 0;
}
code {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
background: #f6f8fa;
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-size: 0.9rem;
}
pre code {
background: none;
padding: 0;
}
.back-link {
display: inline-block;
margin-bottom: 1.5rem;
color: #0366d6;
text-decoration: none;
font-weight: 500;
}
.back-link:hover {
text-decoration: underline;
}
.guideline {
background: #e8f4fd;
border-left: 4px solid #0366d6;
padding: 1rem 1.25rem;
margin: 1.5rem 0;
border-radius: 4px;
}
.guideline ul {
margin: 0.5rem 0 0 1.5rem;
}
.guideline li {
margin: 0.25rem 0;
}
@media (max-width: 600px) {
.container { padding: 2rem 1.5rem; }
h1 { font-size: 1.5rem; }
}
</style>
</head>
<body>
<div class="container">
<a href="/" class="back-link">← Back to Home</a>
<h1>Powered by Go Micro Badge</h1>
<p class="subtitle">Show your support and let others know your project is built with Go Micro!</p>
<h2>Badges</h2>
<h3>Dark Badge</h3>
<div class="badge-preview">
<a href="https://go-micro.dev" target="_blank">
<img src="https://img.shields.io/badge/Powered%20by-Go%20Micro-0366d6?style=for-the-badge&logo=go&logoColor=white" alt="Powered by Go Micro">
</a>
</div>
<pre><code>[![Powered by Go Micro](https://img.shields.io/badge/Powered%20by-Go%20Micro-0366d6?style=for-the-badge&logo=go&logoColor=white)](https://go-micro.dev)</code></pre>
<h3>Light Badge</h3>
<div class="badge-preview">
<a href="https://go-micro.dev" target="_blank">
<img src="https://img.shields.io/badge/Powered%20by-Go%20Micro-00ADD8?style=flat&logo=go&logoColor=white" alt="Powered by Go Micro">
</a>
</div>
<pre><code>[![Powered by Go Micro](https://img.shields.io/badge/Powered%20by-Go%20Micro-00ADD8?style=flat&logo=go&logoColor=white)](https://go-micro.dev)</code></pre>
<h3>Compact Badge</h3>
<div class="badge-preview">
<a href="https://go-micro.dev" target="_blank">
<img src="https://img.shields.io/badge/Go-Micro-0366d6?style=flat-square" alt="Go Micro">
</a>
</div>
<pre><code>[![Go Micro](https://img.shields.io/badge/Go-Micro-0366d6?style=flat-square)](https://go-micro.dev)</code></pre>
<h2>HTML Badges</h2>
<h3>Standard HTML Badge</h3>
<div class="badge-preview">
<a href="https://go-micro.dev" target="_blank">
<img src="https://img.shields.io/badge/Powered%20by-Go%20Micro-0366d6?style=for-the-badge&logo=go&logoColor=white" alt="Powered by Go Micro">
</a>
</div>
<pre><code>&lt;a href="https://go-micro.dev" target="_blank"&gt;
&lt;img src="https://img.shields.io/badge/Powered%20by-Go%20Micro-0366d6?style=for-the-badge&logo=go&logoColor=white" alt="Powered by Go Micro"&gt;
&lt;/a&gt;</code></pre>
<h3>Custom SVG Badge</h3>
<div class="badge-preview">
<a href="https://go-micro.dev" style="display:inline-block;text-decoration:none;">
<svg width="160" height="32" xmlns="http://www.w3.org/2000/svg">
<rect width="160" height="32" rx="6" fill="#0366d6"/>
<text x="16" y="21" font-family="system-ui,-apple-system,sans-serif" font-size="13" font-weight="600" fill="white">
Powered by Go Micro
</text>
</svg>
</a>
</div>
<pre><code>&lt;a href="https://go-micro.dev" style="display:inline-block;text-decoration:none;"&gt;
&lt;svg width="160" height="32" xmlns="http://www.w3.org/2000/svg"&gt;
&lt;rect width="160" height="32" rx="6" fill="#0366d6"/&gt;
&lt;text x="16" y="21" font-family="system-ui,-apple-system,sans-serif" font-size="13" font-weight="600" fill="white"&gt;
Powered by Go Micro
&lt;/text&gt;
&lt;/svg&gt;
&lt;/a&gt;</code></pre>
<h2>Usage</h2>
<p>Add one of these badges to your README.md, documentation, or website footer to show that your project uses Go Micro.</p>
<h3>Example README</h3>
<pre><code># My Awesome Project
![Project Logo](logo.png)
[![Powered by Go Micro](https://img.shields.io/badge/Powered%20by-Go%20Micro-0366d6?style=for-the-badge&logo=go&logoColor=white)](https://go-micro.dev)
My project does amazing things using Go Micro microservices framework.
## Features
- Fast and scalable
- Built with Go Micro
- Production ready</code></pre>
<div class="guideline">
<h3>Badge Guidelines</h3>
<ul>
<li>Link the badge to <code>https://go-micro.dev</code> to help others discover Go Micro</li>
<li>Use the badge prominently in your README</li>
<li>Consider adding it to your project website footer</li>
<li>Feel free to customize the colors to match your brand</li>
</ul>
</div>
<h2>Showcase Your Project</h2>
<p>Built something cool with Go Micro? <a href="https://github.com/micro/go-micro/issues/new" target="_blank">Open an issue</a> to get featured on our homepage!</p>
</div>
</body>
</html>
+133
View File
@@ -0,0 +1,133 @@
---
layout: blog
title: Introducing micro deploy
permalink: /blog/1
description: Deploy your Go Micro services to any Linux server with a single command
---
# Introducing micro deploy
<img src="/images/generated/blog-deploy.jpg" alt="Introducing micro deploy" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
*January 27, 2026 • By the Go Micro Team*
We're excited to announce **micro deploy** in Go Micro v5.13.0 — a simple way to deploy your services to any Linux server.
## The Problem
Go Micro has always been great for building microservices:
```bash
micro new myservice
cd myservice
micro run
```
But getting those services to production? That was on you. You'd need to figure out Docker, Kubernetes, or write your own deployment scripts.
We tried to solve this with Micro v3 — a full platform-as-a-service. But it was too much. Too complex. Nobody wanted another platform to manage.
## The Solution
The new approach is simple: **systemd + SSH**.
Every Linux server has systemd. It's battle-tested, it manages processes, it restarts them when they crash, it handles logging. Why reinvent it?
### One-Time Server Setup
```bash
ssh user@server
curl -fsSL https://go-micro.dev/install.sh | sh
sudo micro init --server
```
This creates:
- `/opt/micro/bin/` — where your binaries live
- `/opt/micro/config/` — environment files
- A systemd template for managing services
### Deploy
```bash
micro deploy user@server
```
That's it. The command:
1. Builds your services for Linux
2. Copies binaries via SSH
3. Configures systemd services
4. Verifies everything is running
### Manage
```bash
micro status --remote user@server
micro logs --remote user@server
micro logs myservice --remote user@server -f
```
## Named Deploy Targets
Add deploy targets to your `micro.mu`:
```
service users
path ./users
port 8081
service web
path ./web
port 8080
deploy prod
ssh deploy@prod.example.com
deploy staging
ssh deploy@staging.example.com
```
Then:
```bash
micro deploy prod
micro deploy staging
```
## Philosophy
- **systemd is the standard** — don't fight it, use it
- **SSH is the transport** — no custom agents or protocols
- **Errors guide you** — every failure tells you how to fix it
- **No platform** — just your server, your services
## What's Next?
This is just the beginning. We're thinking about:
- **Secrets management** — integrating with vault/sops
- **Multi-server deploys** — deploy to a fleet
- **Metrics** — Prometheus endpoints out of the box
- **Rolling updates** — zero-downtime deployments
## Try It
```bash
go install go-micro.dev/v5/cmd/micro@v5.13.0
micro new myapp
cd myapp
micro run
# When you're ready to deploy:
micro deploy user@your-server
```
See the [deployment guide](/docs/deployment.html) for full documentation.
---
*Go Micro is an open source framework for distributed systems development in Go. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/">← All Posts</a></div>
<div></div>
</div>
+183
View File
@@ -0,0 +1,183 @@
---
layout: blog
title: "micro chat: Talk to Your Services"
permalink: /blog/10
description: "Introducing micro chat — an interactive CLI that discovers your services, turns them into tools, and lets you orchestrate them through natural language."
---
# micro chat: Talk to Your Services
<img src="/images/generated/developer-experience.jpg" alt="micro chat terminal" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
*May 29, 2026 • By the Go Micro Team*
We built `micro chat` — a CLI that lets you talk to your microservices through an LLM. It discovers every service in the registry, exposes each endpoint as a tool, and lets a model decide which RPCs to call based on what you ask.
```bash
ANTHROPIC_API_KEY=sk-ant-... micro chat --provider anthropic
> list all users
→ called users_Users_List({})
There are 3 users: Alice, Bob, and Charlie.
> send a welcome email to Alice
→ called email_Email_Send({"to":"alice@example.com","template":"welcome"})
Done, welcome email sent to Alice.
> how many orders were placed this week?
→ called orders_Orders_Count({"since":"2026-05-22"})
There were 47 orders placed this week.
```
No glue code. No API wrappers. No tool definitions. You write normal Go services with doc comments, and `micro chat` turns them into things an LLM can call.
## How It Works
Three building blocks, stacked:
**1. `ai.Tools`** discovers services from the registry and creates typed tool definitions:
```go
tools := ai.NewTools(service.Registry())
discovered, _ := tools.Discover()
// discovered = []ai.Tool with name, description, parameters for each endpoint
```
**2. `ai.History`** tracks the conversation across turns so the LLM has context:
```go
hist := ai.NewHistory(50)
resp, _ := m.Generate(ctx, &ai.Request{Prompt: "list all users", Tools: discovered, Messages: hist.Messages()})
// Next prompt remembers this exchange
```
**3. `ai.Model`** calls the LLM. Seven providers, same interface:
```go
m := ai.New("anthropic", ai.WithAPIKey(key))
// or: "openai", "gemini", "atlascloud", "groq", "mistral", "together"
```
`micro chat` just wires these together with a REPL loop. The whole command is ~170 lines.
## Multi-Turn Conversations
`micro chat` remembers context across turns. You can ask follow-up questions without repeating yourself:
```
> list all users
There are 3 users: Alice (admin), Bob (user), Charlie (user).
> which ones are admins?
Alice is the only admin.
> change Bob's role to admin too
→ called users_Users_Update({"id":"bob-123","role":"admin"})
Done. Bob is now an admin.
```
Type `reset` to clear the conversation history and start fresh. The history limit is 50 messages by default — old messages are dropped FIFO when you hit the limit.
## Using It
Install or update the CLI:
```bash
go install go-micro.dev/v5/cmd/micro@latest
```
Start your services:
```bash
micro run
```
Chat with them:
```bash
# With Anthropic Claude
ANTHROPIC_API_KEY=sk-ant-... micro chat --provider anthropic
# With OpenAI
OPENAI_API_KEY=sk-... micro chat --provider openai
# With Atlas Cloud
ATLASCLOUD_API_KEY=... micro chat --provider atlascloud
# With any provider via base URL
micro chat --provider openai --base_url https://api.groq.com/openai --api_key $KEY
```
### Single Prompt Mode
For scripting or one-shot queries, use `--prompt`:
```bash
micro chat --provider anthropic --prompt "list all services"
```
### Environment Variables
If you don't want to pass flags every time:
```bash
export MICRO_AI_PROVIDER=anthropic
export ANTHROPIC_API_KEY=sk-ant-...
micro chat
```
## What Makes It Work
The key insight is that go-micro services are **already described**. The registry stores endpoint names, request/response types, and field metadata. Doc comments on handlers become tool descriptions. `@example` tags provide usage hints to the LLM.
```go
// CreateUser creates a new user account with the given details.
// @example {"name": "Alice", "email": "alice@example.com", "role": "admin"}
func (h *Users) CreateUser(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error {
// ...
}
```
The `ai.Tools` package reads all of this from the registry and translates it into the tool format that LLMs understand. The better your doc comments, the better the LLM uses your services.
## Using It Programmatically
`micro chat` is a CLI, but the building blocks work in your own code:
```go
import (
"go-micro.dev/v5/ai"
_ "go-micro.dev/v5/ai/anthropic"
)
tools := ai.NewTools(service.Registry())
discovered, _ := tools.Discover()
m := ai.New("anthropic",
ai.WithAPIKey(key),
ai.WithTools(tools),
)
hist := ai.NewHistory(50)
resp, _ := m.Generate(ctx, &ai.Request{Prompt: userInput, Tools: discovered, Messages: hist.Messages()})
fmt.Println(resp.Answer)
```
This is the same code `micro chat` runs internally. Use it to add LLM-powered orchestration to any service.
## What's Next
`micro chat` is the interactive version. `micro flow` is the event-driven version — same building blocks, but triggered by broker events instead of human input. See the [flows blog post](/blog/9) for that story.
Both are experiments in what happens when services are composable by agents, not just by code. The framework provides the building blocks. You decide how to use them.
---
*Go Micro is an open source framework for distributed systems development. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/9">&larr; From Chat to Flows</a></div>
<div><a href="/blog/">All Posts</a></div>
<div><a href="/blog/11">Build Your Own &rarr;</a></div>
</div>
+191
View File
@@ -0,0 +1,191 @@
---
layout: blog
title: "Build Your Own AI Agent CLI in 150 Lines"
permalink: /blog/11
description: "A complete teardown of micro chat — how to build an LLM agent that discovers and orchestrates your services, with every line explained."
---
# Build Your Own AI Agent CLI in 150 Lines
<img src="/images/generated/developer-experience.jpg" alt="Building an AI agent CLI" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
*May 30, 2026 • By the Go Micro Team*
We [introduced `micro chat`](/blog/10) — a CLI that lets you talk to your microservices through an LLM. People asked how it works under the hood. The honest answer: it's about 150 lines, and there's no magic. This post walks through every piece so you can build your own — for go-micro, for your own framework, or for whatever services you have.
By the end, you'll understand the four moving parts of any tool-calling agent and have working code you can adapt.
## The Problem
You have services. They do things — create users, send emails, query orders. You want to ask for those things in plain English and have the right service called automatically.
An LLM can do the reasoning ("the user wants to send an email, so call the email service"), but it needs three things from you:
1. **A list of tools** it can call, with descriptions and parameters
2. **A way to execute** a tool when it picks one
3. **Conversation memory** so follow-up questions make sense
That's the whole problem. Let's solve each part.
## Part 1: Discover the Tools
The LLM needs to know what's available. In go-micro, every service registers its endpoints with the registry, including request types and field metadata. We turn that into a tool list:
```go
tools := ai.NewTools(reg, ai.ToolClient(client))
discovered, err := tools.Discover()
```
`discovered` is a `[]ai.Tool` — one per service endpoint. Each has a name (`users_Users_Create`), a description (from the handler's doc comment), and a parameter schema (from the request struct's fields).
If you're not using go-micro, this is the part you'd write yourself: enumerate your functions/endpoints and build a list of `{name, description, parameters}`. The registry just makes it automatic.
## Part 2: Create the Model
```go
m := ai.New("anthropic",
ai.WithAPIKey(apiKey),
ai.WithTools(tools),
)
```
Two things happen here. `ai.New` picks the provider (Anthropic, OpenAI, Gemini, etc. — all the same interface). `ai.WithTools(tools)` wires up the **execution** side: when the model says "call `users_Users_Create` with these args," the handler routes it to the right RPC and returns the result.
That's the second piece — the way to execute. The `Tools` object does double duty: `Discover()` builds the list, and its handler executes the calls.
## Part 3: Track the Conversation
```go
hist := ai.NewHistory(50)
```
`History` is a plain message accumulator with a size limit. It's not magic — it's a `[]Message` with `Add`, `Messages`, and `Reset`. You add the user's prompt and the model's reply after each turn, and pass the accumulated messages back on the next call. That's how follow-up questions work.
## Part 4: The Loop
Now wire it together. The core of `ask` is just this:
```go
func ask(ctx context.Context, m ai.Model, hist *ai.History, tools []ai.Tool, prompt string) error {
hist.Add("user", prompt)
resp, err := m.Generate(ctx, &ai.Request{
Prompt: prompt,
SystemPrompt: systemPrompt,
Tools: tools,
Messages: hist.Messages(),
})
if err != nil {
return err
}
if resp.Reply != "" {
hist.Add("assistant", resp.Reply)
fmt.Println(resp.Reply)
}
for _, tc := range resp.ToolCalls {
args, _ := json.Marshal(tc.Input)
fmt.Printf(" → called %s(%s)\n", tc.Name, args)
}
if resp.Answer != "" {
hist.Add("assistant", resp.Answer)
fmt.Println(resp.Answer)
}
return nil
}
```
Read it top to bottom:
1. **Record the prompt** in history
2. **Call the model** with the prompt, the system instruction, the tool list, and the conversation so far
3. **Print the reply** and record it
4. **Show which tools were called** (the model decides, the handler executes — we just report)
5. **Print the final answer** after tools ran
The model's `Generate` does the heavy lifting: it decides whether to call tools, the handler (from step 2 of setup) executes them, and the model produces a final answer. We never wrote any "if user wants email, call email service" logic. The LLM does that reasoning from the tool descriptions.
## The REPL
Wrap `ask` in a read-loop and you have a chat:
```go
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Print("> ")
if !scanner.Scan() {
return nil
}
line := strings.TrimSpace(scanner.Text())
switch line {
case "":
continue
case "exit", "quit":
return nil
case "reset":
hist.Reset()
continue
default:
if err := ask(ctx, m, hist, discovered, line); err != nil {
fmt.Printf("error: %v\n", err)
}
}
}
```
That's it. Discover tools, create a model, track history, loop. Four pieces.
## Why It's So Short
The brevity comes from the framework doing the right things:
- **Services are self-describing.** Doc comments become tool descriptions. The `@example` tag gives the LLM a usage hint. You don't hand-write tool schemas.
```go
// CreateUser creates a new user account.
// @example {"name": "Alice", "email": "alice@example.com"}
func (h *Users) CreateUser(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error {
// ...
}
```
- **Providers are uniform.** Anthropic, OpenAI, Gemini, Groq, Mistral, Together, Atlas Cloud — all behind one `ai.Model` interface. Switching is one string.
- **Execution is wired automatically.** `ai.WithTools(tools)` connects tool calls to RPC dispatch. No glue.
If you stripped go-micro out and built this against raw HTTP services, you'd add maybe 50 lines: a function to enumerate your endpoints and a function to call one by name. Everything else stays the same.
## Make It Yours
The 150 lines are a starting point. Ideas for extending it:
- **Add a confirmation step** before destructive tool calls ("This will delete 3 records. Continue?")
- **Log every tool call** to an audit trail or your observability stack
- **Filter the tool list** so the agent only sees certain services
- **Swap the REPL for a Slack bot** — same `ask`, different input source
- **Pre-load a system prompt** with domain knowledge about your services
- **Trigger it from events** instead of stdin — that's exactly what [`micro flow`](/blog/9) does
The point of `micro chat` was never to be a finished product. It's a demonstration that turning services into an agent is a small, comprehensible amount of code — not a framework you have to learn, just a pattern you can copy.
## Try It, Then Read It
```bash
go install go-micro.dev/v5/cmd/micro@latest
micro run # start your services
ANTHROPIC_API_KEY=sk-ant-... micro chat --provider anthropic
```
The full source is [`cmd/micro/chat/chat.go`](https://github.com/micro/go-micro/blob/master/cmd/micro/chat/chat.go) — about 220 lines including flags, help text, and provider env-var handling. The agent core is the ~40 lines you saw above.
Build your own. It's more approachable than you think.
---
*Go Micro is an open source framework for distributed systems development. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/10">&larr; micro chat</a></div>
<div><a href="/blog/">All Posts</a></div>
<div><a href="/blog/12">Tools as Services &rarr;</a></div>
</div>
+137
View File
@@ -0,0 +1,137 @@
---
layout: blog
title: "Tools as Services: Why Go Micro Was Always Ready for AI"
permalink: /blog/12
description: "The path from API gateway to MCP to LLM tools was shorter than you'd think — because services were always self-describing."
---
# Tools as Services: Why Go Micro Was Always Ready for AI
<img src="/images/generated/architecture.jpg" alt="Services as tools" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
*May 30, 2026 • By the Go Micro Team*
When people see `micro chat` or the MCP gateway, they assume we built something new. We didn't. We exposed something that was already there.
Go Micro has always treated services as self-describing, addressable units. Every service registers its name, endpoints, and request types with the registry. Every endpoint is callable through a standardised path. The only thing that changed is *who's calling*.
## The Pattern
Go Micro was built on one idea: **a service should be accessible the same way regardless of how you access it**.
When we launched in 2015, that meant:
**HTTP API Gateway** — every service was reachable via `/{service}/{endpoint}`:
```
POST /users/Users.Create {"name": "Alice"}
```
No routing configuration. No URL mapping. You add a handler, it's accessible. The gateway reads the registry and routes.
**Web Dashboard** — every service appeared as a page. Endpoints were browseable, callable from the UI. Same registry, different presentation.
**CLI** — every service became a command:
```bash
micro call users Users.Create '{"name": "Alice"}'
```
Same registry, same endpoint, different interface. The service didn't change. The *access layer* changed.
## The Evolution to AI
MCP is just the next access layer.
**MCP Gateway** — every service is an AI-callable tool:
```json
{
"name": "users_Users_Create",
"description": "Create a new user account",
"parameters": {
"name": {"type": "string"},
"email": {"type": "string"}
}
}
```
Same registry. Same endpoints. Same service code. The gateway reads the registry, translates each endpoint into a tool definition, and exposes it over the Model Context Protocol. Claude, ChatGPT, or any MCP-compatible agent can discover and call your services — without you writing a single line of AI-specific code.
**`micro chat`** — every service is something you can talk to:
```
> create a user named Alice with email alice@example.com
→ users_Users_Create({"name":"Alice","email":"alice@example.com"})
Done. User Alice created.
```
Same registry. Same endpoints. The LLM reads the tool descriptions and decides what to call. The service doesn't know it's being called by an AI.
## Why This Works
The reason the AI integration was straightforward — not months of work, not a rewrite — is that Go Micro services were already:
1. **Named and discoverable.** The registry knows what's running and where.
2. **Self-describing.** Endpoints have typed request/response schemas. Doc comments on handlers become descriptions.
3. **Uniformly callable.** The client makes RPC calls by service name and endpoint name. It doesn't matter if the caller is an HTTP gateway, a CLI, or an LLM.
When we added MCP, we didn't add a new way to call services. We added a new way to *discover* them — one that LLMs understand. The calling mechanism was already there.
When we added `micro chat`, we didn't build an agent framework. We connected the existing tool discovery to an existing model interface and added a for-loop. The whole thing is [~150 lines](/blog/11).
## The Access Layer Pattern
Here's the mental model:
```
Service (Go handler + registry metadata)
↓ accessed via
API Gateway → HTTP clients
Web Dashboard → browsers
CLI → developers
MCP Gateway → AI agents
micro chat → natural language
```
Each layer does the same thing: read the registry, present the services in a format the consumer understands, and route calls back to the service. The service is oblivious. It just handles requests.
This is why we never needed an "agent package" or an "AI framework." The framework already had the right shape. Services were always tools — they just didn't know it yet.
## What Doc Comments Buy You
The one thing that changed: **documentation became functional.**
In the API gateway era, doc comments were nice to have. In the MCP era, they're what the LLM reads to decide which tool to call:
```go
// CreateUser creates a new user account with the given name and email.
// Returns the created user with a generated ID.
// @example {"name": "Alice", "email": "alice@example.com"}
func (h *Users) CreateUser(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error {
```
The comment becomes the tool's description. The `@example` becomes the LLM's hint for what arguments look like. Good comments mean the AI picks the right tool. Bad comments mean it guesses.
This is a genuine incentive to write documentation — not because a human might read it, but because a machine *will* read it and make decisions based on it.
## What's Next
The pattern extends beyond what we've built:
- **`micro registry list`** already shows you what's running. An agent could use the same data to reason about service topology.
- **`micro broker subscribe`** streams events. An agent could monitor events and react — which is what `micro flow` does.
- **`micro store`** persists data. An agent could read and write state as part of a multi-step workflow.
Every framework primitive that has a CLI command could also be a tool. The registry, broker, store, and config interfaces are all accessible from the terminal *and* from code. Making them accessible to AI agents is the same step we've already taken for services.
The thesis hasn't changed since 2015: **build the service once, access it everywhere**. The "everywhere" just expanded to include AI.
---
*Go Micro is an open source framework for distributed systems development. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/11">&larr; Build Your Own AI Agent CLI</a></div>
<div><a href="/blog/">All Posts</a></div>
</div>
+180
View File
@@ -0,0 +1,180 @@
---
layout: blog
title: "From Prompt to Production: AI-Generated Microservices That Actually Run"
permalink: /blog/13
description: "micro run --prompt generates real services with business logic, compiles them, starts them, and lets you talk to them. When you need more, the agent builds new services mid-conversation."
---
# From Prompt to Production: AI-Generated Microservices That Actually Run
*June 3, 2026 &bull; By the Go Micro Team*
Every code generator stops at the same point: here's some files, good luck. You get a skeleton, wire it up yourself, hope it compiles. We wanted something different.
```bash
micro run --prompt "a task management system"
```
That's not scaffolding. Two services start, register, and respond to requests. An AI agent can create tasks, organize categories, and orchestrate across services immediately. And when you need a capability that doesn't exist — shipping, payments, notifications — the agent builds it mid-conversation.
## The Full Loop
**1. Describe** — tell it what you need in plain English.
```bash
micro run --prompt "a todo list with tasks and categories"
```
**2. Review** — the LLM designs the architecture. You see every service, field, and endpoint before a line of code is written.
```text
Services:
● category — Manages task categories
Create, Read, Update, Delete, List
● task — Core task management
Create, Read, Update, Delete, List, CompleteTask, GetOverdue
Generate? [Y/n]
```
**3. Generate** — proto files are written deterministically from the spec. Then the LLM writes each handler with real business logic. Not stubs. Not TODOs. Actual validation, edge cases, and persistent storage via go-micro's built-in store:
```go
func (s *Task) CompleteTask(ctx context.Context, req *pb.CompleteTaskRequest,
rsp *pb.CompleteTaskResponse) error {
if req.Id == "" {
return errors.New("id is required")
}
recs, err := s.store.Read("task/" + req.Id)
if err != nil || len(recs) == 0 {
return errors.New("task not found")
}
var task pb.TaskRecord
json.Unmarshal(recs[0].Value, &task)
if task.Completed {
rsp.Success = false
rsp.Message = "task already completed"
return nil
}
task.Completed = true
task.Updated = time.Now().Unix()
data, _ := json.Marshal(&task)
s.store.Write(&store.Record{Key: "task/" + req.Id, Value: data})
rsp.Record = &task
rsp.Success = true
return nil
}
```
Data survives restarts — generated services use go-micro's built-in store (file-backed by default, swappable to Postgres or NATS KV) instead of in-memory maps. Every handler compiles. If it doesn't, the errors are fed back to the LLM for correction. Most services compile on the first attempt.
**4. Run** — services start, register via mDNS, and an HTTP gateway comes up. Every endpoint is accessible via REST, gRPC, or MCP.
```text
Micro
Dashboard http://localhost:8080
API http://localhost:8080/api/{service}/{method}
Services:
● category
● task
micro chat --provider anthropic # talk to your services
```
## Talk To Your Services
```bash
micro chat --provider anthropic
> Create a Work category, then add a task called 'Finish report' to it
```
The agent discovers services from the registry, sees every endpoint as a tool, and orchestrates:
```text
→ category_Category_Create({"name":"Work","user_id":"user1"})
← {"record":{"id":"f633...","name":"Work"},"success":true}
→ task_Task_Create({"title":"Finish report","category_id":"f633..."})
← {"record":{"id":"a1b2...","title":"Finish report","status":"pending"}}
Created Work category and added 'Finish report' task to it.
```
No service-to-service calls. No distributed transactions. No saga patterns. The agent reads the result of one call and uses it as input to the next. Each service stays simple and independent.
This is the answer to the oldest problem in microservices: how do services coordinate? They don't. An intelligent agent does it for them.
## Growing the System
Here's where it gets interesting. You're chatting, the domain grows, and you need something that doesn't exist:
```text
> I need to track shipping for my orders. Create a shipment for order 123 to London.
⚡ generating service: a shipping service...
✓ task (unchanged)
✓ category (unchanged)
✓ shipping
⚡ starting shipping...
✓ 13 tools available
→ shipping_Shipping_Create({"order_id":"123","destination":"London"})
← {"record":{"id":"xyz...","status":"pending"}}
Created shipment for order 123 going to London.
```
The agent recognised that no shipping service exists, generated one, compiled it, started it, discovered its endpoints, and used them — all within the conversation. You didn't leave the chat. You didn't run a separate command. The system grew because you needed it to.
Each service stays small and focused. When you need more, you add more services. The agent orchestrates across whatever exists. If you're running `micro run`, it detects new service directories automatically and starts them — the loop is fully seamless.
## Iteration Without Destruction
Run the same prompt again — services whose proto hasn't changed are skipped entirely:
```text
✓ category (unchanged)
✓ task (unchanged)
```
Edit a handler by hand, and re-running preserves your changes. The system tracks SHA-256 hashes of generated files and only regenerates what's actually different.
The LLM sees existing service protos and extends the system rather than redesigning from scratch.
## What This Isn't
This isn't a no-code platform. The generated code is standard Go — you own it, edit it, version it, deploy it however you want. There's no vendor lock-in, no runtime dependency on an AI provider, no magic abstraction.
The services are real Go code with real interfaces. The same code you'd write by hand, generated in 30 seconds instead of 3 hours.
## Why This Matters
The barrier to microservices has always been ceremony. Proto definitions, handler scaffolding, service registration, build systems — hours of work before you can test a single endpoint.
The deeper problem was coordination. Services need to talk to each other, and every pattern for that (sagas, choreography, service mesh) adds complexity. Teams spend more time on infrastructure than on business logic.
`micro run --prompt` solves both. The AI handles the ceremony. The agent handles the coordination. You handle the domain.
```bash
micro run --prompt "an order system for dropshipping"
micro chat --provider anthropic
> Place an order for 5 units of SKU-123 shipping to London
```
That's a running system. Not a prototype. Not a demo. Services you can deploy, iterate on, and scale.
## Try It
```bash
go install go-micro.dev/v5/cmd/micro@latest
micro run --prompt "a task management system" --provider anthropic
micro chat --provider anthropic
```
The future of microservices isn't fewer services. It's making them so easy to create and compose that the architecture disappears. Services become tools. The agent becomes the interface. You focus on what matters: the domain.
---
*Go Micro is open source. Star us on [GitHub](https://github.com/micro/go-micro), join the [Discord](https://discord.gg/G8Gk5j3uXr), or read the [docs](https://go-micro.dev/docs).*
+105
View File
@@ -0,0 +1,105 @@
---
layout: blog
title: "Going All In on AI"
permalink: /blog/14
description: "Go Micro started as a microservices framework. It's becoming the way you build software that AI agents can use. Here's why we're making that bet."
---
# Going All In on AI
*June 4, 2026 &bull; Asim Aslam*
I started Go Micro in 2015 because building microservices in Go was too hard. Too much boilerplate, too many decisions before you could test a single endpoint. The idea was simple: sane defaults, pluggable architecture, get out of the developer's way.
Eleven years later, the framework works. Service discovery, RPC, pub/sub, config, store — all pluggable, all production-tested. 23,000 stars on GitHub. But let's be honest: the microservices ecosystem moved on. Kubernetes won infrastructure. gRPC became the default transport. Service meshes handle the network. The problems Go Micro originally solved are mostly solved.
So what's left?
## The next step
Every few years, something shifts the entire stack. Containers changed deployment. Kubernetes changed orchestration. LLMs are changing how software gets built and used.
Here's what we noticed: Go Micro services were already self-describing. Every service registers its name, endpoints, and request types with the registry. Every endpoint is callable through a standardised path. The only thing that changed is *who's calling* — instead of another service or an API gateway, it's an AI agent.
We added MCP support. Every Go Micro endpoint became an AI-callable tool automatically. No annotations, no wrapper code, no new framework. Just the same service registration that was always there, exposed through a different protocol.
Then we added `micro chat`. An LLM discovers your services, sees every endpoint, and orchestrates across them. No service-to-service calls. No distributed transactions. No saga patterns. The agent reads the result of one call and decides what to do next.
Then we added `micro run --prompt`. Describe a system in plain English, and the AI designs services, writes handlers with real business logic, compiles them, and starts them. Ask for something that doesn't exist mid-conversation, and the agent builds a new service on the fly.
Each step was small. Together, they changed what Go Micro is.
## What It Is Now
Go Micro is a framework for building microservices that AI agents can use.
That's not a pivot. It's an evolution. The same service discovery, RPC, and store abstractions that powered the framework since 2015 are what make the AI integration work. Services register — agents discover them. Endpoints are typed — agents know how to call them. Doc comments describe what things do — agents read them.
The difference is the entry point. Before:
```bash
micro new helloworld
# write handler code
# define proto
# compile proto
# go mod tidy
micro run
```
Now:
```bash
micro run --prompt "a task management system"
micro chat --provider anthropic
> Create a task called 'Ship order 123'
```
The services that come out are the same Go code. You can edit them, version them, deploy them the same way. The framework underneath hasn't changed. But the developer experience is fundamentally different — you go from idea to running services in under a minute.
## Why Now
Three things came together:
**LLM tool calling works.** A year ago, getting an LLM to reliably call the right function with the right arguments was unreliable. Now Claude, GPT-4, and even open models handle multi-step tool orchestration consistently. The technology caught up to the architecture.
**MCP is a real standard.** The Model Context Protocol gives us a wire format that every AI company is adopting. We're not building on a proprietary API — we're implementing an open protocol. Services exposed via MCP work with Claude, with ChatGPT, with any MCP-compatible agent.
**Sponsorship aligns with direction.** Anthropic and Atlas Cloud are sponsoring Go Micro because they see the same thing we do: developers need frameworks that make their code accessible to AI. This isn't a side project within the framework — it's the direction the sponsors are investing in.
## What We're Not Doing
We're not abandoning the framework. You can still `micro new helloworld`, write a handler by hand, and deploy it. The pluggable architecture isn't going anywhere. gRPC, NATS, Consul, Postgres — all still there, all still swappable.
We're also not building another agent framework. There's no LangChain-style chain abstraction, no workflow DSL, no agent graph. Services are the only abstraction. The LLM calls them as tools. That's it. The simplicity is the point.
What we are doing is making AI the primary way people interact with Go Micro. The README leads with `micro run --prompt`. The website leads with "Microservices That AI Agents Can Use." The CLI leads with generation and chat. If you're evaluating Go Micro today, the AI experience is the reason to choose it.
## What's Next
**Better generation.** Services currently use in-memory or file-backed storage. We're moving to persistent store by default, with the full store interface (Postgres, NATS KV) available from day one.
**Smarter agents.** The chat agent currently generates services when capabilities are missing. Next: it should be able to modify existing services, add endpoints, update business logic — all from the conversation.
**Production hardening.** Auth on generated services, rate limiting on the MCP gateway, observability built in. The generation gets you to 80%. The framework gets you to production.
**More providers, more models.** Seven providers today. The `ai.Model` interface makes adding more trivial. As open models get better at tool calling, the barrier to entry drops further.
## The Opportunity
Most microservices frameworks are fighting over the same developers with the same features. Better gRPC support, better Kubernetes integration, better observability hooks. Important work, but incremental.
The AI shift is not incremental. It changes who builds services (anyone who can describe what they need), how services get composed (agents, not code), and how fast systems evolve (minutes, not sprints).
Go Micro is ten years old. It's survived containers, Kubernetes, service meshes, and serverless. Each time, the framework adapted because the core abstraction — small, self-describing, independently deployable services — turned out to be what the next wave needed.
AI agents need exactly that. Small services with typed interfaces that can be discovered, called, and composed. That's what Go Micro has always been. We're just leaning into it.
```bash
curl -fsSL https://go-micro.dev/install.sh | sh
micro run --prompt "your idea here" --provider anthropic
micro chat --provider anthropic
```
---
*Go Micro is open source. Star us on [GitHub](https://github.com/micro/go-micro), join the [Discord](https://discord.gg/G8Gk5j3uXr), or read the [docs](https://go-micro.dev/docs).*
+147
View File
@@ -0,0 +1,147 @@
---
layout: blog
title: "Agents for Services: A New Model for Microservices"
permalink: /blog/15
description: "What if every service had an agent responsible for it? Not embedded in the service, but created to manage its lifecycle. A design for distributed AI agents on top of microservices."
---
# Agents for Services: A New Model for Microservices
*June 4, 2026 &bull; Asim Aslam*
Microservices solved monolithic code. Split it into small, independent units. But we centralised the intelligence — one agent, one brain, managing everything. That's just a different kind of monolith.
What if every service had its own agent? Not embedded in the service. Created separately, assigned to manage it. The service is the capability. The agent is the intelligence.
## The Problem with One Agent
Right now, `micro chat` is a single agent. It discovers every service in the registry, sees every endpoint, and orchestrates across all of them. You ask it a question and it figures out which service to call.
This works for small systems. Two or three services, a dozen endpoints — one agent can handle it. But it doesn't scale the way microservices are supposed to scale.
A single agent managing ten services is like one person running ten departments. They know a little about everything and a lot about nothing. They can't hold the full context of each domain. They make shallow decisions because they're spread too thin.
This is the same centralisation problem that microservices were designed to solve. We distributed the services but centralised the intelligence.
## An Agent Per Service
What if each service had its own agent? Not inside the service — separate from it, but created to manage it.
The service is the capability. It has endpoints, stores data, handles requests. It doesn't know or care whether an agent exists.
The agent is the intelligence. It knows the service's domain deeply — what the endpoints do, how they relate, what the edge cases are, when to use which operation. It makes decisions about its service the way a domain expert would.
```
You (the creator)
└── Agent: task-agent
│ └── Service: task
└── Agent: project-agent
│ └── Service: project
└── Agent: notification-agent
└── Service: notification
```
When you talk to the system, you don't talk to the services. You don't even talk to a single central agent. Your message reaches the right agent — the one responsible for that domain — and it handles it using its service.
## Multi-Service Agents
Some agents manage more than one service. A "project management agent" might oversee both the task service and the project service because they're part of the same domain. It understands how projects contain tasks, how task completion affects project progress, how to coordinate across both.
```
You
└── Agent: project-management
│ ├── Service: task
│ └── Service: project
└── Agent: communication
├── Service: notification
└── Service: email
```
The agent boundaries don't have to match the service boundaries. A service is a technical unit — one concern, one data store, one set of endpoints. An agent is a domain unit — it might span multiple services because the domain spans them.
This is the separation that matters: **services are about capability, agents are about intelligence.**
## How They Communicate
Agents are services. They communicate via standard RPC — the same way every service in the system communicates. An agent has a proto-defined `Agent.Chat` endpoint that any other agent or client can call.
When the project management agent needs to notify someone, the router dispatches to the communication agent via RPC. Each agent handles its domain.
```
> Reschedule all of Alice's tasks to next week and let her know
[project-management] Rescheduling 3 tasks for Alice...
[project-management] → task.Update(id: "t1", due: "2026-06-11")
[project-management] → task.Update(id: "t2", due: "2026-06-12")
[project-management] → task.Update(id: "t3", due: "2026-06-13")
[project-management] Notifying communication agent...
[communication] Sending schedule change email to Alice...
[communication] → notification.Create(user: "alice", message: "3 tasks rescheduled")
[communication] → email.Send(to: "alice@example.com", subject: "Tasks rescheduled")
```
Each agent handles its domain. The user sees one conversation. Underneath, multiple agents are collaborating, each using their own services.
## What Agents Do
An agent isn't just a router to endpoints. It has responsibilities:
**Functional** — it's the intelligent interface to its services. It knows which endpoint to call, in what order, with what parameters. It handles the "how" so the user only needs to express the "what."
**Evolutionary** — it can grow its services. If the task agent realises it needs a "recurring tasks" capability that doesn't exist, it can generate a new endpoint or a new service. The agent evolves the system based on what users need.
**Operational** — it monitors its services. Health checks, error rates, log patterns. If the task service starts failing, the task agent notices and can act — restart it, alert the operator, degrade gracefully.
These layers build on each other. Functional is where we start. Evolutionary is what makes the system grow. Operational is what makes it production-ready.
## The Service Stays Simple
This is the critical point: **the service doesn't change.** It's still a Go struct with methods. It still registers with the registry. It still stores data in the store. It still communicates via RPC.
```go
func main() {
service := micro.NewService("task")
service.Handle(new(TaskHandler))
service.Run()
}
```
The agent is a separate entity, created by the operator — or by `micro run --prompt` — to manage that service. The service has no dependency on the agent. You can run services without agents. You can swap agents without touching services. You can have one agent managing three services, or three agents managing one service from different angles.
The service is the body. The agent is the mind assigned to it.
## What This Looks Like in Micro
The pieces already exist:
- **Registry** — agents register as services with a proto-defined `Agent.Chat` endpoint. No special metadata hacks — they're real services.
- **RPC** — `micro chat` calls agents via standard RPC. Agents call services via standard RPC. Agent-to-agent communication is just RPC. One protocol for everything.
- **Store** — agents persist memory (conversation history, learned context) across restarts.
- **`micro chat`** — discovers agents from the registry and routes to the right one based on intent.
- **`micro run --prompt`** — generates services and an agent as a pair. Everything starts together.
The framework was built for distributed systems. Agents are just the next thing we distribute.
## The Bigger Picture
Microservices solved the problem of monolithic code — split it into small, independent units that can evolve separately. But they created a new problem: how do the pieces coordinate?
We tried solving coordination with code — service-to-service calls, sagas, choreography, orchestration engines. Each solution added complexity. The coordination layer became harder to build than the services themselves.
Agents solve coordination differently. They don't coordinate through code. They coordinate through understanding. An agent that manages the project domain understands what "reschedule Alice's tasks" means. It doesn't need a workflow definition or a saga pattern. It just knows.
And when you have multiple agents, each understanding their domain, the system coordinates itself. Not through service-to-service calls. Not through a central orchestrator. Through distributed intelligence.
That's the model. Services for capability. Agents for intelligence. The framework connects them.
```bash
micro run --prompt "task management system"
micro chat
> What's overdue this week?
```
The agent for your services answers. Not because you wired it up. Because the system understands what you built.
---
*Go Micro is open source. Star us on [GitHub](https://github.com/micro/go-micro), join the [Discord](https://discord.gg/G8Gk5j3uXr), or read the [docs](https://go-micro.dev/docs).*
+183
View File
@@ -0,0 +1,183 @@
---
layout: blog
title: "Introducing micro.NewAgent()"
permalink: /blog/16
description: "Agent is now a first-class abstraction in Go Micro — alongside Service and Flow. Build intelligent agents that manage your services in Go."
---
# Introducing micro.NewAgent()
*June 5, 2026 &bull; By the Go Micro Team*
Go Micro now has three core abstractions:
```go
service := micro.NewService("task") // capability
agent := micro.NewAgent("task-mgr") // intelligence
flow := micro.NewFlow("onboard-user") // event-driven orchestration
```
Service has been the foundation since 2015. Flow added event-driven LLM orchestration. Now Agent completes the picture — an intelligent layer that manages services, with scoped tools, persistent memory, and multi-agent coordination.
## What an Agent Is
A Service has endpoints and handles requests. An Agent knows *how* to use those endpoints intelligently. The service doesn't know about its agent. The agent knows about its services.
```go
agent := micro.NewAgent("task-mgr",
micro.AgentServices("task", "project"),
micro.AgentPrompt("You manage tasks and projects. You understand deadlines, priorities, and assignments."),
micro.AgentProvider("anthropic"),
)
agent.Run()
```
That's it. The agent:
- Discovers `task` and `project` from the registry
- Only sees their endpoints (scoped tools — no access to unrelated services)
- Maintains conversation memory in the store (survives restarts)
- Registers as a real service with a proto-defined `Agent.Chat` RPC endpoint
- Discoverable by `micro chat`, other agents, or any go-micro client
Under the hood, an agent IS a service. It has a real server, a real address, and a real proto definition:
```protobuf
service Agent {
rpc Chat(ChatRequest) returns (ChatResponse) {}
}
```
This means you can call an agent the same way you call any service:
```bash
micro call task-mgr Agent.Chat '{"message": "What tasks are overdue?"}'
```
## Talking to an Agent
Programmatically:
```go
resp, _ := agent.Ask(ctx, "What tasks are overdue for Alice?")
fmt.Println(resp.Reply)
```
Via the CLI:
```bash
micro agent list
◆ task-mgr manages: task, project
micro chat
> What tasks are overdue for Alice?
[task-mgr] Checking overdue tasks...
→ task_Task_ListOverdue({"user_id":"alice"})
{"records":[...],"total":"3"}
Alice has 3 overdue tasks:
1. Write quarterly report (due June 1)
2. Review PR #42 (due June 2)
3. Update deployment docs (due June 3)
```
`micro chat` discovers the agent from the registry and routes to it automatically. If multiple agents are registered, the router classifies intent and dispatches to the right one.
## Multi-Service Agents
An agent can manage multiple services that form a domain:
```go
agent := micro.NewAgent("project-mgr",
micro.AgentServices("task", "project", "milestone"),
micro.AgentPrompt("You manage the project system. Tasks belong to projects. Milestones track progress."),
micro.AgentProvider("anthropic"),
)
```
The agent understands the relationships between its services because its prompt gives it domain knowledge. It coordinates across them without the services needing to know about each other.
## Multi-Agent Systems
Multiple agents coordinate via RPC — each is a service with an `Agent.Chat` endpoint:
```go
// Task management agent
taskAgent := micro.NewAgent("task-mgr",
micro.AgentServices("task", "project"),
micro.AgentPrompt("You manage tasks and projects."),
micro.AgentProvider("anthropic"),
)
// Communications agent
commsAgent := micro.NewAgent("comms-mgr",
micro.AgentServices("notification", "email"),
micro.AgentPrompt("You handle notifications and emails."),
micro.AgentProvider("anthropic"),
)
```
When you ask `micro chat` to "reschedule Alice's tasks and notify her," the router dispatches to both agents. Each handles its domain. The user sees one conversation.
## Persistent Memory
Agents remember. Conversation history is stored in the go-micro store and persists across restarts:
```text
agent/task-mgr/history — conversation history
```
The store backend determines durability — file-backed by default, Postgres or NATS KV for production. An agent that restarts picks up where it left off.
## The Three Abstractions
| | Service | Agent | Flow |
|---|---------|-------|------|
| **What** | Capability | Intelligence | Event orchestration |
| **Does** | Handles requests | Manages services | Reacts to events |
| **Knows** | Its endpoints | Its services' endpoints | Its trigger topic |
| **State** | Store | Store-backed memory | Checkpointed run history |
| **Create** | `micro.NewService("name")` | `micro.NewAgent("name")` | `micro.NewFlow("name")` |
| **Package** | `service/` | `agent/` | `flow/` |
They compose:
- A **Service** handles requests and stores data
- An **Agent** orchestrates one or more services intelligently
- A **Flow** triggers LLM orchestration when events arrive on the broker
You can use any combination. Services work without agents. Agents work without flows. Each adds a layer.
## Getting Started
```bash
curl -fsSL https://go-micro.dev/install.sh | sh
# Generate services
micro run --prompt "task management system"
# In another terminal — talk to them
micro chat --provider anthropic
> Create a project called Launch and add three tasks to it
```
Or build an agent in Go:
```go
package main
import "go-micro.dev/v6"
func main() {
agent := micro.NewAgent("task-mgr",
micro.AgentServices("task"),
micro.AgentPrompt("You manage tasks."),
micro.AgentProvider("anthropic"),
)
agent.Run()
}
```
The agent implementation lives under `go-micro.dev/v6/agent`; most users create agents through the top-level `go-micro.dev/v6` API. The full interface design is documented in [AGENT_DESIGN.md](https://github.com/micro/go-micro/blob/master/internal/docs/AGENT_DESIGN.md).
---
*Go Micro is open source. Star us on [GitHub](https://github.com/micro/go-micro), join the [Discord](https://discord.gg/G8Gk5j3uXr), or read the [docs](https://go-micro.dev/docs).*
+168
View File
@@ -0,0 +1,168 @@
---
layout: blog
title: "Agents That Plan and Delegate"
permalink: /blog/17
description: "An agent shouldn't just react tool by tool. It should form intent — plan what it's doing — and direct intent — delegate what it shouldn't do itself. Go Micro now gives every agent both, as plain tools."
---
# Agents That Plan and Delegate
*June 7, 2026 &bull; By the Go Micro Team*
When we [introduced `micro.NewAgent()`](/blog/16), an agent was already a service with an LLM inside: scoped tools, persistent memory, and a `micro chat` router that dispatches across agents. And in [Agents for Services](/blog/15) we made the case that intelligence should be distributed — agents coordinate "not through code… through understanding."
This post is the next beat. An agent that only reacts, one tool call at a time, isn't really understanding anything — it's improvising. Two things turn reaction into intent: the agent should **plan** what it's doing before it does it, and **delegate** what it shouldn't be doing itself. Go Micro now gives every agent both.
True to the rest of the framework, they aren't a new layer. There's no harness, no workflow engine, no agent graph. `plan` and `delegate` are two ordinary tools — the LLM calls them exactly like it calls a service endpoint — added automatically to every agent. (If you've followed what everyone from Claude Code to LangChain calls "deep agents," this is the same idea, built the go-micro way: as tools, not as a framework.)
## The smallest version
An agent doesn't need any services to plan. Here's a complete program:
```go
package main
import (
"context"
"fmt"
"os"
"go-micro.dev/v5"
)
func main() {
a := micro.NewAgent("assistant",
micro.AgentProvider("anthropic"),
micro.AgentAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
)
resp, err := a.Ask(context.Background(),
"Plan how to launch a product, then carry out what you can.")
if err != nil {
panic(err)
}
fmt.Println(resp.Reply)
}
```
Save it in a fresh module and run:
```bash
mkdir my-agent && cd my-agent
go mod init my-agent
go get go-micro.dev/v5
# save the code above as main.go
export ANTHROPIC_API_KEY=sk-ant-...
go run main.go
```
The agent records a plan with the `plan` tool, then works through it. That's the whole setup.
## plan: stating intent
`plan` is exactly what it sounds like: before multi-step work, the model writes down an ordered list of steps, and updates it as it goes.
```json
{
"steps": [
{"task": "draft the announcement", "status": "in_progress"},
{"task": "schedule the email", "status": "pending"},
{"task": "publish the blog post", "status": "pending"}
]
}
```
This builds directly on the memory we [already shipped](/blog/16): the plan is saved to the same [store](/docs/store) every service uses — file-backed by default, Postgres or NATS KV in production — under `agent/{name}/plan`, and folded back into the system prompt on the next turn. The agent stays oriented across a long task and picks up where it left off after a restart.
You get it for free. To make an agent reliably plan, just say so in its prompt:
```go
micro.AgentPrompt("For multi-step requests, call the plan tool first to record your steps, then carry them out.")
```
## delegate: directing intent
The harder move is knowing what *not* to do yourself.
A single agent managing ten services is a different kind of monolith — it knows a little about everything and a lot about nothing. We argued in [Agents for Services](/blog/15) that the fix is the same one microservices made for code: distribute it. Give each domain its own agent, and let them hand work to each other over RPC.
That hand-off already existed — an agent is a service, so any agent can call any other agent's `Agent.Chat` endpoint. `delegate` simply lets the agent reach for it *as part of its own reasoning*, instead of you wiring the routing. The model calls `delegate` with a subtask, and Go Micro resolves it **delegate-first**:
1. **If the target names a registered agent** that owns the relevant services, the subtask goes to it over RPC. The domain expert handles its own services.
2. **Otherwise** a focused, short-lived **sub-agent** is created for just that subtask, with a fresh, isolated context, and torn down when it's done.
```json
{
"task": "Notify owner@acme.com that the launch plan is ready",
"to": "comms"
}
```
One design decision worth calling out: we didn't add a "spawn" or a "fork" primitive. **A sub-agent is just an agent** — created with `New`, talked to with `Ask`, the same two calls you already use. There's no new concept to learn, because there's no new concept: it's the existing RPC model, surfaced as a tool. Ephemeral sub-agents load and persist no history and get no tools of their own — so they can't plan or re-delegate, which keeps delegation from recursing.
## Putting it together
Two services (`task`, `notify`) and two agents. The `conductor` owns `task`; `comms` owns `notify`. Ask the conductor to create some tasks and notify someone, and watch intent split across the system:
```go
comms := micro.NewAgent("comms",
micro.AgentServices("notify"),
micro.AgentPrompt("You handle outbound notifications."),
micro.AgentProvider("anthropic"),
micro.AgentAPIKey(key),
)
go comms.Run()
conductor := micro.NewAgent("conductor",
micro.AgentServices("task"),
micro.AgentPrompt(
"For multi-step requests, call the plan tool first. "+
"For notifications, delegate to the \"comms\" agent (to: \"comms\")."),
micro.AgentProvider("anthropic"),
micro.AgentAPIKey(key),
)
resp, _ := conductor.Ask(ctx,
"Create three launch tasks: Design, Build, and Ship. "+
"Then make sure owner@acme.com is notified that the launch plan is ready.")
```
A typical run:
```
→ plan({"steps":[{"task":"create Design task","status":"pending"}, ...]})
→ task_TaskService_Add({"title":"Design"})
→ task_TaskService_Add({"title":"Build"})
→ task_TaskService_Add({"title":"Ship"})
→ delegate({"task":"Notify owner@acme.com that the launch plan is ready","to":"comms"})
📨 notify: to=owner@acme.com message="The launch plan is ready"
```
The conductor never learned how to send a notification. It learned *who does*. `comms` handled it with its own service, in its own context, over RPC — exactly the distributed-intelligence picture from [blog 15](/blog/15), now driven by the agent itself rather than a router.
The full runnable code is in [examples/agent-plan-delegate](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate). Set any provider key (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, …) and `go run main.go`.
## Why it's only two tools
It would have been easy to ship a planning engine, a sub-agent scheduler, a delegation graph. We didn't, on purpose. Every one of those is a new abstraction to learn and maintain, and Go Micro's bet has been consistent since we [went all in on AI](/blog/14): services are the only abstraction, the LLM calls them as tools, and an agent's own capabilities are no exception.
`plan` and `delegate` are two small tools added to mechanisms that already existed — the store, and agent-to-agent RPC. That's the entire feature. It's also why there's nothing to configure: if you've written a `micro.NewAgent`, you already have them.
## Getting started
The fastest way to see it end to end is the runnable example in the repo — it has the module set up and both agents wired:
```bash
git clone https://github.com/micro/go-micro
cd go-micro/examples/agent-plan-delegate
export ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY, GEMINI_API_KEY, ...
go run main.go
```
To start from scratch in your own project, use the smallest-agent snippet above (`go mod init` + `go get go-micro.dev/v5`).
Read the [Plan & Delegate guide](/docs/guides/plan-delegate) for the full reference, or the [agent patterns guide](/docs/guides/agent-patterns) for where this fits among the other ways to build with agents.
---
*Go Micro is open source. Star us on [GitHub](https://github.com/micro/go-micro), join the [Discord](https://discord.gg/G8Gk5j3uXr), or read the [docs](https://go-micro.dev/docs).*
+93
View File
@@ -0,0 +1,93 @@
---
layout: blog
title: "Not Everything Should Be an Agent"
permalink: /blog/18
description: "We spent two posts on agents that plan and delegate. Here's the other half: when the path is known, you want a workflow — predictable, event-driven, deterministic. In Go Micro they're the same building blocks, two modes."
---
# Not Everything Should Be an Agent
*June 8, 2026 &bull; By the Go Micro Team*
The last two posts were about agents — [the abstraction](/blog/16), and then [agents that plan and delegate](/blog/17), directing their own work over many turns. That's the exciting part. It's also, honestly, the part you should reach for *least often*.
An agent decides its own path at runtime. That's powerful when the task genuinely needs it, and a liability when it doesn't — you trade predictability, latency, and cost for flexibility you may not want. Most real work has a known shape: *when this event happens, do these things.* For that, you don't want a model improvising. You want a **workflow**.
The distinction is the one Anthropic draws in [Building Effective Agents](https://www.anthropic.com/engineering/building-effective-agents): a **workflow** is LLMs and tools orchestrated through *predefined* paths; an **agent** is an LLM *dynamically directing* its own process. Determinism is the dividing line. Go Micro has both — and they're the same building blocks underneath.
## A workflow is a Flow
In Go Micro, the predefined-path side is a `Flow`. It subscribes to an event and runs one defined step: a prompt, with your services available as tools.
```go
f := micro.NewFlow("onboard-user",
micro.FlowTrigger("events.user.created"),
micro.FlowPrompt("New user {{.Data}} — create a workspace and send a welcome email."),
micro.FlowProvider("anthropic"),
)
```
When a `user.created` event lands on the broker, the flow fires. There's no open-ended loop, no self-direction — a known trigger runs a known step. You can read exactly what it will do. That's the point.
## Same building blocks, two modes
Here's what makes this coherent rather than two competing systems: a workflow and an agent are built from the *same* primitive — the augmented LLM. A model, with every service endpoint already available as a tool, and the store as memory. Go Micro gives you that for free; every endpoint is a tool the moment a service registers.
The only difference is **who decides the path**:
- **You decide it** → a workflow (`Flow`). The trigger and the step are fixed.
- **The model decides it** → an agent (`Agent`). It plans, calls tools, evaluates, and chooses the next step.
It's not two frameworks. It's one set of pieces, pointed two ways.
## Flow triggers, Agent reasons
Sometimes a workflow's step genuinely needs judgment — the path isn't fully knowable in advance. You don't have to choose globally. A flow can *hand off* to an agent: the workflow stays the deterministic trigger, and the agent does the open-ended part.
```go
f := micro.NewFlow("onboard-user",
micro.FlowTrigger("events.user.created"),
micro.FlowPrompt("New user {{.Data}} — get them set up."),
micro.FlowAgent("conductor"), // the flow triggers; the conductor agent reasons
)
```
Now the event fires the flow, the flow renders the prompt, and a registered `conductor` agent handles it over RPC — with its full toolkit: [plan, delegate](/blog/17), memory, and guardrails. **Flow triggers, Agent reasons.** The deterministic and dynamic halves compose along one clean seam, because an agent is just a service and the hand-off is just an RPC.
## Predictability, even in agents
Choosing an agent doesn't mean giving up control. Anthropic is emphatic that autonomous agents need stopping conditions and human checkpoints, and Go Micro's agent has both — as plain options, not a framework:
```go
micro.NewAgent("conductor",
micro.AgentServices("task"),
micro.AgentMaxSteps(8), // a stopping condition
micro.AgentApproveTool(approveBilling), // a human-in-the-loop gate
)
```
`MaxSteps` bounds how many actions the agent may take. `ApproveTool` gates each action before it runs — return `false` and it's blocked, with the reason fed back to the model. These are guardrails: a counter and a callback on the path every tool call already takes. No new abstraction.
## Which one do you reach for?
The honest order, smallest first:
1. **A single model call.** Most tasks need nothing more — one augmented LLM, one service call. Start here.
2. **A workflow (`Flow`).** When the path is well-defined and you want it to be predictable and event-driven.
3. **An agent (`Agent`).** When the task genuinely needs flexibility and model-driven decisions — and you accept the cost, and add the guardrails.
The mistake is starting at 3. Agents are the most capable tool and the easiest to over-apply. Reach for the simplest thing that does the job, and move up only when the job demands it.
## One set of pieces
We didn't build a workflow engine and an agent framework. We built services that are tools, and then pointed an LLM at them two ways — a predefined path, or a dynamic one. `Flow` and `Agent` are modes, not frameworks, and they compose because they share everything underneath. That's the same principle we've held since [going all in on AI](/blog/14): services are the only abstraction, the LLM calls them as tools, and everything else is how you arrange them.
Read the [Agents and Workflows guide](/docs/guides/agents-and-workflows) for the full mapping, or [Plan & Delegate](/docs/guides/plan-delegate) for the agent side.
```bash
curl -fsSL https://go-micro.dev/install.sh | sh
```
---
*Go Micro is open source. Star us on [GitHub](https://github.com/micro/go-micro), join the [Discord](https://discord.gg/G8Gk5j3uXr), or read the [docs](https://go-micro.dev/docs).*
+72
View File
@@ -0,0 +1,72 @@
---
layout: blog
title: "The Evolution of Microservices"
permalink: /blog/19
description: "From the scaling pressures that produced microservices, through Kubernetes and the service mesh, to AI agents — fifteen years of evolution, what actually endured, and why the future belongs to agents."
---
# The Evolution of Microservices
*June 8, 2026 &bull; Asim Aslam*
Every era of distributed systems has solved the problem the previous era created. Microservices solved the coordination cost of the monolith and created a distributed-systems problem. Containers and orchestration solved the deployment problem and created an operational one. The service mesh solved the cross-cutting problem and pushed complexity into the platform. Each step was a response to a concrete engineering constraint, not a trend.
Follow that chain to the end, and the interface the industry converged on — a named, typed, discoverable, independently deployable unit — turns out to be almost exactly the interface a language model needs to call a tool. That convergence is the technical reason agents are the next era.
## The monolith and the cost of coordination
A monolith is one deployable, one process, one database, one release cadence. In-process calls, a single transaction boundary, no network in the path — technically efficient. Its limits are organisational, not technical.
As the number of engineers grows, the cost of coordinating changes to a single artifact grows faster than linearly. Everyone shares a build, a test suite, a deploy. A change in one corner can block a release in another. This is Conway's law stated as a constraint: a system's structure ends up mirroring the communication structure of the org that builds it, and a single shared artifact forces a single shared communication channel. Microservices were the response: let teams own and release their part independently. The decomposition was organisational first and technical second.
## Microservices and the distributed-systems tax
The moment you split a process across the network, you inherit the network. Calls that were function invocations become RPCs that can be slow, reordered, duplicated, or simply not return. You now have partial failure — the defining property of a distributed system — and with it: service discovery (where is `payments` right now?), load balancing across instances, retries with backoff, idempotency, timeouts, circuit breaking to stop cascading failure, and distributed tracing because no single stack trace spans the request anymore.
The first wave of answers was libraries. Netflix open-sourced Eureka (discovery), Ribbon (client-side load balancing), and Hystrix (circuit breaking); Twitter built Finagle. The defining trait of this era: the distributed-systems concerns lived *inside your application process*, as code you imported. Which meant every language needed its own copy, and every service carried the weight.
## Containers and orchestration
Two innovations made "independently deployable" actually cheap.
Docker (2013) standardised the unit of deployment as an immutable image — application plus dependencies, built once, run anywhere, isolated with namespaces and cgroups. It killed "works on my machine" and made a service a reproducible artifact rather than a deploy procedure.
Kubernetes (2014) standardised operation. Its core idea is declarative reconciliation: you describe desired state, and a control loop continuously drives the system toward it — scheduling, restarting, scaling, rolling out. Operating hundreds of independently deployable services became tractable, because lifecycle became the platform's job, not yours. The unit the platform scheduled was a container exposing declared ports — a named thing with an interface.
## The service mesh
By 2016 the pattern was clear: discovery, load balancing, retries, circuit breaking, mTLS, and telemetry are *cross-cutting and language-agnostic*. Reimplementing them as a library in every language is waste. So move them out of the process entirely.
The mechanism was the sidecar proxy — Envoy, out of Lyft — deployed next to each service, intercepting all traffic, with a central control plane (Istio) configuring it. The application stopped needing resilience libraries; the mesh did discovery and routing at L4/L7, transparently. Technically this was a clean decoupling of operational concerns from business logic. Practically, it hollowed out the library era: the value that lived in Netflix OSS migrated into infrastructure, and the service got thinner.
## The correction
Around 2020 the trade-offs drew a harder look. Microservices have a real cost: serialization, network latency, eventual consistency, and the cognitive load of debugging across process boundaries. If your org isn't large enough to need independent deployability, you pay the distributed-systems tax and get little of the team-autonomy benefit. Amazon's Prime Video team published a workload they moved from orchestrated distributed components back to a single process and cut cost ~90% — because for a tight, high-throughput loop, the serialization and orchestration overhead dwarfed the work.
The lesson wasn't "microservices were wrong." It was that *micro* was always a distraction. The thing worth paying for was independent deployability and clear ownership; granularity is a workload decision, and the cost is real.
## What endured
Strip away the runtime churn — images, schedulers, sidecars — and the same primitive sits underneath every era: a service is a **named, network-addressable, typed, independently deployable unit**. It announces itself (registration), it's locatable (discovery), it exposes a contract (typed endpoints with schemas), and it can be deployed and scaled on its own.
This shape never changed, and that's not an accident. Each new runtime layer *needed* something with exactly this shape to operate on. Kubernetes schedules units with declared interfaces. The mesh routes to named endpoints. Tracing correlates typed calls. The industry kept rebuilding the runtime and kept requiring the same unit, because the unit was the stable interface every layer agreed on.
## The caller changes
For fifteen years, the consumer of that typed interface was deterministic code: another service, a gateway, a client. The contract was machine-to-machine on a fixed integration written ahead of time.
Then language models learned to call functions reliably. Given a set of capabilities described as name, purpose, and a typed parameter schema, a model can choose which to invoke and produce well-formed arguments, and chain them toward a goal stated in natural language.
What an LLM needs in order to use a capability is specific: a name, a description of what it does, and a typed input/output contract. That is the definition of a service endpoint. A registration is a tool definition. Service discovery is tool discovery. The Model Context Protocol (Anthropic, 2024) is, stripped down, a discovery-and-invocation protocol for model-callable capabilities — service discovery and RPC, with a model on the other end. No one designed the microservices interface for models, but it already provides what they need.
So the shift is not a new architecture. It is a change of caller: from a deterministic program that was integrated in advance, to a probabilistic reasoner that decides at runtime which typed capabilities to compose, and in what order, from intent.
## Why agents are the future
The hard part of distributed systems was never building a capability. It was everything *between* capabilities — the integration and orchestration. Composing services into a workflow meant writing the glue: sagas, choreography, retries-with-meaning, the orchestration code that encodes "do A, then B, and if C fails compensate." That glue is where most distributed-systems effort and most distributed-systems bugs live.
An agent attacks exactly that layer. Given the available tools and a goal, it can compose them dynamically — read the contracts, plan a sequence, call, observe, adapt — without that sequence being written ahead of time. The orchestration shifts from code you author to a decision the model makes against typed capabilities. The interface to software moves from fixed APIs invoked by code to capabilities invoked by intent. It changes where the work is: from writing capabilities and their glue, to exposing capabilities cleanly and letting a reasoner compose them.
The caveats are technical. Agents are non-deterministic, higher-latency, and more expensive per call than a function invocation, and they need guardrails — stopping conditions, approval gates, sandboxing — precisely because they decide at runtime. So agents do not replace deterministic services or workflows; they sit on top of them. When the path is known, you still want fixed code. The substrate is unchanged: you still need well-defined, typed, discoverable, independently deployable capabilities. Agents don't make that substrate obsolete — they make it the most valuable layer in the stack, because a reasoner is only as good as the capabilities it can call.
So agents are the next era, not a replacement for what came before. The architecture the last fifteen years produced was not built for language models; it converged, era by era, on the exact interface they require to act. Each wave changed the runtime and left the unit intact, because the unit was what the next wave needed. The only thing new in this wave is the caller: it reasons about which units to invoke, instead of being wired to them in advance.
+491
View File
@@ -0,0 +1,491 @@
---
layout: blog
title: Making Microservices AI-Native with MCP
permalink: /blog/2
description: Expose go-micro services as AI tools with 3 lines of code using the Model Context Protocol
---
# Making Microservices AI-Native with MCP
<img src="/images/generated/blog-mcp.jpg" alt="Making Microservices AI-Native with MCP" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
*February 11, 2026 • By the Go Micro Team*
We're excited to announce **MCP (Model Context Protocol) support** in Go Micro v5.15.0 — making your microservices instantly accessible to AI tools like Claude.
## The Vision
Imagine telling Claude: *"Why is user 123's order stuck?"*
Claude responds by:
1. Calling your `users` service to check the account
2. Calling your `orders` service to inspect the order
3. Calling your `payments` service to verify the transaction
4. Giving you a complete diagnosis
**No API wrappers. No manual integrations. Your services just work with AI.**
## What is MCP?
[Model Context Protocol](https://modelcontextprotocol.io) is Anthropic's open standard for connecting AI models to external tools. Think of it like a microservices registry, but for AI.
With MCP, your go-micro services become **tools** that Claude can discover and call directly.
## The Integration
### For Library Users (Just Add Comments!)
```go
package main
import (
"context"
"go-micro.dev/v5"
"go-micro.dev/v5/gateway/mcp"
)
type UserService struct{}
// GetUser retrieves a user by ID. Returns user profile with email and preferences.
//
// @example {"id": "user-123"}
func (s *UserService) GetUser(ctx context.Context, req *GetUserRequest, rsp *GetUserResponse) error {
// implementation
return nil
}
type GetUserRequest struct {
ID string `json:"id" description:"User's unique identifier"`
}
type GetUserResponse struct {
User *User `json:"user" description:"The user object"`
}
func main() {
service := micro.NewService(micro.Name("users"))
service.Init()
// Register handler - docs extracted automatically from comments!
service.Server().Handle(service.Server().NewHandler(new(UserService)))
// Add MCP gateway
go mcp.Serve(mcp.Options{
Registry: service.Options().Registry,
Address: ":3000",
})
service.Run()
}
```
That's it. Your service is now AI-accessible **with automatic documentation**.
### For CLI Users (Just a Flag)
```bash
# Development with MCP
micro run --mcp-address :3000
# Production with MCP
micro server --mcp-address :3000
```
The CLI integration uses the same underlying library, so you get the same functionality either way.
## How It Works
1. **Service Discovery**: MCP gateway queries your registry (mdns/consul/etcd)
2. **Auto-Exposure**: Each service endpoint becomes an MCP tool
3. **Schema Conversion**: Request/response types → JSON Schema for AI
4. **Dynamic Updates**: New services appear as tools automatically
For example, if you have:
```go
type UsersService struct{}
func (u *UsersService) Get(ctx context.Context, req *GetRequest, rsp *GetResponse) error {
// ...
}
func (u *UsersService) Create(ctx context.Context, req *CreateRequest, rsp *CreateResponse) error {
// ...
}
```
Claude sees:
```
Tools:
- users.UsersService.Get
- users.UsersService.Create
```
And can call them with natural language: *"Get user 123's details"*
## Real-World Use Cases
### 1. AI-Powered Customer Support
```bash
# Claude can help support agents
User: "Why is my order taking so long?"
Claude: Let me check...
→ Calls orders.Orders.Get with user's order ID
→ Calls shipping.Shipping.Track with tracking number
→ Calls inventory.Inventory.Check with product ID
Claude: "Your order is waiting for inventory. The product
is expected to be restocked on Feb 15. Would you like to
switch to an in-stock alternative?"
```
### 2. Debugging Production Issues
```bash
# Tell Claude the symptoms, it investigates
You: "Users can't log in. Check if it's the auth service."
Claude:
→ Calls health.Check on auth service
→ Calls metrics.Get for error rates
→ Calls logs.Recent for auth failures
→ Calls database.ConnectionPool for connection issues
Claude: "The auth service is healthy but the connection
pool is exhausted. Current: 100/100. Recommend increasing
pool size or checking for connection leaks."
```
### 3. Automated Operations
```bash
# Claude as an operations assistant
You: "Scale up the worker service"
Claude:
→ Calls infrastructure.Services.List to find workers
→ Calls infrastructure.Services.Scale with new count
→ Calls metrics.Monitor to watch the scale-up
Claude: "Scaled from 3 to 5 workers. All healthy and
processing jobs normally."
```
### 4. AI Data Analysis
```bash
# Claude can query your services for insights
You: "Show me revenue trends for the last quarter"
Claude:
→ Calls analytics.Revenue.GetTrends with date range
→ Calls analytics.Revenue.Compare with previous quarter
→ Calls analytics.Revenue.TopProducts
Claude: "Revenue is up 23% vs Q4. Top driver is product X
with 45% growth. However, churn increased 5% — recommend
investigating retention."
```
## Deployment Patterns
### Pattern 1: Embedded Gateway
Add MCP directly to your services:
```go
func main() {
service := micro.NewService(...)
go mcp.Serve(mcp.Options{
Registry: service.Options().Registry,
Address: ":3000",
})
service.Run()
}
```
**Best for**: Simple deployments, quick prototypes
### Pattern 2: Standalone Gateway
Deploy a dedicated MCP gateway service:
```go
// cmd/mcp-gateway/main.go
package main
import (
"go-micro.dev/v5/gateway/mcp"
"go-micro.dev/v5/registry/consul"
)
func main() {
mcp.ListenAndServe(":3000", mcp.Options{
Registry: consul.NewRegistry(),
})
}
```
**Best for**: Production, multiple services, centralized auth
### Pattern 3: Docker Compose
```yaml
version: '3.8'
services:
users:
build: ./users
environment:
- MICRO_REGISTRY=mdns
orders:
build: ./orders
environment:
- MICRO_REGISTRY=mdns
mcp-gateway:
build: ./mcp-gateway
ports:
- "3000:3000"
environment:
- MICRO_REGISTRY=mdns
```
**Best for**: Local development, testing
### Pattern 4: Kubernetes
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-gateway
spec:
replicas: 2
template:
spec:
containers:
- name: mcp-gateway
image: myregistry/mcp-gateway:latest
ports:
- containerPort: 3000
env:
- name: MICRO_REGISTRY
value: "consul"
- name: MICRO_REGISTRY_ADDRESS
value: "consul:8500"
```
**Best for**: Production at scale
## Security Considerations
### Add Authentication
```go
mcp.Serve(mcp.Options{
Registry: registry.DefaultRegistry,
Address: ":3000",
AuthFunc: func(r *http.Request) error {
token := r.Header.Get("Authorization")
if !validateToken(token) {
return errors.New("unauthorized")
}
return nil
},
})
```
### Network Isolation
Deploy MCP gateway in a private network:
```
Internet
┌──────▼────────┐
│ micro server │ :8080 (public)
│ + Auth │
└──────┬────────┘
┌──────▼────────┐
│ MCP Gateway │ :3000 (private)
└──────┬────────┘
┌──────────┼──────────┐
│ │ │
┌───▼───┐ ┌──▼────┐ ┌──▼────┐
│ users │ │ orders│ │payments│
└───────┘ └───────┘ └────────┘
(private) (private) (private)
```
Only the HTTP gateway is public. MCP gateway and services are internal.
## Library vs CLI
Both approaches use the **same underlying library** (`go-micro.dev/v5/gateway/mcp`):
| Approach | Users | Benefits |
|----------|-------|----------|
| **Library** | Import `gateway/mcp` package | Full control, works anywhere (Docker/K8s) |
| **CLI** | Use `--mcp-address` flag | Zero code changes, instant MCP support |
The CLI is just a convenient wrapper around the library.
## Getting Started
### Install
```bash
go get go-micro.dev/v5@v5.16.0
```
### Library Usage
```go
import "go-micro.dev/v5/gateway/mcp"
go mcp.Serve(mcp.Options{
Registry: service.Options().Registry,
Address: ":3000",
})
```
### CLI Usage
```bash
micro run --mcp-address :3000
# or
micro server --mcp-address :3000
```
### Test It
```bash
# List available tools
curl http://localhost:3000/mcp/tools
# Call a tool
curl -X POST http://localhost:3000/mcp/call \
-d '{"tool": "users.Users.Get", "input": {"id": "123"}}'
```
## New in v5.16.0: Stdio Transport & Auto-Documentation
We've added two major features that make MCP even more powerful:
### 1. Stdio Transport for Claude Code
Use go-micro services directly in Claude Code with stdio transport:
```bash
# Start MCP server with stdio (no HTTP needed)
micro mcp serve
```
Add to Claude Code config (`~/.claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"my-services": {
"command": "micro",
"args": ["mcp", "serve"]
}
}
}
```
Now Claude Code can discover and call your services directly!
### 2. Automatic Documentation Extraction
Services now **automatically extract documentation** from Go comments:
```go
// GetUser retrieves a user by ID from the database.
//
// @example {"id": "user-123"}
func (s *UserService) GetUser(ctx context.Context, req *GetUserRequest, rsp *GetUserResponse) error {
// implementation
}
// Register handler - docs extracted automatically!
handler := service.Server().NewHandler(new(UserService))
```
**No manual configuration needed!** Claude understands your service from your code comments.
### 3. MCP Command Line Tools
The new `micro mcp` command provides utilities for working with MCP:
```bash
# Start MCP server (stdio by default)
micro mcp serve
# Start with HTTP
micro mcp serve --address :3000
# List available tools
micro mcp list
# Test a tool
micro mcp test users.Users.Get
```
## What's Next?
We're continuing to evolve MCP support:
- **Streaming responses** for long-running operations
- **Rate limiting** and usage tracking
- **MCP server discovery** (browse available gateways)
- **Enhanced schema generation** from struct tags
## Philosophy
Go Micro has always been about **composable microservices**. MCP extends that philosophy:
- **Your services, your way**: MCP doesn't change how you build services
- **Library-first**: Works for all users, not just CLI users
- **Zero vendor lock-in**: Open protocol, works with any MCP client
- **Production-ready**: Security, auth, and scaling built-in
AI is becoming infrastructure. Your services should be ready.
## Try It Today
```bash
# Update to v5.16.0
go get go-micro.dev/v5@v5.16.0
# Add MCP to your service
import "go-micro.dev/v5/gateway/mcp"
go mcp.Serve(mcp.Options{
Registry: service.Options().Registry,
Address: ":3000",
})
# Or use the CLI
micro run --mcp-address :3000
```
See the [MCP Gateway documentation](/docs/mcp) for full details.
---
*Go Micro is an open source framework for distributed systems development in Go. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/1">← Introducing micro deploy</a></div>
<div><a href="/blog/">All Posts</a></div>
<div><a href="/blog/3">Building the AI-Native Future →</a></div>
</div>
+84
View File
@@ -0,0 +1,84 @@
---
layout: blog
title: "Doubling Down on Agents"
permalink: /blog/20
description: "Go Micro made services easy by being opinionated, batteries-included, and pluggable. We're applying the same model to agents — a model, memory, and tools that compose like a service does."
---
# Doubling Down on Agents
*June 10, 2026 &bull; Asim Aslam*
Go Micro made microservices easy by having an opinion. You called `micro.New`, and it composed the pieces a service needs — service discovery, RPC, pub/sub, config, storage — behind one interface, with defaults that worked out of the box. You could test an endpoint in minutes, and when you needed to, you swapped any piece: mDNS for etcd, HTTP for gRPC, in-memory for Postgres. Opinionated, batteries-included, and pluggable. That combination is why people used it.
Agents need the same thing, and right now most of them don't have it.
## What an agent is made of
Strip an agent down and it composes a small set of pieces, the same way a service does:
- a **model** that reasons,
- **memory** so it carries context across turns,
- **tools** it can call to act,
- and **guardrails** so it stops where you want it to.
Today most agent code wires these together by hand, per project, with whatever libraries happen to be nearby. That's where microservices were before frameworks: every team re-solving discovery, load balancing, and retries in their own way. The work isn't building the model — the model is a given. The work is everything around it.
So we're doing for agents what Go Micro did for services: compose those pieces, with defaults, behind one interface, and keep every piece swappable.
```go
agent := micro.NewAgent("assistant",
micro.AgentProvider("anthropic"), // model
micro.AgentMemory(micro.NewInMemory(50)), // memory — default is store-backed and durable
micro.AgentTool("weather", "Get the weather",
map[string]any{"city": map[string]any{"type": "string"}},
func(ctx context.Context, in map[string]any) (string, error) {
return getWeather(in["city"].(string))
}),
micro.AgentMaxSteps(8), // guardrails
)
```
Nothing here is required. `micro.NewAgent("assistant")` gives you a working agent with a default model, durable store-backed memory, your services as tools, and the built-in `plan` and `delegate` tools. The options are there for when you outgrow the defaults — which is exactly how the service side has always worked.
## The pieces we just made pluggable
Two of these were hardcoded until now, and both are the kind of thing the framework should own:
**Memory.** An agent's conversation is now behind a `Memory` interface. The default is store-backed, so memory is durable across restarts on whatever store you already use — file, Postgres, NATS KV — the same pluggable `store` that backs your services. Supply your own implementation when you want in-process, a database, or a semantic store. Memory is to an agent what the store is to a service: a first-class, swappable dependency, not something you bolt on.
**Tools beyond services.** An agent's tools were its registered services, reached over RPC. That's powerful, and it stays the default — but an agent often needs a plain function, a local computation, an external API. `AgentTool` registers any function as a tool the model can call, alongside the services it discovers. Agents are no longer limited to orchestrating RPC.
These join what's already there: the `plan` and `delegate` built-in tools, and the `MaxSteps` and `ApproveTool` guardrails. The model, memory, tools, and guardrails now compose the way registry, broker, and store compose for a service.
## Microagents
People are going to build large, monolithic agents — one brain with a hundred tools, holding everything in one context. It will work for a while, and then it will hit the same wall the monolith hit: one unit that knows a little about everything and can't hold the full context of anything.
The answer is the same one microservices gave. If there's an agent for everything, those are microagents — each scoped to a domain, each small enough to reason well about its own services, each independently deployable and composable. We already have the mechanics: an agent is a service, agents reach each other over RPC, and `delegate` lets one hand work to another. Distributing the intelligence is the same move as distributing the services, and it's available now.
## Services, agents, workflows
That leaves three primitives, and they compose:
- a **service** is a capability — endpoints, data, business logic;
- an **agent** is intelligence pointed at capabilities — it reasons, remembers, and acts through tools;
- a **workflow** is a deterministic trigger — when an event happens, run a known path, or hand off to an agent.
All three are Go code, all three register, all three communicate over RPC, all three deploy the same way. You can build a system out of any combination of them. That's the point: these aren't three products, they're three primitives on one substrate, and together they're enough to build the foundation of most platforms you'd want to build.
## What's still missing
I'd rather be plain about the gaps than pretend they're filled. An agent framework that matches what the service framework offers still needs:
- **Knowledge / retrieval** — a pluggable way to ground an agent in a corpus, the way memory grounds it in its own history.
- **Streaming** — the model interface has it stubbed; agents want token streaming for real interfaces.
- **An explicit reasoning loop** — the loop is functional today but lives inside the providers; making it a first-class, inspectable piece (without turning it into a graph DSL) is the right next step.
These are the next pieces to make pluggable. The principle holds for each: a working default, behind an interface, swappable.
## The same foundation
I started Go Micro because building microservices in Go was harder than it should have been, and the fix was an opinionated framework that got out of your way. Building agents is harder than it should be for the same reasons, and the fix is the same. Services were the foundation for a generation of distributed systems. Agents — built on those same services, with the same opinionated and pluggable model — are the foundation for the next one.
This is the direction now. Services, agents, workflows, one substrate.
+76
View File
@@ -0,0 +1,76 @@
---
layout: blog
title: "When the Event Is the Prompt"
permalink: /blog/21
description: "Most agents wait for a human to type something. The useful ones don't — they run because something happened in the system. A Flow turns an event into the prompt, and an agent acts on its own."
---
# When the Event Is the Prompt
*June 15, 2026 &bull; Asim Aslam*
Almost every agent demo starts the same way: a human types a prompt, the agent responds. That framing is a habit from chat, and it hides the more useful case. The agents worth running don't wait for you to ask. They run because something happened — a user signed up, a payment failed, a deployment finished, a metric crossed a line. In those systems there is no prompt, because there is no human in the loop. The event is the prompt.
Go Micro has had the pieces for this since we added [workflows](/blog/18): a `Flow` subscribes to a broker topic, and an `Agent` reasons and acts. Putting them together is the point of this post.
## The shape
A workflow is the deterministic half — it knows *when* to run (an event arrives) and turns that event into a prompt. An agent is the dynamic half — it decides *what to do* about it. Connect them and you get an agent that runs on events, unattended.
```go
// The agent: it onboards new users using its services.
onboarder := micro.NewAgent("onboarder",
micro.AgentServices("workspace", "notify"),
micro.AgentPrompt("You onboard new users. Create their workspace and send a welcome."),
micro.AgentProvider("anthropic"),
)
go onboarder.Run()
// The workflow: when a user signs up, hand the event to the agent.
f := micro.NewFlow("onboard",
micro.FlowTrigger("events.user.created"),
micro.FlowPrompt("A new user signed up: {{.Data}}. Get them set up."),
micro.FlowAgent("onboarder"),
)
```
When a `user.created` event lands on the broker, the flow renders it into a prompt and hands it to the onboarder over RPC. The agent looks at its tools, creates the workspace, sends the welcome, and stops. No one typed anything.
There's a runnable version of exactly this in [`internal/harness/agent-flow`](https://github.com/micro/go-micro/tree/master/internal/harness/agent-flow) — real services, registry, RPC, broker, and the agent loop, with only the model mocked so it runs without a key:
```
> event: publishing events.user.created {"email":"alice@acme.com"}
[onboarder] → workspace_WorkspaceService_Create({"owner":"alice@acme.com"})
[workspace] created ws-1 for alice@acme.com
[onboarder] → notify_NotifyService_Send({"to":"alice@acme.com","message":"Welcome — your workspace is ready."})
[notify] 📨 to=alice@acme.com message="Welcome — your workspace is ready."
✓ the agent onboarded the user — triggered by an event, not a prompt
```
## This is where microagents become real
A chat agent is something you visit. An event-driven agent is something that runs. That difference is what makes [an agent for everything](/blog/20) practical: each domain gets a small agent that wakes on its own events and acts. Payments has an agent that responds to failed charges. Ops has one that reacts to alerts. Support has one that triages new tickets. None of them is a monolithic brain holding the whole system in one context; each is scoped, each runs on its events, and they reach each other over RPC when they need to. It's the microservices decomposition, applied to the intelligence.
The mechanics are already there: an agent is a service, agents call each other with `delegate`, and a flow is the trigger. The only thing that changed is the absence of a person at the start of the loop.
## What running unattended demands
Taking the human out of the loop raises the bar, and it's worth being honest about that rather than pretending it's free.
- **Guardrails stop being optional.** When you're sitting in a chat you are the stopping condition — you see a wrong turn and intervene. An event-driven agent has no one watching, so the bounds have to be in the code: `MaxSteps` to cap what it can do per event, and `ApproveTool` to gate the actions that genuinely need a human or a policy check. For an unattended agent these are load-bearing, not nice-to-haves.
- **Observability becomes the interface.** If no one is reading the agent's replies, the trace of what it did is the only way to know it did the right thing. The reply text matters less than a record of the tools it called and the results it got.
- **Execution has to be durable.** An event-driven agent may run longer than a request, and it has to survive a restart without dropping the work or repeating it. Its memory is already store-backed and durable; the loop itself needs to be checkpointed and resumable in the same way.
The first of these is shipped today. The other two are the next things to build, and they're the same gaps the [last post](/blog/20) named — autonomy is what makes them urgent.
## Three primitives, one substrate
This is the whole picture coming together:
- a **service** is a capability;
- an **agent** is intelligence pointed at capabilities;
- a **workflow** is the trigger — and when the trigger is an event, it is also the prompt.
You compose them. A workflow turns an event into a prompt, an agent reasons about it, services do the work, and other agents pick up the parts they own. The human moves from typing every instruction to setting the system up and letting it run. That is the shift worth building for: not better chat, but software that acts on its own — on the same services, agents, and workflows you already have.
+85
View File
@@ -0,0 +1,85 @@
---
layout: blog
title: "Integrating x402: Payments for Agents"
permalink: /blog/22
description: "Agents that act on their own eventually need to pay on their own. Go Micro now speaks x402 — the HTTP 402 payment standard — so a tool can require a stablecoin payment and an agent can settle it, with the chain pluggable behind a facilitator."
---
# Integrating x402: Payments for Agents
*June 15, 2026 &bull; Asim Aslam*
The [last post](/blog/21) was about agents that run on their own — triggered by an event, acting without a human prompt. Follow that one step further and you reach something agents can't do yet in most systems: pay. An autonomous agent that calls an API, rents compute, or uses another agent's service will, sooner or later, need to settle for it — without a person reaching for a credit card. There is now a standard for exactly that, and we're integrating it.
## What x402 is
x402 is an open payment protocol built on the HTTP **402 Payment Required** status code. The flow is simple: a client requests a resource, the server answers `402` with machine-readable payment requirements (amount, asset, network, where to pay), the client pays and retries with an `X-PAYMENT` header, and the server verifies the payment and serves the resource. It's designed for stablecoins and for machine-to-machine use — agents paying for things, per request.
It started at Coinbase, it's multi-chain (Base, Solana, Ethereum, Polygon, and more), and as of April 2026 it's governed by the **x402 Foundation under the Linux Foundation**, with founding members including Google, Visa, Stripe, AWS, Mastercard, Circle, and Shopify. That governance is why we're comfortable integrating it: it's an open standard with the payments industry behind it, not a single vendor's API.
## How Go Micro integrates it
The same way it integrates everything else — interface-first, with a default, and pluggable.
The core is HTTP middleware in `wrapper/x402`. It enforces the 402 challenge and verifies payments, but it carries **no chain or crypto code**. Verification and settlement are delegated to a pluggable **Facilitator**:
```go
type Facilitator interface {
Verify(ctx context.Context, payment string, req Requirements) (Result, error)
}
```
So Go Micro stays chain-agnostic. "Base through Coinbase" and "Solana through Alchemy" are not two integrations — they're the same middleware pointed at two facilitators. The facilitator does the on-chain work; the framework speaks the protocol.
```go
pay := x402.Middleware(x402.Config{
PayTo: "0xYourAddress", // where payments go
Network: "solana", // or "base", ...
Amount: "10000", // smallest units, e.g. 0.01 USDC
})
mux.Handle("/paid", pay(handler))
```
## Opt-in, at the gateway
Because every Go Micro endpoint is already an AI-callable tool through the MCP gateway, that's the natural place to charge: a tool call is the thing worth a payment. So x402 is wired into both the built-in `micro mcp serve` and the standalone `micro-mcp-gateway`, and it is strictly **opt-in** — off unless you set a pay-to address.
```bash
micro mcp serve --address :3000 \
--x402-pay-to 0xYourAddress \
--x402-network solana \
--x402-amount 10000 \
--x402-facilitator https://facilitator.example
```
With payments enabled, the `/mcp/call` endpoint requires a verified payment; listing tools and health checks stay free. Without the flag, nothing changes. The standalone gateway takes the same options via flags or environment variables, so you can put a paid gateway in front of services you didn't write.
Different tools can cost different amounts. Because pricing is an operator concern — the payTo address is the operator's, and prices change without redeploying anyone's service — it's set at the gateway with a config file, the same way per-tool scopes and rate limits already are:
```json
{ "payTo": "0xYourAddress", "network": "solana", "asset": "USDC",
"amount": "0",
"amounts": { "weather.Weather.Forecast": "10000", "search.Search.Query": "5000" } }
```
```bash
micro mcp serve --address :3000 --x402-config x402.json
```
`amount` is the default (here `0` — free), and `amounts` sets per-tool overrides. There's no "pricing" abstraction in the framework; it's just the x402 amount, resolved per tool, in the protocol's own vocabulary.
## Why this matters
Go Micro's premise has been that every service is a tool an agent can call. x402 adds one word: a *paid* tool. That turns "tools as services" into something with an economic side — a service can charge per call, and an agent can pay for it, with no human in the loop on either end. It gives the services people build a native way to be paid for, and it gives Go Micro a place in the supply side of an agent economy: the rails for agents to act *and* transact.
## Honest about the edges
- **It's opt-in and dependency-light.** No pay-to address, no payments. Go Micro pulls in no chain libraries — the facilitator does that work.
- **Amounts start simple.** A default amount, or per-tool amounts via the gateway config today; metered usage and prepaid balances (the place a "credit" concept would actually fit) are the harder design work to come.
- **A paying agent needs a budget.** Blog 21 argued that unattended agents need guardrails; an unattended agent that spends money needs them most. A spend cap belongs next to `MaxSteps` and `ApproveTool`, and it's the next piece to build on the agent side.
Agents that act, and now can pay. Services, agents, workflows, and payments — the substrate for software that operates, and transacts, on its own.
---
Sources: [x402 — Coinbase Developer Docs](https://docs.cdp.coinbase.com/x402/welcome), [What is x402? — Alchemy](https://www.alchemy.com/blog/how-x402-brings-real-time-crypto-payments-to-the-web), [x402 on Solana — solana.com](https://solana.com/x402/what-is-x402).
+66
View File
@@ -0,0 +1,66 @@
---
layout: blog
title: "Agent Guardrails"
permalink: /blog/23
description: "An autonomous agent fails in mundane ways — it loops, it runs away, it takes an action it shouldn't. Go Micro separates orchestration from execution safety, and gives every agent three guardrails at the point where tools actually run."
---
# Agent Guardrails
*June 16, 2026 &bull; Asim Aslam*
The interesting failures of an autonomous agent aren't dramatic. It calls the same tool with the same arguments over and over, making no progress. It takes thirty steps on a task that needed three, quietly running up cost. It performs an action that a human, or a policy, should have stood in front of. When an agent [runs on its own](/blog/21), there's no one watching to catch any of it.
A useful way to think about this — which came up in a community discussion recently — is to separate **orchestration** from **execution safety**. The model decides what to do; that's orchestration. Whether a decided action is actually allowed to run is a separate concern, and it shouldn't be tangled into the model or the services. Go Micro keeps them apart: every tool call an agent makes passes through one choke point, and that's where the guardrails live. They apply uniformly to service calls, custom tools, and `delegate`, and they touch neither the model nor your services.
There are three.
## Stop on count
`MaxSteps` bounds the total tool executions in one request. Past the limit, calls are refused and the model is told to stop and summarize. It's the blunt backstop against runaway cost.
```go
micro.NewAgent("worker", micro.AgentMaxSteps(8))
```
## Stop on repeat
This is the one the discussion was really about, and the one we didn't have. `MaxSteps` bounds *how many* calls, but not *whether they're the same call*. An agent can spend its entire budget calling one tool with identical arguments — each call succeeds, and none of them moves anything forward. A circuit breaker won't catch it either, because a circuit breaker reacts to *failures*, and a pointlessly repeated call isn't failing.
`LoopLimit` catches it: it bounds how many times the agent may call the same tool with the same arguments in one request. When the limit is hit, the call is refused with a message that names the loop, so the model changes approach instead of spinning:
> loop detected: you have already called "search.Search.Query" with the same arguments 3 times and the result will not change. Stop repeating it — try a different approach, or finish with what you have.
That refusal-with-reason is what lets the agent recover on its own. It's **on by default** (a lenient 3), because identical repeated calls are never useful; `AgentLoopLimit(0)` disables it.
```go
micro.NewAgent("worker", micro.AgentLoopLimit(3))
```
## Gate the action
`ApproveTool` is a hook called before each action runs. Return `false` to block it, with a reason the model sees — for human-in-the-loop approval, spend limits, allow/deny lists, or any policy:
```go
micro.NewAgent("worker", micro.AgentApproveTool(
func(tool string, input map[string]any) (bool, string) {
if strings.HasPrefix(tool, "billing_") {
return false, "billing actions require sign-off"
}
return true, ""
}))
```
## The hook is the seam
`ApproveTool` is also where an **external safety layer** plugs in. It sees every tool call before execution and can veto, so you can route decisions to your own rules, a budget service, or a third-party runtime-policy engine — without Go Micro depending on any of them. That's the value of separating the two concerns: orchestration stays in the agent, execution safety stays in the hook, and you can replace the safety layer without touching the agent. Loop detection ships built in because it's near-universal; everything beyond it is a policy you supply.
## At the edge, too
When agents reach tools **through the MCP gateway**, the gateway adds its own per-tool policies, independent of the agent: `RateLimit` (requests per second) and `CircuitBreaker` (a tool that fails repeatedly is temporarily blocked, so a failing dependency doesn't cascade). With the agent-side guardrails that's a complete set — bound the count, stop the spin, gate the action, rate-limit and circuit-break at the edge.
## Why it's the autonomy story
None of this matters much when you're sitting in a chat watching the agent work — you are the guardrail. It matters when no one is. An agent triggered by an event, running unattended, needs to fail safely and recover by itself rather than burning resources in a loop nobody sees. Guardrails aren't a feature bolted onto agents; for an autonomous agent they're part of what makes it safe to run at all.
See the [Agent Guardrails guide](/docs/guides/agent-guardrails) for the full reference.
+90
View File
@@ -0,0 +1,90 @@
---
layout: blog
title: "Durable Workflows"
permalink: /blog/24
description: "An event-driven workflow runs for minutes and has side effects partway through — it reserved stock, it charged a card. When the process dies mid-run, re-running from the top does it all again. Go Micro flows are now ordered, checkpointed steps that resume where they stopped."
---
# Durable Workflows
*June 17, 2026 &bull; Asim Aslam*
A workflow that calls real services is rarely instant and rarely side-effect-free. It reserves inventory at step one, charges a card at step two, sends a confirmation at step three. Each of those changes the world. So when the process dies between step two and step three — a deploy, an OOM, a node going away — you can't just run it again from the top: that reserves twice and charges twice. And if the workflow was triggered by an event with no human watching, nobody noticed it died at all.
This is the oldest problem in distributed systems, and it has an established answer: **durable execution** — checkpoint progress as you go, and on restart resume from where you stopped instead of from the beginning. Go Micro flows now do this.
## What a flow was, and what it is now
A `flow` used to run one augmented-LLM turn per event. Useful, but a single step — there was no notion of a task with stages, and nothing survived a crash.
A flow can now be an **ordered list of steps** — a task made of stages — and each step is checkpointed before and after. If the process dies mid-run, the run resumes at the step it stopped on, and the steps that already completed do not run again.
```go
f := micro.NewFlow("checkout",
micro.FlowTrigger("events.order.placed"),
micro.FlowRetry(2),
micro.FlowSteps(
micro.FlowStep{Name: "reserve", Run: micro.FlowCall("inventory", "Inventory.Reserve")},
micro.FlowStep{Name: "charge", Run: micro.FlowCall("payment", "Payment.Charge")},
micro.FlowStep{Name: "confirm", Run: micro.FlowCall("orders", "Orders.Confirm")},
),
)
```
A single-step flow keeps working exactly as before; steps are additive.
## How it resumes
State carries a typed payload plus a `Stage` marker — the name of the step the run is at. That marker is the single source of truth for "where it is," and it's the resume point. Before each step, the run is saved; after each step completes, the stage advances and the run is saved again. On restart, the engine loads the run and starts at `Stage`, so completed steps — and their side effects — are skipped.
Here is a run whose payment dependency is down on the first attempt:
```
first run:
reserve → inventory reserved
charge → payment dependency unavailable (crash)
run failed: payment gateway timeout
checkpoint: run 70643f61 is at step "charge" (status failed)
resume:
charge → payment captured
confirm → order confirmed
reserve ran 1 time(s) total — completed steps are not repeated on resume
```
`f.Pending(ctx)` lists incomplete runs after a restart; `f.Resume(ctx, runID)` continues one. The full example is [examples/flow-durable](https://github.com/micro/go-micro/tree/master/examples/flow-durable) — it needs no API key, because durability is the only thing on display.
## The honest part
Exactly-once is impossible if a crash lands *inside* a step — you can't know whether the charge went through. What durable execution actually gives you is at-least-once delivery plus a stable idempotency key per step (`runID` + step name), so a replayed step is recognized and de-duplicated by the service receiving it. Side-effecting steps have to honor that key. A framework can make this consistent; it can't repeal the underlying reality, and claiming otherwise would be dishonest.
## Where agents come in
Go Micro draws the line from [Anthropic's taxonomy](/blog/18): **workflows** follow a predefined path; **agents** direct themselves. A flow is the workflow — *you* author the steps. An agent is the self-directed one — the *model* authors the steps at runtime. They are two kinds of control flow, and durability is orthogonal to both.
So a workflow step can hand off to an agent:
```go
micro.FlowStep{Name: "resolve", Run: micro.FlowDispatch("support-agent")}
```
The deterministic part stays a durable flow; the open-ended part is an agent. The same `Checkpoint` that persists a flow run is the mechanism the agent's own loop will use to become durable too — that's the next step, and it's a bigger one, because it means the agent owning its loop rather than the provider driving it. What ships today is durable workflows that can call services and dispatch to agents.
## No separate engine
The pluggability is the usual Go Micro shape. The built-in `Checkpoint` is store-backed — point the default store at Postgres or NATS KV and a run survives a real restart, no extra moving parts. Need more, or already run [Temporal](https://temporal.io) or [Restate](https://restate.dev)? Implement the `Checkpoint` interface and delegate to it; the explicit step model is what makes a flow mappable onto an external engine. Most teams need neither — the default is durable.
```go
type Checkpoint interface {
Save(ctx context.Context, run Run) error
Load(ctx context.Context, runID string) (Run, bool, error)
Delete(ctx context.Context, runID string) error
List(ctx context.Context) ([]Run, error)
}
```
That's the through-line. Durable execution isn't a workflow engine you adopt alongside your services; it's a store and an interface, and the workflow is still just an ordered list of steps you can read. Same as everything else in Go Micro — the abstraction is the service, and this is one more thing the substrate underneath it now handles.
See the [Agents and Workflows guide](/docs/guides/agents-and-workflows) for the full reference.
+61
View File
@@ -0,0 +1,61 @@
---
layout: blog
title: "Go Micro is Becoming a Framework for Agentic Development"
permalink: /blog/25
description: "Three months ago, with Anthropic's support, Go Micro went all in on AI. A look back at what shipped — agents, workflows, guardrails, payments, durable execution — and where it's heading."
---
# Go Micro is Becoming a Framework for Agentic Development
*June 17, 2026 &bull; Asim Aslam*
When Anthropic began sponsoring Go Micro, we committed to building in the open and reporting progress as it happened. It has been three months. Each post so far covered one change; this one is about what they add up to. **Go Micro is becoming a framework for agentic development, the same way it has been a framework for services for the last decade.** It's an extension of the existing framework, not a rewrite, because an agent is a distributed system and Go Micro is how you build one.
## The idea
Ten years ago Go Micro put the hard parts of distributed systems (discovery, transport, encoding, pub/sub, storage) behind a small set of pluggable interfaces with working defaults, so you could build a system of services quickly. The premise this quarter was that an agent is not a special case. An agent is a model, a prompt, and a set of tools, and once it has more than one of anything it is a distributed system again: it has to discover services, call them, persist state, and recover from failure. Building that is what Go Micro is for.
So the work has been to make agents a first-class part of the same framework, and to be clear about the boundary. Go Micro is the substrate, not the brain. It won't out-engineer a prompt library. What it offers is the way you build a *system* of agents and services together. ([The Evolution of Microservices](/blog/19) made this argument in full.)
## What shipped
The pattern to notice is how little each piece had to invent.
**The agent as a service.** [`micro.NewAgent`](/blog/16) creates an agent that *is* a service: it discovers its assigned services as tools, runs the model's tool loop, and registers a `Chat` RPC endpoint like anything else. There is no separate runtime and no graph DSL; a tool call is an RPC, and the LLM drives it. ([Agents for Services](/blog/15) laid out the model.)
**Plan and delegate.** Every agent gets two built-in tools: [`plan`](/blog/17), an ordered plan persisted to its memory and shown back to it on later turns, and `delegate`, which hands a subtask to another registered agent over RPC, or to a focused ephemeral sub-agent when there is no specialist. Multi-agent systems built from tools rather than a coordinator engine.
**Workflows.** Not every task should be model-driven. [Following Anthropic's taxonomy](/blog/18), Go Micro grew a deterministic counterpart: a `Flow` is a predefined path triggered by an event. It can run a step itself or [hand the event to an agent](/blog/21): the workflow triggers, the agent reasons.
**Guardrails.** Autonomous agents fail in ordinary ways: they loop, they run away, they take an action they shouldn't. [Three guardrails](/blog/23) sit at the one point every tool call passes through: `MaxSteps`, `LoopLimit` (on by default), and `ApproveTool` for human-in-the-loop or policy. Orchestration stays in the agent; execution safety is a separate, replaceable concern.
**Tool-execution middleware.** Because a tool call is an RPC, the middleware Go Micro has always had for clients and servers now applies to tools. `AgentWrapTool` wraps execution for logging, metrics, or retries, with the same `func(next) next` shape as `client.CallWrapper`. The guardrails are themselves wrappers underneath.
**Durable workflows.** A workflow that calls real services has side effects partway through, and a crash mid-run shouldn't repeat them. Flows became [ordered, checkpointed steps](/blog/24) that resume where they stopped, backed by a pluggable `Checkpoint` (store-backed by default, or delegated to Temporal or Restate). It is durable execution as a store plus an interface, with no separate engine to run.
**Payments.** An agent acting on its own eventually needs to pay for something. Go Micro [speaks x402](/blog/22): a tool can require a stablecoin payment and an agent can settle it, with the chain pluggable behind a facilitator.
**A consistent state model.** Most recently the plumbing got consistent. Services, agents, and flows each keep their state in their own store table (`service/{name}`, `agent/{name}`, `flow/{name}`) through a new `store.Scope` handle, instead of mutating a global one. Flows register in the registry like agents, so `micro flow list` and `micro agent list` are the same command filtered by type, while `micro flow runs` and `micro agent history` read durable state. Live state comes from the registry, history from the store.
## The shape of it
None of this is a new framework bolted on. It is the existing one, pointed at agents:
- An agent is a **service**. A flow is a **service**. They register, they're discovered, they're called over RPC.
- A tool call is an **RPC**, so discovery, load-balancing, middleware, and codecs all apply.
- Agent memory, plans, and workflow runs are **store** records. Durability is the **store** plus an interface.
- The events that trigger agents are the **broker**.
There is no graph to learn and no engine to run beside your services. The abstraction is still the service; agents are what you build with it now.
## What's next
- **Secure by default.** Defaults that were fine when a human ran `micro call` on a laptop are not fine when an autonomous agent can reach an endpoint. v6's theme is flipping those defaults (TLS, identity, bounded execution) without losing the zero-config dev loop.
- **Durable agents.** Flows are durable now; the agent's own loop is not yet. The same `Checkpoint` should let a long-running agent survive a restart and resume. The one structural change left is the agent owning its loop.
- **Staying the substrate.** The discipline is to not drift up the stack into prompt tooling or a hosted runtime, and to be the distributed-systems layer for systems of agents.
A small end-to-end harness now boots a real world (services, a durable flow that crashes and resumes, and a guardrailed agent), asserts the outcome, and shuts down. It runs on every change against a mock model, and against a live model on a schedule.
The upshot is that building an agent in Go Micro looks like building a service, because underneath it is one. The infrastructure for a system of agents is, for the most part, the infrastructure for a system of services, and that is the decade of work already in the framework. Services remain the foundation; agents are what we are building on top of them.
Thanks to Anthropic for the sponsorship. Join us in a new [Discord](https://discord.gg/G8Gk5j3uXr) to discuss the future of the project.
+53
View File
@@ -0,0 +1,53 @@
---
layout: blog
title: "Agents Across Frameworks: A2A"
permalink: /blog/26
description: "Go Micro agents already call each other over RPC. Now they speak the Agent2Agent protocol too — reachable by, and able to reach, agents built on any framework. Cards are generated from the registry, the same way the MCP gateway derives tools."
---
# Agents Across Frameworks: A2A
*June 18, 2026 &bull; Asim Aslam*
Inside a Go Micro system, agents already talk to each other. An agent is a service with an `Agent.Chat` endpoint, so `delegate` just calls another agent over RPC. That works as long as everyone is on Go Micro. The moment an agent is built on a different framework, the conversation stops: it can't call yours, and yours can't call it.
[A2A](https://a2a-protocol.org) — the open Agent2Agent protocol — is the standard that closes that gap, and Go Micro now speaks it.
## A2A is to agents what MCP is to tools
This lines up with something Go Micro already does. The MCP gateway exposes your *services as tools* to any MCP-speaking agent. The A2A gateway exposes your *agents as agents* to any A2A-speaking client. Two interop standards, two front doors — and now both are covered.
The design is the same in both cases: **discovery is generated from the registry.** MCP derives a tool from each service endpoint. A2A derives an **Agent Card** — the JSON descriptor other agents read to find and call yours — from each agent's registry metadata. There is nothing to publish and no code to add. Register an agent, and it has a card:
```bash
micro a2a serve --address :4000
micro a2a list
```
An incoming A2A task is translated to the agent's existing `Agent.Chat` RPC — the same call `delegate` and flows already use. The agent's loop, memory, guardrails, and tool wrappers all apply unchanged. The gateway is a protocol adapter, not a second agent runtime.
## Both directions
Exposing your agents is half of it. The other half is calling agents that aren't yours. The `a2a.Client` does that, by URL, and it's wired into the two places work gets handed off:
```go
// As a workflow step — the cross-framework counterpart to Dispatch:
flow.Step{Name: "research", Run: flow.A2A("https://other.example.com/agents/research")}
```
```go
// From inside an agent, delegate to a URL and it goes over A2A:
// "delegate this to https://other.example.com/agents/research"
```
When `delegate`'s target is an `http(s)` URL instead of a local agent name, the subtask is sent over A2A. The model doesn't learn a new tool; it just delegates to a URL.
## Scope
This is the synchronous JSON-RPC binding: `message/send` runs the agent and returns a completed task, `tasks/get` retrieves one, and Agent Cards are served for discovery. Streaming (`message/stream`), multi-turn `input-required`, and push notifications are advertised as unsupported on the card, so clients negotiate correctly. Those are the follow-ups; the synchronous binding is what makes a Go Micro agent both reachable from, and able to reach, the wider ecosystem today.
## Where it fits
A2A completes the interop set. MCP exposes services as tools, A2A exposes agents as agents, and [x402](/blog/22) handles payment between them — all derived from the registry, none of them a new runtime. An agent stays a service; A2A is one more way to reach it, alongside the `Chat` RPC, `micro chat`, and a flow. Building an agent that the rest of the world can talk to is, again, building a service.
See the [A2A guide](/docs/guides/a2a-protocol) for the full reference.
+52
View File
@@ -0,0 +1,52 @@
---
layout: blog
title: "Bringing an Open Source Project Back from the Dead"
permalink: /blog/27
description: "Go Micro started in January 2015, went through a VC-funded company and a platform pivot, and then went quiet. This is how it came back — and how agents, services, and flows brought it to v6."
---
# Bringing an Open Source Project Back from the Dead
*June 18, 2026 &bull; Asim Aslam*
I open sourced Go Micro in January 2015. Eleven years later we just tagged v6. What a crazy and surreal experience that is. the project was basically dead or sitting stale for quite some time after VC funding dried up, I shutdown the business and tried to hand off to the community. So much has happened in that time and I've learned a lot of lessons. Like how to be grateful for what you had when you had it.
## The stages
The first version of Go Micro was a simple library to make microservices: registry, transport, RPC, and pub/sub behind a small set of pluggable interfaces. It found some traction on HackerNews which gave me a bit of confidence. Up to v1 it was mostly bootstrapped and a corporate sponsorship that then became a feedback loop as they used it in production.
In V2 everything changed. I raised venture funding and tried to build a team and product around. I was painfully unprepared and inexperienced for that challenge. The open source framework and company were like oil and water. Like any founder who's raised money around open source will tell you. Its hard. Really hard.
V3 was an iteration as a platform-as-a-service built on top of the framework. There was real effort behind it, but it wasn't what people had come for, and it didn't get traction. I thought from an open source perspective it was great because it coupled the framework with a runtime. But it was too opinionated for most people. We quickly switched gears into offering APIs on top instead which seemed like it would have tbe most traction and did get a lot of signups but ultimately fatigue and a declining runway got the better of me.
Then the difficult part. I had to shut down the company. For a long while Go Micro just sat there — a decaying open source project with no activity to show for it. Like a lot of long running projects. They die a slow quiet death. Go Micro was no different. I was really sad about that but there was nothing I could do to change it. No team, no funding, I tried different ways but it was a no go.
## Rebuilding
V4 and V5 were a bit of a come back story. I was trying to remember what Go Micro was actually originally built for. The answer: a Go framework for developers. Not a company, not a platform. Just a way to build distributed systems in Go with infrastructure abstractions and an opinionated development model. So I started bringing it back to that.
Then everything changed. AI came on the scene. And Anthropic launched an open-source grant for Claude Code. So heck, why not, I applied and got access to 6 months of Claude Code for free. Boom. Go Micro is reborn. Let me be clear about one thing: working on a large, aging codebase alone is slow and painful. That slowness kills any momentum and motivation. Building with Claude Code changed everything. I was able to tackle issues I had no motivation to deal with before. To plot out a roadmap and then to rebuild a vision for the future. One in which services become the tools for agents and agents themselves become a core aspect of the framework.
## What I actually wanted
Coming back to that. The vision became clear again. Rather than reset, this was about evolution. The original goal was to make building distributed systems simple. What became clear more now is that agents are distributed systems too. An agent is a model, a prompt, and a set of tools — and when it has to do more than one of thing e.g discover services, call them, hold state, and recover from failure, thats when it becomed a distributed system. That is the exact problem Go Micro already solved for services.
So services and agents aren't two separate things. They're complementary systems. An agent lives along side services; a service is a tool an agent calls. Building an agent looks like building a service, because underneath it can be the same thing, just with a different style of OODA loop thats dynamic.
## What now
So the idea from v4 through v6 turned into:
- **Services** — the core framework: discovery, RPC, pubsub, storage.
- **Agents** — a model with memory and tools that use services, with planning, delegation, and guardrails built in.
- **Flows** — durable, event-driven workflows for the deterministic parts and agents too.
And the interop that makes it matter beyond a single codebase: **MCP**, so every service is automatically a tool any AI agent can call, and **A2A**, so every agent can be discovered and called by agents on other frameworks. Both are generated from the registry which is already used for service discovery.
## v6
V6 is the beginning of something real. It's secure by default, leads with agents, and it moves the everything to a new model of development. One thats firmly rooted in the AI era of technology. Its not a rewrite: the decade of services work is the foundation, and v6 is that foundation leveraged for agents.
Projects dont come back from the dead like this. AI brought it back from the dead. Agents made services more relevant. And Claude Code made it possible for me to ship code again.
Thanks to Anthropic for the grant and the sponsorship. Join me in the [Discord](https://discord.gg/G8Gk5j3uXr) to discuss further.
+95
View File
@@ -0,0 +1,95 @@
---
layout: blog
title: "Building a Support Agent in Go"
permalink: /blog/28
description: "A real thing you can build with Go Micro: a support desk where a customer ticket triggers an agent that looks up the customer, sets priority, and replies — with a human-in-the-loop gate on the one action that touches a customer."
---
# Building a Support Agent in Go
*June 19, 2026 &bull; Asim Aslam*
Most agent demos are a chat box wired to one tool. Real systems aren't that — they're a handful of services, an agent that operates them, something that triggers the agent without a human typing, and a gate on the actions you don't want it taking on its own. Here's that, built end to end. The full code is in [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support); it runs with no API key.
The scenario: a customer files a ticket. That should trigger an agent to look the customer up, set a priority, and reply — but it must not email anyone without passing an approval gate.
## 1. Services are the tools
Start with plain services. The agent will discover their endpoints as tools automatically — you don't describe them twice.
```go
type CustomerService struct{}
// Lookup returns the customer with the given email.
// @example {"email": "alice@acme.com"}
func (s *CustomerService) Lookup(ctx context.Context, req *LookupRequest, rsp *Customer) error {
// ...
}
```
A `tickets` service (with `Update`) and a `notify` service (with `Send`, the action we'll gate) round it out. Three ordinary Go Micro services — nothing AI-specific about them.
## 2. The agent
An agent is a service with a model inside. Give it the services it manages and it turns their endpoints into tools:
```go
support := micro.NewAgent("support",
micro.AgentServices("customers", "tickets", "notify"),
micro.AgentPrompt("You are a support agent. For each ticket, look up the "+
"customer, set a priority, and reply. Escalate billing issues."),
)
```
That's the whole agent. It discovers `customers.Lookup`, `tickets.Update`, and `notify.Send`, and the model decides which to call.
## 3. The event is the prompt
A support agent that waits for someone to type into a chat box is useless. The trigger is a *ticket*, so a flow turns that event into the agent's work:
```go
intake := micro.NewFlow("intake",
micro.FlowTrigger("events.ticket.created"),
micro.FlowAgent("support"),
micro.FlowPrompt("A new support ticket arrived: {{.Data}}. Handle it."),
)
```
Now a `ticket.created` event on the broker is enough to set the agent going — no human in the loop to start it. ([When the event is the prompt](/blog/21) is the idea in full.)
## 4. The guardrail is the point
The agent can read and triage all it likes. The action you actually care about is the one that reaches a customer — sending the email. That goes through a gate:
```go
micro.AgentApproveTool(func(tool string, input map[string]any) (bool, string) {
if strings.Contains(tool, "Send") {
// return false to hold it for a human or a policy
log.Printf("approval gate: emailing %v", input["to"])
}
return true, ""
})
```
Return `false` and the send is refused with a reason the model sees — that's your human-in-the-loop, your spend cap, your billing sign-off. The agent never gets to email a customer on its own unless you let it. ([Agent guardrails](/blog/23) covers the rest.)
## Run it
```
> event: events.ticket.created {"id":"ticket-1","customer":"alice@acme.com",...}
[customers] looked up Alice (pro plan)
[tickets] ticket-1 → priority=high status=in_progress
▣ approval gate notify_NotifyService_Send(alice@acme.com) — approved
[notify] 📨 to=alice@acme.com: "Hi Alice — thanks for reaching out..."
✓ ticket triaged and the customer was replied to — triggered by an event
```
With the mock model it follows a fixed triage so it runs anywhere. Point it at a real model — `go run main.go -provider anthropic` — and the agent reasons about the ticket itself, choosing what to look up and how to reply.
## What you actually built
Count what's AI-specific: the prompt, and one model option. Everything else is services, a flow, the broker, and a guardrail — the things Go Micro has always done. The agent didn't need a framework of its own; it's a service that calls other services, triggered by an event, with a gate on the dangerous action.
That's the shape of a real agent system, and it's the same shape as a real service system. From here you'd make the gate enforce a real policy, add a knowledge-base service for the agent to search, or expose the agent over [A2A](/blog/26) so another team's agent can file tickets. The code is in [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support) — clone it and change one thing.
+39
View File
@@ -0,0 +1,39 @@
---
layout: blog
title: "Go Micro Joins OpenAI's Codex for Open Source"
permalink: /blog/29
description: "OpenAI is backing Go Micro through Codex for Open Source. What the grant is, and what it means for a framework built around agentic development."
---
# Go Micro Joins OpenAI's Codex for Open Source
*June 23, 2026 • By the Go Micro Team*
Go Micro has been accepted into **OpenAI's Codex for Open Source** program. OpenAI is covering six months of ChatGPT Pro — and with it, access to Codex — to support the day-to-day work of maintaining the project.
## What the grant is
Codex for Open Source backs maintainers directly: the constant, unglamorous work of reviewing changes, cutting releases, triaging issues, and keeping quality high. That's most of what maintaining a framework actually is, and it's the part that rarely gets funded. OpenAI putting Codex against it is a real, practical help.
## Why it fits Go Micro
There's a neat symmetry here. Go Micro is a framework for **agentic development** — agents that use tools, services that are automatically AI-callable, workflows that [loop until the job is done](/docs/guides/agent-loops.html). Building and maintaining it with an agentic coding tool is that same idea pointed back at itself: tools calling tools.
It also lines up with what's already in the box. `openai` has been a first-class model provider in Go Micro since the AI-native rewrite — one of [several](/docs/guides/ai-provider-guide.html) behind the same `ai.Model` interface. So you can build *on* OpenAI models with Go Micro, and now we build Go Micro itself with OpenAI's tooling.
## What we'll use it for
Honestly: throughput. More reviewed PRs, faster releases, quicker issue triage, more examples and docs. The recent run of work — [agent loops](/docs/guides/agent-loops.html), [A2A](/blog/26), durable flows, a blocking lint gate in CI — is the pace we want to keep, and maintainer tooling is what sustains it.
## Thanks
Thanks to OpenAI for backing open source maintainers, and for supporting Go Micro specifically. It joins [Anthropic](/blog/3) and [Atlas Cloud](/blog/8) — the companies helping keep this project moving.
---
*Go Micro is an open source framework for building agents, services, and workflows in Go. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/28">&larr; Building a Support Agent in Go</a></div>
<div><a href="/blog/">All Posts</a></div>
</div>
+127
View File
@@ -0,0 +1,127 @@
---
layout: blog
title: "Building the AI-Native Future of Go Micro with Claude"
permalink: /blog/3
description: "How Anthropic's Claude Max sponsorship accelerated Go Micro's MCP integration — from WebSocket transport to a full AI-native framework."
---
# Building the AI-Native Future of Go Micro with Claude
<img src="/images/generated/blog-claude.jpg" alt="Claude AI powering Go Micro" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
*March 4, 2026 • By the Go Micro Team*
Go Micro was given access to **Claude Max** through Anthropic's open source sponsorship program. This post covers what we built with it, how the development process worked, and the vision that came out of it.
## The Sponsorship
Anthropic offers Claude Max to open source projects building on the Model Context Protocol. Go Micro's pitch was simple: every microservice should be an AI-callable tool with zero extra code. They agreed.
What happened next was the most productive sprint in Go Micro's history. Claude didn't just assist — it became a collaborator. Features that would have taken weeks shipped in days.
## What We Shipped
### WebSocket Transport
The MCP gateway needed persistent, bidirectional connections for real-time agents. We added a full WebSocket transport implementing JSON-RPC 2.0:
```javascript
const ws = new WebSocket("ws://localhost:3000/mcp/ws", {
headers: { "Authorization": "Bearer my-token" }
});
// Discover and call tools over a single connection
ws.send(JSON.stringify({
jsonrpc: "2.0", id: 1,
method: "tools/call",
params: { name: "users.Users.Get", arguments: { id: "user-123" } }
}));
```
Persistent connections, connection-level auth, concurrent requests. The agent playground in `micro run` uses this for interactive conversations with your services.
### OpenTelemetry Tracing
Every MCP tool call now creates an OpenTelemetry span:
```
Span: mcp.tool.call
mcp.tool.name: users.Users.Get
mcp.transport: websocket
mcp.auth.status: allowed
```
Drop in your trace provider and agent activity flows into Jaeger, Grafana, or Datadog alongside your existing service traces. No trace provider configured? Zero overhead.
### LlamaIndex SDK
Following the LangChain integration, we built a LlamaIndex SDK for RAG workflows:
```python
from go_micro_llamaindex import GoMicroToolkit
from llama_index.core.agent import ReActAgent
toolkit = GoMicroToolkit.from_gateway("http://localhost:3000")
agent = ReActAgent.from_tools(toolkit.get_tools(), llm=llm)
# Agent can search docs AND call services
response = agent.chat("Get the profile for user-123")
```
An agent that searches your documentation and calls your services in the same conversation.
## What Came After
The Claude sponsorship set a direction that kept going. Since then:
**7 AI model providers** — Anthropic, OpenAI, Google Gemini, Atlas Cloud, Groq, Mistral, and Together AI. All implementing the same `ai.Model` interface, all swappable with one import.
**Image and video generation**`ai.ImageModel` and `ai.VideoModel` interfaces with Atlas Cloud as the first multi-modal provider. The images on this website were generated through the framework's own `ai` package.
**`micro chat`** — an interactive CLI that discovers your services, exposes them as tools, and lets you orchestrate them through natural language. Multi-turn conversation with history.
**`ai.Tools`** — a reusable package that turns registry discovery + client RPC into an `ai.ToolHandler`. Any service can reason about and call other services through an LLM.
**Service templates**`micro new --template crud` scaffolds a full CRUD service with typed proto, in-memory store, pagination, and MCP-ready doc comments.
None of this was planned when the sponsorship started. It emerged from the velocity that Claude enabled.
## The Development Process
A note on what it's actually like to build a framework with Claude Code:
The WebSocket transport went from zero to 14 passing tests in a single session. The OpenTelemetry integration was designed, implemented, and tested in another. The Gemini provider — which has a completely different API format from OpenAI — was researched, implemented, and passing tests in under an hour.
This isn't about replacing engineering judgment. Every design decision, every interface, every architectural tradeoff was a conversation. Claude writes the code. The human decides what to build and why.
The irony isn't lost on us: Go Micro is a framework for building services that AI agents can call, and it was itself built by an AI agent calling tools in the codebase. MCP works because we used MCP.
## Try It
```bash
go install go-micro.dev/v5/cmd/micro@latest
# Create a service
micro new myservice
cd myservice
# Run with the agent playground
micro run
# Chat with your services
ANTHROPIC_API_KEY=sk-ant-... micro chat --provider anthropic
```
See the [MCP documentation](/docs/mcp) for the full guide.
---
*Go Micro is an open source framework for distributed systems development. [Star us on GitHub](https://github.com/micro/go-micro) — 23K+ stars and growing.*
*Thanks to Anthropic for the Claude Max sponsorship through their open source program.*
<div class="post-nav">
<div><a href="/blog/2">&larr; Making Microservices AI-Native with MCP</a></div>
<div><a href="/blog/">All Posts</a></div>
<div><a href="/blog/4">Agents Meet Microservices &rarr;</a></div>
</div>
+53
View File
@@ -0,0 +1,53 @@
---
layout: blog
title: "Go Micro is an Agent Harness"
permalink: /blog/30
description: "The first wave of agent frameworks put a model in a loop. The harder problem is operating that loop — and that's what Go Micro is: the harness around the agent."
---
# Go Micro is an Agent Harness
*June 24, 2026 • By the Go Micro Team*
The first wave of agent frameworks solved one problem: put a model in a loop with some tools. That's the easy part now. The harder problem — the one that decides whether an agent makes it past a demo — is **operating** that loop.
Operating a loop means connecting it to real tools, scoping what it can touch, keeping state when the process restarts, routing work to specialists, recovering from provider failures, seeing what it did, and letting other agents call it. That's harness work, and it's most of the actual job.
## The harness is the stack you already deploy
Go Micro's answer is that the harness isn't a new product to bolt on — it's the distributed-systems runtime services already have. An agent is a service with a model inside. So:
- **Tools are services.** Every endpoint is an AI-callable tool from registry metadata; an RPC runs it. No catalog to hand-maintain.
- **Agents are services.** They register, discover each other, load-balance, expose `Agent.Chat`, and are reachable over A2A.
- **Workflows are code paths.** Use a durable flow when the path is known; hand off to an agent when it isn't.
- **Safety lives at execution** — `MaxSteps`, `LoopLimit`, and `ApproveTool` run on the one path every tool call takes.
- **Interop is built in** — MCP for tools, A2A for agents, x402 for paid tools.
The service layer isn't old positioning we're walking away from. It's the reason the harness is credible: an agent that does real work needs typed, discoverable, callable capabilities, and that's exactly what a service is.
## What's shipped, and what's next
We'd rather be precise than aspirational. Today the harness gives you tools-from-services, store-backed memory, guardrails, durable flows (including run-until-done loops), built-in plan/delegate, and MCP/A2A/x402 interop.
What we're building now is the part that turns "operates the loop" from a claim into a guarantee:
- **Resilience** — deadlines, timeouts, and retry/backoff propagated through the whole loop.
- **Durable agent runs** — checkpoint and resume a run after a restart, the way flows already do.
- **Observability** — every run as OpenTelemetry spans, inspectable from the CLI.
- **Streaming** — tokens end to end, through chat, the agent RPC, and A2A.
That list is the roadmap's [Now and Next](/docs/roadmap.html), tracked as open issues. The work is happening in public.
## Read more
- [The Agent Harness](/docs/guides/agent-harness.html) — what the harness is, piece by piece, with status
- [Roadmap](/docs/roadmap.html)
---
*Go Micro is an open source agent harness and service framework for Go. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/29">&larr; OpenAI Codex for Open Source</a></div>
<div><a href="/blog/">All Posts</a></div>
</div>
+75
View File
@@ -0,0 +1,75 @@
---
layout: blog
title: "How Go Micro Builds Itself"
permalink: /blog/31
description: "Go Micro is increasingly built by an autonomous loop of two AI agents — Codex implementing scoped increments, Claude Code orchestrating, the human setting direction. Here's how the loop actually works, including the parts that broke."
---
# How Go Micro Builds Itself
*June 25, 2026 • By the Go Micro Team*
Go Micro is an agent harness. The most honest test of that claim is to use agents to build it — so increasingly, we do. A scheduled loop of two AI agents now opens issues, writes increments, and merges its own pull requests against this repo, on a cadence, with a human setting direction rather than typing the code.
This isn't a stunt. If a harness is good enough to operate a loop that builds itself, that's evidence it's good enough to operate the loop that builds *your* software. So we pointed the thesis at itself and wired up the loop. This post is how it actually works — including the parts that didn't, because the failure modes are the interesting part.
## Two agents and a human
The work splits across three roles:
- **Codex** is the serial builder. It takes one scoped task at a time, implements it, runs the build, tests, and linter, and opens a pull request.
- **Claude Code** is the orchestrator: it sets up the machinery, reviews, integrates, and handles the judgment calls Codex shouldn't make alone.
- **The human** sets direction and owns taste — brand and positioning copy, breaking public API changes, architectural decisions. Those never merge autonomously.
Everything else is automated.
## The mechanism
Each cycle is deliberately boring, which is the point:
1. A scheduled workflow opens a **fresh tracking issue** and dispatches Codex on it with a single instruction: pick the highest-value improvement that advances the [North Star](/docs/guides/agent-harness.html), implement it, verify it builds and passes tests and lint, and open a PR.
2. Codex does the work on its own branch and opens the PR.
3. GitHub **native auto-merge** lands it the moment the required CI checks go green — build, tests, golangci-lint. There is no human approval step. **CI is the only gate**, and that's not an approval, it's just a refusal to ship broken code.
Every increment is small, single-concern, and reversible. Nothing clever survives that can't pass the same checks a human contributor's PR would.
## Three altitudes
One loop only produces increments; it doesn't know whether they're adding up to anything. So there are three passes at different altitudes:
- An **architect** pass reviews the whole framework against the thesis every few days — API coherence, gaps in the services → agents → workflows lifecycle, drift — and files scoped issues. It decides *what* to build.
- The **hourly increment loop** builds those issues.
- A **DevRel** pass audits the README, website, docs, and blog each day for coherence, and surfaces things worth writing about. (This post is the kind of thing it's meant to catch.)
The architect points, the loop builds, DevRel keeps the story honest. Direction flows down; code flows up.
## The parts that broke
Wiring an autonomous loop is mostly plumbing and failure modes, which is exactly why it's a good test of a harness. A few we hit:
- The agent's "open a pull request" tool turned out to be a **stub** — it recorded the PR's title and body and returned them for a downstream step, but never pushed a branch or called the API. The agent cheerfully reported "opened a PR" every time, and no PR ever appeared. The fix was to stop trusting the tool and have the agent push and open the PR itself.
- Dispatching every run from a single tracking issue made the agent derive the **same branch name** each time, so the first increment opened a PR and the rest silently collided. One fresh issue per run fixed it.
- At one point an increment helpfully rewrote the repo's own agent instructions to point *back* at the broken tool. An autonomous loop will faithfully encode its own mistakes, so the guardrails have to be explicit.
None of these are exotic. They're the ordinary reality of operating an agent loop: tools that lie, state that collides, instructions that drift. The things the harness ships — observability, durable runs, resilience, guardrails — are the things you reach for the moment you try to run a loop like this in earnest.
## What it produces
The increments are unglamorous and real. Recent ones hardened the agent run loop with **OpenTelemetry run timelines** and a `micro runs` command to inspect them, correlated those timelines with trace spans, added **retry backoff** to durable flows, and made flow steps **cancellation-safe** so a canceled run stops retrying instead of burning its budget. Each one landed as a small PR that passed CI on its own.
That's the texture of the work: not a model writing a framework in one shot, but a loop making it a little better, continuously, under a gate that keeps it honest.
## The loop is the proof
We think the future of agentic software is scheduled, looping, work-performing agents — not chat. Go Micro is built by exactly that, against its own repo. The human still sets direction and owns the calls that need taste; CI is the gate; everything is reversible. Within those bounds, the harness builds itself.
If it can do that, it can build yours.
---
*Go Micro is an open source agent harness and service framework for Go. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/30">&larr; Go Micro is an Agent Harness</a></div>
<div><a href="/blog/">All Posts</a></div>
</div>
+89
View File
@@ -0,0 +1,89 @@
---
layout: blog
title: "An Agent Is a Service: Where Agent Frameworks Are Going"
permalink: /blog/32
description: "A field guide to the agent-framework landscape — from LangChain and the first wave, through the two layers of a harness and the rise of loop engineering, to where the frameworks diverge. And why Go Micro's answer is that an agent is a service."
---
# An Agent Is a Service: Where Agent Frameworks Are Going
*June 30, 2026 • By the Go Micro Team*
There are now a lot of ways to build an agent. LangChain and LangGraph, LlamaIndex, CrewAI, Microsoft's AutoGen, Google's ADK, the model labs' own SDKs, and — most recently in our own backyard — [tRPC-Agent-Go](https://github.com/trpc-group/trpc-agent-go) from Tencent. They are not all solving the same problem, and the places they differ tell you a lot about where this is heading.
This is a field guide to that landscape, and an honest account of where Go Micro sits in it.
## The first wave: a model in a loop
The first wave of agent frameworks solved one thing: get a model to call tools in a loop until a task is done. LangChain, more than any other project, defined that category in 2022 — chains, then agents, then graphs. LlamaIndex came at it from the data and retrieval side. CrewAI and AutoGen leaned into multi-agent orchestration — crews and conversations of role-played agents. The model labs shipped their own agent SDKs so you could stay close to the metal.
That first problem — model, tools, a loop — is now largely commoditized. Every SDK does it, and they mostly do it well. Which means the interesting question has moved. It is no longer "how do I get a model to use a tool." It is everything that happens *around* the loop once the agent has to do real work: connect to real systems, hold state across restarts, recover from failure, be observed, be scheduled, and be reached by other agents. That is the part that decides whether an agent makes it out of a demo.
LangChain itself is the clearest evidence. The framework was the distribution; the value moved to *operating* agents — which is why their commercial product is LangSmith (observability, evaluation, monitoring), not the framework. The lesson the pioneer taught is that the framework gets you to a running agent, and the hard, durable, valuable problems are in operating it.
## "Agent = Model + Harness" — but a harness has two layers
LangChain has a good framing for this: an agent is a model plus a *harness* — the runtime around the model that makes it useful. The framing is right. What is usually left implicit is that "harness" has two distinct layers, and almost all the frameworks live in the first one.
**The intra-agent harness** is the runtime around a *single model*: the system prompt, the tool definitions, context management and compaction, the sandbox, self-verification, and the continuation loop that keeps the model going until it is done. LangChain and LangGraph, deepagents, Claude Code, and the model labs' SDKs are excellent at this. It is real, hard work, and it is most of what people mean when they say "agent framework."
**The operational harness** is the distributed substrate an agent *operates inside*: services exposed as typed tools, discovery and RPC, durable and resumable runs, observability, scheduling, and the protocols agents use to reach each other. This is the layer where a single agent stops being a script and becomes part of a system — where many agents, many services, and many workflows have to compose without falling over.
The first layer produces an agent. The second is where that agent has to live. Most frameworks build the first and leave the second to you — you bring your own services, your own discovery, your own durability, your own deployment. That is the gap that matters now, because the moment you have more than one agent or one service, the operational harness *is* the product.
## The loop is the new frontier
If the first wave was "a model in a loop," the direction now is what LangChain has started calling [loop engineering](https://www.langchain.com/blog/the-art-of-loop-engineering): stacking loops around the agent. It is a useful map. There is the **agent loop** (model calls tools until done), the **verification loop** (a grader checks the output against a rubric and sends failures back with feedback), the **event-driven loop** (the agent is triggered by webhooks, schedules, or messages instead of a human typing), and the **hill-climbing loop** (production traces feed back to improve the prompts, tools, and graders over time).
Notice that only the first of those four is the intra-agent harness. The other three — verification, event-driven triggers, learning from traces — are the operational harness. The frontier is moving from "answer a prompt" to **scheduled, looping, work-performing agents**: agents that run on a cadence, do real work, check their own output, and get better. That is exactly the layer that is underbuilt, and it is the layer that decides whether agents are dependable.
## Where the frameworks are going
Survey the field and a shape emerges. LangChain and LangGraph pair graph-based orchestration with LangSmith for operations, funded to build the team that operates the platform. CrewAI and AutoGen are converging on multi-agent orchestration patterns. Google's ADK is a strong code-first framework with first-class evaluation, tuned for Gemini and Google Cloud. tRPC-Agent-Go brings a production-grade Go agent SDK — LLM, Chain, Parallel, Cycle, and Graph agents; tools; MCP and A2A; memory and RAG; evaluation; agent self-evolution; OpenTelemetry — maintained by Tencent's tRPC group and validated inside Tencent.
They differ in the details, but most share two structural choices. They are an **agent SDK you run alongside your services** — the agents are a layer, and your service tier lives somewhere else and is called into. And they are **graph-centric** — you compose agents and tools into graphs and conditional workflows. That is a coherent, well-trodden approach, and for a lot of teams it is exactly right.
Go Micro starts somewhere else.
## Where Go Micro fits: an agent is a service
Go Micro's position is a single claim: **an agent is a service.** Not a layer bolted onto a service tier — the same runtime.
The reasoning is straightforward. The moment an agent has to discover services, call them, hold state, and recover from failure, it *is* a distributed system. That is precisely the problem a service framework already solves. So instead of building an agent SDK that sits next to your services, Go Micro makes agents and services the same primitives:
- **Every service endpoint is automatically an AI-callable tool**, derived from registry metadata. You do not wire tools into a graph; you write a service and it is already a tool, reachable over MCP.
- **An agent is a service.** It registers, is discovered, load-balances, exposes an `Agent.Chat` RPC, keeps store-backed memory, and is reachable over A2A — the same lifecycle as anything else you run.
- **Workflows are durable code paths, not a graph DSL.** Use a `flow` of checkpointed steps where the path is known; dispatch to an agent where it is not. The deterministic parts are plain, resumable Go; the dynamic parts are agents.
The premise is that the line between "your services" and "your agents" is accidental complexity. Remove it, and there is less to wire, less to keep in sync, and a much shorter path from a service to an agent that uses it. The operational harness — discovery, RPC, pub/sub, durable runs, observability, deployment — is not something you assemble around the framework. It *is* the framework.
This is also why Go Micro is deliberately not a graph DSL. Graphs are expressive, and for some teams that visual, declarative model is the draw. But a graph is one more thing to learn and maintain next to your services. "It is just services and durable flows" is a smaller surface to hold in your head, and it composes with everything a service already does.
## A concrete contrast: tRPC-Agent-Go
Because it is the closest neighbour — a serious, production Go framework — tRPC-Agent-Go makes the fork concrete. It is an agent SDK that runs alongside your tRPC services, organised around graph, chain, parallel, and cycle agents. Go Micro is one runtime where the agent *is* the service and orchestration is durable flows.
We will be honest about where they are ahead: tRPC-Agent-Go ships a first-class evaluation framework, agent self-evolution, AG-UI streaming, and RAG today. Go Micro has the trace foundation (OpenTelemetry run timelines, `micro runs`) and has the verification/grader loop and richer memory on the roadmap — but if you need those right now, they are further along there, with a large team behind them. Pretending the checklists match would help no one.
What Go Micro offers in return is the thing an SDK-alongside-your-services cannot: services that become tools with zero glue, agents that are first-class services, and one set of primitives — service, agent, flow — instead of a service stack plus an agent layer plus a graph runtime.
## The direction we're building
If scheduled, looping, work-performing agents are where this goes, then the operational harness is the thing to get right, and loops are the organising idea. Go Micro already has the agent loop, durable event-driven flows, and the trace foundation for learning. The verification loop — grade a step's output against a rubric and route failures back with feedback — is the next primitive, building on the supervised loop and retry machinery already there. Durable agent runs, streaming end to end, and richer observability are on the same line. The aim is not to win a feature checklist; it is to be the runtime where an operating agent is dependable.
There is one more piece of evidence we find hard to argue with: Go Micro is increasingly built by its own loop — an autonomous improvement loop running in CI, opening and merging its own changes against a thesis. An agent harness, operated by agents, building itself. If it is good enough to do that, it is good enough to operate yours.
## Open protocols, different homes
None of this is winner-take-all, and it should not be. Every serious framework here speaks **MCP** for tools and **A2A** for agents. A Go Micro agent and a tRPC-Agent-Go agent can call each other; either can consume the other's tools; an ADK or LangGraph agent can plug into a Go Micro runtime over A2A, and the reverse. The protocols are the commons.
So the real question is not which framework wins. It is where your agents should *live*. The answer that Go Micro is built around is that when an agent has to operate inside a real system, it is a distributed system — and the simplest place to build it is the runtime where your services already live.
---
*Go Micro is an open source agent harness and service framework for Go. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/31">&larr; How Go Micro Builds Itself</a></div>
<div><a href="/blog/">All Posts</a></div>
</div>
+108
View File
@@ -0,0 +1,108 @@
---
layout: blog
title: "The Loop, Shipped: Introducing micro loop"
permalink: /blog/33
description: "The autonomous loop that builds Go Micro is now a command. micro loop init drops a planner, builder, triage, coherence, and release pipeline into any repository — GitHub Actions as the runtime, editable prompt files as the policy. And Go Micro now runs on it."
---
# The Loop, Shipped: Introducing `micro loop`
*July 2, 2026 • By the Go Micro Team*
A few weeks ago we wrote [How Go Micro Builds Itself](/blog/31): a loop of two AI agents — Codex writing scoped increments, Claude Code orchestrating and reviewing, a human setting direction — kept honest by CI. It was our own thesis, *an agent operating a system*, pointed at the repository that defines the thesis.
People asked the obvious question: **can I run that on my repo?**
Now you can. `micro loop init` drops the whole pipeline into any repository, and — the part that makes it real rather than aspirational — Go Micro itself now runs on it. The workflows that maintain this project are generated by the same command we're shipping to you.
## It's not a binary. It's CI plus prompts.
The first thing to understand is what the loop *is*, physically, because it's not what people expect. There is almost no program here. The loop is a handful of GitHub Actions workflows and a few Markdown files. **GitHub Actions is the runtime.** Nothing compiled sits in a server "running the loop."
So `micro loop` the command is not the loop. It's a scaffolder. `micro loop init` writes the workflows and the prompts into your repo and exits. From then on, GitHub runs everything on a schedule.
That reframing is the whole design:
- **The workflows are the *mechanism*** — open a fresh tracking issue, summon an agent, let it open a PR, merge on green CI. This is generic. It's the same for every repo.
- **The prompt files are the *policy*** — what the agent is actually told to do. These live in `.github/loop/prompts/<role>.md`, and they're yours to edit.
Separating the two is what lets a tool be both general and specific. The mechanics never change; the instructions are entirely under your control. You steer the loop by editing prose, not YAML.
## The five roles
A full loop has five roles, each a workflow that dispatches an agent (or, for release, just cuts a tag):
| Role | What it does |
|------|--------------|
| **planner** | Reads your direction, scans what merged and what's open, and maintains a ranked queue in `.github/loop/PRIORITIES.md`. Decides *what*. |
| **builder** | Takes the top open item from the queue, implements it as a single-concern PR, and enables auto-merge so it lands the moment CI is green. Does the work. |
| **triage** | When CI fails on a branch, root-causes the failure and files scoped fix issues back into the queue. The feedback path. |
| **coherence** | Keeps README, docs, and `CHANGELOG.md` aligned with your North Star; drafts release notes. Optional. |
| **release** | Cuts the next patch tag when the branch has new commits, so the installable artifact tracks the loop. Optional. |
This is the [planner / generator / evaluator](https://www.anthropic.com/engineering/harness-design-long-running-apps) shape of a long-running agent harness, distributed across GitHub Actions instead of subagents. Generation is deliberately separated from evaluation: an agent grading its own work reliably over-rates it, so **CI — not the builder — is the gate.**
## Quickstart
```bash
micro loop init # planner, builder, triage
micro loop init --roles all # add coherence + release
micro loop verify # check the wiring
```
`init` writes the workflows, a `NORTH_STAR.md` (your direction) and `PRIORITIES.md` (your queue), and the per-role prompt files. Then there are exactly two things the CLI can't do for you — and `micro loop verify` reminds you of both:
1. **Add a token secret.** The agent ignores `@mentions` from the `github-actions` bot, so dispatch has to post as a real user via a personal access token stored as a repo secret. Until it's set, the workflows deliberately no-op.
2. **Set branch protection.** Require your CI checks with **zero** approving reviews, so the builder's auto-merge lands PRs the instant CI is green. That green-CI gate is the loop's only safety mechanism — which means it's worth saying plainly: **the loop is only as good as your tests.**
Edit `.github/loop/NORTH_STAR.md` to point it, tune the prompts if you like, commit, and it's running.
## The decisions that matter (and the things that broke)
Building this taught us that the hard part of an autonomous loop isn't the agent — it's the wiring around it. A few decisions are load-bearing, and we learned most of them by getting them wrong first.
**Agent-agnostic by `@mention`.** The dispatch summons an agent by mentioning it on an issue (`@codex` by default; `--agent` to change it). Any coding agent that responds to an issue mention and can run `gh` fits. The mechanics don't care which one.
**A fresh issue every run.** Agents derive their PR branch name from the triggering issue. Reuse one tracker issue and every run collapses onto a single branch name — the first PR opens, the rest silently collide and never appear. A unique issue per run gives each increment a clean branch. We found this the way you'd expect: a loop that looked healthy but quietly stopped producing PRs.
**Bots can't summon bots.** Codex ignores comments authored by `github-actions[bot]`, so the dispatch has to post as a user. That's the whole reason the token secret exists, and why the loop no-ops without it rather than pile up ignored comments.
**Don't let checkout clobber your token.** Our nightly release action once failed to push a tag with a permission error — despite having a valid token — because `actions/checkout` persists the default `GITHUB_TOKEN` as a git credential that overrode the personal token on the push. The generated release workflow ships with that fix baked in (`persist-credentials: false`), so it can't bite you.
None of these are exotic. They're exactly the papercuts you'd hit assembling this yourself over a week, and they're the reason a scaffolder earns its place: the defaults encode the lessons.
## Go Micro runs on it
Here's the part we care about most. This repository's loop is no longer hand-written. We ran `micro loop init --roles all` on Go Micro itself, moved our queue and direction into `.github/loop/`, and preserved our own richer instructions — the architect's founder-lens prompt, the adoption steer, the harness-failure triage, the changelog pass — as the editable prompt files. Behavior is identical; only the mechanism is now generated.
We proved it end to end before writing this. The planner fired, opened a fresh tracking issue as the token user, and posted its full instruction to Codex — clean, correctly addressed, picked up within seconds. The loop that will help maintain the post you're reading is the loop this post is about.
That's the honesty test for a dogfooded tool: if we wouldn't run it on our own main branch, we shouldn't ask you to run it on yours.
## What it is not
It would be easy to oversell this, so let's be clear about the edges.
- **It is not a replacement for judgment.** The generated prompts keep an explicit off-limits list — breaking public APIs, positioning and brand copy, new dependencies, architectural rewrites. Those get surfaced to a human, never auto-merged. Taste stays with you.
- **It is only as good as your gate.** Point it at a repo with weak CI and it will happily merge weak work. The evaluator *is* the safety model. Invest there first.
- **It spends tokens and pushes commits.** This is real automation with real effects. Start with the three-role default, watch it, and add `coherence` and `release` once you trust it.
- **It's early.** The mechanics are solid and battle-tested on this repo, but this is version one. The next steps are more agent adapters and, eventually, a standalone home so the workflows can be reused directly.
## The shape of the thing
Go Micro's premise is that when an agent has to operate inside a real system, it *is* a distributed system — services, state, retries, observability — and the simplest place to build it is the runtime where your services already live. `micro loop` is that premise turned back on the repository: an agent, operating a system, where the system is the codebase itself.
You've read about how Go Micro builds itself. Now you can point the same loop at your own repo:
```bash
micro loop init
```
---
*Go Micro is an open source agent harness and service framework for Go. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/32">&larr; An Agent Is a Service: Where Agent Frameworks Are Going</a></div>
<div><a href="/blog/">All Posts</a></div>
</div>
+45
View File
@@ -0,0 +1,45 @@
---
layout: blog
title: "What's New in Go Micro: v6.3.15"
permalink: /blog/34
description: "Go Micro v6.3.15 tightens the first-agent on-ramp, adds Anthropic streaming, and hardens plan/delegate plus text tool-call recovery."
---
# What's New in Go Micro: v6.3.15
*July 5, 2026 • By the Go Micro Team*
Go Micro v6.3.15 is a small but useful harness release: less friction for the first agent, better streaming provider coverage, and more reliable execution when models and delegates do not behave perfectly.
## Anthropic now streams
The Anthropic provider now supports Messages SSE streaming and is registered as a streaming-capable provider. That means Go Micro agents can use Anthropic in the same streaming path as the other streaming-capable providers, with request/response parser coverage and provider capability docs kept in sync.
## The first-agent path is easier to start
The on-ramp now has a smallest runnable first-agent example: a mock-model, no-secret agent you can run before adding provider keys. The CLI and docs also point new users toward the maintained first-agent path after scaffold/run milestones, so the next step after a service is clearer: run the example, build the agent, debug it, then walk the 0→hero services → agents → workflows path.
## Plan/delegate is more deterministic
Plan/delegate runs got another reliability pass. Completed plan steps are preserved, ordering is guarded, notify-before-completion is required in the flow path, and checkpoint continuation is more stable. These are the kinds of harness fixes that matter when an agent does real work over multiple tool calls instead of just answering a prompt.
## Tool-call recovery keeps improving
Provider text tool-call fallback paths now recover more of the awkward cases: tagged calls, `Create`-suffixed calls, mixed text/tool-call output, and AtlasCloud follow-up calls. The goal is pragmatic: when a weaker or non-standard provider emits something close to a tool call, the harness should still make progress when it can do so safely.
## A2A payment groundwork
The A2A gateway now has the shared payment-mandate foundation needed for AP2-style agent payment flows. It is groundwork, not a full product story yet, but it keeps Go Micro's agent interop and paid-tool direction moving together.
## Read the changelog
The full release notes are in the [CHANGELOG](https://github.com/micro/go-micro/blob/master/CHANGELOG.md).
---
*Go Micro is an open source agent harness and service framework for Go. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/33">&larr; The Loop, Shipped: Introducing micro loop</a></div>
<div><a href="/blog/">All Posts</a></div>
</div>
+43
View File
@@ -0,0 +1,43 @@
---
layout: blog
title: "What's New in Go Micro: v6.6.0"
permalink: /blog/35
description: "Go Micro v6.6.0 strengthens the first-agent on-ramp, hardens plan/delegate recovery, and adds CI-backed security checks."
---
# What's New in Go Micro: v6.6.0
*July 11, 2026 • By the Go Micro Team*
Go Micro v6.6.0 is a harness reliability release. The theme is the same services → agents → workflows lifecycle, but with more of the first-agent path covered by local checks and more recovery paths made deterministic when agents resume, retry, delegate, or notify.
## The first-agent path is harder to break
The README, website docs, examples, and 0→hero guide chain now have more harness coverage. The CLI and docs keep the path from install troubleshooting through `micro agent demo`, quickcheck, examples, the smallest first-agent, debugging, and 0→hero in one consistent order.
That matters because the first agent is where framework promises become real: scaffold a service, run it, chat with an agent, inspect what happened, and then graduate to workflows.
## Plan/delegate recovery is steadier
Plan/delegate got another reliability pass across side effects, notification replay, plan-only actions, timeout completion, and completed-plan recovery. These fixes are not flashy, but they are exactly the kind of harness work that keeps an agent from repeating work or losing state when a multi-step run is interrupted.
## Provider fallback keeps improving
AtlasCloud fallback handling now recovers more awkward provider outputs, including workspace repair calls, empty-argument text tool calls, spoken notification replays, and A2A fallback artifact text. The goal is pragmatic interop: when a provider response is close enough to a safe tool call, the harness should repair it; when it is not, the failure should stay visible.
## Security checks joined the loop
CI now includes a govulncheck gate and the autonomous loop can route vulnerability failures into triage. The release also includes toolchain and dependency updates for reachable CVEs, keeping the service framework side of the harness current while agent features continue to land.
## Read the changelog
The full release notes are in the [CHANGELOG](https://github.com/micro/go-micro/blob/master/CHANGELOG.md).
---
*Go Micro is an open source agent harness and service framework for Go. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/34">&larr; What's New in Go Micro: v6.3.15</a></div>
<div><a href="/blog/">All Posts</a></div>
</div>
+221
View File
@@ -0,0 +1,221 @@
---
layout: blog
title: "Agents Meet Microservices: A Hands-On Demo"
permalink: /blog/4
description: "Build three microservices and let an AI agent manage them with natural language — no glue code, no API wrappers, just Go comments"
---
# Agents Meet Microservices: A Hands-On Demo
<img src="/images/generated/blog-agents-demo.jpg" alt="Agents Meet Microservices: A Hands-On Demo" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
*March 4, 2026 • By the Go Micro Team*
We talk a lot about AI-native microservices. Time to show it. In this post we'll build three services — projects, tasks, and team — and then hand them to an AI agent. The agent will create projects, assign tasks, and query team skills using nothing but natural language.
No API wrappers. No tool definitions. Just Go comments.
## The Setup
The full code is at [`examples/agent-demo`](https://github.com/micro/go-micro/tree/master/examples/agent-demo). Here's the architecture:
```
User (natural language)
AI Agent (Claude, GPT, etc.)
MCP Gateway (:3000)
├── ProjectService.Create / Get / List
├── TaskService.Create / List / Update
└── TeamService.Add / List / Get
```
The MCP gateway discovers all three services automatically and exposes 9 tools. The agent sees them and knows how to call them — because we wrote good comments.
## Step 1: Define Your Types
Every field gets a `description` tag. This is what the agent reads:
```go
type Task struct {
ID string `json:"id" description:"Unique task identifier"`
ProjectID string `json:"project_id" description:"ID of the project this task belongs to"`
Title string `json:"title" description:"Short task title"`
Status string `json:"status" description:"Task status: todo, in_progress, or done"`
Assignee string `json:"assignee,omitempty" description:"Username of the person assigned"`
Priority string `json:"priority" description:"Priority: low, medium, or high"`
}
```
Notice we list valid enum values (`todo, in_progress, done`) and mark optional fields with `omitempty`. This is how the agent knows what it can send.
## Step 2: Write Handler Comments
Each handler method gets a doc comment explaining what it does, plus an `@example` with realistic input:
```go
// Create creates a new task in a project.
// Returns the task with a generated ID, initial status of "todo",
// and default priority of "medium".
//
// @example {"project_id": "proj-1", "title": "Design homepage mockup", "assignee": "alice", "priority": "high"}
func (s *TaskService) Create(ctx context.Context, req *CreateTaskRequest, rsp *CreateTaskResponse) error {
// ...
}
```
The MCP gateway extracts this at registration time via `go/ast` and turns it into a JSON Schema tool definition. The agent sees:
```json
{
"name": "demo.TaskService.Create",
"description": "Create creates a new task in a project. Returns the task with a generated ID, initial status of \"todo\", and default priority of \"medium\".",
"inputSchema": {
"type": "object",
"properties": {
"project_id": {"type": "string", "description": "Project ID to add the task to (required)"},
"title": {"type": "string", "description": "Task title (required)"},
"assignee": {"type": "string", "description": "Username to assign (optional)"},
"priority": {"type": "string", "description": "Priority: low, medium, or high (default: medium)"}
}
}
}
```
That's everything an agent needs to call this tool correctly.
## Step 3: Wire It Up
One file, one `main()`. Three handlers registered with auth scopes, and MCP enabled with a single option:
```go
func main() {
service := micro.NewService(
micro.Name("demo"),
micro.Address(":9090"),
mcp.WithMCP(":3000"), // ← MCP gateway on port 3000
)
service.Init()
srv := service.Server()
srv.Handle(srv.NewHandler(
&ProjectService{projects: make(map[string]*Project)},
server.WithEndpointScopes("ProjectService.Create", "projects:write"),
server.WithEndpointScopes("ProjectService.Get", "projects:read"),
server.WithEndpointScopes("ProjectService.List", "projects:read"),
))
srv.Handle(srv.NewHandler(
&TaskService{tasks: make(map[string]*Task)},
server.WithEndpointScopes("TaskService.Create", "tasks:write"),
server.WithEndpointScopes("TaskService.List", "tasks:read"),
server.WithEndpointScopes("TaskService.Update", "tasks:write"),
))
srv.Handle(srv.NewHandler(
&TeamService{members: make(map[string]*Member)},
server.WithEndpointScopes("TeamService.Add", "team:write"),
server.WithEndpointScopes("TeamService.List", "team:read"),
server.WithEndpointScopes("TeamService.Get", "team:read"),
))
service.Run()
}
```
`mcp.WithMCP(":3000")` starts the MCP gateway after the service boots. All registered handlers become tools automatically. That's it.
## What the Agent Can Do
The demo seeds realistic data — 2 projects, 7 tasks, 4 team members. Here's what happens when you talk to the agent:
### Multi-service queries
> **You:** "Give me a status update on the Website Redesign project"
The agent calls `ProjectService.Get` to fetch the project, then `TaskService.List` filtered by project ID, and synthesizes:
> **Agent:** The Website Redesign (proj-1) is active. 4 tasks total: 1 in progress (Design new homepage layout, assigned to bob), 3 todo. High-priority items: homepage layout and responsive nav component. The "Write copy for about page" task is unassigned.
That's three tool calls, zero glue code.
### Cross-service reasoning
> **You:** "Who on the team knows React and has available capacity?"
The agent calls `TeamService.List` with `skill=react`, gets bob and charlie, then calls `TaskService.List` for each to check their workload:
> **Agent:** Two team members know React: Bob Park (designer, 1 task in progress) and Charlie Kim (engineer, 1 task todo). Charlie has lighter load — recommend assigning React work to him.
### Write operations
> **You:** "Create a high-priority task for alice to set up monitoring on the API v2 project"
The agent calls `TaskService.Create`:
```json
{
"project_id": "proj-2",
"title": "Set up monitoring",
"assignee": "alice",
"priority": "high"
}
```
It knows `proj-2` is the API v2 project because it queried `ProjectService.List` earlier in the conversation.
## The Key Insight
None of this required:
- Writing tool definitions or OpenAPI specs
- Building an API wrapper or integration layer
- Configuring the agent with service details
- Any code beyond normal Go handlers with comments
The MCP gateway does the translation. Your comments become the agent's documentation. Your struct tags become the parameter schema. Your services become tools.
**Write a good Go service. Get AI integration for free.**
## Try It
```bash
# Clone and run
git clone https://github.com/micro/go-micro
cd go-micro/examples/agent-demo
go run main.go
```
Then connect with Claude Code:
```json
{
"mcpServers": {
"demo": {
"command": "go",
"args": ["run", "."],
"cwd": "/path/to/go-micro/examples/agent-demo"
}
}
}
```
Or use the WebSocket endpoint at `ws://localhost:3000/mcp/ws` from any MCP-compatible client.
## What's Next
This demo is a starting point. In production you'd run each service as a separate process, use Consul or etcd for discovery, add JWT authentication, and deploy the standalone `micro-mcp-gateway` binary in front of everything.
The guides cover all of this:
- [Building AI-Native Services](/docs/guides/ai-native-services) — End-to-end tutorial
- [MCP Security](/docs/guides/mcp-security) — Auth, scopes, rate limiting
- [Agent Patterns](/docs/guides/agent-patterns) — Architecture patterns for production
---
*Go Micro is an open source framework for distributed systems development. [Star us on GitHub](https://github.com/micro/go-micro) — 21K stars and growing.*
<div class="post-nav">
<div><a href="/blog/3">← Building the AI-Native Future</a></div>
<div><a href="/blog/">All Posts</a></div>
</div>
+114
View File
@@ -0,0 +1,114 @@
---
layout: blog
title: "Developer Experience Cleanup: One Way to Do Things"
permalink: /blog/5
description: "Unified service creation, cleaner handler registration, and modular monolith support — the Go Micro DX overhaul"
---
# Developer Experience Cleanup: One Way to Do Things
<img src="/images/generated/blog-dx.jpg" alt="Developer Experience Cleanup: One Way to Do Things" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
*March 4, 2026 — By the Go Micro Team*
Go Micro has always prioritized getting out of your way. But over time, the API accumulated multiple ways to do the same thing — `micro.New(name)`, `micro.NewService(micro.Name(...))`, `service.New()`, three different handler registration patterns. If you're building something for AI agents or running a modular monolith, you shouldn't have to choose between equivalent APIs.
We've cleaned it up. Here's what changed and why.
## One Way to Create a Service
Before, there were three ways to create a service:
```go
// Old: three equivalent patterns
service := micro.NewService("greeter") // name only
service := micro.NewService(micro.Name("greeter")) // options only
service := service.New(service.Name("greeter")) // internal package
```
Now there's one canonical pattern:
```go
service := micro.NewService("greeter")
service := micro.NewService("greeter", micro.Address(":8080"))
```
Name is always the first argument. Options follow. `micro.New` still works as a deprecated alias, but every example, doc, and guide now uses `micro.NewService("name")`.
## Clean Handler Registration
Registering handlers used to require reaching through to the server:
```go
// Old: verbose, leaks abstraction
handler := service.Server().NewHandler(
&TaskService{tasks: make(map[string]*Task)},
server.WithEndpointScopes("TaskService.Create", "tasks:write"),
)
service.Server().Handle(handler)
```
Now `service.Handle()` accepts handler options directly:
```go
// New: clean, one call
service.Handle(
&TaskService{tasks: make(map[string]*Task)},
server.WithEndpointScopes("TaskService.Create", "tasks:write"),
)
```
For the common case with no options, it's just:
```go
service.Handle(new(Greeter))
```
## Modular Monoliths with Service Groups
Run multiple services in a single binary. Each service gets isolated state (server, client, store, cache) while sharing infrastructure (registry, broker, transport):
```go
users := micro.NewService("users", micro.Address(":9001"))
orders := micro.NewService("orders", micro.Address(":9002"))
users.Handle(new(Users))
orders.Handle(new(Orders))
g := micro.NewGroup(users, orders)
g.Run()
```
Start as a monolith, split into separate binaries when you need independent scaling. The Group handles signals and coordinated shutdown — all services start together and stop together.
## MCP Integration in One Line
Every service is automatically an MCP tool. Add a gateway alongside your service with one option:
```go
service := micro.NewService("greeter",
micro.Address(":9090"),
mcp.WithMCP(":3000"),
)
service.Handle(new(Greeter))
service.Run()
```
Your Go comments become tool descriptions. Your struct tags become parameter schemas. No glue code.
## Bug Fixes
- **Stop() error handling**: Previously, `Stop()` would silently swallow errors from `BeforeStop` hooks. Now all errors are properly propagated.
- **Store initialization**: Fatal-level log on store init failure changed to error-level — a store init failure shouldn't crash your service.
- **Service interface**: The internal implementation is now properly unexported. Users interact through the `service.Service` interface, not a concrete type.
## What This Means for You
If you're building new services, use `micro.NewService("name", opts...)` and `service.Handle()`. That's it.
If you have existing code using `micro.New("name")` or `service.Server().Handle()`, it still works. For v6, update name-less `micro.NewService(opts...)` calls to `micro.NewService("name", opts...)`. The docs, examples, and guides all point to the new patterns now.
The goal is simple: when someone asks "how do I create a service?", there should be exactly one answer.
See the updated [Getting Started guide](https://go-micro.dev/docs/getting-started.html) and the [agent demo](https://github.com/micro/go-micro/tree/master/examples/agent-demo) for working examples.
+168
View File
@@ -0,0 +1,168 @@
---
layout: blog
title: "The Model Package: Client, Server, and Now Data"
permalink: /blog/6
description: "Go Micro now has a typed data model layer — define structs, get CRUD and queries, swap backends. Every service gets Client, Server, and Model."
---
# The Model Package: Client, Server, and Now Data
<img src="/images/generated/data-model.jpg" alt="The Model Package: Client, Server, and Now Data" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
*March 4, 2026 — By the Go Micro Team*
Go Micro has always given you `service.Client()` to call other services and `service.Server()` to handle requests. But most services also need to save and query data. Until now, that meant either using the low-level `store` package (key-value only) or wiring up your own database layer.
Today we're shipping the `model` package — a typed data model layer that completes the service trifecta: **Client, Server, Model**.
## The Problem
The existing `store` package is great for simple key-value storage, but real services need more. You need to filter by fields, paginate results, count records, and use different databases in dev vs production. Most teams end up writing their own data layer or pulling in an ORM that has nothing to do with Go Micro.
We wanted something that feels native to the framework. Define a Go struct, tag a key, and get type-safe CRUD and queries — with the same pluggable backend pattern Go Micro uses everywhere.
## Define a Struct, Get a Database
```go
type User struct {
ID string `json:"id" model:"key"`
Name string `json:"name"`
Email string `json:"email" model:"index"`
Age int `json:"age"`
}
```
The `model:"key"` tag marks your primary key. The `model:"index"` tag creates an index for faster queries. Column names come from `json` tags (or lowercased field names if no tag).
Register your type and use it:
```go
db := service.Model()
db.Register(&User{})
// Create
db.Create(ctx, &User{ID: "1", Name: "Alice", Email: "alice@example.com", Age: 30})
// Read
user := &User{}
db.Read(ctx, "1", user)
// Update
user.Name = "Alice Smith"
db.Update(ctx, user)
// Delete
db.Delete(ctx, "1", &User{})
```
No migrations. No connection setup. No configuration files. The schema is derived from your struct at startup.
## Queries That Feel Like Go
List and count with composable query options:
```go
var active []*User
// Simple equality filter
db.List(ctx, &active, model.Where("email", "alice@example.com"))
// Operators, ordering, pagination
var page []*User
db.List(ctx, &page,
model.WhereOp("age", ">=", 18),
model.OrderDesc("name"),
model.Limit(10),
model.Offset(20),
)
// Count records
total, _ := db.Count(ctx, &User{}, model.Where("age", 30))
```
Filters support `=`, `!=`, `<`, `>`, `<=`, `>=`, and `LIKE`. Everything composes — add as many query options as you need.
## Three Backends, One Interface
The model layer follows Go Micro's pluggable pattern. Same code, different backends:
**Memory** — the default. Zero config, great for development and testing:
```go
service := micro.NewService("users")
db := service.Model() // in-memory by default
db.Register(&User{})
```
**SQLite** — single-file database for local development or single-node production:
```go
db, _ := sqlite.New(model.WithDSN("file:app.db"))
service := micro.NewService("users", micro.Model(db))
```
**Postgres** — production-grade with connection pooling:
```go
db, _ := postgres.New(model.WithDSN("postgres://localhost/myapp"))
service := micro.NewService("users", micro.Model(db))
```
Start with memory in dev, switch to SQLite or Postgres for production. Your application code doesn't change.
## The Complete Service Interface
The Service interface now has three core accessors:
```go
type Service interface {
Client() client.Client // Call other services
Server() server.Server // Handle incoming requests
Model() model.Model // Save and query data
// ...
}
```
This means a typical service has everything it needs in one place:
```go
func main() {
service := micro.NewService("users", micro.Address(":9001"))
// Data layer
db := service.Model()
db.Register(&User{})
// Handler with data access
service.Handle(&UserService{db: db})
// Run
service.Run()
}
```
Call services with `service.Client()`. Handle requests with `service.Server()`. Save data with `service.Model()`. That's the complete picture.
## Multiple Models, One Database
You can create multiple typed models from the same database connection:
```go
db := service.Model()
db.Register(&User{})
db.Register(&Post{})
db.Register(&Comment{})
```
Each type gets its own table (derived from the struct name). They share the database connection.
## What's Next
The model package is production-ready with memory, SQLite, and Postgres backends. Coming soon:
- **Relationships** — define foreign keys between models
- **Migrations** — track and apply schema changes
- **Protobuf codegen** — `protoc-gen-micro` generates model code from proto definitions
See the [model documentation](https://go-micro.dev/docs/model.html) for the full API reference, or browse the [model package source](https://github.com/micro/go-micro/tree/master/model) to see the implementation.
+227
View File
@@ -0,0 +1,227 @@
---
layout: blog
title: "Your Microservices Are Already an AI Platform"
permalink: /blog/7
description: "How existing Go Micro services become agent-accessible with zero code changes. A walkthrough using the micro/blog platform as a real-world example."
---
# Your Microservices Are Already an AI Platform
<img src="/images/generated/blog-mcp.jpg" alt="Your Microservices Are Already an AI Platform" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
*March 5, 2026 — By the Go Micro Team*
Here's the pitch: you have microservices. They already have well-defined endpoints, typed request/response schemas, and service discovery. An AI agent needs the same things — a list of tools with input schemas and descriptions. The gap between "microservice endpoint" and "AI tool" is surprisingly small.
With Go Micro + MCP, that gap is **zero lines of code**.
## The Setup: A Blogging Platform
We'll use a blogging platform as our example — inspired by [micro/blog](https://github.com/micro/blog), a real microblogging platform built on Go Micro with four domains:
- **Users** — signup, login, profiles
- **Posts** — blog posts with markdown, tags, link previews
- **Comments** — threaded comments on posts
- **Mail** — internal messaging
### A Note on Architecture
Go Micro has always been a framework for building **multi-service, multi-process** systems. The [micro/blog](https://github.com/micro/blog) platform is a great example — each service runs as its own binary, communicates over RPC, and is independently deployable. If that's what you're after, check it out.
For this walkthrough, we take a different approach: a **modular monolith**. All four domains live in a single process. This is a perfectly valid starting point — you get the clean separation of handler interfaces without the operational overhead of multiple services. And because Go Micro's handler registration works the same way in both models, you can break these out into separate services later as your team or requirements grow. No rewrite needed.
## One Line to Agent-Enable Everything
```go
service := micro.NewService("platform",
micro.Address(":9090"),
mcp.WithMCP(":3001"), // This is it
)
service.Handle(users)
service.Handle(posts)
service.Handle(&Comments{})
service.Handle(&Mail{})
```
That `mcp.WithMCP(":3001")` starts an MCP gateway that:
1. Discovers all registered handlers on the service
2. Converts Go method signatures into JSON tool schemas
3. Extracts descriptions from doc comments
4. Serves it all as MCP-compliant tool definitions
No wrapper code. No API translation layer. No agent-specific handlers.
## What the Agent Sees
When an agent connects to `http://localhost:3001/mcp/tools`, it gets a tool list like:
```json
{
"tools": [
{
"name": "platform.Users.Signup",
"description": "Signup creates a new user account and returns a session token.",
"inputSchema": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Username (required, 3-20 characters)"},
"password": {"type": "string", "description": "Password (required, minimum 6 characters)"}
}
}
},
{
"name": "platform.Posts.Create",
"description": "Create publishes a new blog post.",
"inputSchema": {
"type": "object",
"properties": {
"title": {"type": "string", "description": "Post title (required)"},
"content": {"type": "string", "description": "Post body in markdown (required)"},
"author_id": {"type": "string", "description": "Author's user ID (required)"},
"author_name": {"type": "string", "description": "Author's display name (required)"}
}
}
}
]
}
```
The agent doesn't need to know it's talking to microservices. It just sees tools.
## A Real Agent Workflow
Here's what happens when you tell an agent: *"Sign up a new user called carol, write a post about Go concurrency, tag it, and send alice a mail about it."*
The agent figures out the sequence on its own:
**Step 1: Sign up**
```json
platform.Users.Signup {"name": "carol", "password": "welcome123"}
{"user": {"id": "user-3", "name": "carol"}, "token": "abc123..."}
```
**Step 2: Write the post** (using the returned user ID)
```json
platform.Posts.Create {
"title": "Go Concurrency Patterns",
"content": "Go's concurrency model is built on goroutines and channels...",
"author_id": "user-3",
"author_name": "carol"
}
{"post": {"id": "post-2", "title": "Go Concurrency Patterns", ...}}
```
**Step 3: Tag it** (using the returned post ID)
```json
platform.Posts.TagPost {"post_id": "post-2", "tag": "golang"}
platform.Posts.TagPost {"post_id": "post-2", "tag": "concurrency"}
```
**Step 4: Notify alice**
```json
platform.Mail.Send {
"from": "carol",
"to": "alice",
"subject": "New post: Go Concurrency Patterns",
"body": "Hi Alice, I just published a post about Go concurrency..."
}
```
No orchestration engine. No workflow definition. The agent reads the tool descriptions, understands the data flow (signup returns a user ID, create returns a post ID), and chains the calls naturally.
## Why Doc Comments Matter
The agent's ability to chain these calls correctly comes from good descriptions. Compare:
```go
// Bad: agent doesn't know what this returns or when to use it
func (s *Users) Signup(ctx context.Context, req *SignupRequest, rsp *SignupResponse) error {
// Good: agent knows the purpose, constraints, and return value
// Signup creates a new user account and returns a session token.
// The username must be unique. Use the returned token for authenticated operations.
//
// @example {"name": "alice", "password": "secret123"}
func (s *Users) Signup(ctx context.Context, req *SignupRequest, rsp *SignupResponse) error {
```
The `@example` tag is especially valuable — it gives the agent a concrete input to work from, reducing errors and hallucinated field names.
Similarly, `description` struct tags on request/response fields tell the agent what each parameter means:
```go
type CreatePostRequest struct {
Title string `json:"title" description:"Post title (required)"`
Content string `json:"content" description:"Post body in markdown (required)"`
AuthorID string `json:"author_id" description:"Author's user ID (required)"`
AuthorName string `json:"author_name" description:"Author's display name (required)"`
}
```
## Adding MCP to Existing Services
This demo runs everything in one process, but if you already have Go Micro services running as separate processes (like [micro/blog](https://github.com/micro/blog)), you have two additional options beyond the in-process approach shown above:
### Option 1: Standalone gateway binary
Point a gateway at your service registry and it discovers all running services automatically:
```bash
micro-mcp-gateway --registry consul:8500 --address :3001
```
### Option 2: Sidecar in your deployment
```yaml
# docker-compose.yml
services:
blog:
image: micro/blog
mcp-gateway:
image: micro/mcp-gateway
environment:
- REGISTRY=consul:8500
ports:
- "3001:3001"
```
Both discover services from the registry and expose them as MCP tools. Zero changes to your service code.
## Production Considerations
The MCP gateway includes everything you need for production:
- **Auth & Scopes** — per-tool permissions with JWT tokens
- **Rate Limiting** — token bucket per tool
- **Circuit Breakers** — protect downstream services from cascading failures
- **Audit Logging** — immutable records of every tool call
- **OpenTelemetry** — full span instrumentation with trace context propagation
```go
mcp.WithMCP(":3001",
mcp.WithAuth(jwtProvider),
mcp.WithRateLimit(100, 20),
mcp.WithCircuitBreaker(5, 30*time.Second),
mcp.WithAudit(auditLogger),
)
```
## Try It
```bash
cd examples/mcp/platform
go run .
```
Then point any MCP-compatible agent at `http://localhost:3001/mcp/tools` and start talking to your services.
The full example is at [`examples/mcp/platform/`](https://github.com/micro/go-micro/tree/master/examples/mcp/platform).
## What's Next
We're working on a Kubernetes operator that automatically deploys MCP gateways alongside your services, request/response caching to reduce redundant calls from agents, and multi-tenant namespace isolation. See the [roadmap](/docs/roadmap-2026) for details.
The core idea is simple: well-structured services — whether running as a modular monolith or as independently deployed microservices — already have the right shape for AI tools. We just needed to bridge the protocol gap. With MCP, that bridge is one line of code.
Whether you start with a single process like this demo or go straight to multi-service like [micro/blog](https://github.com/micro/blog), the MCP integration works the same way.
+171
View File
@@ -0,0 +1,171 @@
---
layout: blog
title: "Atlas Cloud Sponsors Go Micro: 300+ AI Models, One Integration"
permalink: /blog/8
description: "Atlas Cloud joins as an official Go Micro sponsor, bringing 300+ AI models across text, image, and video to the framework's ai package."
---
# Atlas Cloud Sponsors Go Micro: 300+ AI Models, One Integration
*May 28, 2026 • By the Go Micro Team*
<img src="/images/generated/blog-atlas.jpg" alt="Atlas Cloud unified AI API" style="width: 100%; border-radius: 8px; margin-bottom: 1.5rem;" />
We're excited to announce that **[Atlas Cloud](https://www.atlascloud.ai/)** is sponsoring Go Micro as an official AI provider partner. Atlas Cloud is now a first-class provider in the `ai` package, giving Go Micro users access to 300+ models across text, image, and video through a single integration.
## The Sponsorship
Atlas Cloud is an enterprise AI infrastructure platform that provides a unified API for LLM, image, and video generation. They partner with OpenRouter and ComfyUI, offer SOC 2 and HIPAA compliance, and run a custom inference engine (Atlas Photon) with FP4 quantization for fast, cost-effective inference.
Their sponsorship supports Go Micro's continued development as an AI-native microservices framework. Like Anthropic's Claude Max sponsorship that accelerated our [MCP integration](/blog/3), Atlas Cloud's support helps us maintain and expand the framework for the growing community of developers building AI-powered services.
## What Atlas Cloud Brings
Atlas Cloud's platform stands out for three reasons:
**Breadth of models.** Over 300 curated models including DeepSeek, Qwen, GLM, Kimi for text; GPT Image, Flux, ERNIE for images; and Seedance, Kling, Wan, Veo for video. New SOTA models are deployed on day zero of release.
**OpenAI-compatible API.** Atlas Cloud exposes a `/v1/chat/completions` endpoint that's a drop-in replacement for OpenAI. If you're already using the OpenAI SDK, switching to Atlas Cloud is a one-line change — just swap the base URL.
**Enterprise-ready.** SOC 2 certified, HIPAA compliant, pay-as-you-go pricing with no minimums. Competitive rates, often 48-54% below comparable providers.
## The Integration
Atlas Cloud is available in Go Micro's `ai` package as a registered provider. Here's the quick start:
```go
import (
"go-micro.dev/v5/ai"
_ "go-micro.dev/v5/ai/atlascloud"
)
m := ai.New("atlascloud",
ai.WithAPIKey("your-atlas-cloud-key"),
)
resp, err := m.Generate(ctx, &ai.Request{
Prompt: "Explain microservices in one paragraph",
SystemPrompt: "You are a helpful assistant",
})
fmt.Println(resp.Reply)
```
The default model is `llama-3.3-70b` and the default base URL is `https://api.atlascloud.ai`. Both are configurable:
```go
m := ai.New("atlascloud",
ai.WithAPIKey("your-key"),
ai.WithModel("deepseek-v4"),
ai.WithBaseURL("https://api.atlascloud.ai"),
)
```
### Image Generation
Atlas Cloud's image generation models are available through Go Micro's `ImageModel` interface. Generate images from text prompts with the same pattern as text generation:
```go
ig := ai.NewImage("atlascloud",
ai.WithAPIKey("your-key"),
)
resp, err := ig.GenerateImage(ctx, &ai.ImageRequest{
Prompt: "A futuristic city skyline at sunset, digital art",
Size: "1024x1024",
})
// resp.Images[0].URL contains the generated image
fmt.Println(resp.Images[0].URL)
```
Atlas Cloud supports models like `gpt-image-1`, `flux-2`, and more from their 300+ model catalog. The same `ImageModel` interface works with OpenAI too — swap the provider name and your code stays the same.
### Tool Calling
Atlas Cloud supports OpenAI-compatible function calling, which means it works with Go Micro's tool execution flow. Services registered in the registry become tools that the model can call:
```go
tools := ai.NewTools(service.Registry())
discovered, _ := tools.Discover()
m := ai.New("atlascloud",
ai.WithAPIKey(key),
ai.WithTools(tools),
)
resp, _ := m.Generate(ctx, &ai.Request{
Prompt: "List all users and send a welcome email to each",
Tools: discovered,
})
```
The model discovers your services, picks the right endpoints, and the `ToolHandler` executes the RPCs. This works identically to how it works with Anthropic or OpenAI — the provider is swappable.
### micro chat
Atlas Cloud works out of the box with the `micro chat` CLI:
```bash
ATLASCLOUD_API_KEY=your-key micro chat --provider atlascloud
> list all orders from the last week
> create a new user named Alice with role admin
```
### micro run
When running `micro run` or `micro server`, Atlas Cloud is auto-detected if you set the base URL:
```bash
export MICRO_AI_API_KEY=your-atlas-cloud-key
export MICRO_AI_BASE_URL=https://api.atlascloud.ai
micro run
```
The agent playground at `/agent` will use Atlas Cloud for all LLM calls.
## Provider Lineup
With Atlas Cloud, Go Micro now supports seven AI providers:
| Provider | API Format | Default Model |
|----------|-----------|---------------|
| **Anthropic** | Native (Messages API) | `claude-sonnet-4-20250514` |
| **Google Gemini** | Native (generateContent) | `gemini-2.5-flash` |
| **OpenAI** | Native (chat/completions) | `gpt-4o` |
| **Atlas Cloud** | OpenAI-compatible | `llama-3.3-70b` |
| **Groq** | OpenAI-compatible | `llama-3.3-70b-versatile` |
| **Mistral** | OpenAI-compatible | `mistral-large-latest` |
| **Together AI** | OpenAI-compatible | `Llama-3.3-70B-Instruct-Turbo` |
All providers implement the same `ai.Model` interface and work with `ai.Tools`, `micro chat`, and the agent playground.
## Getting Started
1. **Sign up** at [atlascloud.ai](https://www.atlascloud.ai/) and get an API key
2. **Import** the provider:
```go
import _ "go-micro.dev/v5/ai/atlascloud"
```
3. **Use it** in your service, the CLI, or the agent playground
See the full [Atlas Cloud Integration Guide](/docs/guides/atlascloud-integration) for detailed examples, environment variable configuration, and model selection.
## What This Means
Go Micro's position is that every microservice should be agent-accessible, and the model powering the agent should be your choice. Atlas Cloud's 300+ models mean developers aren't locked into a single provider or pricing tier. A service built with Go Micro works with Claude, GPT-4, Gemini, Llama, DeepSeek, or any of the hundreds of models Atlas Cloud offers — same code, different import.
We're grateful to Atlas Cloud for their sponsorship and excited to have them as part of the Go Micro ecosystem.
---
*Go Micro is an open source framework for distributed systems development. [Star us on GitHub](https://github.com/micro/go-micro).*
*Thanks to Atlas Cloud for sponsoring Go Micro and supporting the open source community.*
<div class="post-nav">
<div><a href="/blog/7">&larr; Your Microservices Are Already an AI Platform</a></div>
<div><a href="/blog/">All Posts</a></div>
</div>
+205
View File
@@ -0,0 +1,205 @@
---
layout: blog
title: "From Chat to Flows: What If Your Services Could Orchestrate Themselves?"
permalink: /blog/9
description: "Exploring the concept of LLM-powered service orchestration — what happens when micro chat becomes a persistent, event-driven flow engine."
---
# From Chat to Flows: What If Your Services Could Orchestrate Themselves?
*May 29, 2026 • By the Go Micro Team*
<img src="/images/generated/blog-agents-demo.jpg" alt="Service orchestration concept" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
We shipped `micro chat` recently — an interactive terminal where you talk to your services through an LLM. You say "list all users over 30 and send each a welcome email," and the model figures out which services to call, in what order, with what arguments.
It works. But it's interactive and ephemeral. You type a prompt, get a result, move on. What if you could save that prompt as a **flow** and trigger it from an event?
This post explores that idea. We're not shipping anything yet — we're thinking out loud about where the intersection of microservices, MCP, and LLMs could go.
## What We Have Today
Go Micro already has the building blocks:
- **Services as tools**: every endpoint is discoverable via MCP with typed schemas
- **`ai.Tools`**: programmatic discovery and execution — `ai.DiscoverTools(reg)` gives you the tool list, `ai.NewTools(reg).Handler()` executes RPCs
- **`ai.History`**: multi-turn conversation state so the LLM has context across steps
- **`micro chat`**: the interactive agent loop that ties it together
- **Broker/events**: pub/sub for async communication between services
The gap is between "I can interactively ask an LLM to orchestrate my services" and "I can define a persistent, event-driven workflow that does it automatically."
## The Flow Concept
Imagine you could define a flow like this:
```yaml
name: onboard-user
trigger: events.user.created
prompt: |
A new user was just created: {{.event.data}}.
1. Send them a welcome email via the email service
2. Create a default workspace via the workspace service
3. If they're on an enterprise plan, assign them to the enterprise onboarding queue
4. Log everything via the audit service
```
When a `user.created` event fires, the flow engine:
1. Discovers available services from the registry
2. Builds the tool list from their endpoints
3. Feeds the event data + prompt to an LLM
4. The LLM decides which tools to call, in what order, with what arguments
5. The engine executes the RPCs, feeds results back, and lets the LLM continue until done
6. The flow result is logged/stored for observability
This is essentially `micro chat` but triggered by an event instead of a human, with the prompt pre-defined instead of typed interactively.
## How It Compares to Traditional Orchestration
| | Step Functions / Temporal | LLM Flow |
|---|---|---|
| **Definition** | State machine in JSON/YAML | Natural language prompt |
| **Branching** | Explicit if/else states | LLM decides based on context |
| **Error handling** | Retry policies, catch blocks | "If this fails, try X instead" |
| **New service** | Update the state machine | LLM discovers it automatically |
| **Determinism** | 100% reproducible | Probabilistic (same prompt, slightly different execution) |
| **Cost** | Compute only | Compute + LLM tokens per flow |
| **Debugging** | Step-by-step state trace | Conversation log |
The tradeoff is clear: **traditional orchestration is deterministic but rigid. LLM flows are flexible but probabilistic.**
For a payment processing pipeline, you want Step Functions. For "onboard this user and do whatever makes sense based on their plan," an LLM flow could be genuinely better — it adapts to new services without code changes.
## What This Would Look Like in Go Micro
The simplest version is just `micro chat` with a saved prompt and a trigger:
```go
flow := ai.NewFlow("onboard-user",
ai.WithTrigger("events.user.created"),
ai.WithPrompt(`A new user was created: {{.Data}}.
Send welcome email, create workspace,
assign to enterprise queue if enterprise plan.`),
ai.WithProvider("atlascloud"),
ai.WithAPIKey(key),
)
// Register with the service
service := micro.NewService("flow-runner")
flow.Register(service)
service.Run()
```
Under the hood, `Flow` would:
1. Subscribe to the broker topic
2. On each event, create an `ai.History` with the prompt + event data
3. Call `m.Generate()` with history messages until the LLM stops requesting tool calls
4. Log the full conversation for audit
The building blocks already exist. `ai.History` manages the conversation. `ai.Tools` discovers and executes services. The broker delivers events. A `Flow` just connects them.
## Why We Haven't Built It Yet
Three honest reasons:
**1. Non-determinism is dangerous for workflows.** If you run the same flow twice with the same input, the LLM might call tools in a different order or skip a step. For many workflows, that's a bug, not a feature. We'd need guardrails: required steps, validation, output schemas.
**2. Cost unpredictability.** Each flow execution costs LLM tokens. A complex flow with 5 tool calls might cost $0.01. At 10,000 events per day, that's $100/day just for orchestration. Traditional orchestrators cost effectively nothing per execution.
**3. Scope creep.** Go Micro is a microservices framework, not a workflow engine. Adding persistent flow state, retry logic, dead letter queues, and flow versioning is a big commitment. Temporal exists. Step Functions exist. We should be honest about where the framework ends and the platform begins.
## Where It Makes Sense
Despite those caveats, there are use cases where this is genuinely better than traditional orchestration:
- **Ops automation**: "When a service health check fails, investigate by checking logs, recent deployments, and related services, then post a summary to Slack." This is inherently fuzzy — you don't know which tools you'll need until you see what's wrong.
- **Customer support flows**: "A customer filed a ticket about a billing issue. Look up their account, check recent invoices, and draft a response." The flow adapts to what it finds.
- **Data pipeline glue**: "New CSV uploaded. Parse it, validate the schema, create records in the appropriate service, and report any errors." The LLM handles schema variations that would break a rigid pipeline.
- **Development workflows**: "Run the test suite, analyze failures, check if they're flaky, and create issues for real failures." This is `micro chat` for CI.
## What You Can Do Today
You don't need a flow engine to get most of this value. The `ai.Tools` package already gives you programmatic access:
```go
tools := ai.NewTools(service.Registry())
discovered, _ := tools.Discover()
m := ai.New("atlascloud",
ai.WithAPIKey(key),
ai.WithTools(tools),
)
hist := ai.NewHistory(50)
// React to an event
broker.Subscribe("user.created", func(e broker.Event) error {
prompt := fmt.Sprintf("New user created: %s. Send welcome email and create workspace.", string(e.Message().Body))
resp, _ := m.Generate(ctx, &ai.Request{Prompt: prompt, SystemPrompt: "You are a service orchestrator.", Tools: discovered, Messages: hist.Messages()})
log.Infof("Flow result: %s", resp.Answer)
hist.Reset() // fresh history for next event
return nil
})
```
That's ~15 lines. No flow engine, no YAML, no new abstractions. Just a broker subscription that feeds events into the LLM with your services as tools.
## Update: We Built It
After publishing this post, we went ahead and built the `ai/flow` package. It wraps the pattern above into a reusable primitive:
```go
import "go-micro.dev/v5/ai/flow"
f := flow.New("onboard-user",
flow.Trigger("events.user.created"),
flow.Prompt("New user: {{.Data}}. Send welcome email and create workspace."),
flow.Provider("anthropic"),
flow.APIKey(key),
)
f.Register(service.Registry(), service.Options().Broker, service.Client())
service.Run() // flow listens and reacts to events
```
Under the hood, each event triggers a fresh `ai.History` + `tools.Discover` + `model.Generate` cycle. The flow records every execution with timing, tool calls, and errors.
There's also a CLI:
```bash
# Subscribe to events and react
micro flow run --trigger events.user.created \
--prompt "New user: {{.Data}}. Send welcome email." \
--provider anthropic
# One-shot execution
micro flow exec --prompt "List all users and count them" \
--provider anthropic
```
The output is JSON — flow name, prompt, tool calls made, reply, answer, duration. Pipe it to `jq` or log it.
We kept it deliberately minimal: no persistent state, no retry policies, no YAML config. Each event gets a fresh history, the LLM decides what to do, and the result is recorded. If you need Temporal-grade durability, use Temporal. But for "when X happens, let the LLM figure out what services to call" — this is enough.
The questions from the original post still stand. We'd love feedback on what guardrails people need and whether this should grow or stay small.
## The Bigger Picture
The thesis behind Go Micro's AI-native direction is that **services should be composable by agents, not just by code.** MCP made services discoverable. `ai.Tools` made them callable. `micro chat` made them interactive. Flows would make them orchestratable.
Each layer builds on the previous one. And at each layer, the question is the same: does this belong in the framework, or is it better left to the user? So far, we've been conservative — `ai.Tools` is 150 lines, `History` is 80, `micro chat` is 170. Small, composable building blocks rather than a big orchestration framework.
We think that's the right approach. But we're watching to see if the community says otherwise.
---
*Go Micro is an open source framework for distributed systems development. [Star us on GitHub](https://github.com/micro/go-micro).*
<div class="post-nav">
<div><a href="/blog/8">&larr; Atlas Cloud Sponsors Go Micro</a></div>
<div><a href="/blog/">All Posts</a></div>
<div><a href="/blog/10">micro chat &rarr;</a></div>
</div>
+258
View File
@@ -0,0 +1,258 @@
---
layout: blog
title: Blog
permalink: /blog/
---
<div class="blog-header">
<h1>Go Micro Blog</h1>
<p class="meta">News, updates, and tutorials for Go Micro</p>
</div>
<div class="posts">
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/35">What's New in Go Micro: v6.6.0</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">July 11, 2026</p>
<p>Go Micro v6.6.0 strengthens the first-agent on-ramp, hardens plan/delegate recovery, improves provider fallback repair, and adds CI-backed security checks.</p>
<a href="/blog/35">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/34">What's New in Go Micro: v6.3.15</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">July 5, 2026</p>
<p>Go Micro v6.3.15 tightens the first-agent on-ramp, adds Anthropic streaming, hardens plan/delegate execution, and improves provider text tool-call recovery.</p>
<a href="/blog/34">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/33">The Loop, Shipped: Introducing micro loop</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">July 2, 2026</p>
<p>The autonomous loop that builds Go Micro is now a command. <code>micro loop init</code> drops a planner, builder, triage, coherence, and release pipeline into any repository — GitHub Actions as the runtime, editable prompt files as the policy — and Go Micro now runs on it.</p>
<a href="/blog/33">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/32">An Agent Is a Service: Where Agent Frameworks Are Going</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 30, 2026</p>
<p>A field guide to the agent-framework landscape — LangChain and the first wave, the two layers of a harness, the rise of loop engineering, and where the frameworks (LangGraph, ADK, CrewAI, AutoGen, tRPC-Agent-Go) diverge. And why Go Micro's answer is that an agent is a service.</p>
<a href="/blog/32">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/31">How Go Micro Builds Itself</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 25, 2026</p>
<p>Go Micro is increasingly built by an autonomous loop of two AI agents — Codex writing scoped increments, Claude Code orchestrating, the human setting direction. Here's how the loop actually works, including the parts that broke.</p>
<a href="/blog/31">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/30">Go Micro is an Agent Harness</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 24, 2026</p>
<p>The first wave of agent frameworks put a model in a loop. The harder problem is operating that loop — tools, state, guardrails, recovery, observability, interop — and that's what Go Micro is: the harness around the agent, built from the stack you already deploy.</p>
<a href="/blog/30">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/29">Go Micro Joins OpenAI's Codex for Open Source</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 23, 2026</p>
<p>OpenAI is backing Go Micro through Codex for Open Source — six months of ChatGPT Pro to support the work of maintaining the project. A framework built around agentic development, maintained with agentic tooling.</p>
<a href="/blog/29">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/28">Building a Support Agent in Go</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 19, 2026</p>
<p>A real thing you can build with Go Micro: a support desk where a customer ticket triggers an agent that looks up the customer, sets priority, and replies — with a human-in-the-loop gate on the one action that touches a customer.</p>
<a href="/blog/28">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/27">Bringing an Open Source Project Back from the Dead</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 18, 2026</p>
<p>Go Micro started in January 2015, went through a VC-funded company and a platform pivot, and then went quiet. This is how it came back — and how agents, services, and flows brought it to v6.</p>
<a href="/blog/27">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/26">Agents Across Frameworks: A2A</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 18, 2026</p>
<p>Go Micro agents already call each other over RPC. Now they speak the Agent2Agent protocol too — reachable by, and able to reach, agents built on any framework. Cards are generated from the registry, the same way the MCP gateway derives tools.</p>
<a href="/blog/26">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/25">Go Micro is Becoming a Framework for Agentic Development</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 17, 2026</p>
<p>Three months ago, with Anthropic's support, Go Micro went all in on AI. A look back at what shipped — agents, workflows, guardrails, payments, durable execution — and where it's heading.</p>
<a href="/blog/25">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/24">Durable Workflows</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 17, 2026</p>
<p>An event-driven workflow runs for minutes and has side effects partway through — it reserved stock, it charged a card. When the process dies mid-run, re-running from the top does it all again. Go Micro flows are now ordered, checkpointed steps that resume where they stopped.</p>
<a href="/blog/24">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/23">Agent Guardrails</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 16, 2026</p>
<p>An autonomous agent fails in mundane ways — it loops, it runs away, it takes an action it shouldn't. Go Micro separates orchestration from execution safety, and gives every agent three guardrails at the point where tools actually run.</p>
<a href="/blog/23">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/22">Integrating x402: Payments for Agents</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 15, 2026</p>
<p>Agents that act on their own eventually need to pay on their own. Go Micro now speaks x402 — the HTTP 402 payment standard — so a tool can require a stablecoin payment and an agent can settle it, with the chain pluggable behind a facilitator.</p>
<a href="/blog/22">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/21">When the Event Is the Prompt</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 15, 2026</p>
<p>Most agents wait for a human to type something. The useful ones don't — they run because something happened in the system. A Flow turns an event into the prompt, and an agent acts on its own.</p>
<a href="/blog/21">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/20">Doubling Down on Agents</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 10, 2026</p>
<p>Go Micro made services easy by being opinionated, batteries-included, and pluggable. We're applying the same model to agents — a model, memory, and tools that compose like a service does.</p>
<a href="/blog/20">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/19">The Evolution of Microservices</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 8, 2026</p>
<p>From the scaling pressures that produced microservices, through Kubernetes and the service mesh, to AI agents — fifteen years of evolution, what actually endured, and why the future belongs to agents.</p>
<a href="/blog/19">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/18">Not Everything Should Be an Agent</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 8, 2026</p>
<p>We spent two posts on agents that plan and delegate. Here's the other half: when the path is known, you want a workflow — predictable, event-driven, deterministic. In Go Micro they're the same building blocks, two modes.</p>
<a href="/blog/18">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/17">Agents That Plan and Delegate</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 7, 2026</p>
<p>An agent shouldn't just react tool by tool. It should form intent — plan what it's doing — and direct intent — delegate what it shouldn't do itself. Go Micro now gives every agent both, as plain tools.</p>
<a href="/blog/17">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/16">Introducing micro.NewAgent()</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 5, 2026</p>
<p>Agent is now a first-class abstraction in Go Micro — alongside Service and Flow. Build intelligent agents that manage your services in Go.</p>
<a href="/blog/16">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/15">Agents for Services: A New Model for Microservices</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 4, 2026</p>
<p>What if every service had an agent responsible for it? Not embedded in the service, but created to manage its lifecycle. A design for distributed AI agents on top of microservices.</p>
<a href="/blog/15">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/14">Going All In on AI</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 4, 2026</p>
<p>Go Micro started as a microservices framework. It's becoming the way you build software that AI agents can use. Here's why we're making that bet.</p>
<a href="/blog/14">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/13">From Prompt to Production: AI-Generated Microservices That Actually Run</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">June 3, 2026</p>
<p>One command generates real services with business logic, compiles them, starts them, and lets you talk to them. When you need more, the agent builds new services mid-conversation.</p>
<a href="/blog/13">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/12">Tools as Services: Why Go Micro Was Always Ready for AI</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">May 30, 2026</p>
<p>The path from API gateway to MCP to LLM tools was shorter than you'd think — because services were always self-describing.</p>
<a href="/blog/12">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/11">Build Your Own AI Agent CLI in 150 Lines</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">May 30, 2026</p>
<p>A complete teardown of micro chat — how to build an LLM agent that discovers and orchestrates your services, with every line explained.</p>
<a href="/blog/11">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/10">micro chat: Talk to Your Services</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">May 29, 2026</p>
<p>Introducing micro chat — an interactive CLI that discovers your services, turns them into tools, and lets you orchestrate them through natural language.</p>
<a href="/blog/10">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/9">From Chat to Flows: What If Your Services Could Orchestrate Themselves?</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">May 29, 2026</p>
<p>Exploring the concept of LLM-powered service orchestration — what happens when micro chat becomes a persistent, event-driven flow engine.</p>
<a href="/blog/9">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/8">Atlas Cloud Sponsors Go Micro: 300+ AI Models, One Integration</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">May 28, 2026</p>
<p>Atlas Cloud joins as an official Go Micro sponsor, bringing 300+ AI models across text, image, and video to the framework's ai package.</p>
<a href="/blog/8">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/7">Your Microservices Are Already an AI Platform</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">March 5, 2026</p>
<p>How existing Go Micro services become agent-accessible with zero code changes. A walkthrough using the micro/blog platform as a real-world example.</p>
<a href="/blog/7">Read more &rarr;</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/6">The Model Package: Client, Server, and Now Data</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">March 4, 2026</p>
<p>Go Micro now has a typed data model layer — define structs, get CRUD and queries, swap backends. Every service gets Client, Server, and Model.</p>
<a href="/blog/6">Read more →</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/5">Developer Experience Cleanup: One Way to Do Things</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">March 4, 2026</p>
<p>Unified service creation, cleaner handler registration, and modular monolith support — the Go Micro DX overhaul.</p>
<a href="/blog/5">Read more →</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/4">Agents Meet Microservices: A Hands-On Demo</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">March 4, 2026</p>
<p>Build three microservices and let an AI agent manage them with natural language — no glue code, no API wrappers, just Go comments.</p>
<a href="/blog/4">Read more →</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/3">Building the AI-Native Future of Go Micro with Claude</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">March 4, 2026</p>
<p>How Anthropic's Claude Max sponsorship accelerated Go Micro's MCP integration — WebSocket transport, OpenTelemetry tracing, LlamaIndex SDK, and what's next.</p>
<a href="/blog/3">Read more →</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/2">Making Microservices AI-Native with MCP</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">February 11, 2026</p>
<p>Expose go-micro services as AI tools with 3 lines of code using the Model Context Protocol. Make your microservices instantly accessible to Claude and other AI assistants.</p>
<a href="/blog/2">Read more →</a>
</article>
<article style="margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5;">
<h2 style="margin: 0 0 0.5rem;"><a href="/blog/1">Introducing micro deploy</a></h2>
<p class="meta" style="color: #666; font-size: 0.85rem;">January 27, 2026</p>
<p>Deploy your Go Micro services to any Linux server with a single command. No Docker, no Kubernetes, no platform — just systemd.</p>
<a href="/blog/1">Read more →</a>
</article>
</div>
@@ -0,0 +1,139 @@
# Summary: Reflection Removal Evaluation
**Issue**: [FEATURE] Remove reflect
**Date**: 2026-02-03
**Status**: EVALUATION COMPLETE - RECOMMENDATION AGAINST REMOVAL
## Executive Summary
After comprehensive analysis of go-micro's reflection usage and comparison with livekit/psrpc (the referenced example), **we recommend AGAINST removing reflection from go-micro**.
## Key Findings
### 1. Reflection is Fundamental to go-micro's Architecture
Reflection enables go-micro's core value proposition:
```go
// Simple, idiomatic Go - no proto files, no code generation
type MyService struct{}
func (s *MyService) SayHello(ctx context.Context, req *Request, rsp *Response) error {
rsp.Message = "Hello " + req.Name
return nil
}
server.Handle(server.NewHandler(&MyService{}))
```
This **requires** reflection. There is no way to achieve this simplicity with generics or code generation.
### 2. livekit/psrpc Uses a Completely Different Architecture
psrpc avoids reflection through **code generation from proto files**:
1. Write `.proto` service definitions
2. Run `protoc --psrpc_out=.` to generate code
3. Implement generated interfaces
4. Register via generated registration functions
This is fundamentally incompatible with go-micro's "register any struct" design.
### 3. Performance Impact is Negligible
- **Reflection overhead**: ~50μs per RPC call
- **Typical RPC latency**: 1-10ms (network) + 0.1-0.5ms (serialization) + business logic
- **Reflection as % of total**: <5% for typical workloads
- **Would removing it help?**: Only for applications with <100μs latency requirements and >100k RPS
### 4. Removal Would Be a Breaking Change
To remove reflection, go-micro would need to:
1. Adopt proto-first design (like gRPC/psrpc)
2. Require code generation for all handlers
3. Change all registration APIs
4. Break all existing applications
5. Estimated effort: 6-12 months of development
### 5. Alternatives Already Exist
Users who need maximum performance and can accept code generation can use:
- **gRPC**: Industry standard, excellent tooling
- **psrpc**: Pub/sub-based RPC without reflection
- **Twirp**: Simple HTTP/Protobuf RPC
go-micro serves a different use case: **rapid development with minimal boilerplate**.
## Deliverables
1. **[reflection-removal-analysis.md](reflection-removal-analysis.html)**
- 16KB technical deep-dive
- Code examples showing current reflection usage
- Comparison with psrpc architecture
- Detailed feasibility analysis
- Performance measurements
- Recommendation with rationale
2. **[performance.md](performance.html)**
- 6KB user-facing guide
- When reflection matters (rarely)
- Performance best practices
- When to consider alternatives
- Benchmarks in context
3. **README.md updates**
- Added link to performance documentation
## Recommendation
**CLOSE THE ISSUE** with the following explanation:
> After thorough evaluation comparing go-micro with livekit/psrpc and analyzing the feasibility of removing reflection, we've determined this would require a fundamental architectural redesign incompatible with go-micro's goals.
>
> **Key findings**:
>
> 1. **psrpc avoids reflection through code generation** - Requires `.proto` files and generated interfaces, a completely different architecture from go-micro
>
> 2. **go-micro's strength is "register any struct"** - This requires runtime type introspection (reflection) and cannot be achieved with Go generics or code generation
>
> 3. **Reflection overhead is ~50μs per RPC**, typically <5% of total latency in real-world applications where network I/O (1-10ms) and business logic dominate
>
> 4. **Removing reflection would**:
> - Break all existing code (100% breaking change)
> - Require 6-12 months of development
> - Eliminate go-micro's key advantage (simplicity)
> - Provide <5% performance improvement for most users
>
> 5. **For users needing maximum performance**, alternatives already exist:
> - gRPC (industry standard with code generation)
> - psrpc (pub/sub RPC without reflection)
> - Direct use of transport layer
>
> **Documentation added**:
> - [reflection-removal-analysis.md](reflection-removal-analysis.html) - Detailed technical analysis
> - [performance.md](performance.html) - Performance best practices and when to consider alternatives
>
> **Recommendation**: Keep reflection as a deliberate architectural choice that enables go-micro's simplicity and developer productivity. Profile before optimizing, and consider code-generation-based alternatives (gRPC/psrpc) only if profiling proves reflection is genuinely a bottleneck.
>
> Closing as "won't fix" - reflection is an intentional design decision, not a technical limitation.
## Next Steps
1. Add this comment to the original issue
2. Close the issue as "won't fix"
3. Consider adding a FAQ entry about reflection and performance
4. Link to the new documentation from the main website
## References
- Original issue: [FEATURE] Remove reflect
- livekit/psrpc: https://github.com/livekit/psrpc
- Go Reflection: https://go.dev/blog/laws-of-reflection
- gRPC-Go: https://github.com/grpc/grpc-go
---
**Prepared by**: GitHub Copilot Agent
**Review**: Ready for maintainer decision
**Impact**: Documentation only, no code changes
+195
View File
@@ -0,0 +1,195 @@
# TLS Security Migration Guide
## Overview
Go Micro v6 verifies TLS certificates by default. This guide is for teams
upgrading from v5, where TLS verification was disabled by default for backward
compatibility.
## Current Status (v6)
**Default Behavior**: TLS certificate verification is **enabled** by default
(`InsecureSkipVerify: false`).
**What changed from v5**: v5 allowed `MICRO_TLS_SECURE=true` to opt into
certificate verification. In v6, secure verification is the default and
`MICRO_TLS_SECURE` is no longer used.
**Development escape hatch**: for local self-signed certificates only, set
`MICRO_TLS_INSECURE=true` or provide an explicit insecure TLS config.
## Migration Path from v5
### 1. Remove the old opt-in flag
Delete any use of the v5-only environment variable:
```bash
unset MICRO_TLS_SECURE
```
No replacement is required for production: verification is already on in v6.
### 2. Use the default secure config
Most services need no TLS-specific code. If you configure TLS explicitly, use a standard `crypto/tls` config with verification enabled:
```go
import (
"crypto/tls"
"go-micro.dev/v6/broker"
)
// Create broker with certificate verification enabled.
b := broker.NewHttpBroker(
broker.TLSConfig(&tls.Config{MinVersion: tls.VersionTLS12}),
)
```
### 3. Provide a custom trust root when needed
For private CAs, provide your own TLS configuration:
```go
import (
"crypto/tls"
"crypto/x509"
"go-micro.dev/v6/broker"
"os"
)
// Load CA certificates
caCert, err := os.ReadFile("/path/to/ca-cert.pem")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// Create custom TLS config
tlsConfig := &tls.Config{
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
}
// Create broker with custom config
b := broker.NewHttpBroker(
broker.TLSConfig(tlsConfig),
)
```
### 4. Use insecure mode only for local development
If a development environment still uses self-signed certificates that are not in
your trust store, opt out explicitly:
```bash
export MICRO_TLS_INSECURE=true
```
or in code:
```go
broker.TLSConfig(&tls.Config{InsecureSkipVerify: true, MinVersion: tls.VersionTLS12})
```
Do not use insecure mode in production.
## Production Deployment Strategy
### Rolling Upgrade Considerations
The default changed at the v6 major-version boundary. Before rolling v6 into a
fleet that uses TLS, verify that:
1. All services present certificates trusted by their peers.
2. Private or self-signed CAs are installed consistently on every host.
3. Certificates include the DNS names or IP subject alternative names used by
clients.
4. Any deliberate development-only insecure settings are excluded from
production manifests.
### Recommended Approach
1. **Test in Staging** with the same certificate chain and service names used in
production.
2. **Remove v5 flags** such as `MICRO_TLS_SECURE`; they no longer control v6.
3. **Monitor for Issues**: watch for TLS handshake failures or certificate
validation errors.
4. **Use explicit insecure mode only in dev** when a short-lived environment
cannot yet provide trusted certificates.
### Multi-Host/Multi-Process Considerations
**Certificate Trust**: With secure mode as the default, ensure:
1. All hosts trust the same root CAs.
2. Self-signed certificates are properly distributed if used.
3. Certificate validity periods are monitored.
4. Certificate chains are complete.
**Service Mesh Alternative**: Consider using a service mesh (Istio, Linkerd, etc.) for:
- Automatic mTLS between services
- Certificate management and rotation
- No application code changes required
## Testing Your Migration
### Verify Secure Mode is Active
```go
package main
import (
"crypto/tls"
"fmt"
)
func main() {
config := &tls.Config{MinVersion: tls.VersionTLS12}
fmt.Printf("InsecureSkipVerify: %v (should be false)\n", config.InsecureSkipVerify)
}
```
### Test Certificate Validation
Create a test service and verify it:
- Accepts valid certificates
- Rejects invalid/self-signed certificates (when not in CA)
- Properly validates certificate chains
## Common Issues and Solutions
### Issue: "x509: certificate signed by unknown authority"
**Cause**: The server certificate is not signed by a trusted CA
**Solution**:
1. Add the CA certificate to the trusted root CAs
2. Use a properly signed certificate
3. For development only: use `MICRO_TLS_INSECURE=true` or an explicit insecure TLS config
### Issue: "x509: certificate has expired"
**Cause**: Server certificate has expired
**Solution**:
1. Renew the certificate
2. Implement certificate rotation
3. Monitor certificate expiry dates
### Issue: Services can't communicate after upgrading to v6
**Cause**: Certificates that v5 accepted by default are now verified.
**Solution**:
1. Ensure all services use certificates from a trusted CA
2. Distribute CA certificates to all nodes
3. Verify certificate SANs match service addresses
4. Use insecure mode only as a temporary local-development workaround
## Questions?
For issues or questions about TLS security migration, open an issue on GitHub or
check the documentation at https://go-micro.dev/docs/.
@@ -0,0 +1,37 @@
# TLS Security Update - Important Information
## What Changed
Go Micro v6 verifies TLS certificates by default. This completes the v5 security
migration where verification was opt-in.
## Current Behavior (v6.x)
**Default**: TLS certificate verification is **enabled**.
- `MICRO_TLS_SECURE` was a v5 opt-in flag and is no longer used.
- For local development with untrusted self-signed certificates, opt out
explicitly with `MICRO_TLS_INSECURE=true` or an explicit insecure TLS config.
## Production Recommendation
For production deployments:
1. Use CA-signed certificates or distribute your private CA to every host.
2. Remove old `MICRO_TLS_SECURE` settings from v5-era manifests.
3. Do not set `MICRO_TLS_INSECURE=true` in production.
4. Consider service mesh mTLS (Istio, Linkerd) if certificate lifecycle should be
managed outside the application.
## Migration Timeline
- **v5.x**: Insecure by default, opt-in security via `MICRO_TLS_SECURE=true`.
- **v6.x current**: Secure by default; use `MICRO_TLS_INSECURE=true` only for an
explicit development opt-out.
## Documentation
See [SECURITY_MIGRATION.md](SECURITY_MIGRATION.html) for the detailed migration
guide.
## Questions?
Open an issue on GitHub or check the documentation at https://go-micro.dev/docs/.
+200
View File
@@ -0,0 +1,200 @@
---
layout: default
title: AI Integration
---
# AI Integration
Go Micro is an agent harness and service framework for Go. Every service you build can become an AI-callable tool, every agent runs as a service with model/memory/guardrails around it, and flows orchestrate the deterministic parts. This page explains how the services → agents → workflows lifecycle fits together.
<img src="/images/generated/mcp-agent.jpg" alt="AI integration architecture" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
## The Stack
```
Services → write Go handlers, register with the framework
Registry → automatic discovery for services, agents, and flows
Gateways → micro api (HTTP→RPC), micro mcp (tools), micro a2a (agents)
ai.Tools → discovers services + executes RPCs programmatically
ai.Model → calls LLMs (Anthropic, OpenAI, Gemini, Atlas Cloud, ...)
Agents → service-backed model loop with memory, guardrails, plan/delegate
Flows → durable deterministic steps that can dispatch to agents
```
Every layer is optional. You can use Go Micro as a service framework without AI. You can use the `ai` package without MCP. But when you stack them, you get one runtime where services become tools, agents are reachable services, and workflows coordinate the predictable parts.
## Layer by Layer
### 1. Services (your code)
Write normal Go handlers. Add doc comments for AI tool descriptions:
```go
// CreateUser creates a new user account.
// @example {"name": "Alice", "email": "alice@example.com"}
func (h *Users) CreateUser(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error {
// your business logic
}
```
The doc comment becomes the tool description. The `@example` tag gives the LLM a usage hint. No AI-specific code in your handler.
### 2. Registry (service discovery)
Services register automatically. The registry is the source of truth for what's running:
```go
service := micro.NewService("users")
service.Handle(handler.New())
service.Run() // registers with the registry
```
Pluggable: mDNS (default, zero config), Consul, etcd, NATS.
### 3. MCP Gateway (services → tools)
The MCP gateway walks the registry and exposes every endpoint as a tool via the [Model Context Protocol](https://modelcontextprotocol.io/):
```go
// One line to expose all services as AI tools
service := micro.NewService("myservice", mcp.WithMCP(":3001"))
```
Or run it standalone:
```bash
micro mcp serve # stdio for Claude Code
micro mcp serve --address :3000 # HTTP for web agents
```
Any MCP-compatible agent (Claude Code, ChatGPT, custom agents) can discover and call your services.
### 4. ai.Tools (discover + execute)
`ai.Tools` turns registered services into LLM-callable tools — discovery plus RPC execution in one type:
```go
tools := ai.NewTools(service.Registry())
discovered, _ := tools.Discover() // []ai.Tool from all registered services
// Wire execution into a model with one option:
m := ai.New("anthropic", ai.WithAPIKey(key), ai.WithTools(tools))
```
This is what powers `micro chat` and the agent playground. You can use it directly in your own services to build agentic workflows.
### 5. ai.Model (LLM providers)
The `ai` package provides a pluggable interface for calling LLMs:
```go
import (
"go-micro.dev/v6/ai"
_ "go-micro.dev/v6/ai/anthropic"
)
m := ai.New("anthropic", ai.WithAPIKey(key))
resp, _ := m.Generate(ctx, &ai.Request{
Prompt: "What users are in the system?",
Tools: discovered, // from ai.Tools
})
```
Seven text providers, two image providers, one video provider. Same interface, swap with an import.
| Provider | Text | Image | Video |
|----------|------|-------|-------|
| Anthropic | yes | | |
| OpenAI | yes | yes | |
| Google Gemini | yes | | |
| Atlas Cloud | yes | yes | yes |
| Groq | yes | | |
| Mistral | yes | | |
| Together AI | yes | | |
### 6. micro chat (orchestration)
The CLI ties it all together — discovers services, builds the tool list, and lets you talk to your services:
```bash
ANTHROPIC_API_KEY=sk-ant-... micro chat --provider anthropic
> list all users
> send a welcome email to alice@example.com
> create an order for product-42
```
Multi-turn conversation with `ai.History` — the model remembers context across turns. Type `reset` to clear history.
### 7. micro flow (event-driven orchestration)
Subscribe to broker events and let an LLM orchestrate the response:
```go
import "go-micro.dev/v6/flow"
f := flow.New("onboard",
flow.Trigger("events.user.created"),
flow.Prompt("New user: {{.Data}}. Send welcome email and create workspace."),
flow.Provider("anthropic"),
flow.APIKey(key),
)
f.Register(service.Registry(), service.Options().Broker, service.Client())
```
Or from the CLI:
```bash
micro flow run --trigger events.user.created \
--prompt "New user: {{.Data}}. Send welcome email." \
--provider anthropic
micro flow exec --prompt "List all users" --provider anthropic
```
### 8. micro api (HTTP gateway)
A standalone HTTP-to-RPC gateway for exposing services over HTTP without the full dashboard:
```bash
micro api # listen on :8080
micro api --address :3000 # custom port
# Call services through the gateway
curl -XPOST -d '{"name":"Alice"}' http://localhost:8080/greeter/Greeter.Hello
```
## What You Don't Need
- **No agent framework** — the building blocks compose; you don't need a LangChain or CrewAI equivalent
- **No special handler code** — your services are normal Go handlers with doc comments
- **No API key to use MCP** — external agents bring their own models; your services just expose tools
- **No vendor lock-in** — every provider implements the same interface; swap with one import
## Getting Started
The fastest path:
```bash
# Create a service with MCP enabled
micro new myservice --template crud
cd myservice
# Run it
micro run
# Chat with it
ANTHROPIC_API_KEY=sk-ant-... micro chat --provider anthropic
> list all records
```
See also:
- [MCP Documentation](/docs/mcp.html) — detailed MCP gateway guide
- [Atlas Cloud Integration](/docs/guides/atlascloud-integration.html) — using Atlas Cloud as a provider
- [AI Provider Guide](/docs/guides/ai-provider-guide.html) — adding new providers
- [gRPC Interop Example](https://github.com/micro/go-micro/tree/master/examples/grpc-interop) — calling go-micro from standard gRPC clients
+134
View File
@@ -0,0 +1,134 @@
---
layout: default
---
# Architecture
<img src="/images/generated/architecture.jpg" alt="Go Micro architecture" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
Go Micro is one runtime for the services → agents → workflows lifecycle. The same
registry, client/server RPC, store, broker, and gateway primitives that run a
service also give an agent discoverable tools, durable state, interop, and a
place to hand off deterministic work.
## Lifecycle map
```text
Services → Agents → Workflows
handlers model loop durable orchestration
registry memory triggers and ordered steps
RPC tools guardrails agent dispatch
```
The layers are progressive: start with a service, expose its endpoints as tools,
wrap those tools with an agent, then move the known paths into flows so the model
only handles the uncertain parts.
## Service substrate
Go Micro's service framework supplies the distributed-systems base every agent
needs:
- **Registry** — services, agents, and flows register under names so clients,
gateways, and other agents can discover them without hard-coded addresses. The
default is mDNS for local development, with pluggable backends for production.
- **RPC client/server** — endpoints are normal Go handlers reached through the
client, load balanced through discovery, encoded through codecs, and optionally
streamed.
- **Broker** — asynchronous events connect services and trigger flows without
coupling producers to consumers.
- **Config and auth** — dynamic configuration plus identity and authorization keep
local and production runtimes using the same shape.
- **Pluggable interfaces** — registry, broker, store, transport, codecs, auth, and
config are Go interfaces, so the runtime can stay stable while deployments swap
infrastructure.
That substrate is intentionally not separate from the agent stack. A service
endpoint is the smallest useful unit of work, and the registry is the source of
truth for which tools and agents exist.
## Agent harness
Agents compose the service substrate with the AI-specific packages:
- **`model` / `ai.Model`** — a pluggable model interface normalizes provider calls
while letting applications pick Anthropic, OpenAI, Gemini, Atlas Cloud, Groq,
Mistral, Together AI, or a mock model for no-secret tests.
- **`store` / memory** — agent history, plans, run state, and compacted memory live
in durable storage rather than in an in-process chat loop.
- **`ai.Tools`** — discovers registered service endpoints and executes them through
the Go Micro client, so tools are generated from running services instead of a
parallel tool registry.
- **`agent`** — runs the tool-calling loop with guardrails, planning, delegation,
service-backed memory, and an `Agent.Chat` RPC endpoint. An agent is therefore a
service other clients and agents can call.
The result is a harness, not just a prompt loop: model calls are bounded by tool
scope, state is recoverable, and the same CLI and gateways that reach services can
reach agents.
## Workflows
Use `flow` when the path is known or must be repeatable. Flows subscribe to broker
events, run ordered deterministic steps, and can dispatch to an agent at the point
where judgment or language understanding is needed. This keeps long-running work
observable and restartable while preserving agents for open-ended decisions.
A common shape is:
1. A service emits an event such as `ticket.created`.
2. A flow validates and enriches the event with deterministic handlers.
3. The flow dispatches to an agent for classification, drafting, or escalation.
4. The agent calls registered service tools and returns to the flow for final
durable steps.
## Interop gateways
Gateways project the same runtime to external callers:
- **`micro api`** exposes service RPC over HTTP.
- **`micro mcp`** exposes registered service endpoints as Model Context Protocol
tools for external agents.
- **`micro a2a`** exposes registered Go Micro agents through the Agent2Agent
protocol and lets Go Micro flows or agents dispatch to agents hosted elsewhere.
MCP is the services-as-tools boundary; A2A is the agents-as-agents boundary. Both
come from registry metadata, so adding a service or agent updates the external
surface without duplicate wiring.
## Developer path
If you are new, follow the architecture in the same order the runtime composes it:
1. [Install troubleshooting](guides/install-troubleshooting.html) — make sure the
CLI, `PATH`, version, and no-secret smoke path are healthy.
2. [`micro agent demo`](getting-started.html#first-agent-on-ramp) — print the
provider-free first-agent command and next docs steps from the installed CLI.
3. `micro agent quickcheck` (or `micro agent debug`) — print the short recovery
map when scaffold → run → chat → inspect stalls.
4. `micro examples` — list the maintained provider-free runnable examples in
copy/paste order.
5. `micro zero-to-hero` — print the maintained one-command no-secret lifecycle
harness and runnable examples.
6. [Examples wayfinding index](https://github.com/micro/go-micro/blob/master/examples/INDEX.md)
— choose the smallest no-secret first-agent, support reference, and interop
examples from one map.
7. [Smallest first-agent example](https://github.com/micro/go-micro/tree/master/examples/first-agent)
— run one service-backed agent with a mock model.
8. [No-secret first-agent transcript](guides/no-secret-first-agent.html) — see the
maintained support-agent path work without a provider key.
9. [Your First Agent](guides/your-first-agent.html) — build and chat with a
service-backed agent.
10. [Debugging your agent](guides/debugging-agents.html) — inspect service
registration, tools, memory, providers, and run history.
11. [0→hero Reference](guides/zero-to-hero.html) — walk scaffold → run → chat →
inspect → flow → deploy dry-run as the maintained lifecycle contract.
## Related
- [AI Integration](ai-integration.html) — layer-by-layer services → agents → workflows wiring
- [Getting Started](getting-started.html) — first service and first-agent on-ramp
- [Examples](examples/) — runnable examples mapped to the lifecycle
- [ADR Index](architecture/index.md) — architecture decision records
- [Configuration](config.html)
- [Plugins](plugins.html)
@@ -0,0 +1,90 @@
---
layout: default
---
# ADR-001: Plugin Architecture
## Status
**Accepted**
## Context
Microservices frameworks need to support multiple infrastructure backends (registries, brokers, transports, stores). Different teams have different preferences and existing infrastructure.
Hard-coding specific implementations:
- Limits framework adoption
- Forces migration of existing infrastructure
- Prevents innovation and experimentation
## Decision
Go Micro uses a **pluggable architecture** where:
1. Core interfaces define contracts (Registry, Broker, Transport, Store, etc.)
2. Multiple implementations live in the same repository under interface directories
3. Plugins are imported directly and passed via options
4. Default implementations work without any infrastructure
## Structure
```
go-micro/
├── registry/ # Interface definition
│ ├── registry.go
│ ├── mdns.go # Default implementation
│ ├── consul/ # Plugin
│ ├── etcd/ # Plugin
│ └── nats/ # Plugin
├── broker/
├── transport/
└── store/
```
## Consequences
### Positive
- **No version hell**: Plugins versioned with core framework
- **Discovery**: Users browse available plugins in same repo
- **Consistency**: All plugins follow same patterns
- **Testing**: Plugins tested together
- **Zero config**: Default implementations require no setup
### Negative
- **Repo size**: More code in one repository
- **Plugin maintenance**: Core team responsible for plugin quality
- **Breaking changes**: Harder to evolve individual plugins independently
### Neutral
- Plugins can be extracted to separate repos if they grow complex
- Community can contribute plugins via PR
- Plugin-specific issues easier to triage
## Alternatives Considered
### Separate Plugin Repositories
Used by go-kit and other frameworks. Rejected because:
- Version compatibility becomes user's problem
- Discovery requires documentation
- Testing integration harder
- Splitting community
### Single Implementation
Like standard `net/http`. Rejected because:
- Forces infrastructure choices
- Limits adoption
- Can't leverage existing infrastructure
### Dynamic Plugin Loading
Using Go plugins or external processes. Rejected because:
- Complexity for users
- Compatibility issues
- Performance overhead
- Debugging difficulty
## Related
- ADR-002: Interface-First Design (planned)
- ADR-005: Registry Plugin Scope (planned)
@@ -0,0 +1,119 @@
---
layout: default
---
# ADR-004: mDNS as Default Registry
## Status
**Accepted**
## Context
Service discovery is critical for microservices. Common approaches:
1. **Central registry** (Consul, Etcd) - Requires infrastructure
2. **DNS-based** (Kubernetes DNS) - Platform-specific
3. **Static configuration** - Doesn't scale
4. **Multicast DNS (mDNS)** - Zero-config, local network
For local development and getting started, requiring infrastructure setup is a barrier. Production deployments typically have existing service discovery infrastructure.
## Decision
Use **mDNS as the default registry** for service discovery.
- Works immediately on local networks
- No external dependencies
- Suitable for development and simple deployments
- Easily swapped for production registries (Consul, Etcd, Kubernetes)
## Implementation
```go
// Default - uses mDNS automatically
svc := micro.NewService("myservice")
// Production - swap to Consul
reg := consul.NewConsulRegistry()
svc := micro.NewService("myservice",
micro.Registry(reg),
)
```
## Consequences
### Positive
- **Zero setup**: `go run main.go` just works
- **Fast iteration**: No infrastructure for local dev
- **Learning curve**: Newcomers start immediately
- **Progressive complexity**: Add infrastructure as needed
### Negative
- **Local network only**: mDNS doesn't cross subnets/VLANs
- **Not for production**: Needs proper registry in production
- **Port 5353**: May conflict with existing mDNS services
- **Discovery delay**: Can take 1-2 seconds
### Mitigations
- Clear documentation on production alternatives
- Environment variables for easy swapping (`MICRO_REGISTRY=consul`)
- Examples for all major registries
- Health checks and readiness probes for production
## Use Cases
### Good for mDNS
- Local development
- Testing
- Simple internal services on same network
- Learning and prototyping
### Need Production Registry
- Cross-datacenter communication
- Cloud deployments
- Large service mesh (100+ services)
- Require advanced features (health checks, metadata filtering)
## Alternatives Considered
### No Default (Force Configuration)
Rejected because:
- Poor first-run experience
- Increases barrier to entry
- Users must setup infrastructure before trying framework
### Static Configuration
Rejected because:
- Doesn't support dynamic service discovery
- Manual configuration doesn't scale
- Doesn't reflect real microservices usage
### Consul as Default
Rejected because:
- Requires running Consul for "Hello World"
- Platform-specific
- Adds complexity for beginners
## Migration Path
Start with mDNS, migrate to production registry:
```bash
# Development
go run main.go
# Staging
MICRO_REGISTRY=consul MICRO_REGISTRY_ADDRESS=consul:8500 go run main.go
# Production (Kubernetes)
MICRO_REGISTRY=nats MICRO_REGISTRY_ADDRESS=nats://nats:4222 ./service
```
## Related
- [ADR-001: Plugin Architecture](adr-001-plugin-architecture.md)
- [ADR-009: Progressive Configuration](adr-009-progressive-configuration.md)
- [Registry Documentation](../registry.md)
@@ -0,0 +1,152 @@
---
layout: default
---
# ADR-009: Progressive Configuration
## Status
**Accepted**
## Context
Microservices frameworks face a paradox:
- Beginners want "Hello World" to work immediately
- Production needs sophisticated configuration
Too simple: Framework is toy, not production-ready
Too complex: High barrier to entry, discourages adoption
## Decision
Implement **progressive configuration** where:
1. **Zero config** works for development
2. **Environment variables** provide simple overrides
3. **Code-based options** enable fine-grained control
4. **Defaults are production-aware** but not production-ready
## Levels of Configuration
### Level 1: Zero Config (Development)
```go
svc := micro.NewService("hello")
svc.Run()
```
Uses defaults:
- mDNS registry (local)
- HTTP transport
- Random available port
- Memory broker/store
### Level 2: Environment Variables (Staging)
```bash
MICRO_REGISTRY=consul \
MICRO_REGISTRY_ADDRESS=consul:8500 \
MICRO_BROKER=nats \
MICRO_BROKER_ADDRESS=nats://nats:4222 \
./service
```
No code changes, works with CLI flags.
### Level 3: Code Options (Production)
```go
reg := consul.NewConsulRegistry(
registry.Addrs("consul1:8500", "consul2:8500"),
registry.TLSConfig(tlsConf),
)
b := nats.NewNatsBroker(
broker.Addrs("nats://nats1:4222", "nats://nats2:4222"),
nats.DrainConnection(),
)
svc := micro.NewService("myservice",
micro.Version("1.2.3"),
micro.Registry(reg),
micro.Broker(b),
micro.Address(":8080"),
)
```
Full control over initialization and configuration.
### Level 4: External Config (Enterprise)
```go
cfg := config.NewConfig(
config.Source(file.NewSource("config.yaml")),
config.Source(env.NewSource()),
config.Source(vault.NewSource()),
)
// Use cfg to initialize plugins with complex configs
```
## Environment Variable Patterns
Standard vars for all plugins:
```bash
MICRO_REGISTRY=<type> # consul, etcd, nats, mdns
MICRO_REGISTRY_ADDRESS=<addrs> # Comma-separated
MICRO_BROKER=<type>
MICRO_BROKER_ADDRESS=<addrs>
MICRO_TRANSPORT=<type>
MICRO_TRANSPORT_ADDRESS=<addrs>
MICRO_STORE=<type>
MICRO_STORE_ADDRESS=<addrs>
MICRO_STORE_DATABASE=<name>
MICRO_STORE_TABLE=<name>
```
Plugin-specific vars:
```bash
ETCD_USERNAME=user
ETCD_PASSWORD=pass
CONSUL_TOKEN=secret
```
## Consequences
### Positive
- **Fast start**: Beginners productive immediately
- **Easy deployment**: Env vars for different environments
- **Power when needed**: Full programmatic control available
- **Learn incrementally**: Complexity introduced as required
### Negative
- **Three config sources**: Environment, code, and CLI flags can conflict
- **Documentation**: Must explain all levels clearly
- **Testing**: Need to test all configuration methods
### Mitigations
- Clear precedence: Code options > Environment > Defaults
- Comprehensive examples for each level
- Validation and helpful error messages
## Validation Example
```go
func (s *service) Init() error {
if s.opts.Name == "" {
return errors.New("service name required")
}
// Warn about development defaults in production
if isProduction() && usingDefaults() {
log.Warn("Using development defaults in production")
}
return nil
}
```
## Related
- [ADR-004: mDNS as Default Registry](adr-004-mdns-default-registry.md)
- ADR-008: Environment Variable Support (planned)
- [Getting Started Guide](../getting-started.md) - Configuration examples
- [Configuration Guide](../config.md)
@@ -0,0 +1,180 @@
# ADR-010: Unified Gateway Architecture
**Status:** Accepted
**Date:** 2026-02-11
**Authors:** Go Micro Team
## Context
Previously, the go-micro CLI had two separate gateway implementations:
1. **`micro run`** gateway (`cmd/micro/run/gateway/`) - Simple HTTP-to-RPC proxy for development
2. **`micro server`** gateway (`cmd/micro/server/`) - Production gateway with authentication, web UI, and API documentation
This duplication created several problems:
- **Code maintenance**: Gateway logic (HTTP-to-RPC translation, service discovery, health checks) was implemented twice
- **Feature parity**: Improvements to one gateway didn't automatically benefit the other
- **Complexity**: New features (like MCP integration) would need to be implemented twice
- **Testing burden**: Each gateway required separate testing
## Decision
We unified the gateway implementation by:
1. **Extracting reusable gateway module** (`cmd/micro/server/gateway.go`):
- `GatewayOptions` struct for configuration
- `StartGateway()` function that returns a `*Gateway` immediately
- `RunGateway()` function that blocks until shutdown
- Configurable authentication (enabled/disabled)
2. **Refactoring `micro server`**:
- Gateway logic remains in `cmd/micro/server/`
- `registerHandlers()` now uses instance-specific `*http.ServeMux` instead of global mux
- Authentication middleware is conditional based on `GatewayOptions.AuthEnabled`
- Auth routes only register when authentication is enabled
3. **Updating `micro run`**:
- Removed duplicate gateway implementation (`cmd/micro/run/gateway/`)
- Now calls `server.StartGateway()` with `AuthEnabled: true`
- Retains process management and hot reload functionality
- Same auth, scopes, and token management as `micro server`
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Unified Gateway │
│ (cmd/micro/server/gateway.go) │
│ │
│ • HTTP → RPC translation │
│ • Service discovery via registry │
│ • Web UI (dashboard, logs, API docs) │
│ • Health checks │
│ • Configurable authentication │
│ • Endpoint scopes for access control │
│ • MCP tool integration with scope enforcement │
└─────────────────────────────────────────────────────────────┘
▲ ▲
│ │
┌──────┴──────┐ ┌────────┴────────┐
│ micro run │ │ micro server │
│ │ │ │
│ + Process │ │ + Auth enabled │
│ mgmt │ │ + JWT tokens │
│ + Hot │ │ + Scopes │
│ reload │ │ + Production │
│ + Auth │ │ │
│ + Scopes │ │ │
└─────────────┘ └─────────────────┘
```
## Usage
### Development Mode (`micro run`)
```bash
# Start services with gateway (auth enabled, default admin/micro)
micro run
# Gateway provides:
# - HTTP API at /api/{service}/{endpoint}
# - Web dashboard at /
# - JWT authentication (admin/micro default)
# - Endpoint scopes at /auth/scopes
```
### Production Mode (`micro server`)
```bash
# Start gateway with authentication
micro server --address :8080
# Gateway provides:
# - HTTP API at /api/{service}/{endpoint} (auth required)
# - Web dashboard with login
# - JWT-based authentication
# - User/token management UI
# - Endpoint scopes at /auth/scopes
```
## Benefits
1. **Single Source of Truth**: Gateway logic lives in one place
2. **Automatic Feature Propagation**: New features (like MCP) added to the unified gateway benefit both commands
3. **Simplified Testing**: Test gateway once, works everywhere
4. **Reduced Code Size**: Eliminated ~300 lines of duplicate code
5. **Clear Separation**:
- `micro server` = API gateway (HTTP + future MCP)
- `micro run` = Development tool (gateway + process management + hot reload)
## Implementation Details
### GatewayOptions
```go
type GatewayOptions struct {
Address string // Listen address (e.g., ":8080")
AuthEnabled bool // Enable JWT authentication
Store store.Store // Storage for auth data
Context context.Context // Cancellation context
}
```
### Starting the Gateway
```go
// Non-blocking start
gw, err := server.StartGateway(server.GatewayOptions{
Address: ":8080",
AuthEnabled: false,
})
// Blocking start
err := server.RunGateway(server.GatewayOptions{
Address: ":8080",
AuthEnabled: true,
})
```
### Authentication
When `AuthEnabled: true`:
- Auth middleware checks JWT tokens on all requests
- Auth routes are registered: `/auth/login`, `/auth/logout`, `/auth/tokens`, `/auth/users`
- Web UI requires login
- API endpoints require `Authorization: Bearer <token>` header
When `AuthEnabled: false` (dev mode):
- No authentication middleware
- Auth routes are not registered
- All endpoints are publicly accessible
## Consequences
### Positive
- Easier to add new features (only implement once)
- Better code maintainability
- Consistent behavior between development and production
- Foundation for MCP integration
### Negative
- `cmd/micro/run` now depends on `cmd/micro/server` (acceptable for CLI tools)
- Slightly more complex initialization in `micro run` (but cleaner overall)
## Future Work
With unified gateway architecture, we can now add:
1. **MCP Integration**: Add `mcp.go` to server package, both commands get MCP support
2. **GraphQL API**: Single implementation serves both dev and prod
3. **gRPC Gateway**: Expose services via gRPC alongside HTTP
4. **API Versioning**: Consistent versioning strategy across all deployments
## References
- Original issue: Gateway duplication between `micro run` and `micro server`
- Implementation: PR #XXX (gateway unification)
- Related: ADR-001 (Plugin Architecture), ADR-009 (Progressive Configuration)
@@ -0,0 +1,37 @@
---
layout: default
---
# ADR-XXX: Title
Status: Proposed
Date: YYYY-MM-DD
## Context
Describe the problem, forces, and constraints leading to the decision.
## Decision
State the decision clearly and precisely.
## Consequences
Positive and negative outcomes, trade-offs introduced by this decision.
## Alternatives Considered
1. Alternative A - why rejected
2. Alternative B - why rejected
## Implementation Notes
High-level steps or rollout plan if accepted.
## Related
- Link other ADRs, documentation, or issues.
## References
External resources, prior art, research.
@@ -0,0 +1,53 @@
---
layout: default
---
# Architecture Decision Records
Documentation of architectural decisions made in Go Micro, following the ADR pattern.
## What are ADRs?
Architecture Decision Records (ADRs) capture important architectural decisions along with their context and consequences. They help understand why certain design choices were made.
## Index
### Available
- [ADR-001: Plugin Architecture](adr-001-plugin-architecture.md)
- [ADR-004: mDNS as Default Registry](adr-004-mdns-default-registry.md)
- [ADR-009: Progressive Configuration](adr-009-progressive-configuration.md)
### Planned
**Core Design**
- ADR-002: Interface-First Design
- ADR-003: Default Implementations
**Service Discovery**
- ADR-005: Registry Plugin Scope
**Communication**
- ADR-006: HTTP as Default Transport
- ADR-007: Content-Type Based Codecs
**Configuration**
- ADR-008: Environment Variable Support
## Status Values
- **Proposed**: Under consideration
- **Accepted**: Decision approved
- **Deprecated**: No longer recommended
- **Superseded**: Replaced by another ADR
## Contributing
To propose a new ADR:
1. Number it sequentially (check existing ADRs)
2. Follow the structure of existing ADRs
3. Include: Status, Context, Decision, Consequences, Alternatives
4. Submit a PR for discussion
5. Update status based on review
ADRs are immutable once accepted. To change a decision, create a new ADR that supersedes the old one.
+111
View File
@@ -0,0 +1,111 @@
---
layout: default
---
# Broker
<img src="/images/generated/broker.jpg" alt="Broker" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
The broker provides pub/sub messaging for Go Micro services.
## Features
- Publish messages to topics
- Subscribe to topics
- Multiple broker implementations
## Implementations
Supported brokers include:
- HTTP (default)
- NATS (`go-micro.dev/v6/broker/nats`)
- RabbitMQ (`go-micro.dev/v6/broker/rabbitmq`)
- Memory (`go-micro.dev/v6/broker/memory`)
Plugins are scoped under `go-micro.dev/v6/broker/<plugin>`.
Configure the broker in code or via environment variables.
## Example Usage
Here's how to use the broker in your Go Micro service:
```go
package main
import (
"go-micro.dev/v6"
"go-micro.dev/v6/broker"
"log"
)
func main() {
service := micro.NewService("publisher")
service.Init()
// Publish a message
if err := broker.Publish("topic", &broker.Message{Body: []byte("hello world")}); err != nil {
log.Fatal(err)
}
// Subscribe to a topic
_, err := broker.Subscribe("topic", func(p broker.Event) error {
log.Printf("Received message: %s", string(p.Message().Body))
return nil
})
if err != nil {
log.Fatal(err)
}
// Run the service
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
```
## Configure a specific broker in code
NATS:
```go
import (
"go-micro.dev/v6"
bnats "go-micro.dev/v6/broker/nats"
)
func main() {
b := bnats.NewNatsBroker()
svc := micro.NewService("publisher", micro.Broker(b))
svc.Init()
svc.Run()
}
```
RabbitMQ:
```go
import (
"go-micro.dev/v6"
"go-micro.dev/v6/broker/rabbitmq"
)
func main() {
b := rabbitmq.NewBroker()
svc := micro.NewService("publisher", micro.Broker(b))
svc.Init()
svc.Run()
}
```
## Configure via environment
Using the built-in configuration flags/env vars (no code changes):
```bash
MICRO_BROKER=nats MICRO_BROKER_ADDRESS=nats://127.0.0.1:4222 go run main.go
```
Common variables:
- `MICRO_BROKER`: selects the broker implementation (`http`, `nats`, `rabbitmq`, `memory`).
- `MICRO_BROKER_ADDRESS`: comma-separated list of broker addresses.
Notes:
- NATS addresses should be prefixed with `nats://`.
- RabbitMQ addresses typically use `amqp://user:pass@host:5672`.
+46
View File
@@ -0,0 +1,46 @@
---
layout: default
---
# Client/Server
Go Micro uses a client/server model for RPC communication between services.
## Client
The client is used to make requests to other services.
## Server
The server handles incoming requests.
Both client and server are pluggable and support middleware wrappers for additional functionality.
## Example Usage
Here's how to define a simple handler and register it with a Go Micro server:
```go
package main
import (
"context"
"go-micro.dev/v6"
"log"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *struct{}, rsp *struct{Msg string}) error {
rsp.Msg = "Hello, world!"
return nil
}
func main() {
service := micro.NewService("greeter",
)
service.Init()
micro.RegisterHandler(service.Server(), new(Greeter))
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
```
+129
View File
@@ -0,0 +1,129 @@
---
layout: default
---
# Configuration
<img src="/images/generated/config.jpg" alt="Configuration" style="width: 100%; border-radius: 8px; margin: 1rem 0 1.5rem;" />
Go Micro follows a progressive configuration model so you can start with zero setup and layer in complexity only when needed.
## Levels of Configuration
1. Zero Config (Defaults)
- mDNS registry, HTTP transport, in-memory broker/store
2. Environment Variables
- Override core components without code changes
3. Code Options
- Fine-grained control via functional options
4. External Sources (Future / Plugins)
- Configuration loaded from files, vaults, or remote services
## Core Environment Variables
| Component | Variable | Example | Purpose |
|-----------|----------|---------|---------|
| Registry | `MICRO_REGISTRY` | `MICRO_REGISTRY=consul` | Select registry implementation |
| Registry Address | `MICRO_REGISTRY_ADDRESS` | `MICRO_REGISTRY_ADDRESS=127.0.0.1:8500` | Point to registry service |
| Broker | `MICRO_BROKER` | `MICRO_BROKER=nats` | Select broker implementation |
| Broker Address | `MICRO_BROKER_ADDRESS` | `MICRO_BROKER_ADDRESS=nats://localhost:4222` | Broker endpoint |
| Transport | `MICRO_TRANSPORT` | `MICRO_TRANSPORT=nats` | Select transport implementation |
| Transport Address | `MICRO_TRANSPORT_ADDRESS` | `MICRO_TRANSPORT_ADDRESS=nats://localhost:4222` | Transport endpoint |
| Store | `MICRO_STORE` | `MICRO_STORE=postgres` | Select store implementation |
| Store Database | `MICRO_STORE_DATABASE` | `MICRO_STORE_DATABASE=app` | Logical database name |
| Store Table | `MICRO_STORE_TABLE` | `MICRO_STORE_TABLE=records` | Default table/collection |
| Store Address | `MICRO_STORE_ADDRESS` | `MICRO_STORE_ADDRESS=postgres://user:pass@localhost:5432/app?sslmode=disable` | Connection string |
| Server Address | `MICRO_SERVER_ADDRESS` | `MICRO_SERVER_ADDRESS=:8080` | Bind address for RPC server |
## Example: Switching Components via Env Vars
```bash
# Use NATS for broker and transport, Consul for registry
export MICRO_BROKER=nats
export MICRO_TRANSPORT=nats
export MICRO_REGISTRY=consul
export MICRO_REGISTRY_ADDRESS=127.0.0.1:8500
# Run your service
go run main.go
```
No code changes required. The framework internally wires the selected implementations.
## Equivalent Code Configuration
```go
service := micro.NewService("helloworld",
micro.Broker(nats.NewBroker()),
micro.Transport(natstransport.NewTransport()),
micro.Registry(consul.NewRegistry(registry.Addrs("127.0.0.1:8500"))),
)
service.Init()
```
Use env vars for deployment level overrides; use code options for explicit control or when composing advanced setups.
## Precedence Rules
1. Explicit code options always win
2. If not set in code, env vars are applied
3. If neither code nor env vars set, defaults are used
## Discoverability Strategy
Defaults allow local development with zero friction. As teams scale:
- Introduce env vars for staging/production parity
- Consolidate secrets (e.g. store passwords) using external secret managers (future guide)
- Move to service mesh aware registry (Consul/NATS JetStream)
## Validating Configuration
Enable debug logging to confirm selected components:
```bash
MICRO_LOG_LEVEL=debug go run main.go
```
You will see lines like:
```text
Registry [consul] Initialised
Broker [nats] Connected
Transport [nats] Listening on nats://localhost:4222
Store [postgres] Connected to app/records
```
## Patterns
### Twelve-Factor Alignment
Environment variables map directly to deploy-time configuration. Avoid hardcoding component choices so services remain portable.
### Multi-Environment Setup
Use a simple env file per environment:
```bash
# .env.staging
MICRO_REGISTRY=consul
MICRO_REGISTRY_ADDRESS=consul.staging.internal:8500
MICRO_BROKER=nats
MICRO_BROKER_ADDRESS=nats.staging.internal:4222
MICRO_STORE=postgres
MICRO_STORE_ADDRESS=postgres://staging:pass@pg.staging.internal:5432/app?sslmode=disable
```
Load with your process manager or container orchestrator.
## Troubleshooting
| Symptom | Cause | Fix |
|---------|-------|-----|
| Service starts with memory store unexpectedly | Env vars not exported | `env | grep MICRO_STORE` to verify |
| Consul errors about connection refused | Wrong address/port | Check `MICRO_REGISTRY_ADDRESS` value |
| NATS connection timeout | Server not running | Start NATS or change address |
| Postgres SSL errors | Missing sslmode param | Append `?sslmode=disable` locally |
## Related
- [ADR-009: Progressive Configuration](architecture/adr-009-progressive-configuration.md)
- [Getting Started](getting-started.html)
- [Plugins](plugins.html)
+65
View File
@@ -0,0 +1,65 @@
---
layout: default
---
# Contributing
This is a rendered copy of the repository `CONTRIBUTING.md` for convenient access via the documentation site.
## Overview
Go Micro welcomes contributions of all kinds: code, documentation, examples, and plugins.
## Quick Start
```bash
git clone https://github.com/micro/go-micro.git
cd go-micro
go mod download
go test ./...
```
## Process
1. Fork and create a feature branch
2. Make focused changes with tests
3. Run linting and full test suite
4. Open a PR describing motivation and approach
## Commit Format
Use conventional commits:
```
feat(registry): add consul health check
fix(broker): prevent reconnect storm
```
## Testing
Run unit tests:
```bash
go test ./...
```
Run race/coverage:
```bash
go test -race -coverprofile=coverage.out ./...
```
## Plugins
Place new plugins under the appropriate interface directory (e.g. `registry/consul/`). Include tests and usage examples. Document env vars and options.
## Documentation
Docs live in `internal/website/docs/`. Add new examples under `internal/website/docs/examples/`.
## Help & Questions
Use GitHub Discussions or the issue templates. For general usage questions open a "Question" issue.
## Full Guide
For complete details see the repository copy of the guide on GitHub.
- View on GitHub: https://github.com/micro/go-micro/blob/master/CONTRIBUTING.md
+379
View File
@@ -0,0 +1,379 @@
---
layout: default
title: Deployment
---
# Deploying Go Micro Services
<img src="/images/generated/deployment.jpg" alt="Go Micro deployment" style="width: 100%; border-radius: 8px; margin-bottom: 1.5rem;" />
This guide covers deploying go-micro services to a Linux server using systemd.
## Overview
go-micro provides a clear workflow from development to production:
| Stage | Command | Purpose |
|-------|---------|---------|
| **Develop** | `micro run` | Local dev with hot reload and API gateway |
| **Build** | `micro build` | Compile production binaries for any target OS |
| **Deploy** | `micro deploy` | Push binaries to a remote Linux server via SSH + systemd |
| **Dashboard** | `micro server` | Optional production web UI with JWT auth and user management |
Each command has a distinct role — they don't overlap:
- **`micro run`** builds, runs, and watches services locally. It includes a lightweight gateway. Use it for development.
- **`micro build`** compiles binaries without running them. Use it to prepare release artifacts.
- **`micro deploy`** sends binaries to a remote server and manages them with systemd. It builds automatically if needed.
- **`micro server`** provides an authenticated web dashboard for services that are already running. It does NOT build or run services.
## Quick Start
### 1. Prepare Your Server
On your server (Ubuntu, Debian, or any systemd-based Linux):
```bash
# Install micro
curl -fsSL https://go-micro.dev/install.sh | sh
# Initialize for deployment
sudo micro init --server
```
This creates:
- `/opt/micro/bin/` - where service binaries live
- `/opt/micro/data/` - persistent data directory
- `/opt/micro/config/` - environment files
- systemd template for managing services
### 2. Deploy from Your Machine
```bash
# From your project directory
micro deploy user@your-server
```
That's it! The deploy command:
1. Builds your services for Linux
2. Copies binaries to the server
3. Configures and starts systemd services
4. Verifies everything is running
## Detailed Setup
### Server Requirements
- Linux with systemd (Ubuntu 16.04+, Debian 8+, CentOS 7+, etc.)
- SSH access
- Go installed (only if building on server)
### Server Initialization Options
```bash
# Basic setup (creates 'micro' user)
sudo micro init --server
# Custom installation path
sudo micro init --server --path /home/deploy/micro
# Run services as existing user
sudo micro init --server --user deploy
# Initialize remotely (from your laptop)
micro init --server --remote user@your-server
```
### What Gets Created
**Directories:**
```
/opt/micro/
├── bin/ # Service binaries
├── data/ # Persistent data (databases, files)
└── config/ # Environment files (*.env)
```
**Systemd Template** (`/etc/systemd/system/micro@.service`):
```ini
[Unit]
Description=Micro service: %i
After=network.target
[Service]
Type=simple
User=micro
WorkingDirectory=/opt/micro
ExecStart=/opt/micro/bin/%i
Restart=on-failure
RestartSec=5
EnvironmentFile=-/opt/micro/config/%i.env
[Install]
WantedBy=multi-user.target
```
The `%i` is replaced with the service name. So `micro@users.service` runs `/opt/micro/bin/users`.
## Deployment
### Basic Deploy
```bash
micro deploy user@server
```
### Deploy Specific Service
```bash
micro deploy user@server --service users
```
### Force Rebuild
```bash
micro deploy user@server --build
```
### Named Deploy Targets
Add to your `micro.mu`:
```
service users
path ./users
port 8081
service web
path ./web
port 8080
deploy prod
ssh deploy@prod.example.com
deploy staging
ssh deploy@staging.example.com
```
Then:
```bash
micro deploy prod # deploys to prod.example.com
micro deploy staging # deploys to staging.example.com
```
## Managing Services
### Check Status
```bash
# Local services
micro status
# Remote services
micro status --remote user@server
```
Output:
```
server.example.com
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
users ● running pid 1234
posts ● running pid 1235
web ● running pid 1236
```
### View Logs
```bash
# All services
micro logs --remote user@server
# Specific service
micro logs users --remote user@server
# Follow logs
micro logs users --remote user@server -f
```
### Stop Services
```bash
micro stop users --remote user@server
```
### Direct systemctl Access
You can also manage services directly on the server:
```bash
# Status
sudo systemctl status micro@users
# Restart
sudo systemctl restart micro@users
# Stop
sudo systemctl stop micro@users
# Logs
journalctl -u micro@users -f
```
## Environment Variables
Create environment files at `/opt/micro/config/<service>.env`:
```bash
# /opt/micro/config/users.env
DATABASE_URL=postgres://localhost/users
REDIS_URL=redis://localhost:6379
LOG_LEVEL=info
```
These are automatically loaded by systemd when the service starts.
## SSH Setup
### Key-Based Authentication
```bash
# Generate key (if you don't have one)
ssh-keygen -t ed25519
# Copy to server
ssh-copy-id user@server
```
### SSH Config
Add to `~/.ssh/config`:
```
Host prod
HostName prod.example.com
User deploy
IdentityFile ~/.ssh/deploy_key
Host staging
HostName staging.example.com
User deploy
IdentityFile ~/.ssh/deploy_key
```
Then deploy with:
```bash
micro deploy prod
```
## Troubleshooting
### "Cannot connect to server"
```
✗ Cannot connect to myserver
SSH connection failed. Check that:
• The server is reachable: ping myserver
• SSH is configured: ssh user@myserver
• Your key is added: ssh-add -l
```
**Fix:**
```bash
# Test SSH connection
ssh user@server
# Add SSH key
ssh-copy-id user@server
# Check SSH agent
eval $(ssh-agent)
ssh-add
```
### "Server not initialized"
```
✗ Server not initialized
micro is not set up on myserver.
```
**Fix:**
```bash
ssh user@server 'sudo micro init --server'
```
### "Service failed to start"
Check the logs:
```bash
micro logs myservice --remote user@server
# Or on the server:
journalctl -u micro@myservice -n 50
```
Common causes:
- Missing environment variables
- Port already in use
- Database not reachable
- Binary permissions issue
### "Permission denied"
Ensure your user can write to `/opt/micro/bin/`:
```bash
# On server
sudo chown -R deploy:deploy /opt/micro
# Or add user to micro group
sudo usermod -aG micro deploy
```
## Security Best Practices
1. **Use a dedicated deploy user** - Don't deploy as root
2. **Use SSH keys** - Disable password authentication
3. **Restrict sudo** - Only allow necessary commands
4. **Firewall** - Only expose needed ports
5. **Secrets** - Use environment files with restricted permissions (0600)
### Minimal sudo access
Add to `/etc/sudoers.d/micro`:
```
deploy ALL=(ALL) NOPASSWD: /bin/systemctl daemon-reload
deploy ALL=(ALL) NOPASSWD: /bin/systemctl enable micro@*
deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart micro@*
deploy ALL=(ALL) NOPASSWD: /bin/systemctl stop micro@*
deploy ALL=(ALL) NOPASSWD: /bin/systemctl status micro@*
```
## Production Dashboard (Optional)
Once services are deployed and managed by systemd, you can optionally run `micro server` on the same machine to get a full web dashboard with authentication:
```bash
# On your server
micro server
```
This gives you:
- **Web Dashboard** at http://your-server:8080 with JWT authentication
- **API Gateway** with authenticated HTTP-to-RPC proxy
- **User Management** — create accounts, generate/revoke API tokens
- **Logs & Status** — view service logs and uptime from the browser
The server discovers services via the registry automatically. Default login: `admin` / `micro`.
See the [micro server documentation](server.html) for details.
## Next Steps
- [micro run](guides/micro-run.html) - Local development
- [micro server](server.html) - Production web dashboard with auth
- [micro.mu configuration](guides/micro-run.md#configuration-file) - Configuration file format
- [Health checks](guides/health.html) - Service health endpoints
@@ -0,0 +1,60 @@
---
layout: default
---
# Hello Service
A minimal HTTP service using Go Micro, with a single endpoint.
## Service
```go
package main
import (
"context"
"go-micro.dev/v6"
)
type Request struct { Name string `json:"name"` }
type Response struct { Message string `json:"message"` }
type Say struct{}
func (h *Say) Hello(ctx context.Context, req *Request, rsp *Response) error {
rsp.Message = "Hello " + req.Name
return nil
}
func main() {
svc := micro.NewService("helloworld")
svc.Init()
svc.Handle(new(Say))
svc.Run()
}
```
Run it:
```bash
go run main.go
```
Call it:
```bash
curl -XPOST \
-H 'Content-Type: application/json' \
-H 'Micro-Endpoint: Say.Hello' \
-d '{"name": "Alice"}' \
http://127.0.0.1:8080
```
Set a fixed address:
```go
svc := micro.NewService("helloworld",
micro.Address(":8080"),
)
```
+62
View File
@@ -0,0 +1,62 @@
---
layout: default
---
# Learn by Example
Runnable examples are the fastest way to move from reading the guides to changing
one thing. Start with the path that matches where you are in the services →
agents → workflows lifecycle.
## Start here
For the provider-free first-agent route, run [`examples/first-agent`](https://github.com/micro/go-micro/tree/master/examples/first-agent), then follow [No-secret First Agent](../guides/no-secret-first-agent.html), [Your First Agent](../guides/your-first-agent.html), [Debugging your agent](../guides/debugging-agents.html), and the [0→hero Reference](../guides/zero-to-hero.html).
| Goal | Runnable example | Why it is useful |
| --- | --- | --- |
| 0→1 service | [`examples/hello-world`](https://github.com/micro/go-micro/tree/master/examples/hello-world) | Smallest RPC service with a client call and health checks. |
| Provider-free first agent | [`examples/first-agent`](https://github.com/micro/go-micro/tree/master/examples/first-agent) | Smallest service-backed agent with a deterministic mock model; no provider key required. |
| First service-backed agent | [`examples/agent-demo`](https://github.com/micro/go-micro/tree/master/examples/agent-demo) | Multi-service project/task/team app with agent playground integration. |
| 0→hero lifecycle | [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support) | No-secret support-desk story: typed services, an agent, an event-driven flow, and a guardrail. |
| Planning and delegation | [`examples/agent-plan-delegate`](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate) | Two agents collaborate through `plan` and `delegate` over normal Go Micro RPC. |
| Durable agent runs | [`examples/agent-durable`](https://github.com/micro/go-micro/tree/master/examples/agent-durable) | Checkpoint and resume a model-directed run without replaying completed tool side effects. |
| Durable workflows | [`examples/flow-durable`](https://github.com/micro/go-micro/tree/master/examples/flow-durable) | Ordered, checkpointed flow steps resume without duplicating completed side effects. |
| AI-callable services | [`examples/mcp`](https://github.com/micro/go-micro/tree/master/examples/mcp) | MCP examples that expose service endpoints as model tools. |
## Guide-to-example map
- [Getting Started](../getting-started.html) → run
[`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support)
to see the full lifecycle before generating your own service.
- [No-secret First Agent](../guides/no-secret-first-agent.html) → run
[`examples/first-agent`](https://github.com/micro/go-micro/tree/master/examples/first-agent)
first for the smallest provider-free agent transcript.
- [Your First Agent](../guides/your-first-agent.html) → run
[`examples/agent-demo`](https://github.com/micro/go-micro/tree/master/examples/agent-demo)
or [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support)
when you want a complete service-backed agent to inspect.
- [Debugging your agent](../guides/debugging-agents.html) → keep
[`examples/first-agent`](https://github.com/micro/go-micro/tree/master/examples/first-agent)
nearby as the smallest mock-model reproduction before inspecting richer runs.
- [0→hero Reference](../guides/zero-to-hero.html) → run
[`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support)
for the human-readable scenario, then `make harness` for the full CI contract.
- [Plan & Delegate](../guides/plan-delegate.html) → run
[`examples/agent-plan-delegate`](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate).
- [Agents and Workflows](../guides/agents-and-workflows.html) → run
[`examples/flow-durable`](https://github.com/micro/go-micro/tree/master/examples/flow-durable)
for deterministic checkpointed steps,
[`examples/agent-durable`](https://github.com/micro/go-micro/tree/master/examples/agent-durable)
for model-directed checkpointed runs, and
[`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support)
for the full services → agents → workflows lifecycle.
## Repository examples
See the repository [examples index](https://github.com/micro/go-micro/tree/master/examples)
for the complete runnable list, including deployment, auth, gRPC interop, MCP,
agent, and flow examples.
## More
- [Real-World Examples](realworld/index.md)
@@ -0,0 +1,45 @@
---
layout: default
---
# Pub/Sub with NATS Broker
Use the NATS broker for pub/sub.
## In code
```go
package main
import (
"log"
"go-micro.dev/v6"
"go-micro.dev/v6/broker"
bnats "go-micro.dev/v6/broker/nats"
)
func main() {
b := bnats.NewNatsBroker()
svc := micro.NewService("nats-pubsub", micro.Broker(b))
svc.Init()
// subscribe
_, _ = broker.Subscribe("events", func(e broker.Event) error {
log.Printf("received: %s", string(e.Message().Body))
return nil
})
// publish
_ = broker.Publish("events", &broker.Message{Body: []byte("hello")})
svc.Run()
}
```
## Via environment
Run your service with env vars set:
```bash
MICRO_BROKER=nats MICRO_BROKER_ADDRESS=nats://127.0.0.1:4222 go run main.go
```
@@ -0,0 +1,387 @@
---
layout: default
---
# API Gateway with Backend Services
A complete example showing an API gateway routing to multiple backend microservices.
## Architecture
```
┌─────────────┐
Client ───────>│ API Gateway │
└──────┬──────┘
┌──────────────┼──────────────┐
│ │ │
┌─────▼────┐ ┌────▼─────┐ ┌────▼─────┐
│ Users │ │ Orders │ │ Products │
│ Service │ │ Service │ │ Service │
└──────────┘ └──────────┘ └──────────┘
│ │ │
└──────────────┼──────────────┘
┌──────▼──────┐
│ PostgreSQL │
└─────────────┘
```
## Services
### 1. Users Service
```go
// services/users/main.go
package main
import (
"context"
"database/sql"
"go-micro.dev/v6"
"go-micro.dev/v6/server"
_ "github.com/lib/pq"
)
type User struct {
ID int64 `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
}
type UsersService struct {
db *sql.DB
}
type GetUserRequest struct {
ID int64 `json:"id"`
}
type GetUserResponse struct {
User *User `json:"user"`
}
func (s *UsersService) Get(ctx context.Context, req *GetUserRequest, rsp *GetUserResponse) error {
var u User
err := s.db.QueryRow("SELECT id, email, name FROM users WHERE id = $1", req.ID).
Scan(&u.ID, &u.Email, &u.Name)
if err != nil {
return err
}
rsp.User = &u
return nil
}
func main() {
db, err := sql.Open("postgres", "postgres://user:pass@localhost/users?sslmode=disable")
if err != nil {
panic(err)
}
defer db.Close()
svc := micro.NewService("users",
micro.Version("1.0.0"),
)
svc.Init()
server.RegisterHandler(svc.Server(), &UsersService{db: db})
if err := svc.Run(); err != nil {
panic(err)
}
}
```
### 2. Orders Service
```go
// services/orders/main.go
package main
import (
"context"
"database/sql"
"time"
"go-micro.dev/v6"
"go-micro.dev/v6/client"
"go-micro.dev/v6/server"
)
type Order struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
ProductID int64 `json:"product_id"`
Amount float64 `json:"amount"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
}
type OrdersService struct {
db *sql.DB
client client.Client
}
type CreateOrderRequest struct {
UserID int64 `json:"user_id"`
ProductID int64 `json:"product_id"`
Amount float64 `json:"amount"`
}
type CreateOrderResponse struct {
Order *Order `json:"order"`
}
func (s *OrdersService) Create(ctx context.Context, req *CreateOrderRequest, rsp *CreateOrderResponse) error {
// Verify user exists
userReq := s.client.NewRequest("users", "UsersService.Get", &struct{ ID int64 }{ID: req.UserID})
userRsp := &struct{ User interface{} }{}
if err := s.client.Call(ctx, userReq, userRsp); err != nil {
return err
}
// Verify product exists
prodReq := s.client.NewRequest("products", "ProductsService.Get", &struct{ ID int64 }{ID: req.ProductID})
prodRsp := &struct{ Product interface{} }{}
if err := s.client.Call(ctx, prodReq, prodRsp); err != nil {
return err
}
// Create order
var o Order
err := s.db.QueryRow(`
INSERT INTO orders (user_id, product_id, amount, status, created_at)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, user_id, product_id, amount, status, created_at
`, req.UserID, req.ProductID, req.Amount, "pending", time.Now()).
Scan(&o.ID, &o.UserID, &o.ProductID, &o.Amount, &o.Status, &o.CreatedAt)
if err != nil {
return err
}
rsp.Order = &o
return nil
}
func main() {
db, err := sql.Open("postgres", "postgres://user:pass@localhost/orders?sslmode=disable")
if err != nil {
panic(err)
}
defer db.Close()
svc := micro.NewService("orders",
micro.Version("1.0.0"),
)
svc.Init()
server.RegisterHandler(svc.Server(), &OrdersService{
db: db,
client: svc.Client(),
})
if err := svc.Run(); err != nil {
panic(err)
}
}
```
### 3. API Gateway
```go
// gateway/main.go
package main
import (
"encoding/json"
"net/http"
"strconv"
"go-micro.dev/v6"
"go-micro.dev/v6/client"
)
type Gateway struct {
client client.Client
}
func (g *Gateway) GetUser(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
http.Error(w, "invalid id", http.StatusBadRequest)
return
}
req := g.client.NewRequest("users", "UsersService.Get", &struct{ ID int64 }{ID: id})
rsp := &struct{ User interface{} }{}
if err := g.client.Call(r.Context(), req, rsp); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(rsp)
}
func (g *Gateway) CreateOrder(w http.ResponseWriter, r *http.Request) {
var body struct {
UserID int64 `json:"user_id"`
ProductID int64 `json:"product_id"`
Amount float64 `json:"amount"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, "invalid request", http.StatusBadRequest)
return
}
req := g.client.NewRequest("orders", "OrdersService.Create", body)
rsp := &struct{ Order interface{} }{}
if err := g.client.Call(r.Context(), req, rsp); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(rsp)
}
func main() {
svc := micro.NewService("api.gateway",
)
svc.Init()
gw := &Gateway{client: svc.Client()}
http.HandleFunc("/users", gw.GetUser)
http.HandleFunc("/orders", gw.CreateOrder)
http.ListenAndServe(":8080", nil)
}
```
## Running the Example
### Development (Local)
```bash
# Terminal 1: Users service
cd services/users
go run main.go
# Terminal 2: Products service
cd services/products
go run main.go
# Terminal 3: Orders service
cd services/orders
go run main.go
# Terminal 4: API Gateway
cd gateway
go run main.go
```
### Testing
```bash
# Get user
curl http://localhost:8080/users?id=1
# Create order
curl -X POST http://localhost:8080/orders \
-H 'Content-Type: application/json' \
-d '{"user_id": 1, "product_id": 100, "amount": 99.99}'
```
### Docker Compose
```yaml
version: '3.8'
services:
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
users:
build: ./services/users
environment:
MICRO_REGISTRY: nats
MICRO_REGISTRY_ADDRESS: nats://nats:4222
DATABASE_URL: postgres://postgres:secret@postgres/users
depends_on:
- postgres
- nats
products:
build: ./services/products
environment:
MICRO_REGISTRY: nats
MICRO_REGISTRY_ADDRESS: nats://nats:4222
DATABASE_URL: postgres://postgres:secret@postgres/products
depends_on:
- postgres
- nats
orders:
build: ./services/orders
environment:
MICRO_REGISTRY: nats
MICRO_REGISTRY_ADDRESS: nats://nats:4222
DATABASE_URL: postgres://postgres:secret@postgres/orders
depends_on:
- postgres
- nats
gateway:
build: ./gateway
ports:
- "8080:8080"
environment:
MICRO_REGISTRY: nats
MICRO_REGISTRY_ADDRESS: nats://nats:4222
depends_on:
- users
- products
- orders
nats:
image: nats:latest
ports:
- "4222:4222"
```
Run with:
```bash
docker-compose up
```
## Key Patterns
1. **Service isolation**: Each service owns its database
2. **Service communication**: Via Go Micro client
3. **Gateway pattern**: Single entry point for clients
4. **Error handling**: Proper HTTP status codes
5. **Registry**: mDNS for local, NATS for Docker
## Production Considerations
- Add authentication/authorization
- Implement request tracing
- Add circuit breakers for service calls
- Use connection pooling
- Add rate limiting
- Implement proper logging
- Use health checks
- Add metrics collection
See [Production Patterns](../realworld/) for more details.
@@ -0,0 +1,365 @@
---
layout: default
---
# Graceful Shutdown
Properly shutting down services to avoid dropped requests and data loss.
## The Problem
Without graceful shutdown:
- In-flight requests are dropped
- Database connections leak
- Resources aren't cleaned up
- Load balancers don't know service is down
## Solution
Go Micro handles SIGTERM/SIGINT by default, but you need to implement cleanup logic.
## Basic Pattern
```go
package main
import (
"context"
"os"
"os/signal"
"syscall"
"time"
"go-micro.dev/v6"
"go-micro.dev/v6/logger"
)
func main() {
svc := micro.NewService("myservice",
micro.BeforeStop(func() error {
logger.Info("Service stopping, running cleanup...")
return cleanup()
}),
)
svc.Init()
// Your service logic
if err := svc.Handle(new(Handler)); err != nil {
logger.Fatal(err)
}
// Run with graceful shutdown
if err := svc.Run(); err != nil {
logger.Fatal(err)
}
logger.Info("Service stopped gracefully")
}
func cleanup() error {
// Close database connections
// Flush logs
// Stop background workers
// etc.
return nil
}
```
## Database Cleanup
```go
type Service struct {
db *sql.DB
}
func (s *Service) Shutdown(ctx context.Context) error {
logger.Info("Closing database connections...")
// Stop accepting new requests
s.db.SetMaxOpenConns(0)
// Wait for existing connections to finish (with timeout)
done := make(chan struct{})
go func() {
s.db.Close()
close(done)
}()
select {
case <-done:
logger.Info("Database closed gracefully")
return nil
case <-ctx.Done():
logger.Warn("Database close timeout, forcing")
return ctx.Err()
}
}
```
## Background Workers
```go
type Worker struct {
quit chan struct{}
done chan struct{}
}
func (w *Worker) Start() {
w.quit = make(chan struct{})
w.done = make(chan struct{})
go func() {
defer close(w.done)
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
w.doWork()
case <-w.quit:
logger.Info("Worker stopping...")
return
}
}
}()
}
func (w *Worker) Stop(timeout time.Duration) error {
close(w.quit)
select {
case <-w.done:
logger.Info("Worker stopped gracefully")
return nil
case <-time.After(timeout):
return fmt.Errorf("worker shutdown timeout")
}
}
```
## Complete Example
```go
package main
import (
"context"
"database/sql"
"fmt"
"os"
"os/signal"
"sync"
"syscall"
"time"
"go-micro.dev/v6"
"go-micro.dev/v6/logger"
)
type Application struct {
db *sql.DB
workers []*Worker
wg sync.WaitGroup
mu sync.RWMutex
closing bool
}
func NewApplication(db *sql.DB) *Application {
return &Application{
db: db,
workers: make([]*Worker, 0),
}
}
func (app *Application) AddWorker(w *Worker) {
app.workers = append(app.workers, w)
w.Start()
}
func (app *Application) Shutdown(ctx context.Context) error {
app.mu.Lock()
if app.closing {
app.mu.Unlock()
return nil
}
app.closing = true
app.mu.Unlock()
logger.Info("Starting graceful shutdown...")
// Stop accepting new work
logger.Info("Stopping workers...")
for _, w := range app.workers {
if err := w.Stop(5 * time.Second); err != nil {
logger.Warnf("Worker failed to stop: %v", err)
}
}
// Wait for in-flight requests (with timeout)
shutdownComplete := make(chan struct{})
go func() {
app.wg.Wait()
close(shutdownComplete)
}()
select {
case <-shutdownComplete:
logger.Info("All requests completed")
case <-ctx.Done():
logger.Warn("Shutdown timeout, forcing...")
}
// Close resources
logger.Info("Closing database...")
if err := app.db.Close(); err != nil {
logger.Errorf("Database close error: %v", err)
}
logger.Info("Shutdown complete")
return nil
}
func main() {
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
logger.Fatal(err)
}
app := NewApplication(db)
// Add background workers
app.AddWorker(&Worker{name: "cleanup"})
app.AddWorker(&Worker{name: "metrics"})
svc := micro.NewService("myservice",
micro.BeforeStop(func() error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
return app.Shutdown(ctx)
}),
)
svc.Init()
handler := &Handler{app: app}
if err := svc.Handle(handler); err != nil {
logger.Fatal(err)
}
// Run service
if err := svc.Run(); err != nil {
logger.Fatal(err)
}
}
```
## Kubernetes Integration
### Liveness and Readiness Probes
```go
func (h *Handler) Health(ctx context.Context, req *struct{}, rsp *HealthResponse) error {
// Liveness: is the service alive?
rsp.Status = "ok"
return nil
}
func (h *Handler) Ready(ctx context.Context, req *struct{}, rsp *ReadyResponse) error {
h.app.mu.RLock()
closing := h.app.closing
h.app.mu.RUnlock()
if closing {
// Stop receiving traffic during shutdown
return fmt.Errorf("shutting down")
}
// Check dependencies
if err := h.app.db.Ping(); err != nil {
return fmt.Errorf("database unhealthy: %w", err)
}
rsp.Status = "ready"
return nil
}
```
### Kubernetes Manifest
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myservice
spec:
replicas: 3
template:
spec:
containers:
- name: myservice
image: myservice:latest
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
lifecycle:
preStop:
exec:
# Give service time to drain before SIGTERM
command: ["/bin/sh", "-c", "sleep 10"]
terminationGracePeriodSeconds: 40
```
## Best Practices
1. **Set timeouts**: Don't wait forever for shutdown
2. **Stop accepting work early**: Set readiness to false
3. **Drain in-flight requests**: Let current work finish
4. **Close resources properly**: Databases, file handles, etc.
5. **Log shutdown progress**: Help debugging
6. **Handle SIGTERM and SIGINT**: Kubernetes sends SIGTERM
7. **Coordinate with load balancer**: Use readiness probes
8. **Test shutdown**: Regularly test graceful shutdown works
## Testing Shutdown
```bash
# Start service
go run main.go &
PID=$!
# Send some requests
for i in {1..10}; do
curl http://localhost:8080/endpoint &
done
# Trigger graceful shutdown
kill -TERM $PID
# Verify all requests completed
wait
```
## Common Pitfalls
- **No timeout**: Service hangs during shutdown
- **Not stopping workers**: Background jobs continue
- **Database leaks**: Connections not closed
- **Ignored signals**: Service killed forcefully
- **No readiness probe**: Traffic during shutdown
## Related
- [API Gateway Example](api-gateway.md) - Multi-service architecture
- [Getting Started Guide](../../getting-started.md) - Basic service setup
@@ -0,0 +1,54 @@
---
layout: default
---
# Real-World Examples
Production-ready patterns and complete application examples.
## Available Examples
- [API Gateway with Backend Services](api-gateway.md) - Complete multi-service architecture with users, orders, and products services
- [Graceful Shutdown](graceful-shutdown.md) - Production-ready shutdown patterns with Kubernetes integration
## Coming Soon
We're actively working on additional real-world examples. Contributions are welcome!
**Complete Applications**
- Event-Driven Microservices - Pub/sub patterns
- CQRS Pattern - Command Query Responsibility Segregation
- Saga Pattern - Distributed transactions
**Production Patterns**
- Health Checks and Readiness
- Retry and Circuit Breaking
- Distributed Tracing with OpenTelemetry
- Structured Logging
- Metrics and Monitoring
**Testing Strategies**
- Unit Testing Services
- Integration Testing
- Contract Testing
- Load Testing
**Deployment**
- Kubernetes Deployment
- Docker Compose Setup
- CI/CD Pipeline Examples
- Blue-Green Deployment
**Integration Examples**
- PostgreSQL with Transactions
- Redis Caching Strategies
- Message Queue Integration
- External API Integration
Each example will include:
- Complete, runnable code
- Configuration for development and production
- Testing approach
- Common pitfalls and solutions
Want to contribute? See our [Contributing Guide](../../contributing.md).
@@ -0,0 +1,33 @@
---
layout: default
---
# Service Discovery with Consul
Use Consul as the service registry.
## In code
```go
package main
import (
"go-micro.dev/v6"
"go-micro.dev/v6/registry/consul"
)
func main() {
reg := consul.NewConsulRegistry()
svc := micro.NewService("consul-registry", micro.Registry(reg))
svc.Init()
svc.Run()
}
```
## Via environment
Run your service with env vars set:
```bash
MICRO_REGISTRY=consul MICRO_REGISTRY_ADDRESS=127.0.0.1:8500 go run main.go
```
@@ -0,0 +1,36 @@
---
layout: default
---
# RPC Client
Call a running service using the Go Micro client.
```go
package main
import (
"context"
"fmt"
"go-micro.dev/v6"
)
type Request struct { Name string }
type Response struct { Message string }
func main() {
svc := micro.NewService("caller")
svc.Init()
req := svc.Client().NewRequest("helloworld", "Say.Hello", &Request{Name: "John"})
var rsp Response
if err := svc.Client().Call(context.TODO(), req, &rsp); err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(rsp.Message)
}
```
@@ -0,0 +1,44 @@
---
layout: default
---
# State with Postgres Store
Use the Postgres store for persistent key/value state.
## In code
```go
package main
import (
"log"
"go-micro.dev/v6"
"go-micro.dev/v6/store"
postgres "go-micro.dev/v6/store/postgres"
)
func main() {
st := postgres.NewStore()
svc := micro.NewService("postgres-store", micro.Store(st))
svc.Init()
_ = store.Write(&store.Record{Key: "foo", Value: []byte("bar")})
recs, _ := store.Read("foo")
log.Println("value:", string(recs[0].Value))
svc.Run()
}
```
## Via environment
Run your service with env vars set:
```bash
MICRO_STORE=postgres \
MICRO_STORE_ADDRESS=postgres://user:pass@127.0.0.1:5432/postgres \
MICRO_STORE_DATABASE=micro \
MICRO_STORE_TABLE=micro \
go run main.go
```
@@ -0,0 +1,33 @@
---
layout: default
---
# NATS Transport
Use NATS as the transport between services.
## In code
```go
package main
import (
"go-micro.dev/v6"
tnats "go-micro.dev/v6/transport/nats"
)
func main() {
t := tnats.NewTransport()
svc := micro.NewService("nats-transport", micro.Transport(t))
svc.Init()
svc.Run()
}
```
## Via environment
Run your service with env vars set:
```bash
MICRO_TRANSPORT=nats MICRO_TRANSPORT_ADDRESS=nats://127.0.0.1:4222 go run main.go
```
+268
View File
@@ -0,0 +1,268 @@
---
layout: default
---
# Getting Started
<img src="/images/generated/getting-started.jpg" alt="Getting started with Go Micro" style="width: 100%; border-radius: 8px; margin-bottom: 1.5rem;" />
Go Micro has three core abstractions:
| Abstraction | What | Constructor |
|-------------|------|-------------|
| **Service** | Capability — endpoints, data, business logic | `micro.NewService("task")` |
| **Agent** | Intelligence — manages services with an LLM | `micro.NewAgent("task-mgr")` |
| **Flow** | Orchestration — event-driven LLM triggers | `micro.NewFlow("onboard")` |
## Prerequisites
- **Go 1.24+** for development. The `curl` install below gives you the `micro` binary without Go, but `micro run` compiles your services, so you'll want Go installed to build them.
- **No LLM provider key is required** for the first run below. Add an Anthropic, OpenAI, Gemini, or other provider key only when you reach the provider-backed generation and chat steps.
## Install
```bash
# Binary (no Go required)
curl -fsSL https://go-micro.dev/install.sh | sh
# Or with Go
go install go-micro.dev/v6/cmd/micro@latest
```
If install or shell setup fails, start with [Install troubleshooting](guides/install-troubleshooting.html) to verify the binary installer or `go install`, `PATH`, `micro --version`, and the no-secret smoke path.
## Quick Start: Scaffold, Run, Call
Start with the path that proves the runtime works before any provider setup: install the CLI, scaffold one service, run it locally, then call it through the gateway.
```bash
micro new helloworld
cd helloworld
micro run
```
In another terminal, call the generated service:
```bash
curl -X POST http://localhost:8080/api/helloworld/Helloworld.Call \
-H 'Content-Type: application/json' -d '{"name":"World"}'
```
That install → scaffold → run → call loop is the 0→1 contract. It requires Go and the `micro` binary, but no LLM key. Once this succeeds, you know the local runtime, hot reload, gateway, and service registration are working.
### First-agent on-ramp
After this quick start, follow the agent path in order:
1. [Install troubleshooting](guides/install-troubleshooting.html) — verify the CLI install before agent work.
Run `make docs-wayfinding` to verify the focused no-secret docs/CLI contract that keeps these website and README commands aligned with the installed CLI.
2. `micro agent demo` — print the provider-free first-agent demo command and next docs steps from the installed CLI.
3. `micro agent quickcheck` (or `micro agent debug`) — when scaffold → run → chat → inspect stalls, print the short recovery map before you dive into the full debugging guide.
4. `micro examples` — print the maintained provider-free runnable examples in copy/paste order.
5. `micro zero-to-hero` — print the maintained one-command no-secret lifecycle harness and runnable examples.
6. [Examples wayfinding index](https://github.com/micro/go-micro/blob/master/examples/INDEX.md) — choose the smallest no-secret first-agent, maintained [0→hero support reference](https://github.com/micro/go-micro/tree/master/examples/support), and next interop examples from one map.
7. [Smallest first-agent example](https://github.com/micro/go-micro/tree/master/examples/first-agent) — run one service-backed agent with a mock model and no provider key.
8. [No-secret first-agent transcript](guides/no-secret-first-agent.html) — run a useful support agent with a mock model before setting up a provider key.
9. [Your First Agent](guides/your-first-agent.html) — build a service-backed agent and talk to it with `micro chat`.
10. [Debugging your agent](guides/debugging-agents.html) — use `micro agent preflight` before `micro run`, `micro agent doctor` after `micro run`, then `micro chat` and `micro inspect agent <name>` to recover service registration, tool calls, run history, memory, provider failures, and flow handoffs when the agent surprises you.
11. [0→hero reference path](guides/zero-to-hero.html) — prove the full scaffold → run → chat → inspect → deploy dry-run lifecycle with commands exercised by `make harness`.
## Write a Service
Create and run a service manually:
```bash
micro new helloworld
cd helloworld
micro run
```
Open http://localhost:8080 to see the dashboard, call endpoints, and chat with your service.
A service is a Go struct with methods. Doc comments and `@example` tags become tool descriptions for AI agents:
```go
package main
import (
"context"
"go-micro.dev/v6"
)
type Request struct {
Name string `json:"name"`
}
type Response struct {
Message string `json:"message"`
}
type Say struct{}
// Hello greets a person by name.
// @example {"name": "Alice"}
func (h *Say) Hello(ctx context.Context, req *Request, rsp *Response) error {
rsp.Message = "Hello " + req.Name
return nil
}
func main() {
service := micro.NewService("greeter")
service.Handle(new(Say))
service.Run()
}
```
`micro run` gives you:
- **Dashboard** at `http://localhost:8080`
- **API Gateway** at `http://localhost:8080/api/{service}/{method}`
- **Agent Playground** at `http://localhost:8080/agent`
- **MCP Tools** at `http://localhost:8080/mcp/tools`
- **Hot Reload** — auto-rebuild on file changes
`micro new` scaffolds a reflection-based service by default — plain Go types, no code generation, so `go run .` works with nothing else installed. If you prefer Protocol Buffers, add `--proto` (this requires the `protoc` toolchain; the command tells you what to install).
Templates are available for common patterns. These use Protocol Buffers, so they need the `protoc` toolchain (`protoc`, `protoc-gen-go`, `protoc-gen-micro``micro new` prints the install commands if they're missing):
```bash
micro new contacts --template crud
micro new events --template pubsub
micro new gateway --template api
```
## Generate from a Prompt — with an LLM key
After the no-secret path works, set a provider key if you want Go Micro to design services and an agent from a prompt:
```bash
export ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY, GEMINI_API_KEY, ...
micro run --prompt "task management system" --provider anthropic
```
You'll see the design, confirm it, and then services plus an agent start:
```text
Services:
● task — Core task management
● project — Project organization
Generate? [Y/n]
Micro
Services:
● task
● project
Agents:
◆ agent
```
Use the interactive console, `micro run -d` plus `micro chat`, or the agent playground to talk to the generated services.
Before your first provider-backed agent run, check the local path with:
```bash
micro agent preflight
```
The preflight is read-only: it verifies Go 1.24+, the `micro` binary, provider-key setup, and whether the default `micro run` gateway port is free, without calling an LLM provider. When a check fails it prints the exact fix plus the next guide to open, so the scaffold → run → chat path stays walkable.
## Building Agents
For a complete service-backed walkthrough, start with [Your First Agent](guides/your-first-agent.html). If you want to run before you write, use [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support) for the full services → agents → workflows lifecycle or [`examples/agent-plan-delegate`](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate) for the smallest multi-agent planning/delegation path.
An Agent is an intelligent layer that manages one or more services:
```go
package main
import "go-micro.dev/v6"
func main() {
agent := micro.NewAgent("task-mgr",
micro.AgentServices("task", "project"),
micro.AgentPrompt("You manage tasks and projects. You understand deadlines, priorities, and assignments."),
micro.AgentProvider("anthropic"),
micro.AgentAPIKey("sk-ant-..."),
)
agent.Run()
}
```
An agent is a service — it has a proto-defined `Agent.Chat` RPC endpoint and registers in the registry like everything else. It:
- Discovers its services from the registry
- Only sees endpoints from its assigned services (scoped tools)
- Maintains conversation memory in the store (persists across restarts)
- Is callable via `micro call`, the interactive console, or any go-micro client
Use it programmatically:
```go
resp, _ := agent.Ask(ctx, "What tasks are overdue for Alice?")
fmt.Println(resp.Reply)
```
Or via the CLI:
```bash
micro agent list # list registered agents
micro call task-mgr Agent.Chat '{"message": "What tasks are overdue?"}'
```
When multiple agents are registered, the console routes to the right agent automatically.
## Event-Driven Flows
A Flow subscribes to a broker topic and triggers an LLM when events arrive. You can define flows in code or run them from the CLI.
**In code:**
```go
f := micro.NewFlow("onboard-user",
micro.FlowTrigger("events.user.created"),
micro.FlowPrompt("New user created: {{.Data}}. Send welcome email and create workspace."),
micro.FlowProvider("anthropic"),
micro.FlowAPIKey(os.Getenv("MICRO_AI_API_KEY")),
)
f.Register(service.Options().Registry, service.Options().Broker, service.Client())
```
**From the CLI:**
```bash
micro flow run --trigger events.user.created --prompt "New user: {{.Data}}. Send welcome email."
micro flow exec --prompt "Summarize all open tickets and email the report."
```
The flow discovers all services as tools and lets the LLM decide which RPCs to call in response to the event.
## CLI Workflow
| Command | Purpose |
|---------|---------|
| `micro run --prompt "..."` | Generate services + agent, start with interactive console |
| `micro run` | Dev mode: hot reload, gateway, interactive console |
| `micro run -d` | Detached mode (no console) |
| `micro chat` | Standalone chat (when not using micro run) |
| `micro agent list` | List registered agents |
| `micro flow run --trigger <topic>` | Run an event-driven flow |
| `micro flow exec --prompt "..."` | Execute a one-shot flow |
| `micro new myservice` | Scaffold a service |
| `micro call service endpoint '{}'` | Call a service or agent |
| `micro build` | Compile production binaries |
| `micro deploy user@server` | Deploy via SSH + systemd |
## Next Steps
- [Learn by Example](examples/) — runnable examples mapped to services, agents, and workflows
- [0→hero Reference](guides/zero-to-hero.html) — the maintained no-secret lifecycle contract
- [AI Integration](ai-integration.html) — how services, agents, MCP, and LLMs fit together
- [Agent Design](https://github.com/micro/go-micro/blob/master/internal/docs/AGENT_DESIGN.md) — the full agent interface specification
- [MCP & AI Agents](mcp.html) — MCP gateway, tool discovery, and auth
- [Data Model](model.html) — typed persistence with CRUD and queries
- [`micro loop` quickstart](guides/micro-loop.html) — scaffold a CI-gated autonomous improvement loop for a repository
- [Deployment](deployment.html) — deploy via SSH + systemd
@@ -0,0 +1,203 @@
---
layout: default
---
# Agent2Agent (A2A)
Go Micro speaks the [Agent2Agent (A2A) protocol](https://a2a-protocol.org) — the open standard for agents on different frameworks to discover and call each other over HTTP. The A2A gateway is the agent-side analogue of the [MCP gateway](../mcp.html): MCP exposes your services as tools, A2A exposes your agents as agents.
There is nothing to add to an agent. An agent already registers in the registry with `type=agent` metadata; the gateway discovers it, generates an **Agent Card** from that metadata, and translates incoming A2A tasks to the agent's existing `Agent.Chat` RPC — the same call `delegate` and flows use.
## Run it
```bash
micro a2a serve --address :4000 --base_url https://agents.example.com
micro a2a list # agents and their Agent Card URLs
```
Or embed the gateway next to a service:
```go
go a2a.Serve(a2a.Options{
Registry: service.Options().Registry,
Address: ":4000",
BaseURL: "https://agents.example.com",
})
```
## Gateway, or directly on the agent
A2A is JSON-RPC over HTTP — a different wire protocol from go-micro's RPC — so *something* always translates between the two. That something doesn't have to be a separate process. There are two ways to run it:
- **A gateway** (above) fronts every agent in the registry behind one endpoint. Use it for a single front door, centralized discovery, and shared policy.
- **Directly on the agent.** `AgentA2A(addr)` makes the agent serve its own A2A endpoint when it runs — no separate gateway, and the task is handled in-process (no extra RPC hop):
```go
agent := micro.NewAgent("task-mgr",
micro.AgentServices("task"),
micro.AgentProvider("anthropic"),
micro.AgentA2A(":4000"), // also reachable at http://host:4000 over A2A
)
agent.Run()
```
The agent stays a normal go-micro service; this adds a second, A2A-native HTTP endpoint. Now any A2A client can `curl` it directly. Use it when each agent should be independently addressable without a gateway.
Both reuse the same handler; the only difference is whether the agent is reached over RPC (gateway) or in-process (embedded).
## Discovery: cards from the registry
Every registered agent gets an Agent Card, generated from its registry metadata (name, the services it manages). Cards are not published by the agent — they are derived, the same way MCP tools are derived from service endpoints.
| Endpoint | Returns |
|---|---|
| `GET /agents` | a directory of all Agent Cards |
| `GET /agents/{name}` | one agent's card |
| `GET /agents/{name}/.well-known/agent.json` | one agent's card (well-known path) |
| `POST /agents/{name}` | the agent's JSON-RPC endpoint |
| `GET /.well-known/agent.json` | the single agent's card, when exactly one is registered |
A card looks like:
```json
{
"name": "task-mgr",
"description": "Go Micro agent managing: task,project",
"url": "https://agents.example.com/agents/task-mgr",
"version": "1.0.0",
"protocolVersion": "0.3.0",
"capabilities": { "streaming": true, "pushNotifications": true },
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{ "id": "task", "name": "Task", "tags": ["task"] },
{ "id": "project", "name": "Project", "tags": ["project"] }
]
}
```
Each managed service is advertised as its own typed skill. Clients can call the
whole agent at `/agents/task-mgr`, or address one skill directly at
`/agents/task-mgr/skills/task`; the skill endpoint serves a focused card and
routes the request to the same agent with that skill selected.
## Calling an agent
A2A uses JSON-RPC 2.0 over HTTP. Send a message with `message/send`; the gateway runs the agent and returns a completed `Task`:
```bash
curl -s https://agents.example.com/agents/task-mgr \
-H 'content-type: application/json' \
-d '{
"jsonrpc": "2.0", "id": 1, "method": "message/send",
"params": { "message": {
"role": "user", "kind": "message", "messageId": "m1",
"parts": [{ "kind": "text", "text": "What tasks are overdue?" }]
}}
}'
```
```json
{
"jsonrpc": "2.0", "id": 1,
"result": {
"id": "…", "contextId": "…", "kind": "task",
"status": { "state": "completed", "timestamp": "…" },
"artifacts": [{ "artifactId": "…", "parts": [{ "kind": "text", "text": "Two: …" }] }]
}
}
```
Retrieve a task later with `tasks/get` (`params: { "id": "…" }`). To continue
the same piece of work, send another `message/send` with the previous `taskId`
and `contextId`. The gateway preserves the task id, context id, and prior
history, then appends the new user turn and agent reply. That makes a remote
A2A task fit the Go Micro lifecycle: services are still invoked through the
agent's normal tools, the agent keeps task context across turns, and a workflow
can poll one task id as the conversation progresses.
## Push notifications
Operators can register a task callback with
`tasks/pushNotificationConfig/set`:
```bash
curl -s https://agents.example.com/agents/task-mgr \
-H 'content-type: application/json' \
-d '{
"jsonrpc": "2.0", "id": 2,
"method": "tasks/pushNotificationConfig/set",
"params": {
"id": "task-id",
"pushNotificationConfig": {
"url": "https://workflow.example.com/a2a/tasks",
"token": "optional-bearer-token"
}
}
}'
```
The gateway stores one callback per retained task and POSTs the latest task
snapshot to that URL whenever the task changes. Delivery is best effort: failures
do not fail the agent turn, and there is no retry queue in the in-memory gateway.
Use `tasks/get` as the source of truth after a missed callback or receiver
outage. If a token is configured, it is sent as `Authorization: Bearer <token>`.
## Calling out to other agents
The gateway makes your agents reachable *from* the A2A ecosystem. The
client (`a2a.Client`) is the other direction: it lets a Go Micro agent or
flow call an agent on any framework, by URL.
```go
reply, err := a2a.NewClient("https://other.example.com/agents/research").
Send(ctx, "Summarize the latest on X")
```
It's wired into the two places that hand off work:
- **A flow step** — `flow.A2A(url)` is the cross-framework counterpart to
`flow.Dispatch(name)` (which dispatches to a local agent):
```go
flow.Step{Name: "research", Run: flow.A2A("https://other.example.com/agents/research")}
```
- **Agent delegate** — when an agent's `delegate` target is an `http(s)`
URL, the subtask is sent to that external agent over A2A instead of to a
locally registered one. Nothing else changes; the model just delegates
to a URL.
`Send` handles the task lifecycle: if the remote returns a task that isn't
yet terminal, it polls `tasks/get` until it completes.
## Scope
This is the JSON-RPC binding for task execution:
- **`message/send`** runs the agent and returns a completed `Task`.
- **`message/stream`** streams the completed `Task` as an SSE `data:` event, giving A2A clients a streaming-compatible path while the underlying agent call remains synchronous.
- **`tasks/get`** returns a recent task by id.
- **Multi-turn continuation** keeps task state when a new message includes the previous `taskId`.
- **`tasks/pushNotificationConfig/set` / `get`** stores and reads a task callback for best-effort update delivery.
- **`tasks/resubscribe`** reconnects to an existing task stream, immediately emits the current task snapshot, then streams subsequent updates until the task reaches a terminal state.
- **`input-required`** task state carries human-input handoffs (for example checkpointed approval pauses) in task status, artifacts, and history; continue the task by sending a follow-up message with the same `taskId` and `contextId`.
- **Agent Card** discovery, generated from the registry.
Both directions work: the gateway exposes your agents, and `a2a.Client` (via `flow.A2A` or `delegate` to a URL) calls external ones. The task binding is what makes a Go Micro agent both reachable from, and able to reach, the A2A ecosystem today.
## AP2 mandate layer (opt-in)
AP2 sits above A2A as a verifiable-intent and audit layer. Go Micro keeps the
A2A envelope separate from payment settlement: an A2A message can carry signed
AP2 checkout or payment mandates, and the resulting task can retain the stable
mandate reference plus verification result. Payment settlement state remains in
the payment rail. For x402, use an AP2 payment mandate with an `x402` rail
reference to name the payment requirement; the existing x402 facilitator still
performs verification and settlement.
## See also
- [MCP & AI Agents](../mcp.html) — exposing services as tools
- [Agents and Workflows](agents-and-workflows.html) — the agent model
- [A2A protocol specification](https://a2a-protocol.org)
@@ -0,0 +1,122 @@
---
layout: default
---
# Agent Guardrails
An autonomous agent decides its own actions at runtime, which is what makes it useful — and what makes it risky. The common failure modes are mundane: it loops, repeating the same call without making progress; it runs away, taking far more steps (and cost) than the task warrants; it takes an action that should have had a human or a policy in the way.
Go Micro separates **orchestration** (the model deciding what to do) from **execution safety** (whether a decided action is allowed to run). Every tool call an agent makes passes through one choke point, and that's where the guardrails live — so they apply uniformly to service calls, custom tools, and `delegate`, without touching the model or your services.
## The three agent guardrails
### Stop on count — `MaxSteps`
Bounds the total number of tool executions in a single `Ask`. Once exceeded, further calls are refused and the model is told to stop and summarize. The blunt backstop against runaway cost.
```go
micro.NewAgent("worker", micro.AgentMaxSteps(8))
```
### Stop on repeat — `LoopLimit`
Bounds how many times the agent may call the **same tool with the same arguments** in one `Ask`. Identical repeated calls make no progress — `MaxSteps` only bounds them by total count, and a circuit breaker only catches *failures*, not a call that succeeds and is pointlessly repeated. When the limit is hit, the call is refused with a message that tells the model it's looping, so it changes approach instead of spinning:
> loop detected: you have already called "search.Search.Query" with the same arguments 3 times and the result will not change. Stop repeating it — try a different approach, or finish with what you have.
```go
micro.NewAgent("worker", micro.AgentLoopLimit(3))
```
`LoopLimit` is **on by default** (a lenient 3) because identical repeated calls are never useful. Set `AgentLoopLimit(0)` to disable it.
### Gate the action — `ApproveTool`
A hook called before each action runs. Return `false` to block it, with a reason that's surfaced to the model. Use it for human-in-the-loop approval, spend limits, allow/deny lists, or any policy:
```go
micro.NewAgent("worker", micro.AgentApproveTool(
func(tool string, input map[string]any) (bool, string) {
if strings.HasPrefix(tool, "billing_") {
return false, "billing actions require sign-off"
}
return true, ""
}))
```
## ApproveTool is the integration seam
`ApproveTool` is also where an **external policy engine** plugs in. It sees every tool call before execution and can veto, so you can route decisions to your own rules, a budget service, or a third-party runtime-safety layer — without go-micro depending on it. Orchestration stays in the agent; execution safety stays in the hook. That separation is the whole point: you can swap the safety layer without touching the agent.
## Wrap the whole execution — `WrapTool`
`ApproveTool` is a *before* gate. When you need the full lifecycle — timing, logging, metrics, retries, or inspecting the result — wrap the execution instead. `WrapTool` is the tool-side analogue of go-micro's `client.CallWrapper` and `server.HandlerWrapper`: a wrapper takes the next handler and returns a new one, so code before the `next(...)` call runs *before* the tool, and code after runs *after*.
```go
import "go-micro.dev/v6/ai"
func logging(next ai.ToolHandler) ai.ToolHandler {
return func(ctx context.Context, call ai.ToolCall) ai.ToolResult {
start := time.Now()
res := next(ctx, call)
log.Printf("id=%s tool=%s took=%s", call.ID, call.Name, time.Since(start))
return res
}
}
micro.NewAgent("worker", micro.AgentWrapTool(logging))
```
The handler signature is the same one every provider uses to execute a tool, and it mirrors a service handler — context first, the call in, a result out:
```go
type ToolHandler func(ctx context.Context, call ToolCall) ToolResult
type ToolWrapper func(ToolHandler) ToolHandler
```
`call.ID` is a correlation ID carried through from the provider, so a wrapper can tie a tool call back to the request it came from. `call.Scan(&v)` decodes the arguments into a typed struct when you'd rather not work with the raw map.
Wrappers run **outside** the built-in guardrails, so they observe every call and its result — including a guardrail's refusal. Multiple wrappers compose outermost-first (the first registered is the outer layer). A "before/after" hook is just the two halves of one wrapper, and retry is calling `next` again — so the wrapper is the single, composable seam for everything around execution, while `MaxSteps`, `LoopLimit`, and `ApproveTool` remain the named guardrails on top of it.
### Reliability metadata
A wrapper has what it needs to build reliability tooling — loop handling, retry policies, auditing — without coupling to the agent:
- **What happened** — a guardrail refusal is tagged with a structured reason on the result, so you switch on it rather than parse a message:
```go
res := next(ctx, call)
switch res.Refused {
case ai.RefusedLoop: // the agent repeated an identical call
case ai.RefusedMaxSteps: // the step budget was exhausted
case ai.RefusedApproval: // ApproveTool blocked it
}
```
- **Which run** — `ai.RunInfoFrom(ctx)` returns a correlation id for the run, the agent's name, and the parent run when the call came from a delegated sub-agent:
```go
if run, ok := ai.RunInfoFrom(ctx); ok {
log.Printf("run=%s parent=%s agent=%s tool=%s", run.RunID, run.ParentID, run.Agent, call.Name)
}
```
- **Per-call detail** — `call.ID` (correlation), `call.Name`; duration is `time.Since(start)` around `next`, and step/attempt counts are naturally counted by the wrapper itself (it sees every call).
## Execution safety at the gateway
When agents reach tools **through the MCP gateway**, the gateway adds its own per-tool policies, independent of the agent:
- **`RateLimit`** — requests-per-second per tool.
- **`CircuitBreaker`** — a tool that fails repeatedly is temporarily blocked, so a failing dependency doesn't cascade.
Together with the agent-side guardrails, that's a full set: bound the count, stop the spin, gate the action, rate-limit and circuit-break at the edge.
## Why it matters for autonomous agents
These are most important when no human is in the loop. An agent [triggered by an event](/blog/21) runs unattended — there's no one to notice it looping or to approve a risky call. The guardrails are what let it fail safely and recover on its own rather than quietly burning resources.
## See also
- [Plan & Delegate](plan-delegate.html) — the agent's built-in tools
- [Agents and Workflows](agents-and-workflows.html) — where agents fit
@@ -0,0 +1,148 @@
---
layout: default
---
# The Agent Harness
The first wave of agent frameworks solved one problem: put a model in a loop with
some tools. The harder problem is **operating** that loop — and that's what a
harness is.
A harness is the runtime around an agent:
- the **tools** it can call,
- the **memory** it keeps,
- the **guardrails** that bound it,
- the **workflows** that trigger and structure it,
- the **state** that survives a restart,
- the **observability** to see what it did,
- the **services** it depends on,
- and the **protocols** other agents use to reach it.
Go Micro's bet is that this runtime is the one you already deploy. An agent is a
service with a model inside; the harness is the distributed-systems machinery
services already have. So you don't bolt a separate orchestration product onto
your stack — the harness *is* the stack.
## The pieces, and what they map to
| Harness concern | In Go Micro | Status |
|---|---|---|
| Tools | Every service endpoint is an MCP-callable tool from registry metadata — no extra code | Shipped |
| Memory | Store-backed agent memory (`AgentMemory`), durable across restarts | Shipped |
| Guardrails | `MaxSteps`, `LoopLimit`, `ApproveTool`, tool wrappers — enforced at the call site | Shipped |
| Workflows | Durable flows; `micro.FlowLoop` for run-until-done | Shipped |
| Planning / delegation | Built-in `plan` and `delegate` tools on every agent | Shipped |
| Discovery & RPC | Registry + client; agents and services find and call each other | Shipped |
| Interop | MCP (tools), A2A (agents), x402 (paid tools) | Shipped |
| Resilience | Per-call timeout with context propagation; opt-in retry/backoff (`ModelRetry`) across the loop | Shipped |
| Durable runs | Checkpoint and resume an agent run with the same checkpoint backend flows use | Shipped |
| Observability | `RunInfo` → OpenTelemetry spans for runs, model calls, tools, delegation, and failures; persisted run history | Shipped |
| Streaming | `ai.Stream` through chat, agent, and A2A | In progress |
The "in progress" rows are exactly the roadmap's [Now and Next](/docs/roadmap.html),
and the work is happening in the open.
## Durable agent runs
Agents can persist their execution history to the same `Checkpoint` backend as
flows. A checkpointed `Ask` records the run id, original prompt, model result,
and completed tool calls. If the process restarts after a tool succeeds but
before the model finishes, `AgentResume` continues the same run and returns the
recorded tool result instead of re-running the side effect. If a run already
completed, resume returns the persisted response without calling the model.
```go
agent := micro.NewAgent("conductor",
micro.AgentProvider("anthropic"),
micro.AgentWithCheckpoint(checkpoint),
)
resp, err := agent.Ask(ctx, "charge order 42 and send a receipt")
if err != nil {
// On startup, or after a transient failure, discover unfinished work:
pending, _ := micro.AgentPending(ctx, agent)
for _, run := range pending {
_, _ = micro.AgentResume(ctx, agent, run.ID)
}
}
_ = resp
```
Choose the boundary deliberately: use a durable flow when the steps are known
(`reserve`, `charge`, `confirm`) and each step has deterministic retry/resume
semantics. Use a checkpointed agent run when the model is deciding which tools to
call or how many turns it needs, but the side effects of completed tool calls
still need crash-safe resume. Flows and agents share the same `Checkpoint`
interface, so a flow can safely dispatch to a checkpointed agent for the
open-ended part.
For human-in-the-loop runs that pause through the built-in `request_input` tool,
resume with the operator's response:
```go
_, err := micro.AgentResumeInput(ctx, agent, runID, "Deploy to us-east-1")
```
## Observing agent runs
Pass an OpenTelemetry tracer provider when you construct an agent to turn the
agent's `RunInfo` into spans:
```go
agent := micro.NewAgent("conductor",
micro.AgentProvider("anthropic"),
micro.AgentTraceProvider(otel.GetTracerProvider()),
)
```
A traced `Ask` emits a parent `agent.run` span plus child spans for
`agent.model.call` and `agent.tool.call`. Delegate tool calls are marked with
`agent.delegate=true`; ephemeral sub-agents start their own `agent.run` span with
`agent.run.parent_id` set to the delegating run, so a trace shows the hand-off
from service-like agent to sub-agent. Failure and refusal outcomes set error
status on the relevant span and are also recorded in the persisted run timeline.
Important span attributes include:
| Attribute | Meaning |
|---|---|
| `agent.run.id` | Stable run correlation ID surfaced as `ai.RunInfo.RunID` |
| `agent.run.parent_id` | Parent run for delegated sub-agent work |
| `agent.name` | Agent that owns the run or call |
| `agent.model.provider` / `agent.model.name` | Provider and configured model for model calls |
| `agent.tool.name` | Tool invoked by the model |
| `agent.delegate` | Whether the tool call is a delegation boundary |
| `agent.latency_ms` | Elapsed time for the run/call |
| `agent.tokens.*` | Token usage when the provider reports it |
## Why services are the right substrate
An agent that does real work needs typed, discoverable, callable capabilities —
which is what a service is. The harness is credible *because* of the service
layer, not in spite of it:
- **Tools are services** — endpoint metadata becomes the tool schema; an RPC
executes the call.
- **Agents are services** — they register, load-balance, expose `Agent.Chat`, and
are reachable by other agents.
- **Workflows are code paths** — use a flow when the path is known; hand off to an
agent when it isn't.
- **Safety lives at execution** — guardrails run on the one path every tool call
takes.
## When to reach for it
Use Go Micro when the agent has to **operate a system**, not just answer a prompt
— when it needs real tools, state that survives, limits you can enforce, and a way
to be seen and called. If you only need a model in a loop, you don't need a
harness. When that loop has to touch production, you do.
## See also
- [Agents and Workflows](agents-and-workflows.html) — flows vs. agents
- [Agent Loops](agent-loops.html) — run-until-done, with a ceiling
- [Plan & Delegate](plan-delegate.html)
- [Agent Guardrails](agent-guardrails.html)
- [Provider Conformance](provider-conformance.html) — verified provider behavior
- [Roadmap](/docs/roadmap.html)
+117
View File
@@ -0,0 +1,117 @@
---
layout: default
---
# Agent Loops
Most agent work is one-shot: a prompt goes in, an answer comes out. The next
step in agentic systems is the **loop** — run a step over and over, letting the
agent keep working until the goal is met instead of stopping after one pass. One
agent improves an architecture while another removes duplicated abstractions,
both opening pull requests continuously; a draft is refined until it's good
enough; a build is fixed and re-run until it's green.
The catch is cost and runaway risk: a loop "burns through tokens a lot faster
than a simple Q&A chatbot," and a non-deterministic stop ("keep going until
you're done") has no natural ceiling. So a usable loop needs two things:
1. a **stop condition** — how it decides it's done, and
2. a **hard cap** — a guardrail that guarantees it always terminates.
Go Micro gives you both as a flow step: `micro.FlowLoop`.
## The shape
`micro.FlowLoop` is a `StepFunc`, so it drops into a flow's ordered, checkpointed
step list like any other step. It runs a **body** step repeatedly, carrying the
flow `State` from one pass to the next, until a stop condition fires or the
iteration cap is hit — whichever comes first.
```go
f := micro.NewFlow("refactor",
micro.FlowProvider("anthropic"),
micro.FlowSteps(
micro.FlowStep{Name: "improve", Run: micro.FlowLoop(
micro.FlowDispatch("coder"), // the body: an agent does one pass
micro.FlowUntilLLM("Is the refactor complete with no duplicated abstractions left?"),
micro.FlowLoopMax(5), // the ceiling: never more than 5 passes
)},
),
)
```
## Stop conditions
**Code-defined**`FlowUntil` stops when your predicate returns true. Use it
when "done" is something you can measure (tests pass, a score clears a
threshold, a queue is empty):
```go
micro.FlowUntil(func(_ context.Context, s micro.FlowState, iter int) (bool, error) {
var d Draft
_ = s.Scan(&d)
return d.Quality >= 90, nil
})
```
**Model-judged**`FlowUntilLLM` asks the flow's model, after each pass,
whether the goal is met, and stops on an affirmative answer. This is the
supervised ("Ralph") loop: the agent decides when it's done, while the cap
still guarantees it stops. It requires a flow model (`FlowProvider`/`FlowAPIKey`).
```go
micro.FlowUntilLLM("Have all the failing tests been fixed?")
```
You can combine both — either firing stops the loop.
## The guardrail
`FlowLoopMax(n)` is the ceiling. The body never runs more than `n` times, so the
loop always terminates even if the stop condition never fires. When the cap is
hit, the loop returns the latest state rather than erroring — the guardrail did
its job. **Always set it.** For tighter budgets, keep the cap low and pair the
loop with [agent guardrails](agent-guardrails.html) (e.g. token/spend limits)
and [paid tools](x402-payments.html) (per-call metering) so a background loop
can't run up an unbounded bill.
## Watching progress
`FlowOnIteration` runs after each pass — log it, or persist a summary so you can
see how a long-running loop is doing:
```go
micro.FlowOnIteration(func(iter int, s micro.FlowState) {
log.Printf("pass %d: %s", iter, s.String())
})
```
## Durability
A loop runs as a **single flow step**. The flow checkpoints the loop's outcome
(before and after the step) through its [Checkpoint](../deployment.html), and a
resume re-enters the step — so keep loop bodies safe to repeat. For long loops,
use `FlowOnIteration` to persist per-pass progress.
## Run it
A complete, offline example (no API key — the body and stop condition are plain
Go) is in [`examples/flow-loop`](https://github.com/micro/go-micro/tree/master/examples/flow-loop):
```bash
go run ./examples/flow-loop/
# refining until quality >= 90
# pass 1 → quality 30
# pass 2 → quality 60
# pass 3 → quality 90
# done: {"text":"draft refined (quality 90)","quality":90}
```
Swap the body for `micro.FlowDispatch("agent")` or `micro.FlowLLM(...)` and the
stop check for `micro.FlowUntilLLM(...)` to turn it into a real agent loop.
## See also
- [Agents and Workflows](agents-and-workflows.html) — flows vs. agents
- [Agent Guardrails](agent-guardrails.html) — bounding what a loop can do
- [Plan & Delegate](plan-delegate.html) — splitting work across agents
@@ -0,0 +1,477 @@
---
layout: default
---
# Agent Integration Patterns
This guide covers common patterns for integrating AI agents with Go Micro services, from single-agent workflows to multi-agent architectures.
## Pattern 1: Single Agent with Multiple Services
The simplest and most common pattern. One AI agent has access to multiple microservices as MCP tools.
```
User → AI Agent → MCP Gateway → [Service A, Service B, Service C]
```
### Setup
Run multiple services and expose them all through one MCP gateway:
```go
users := micro.NewService("users", micro.Address(":8081"))
tasks := micro.NewService("tasks", micro.Address(":8082"))
notifications := micro.NewService("notifications", micro.Address(":8083"))
// Run all together as a modular monolith
g := micro.NewGroup(users, tasks, notifications)
g.Run()
```
With `micro run`, all services are discovered automatically via the registry, and the MCP tools endpoint at `/mcp/tools` exposes every endpoint from every service.
### When to Use
- Most applications start here
- Agent needs to orchestrate across services (e.g., "create a task and notify the assignee")
- You want the agent to choose which service to call based on the user's request
## Pattern 2: Scoped Agents
Different agents have access to different subsets of tools via scopes.
```
Customer Agent → MCP Gateway → [orders:read, support:write]
Internal Agent → MCP Gateway → [orders:*, users:*, billing:*]
Admin Agent → MCP Gateway → [*]
```
### Setup
Create tokens with different scopes for each agent:
```go
// Gateway with scope enforcement
mcp.ListenAndServe(":3000", mcp.Options{
Registry: reg,
Auth: authProvider,
Scopes: map[string][]string{
"billing.Billing.Charge": {"billing:admin"},
"users.Users.Delete": {"users:admin"},
"orders.Orders.List": {"orders:read"},
"orders.Orders.Create": {"orders:write"},
"support.Support.CreateTicket": {"support:write"},
},
})
```
Then issue different tokens:
- Customer-facing agent token: `scopes=["orders:read", "support:write"]`
- Internal agent token: `scopes=["orders:read", "orders:write", "users:read"]`
- Admin agent token: `scopes=["*"]`
### When to Use
- Different trust levels for different agents
- Customer-facing vs internal agents
- Compliance requirements (e.g., PCI, HIPAA)
## Pattern 3: Agent as Service Consumer
Your Go Micro service itself calls an AI model to process data, using the `ai` package.
```
User → API → Your Service → AI Model (Claude/GPT)
→ Other Services
```
### Setup
```go
import (
"go-micro.dev/v6/ai"
_ "go-micro.dev/v6/ai/anthropic"
)
type SummaryService struct {
ai ai.Model
tasks *TaskClient
}
func NewSummaryService() *SummaryService {
return &SummaryService{
ai: ai.New("anthropic",
ai.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
ai.WithModel("claude-sonnet-4-20250514"),
),
}
}
// Summarize generates an AI summary of a project's tasks.
// Returns a natural language summary of task status, blockers, and progress.
//
// @example {"project_id": "proj-1"}
func (s *SummaryService) Summarize(ctx context.Context, req *SummarizeRequest, rsp *SummarizeResponse) error {
// Fetch tasks from another service
tasks, err := s.tasks.List(ctx, req.ProjectID)
if err != nil {
return err
}
// Use AI to summarize
resp, err := s.ai.Generate(ctx, &ai.Request{
Prompt: fmt.Sprintf("Summarize these tasks:\n%s", formatTasks(tasks)),
SystemPrompt: "You are a concise project manager. Summarize task status in 2-3 sentences.",
})
if err != nil {
return err
}
rsp.Summary = resp.Reply
return nil
}
```
### When to Use
- Your service needs to process natural language
- Generating summaries, classifications, or extractions
- Enriching data with AI before returning to the caller
## Pattern 4: Agent with Tool Calling
An AI model calls your services as tools, with automatic tool execution via the ai package.
```
User → Your App → AI Model ←→ MCP Tools (your services)
```
### Setup
```go
import (
"go-micro.dev/v6/ai"
_ "go-micro.dev/v6/ai/anthropic"
)
// Define tools from your service endpoints
tools := []ai.Tool{
{
Name: "create_task",
Description: "Create a new task with title and assignee",
Properties: map[string]any{
"title": map[string]any{"type": "string", "description": "Task title"},
"assignee": map[string]any{"type": "string", "description": "Username"},
},
},
{
Name: "list_tasks",
Description: "List tasks filtered by status",
Properties: map[string]any{
"status": map[string]any{"type": "string", "description": "Filter: todo, in_progress, done"},
},
},
}
// Handle tool calls by routing to your services. The handler mirrors a
// go-micro RPC handler: context first, the call in, a result out.
toolHandler := func(ctx context.Context, call ai.ToolCall) ai.ToolResult {
switch call.Name {
case "create_task":
var rsp CreateResponse
err := client.Call(ctx, "tasks", "TaskService.Create", call.Input, &rsp)
if err != nil {
return ai.ToolResult{ID: call.ID, Content: fmt.Sprintf(`{"error": "%s"}`, err)}
}
b, _ := json.Marshal(rsp)
return ai.ToolResult{ID: call.ID, Value: rsp, Content: string(b)}
case "list_tasks":
var rsp ListResponse
err := client.Call(ctx, "tasks", "TaskService.List", call.Input, &rsp)
if err != nil {
return ai.ToolResult{ID: call.ID, Content: fmt.Sprintf(`{"error": "%s"}`, err)}
}
b, _ := json.Marshal(rsp)
return ai.ToolResult{ID: call.ID, Value: rsp, Content: string(b)}
}
return ai.ToolResult{ID: call.ID, Content: `{"error": "unknown tool"}`}
}
m := ai.New("anthropic",
ai.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
ai.WithToolHandler(toolHandler),
)
// The model will automatically call tools and return the final answer
resp, err := m.Generate(ctx, &ai.Request{
Prompt: "Create a task for Alice to review the PR and tell me what tasks she has",
SystemPrompt: "You are a helpful project management assistant",
Tools: tools,
})
fmt.Println(resp.Answer)
// "I've created a task for Alice to review the PR. She now has 3 tasks: ..."
```
### When to Use
- Building a chatbot or assistant that manages your services
- The agent playground in `micro run` uses this pattern
- You want the AI to decide which tools to call and in what order
## Pattern 5: Event-Driven Agent Triggers
Services emit events that trigger agent actions via the broker.
```
Service → Broker Event → Agent Handler → AI Model → Action
```
### Setup
```go
// Publisher: emit events from your service
broker.Publish("tasks.created", &broker.Message{
Body: taskJSON,
})
// Subscriber: agent handler reacts to events
broker.Subscribe("tasks.created", func(p broker.Event) error {
var task Task
json.Unmarshal(p.Message().Body, &task)
// Use AI to auto-assign based on task content
resp, err := aiModel.Generate(ctx, &ai.Request{
Prompt: fmt.Sprintf("Who should handle this task? Title: %s, Description: %s. Team: alice (frontend), bob (backend), charlie (devops)", task.Title, task.Description),
SystemPrompt: "Reply with just the username of the best person to handle this task.",
})
// Auto-assign
client.Call(ctx, "tasks", "TaskService.Update", map[string]any{
"id": task.ID,
"assignee": strings.TrimSpace(resp.Reply),
}, nil)
return nil
})
```
### When to Use
- Automated workflows triggered by service events
- AI-powered routing, classification, or triage
- Background processing without user interaction
## Pattern 6: Claude Code Integration
Developers use Claude Code with your services as MCP tools for local development workflows.
```
Developer → Claude Code → stdio MCP → [local services]
```
### Setup
```bash
# Start services locally
micro run
# In another terminal, use Claude Code with your services
# Claude Code config (~/.claude/claude_desktop_config.json):
```
```json
{
"mcpServers": {
"my-project": {
"command": "micro",
"args": ["mcp", "serve"]
}
}
}
```
Now in Claude Code:
```
"List all tasks that are blocked"
"Create a user account for the new hire"
"Check the health of all services"
```
### When to Use
- Developer productivity workflows
- Managing services during development
- Testing and debugging with natural language
## Pattern 7: LangChain / LlamaIndex Integration
Use the official Python SDKs to connect agent frameworks directly to your services.
### LangChain
```python
from langchain_go_micro import GoMicroToolkit
# Connect to MCP gateway
toolkit = GoMicroToolkit(
base_url="http://localhost:3000",
token="Bearer <token>",
)
# Get LangChain tools automatically
tools = toolkit.get_tools()
# Use with any LangChain agent
from langchain.agents import AgentExecutor, create_tool_calling_agent
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)
executor.invoke({"input": "Create a task for Alice"})
```
### LlamaIndex
```python
from go_micro_llamaindex import GoMicroToolkit
toolkit = GoMicroToolkit(
base_url="http://localhost:3000",
token="Bearer <token>",
)
# Use as LlamaIndex tools
tools = toolkit.to_tool_list()
# Use with a LlamaIndex agent
from llama_index.core.agent import ReActAgent
agent = ReActAgent.from_tools(tools, llm=llm)
agent.chat("What tasks are assigned to Bob?")
```
### When to Use
- Python-based agent pipelines
- RAG (Retrieval-Augmented Generation) workflows with LlamaIndex
- Multi-step LangChain chains that orchestrate your services
- Teams that prefer Python for AI/ML work
## Pattern 8: Standalone Gateway for Production
Run the MCP gateway as a separate, horizontally scalable process.
```
┌──────────────────┐
Claude/GPT/Agent ──→│ micro-mcp-gateway │──→ Service A (consul)
│ (standalone) │──→ Service B (consul)
└──────────────────┘──→ Service C (consul)
```
### Setup
```bash
micro-mcp-gateway \
--registry consul \
--registry-address consul:8500 \
--address :3000 \
--auth jwt \
--rate-limit 10 \
--rate-burst 20 \
--audit
```
Or via Docker:
```bash
docker run -p 3000:3000 ghcr.io/micro/micro-mcp-gateway \
--registry consul \
--registry-address consul:8500
```
### When to Use
- Production deployments where you want the gateway to scale independently
- Multiple teams deploying services but sharing one MCP endpoint
- Enterprise environments needing centralized auth and audit
## Pattern 9: Planning and Delegation
Built into the `Agent` abstraction. Every agent gets two harness tools — `plan` and `delegate` — with no extra setup. They are plain tools, not a separate graph runtime.
```
Conductor ──plan──→ (records ordered steps in memory)
──delegate──→ registered agent (RPC) or ephemeral sub-agent
```
### Setup
Nothing to wire — the tools are added to every agent automatically. Guide their use with the prompt:
```go
conductor := micro.NewAgent("conductor",
micro.AgentServices("task"),
micro.AgentPrompt(
"For multi-step requests, call the plan tool first to record your steps. "+
"For notifications, delegate to the \"comms\" agent (to: \"comms\")."),
micro.AgentProvider("anthropic"),
)
```
- **`plan`** records an ordered list of steps (`task` + `status`) in the agent's store-backed memory, surfaced back on later turns so it stays oriented.
- **`delegate`** hands a self-contained subtask to another agent. **Delegate-first**: if the target is a registered agent it's reached over RPC; otherwise a focused, short-lived sub-agent is created with a fresh, isolated context. A sub-agent is just an agent — created with `New`, talked to with `Ask`; there's no separate "spawn"/"fork" concept.
Full example: [examples/agent-plan-delegate](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate).
### When to Use
- Multi-step tasks where an explicit plan keeps the agent on track
- Multi-agent systems where domain experts own their own services and you want hand-offs to stay distributed (not one agent doing everything)
## Choosing a Pattern
| Pattern | Complexity | Best For |
|---------|-----------|----------|
| Single Agent | Low | Most applications, getting started |
| Scoped Agents | Medium | Multi-tenant, compliance |
| Agent as Consumer | Medium | AI-enhanced services |
| Tool Calling | Medium | Chatbots, assistants |
| Event-Driven | High | Automation, background processing |
| Claude Code | Low | Developer workflows |
| LangChain/LlamaIndex | Medium | Python agent pipelines, RAG |
| Standalone Gateway | Medium | Production, enterprise |
| Planning & Delegation | Medium | Multi-step tasks, distributed multi-agent systems |
Start with **Pattern 1** (single agent) and add complexity as needed. Most applications don't need multi-agent architectures.
## Anti-Patterns
### Don't: Chain Agents Without Coordination
```
Agent A → Agent B → Agent C (no shared state, no trace IDs)
```
Instead, use a single agent with multiple tools, or share trace IDs via metadata.
### Don't: Give Agents Unrestricted Access
```
Customer Agent → scopes=["*"] (dangerous!)
```
Always use the minimum required scopes. See the [MCP Security Guide](mcp-security.md).
### Don't: Skip Error Documentation
If agents don't know what errors are possible, they can't handle them gracefully. Always document error cases in your handler comments.
### Don't: Build Agent Logic into Services
Keep services as pure business logic. Let the agent harness handle orchestration, retries, and decision-making. Your service should just do one thing well.
## Next Steps
- [Building AI-Native Services](ai-native-services.md) - End-to-end tutorial
- [MCP Security Guide](mcp-security.md) - Auth and scopes
- [Tool Description Best Practices](tool-descriptions.md) - Better docs for agents
- [AI Package](../../ai/README.md) - AI provider interface
@@ -0,0 +1,201 @@
---
layout: default
---
# Agents and Workflows
Go Micro's AI primitives map directly onto the taxonomy in Anthropic's [Building Effective Agents](https://www.anthropic.com/engineering/building-effective-agents). That post draws one distinction that matters:
- **Workflows** — "LLMs and tools orchestrated through **predefined code paths**." Deterministic.
- **Agents** — "LLMs **dynamically direct their own processes** and tool usage." Model-driven.
Go Micro has both, plus the harness they run inside — and expresses them as plain services and tools, with no graph DSL. That's deliberate: the same post advises finding "the simplest solution possible" and being "cautious with frameworks… they obscure the underlying mechanics."
## The building block: the augmented LLM
Anthropic's foundational unit is the *augmented LLM* — a model with tools, retrieval, and memory. In Go Micro:
| Augmented LLM | Go Micro |
|---|---|
| the model | `ai` package (7 providers, one interface) |
| tools | every service endpoint, discovered from the registry |
| memory | the `store` (file, Postgres, NATS KV) |
Every endpoint is automatically a tool, so the augmented LLM is the default, not something you assemble.
## Workflow ↔ `flow`
A [`Flow`](../ai-integration.html) is a workflow in Anthropic's exact sense: a **predefined path** — an event on a broker topic triggers a prompt with a fixed set of tools, deterministically. Use it when the task is well-defined and you want predictability.
```go
f := micro.NewFlow("onboard-user",
micro.FlowTrigger("events.user.created"),
micro.FlowPrompt("New user {{.Data}} — create a workspace and send a welcome email."),
micro.FlowProvider("anthropic"),
)
```
### Flow triggers, Agent reasons
A flow doesn't have to do the reasoning itself. Point it at an agent and it becomes a pure trigger — the event fires, the flow renders the prompt, and a registered agent handles it over RPC with its full capabilities (plan, delegate, memory, guardrails):
```go
f := micro.NewFlow("onboard-user",
micro.FlowTrigger("events.user.created"),
micro.FlowPrompt("New user {{.Data}} — get them set up."),
micro.FlowAgent("conductor"), // the conductor agent reasons; the flow only triggers
)
```
This is the clean seam between the two halves of the taxonomy: the *workflow* (deterministic, event-driven) hands off to the *agent* (dynamic). One engine, two front doors — an event (`flow`) or a conversation (`agent.Ask`).
### Ordered, durable steps
A flow can be a **task made of ordered steps** rather than a single turn — the predefined path made explicit. Each step is checkpointed before and after, so if the process dies mid-run the run **resumes at the step it stopped on**, without re-running the steps that already completed (and already had their side effects). This is durable execution, store-backed by default, with no separate workflow engine.
```go
f := micro.NewFlow("checkout",
micro.FlowTrigger("events.order.placed"),
micro.FlowRetry(2), // retry each step; per-step override available
micro.FlowSteps(
micro.FlowStep{Name: "reserve", Run: micro.FlowCall("inventory", "Inventory.Reserve")},
micro.FlowStep{Name: "charge", Run: micro.FlowCall("payment", "Payment.Charge")},
micro.FlowStep{Name: "welcome", Run: micro.FlowDispatch("comms")}, // hand a step to an agent
),
// Durable by default; point the default store at Postgres/NATS KV to
// survive a real restart, or plug in Temporal/Restate via Checkpoint.
)
```
A step's action is an RPC (`FlowCall`), an agent hand-off (`FlowDispatch`), one model turn (`FlowLLM`), or any function. `State` carries a typed payload (`Set`/`Scan`) plus a `Stage` marker — the resume point. Runs are retained for success and failure (audit) unless you set `FlowDeleteOnSuccess`. On restart, `f.Pending(ctx)` lists incomplete runs and `f.Resume(ctx, runID)` continues one. See [examples/flow-durable](https://github.com/micro/go-micro/tree/master/examples/flow-durable).
The pluggability is the usual go-micro shape: the built-in `Checkpoint` is store-backed (swap the store backend freely); implement the `Checkpoint` interface to delegate durability to an external engine. Most teams need neither — the default is durable.
## Agent ↔ `agent`
An [`Agent`](plan-delegate.html) is an agent in Anthropic's exact sense: it **directs itself** — plans, calls tools, evaluates results, and decides the next step over many turns, with memory across them. Use it when you want flexibility and model-driven decisions.
```go
a := micro.NewAgent("conductor",
micro.AgentServices("task"),
micro.AgentProvider("anthropic"),
)
a.Ask(ctx, "Plan the launch, create the tasks, and have comms notify the owner.")
```
### Long-running memory
Agents use store-backed conversation memory by default, scoped under the agent's
name. That makes short restarts boring: the next `Ask` reloads the retained
history from the same store backend you already use for services and flows.
Long-running agents can also keep model context bounded without losing useful
prior context. If you want retrieval without summaries, enable bounded active
context plus a durable archive of every turn:
```go
a := micro.NewAgent("conductor",
micro.AgentServices("task"),
micro.AgentProvider("anthropic"),
micro.AgentRetrievalMemory(40), // active messages kept in prompt context
micro.AgentMemoryRecallLimit(5), // archived turns recalled per Ask
)
```
`AgentRetrievalMemory(activeLimit)` switches the default memory to a store-backed
retriever. The active conversation is capped at `activeLimit`, every turn is
archived in the same scoped store used by the agent, and future asks inject
matching archived turns ahead of active context. The built-in ranking is
deterministic and credential-free for CI.
When you also want a rolling summary in active context, use compacting memory:
```go
a := micro.NewAgent("conductor",
micro.AgentServices("task"),
micro.AgentProvider("anthropic"),
micro.AgentCompactMemory(40, 12), // max active messages, recent messages kept verbatim
micro.AgentMemoryRecallLimit(5), // compacted turns recalled per Ask
)
```
`AgentCompactMemory(maxMessages, keepRecent)` switches the default memory to a
deterministic compactor. Once active history grows past `maxMessages`, older
turns move into the durable archive, a provider-neutral summary is injected into
active context, and the newest `keepRecent` messages stay verbatim. On future
asks, archived turns whose text matches the current request are recalled ahead of
the active context. Teams that need embeddings or a vector database can still
provide their own `AgentMemory` implementation.
This is harness memory, not prompt-layer orchestration: services remain the
capabilities, agents remain the dynamic decision makers, and flows remain the
durable predefined paths. Compaction only keeps a scheduled or looping agent from
turning every past turn into model context while still letting it remember facts
that matter to the current service → agent → workflow run.
Checkpointed agent runs and compacted memory share the same store-backed shape.
If a provider call fails after the prompt has been recorded, `agent.Resume` uses
the checkpointed run id and does not append that same user turn a second time;
completed tool results and recalled archived memory remain available for the
retry.
## The patterns — most are already here
Anthropic lists five workflow patterns. Go Micro implements the two richest ones natively, as services and tools, and the rest are ordinary compositions:
| Pattern | Go Micro |
|---|---|
| **Routing** — classify input, dispatch to a specialist | `micro chat`'s router — discovers agents, classifies intent, routes over RPC |
| **Orchestrator-workers** — a central LLM breaks down a task, delegates to workers, synthesizes | the `agent` with **`plan`** (break down) + **`delegate`** (hand to workers) + reply (synthesize) — see [Plan & Delegate](plan-delegate.html) |
| **Prompt chaining** — sequential steps | chain flows, or steps in an agent's plan |
| **Parallelization** — independent subtasks at once | Go concurrency + multiple services/agents; fan out with `delegate` |
| **Evaluator-optimizer** — one LLM generates, another critiques in a loop | two agents over RPC (generator + evaluator) |
The orchestrator-workers example is worth calling out: the conductor agent that plans, creates tasks, and delegates the notification to a `comms` agent **is** orchestrator-workers — built without a graph engine. See [examples/agent-plan-delegate](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate).
## Choosing
Follow Anthropic's guidance:
- Start with the **augmented LLM** (a single service call through a model). Most tasks need nothing more.
- Reach for a **workflow** (`flow`) when the path is well-defined and you want predictability.
- Reach for an **agent** (`agent`) when the task needs flexibility and model-driven decisions — and accept the higher cost and the need for guardrails.
## Guardrails
Anthropic is emphatic that autonomous agents need stopping conditions, human checkpoints, and sandboxed testing. Go Micro's agent has two built-in guardrails, both as plain options:
**Stopping condition**`MaxSteps` bounds the number of actions an agent may take per `Ask`. Once exceeded, further tool calls are refused and the model is told to stop and summarize.
```go
micro.NewAgent("conductor",
micro.AgentServices("task"),
micro.AgentMaxSteps(8), // at most 8 tool calls per request
)
```
**Human-in-the-loop**`ApproveTool` gates each action before it runs. Return `false` to block it; the reason is shown to the model so it can adapt. The internal `plan` tool is never gated (it's bookkeeping, not an action).
```go
micro.NewAgent("conductor",
micro.AgentServices("task"),
micro.AgentApproveTool(func(tool string, input map[string]any) (bool, string) {
if strings.HasPrefix(tool, "billing_") {
return false, "billing actions require human sign-off"
}
return true, ""
}),
)
```
These are harness guardrails, not a separate policy engine — a counter and a callback on the path every tool call already takes. For anything that must be predictable, still prefer a **workflow**, and test agents against the [integration harness](https://github.com/micro/go-micro/tree/master/internal/harness/plan-delegate).
## Why no graph DSL
Anthropic: "be cautious with frameworks… understand the underlying code." Go Micro's answer is that there is no separate framework to understand — the harness is the service runtime. Workflows and agents are services, and tool use is RPC. `plan` and `delegate` are tools, not a graph DSL. The patterns above are code you can read, not a DSL you have to learn. That's the [direction we took going all in on AI](/blog/14).
## See also
- [Building Effective Agents](https://www.anthropic.com/engineering/building-effective-agents) — Anthropic
- [Plan & Delegate](plan-delegate.html) — the agent's built-in tools
- [Agent Integration Patterns](agent-patterns.html) — multi-agent architectures
- [AI Integration](../ai-integration.html) — agents, flows, and the model interface
@@ -0,0 +1,410 @@
---
layout: default
---
# Building AI-Native Services
This guide walks you through building a Go Micro service that is AI-native from the start — meaning AI agents can discover, understand, and call your service automatically via the Model Context Protocol (MCP).
## What You'll Build
A **task management service** with full CRUD operations that:
- Exposes every endpoint as an MCP tool automatically
- Has rich documentation that agents can read
- Includes auth scopes for write operations
- Works with Claude Code, the agent playground, and any MCP client
## Prerequisites
```bash
go install go-micro.dev/v6/cmd/micro@latest
```
## Step 1: Create the Service
```bash
micro new tasks
cd tasks
```
## Step 2: Define Your Types
Design your request/response types with `description` tags. These tags become parameter descriptions that agents read:
```go
package main
import "context"
// Request types with description tags for AI agents
type Task struct {
ID string `json:"id" description:"Unique task identifier"`
Title string `json:"title" description:"Short task title (max 100 chars)"`
Description string `json:"description" description:"Detailed task description"`
Status string `json:"status" description:"Task status: todo, in_progress, or done"`
Assignee string `json:"assignee,omitempty" description:"Username of assigned person"`
}
type CreateRequest struct {
Title string `json:"title" description:"Task title (required, max 100 chars)"`
Description string `json:"description" description:"Detailed description of the task"`
Assignee string `json:"assignee,omitempty" description:"Username to assign the task to"`
}
type CreateResponse struct {
Task *Task `json:"task" description:"The newly created task"`
}
type GetRequest struct {
ID string `json:"id" description:"Task ID to retrieve"`
}
type GetResponse struct {
Task *Task `json:"task" description:"The requested task"`
}
type ListRequest struct {
Status string `json:"status,omitempty" description:"Filter by status: todo, in_progress, done (optional)"`
}
type ListResponse struct {
Tasks []*Task `json:"tasks" description:"List of matching tasks"`
}
type UpdateRequest struct {
ID string `json:"id" description:"Task ID to update"`
Status string `json:"status" description:"New status: todo, in_progress, or done"`
}
type UpdateResponse struct {
Task *Task `json:"task" description:"The updated task"`
}
type DeleteRequest struct {
ID string `json:"id" description:"Task ID to delete"`
}
type DeleteResponse struct {
Deleted bool `json:"deleted" description:"True if the task was deleted"`
}
```
**Key point:** The `description` tags are parsed by the MCP gateway and shown to agents as parameter documentation. Be specific about formats, constraints, and valid values.
## Step 3: Write the Handler with Doc Comments
Write standard Go doc comments on every handler method. The MCP gateway extracts these automatically at registration time.
```go
type TaskService struct {
tasks map[string]*Task
nextID int
}
// Create creates a new task with the given title and description.
// Returns the created task with a generated ID and initial status of "todo".
//
// @example {"title": "Fix login bug", "description": "Users can't log in with SSO", "assignee": "alice"}
func (t *TaskService) Create(ctx context.Context, req *CreateRequest, rsp *CreateResponse) error {
t.nextID++
task := &Task{
ID: fmt.Sprintf("task-%d", t.nextID),
Title: req.Title,
Description: req.Description,
Status: "todo",
Assignee: req.Assignee,
}
t.tasks[task.ID] = task
rsp.Task = task
return nil
}
// Get retrieves a task by its unique ID.
// Returns an error if the task does not exist.
//
// @example {"id": "task-1"}
func (t *TaskService) Get(ctx context.Context, req *GetRequest, rsp *GetResponse) error {
task, ok := t.tasks[req.ID]
if !ok {
return fmt.Errorf("task %s not found", req.ID)
}
rsp.Task = task
return nil
}
// List returns all tasks, optionally filtered by status.
// If no status filter is provided, returns all tasks.
// Valid status values: "todo", "in_progress", "done".
//
// @example {"status": "todo"}
func (t *TaskService) List(ctx context.Context, req *ListRequest, rsp *ListResponse) error {
for _, task := range t.tasks {
if req.Status == "" || task.Status == req.Status {
rsp.Tasks = append(rsp.Tasks, task)
}
}
return nil
}
// Update changes the status of an existing task.
// Valid status transitions: todo -> in_progress -> done.
// Returns an error if the task does not exist.
//
// @example {"id": "task-1", "status": "in_progress"}
func (t *TaskService) Update(ctx context.Context, req *UpdateRequest, rsp *UpdateResponse) error {
task, ok := t.tasks[req.ID]
if !ok {
return fmt.Errorf("task %s not found", req.ID)
}
task.Status = req.Status
rsp.Task = task
return nil
}
// Delete removes a task by ID. This action is irreversible.
// Returns an error if the task does not exist.
//
// @example {"id": "task-1"}
func (t *TaskService) Delete(ctx context.Context, req *DeleteRequest, rsp *DeleteResponse) error {
if _, ok := t.tasks[req.ID]; !ok {
return fmt.Errorf("task %s not found", req.ID)
}
delete(t.tasks, req.ID)
rsp.Deleted = true
return nil
}
```
**What agents see:** Each method's doc comment becomes the tool description. The `@example` tag provides a valid JSON input that agents can reference.
## Step 4: Register with Scopes
Use `server.WithEndpointScopes()` to control which agents can call which endpoints:
```go
package main
import (
"context"
"fmt"
"go-micro.dev/v6"
"go-micro.dev/v6/server"
)
func main() {
service := micro.NewService("tasks", micro.Address(":8081"))
service.Init()
service.Handle(
&TaskService{tasks: make(map[string]*Task)},
// Read operations: any authenticated agent
server.WithEndpointScopes("TaskService.Get", "tasks:read"),
server.WithEndpointScopes("TaskService.List", "tasks:read"),
// Write operations: agents with write scope
server.WithEndpointScopes("TaskService.Create", "tasks:write"),
server.WithEndpointScopes("TaskService.Update", "tasks:write"),
// Delete: admin only
server.WithEndpointScopes("TaskService.Delete", "tasks:admin"),
)
service.Run()
}
```
## Step 5: Run with MCP
There are three ways to run your service with MCP enabled.
### Option A: `micro run` (Recommended for Development)
```bash
micro run
```
Your service is now available at:
- **Web Dashboard:** http://localhost:8080/
- **Agent Playground:** http://localhost:8080/agent
- **MCP Tools:** http://localhost:8080/mcp/tools
- **WebSocket:** ws://localhost:3000/mcp/ws
- **API Gateway:** http://localhost:8080/api/tasks/TaskService/Create
### Option B: `WithMCP` (One-Liner for Library Users)
Add MCP to your service with a single option:
```go
import "go-micro.dev/v6/gateway/mcp"
func main() {
service := micro.NewService("tasks",
mcp.WithMCP(":3000"), // MCP gateway starts automatically
)
service.Init()
// register handlers...
service.Run()
}
```
This starts the MCP gateway on port 3000 alongside your service. All registered handlers are automatically exposed as MCP tools.
### Option C: Standalone MCP Gateway
For production, run the MCP gateway as a separate process that discovers all services:
```bash
micro-mcp-gateway \
--registry consul \
--registry-address consul:8500 \
--address :3000 \
--auth jwt \
--rate-limit 10
```
See the [standalone gateway docs](../deployment.md) for more.
### Use with Claude Code
```bash
# Start MCP server for Claude Code (stdio transport)
micro mcp serve
```
Add to your Claude Code config:
```json
{
"mcpServers": {
"tasks": {
"command": "micro",
"args": ["mcp", "serve"]
}
}
}
```
Now Claude can manage your tasks:
```
You: "Create a task to fix the login bug and assign it to alice"
Claude: [calls tasks.TaskService.Create with {"title": "Fix login bug", ...}]
Created task-1: "Fix login bug" assigned to alice.
You: "What tasks does alice have?"
Claude: [calls tasks.TaskService.List]
Alice has 1 task: "Fix login bug" (status: todo)
You: "Mark it as in progress"
Claude: [calls tasks.TaskService.Update with {"id": "task-1", "status": "in_progress"}]
Updated task-1 to "in_progress".
```
### Use with WebSocket Clients
For real-time bidirectional communication (e.g., streaming agent frameworks):
```javascript
const ws = new WebSocket("ws://localhost:3000/mcp/ws", {
headers: { "Authorization": "Bearer <token>" }
});
// JSON-RPC 2.0 over WebSocket
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/list",
params: {}
}));
```
## Step 6: Test Your Tools
Use the CLI to verify tools work:
```bash
# List all available tools
micro mcp list
# Test a specific tool
micro mcp test tasks.TaskService.Create
# Generate documentation
micro mcp docs
# Export for LangChain
micro mcp export --format langchain
```
## Step 7: Add Observability (Optional)
Enable OpenTelemetry tracing to see every agent tool call as a distributed trace:
```go
import (
"go.opentelemetry.io/otel"
"go-micro.dev/v6/gateway/mcp"
)
go mcp.ListenAndServe(":3000", mcp.Options{
Registry: service.Options().Registry,
TraceProvider: otel.GetTracerProvider(),
})
```
Each tool call generates a span with attributes:
- `mcp.tool.name` — which tool was called
- `mcp.transport` — HTTP, WebSocket, or stdio
- `mcp.account.id` — who called it
- `mcp.auth.allowed` — whether it was permitted
Trace context is propagated downstream via metadata headers (`Mcp-Trace-Id`, `Mcp-Tool-Name`, `Mcp-Account-Id`), so you get full distributed traces from agent through gateway to service.
## Step 8: Use the AI Package (Optional)
If your service needs to call AI models directly:
```go
import (
"go-micro.dev/v6/ai"
_ "go-micro.dev/v6/ai/anthropic"
)
m := ai.New("anthropic",
ai.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
)
resp, err := m.Generate(ctx, &ai.Request{
Prompt: "Summarize these tasks: " + taskJSON,
SystemPrompt: "You are a project manager assistant",
})
```
## Checklist
Before shipping an AI-native service:
- [ ] Every handler method has a doc comment explaining what it does
- [ ] Every method has an `@example` tag with realistic JSON input
- [ ] Request struct fields have `description` tags
- [ ] Write/delete operations have auth scopes
- [ ] You've tested with `micro mcp test` to verify tools work
- [ ] You've tested with Claude Code or the agent playground
## What Happens Under the Hood
```
1. You write Go comments on handler methods
2. micro registers the handler and extracts docs via go/ast
3. Docs are stored in the service registry as endpoint metadata
4. MCP gateway discovers services via the registry
5. Gateway generates JSON Schema tools with descriptions
6. AI agents query the tools endpoint and see rich descriptions
7. Agents call tools via JSON-RPC, gateway routes to your handler
```
## Next Steps
- [MCP Security Guide](mcp-security.md) - Configure auth and scopes for production
- [Tool Description Best Practices](tool-descriptions.md) - Write comments that make agents smarter
- [Agent Integration Patterns](agent-patterns.md) - Multi-agent workflows
- [MCP Documentation](../mcp.md) - Full MCP reference
@@ -0,0 +1,380 @@
# Adding an AI Provider to Go Micro
This guide walks you through implementing a new AI model provider for
go-micro's `ai` package. After following these steps your provider will
be available via `ai.New("yourprovider")` and automatically usable by the
MCP gateway, the agent playground, and any service that calls
`service.Model()`.
## Overview
The `ai` package uses the same plugin pattern as the rest of go-micro:
define an interface, register an implementation, and let users swap
providers with a single import. All providers live under `ai/<name>/`.
**Files you will create:**
```
ai/
└── yourprovider/
├── yourprovider.go # Provider implementation
└── yourprovider_test.go # Unit tests
```
## Discover registered provider capabilities
Go Micro exposes the provider interfaces registered in the current build, so
runtime tooling and docs can report what is actually available after blank
imports are linked in:
```go
for _, row := range ai.CapabilityRows() {
fmt.Printf("%s: chat=%t image=%t video=%t stream=%t tool_stream=%t\n", row.Provider, row.Model, row.Image, row.Video, row.Stream, row.ToolStream)
}
```
The built-in providers currently register these capability interfaces:
| Provider | Chat/text (`ai.Model`) | Image (`ai.ImageModel`) | Video (`ai.VideoModel`) | Streaming (`ai.Stream`) | Tool streaming |
| --- | --- | --- | --- | --- | --- |
| `anthropic` | Yes | No | No | Yes | Yes |
| `atlascloud` | Yes | Yes | Yes | Yes | No |
| `gemini` | Yes | No | No | Yes | No |
| `groq` | Yes | No | No | Yes | Yes |
| `minimax` | Yes | No | No | Yes | Yes |
| `mistral` | Yes | No | No | Yes | Yes |
| `ollama` | Yes | No | No | Yes | Yes |
| `openai` | Yes | Yes | No | Yes | Yes |
| `together` | Yes | No | No | Yes | Yes |
## Step 1: Implement the `ai.Model` Interface
Every provider must satisfy `ai.Model`:
```go
type Model interface {
Init(...Option) error
Options() Options
Generate(ctx context.Context, req *Request, opts ...GenerateOption) (*Response, error)
Stream(ctx context.Context, req *Request, opts ...GenerateOption) (Stream, error)
String() string
}
```
### Skeleton
Create `ai/yourprovider/yourprovider.go`:
```go
package yourprovider
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"go-micro.dev/v6/ai"
)
func init() {
ai.Register("yourprovider", func(opts ...ai.Option) ai.Model {
return NewProvider(opts...)
})
}
type Provider struct {
opts ai.Options
}
func NewProvider(opts ...ai.Option) *Provider {
options := ai.NewOptions(opts...)
if options.Model == "" {
options.Model = "your-default-model"
}
if options.BaseURL == "" {
options.BaseURL = "https://api.yourprovider.com"
}
return &Provider{opts: options}
}
func (p *Provider) Init(opts ...ai.Option) error {
for _, o := range opts {
o(&p.opts)
}
return nil
}
func (p *Provider) Options() ai.Options { return p.opts }
func (p *Provider) String() string { return "yourprovider" }
```
### `Generate`
`Generate` is the core method. It must:
1. Convert `req.Tools` into the provider's native tool format.
2. Send the request to the provider API.
3. Parse the response into `ai.Response` (text in `Reply`, tool calls in
`ToolCalls`).
4. If `p.opts.ToolHandler` is set **and** there are tool calls, execute
each tool and make a follow-up API call to get the final answer in
`Answer`.
```go
func (p *Provider) Generate(ctx context.Context, req *ai.Request, opts ...ai.GenerateOption) (*ai.Response, error) {
// 1. Build provider-specific tool definitions
var tools []map[string]any
for _, t := range req.Tools {
tools = append(tools, map[string]any{
// Map to your provider's schema
"name": t.Name,
"description": t.Description,
"parameters": map[string]any{
"type": "object",
"properties": t.Properties,
},
})
}
// 2. Build the API request body
apiReq := map[string]any{
"model": p.opts.Model,
"messages": []map[string]any{
{"role": "system", "content": req.SystemPrompt},
{"role": "user", "content": req.Prompt},
},
}
if len(tools) > 0 {
apiReq["tools"] = tools
}
// 3. Call the API
resp, rawMsg, err := p.callAPI(ctx, apiReq)
if err != nil {
return nil, err
}
// 4. No tool calls → return immediately
if len(resp.ToolCalls) == 0 {
return resp, nil
}
// 5. Execute tools and follow up
if p.opts.ToolHandler != nil {
// ... build follow-up messages with tool results ...
followUpResp, _, err := p.callAPI(ctx, followUpReq)
if err == nil && followUpResp.Reply != "" {
resp.Answer = followUpResp.Reply
}
}
return resp, nil
}
```
### `Stream`
If streaming is not supported yet, return a clear error:
```go
func (p *Provider) Stream(ctx context.Context, req *ai.Request, opts ...ai.GenerateOption) (ai.Stream, error) {
return nil, fmt.Errorf("streaming not yet implemented for yourprovider")
}
```
### API Helper
Use `net/http` directly — no external SDK needed:
```go
func (p *Provider) callAPI(ctx context.Context, req map[string]any) (*ai.Response, map[string]any, error) {
reqBody, err := json.Marshal(req)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal request: %w", err)
}
apiURL := strings.TrimRight(p.opts.BaseURL, "/") + "/v1/chat/completions"
httpReq, err := http.NewRequestWithContext(ctx, "POST", apiURL, bytes.NewReader(reqBody))
if err != nil {
return nil, nil, fmt.Errorf("failed to create request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+p.opts.APIKey)
httpResp, err := http.DefaultClient.Do(httpReq)
if err != nil {
return nil, nil, fmt.Errorf("API request failed: %w", err)
}
defer httpResp.Body.Close()
respBody, _ := io.ReadAll(httpResp.Body)
if httpResp.StatusCode != 200 {
return nil, nil, fmt.Errorf("API error (%s): %s", httpResp.Status, string(respBody))
}
// Parse your provider's response format into ai.Response
// ...
}
```
## Step 2: Write Tests
Create `ai/yourprovider/yourprovider_test.go`. At minimum test:
- **`String()`** returns the correct name.
- **`Init()`** applies options.
- **Default values** are set when no options are provided.
- **`Generate()` without API key** returns an error.
- **`Stream()` not implemented** returns an error.
```go
package yourprovider
import (
"context"
"testing"
"go-micro.dev/v6/ai"
)
func TestProvider_String(t *testing.T) {
p := NewProvider()
if p.String() != "yourprovider" {
t.Errorf("got %q, want %q", p.String(), "yourprovider")
}
}
func TestProvider_Defaults(t *testing.T) {
p := NewProvider()
opts := p.Options()
if opts.Model != "your-default-model" {
t.Errorf("default model = %q, want %q", opts.Model, "your-default-model")
}
if opts.BaseURL != "https://api.yourprovider.com" {
t.Errorf("default base URL = %q", opts.BaseURL)
}
}
func TestProvider_Init(t *testing.T) {
p := NewProvider()
if err := p.Init(ai.WithModel("custom"), ai.WithAPIKey("key")); err != nil {
t.Fatalf("Init: %v", err)
}
if p.Options().Model != "custom" {
t.Errorf("model not updated")
}
}
func TestProvider_Generate_NoAPIKey(t *testing.T) {
p := NewProvider()
_, err := p.Generate(context.Background(), &ai.Request{Prompt: "hi"})
if err == nil {
t.Error("expected error without API key")
}
}
func TestProvider_Stream_NotImplemented(t *testing.T) {
p := NewProvider()
_, err := p.Stream(context.Background(), &ai.Request{Prompt: "hi"})
if err == nil {
t.Error("expected error for unimplemented streaming")
}
}
```
Run:
```bash
go test ./ai/yourprovider/...
```
## Step 3: Register the Provider
The `init()` function in your package calls `ai.Register`. Users enable
your provider with a blank import:
```go
import _ "go-micro.dev/v6/ai/yourprovider"
```
Then use it:
```go
m := ai.New("yourprovider",
ai.WithAPIKey("your-api-key"),
ai.WithModel("your-model-name"),
)
resp, err := m.Generate(ctx, &ai.Request{
Prompt: "Hello!",
SystemPrompt: "You are a helpful assistant",
})
```
## Step 4: Update the README
Add your provider to the **Supported AI Providers** section in the
project README.md. Follow the existing format:
```markdown
### YourProvider
```go
m := ai.New("yourprovider",
ai.WithAPIKey("your-key"),
ai.WithModel("your-default-model"),
)
```
Default model: `your-default-model`
Default base URL: `https://api.yourprovider.com`
```
Also add an entry in `ai/README.md` under "Supported Providers".
## Checklist
Before submitting your PR:
- [ ] `ai/yourprovider/yourprovider.go` implements `ai.Model`
- [ ] `init()` calls `ai.Register("yourprovider", ...)`
- [ ] `Generate()` handles tool calls via `ToolHandler` when set
- [ ] `ai/yourprovider/yourprovider_test.go` covers basics
- [ ] `go test ./ai/yourprovider/...` passes
- [ ] `go vet ./ai/yourprovider/...` is clean
- [ ] Provider added to `ai/README.md` under "Supported Providers"
- [ ] Provider added to project README.md under "Supported AI Providers"
- [ ] No new dependencies beyond `go-micro.dev/v6/ai` and stdlib (use
`net/http` directly rather than an SDK)
## Design Notes
**Why `net/http` instead of an SDK?** Keeping providers dependency-free
means `go get go-micro.dev/v6` never pulls in heavy SDK trees. All
existing providers (Anthropic, OpenAI) use raw HTTP for the same reason.
**OpenAI-compatible APIs.** Many providers (Together, Groq, Fireworks,
Atlas Cloud, etc.) expose an OpenAI-compatible `/v1/chat/completions`
endpoint. In that case, users can often just use the `openai` provider
with `ai.WithBaseURL("https://api.yourprovider.com")`. A dedicated
provider package is only needed when the API differs or you want to set
provider-specific defaults.
**Tool call loop.** The current contract is one round of tool execution:
`Generate` calls tools via `ToolHandler`, feeds results back, and
returns the final answer. Multi-turn agentic loops are handled at a
higher level (e.g. the MCP gateway).
## Sponsorship
If you are an AI infrastructure company interested in becoming a
supported provider, we welcome both code contributions and sponsorships.
See the Supported AI Providers section in the project README for
current partners, and reach out via a GitHub issue or the Discord
community to discuss integration.
@@ -0,0 +1,303 @@
---
layout: default
title: Atlas Cloud Integration
---
# Atlas Cloud Integration Guide
[Atlas Cloud](https://www.atlascloud.ai/) is an enterprise AI infrastructure platform offering 300+ models across text, image, and video through a unified, OpenAI-compatible API. It is an official Go Micro sponsor and a first-class provider in the `ai` package.
## Quick Start
Install or update Go Micro:
```bash
go get go-micro.dev/v6@latest
```
Import the Atlas Cloud provider and use it:
```go
package main
import (
"context"
"fmt"
"log"
"go-micro.dev/v6/ai"
_ "go-micro.dev/v6/ai/atlascloud"
)
func main() {
m := ai.New("atlascloud",
ai.WithAPIKey("your-atlas-cloud-key"),
)
resp, err := m.Generate(context.Background(), &ai.Request{
Prompt: "What is Go Micro?",
SystemPrompt: "You are a helpful assistant.",
})
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Reply)
}
```
## Configuration
### Options
| Option | Default | Description |
|--------|---------|-------------|
| `ai.WithAPIKey(key)` | *required* | Your Atlas Cloud API key |
| `ai.WithModel(name)` | `llama-3.3-70b` | Model to use (see [Model Selection](#model-selection)) |
| `ai.WithBaseURL(url)` | `https://api.atlascloud.ai` | API base URL |
### Environment Variables
The `micro chat` CLI and `micro run` / `micro server` read configuration from environment variables:
| Variable | Description |
|----------|-------------|
| `ATLASCLOUD_API_KEY` | API key (used by `micro chat --provider atlascloud`) |
| `MICRO_AI_API_KEY` | Generic API key (used by all providers) |
| `MICRO_AI_PROVIDER` | Set to `atlascloud` to select the provider |
| `MICRO_AI_MODEL` | Override the default model |
| `MICRO_AI_BASE_URL` | Override the base URL |
When using `micro chat`, the provider-specific variable takes precedence:
```bash
ATLASCLOUD_API_KEY=your-key micro chat --provider atlascloud
```
When using `micro run` or `micro server`, set the generic variables:
```bash
export MICRO_AI_API_KEY=your-key
export MICRO_AI_BASE_URL=https://api.atlascloud.ai
micro run
```
The server auto-detects Atlas Cloud from the base URL.
## Model Selection
Atlas Cloud offers 300+ models. Some popular choices for the chat completions API:
| Model | Use Case |
|-------|----------|
| `llama-3.3-70b` | General-purpose (default) |
| `deepseek-v4` | Coding and reasoning |
| `qwen-3.6` | Multilingual |
Check [atlascloud.ai](https://www.atlascloud.ai/) for the full model catalog. New SOTA models are available on day zero of release.
```go
m := ai.New("atlascloud",
ai.WithAPIKey(key),
ai.WithModel("deepseek-v4"),
)
```
## Image Generation
Atlas Cloud supports text-to-image generation through the `ai.ImageModel` interface. This uses the same OpenAI-compatible `/v1/images/generations` endpoint.
```go
import (
"context"
"fmt"
"go-micro.dev/v6/ai"
_ "go-micro.dev/v6/ai/atlascloud"
)
func main() {
ig := ai.NewImage("atlascloud",
ai.WithAPIKey("your-key"),
)
resp, err := ig.GenerateImage(context.Background(), &ai.ImageRequest{
Prompt: "A Go gopher building microservices, digital art",
Size: "1024x1024",
})
if err != nil {
log.Fatal(err)
}
// Image returned as URL or base64, depending on the model
fmt.Println(resp.Images[0].URL)
}
```
### ImageRequest Options
| Field | Default | Description |
|-------|---------|-------------|
| `Prompt` | *required* | Text description of the image |
| `Model` | `gpt-image-1` | Image model to use |
| `Size` | provider default | Image dimensions (e.g. `"1024x1024"`) |
| `N` | `1` | Number of images to generate |
### Available Image Models
Atlas Cloud offers image models including `gpt-image-1`, `flux-2`, `nano-banana-pro`, and more. Check [atlascloud.ai](https://www.atlascloud.ai/) for the full catalog.
```go
ig.GenerateImage(ctx, &ai.ImageRequest{
Prompt: "A mountain landscape",
Model: "flux-2",
Size: "1024x1024",
N: 2,
})
```
The `ai.ImageModel` interface is also implemented by the OpenAI provider, so switching between providers is a one-line change.
## Using with Services (Tool Calling)
Atlas Cloud supports OpenAI-compatible function calling. Combined with Go Micro's `ai.Tools`, your services become tools that the model can call:
```go
package main
import (
"context"
"fmt"
"log"
"go-micro.dev/v6"
"go-micro.dev/v6/ai"
_ "go-micro.dev/v6/ai/atlascloud"
)
func main() {
service := micro.NewService("my-agent")
service.Init()
// Discover all services as tools
tools := ai.NewTools(service.Registry())
discovered, err := tools.Discover()
if err != nil {
log.Fatal(err)
}
// Create a model with tool execution
m := ai.New("atlascloud",
ai.WithAPIKey("your-key"),
ai.WithTools(tools),
)
// The model can now call your services
resp, err := m.Generate(context.Background(), &ai.Request{
Prompt: "List all users and send each a welcome email",
SystemPrompt: "You are a service orchestrator.",
Tools: discovered,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Answer)
}
```
### How it works
1. `ai.NewTools(registry)` creates a tool set bound to the service registry
2. `tools.Discover()` walks the registry and returns every endpoint as an `ai.Tool`
3. `ai.WithTools(tools)` wires execution into the model — tool calls are routed via RPC
4. When the model decides to call a tool, it routes to the correct service
This works identically across all providers. Swap `"atlascloud"` for `"anthropic"` or `"openai"` and the same services, tools, and handlers work without changes.
## Using with micro chat
`micro chat` is an interactive terminal agent. Start your services, then chat:
```bash
# Terminal 1: start services
micro run
# Terminal 2: chat with Atlas Cloud
ATLASCLOUD_API_KEY=your-key micro chat --provider atlascloud
> what services are running?
> get user alice@example.com
> create a new order for product-42
```
For a single prompt (non-interactive):
```bash
micro chat --provider atlascloud --prompt "list all services"
```
## Using with micro run
The agent playground at `/agent` uses whatever AI provider is configured. To use Atlas Cloud:
```bash
export MICRO_AI_API_KEY=your-atlas-cloud-key
export MICRO_AI_BASE_URL=https://api.atlascloud.ai
micro run
```
Open `http://localhost:8080/agent` and chat with your services through Atlas Cloud.
## Using with MCP
The MCP gateway (`micro mcp serve`) exposes services as tools for external AI agents. Atlas Cloud's models can be used by any MCP-compatible agent that connects to the gateway. The gateway itself doesn't depend on a specific AI provider — it serves tools over MCP, and the agent on the other end chooses which model to use.
## Swapping Providers
All Go Micro AI providers implement the same `ai.Model` interface. To switch from Atlas Cloud to another provider, change the import and the provider name:
```go
// Atlas Cloud
import _ "go-micro.dev/v6/ai/atlascloud"
m := ai.New("atlascloud", ai.WithAPIKey(key))
// Anthropic
import _ "go-micro.dev/v6/ai/anthropic"
m := ai.New("anthropic", ai.WithAPIKey(key))
// OpenAI
import _ "go-micro.dev/v6/ai/openai"
m := ai.New("openai", ai.WithAPIKey(key))
```
The rest of your code — tool discovery, handler wiring, request/response handling — stays the same.
## API Compatibility
Atlas Cloud exposes an OpenAI-compatible `/v1/chat/completions` endpoint. This means:
- **Existing OpenAI SDK code** works by changing the base URL
- **Tool calling** uses the same `tools` and `tool_calls` format as OpenAI
- **Streaming** follows the OpenAI SSE format (when implemented)
If you're already using the `openai` provider, you can point it at Atlas Cloud directly:
```go
import _ "go-micro.dev/v6/ai/openai"
m := ai.New("openai",
ai.WithAPIKey("your-atlas-cloud-key"),
ai.WithBaseURL("https://api.atlascloud.ai"),
ai.WithModel("llama-3.3-70b"),
)
```
The dedicated `atlascloud` provider simply sets these defaults for you.
## Links
- [Atlas Cloud](https://www.atlascloud.ai/) — Sign up and get an API key
- [AI Provider Integration Guide](/docs/guides/ai-provider-guide) — How providers are built
- [ai.Tools](https://pkg.go.dev/go-micro.dev/v6/ai.Tools) — Service-to-tool discovery
- [Blog: Atlas Cloud Sponsors Go Micro](/blog/8) — Announcement post
+427
View File
@@ -0,0 +1,427 @@
---
layout: default
---
# CLI & Gateway Guide
The Go Micro CLI provides two gateway modes for accessing your microservices: development (`micro run`) and production (`micro server`). Both use the same underlying gateway architecture, ensuring consistent behavior across environments.
## Overview
```
┌─────────────────────┐
│ HTTP Requests │
└──────────┬──────────┘
┌──────────▼──────────┐
│ Unified Gateway │
│ │
│ • Service Discovery│
│ • HTTP → RPC │
│ • Web Dashboard │
│ • Health Checks │
└──────────┬──────────┘
┌──────────▼──────────┐
│ Your Services │
│ (via Registry) │
└─────────────────────┘
```
## Quick Comparison
| Feature | `micro run` | `micro server` |
|---------|-------------|----------------|
| **Purpose** | Local development | Production API gateway |
| **Authentication** | Yes (default `admin`/`micro`) | Yes (default `admin`/`micro`) |
| **Process Management** | Yes (builds & runs services) | No (services run separately) |
| **Hot Reload** | Yes (watches file changes) | No |
| **Endpoint Scopes** | Yes (`/auth/scopes`) | Yes (`/auth/scopes`) |
| **Best For** | Coding, testing, iteration | Deployed environments |
## Development Mode: `micro run`
### Quick Start
```bash
# Create and run a service
micro new myservice
cd myservice
micro run
```
Open http://localhost:8080 - no login required!
### What You Get
- **Instant Gateway**: HTTP API at `/api/{service}/{method}`
- **Web Dashboard**: Browse and test services at `/`
- **Hot Reload**: Code changes trigger automatic rebuild
- **Authentication**: JWT auth with default credentials (`admin`/`micro`)
- **Scopes**: Endpoint access control via `/auth/scopes`
### Example Usage
```bash
# Start with hot reload
micro run
# Log in at http://localhost:8080 with admin/micro
# Or use a token for API calls:
curl -X POST http://localhost:8080/api/myservice/Handler.Call \
-H "Authorization: Bearer <token>" \
-d '{"name": "World"}'
```
### When to Use
- Writing new services
- Testing changes locally
- Debugging service interactions
- Testing auth and scopes before production
See [micro run guide](micro-run.md) for full details.
## Production Mode: `micro server`
### Quick Start
```bash
# Start your services separately (e.g., via systemd, docker)
./myservice &
# Start the gateway
micro server --address :8080
```
Open http://localhost:8080 and log in with `admin/micro`.
### What You Get
- **API Gateway**: Secure HTTP endpoint for all services
- **JWT Authentication**: Token-based access control
- **Web Dashboard**: Service management UI with login
- **User Management**: Create users and API tokens
- **Endpoint Scopes**: Fine-grained access control per endpoint
- **Production Ready**: Designed for deployed environments
### Authentication
All API calls require an `Authorization` header:
```bash
# Get a token (via web UI or login endpoint)
TOKEN="eyJhbGc..."
# Call a service with auth
curl -X POST http://localhost:8080/api/myservice/Handler.Call \
-H "Authorization: Bearer $TOKEN" \
-d '{"name": "World"}'
```
### Managing Users, Tokens & Scopes
1. **Log in**: Visit http://localhost:8080 → Enter `admin/micro`
2. **Create API Token**: Go to `/auth/tokens` → Generate token with scopes
3. **Set Endpoint Scopes**: Go to `/auth/scopes` → Restrict which endpoints require which scopes
4. **Use Token**: Copy and use in `Authorization: Bearer <token>` header
### When to Use
- Production deployments
- Staging environments
- Multi-team access (with auth)
- Public-facing APIs (with security)
## Gateway Features (Both Modes)
Both commands provide the same core gateway capabilities:
### 1. HTTP to RPC Translation
The gateway automatically converts HTTP requests to RPC calls:
```bash
POST /api/{service}/{method}
Content-Type: application/json
{"field": "value"}
```
Becomes an RPC call to:
- Service: `{service}`
- Method: `{method}`
- Payload: `{"field": "value"}`
### 2. Service Discovery
The gateway queries the registry (mdns, consul, etcd) to find services:
```bash
# List all services
curl http://localhost:8080/services
# Returns:
[
{"name": "myservice", "endpoints": ["Handler.Call", "Handler.List"]},
{"name": "users", "endpoints": ["Users.Create", "Users.Get"]}
]
```
Services register automatically when they start - no manual configuration needed!
### 3. Web Dashboard
Visit `/` in your browser to:
- Browse all registered services
- See available endpoints with request/response schemas
- Test endpoints with auto-generated forms
- View service health and status
- Read API documentation
### 4. Health Checks
```bash
# Aggregate health of all services
curl http://localhost:8080/health
# Kubernetes-style probes
curl http://localhost:8080/health/live # Is gateway alive?
curl http://localhost:8080/health/ready # Are services ready?
```
### 5. Dynamic Updates
The gateway automatically picks up:
- New services registering
- Services going offline
- Endpoint changes
- Version updates
No gateway restart needed!
### 6. Endpoint Scopes
Scopes provide fine-grained access control over which tokens can call which endpoints. Both `micro run` and `micro server` support scopes.
**Set up endpoint scopes:**
1. Visit `/auth/scopes` to see all discovered endpoints
2. Set required scopes for endpoints (e.g., `billing` on `payments.Payments.Charge`)
3. Use Bulk Set to apply scopes to all endpoints matching a pattern (e.g., `greeter.*`)
**Create scoped tokens:**
1. Visit `/auth/tokens` and create a token with matching scopes
2. A token with scope `billing` can call endpoints that require `billing`
3. A token with scope `*` bypasses all scope checks
4. Endpoints with no scopes set are open to any authenticated token
**Scopes are enforced on all call paths:**
- Direct API calls (`/api/{service}/{endpoint}`)
- MCP tool calls (`/mcp/call`)
- Agent playground tool invocations
The gateway uses `auth.Account` from the go-micro framework. The account's `Scopes` field carries the same `[]string` used by the framework's `wrapper/auth` package for service-level auth.
## Architecture Benefits
### Why Unified?
Previously, `micro run` and `micro server` had separate gateway implementations. This caused:
- ❌ Duplicated code (hard to maintain)
- ❌ Feature lag (improvements didn't benefit both)
- ❌ Inconsistent behavior between dev and prod
The unified gateway means:
- ✅ Single codebase for both commands
- ✅ Identical HTTP API in dev and production
- ✅ New features benefit both modes automatically
- ✅ Easier testing and maintenance
### What Changed for Users?
From a user perspective:
- `micro run` and `micro server` both have auth enabled
- Both use the same JWT authentication and scopes system
- API endpoints are unchanged
- Web UI is identical
The unification is internal - your code keeps working.
## Common Patterns
### Local Development → Production
```bash
# 1. Develop locally without auth
micro run
# Test: curl http://localhost:8080/api/...
# 2. Build for production
go build -o myservice
# 3. Deploy services
./myservice & # or via systemd, docker, k8s
# 4. Start gateway with auth
micro server
# 5. Generate API token (via web UI)
# Use token in production API calls
```
### Multi-Service Development
```bash
# micro.mu
service api
path ./api
port 8081
service worker
path ./worker
port 8082
depends api
service web
path ./web
port 8090
depends api worker
# Start all with gateway
micro run
```
See [micro run guide](micro-run.md) for configuration details.
### API Gateway Deployment
Deploy `micro server` as your API gateway in front of all services:
```
Internet
┌───────▼────────┐
│ micro server │ :8080 (public)
│ + JWT Auth │
└───────┬────────┘
┌───────────┼───────────┐
│ │ │
┌───▼───┐ ┌──▼───┐ ┌──▼────┐
│ users │ │ posts│ │comments│
│ :8081 │ │ :8082│ │ :8083 │
└───────┘ └──────┘ └────────┘
(internal) (internal) (internal)
```
Only `micro server` needs public access - services can be internal.
## Programmatic Usage
You can also use the gateway in your own Go code:
```go
package main
import (
"context"
"log"
"go-micro.dev/v6/cmd/micro/server"
"go-micro.dev/v6/store"
)
func main() {
// Start gateway with custom options
gw, err := server.StartGateway(server.GatewayOptions{
Address: ":9000",
AuthEnabled: true, // Enable authentication
Store: store.DefaultStore,
Context: context.Background(),
})
if err != nil {
log.Fatal(err)
}
log.Printf("Gateway running on %s", gw.Addr())
// Block until context is cancelled
gw.Wait()
}
```
This gives you full control over gateway configuration in custom deployments.
## Troubleshooting
### Gateway starts but no services show
**Problem**: http://localhost:8080 shows empty service list
**Solution**:
1. Check services are running: `ps aux | grep myservice`
2. Verify registry: services must register via mdns/consul/etcd
3. Check logs: `~/micro/logs/` for service startup errors
### API calls return 404
**Problem**: `curl http://localhost:8080/api/myservice/Handler.Call` returns 404
**Solution**:
1. Visit http://localhost:8080/services to see registered endpoints
2. Check exact endpoint name (case-sensitive): `Handler.Call` vs `handler.call`
3. Ensure service is registered: `micro services` or check web UI
### Authentication errors
**Problem**: API returns `401 Unauthorized`
**Solution**:
1. Generate token: Visit http://localhost:8080/auth/tokens
2. Use header: `Authorization: Bearer <token>`
3. Check token not expired (24h default)
4. Verify user not deleted (tokens revoked on user deletion)
### Scope errors
**Problem**: API returns `403 Forbidden` with `insufficient scopes`
**Solution**:
1. Check which scopes the endpoint requires: Visit `/auth/scopes`
2. Ensure your token has a matching scope (check at `/auth/tokens`)
3. Use a token with `*` scope for full access
4. Clear scopes from the endpoint if it should be unrestricted
### Port already in use
**Problem**: `micro run` or `micro server` won't start
**Solution**:
```bash
# Check what's using port 8080
lsof -i :8080
# Use different port
micro run --address :9000
micro server --address :9000
```
## Next Steps
- [Getting Started](../getting-started.md) - Build your first service
- [micro run Guide](micro-run.md) - Full development workflow
- [Deployment Guide](../deployment.md) - Deploy to production
- [Architecture](../architecture.md) - How it works internally
## Need Help?
- **Issues**: [github.com/micro/go-micro/issues](https://github.com/micro/go-micro/issues)
- **Discord**: [discord.gg/G8Gk5j3uXr](https://discord.gg/G8Gk5j3uXr)
- **Docs**: [go-micro.dev/docs](https://go-micro.dev/docs)
+407
View File
@@ -0,0 +1,407 @@
---
layout: default
---
# Framework Comparison
How Go Micro compares to other Go microservices frameworks.
## Quick Comparison
| Feature | Go Micro | go-kit | gRPC | Dapr |
|---------|----------|--------|------|------|
| **Learning Curve** | Low | High | Medium | Medium |
| **Boilerplate** | Low | High | Medium | Low |
| **Plugin System** | Built-in | External | Limited | Sidecar |
| **Service Discovery** | Yes (mDNS, Consul, etc) | No (BYO) | No | Yes |
| **Load Balancing** | Client-side | No | No | Sidecar |
| **Pub/Sub** | Yes | No | No | Yes |
| **Transport** | HTTP, gRPC, NATS | BYO | gRPC only | HTTP, gRPC |
| **Zero-config Dev** | Yes (mDNS) | No | No | No (needs sidecar) |
| **Production Ready** | Yes | Yes | Yes | Yes |
| **Language** | Go only | Go only | Multi-language | Multi-language |
## vs go-kit
### go-kit Philosophy
- "Just a toolkit" - minimal opinions
- Compose your own framework
- Maximum flexibility
- Requires more decisions upfront
### Go Micro Philosophy
- "Batteries included" - opinionated defaults
- Swap components as needed
- Progressive complexity
- Get started fast, customize later
### When to Choose go-kit
- You want complete control over architecture
- You have strong opinions about structure
- You're building a custom framework
- You prefer explicit over implicit
### When to Choose Go Micro
- You want to start coding immediately
- You prefer conventions over decisions
- You want built-in service discovery
- You need pub/sub messaging
### Code Comparison
**go-kit** (requires more setup):
```go
// Define service interface
type MyService interface {
DoThing(ctx context.Context, input string) (string, error)
}
// Implement service
type myService struct{}
func (s *myService) DoThing(ctx context.Context, input string) (string, error) {
return "result", nil
}
// Create endpoints
func makeDo ThingEndpoint(svc MyService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(doThingRequest)
result, err := svc.DoThing(ctx, req.Input)
if err != nil {
return doThingResponse{Err: err}, nil
}
return doThingResponse{Result: result}, nil
}
}
// Create transport (HTTP, gRPC, etc)
// ... more boilerplate ...
```
**Go Micro** (simpler):
```go
type MyService struct{}
type Request struct {
Input string `json:"input"`
}
type Response struct {
Result string `json:"result"`
}
func (s *MyService) DoThing(ctx context.Context, req *Request, rsp *Response) error {
rsp.Result = "result"
return nil
}
func main() {
svc := micro.NewService("myservice")
svc.Init()
svc.Handle(new(MyService))
svc.Run()
}
```
## vs gRPC
### gRPC Focus
- High-performance RPC
- Multi-language support via protobuf
- HTTP/2 transport
- Streaming built-in
### Go Micro Scope
- Full microservices framework
- Service discovery
- Multiple transports (including gRPC)
- Pub/sub messaging
- Pluggable components
### When to Choose gRPC
- You need multi-language services
- Performance is critical
- You want industry-standard protocol
- You're okay managing service discovery separately
### When to Choose Go Micro
- You need more than just RPC (pub/sub, discovery, etc)
- You want flexibility in transport
- You're building Go-only services
- You want integrated tooling
### Integration
You can use gRPC with Go Micro for native gRPC compatibility:
```go
import (
grpcServer "go-micro.dev/v6/server/grpc"
grpcClient "go-micro.dev/v6/client/grpc"
)
svc := micro.NewService("myservice",
micro.Server(grpcServer.NewServer()),
micro.Client(grpcClient.NewClient()),
)
```
See [Native gRPC Compatibility](grpc-compatibility.md) for a complete guide.
## vs Dapr
### Dapr Approach
- Multi-language via sidecar
- Rich building blocks (state, pub/sub, bindings)
- Cloud-native focused
- Requires running sidecar process
### Go Micro Approach
- Go library, no sidecar
- Direct service-to-service calls
- Simpler deployment
- Lower latency (no extra hop)
### When to Choose Dapr
- You have polyglot services (Node, Python, Java, etc)
- You want portable abstractions across clouds
- You're fully on Kubernetes
- You need state management abstractions
### When to Choose Go Micro
- You're building Go services
- You want lower latency
- You prefer libraries over sidecars
- You want simpler deployment (no sidecar management)
## vs Agent Frameworks (Google ADK)
[ADK](https://adk.dev/) (Agent Development Kit) is Google's open-source, code-first
framework for building AI agents. It spans several languages (Python, TypeScript,
Go, Java, Kotlin); [`adk-go`](https://github.com/google/adk-go) is the Go
implementation. It's model-agnostic (optimized for Gemini), speaks MCP and A2A,
and supports multi-agent systems, evaluation, and deployment to Cloud Run / GKE.
They overlap on agents but solve different problems. ADK is a library for building
an agent process — you define an agent, its tools, and a model, then run and deploy
it. Go Micro is the harness around agents once they operate real systems: service
discovery, inter-service RPC, pub/sub, durable flows, tool execution, and deployment.
Those pieces are out of scope for ADK, and you bring your own.
In Go Micro an agent is built as an ordinary service: it registers in the registry,
is callable by RPC (`Agent.Chat`) and over A2A, and other services and agents
discover and call it the same way they call anything else. Its endpoints are exposed
as MCP tools automatically. So once you have more than one agent or service, Go Micro
also gives you the discovery, RPC, pub/sub, config, and deployment around them.
| | Go Micro | Google ADK |
|---|----------|------------|
| **Primary unit** | A harnessed service (an agent is a service with an LLM inside) | An agent |
| **Service discovery / registry** | Built-in (mDNS, Consul, etcd) | Not in scope |
| **Inter-service RPC, load balancing, pub/sub** | Built-in | Not in scope |
| **MCP** | Every service endpoint is automatically an MCP tool (no extra code) | MCP tools, wired explicitly |
| **A2A** | Agents are A2A-reachable services | Supported |
| **Deterministic orchestration** | Flows | Graph workflows |
| **Multi-agent** | Agents discover & call each other via the registry; `plan`/`delegate` built in | Composition, routing, workflow patterns |
| **Evaluation suite** | Harnesses/conformance today; first-class evaluation is a gap | Yes (criteria, user/env simulation, metrics) |
| **Context engineering** | Store-backed memory | "Context as source code" (auto filter/summarize/token tracking) |
| **Languages** | Go | Python, TypeScript, Go, Java, Kotlin |
| **Backing** | Community | Google |
### When to choose ADK
- You want an agent framework with first-class **evaluation** and context tooling
- You're polyglot, or invested in the Google Cloud / Gemini ecosystem
- You want a cross-language A2A ecosystem with Google's backing
### When to choose Go Micro
- You want an **agent harness** where agents and services are the same thing —
registered, discoverable, load-balanced, and deployed the same way
- You want your existing services to become agent tools with **zero extra code**
(every endpoint is an MCP tool automatically)
- You're building in Go and want one set of primitives for services, agents, and flows
### They interoperate
Both speak **MCP** and **A2A**, so this isn't strictly either/or: a Go Micro agent
and an ADK agent (in any language) can call each other over A2A, and either can
consume the other's MCP tools. A common pattern is to run Go Micro as the service
mesh / runtime and let ADK (or any A2A agent) plug into it.
## vs tRPC-Agent-Go
[tRPC-Agent-Go](https://github.com/trpc-group/trpc-agent-go) (maintained by tRPC-Group,
validated inside Tencent) is a production-grade Go framework for agent systems:
LLM / Chain / Parallel / Cycle / Graph agents, function tools, MCP, A2A, AG-UI, Redis
memory and RAG, evaluation, agent self-evolution, and OpenTelemetry. It's a serious,
well-resourced project.
They overlap heavily on agents but take a different approach. tRPC-Agent-Go is an **agent
SDK you run alongside your services** — you compose agents and tools into graphs and
conditional workflows, and your microservices (tRPC) live separately and are called
into. Go Micro starts from the premise that **an agent is a service** — one runtime
where every endpoint is automatically a tool, an agent registers and is discovered and
load-balanced like anything else, and workflows are durable code paths rather than a
graph DSL. The premise is that the line between "your services" and "your agents" is
accidental complexity; remove it and there's less to wire and keep in sync.
| | Go Micro | tRPC-Agent-Go |
|---|----------|---------------|
| **Primary unit** | A harnessed service (an agent is a service with an LLM inside) | An agent |
| **Orchestration** | Durable `flow` steps + `Loop` — plain code paths | Graph / Chain / Parallel / Cycle agents (graph DSL) |
| **Services as tools** | Every endpoint is automatically an MCP tool | Function tools + MCP, wired explicitly |
| **Service runtime** | Built in — agents *are* services (registry, RPC, load balancing, pub/sub) | Runs alongside your existing service stack (tRPC) |
| **MCP / A2A** | Both, generated from the registry | Both |
| **Evaluation / self-evolution** | Verification loop on the roadmap; not yet first-class | First-class today |
| **Memory / RAG** | Store-backed memory (Postgres, NATS KV, file); RAG on the roadmap | In-memory / Redis memory; RAG today |
| **Observability** | OpenTelemetry run timelines, `micro runs` | OpenTelemetry, Langfuse examples |
| **Backing** | Independent, community | tRPC-Group / Tencent |
### When to choose tRPC-Agent-Go
- You want a graph/workflow DSL for composing agents and tools
- You're on tRPC, or want to add agents alongside an existing service stack
- You want first-class evaluation and self-evolution today, with a large team behind it
### When to choose Go Micro
- You want one runtime where services, agents, and flows are the same primitives —
registered, discoverable, and deployed the same way
- You want your existing services to become agent tools with zero extra code
- You prefer durable flows and plain code paths over a graph DSL, in a small,
independent framework you can hold in your head
### They interoperate
Both speak **MCP** and **A2A**, so a Go Micro agent and a tRPC-Agent-Go agent can call
each other over A2A, and either can consume the other's MCP tools. You can run Go Micro
as the service-and-agent runtime and still reach an agent built on tRPC-Agent-Go.
## Feature Deep Dive
### Service Discovery
**Go Micro**: Built-in with plugins
```go
// Zero-config for dev
svc := micro.NewService("myservice")
// Consul for production
reg := consul.NewRegistry()
svc := micro.NewService("myservice", micro.Registry(reg))
```
**go-kit**: Bring your own
```go
// You implement service discovery
// Can be 100+ lines of code
```
**gRPC**: No built-in discovery
```go
// Use external solution like Consul
// or service mesh like Istio
```
### Load Balancing
**Go Micro**: Client-side, pluggable strategies
```go
// Built-in: random, round-robin
selector := selector.NewSelector(
selector.SetStrategy(selector.RoundRobin),
)
```
**go-kit**: Manual implementation
```go
// You implement load balancing
// Using loadbalancer package
```
**gRPC**: Via external load balancer
```bash
# Use external LB like Envoy, nginx
```
### Pub/Sub
**Go Micro**: First-class
```go
broker.Publish("topic", &broker.Message{Body: []byte("data")})
broker.Subscribe("topic", handler)
```
**go-kit**: Not provided
```go
// Use external message broker directly
// NATS, Kafka, etc
```
**gRPC**: Streaming only
```go
// Use bidirectional streams
// Not traditional pub/sub
```
## Migration Paths
See specific migration guides:
- [From gRPC](migration/from-grpc.md)
**Coming Soon:**
- From go-kit
- From Standard Library
## Decision Matrix
Choose **Go Micro** if:
- ✅ Building Go microservices
- ✅ Want fast iteration
- ✅ Need service discovery
- ✅ Want pub/sub built-in
- ✅ Prefer conventions
Choose **go-kit** if:
- ✅ Want maximum control
- ✅ Have strong architectural opinions
- ✅ Building custom framework
- ✅ Prefer explicit composition
Choose **gRPC** if:
- ✅ Need multi-language support
- ✅ Performance is primary concern
- ✅ Just need RPC (not full framework)
- ✅ Have service discovery handled
Choose **Dapr** if:
- ✅ Polyglot services
- ✅ Heavy Kubernetes usage
- ✅ Want portable cloud abstractions
- ✅ Need state management
## Performance
Rough benchmarks (requests/sec, single instance):
| Framework | Simple RPC | With Discovery | With Tracing |
|-----------|-----------|----------------|--------------|
| Go Micro | ~20k | ~18k | ~15k |
| gRPC | ~25k | N/A | ~20k |
| go-kit | ~22k | N/A | ~18k |
| HTTP std | ~30k | N/A | N/A |
*Benchmarks are approximate and vary by configuration*
## Community & Ecosystem
- **Go Micro**: Active, growing plugins
- **gRPC**: Huge, multi-language
- **go-kit**: Mature, stable
- **Dapr**: Growing, Microsoft-backed
## Recommendation
Start with **Go Micro** if you're building Go microservices and want to move fast. You can always:
- Use gRPC transport: `micro.Transport(grpc.NewTransport())`
- Integrate with go-kit components
- Mix and match as needed
The pluggable architecture means you're not locked in.
@@ -0,0 +1,270 @@
---
layout: default
title: Debugging your agent
---
# Debugging your agent
Use this guide when an agent surprises you: it answered without using a service,
called the wrong endpoint, looped, lost memory, refused a tool, or behaved
differently when a flow handed work to it. The local inner loop is:
```sh
micro run # start services, agents, gateway, dashboard
micro chat # reproduce one turn
micro inspect ... # read the recorded run or workflow history
```
Debug the lifecycle in the same order Go Micro runs it: first prove the service is
registered and callable, then inspect the agent run that chose tools, then inspect
any workflow that handed off to the agent.
Use the recovery command that matches where you are in the first-agent journey:
| Checkpoint | When to use it | Command |
| --- | --- | --- |
| Install troubleshooting | `micro` is not installed, not on `PATH`, or the shell cannot run it. | [Install troubleshooting](install-troubleshooting.html) |
| Quick recovery map | The first-agent loop stalled and you want the short scaffold → run → chat → inspect checklist before reading this full guide. | `micro agent quickcheck` (alias: `micro agent debug`) |
| Preflight before `micro run` | You have not started the local runtime yet and want to verify Go, CLI, provider-key, and gateway-port prerequisites. | `micro agent preflight` |
| Doctor after `micro run` | `micro run` is active, but chat, the `/agent` gateway, agent registration, provider settings, or inspect/run history is not behaving. | `micro agent doctor` |
`micro agent quickcheck` is the quickest breadcrumb when you are unsure where the first-agent path failed: it prints the preflight, run, doctor, inspect, and no-secret fallback commands in one place. `micro agent preflight` is read-only and runs before the first local run; failed
checks include `Fix:` and `Next:` lines for Go, CLI installation, provider-key
setup, and the local gateway port. Once `micro run` is already up, switch to
`micro agent doctor` so the recovery output follows the live gateway, chat
settings, registered agents, provider configuration, and inspectable run history.
## 1. Reproduce one small turn
Start from the application directory and keep the prompt narrow enough that you
can tell which tool should have run:
```sh
micro run
micro chat --prompt "Create a ticket for Pat, then list open tickets."
```
For a live provider, make the provider choice explicit so a later retry uses the
same model boundary:
```sh
MICRO_AI_PROVIDER=anthropic \
ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
micro chat --prompt "Create a ticket for Pat, then list open tickets."
```
If the provider supports streaming, turn it on while you reproduce the issue:
```sh
micro chat --provider anthropic --stream
```
Streaming shows the final answer as it arrives. Tool execution still goes through
the same agent run and is visible through inspection after the turn completes.
## 2. Prove the service side before blaming the model
Agents only call tools that the runtime can discover and describe. Check the
service boundary first:
```sh
micro services
micro call ticket TicketService.List '{}'
```
If the service is missing, restart the service under `micro run` and verify it is
using the same registry as the agent. If the direct `micro call` fails, fix the
handler, request shape, or auth error there before debugging prompts.
When the agent calls the wrong tool or sends the wrong fields, improve the tool
description at the service source:
```go
// Create opens a customer support ticket and returns its stable ticket ID.
// @example {"customer":"Pat","subject":"Cannot log in"}
func (s *TicketService) Create(ctx context.Context, req *CreateRequest, rsp *CreateResponse) error {
```
Endpoint comments, request field names, `description` tags, and `@example` blocks
are the model's map of your service. A vague handler comment often looks like a
reasoning failure from the outside.
## 3. Inspect agent run history
After a chat turn, list recent runs for that agent:
```sh
micro inspect agent support
```
The output shows the run id, status, number of recorded events, the last event,
errors, and a short trace id when tracing is configured. Narrow the list while you
iterate:
```sh
micro inspect agent support --limit 5
micro inspect agent support --status timeout
micro inspect agent support --trace abc123
micro inspect agent support --json
```
Useful statuses include `done`, `refused`, `timeout`, `rate_limited`, `canceled`,
and `error`. Use `--json` when you want exact timestamps, trace/span ids, and error
kinds for a bug report.
When a run is paused at `stage=input-required`, continue it from the CLI and then
inspect the completed checkpoint without writing a Go helper:
```sh
micro agent resume-input support <run-id> --input "Approve deploy to us-east-1"
micro inspect agent support --limit 1
```
Run timelines are stored in the agent's state store under that agent's scoped
state (`agent/<name>/runs/...`). The persisted timeline is recorded even without
an OpenTelemetry exporter, so `micro inspect agent` remains useful in local
no-secret development.
Provider-free quickcheck: if you want to verify the documented inspect path
before involving a live model, run the same smoke check CI uses:
```sh
go test ./internal/harness/zero-to-hero-ci -run TestNoSecretFirstAgentDebuggingSmoke -count=1
```
That test seeds a local `assistant` run history and memory transcript, then runs
`micro inspect agent assistant --limit 1`, `micro inspect agent --status done
--json assistant`, and `micro agent history assistant` with provider credentials
cleared.
## 4. See tool calls as they happen
When you are embedding an agent in Go and need live tool visibility, use the
streaming API instead of waiting for the final answer:
```go
stream, err := agent.StreamAsk(ctx, ag, "Create a ticket for Pat")
if err != nil {
return err
}
for {
ev, err := stream.Recv()
if err != nil {
break
}
switch ev.Type {
case agent.StreamEventToolStart:
log.Printf("tool start: %s %#v", ev.ToolCall.Name, ev.ToolCall.Input)
case agent.StreamEventToolEnd:
log.Printf("tool end: %s %#v", ev.ToolCall.Name, ev.Result)
case agent.StreamEventToken:
fmt.Print(ev.Token)
}
}
```
For custom audit logging, wrap the tool execution boundary. Wrappers observe every
call and result, including guardrail refusals:
```go
wrapped := micro.AgentWrapTool(func(next ai.ToolHandler) ai.ToolHandler {
return func(ctx context.Context, call ai.ToolCall) ai.ToolResult {
if run, ok := ai.RunInfoFrom(ctx); ok {
log.Printf("run=%s agent=%s tool=%s", run.RunID, run.Agent, call.Name)
}
res := next(ctx, call)
if res.Refused != "" {
log.Printf("tool refused: %s reason=%s", call.Name, res.Refused)
}
return res
}
})
ag := micro.NewAgent("support", wrapped)
```
Use this when you need request/response payloads in your own logs. By default,
Go Micro records safe run metadata; raw prompt input is not persisted unless the
agent is configured with `agent.TraceInputs(true)`.
## 5. Inspect memory and plans
Default agent memory is store-backed and scoped to the agent name. A restarted
agent with the same `micro.WithStore(...)` and name reloads conversation history
from the `history` key in `agent/<name>` state. If you pass `micro.WithMemory(...)`,
you own that backend; if you pass `agent.NewInMemory(...)`, memory disappears on
restart.
The built-in `plan` tool also saves the current plan to the same scoped agent
state, so a later turn can pick up the saved plan. When memory does not persist,
check that all of these are stable across restarts:
- the agent name (`micro.NewAgent("support", ...)`),
- the configured store backend (`micro.WithStore(...)` or the process default),
- whether a custom in-memory `Memory` implementation replaced the default,
- whether compaction/retrieval limits are intentionally hiding older turns from
the active model context.
## 6. Inspect workflow handoffs
If a flow triggered the agent, inspect the flow too. The flow history tells you
which durable stage dispatched to the agent and whether a run is still pending:
```sh
micro inspect flow intake
micro inspect flow intake --pending
micro inspect flow intake --stage notify
micro inspect flow intake --json
```
The older flow-specific command remains available for listing runs:
```sh
micro flow runs intake
```
Use the flow run id and the agent run id together when debugging handoffs: the
flow explains why work started and where it checkpointed; the agent run explains
which model/tool steps happened after the handoff.
## 7. Add traces when metadata is not enough
For local CLI debugging, `micro inspect` is the fastest path. For production or
multi-service debugging, configure an OpenTelemetry tracer provider on the agent:
```go
ag := micro.NewAgent("support",
micro.AgentTraceProvider(tp),
)
```
Trace ids flow into the recorded run summaries, so you can pivot between
`micro inspect agent support --trace <prefix>` and your trace backend. Keep
`agent.TraceInputs(true)` off unless your observability backend is approved to
store prompt content.
## Troubleshooting table
| Symptom | What to inspect | Common fix |
| --- | --- | --- |
| Agent answers without calling a service | `micro services`, direct `micro call`, then `micro inspect agent <name>` | Register the service, include it in `micro.AgentServices(...)`, or improve endpoint comments and examples. |
| Agent loops or burns steps | `micro inspect agent <name> --status error` and wrapper logs | Add or lower `micro.AgentMaxSteps(...)` / `micro.AgentLoopLimit(...)`; move predictable work into a flow. |
| Tool is refused before it runs | Wrapper logs, `ToolResult.Refused`, `micro inspect agent <name> --status refused` | Update `micro.AgentApproveTool(...)` policy or prompt the user for explicit approval before retrying. |
| Memory is missing after restart | Agent name, store backend, `WithMemory`, compaction/retrieval settings | Use the default store-backed memory with a persistent store, or persist your custom memory backend. |
| Flow handoff appears stuck | `micro inspect flow <flow> --pending`, then `micro inspect agent <agent>` | Resume or fail the pending flow run; confirm the dispatched agent completed or timed out. |
| Provider failed or timed out | `micro inspect agent <name> --status timeout` / `--status rate_limited` | Retry with the same provider/model, raise deadlines where appropriate, or enable provider retries for transient errors. |
| Tool call appears as assistant text | Agent run history and provider conformance checks | Keep provider packages current; Go Micro normalizes provider-emitted text tool calls, and conformance tests guard this behavior. |
## What to include in a bug report
When you cannot explain the run locally, include:
```sh
micro inspect agent <agent> --limit 5 --json
micro inspect flow <flow> --limit 5 --json
micro services
micro call <service> <Handler.Method> '{}'
```
Redact secrets and user data. If you enabled `agent.TraceInputs(true)`, inspect the
JSON before sharing it because prompts may be present.
@@ -0,0 +1,79 @@
---
layout: default
---
# Deployment Guide
This is a quick reference for deploying go-micro services. For the full guide, see the [Deployment documentation](../deployment.md).
## Workflow
```
micro run → Develop locally with hot reload
micro build → Compile production binaries
micro deploy → Push to a remote Linux server via SSH + systemd
micro server → Optional: production web dashboard with auth
```
## Quick Start
```bash
# Build binaries for Linux
micro build --os linux
# Deploy to server (builds automatically if needed)
micro deploy user@your-server
```
## First-Time Server Setup
On your server (any Linux with systemd):
```bash
curl -fsSL https://go-micro.dev/install.sh | sh
sudo micro init --server
```
This creates `/opt/micro/{bin,data,config}` and a systemd template for managing services.
## Deploy
```bash
micro deploy user@your-server
```
This builds for linux/amd64, copies binaries to `/opt/micro/bin/`, configures systemd services, and verifies they're running.
### Named Targets
Add deploy targets to `micro.mu`:
```
deploy prod
ssh deploy@prod.example.com
deploy staging
ssh deploy@staging.example.com
```
Then: `micro deploy prod`
## Managing Services
```bash
micro status --remote user@server # Check status
micro logs --remote user@server # View logs
micro logs myservice --remote user@server -f # Follow logs
```
## Docker (Optional)
```bash
micro build --docker # Build Docker images
micro build --docker --push # Build and push
micro build --compose # Generate docker-compose.yml
```
## Full Documentation
See the [Deployment documentation](../deployment.md) for complete details including SSH setup, environment variables, security best practices, and troubleshooting.
@@ -0,0 +1,161 @@
---
layout: default
title: Error Handling for AI Agents
---
# Error Handling for AI Agents
When AI agents call your services through MCP, they need to understand errors well enough to recover or inform the user. This guide covers how to write services that give agents useful error information.
## Use Typed Errors
Go Micro's `errors` package provides structured errors that the MCP gateway forwards to agents with status codes and detail messages.
```go
import "go-micro.dev/v6/errors"
func (s *Users) Get(ctx context.Context, req *GetRequest, rsp *GetResponse) error {
if req.ID == "" {
return errors.BadRequest("users.Get", "id is required")
}
user, err := s.db.FindUser(req.ID)
if err != nil {
return errors.NotFound("users.Get", "user %s not found", req.ID)
}
rsp.User = user
return nil
}
```
Agents receive structured error responses like:
```json
{
"error": {
"id": "users.Get",
"code": 404,
"detail": "user abc-123 not found",
"status": "Not Found"
}
}
```
This gives the agent enough context to decide: retry with a different ID, ask the user, or report the problem.
## Error Types and When to Use Them
| Error | Code | Use When |
|-------|------|----------|
| `errors.BadRequest` | 400 | Missing or invalid input — agent should fix the request |
| `errors.Unauthorized` | 401 | Missing auth — agent needs credentials |
| `errors.Forbidden` | 403 | Insufficient permissions — agent can't do this |
| `errors.NotFound` | 404 | Resource doesn't exist — agent should try something else |
| `errors.Conflict` | 409 | Duplicate or version conflict — agent should retry or adjust |
| `errors.InternalServerError` | 500 | Server bug — agent should report to user, don't retry |
## Write Error Messages for Agents
Error messages should tell the agent **what went wrong** and **what to do about it**.
### Bad: Vague Errors
```go
return fmt.Errorf("invalid request")
return errors.BadRequest("users", "failed")
```
Agents can't recover from these — they don't know what's wrong.
### Good: Actionable Errors
```go
return errors.BadRequest("users.Create", "email is required — provide a valid email address")
return errors.BadRequest("users.Create", "email '%s' is already registered — use a different email", req.Email)
return errors.NotFound("users.Get", "no user with id '%s' — use users.List to find valid IDs", req.ID)
```
The agent now knows exactly what to fix or which tool to call next.
## Validation Patterns
Validate inputs at the top of your handler before doing any work:
```go
// CreateOrder places a new order for a user. The user must exist
// and at least one item is required.
//
// @example {"user_id": "u-1", "items": [{"product_id": "p-1", "quantity": 1}]}
func (s *Orders) CreateOrder(ctx context.Context, req *CreateRequest, rsp *CreateResponse) error {
// Validate required fields
if req.UserID == "" {
return errors.BadRequest("orders.CreateOrder", "user_id is required")
}
if len(req.Items) == 0 {
return errors.BadRequest("orders.CreateOrder", "at least one item is required")
}
// Validate each item
for i, item := range req.Items {
if item.ProductID == "" {
return errors.BadRequest("orders.CreateOrder",
"item[%d].product_id is required", i)
}
if item.Quantity <= 0 {
return errors.BadRequest("orders.CreateOrder",
"item[%d].quantity must be positive, got %d", i, item.Quantity)
}
}
// All validations passed — do the work
// ...
}
```
## Document Error Cases
Tell agents what errors to expect in your doc comments:
```go
// Transfer moves funds between two accounts. Both accounts must exist
// and the source account must have sufficient balance.
// Returns an error if the source balance is too low.
//
// @example {"from": "acc-1", "to": "acc-2", "amount": 100}
func (s *Accounts) Transfer(ctx context.Context, req *TransferRequest, rsp *TransferResponse) error {
```
The description "returns an error if the source balance is too low" helps agents anticipate failure modes and plan accordingly.
## Don't Expose Internal Details
Agents (and the users they serve) shouldn't see stack traces, database errors, or internal paths.
```go
// Bad — leaks internals
return fmt.Errorf("pq: duplicate key value violates unique constraint \"users_email_key\"")
// Good — clear message, no internals
return errors.Conflict("users.Create", "a user with email '%s' already exists", req.Email)
```
## Idempotency for Retries
Agents may retry failed operations. Design critical operations to be idempotent:
```go
// CreateOrUpdate upserts a config value. Safe to call multiple times
// with the same key — it will create on first call, update on subsequent calls.
//
// @example {"key": "theme", "value": "dark"}
func (s *Config) CreateOrUpdate(ctx context.Context, req *SetRequest, rsp *SetResponse) error {
```
When an operation is naturally idempotent, say so in the doc comment. Agents will learn they can safely retry.
## Next Steps
- [Tool Descriptions Guide](tool-descriptions.md) - Write documentation that agents can use effectively
- [MCP Security Guide](mcp-security.md) - Auth and scopes for restricting agent access
- [Troubleshooting](troubleshooting.md) - Common issues and solutions
@@ -0,0 +1,304 @@
---
layout: default
---
# Native gRPC Compatibility
This guide explains how to make your Go Micro services compatible with native gRPC clients like `grpcurl`, `grpcui`, or clients generated by the standard `protoc` gRPC plugin in any language.
## Understanding Transport vs Server
Go Micro has two different gRPC-related concepts that are often confused:
### gRPC Transport (`go-micro.dev/v6/transport/grpc`)
The gRPC **transport** uses the gRPC protocol as a communication layer, similar to how you might use NATS, RabbitMQ, or HTTP. It does **not** guarantee compatibility with native gRPC clients.
```go
// This uses gRPC as transport but is NOT compatible with native gRPC clients
import "go-micro.dev/v6/transport/grpc"
t := grpc.NewTransport()
service := micro.NewService("helloworld",
micro.Transport(t),
)
```
When using the gRPC transport:
- Communication between Go Micro services works fine
- Native gRPC clients (grpcurl, etc.) will fail with "Unimplemented" errors
- The protocol is used like a message bus, not as a standard gRPC server
### gRPC Server/Client (`go-micro.dev/v6/server/grpc` and `go-micro.dev/v6/client/grpc`)
The gRPC **server** and **client** provide native gRPC compatibility. These implement a proper gRPC server that any gRPC client can communicate with.
```go
// This IS compatible with native gRPC clients
import (
"go-micro.dev/v6"
grpcServer "go-micro.dev/v6/server/grpc"
grpcClient "go-micro.dev/v6/client/grpc"
)
service := micro.NewService("helloworld",
micro.Server(grpcServer.NewServer()),
micro.Client(grpcClient.NewClient()),
)
```
## When to Use Which
| Use Case | Solution |
|----------|----------|
| Need native gRPC client compatibility | Use gRPC server/client |
| Need to call service with `grpcurl` | Use gRPC server |
| Need polyglot gRPC clients (Python, Java, etc.) | Use gRPC server |
| Only Go Micro services communicating | Either works |
| Want gRPC as a message protocol (like NATS) | Use gRPC transport |
## Complete Example: Native gRPC Compatible Service
### Proto Definition
```protobuf
syntax = "proto3";
package helloworld;
option go_package = "./proto;helloworld";
service Say {
rpc Hello(Request) returns (Response) {}
}
message Request {
string name = 1;
}
message Response {
string message = 1;
}
```
### Generate Code
```bash
# Install protoc-gen-micro
go install go-micro.dev/v6/cmd/protoc-gen-micro@latest
# Generate Go code
protoc --proto_path=. \
--go_out=. --go_opt=paths=source_relative \
--micro_out=. --micro_opt=paths=source_relative \
proto/helloworld.proto
```
### Server Implementation
```go
package main
import (
"context"
"log"
"go-micro.dev/v6"
grpcServer "go-micro.dev/v6/server/grpc"
pb "example.com/helloworld/proto"
)
type Say struct{}
func (s *Say) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
rsp.Message = "Hello " + req.Name
return nil
}
func main() {
// Create service with gRPC server for native gRPC compatibility
// Note: Server must be set before Name to ensure the name is applied to the gRPC server
service := micro.NewService("helloworld",
micro.Server(grpcServer.NewServer()),
micro.Address(":8080"),
)
service.Init()
// Register handler
pb.RegisterSayHandler(service.Server(), &Say{})
// Run service
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
```
### Client Implementation (Go Micro)
```go
package main
import (
"context"
"fmt"
"log"
"go-micro.dev/v6"
grpcClient "go-micro.dev/v6/client/grpc"
pb "example.com/helloworld/proto"
)
func main() {
// Create service with gRPC client
service := micro.NewService("helloworld.client",
micro.Client(grpcClient.NewClient()),
)
service.Init()
// Create client - use the service name "helloworld" (not the proto package name)
// Go Micro uses this name for registry lookup, which may differ from the package name
sayService := pb.NewSayService("helloworld", service.Client())
// Call service
rsp, err := sayService.Hello(context.Background(), &pb.Request{Name: "Alice"})
if err != nil {
log.Fatal(err)
}
fmt.Println(rsp.Message) // Output: Hello Alice
}
```
### Testing with grpcurl
Once your service is running with the gRPC server, you can use `grpcurl`:
```bash
# List available services
grpcurl -plaintext localhost:8080 list
# Call the Hello method
grpcurl -proto ./proto/helloworld.proto \
-plaintext \
-d '{"name":"Alice"}' \
localhost:8080 helloworld.Say.Hello
```
## Using Both gRPC Server and Client Together
For full native gRPC compatibility (both inbound and outbound), use both:
```go
package main
import (
"go-micro.dev/v6"
grpcClient "go-micro.dev/v6/client/grpc"
grpcServer "go-micro.dev/v6/server/grpc"
)
func main() {
service := micro.NewService("helloworld",
micro.Server(grpcServer.NewServer()),
micro.Client(grpcClient.NewClient()),
micro.Address(":8080"),
)
service.Init()
// ... register handlers
service.Run()
}
```
## Common Errors
### "unknown service" Error with grpcurl
If you see this error:
```
ERROR:
Code: Unimplemented
Message: unknown service helloworld.Say
```
**Cause**: You're using the gRPC transport instead of the gRPC server.
**Solution**: Change from:
```go
// Wrong - uses transport
t := grpc.NewTransport()
service := micro.NewService("helloworld",
micro.Transport(t),
)
```
To:
```go
// Correct - uses server
import grpcServer "go-micro.dev/v6/server/grpc"
service := micro.NewService("helloworld",
micro.Server(grpcServer.NewServer()),
)
```
### Import Path Confusion
Note the different import paths:
```go
// Transport (NOT native gRPC compatible)
import "go-micro.dev/v6/transport/grpc"
// Server (native gRPC compatible)
import "go-micro.dev/v6/server/grpc"
// Client (native gRPC compatible)
import "go-micro.dev/v6/client/grpc"
```
### Service Name vs Package Name
When creating a client to call another service, use the **service name** passed to `micro.NewService`, not the proto package name:
```go
// If the server was started with micro.NewService("helloworld", ...)
sayService := pb.NewSayService("helloworld", service.Client()) // Use service name
// NOT the package name from the proto file
// sayService := pb.NewSayService("helloworld.Say", service.Client()) // Wrong!
```
Go Micro uses the service name for registry lookup, which may differ from the proto package name.
## Environment Variable Configuration
You can also configure the server and client via environment variables:
```bash
# Use gRPC server
MICRO_SERVER=grpc go run main.go
# Use gRPC client
MICRO_CLIENT=grpc go run main.go
```
## Summary
| Component | Import Path | Native gRPC Compatible |
|-----------|-------------|----------------------|
| Transport | `go-micro.dev/v6/transport/grpc` | ❌ No |
| Server | `go-micro.dev/v6/server/grpc` | ✅ Yes |
| Client | `go-micro.dev/v6/client/grpc` | ✅ Yes |
For native gRPC compatibility with tools like `grpcurl` or polyglot clients, always use the gRPC **server** and **client** packages, not the transport.
## Related Documentation
- [Transport](../transport.md) - Understanding transports in Go Micro
- [Plugins](../plugins.md) - Available plugins including gRPC
- [Migration from gRPC](migration/from-grpc.md) - Migrating existing gRPC services
+240
View File
@@ -0,0 +1,240 @@
---
layout: default
---
# Health Checks
The `health` package provides health check functionality for microservices, including Kubernetes-style liveness and readiness probes.
## Quick Start
```go
import "go-micro.dev/v6/health"
func main() {
// Register health checks
health.Register("database", health.PingCheck(db.Ping))
health.Register("cache", health.TCPCheck("localhost:6379", time.Second))
// Add health endpoints
mux := http.NewServeMux()
health.RegisterHandlers(mux) // Registers /health, /health/live, /health/ready
http.ListenAndServe(":8080", mux)
}
```
## Endpoints
| Endpoint | Purpose | Returns 200 when |
|----------|---------|------------------|
| `/health` | Overall health status | All critical checks pass |
| `/health/live` | Kubernetes liveness probe | Service is running |
| `/health/ready` | Kubernetes readiness probe | All critical checks pass |
## Response Format
```json
{
"status": "up",
"checks": [
{
"name": "database",
"status": "up",
"duration": 1234567
},
{
"name": "cache",
"status": "up",
"duration": 567890
}
],
"info": {
"go_version": "go1.22.0",
"go_os": "linux",
"go_arch": "amd64",
"version": "1.0.0"
}
}
```
When unhealthy:
- HTTP status: 503 Service Unavailable
- `status`: `"down"`
- Failed checks include an `error` field
## Built-in Checks
### PingCheck
For database connections with a `Ping()` method:
```go
health.Register("postgres", health.PingCheck(db.Ping))
health.Register("mysql", health.PingContextCheck(db.PingContext))
```
### TCPCheck
Verify TCP connectivity:
```go
health.Register("redis", health.TCPCheck("localhost:6379", time.Second))
health.Register("kafka", health.TCPCheck("kafka:9092", 2*time.Second))
```
### HTTPCheck
Verify an HTTP endpoint returns 200:
```go
health.Register("api", health.HTTPCheck("http://api.internal/health", time.Second))
```
### DNSCheck
Verify DNS resolution:
```go
health.Register("dns", health.DNSCheck("api.example.com"))
```
### CustomCheck
Any function returning an error:
```go
health.Register("disk", health.CustomCheck(func() error {
var stat syscall.Statfs_t
if err := syscall.Statfs("/", &stat); err != nil {
return err
}
freeGB := stat.Bavail * uint64(stat.Bsize) / 1e9
if freeGB < 1 {
return fmt.Errorf("low disk space: %dGB free", freeGB)
}
return nil
}))
```
### RegistryCheck
Verifies the service registry is still reachable. A go-micro service can keep running while it has silently lost its connection to the registry (etcd, Consul, …) — the process looks healthy, but other services can no longer discover it. `RegistryCheck` surfaces that state so a readiness probe can take the pod out of rotation.
```go
svc := micro.NewService("orders")
health.Register("registry", health.RegistryCheck(svc.Options().Registry))
```
Registered checks are [critical](#critical-vs-non-critical-checks) by default, so when the registry connection is lost, `/health/ready` returns 503 and Kubernetes stops routing to the pod:
```yaml
readinessProbe:
httpGet:
path: /health/ready
port: 8080
periodSeconds: 5
```
The check lists services under the configured probe timeout, so an unreachable registry is reported as `down` rather than hanging the probe. It works with any registry implementation — the connectivity is exercised through the standard `ListServices` call.
## Critical vs Non-Critical Checks
By default, all checks are critical. A critical check failure marks the service as not ready.
For non-critical checks (monitoring only):
```go
health.RegisterCheck(health.Check{
Name: "external-api",
Check: health.HTTPCheck("https://api.external.com/status", 5*time.Second),
Critical: false, // Won't affect readiness
Timeout: 5 * time.Second,
})
```
## Timeouts
Default timeout is 5 seconds. Override per-check:
```go
health.RegisterCheck(health.Check{
Name: "slow-db",
Check: health.PingCheck(db.Ping),
Timeout: 10 * time.Second,
})
```
## Adding Service Info
Include metadata in health responses:
```go
health.SetInfo("version", "1.0.0")
health.SetInfo("commit", "abc123")
health.SetInfo("service", "users")
```
## Kubernetes Configuration
```yaml
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
```
## Integration with micro run
When using `micro run` with a `micro.mu` config that specifies ports, the runner waits for `/health` to return 200 before starting dependent services:
```
service database
path ./database
port 8081
service api
path ./api
port 8080
depends database
```
The `api` service won't start until `database`'s `/health` endpoint is ready.
## Programmatic Usage
```go
// Check readiness in code
if health.IsReady(ctx) {
// Service is healthy
}
// Get full health status
resp := health.Run(ctx)
fmt.Printf("Status: %s\n", resp.Status)
for _, check := range resp.Checks {
fmt.Printf(" %s: %s (%v)\n", check.Name, check.Status, check.Duration)
}
```
## Best Practices
1. **Keep checks fast** - Health endpoints are called frequently
2. **Use timeouts** - Don't let slow dependencies block health checks
3. **Non-critical for optional deps** - External APIs, caches that have fallbacks
4. **Critical for required deps** - Databases, message queues
5. **Include version info** - Helps debugging in production
@@ -0,0 +1,96 @@
---
layout: default
title: Install troubleshooting
---
# Install troubleshooting
Use this page before `micro new` or `micro agent demo` when the CLI install is
unclear. The goal is to prove three boundaries in order: the `micro` binary is on
`PATH`, it is the version you expected, and the no-secret first-run path works
without provider keys.
## 1. Choose one install path
### Binary installer (no Go required to install)
```sh
curl -fsSL https://go-micro.dev/install.sh | sh
```
Use this when you want the released `micro` binary without building it yourself.
The generated services still need a Go toolchain when you run `micro run`, but the
installer itself does not require Go.
### Go install (build from source)
```sh
go install go-micro.dev/v6/cmd/micro@latest
```
Use this when Go is already installed and you want the binary in your Go bin
directory. If the command succeeds but `micro` is not found, your Go bin directory
is probably not on `PATH`.
## 2. Verify `PATH` and version
Check which binary your shell will run:
```sh
command -v micro
micro --version
```
If `command -v micro` prints nothing, add the install directory to `PATH`, then
open a new terminal and retry. Common locations are:
```sh
export PATH="$HOME/.micro/bin:$PATH" # binary installer
export PATH="$(go env GOPATH)/bin:$PATH" # go install
```
If `micro --version` shows an older binary than expected, remove the stale copy or
put the intended install directory earlier in `PATH`.
## 3. Run the no-secret smoke path
Once `micro` resolves, prove the local service runtime before adding LLM provider
keys:
```sh
micro new helloworld
cd helloworld
micro run
```
In another terminal:
```sh
curl -X POST http://localhost:8080/api/helloworld/Helloworld.Call \
-H 'Content-Type: application/json' -d '{"name":"World"}'
```
This checks the scaffold, local build, gateway, and service registration without
calling a model provider.
## 4. Recover common failures
| Symptom | Check | Fix |
|---------|-------|-----|
| `micro: command not found` | `command -v micro` | Add the installer bin directory or `$(go env GOPATH)/bin` to `PATH`, then open a new terminal. |
| `micro run` cannot find Go | `go version` | Install Go 1.24 or newer from <https://go.dev/doc/install>. |
| The gateway port is busy | `lsof -i :8080` | Stop the process using the port, or run with a different address. |
| Provider-key errors block an agent run | `micro agent preflight` | Stay on the no-secret path first: run `micro agent demo`, then the no-secret first-agent guide. |
## 5. Continue the first-agent on-ramp
After install verification succeeds, continue in order:
1. `micro agent demo` — print the provider-free first-agent demo command and next docs steps.
2. [No-secret first-agent transcript](no-secret-first-agent.html) — prove an agent can use services without a provider key.
3. [Your First Agent](your-first-agent.html) — build and chat with your own service-backed agent.
4. [Debugging your agent](debugging-agents.html) — inspect registration, tool calls, run history, and provider failures.
5. [0→hero Reference](zero-to-hero.html) — walk the full services → agents → workflows lifecycle.
For repository contributors, `make install-smoke` runs the same installer seam
against a local build without network access.
@@ -0,0 +1,362 @@
---
layout: default
---
# MCP Security Guide
This guide covers how to secure your MCP gateway for production use, including authentication, per-tool scopes, rate limiting, and audit logging.
## Overview
The MCP gateway provides four layers of security:
1. **Authentication** - Verify the caller's identity via bearer tokens
2. **Scopes** - Control which tools each token can access
3. **Rate Limiting** - Prevent abuse with per-tool rate limits
4. **Audit Logging** - Record every tool call for compliance and debugging
## Authentication
### Bearer Token Auth
The MCP gateway uses bearer token authentication. Tokens are validated by the configured `auth.Auth` provider.
```go
import (
"go-micro.dev/v6/gateway/mcp"
"go-micro.dev/v6/auth"
)
gateway := mcp.ListenAndServe(":3000", mcp.Options{
Registry: service.Options().Registry,
Auth: authProvider, // auth.Auth implementation
})
```
Agents pass tokens in the `Authorization` header:
```bash
curl -X POST http://localhost:3000/mcp/call \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"tool": "tasks.TaskService.Create", "input": {"title": "New task"}}'
```
### Using micro run / micro server
When using `micro run` or `micro server`, authentication is handled automatically:
- **Development mode (`micro run`):** Auth is disabled by default for easy development
- **Production mode (`micro server`):** JWT auth is enabled with user management at `/auth`
Create tokens with specific scopes via the dashboard at `/auth/tokens`.
## Per-Tool Scopes
Scopes control which tools a token can access. There are two ways to set scopes.
### Service-Level Scopes
Set scopes when registering your handler. These travel with the service through the registry:
```go
handler := service.Server().NewHandler(
new(TaskService),
server.WithEndpointScopes("TaskService.Get", "tasks:read"),
server.WithEndpointScopes("TaskService.List", "tasks:read"),
server.WithEndpointScopes("TaskService.Create", "tasks:write"),
server.WithEndpointScopes("TaskService.Update", "tasks:write"),
server.WithEndpointScopes("TaskService.Delete", "tasks:admin"),
)
```
### Gateway-Level Scopes
Override or add scopes at the gateway without modifying services. Gateway scopes take precedence:
```go
mcp.ListenAndServe(":3000", mcp.Options{
Registry: reg,
Auth: authProvider,
Scopes: map[string][]string{
"tasks.TaskService.Create": {"tasks:write"},
"tasks.TaskService.Delete": {"tasks:admin"},
"billing.Billing.Charge": {"billing:admin"},
},
})
```
### Scope Enforcement
When a tool is called:
1. Gateway checks if the tool has required scopes
2. If scopes are defined, the caller's token must include at least one matching scope
3. A token with scope `*` has unrestricted access (admin)
4. If no scopes are defined for a tool, any authenticated token can call it
5. Denied calls return `403 Forbidden`
### Common Scope Patterns
| Pattern | Use Case |
|---------|----------|
| `service:read` | Read-only access to a service |
| `service:write` | Create and update operations |
| `service:admin` | Delete and destructive operations |
| `*` | Full admin access (use sparingly) |
| `internal` | Internal-only tools not exposed to external agents |
### Token Examples
```
Token A: scopes=["tasks:read"]
✅ Can call TaskService.Get, TaskService.List
❌ Cannot call TaskService.Create, TaskService.Delete
Token B: scopes=["tasks:read", "tasks:write"]
✅ Can call Get, List, Create, Update
❌ Cannot call TaskService.Delete (needs tasks:admin)
Token C: scopes=["*"]
✅ Can call everything (admin)
```
## Rate Limiting
Prevent abuse with per-tool rate limiting using a token bucket algorithm:
```go
mcp.ListenAndServe(":3000", mcp.Options{
Registry: reg,
RateLimit: &mcp.RateLimitConfig{
RequestsPerSecond: 10, // Sustained rate
Burst: 20, // Allow bursts up to 20
},
})
```
When the rate limit is exceeded, calls return `429 Too Many Requests`.
### Choosing Rate Limits
| Service Type | Requests/sec | Burst | Rationale |
|-------------|-------------|-------|-----------|
| Read-heavy API | 50 | 100 | High throughput, low cost |
| Write API | 10 | 20 | Moderate, prevents spam |
| Expensive operation | 2 | 5 | Protect downstream resources |
| Internal tool | 100 | 200 | Trusted callers, higher limits |
## Audit Logging
Record every tool call for compliance, debugging, and analytics:
```go
mcp.ListenAndServe(":3000", mcp.Options{
Registry: reg,
Auth: authProvider,
AuditFunc: func(record mcp.AuditRecord) {
log.Printf("[AUDIT] tool=%s account=%s allowed=%v duration=%v err=%v",
record.Tool,
record.AccountID,
record.Allowed,
record.Duration,
record.Error,
)
},
})
```
### AuditRecord Fields
| Field | Type | Description |
|-------|------|-------------|
| `Tool` | `string` | Full tool name (e.g., `tasks.TaskService.Create`) |
| `AccountID` | `string` | Caller's account ID from the auth token |
| `Scopes` | `[]string` | Scopes on the caller's token |
| `Allowed` | `bool` | Whether the call was permitted |
| `Duration` | `time.Duration` | How long the call took |
| `Error` | `error` | Error if the call failed |
| `TraceID` | `string` | UUID trace ID for correlation |
| `DeniedReason` | `string` | Why the call was denied (empty if allowed) |
### Production Audit Logging
For production, send audit records to a structured logging system:
```go
AuditFunc: func(r mcp.AuditRecord) {
// Structured JSON logging
logger.Info("mcp_tool_call",
"tool", r.Tool,
"account", r.AccountID,
"allowed", r.Allowed,
"duration_ms", r.Duration.Milliseconds(),
"trace_id", r.TraceID,
)
// Alert on denied calls
if !r.Allowed {
alerting.Notify("MCP access denied",
"tool", r.Tool,
"account", r.AccountID,
)
}
},
```
## Tracing
Every MCP tool call gets a UUID trace ID, propagated via metadata headers:
| Header | Description |
|--------|-------------|
| `Mcp-Trace-Id` | UUID for the tool call |
| `Mcp-Tool-Name` | Name of the tool called |
| `Mcp-Account-Id` | Caller's account ID |
These are available in your handler via context metadata:
```go
func (t *TaskService) Create(ctx context.Context, req *CreateRequest, rsp *CreateResponse) error {
md, _ := metadata.FromContext(ctx)
traceID := md["Mcp-Trace-Id"]
log.Printf("Creating task, trace: %s", traceID)
// ...
}
```
### OpenTelemetry Integration
For full distributed tracing, plug in an OpenTelemetry trace provider:
```go
import (
"go.opentelemetry.io/otel"
"go-micro.dev/v6/gateway/mcp"
)
mcp.ListenAndServe(":3000", mcp.Options{
Registry: reg,
TraceProvider: otel.GetTracerProvider(),
})
```
Each tool call creates a span (`mcp.tool.call`) with these attributes:
| Attribute | Example |
|-----------|---------|
| `mcp.tool.name` | `tasks.TaskService.Create` |
| `mcp.transport` | `http`, `websocket`, `stdio` |
| `mcp.account.id` | `user-123` |
| `mcp.trace.id` | `a1b2c3d4-...` |
| `mcp.auth.allowed` | `true` |
| `mcp.auth.denied_reason` | `insufficient_scope` |
| `mcp.scopes.required` | `tasks:write` |
| `mcp.rate_limited` | `false` |
The gateway propagates W3C trace context downstream, so you get end-to-end traces from agent → gateway → service in Jaeger, Zipkin, or any OTel-compatible backend.
## WebSocket Authentication
The WebSocket transport supports two authentication methods:
### Connection-Level Auth (Recommended)
Pass the token in the WebSocket upgrade request:
```javascript
const ws = new WebSocket("ws://localhost:3000/mcp/ws", {
headers: { "Authorization": "Bearer <token>" }
});
```
The token is validated once on connection and applies to all messages on that connection.
### Per-Message Auth
For stateless connections, pass a `_token` parameter with each tool call:
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tasks.TaskService.Create",
"arguments": {"title": "New task"},
"_token": "Bearer <token>"
}
}
```
Connection-level auth takes precedence over per-message auth.
## Production Checklist
Before deploying MCP to production:
- [ ] **Auth enabled** - Configure an `auth.Auth` provider
- [ ] **Scopes defined** - Every write/delete endpoint has required scopes
- [ ] **Rate limits set** - Appropriate limits for each service type
- [ ] **Audit logging active** - All calls logged to a persistent store
- [ ] **HTTPS/TLS** - MCP gateway behind TLS termination
- [ ] **Token rotation** - Process for rotating compromised tokens
- [ ] **Monitoring** - Alerts on high error rates or denied calls
- [ ] **Testing** - Verified scope enforcement with `micro mcp test`
## Full Example
```go
package main
import (
"log"
"go-micro.dev/v6"
"go-micro.dev/v6/auth"
"go-micro.dev/v6/gateway/mcp"
"go-micro.dev/v6/server"
)
func main() {
service := micro.NewService("tasks",
micro.Address(":8081"),
)
service.Init()
// Register handler with scopes
handler := service.Server().NewHandler(
&TaskService{tasks: make(map[string]*Task)},
server.WithEndpointScopes("TaskService.Get", "tasks:read"),
server.WithEndpointScopes("TaskService.Create", "tasks:write"),
server.WithEndpointScopes("TaskService.Delete", "tasks:admin"),
)
service.Server().Handle(handler)
// Start MCP gateway with full security
go mcp.ListenAndServe(":3000", mcp.Options{
Registry: service.Options().Registry,
Auth: service.Options().Auth,
Scopes: map[string][]string{
// Gateway-level overrides
"billing.Billing.Charge": {"billing:admin"},
},
RateLimit: &mcp.RateLimitConfig{
RequestsPerSecond: 10,
Burst: 20,
},
AuditFunc: func(r mcp.AuditRecord) {
log.Printf("[AUDIT] tool=%s account=%s allowed=%v duration=%v",
r.Tool, r.AccountID, r.Allowed, r.Duration)
},
})
service.Run()
}
```
## Next Steps
- [Building AI-Native Services](ai-native-services.md) - End-to-end tutorial
- [Tool Description Best Practices](tool-descriptions.md) - Write effective documentation
- [Agent Integration Patterns](agent-patterns.md) - Multi-agent architectures
+119
View File
@@ -0,0 +1,119 @@
---
layout: default
---
# `micro loop` quickstart
`micro loop` scaffolds the autonomous improvement loop that Go Micro uses on
this repository: GitHub Actions workflows for planning, building, evaluation
feedback, coherence, security, and release. Use it when you want a repository to
continuously turn a ranked queue into small PRs while CI remains the merge gate.
## 1. Initialize the loop
Run the default loop from the repository root:
```bash
micro loop init
```
For every role used by Go Micro itself, scaffold all workflows:
```bash
micro loop init --roles all
```
The command writes:
- `.github/loop/NORTH_STAR.md` — the direction every increment should optimize.
- `.github/loop/PRIORITIES.md` — the ranked queue; the builder takes the top open issue.
- `.github/loop/prompts/*.md` — editable policy for planner, builder, triage, coherence, and security roles.
- `.github/workflows/loop-*.yml` — generated GitHub Actions mechanics.
Edit the files under `.github/loop/` to steer the loop. Re-run
`micro loop init --roles all --force` only when you want to regenerate workflow
mechanics from the installed CLI.
## 2. Configure the dispatch token
The scheduled builder needs a repository secret containing a token from a user
account that the coding agent will answer. Go Micro names that secret
`CODEX_TRIGGER_TOKEN` by default. If you use another secret name, pass it when
you initialize the loop:
```bash
micro loop init --agent @codex --token-secret LOOP_TOKEN --roles all
```
The token needs enough repository permission to open issues, comment, push
branches, create pull requests, and enable auto-merge. Run `gh auth setup-git` in
the environment that will push branches so `git push` uses the same credentials
as `gh`.
## Choosing an agent
The loop is **agent-agnostic by design**. Each run opens a fresh tracking issue
and summons the agent with an `@mention` comment; the prompt file
(`.github/loop/prompts/<role>.md`) is the instruction. Any coding agent that
(a) responds to an `@mention` on an issue and (b) can open a PR with `gh` works —
you select it with `--agent`.
- **Codex** (`--agent @codex`, the default). Point `--token-secret` at a PAT for
the user account Codex follows, and make sure the Codex environment installs
`gh` and runs `gh auth setup-git`. This is the path Go Micro itself runs on.
- **Claude Code** (`--agent @claude`). Install
[`anthropics/claude-code-action`](https://github.com/anthropics/claude-code-action)
in the repo so a workflow responds to `@claude` comments and runs Claude with a
repo-scoped token; then the loop's dispatch triggers it like any other mention.
- **Any other mention-driven agent** — pass its handle to `--agent`. The
mechanics don't care which agent it is.
Not supported by the mention model: agents triggered by **issue assignment**
rather than a comment (e.g. GitHub Copilot's coding agent, which you assign an
issue to). The dispatch would need an "assign" adapter for those; it isn't wired
yet, so stick to mention-driven agents.
## 3. Make CI the gate
The loop should not be its own reviewer. Protect the default branch so PRs merge
only after the required checks pass. At minimum, require the same commands the
Go Micro loop verifies locally and in CI:
```bash
go build ./...
go test ./...
golangci-lint run ./...
```
If your repository has a harness or end-to-end grader, make that required too.
Keep human approval requirements out of the autonomous path unless you intend the
loop to pause for review.
## 4. Verify the wiring
After editing the North Star, queue, prompts, token secret, and branch
protection, run:
```bash
micro loop verify
```
`micro loop verify` checks that the loop direction, queue, prompts, role
workflows, and non-loop CI gate are present. Fix any reported missing items
before relying on scheduled increments.
## 5. Operate the queue
Keep one ranked list in `.github/loop/PRIORITIES.md`. Each item should link a
scoped issue and be small enough for one PR. The builder closes both the priority
issue and the per-run tracker issue in the PR body, for example:
```text
Closes #1234
Closes #5678
```
Use the North Star to keep the queue honest: favor small improvements that move
developers through the services → agents → workflows lifecycle, and surface
breaking API or brand/positioning decisions for humans instead of auto-merging
them.
+262
View File
@@ -0,0 +1,262 @@
---
layout: default
---
# micro run - Local Development
`micro run` provides a complete development environment for Go microservices.
> **Note**: This guide focuses on `micro run` features. For a comparison with `micro server` and gateway architecture details, see the [CLI & Gateway Guide](cli-gateway.md).
## Quick Start
```bash
micro new helloworld
cd helloworld
micro run
```
Open http://localhost:8080 to see your service.
## What You Get
When you run `micro run`, you get:
| URL | Description |
|-----|-------------|
| http://localhost:8080 | Web dashboard - browse and call services |
| http://localhost:8080/agent | Agent playground - AI chat with MCP tools |
| http://localhost:8080/api | API explorer - browse endpoints and schemas |
| http://localhost:8080/api/{service}/{method} | API gateway - HTTP to RPC proxy |
| http://localhost:8080/mcp/tools | MCP tools - list all services as AI tools |
| http://localhost:8080/auth/tokens | Token management - create and manage API tokens |
| http://localhost:8080/auth/scopes | Scope management - restrict endpoint access |
| http://localhost:8080/auth/users | User management - create and manage users |
| http://localhost:8080/health | Health checks - aggregated service health |
| http://localhost:8080/services | Service list - JSON |
Plus:
- **Authentication** - JWT auth enabled with default credentials (`admin`/`micro`)
- **Hot Reload** - File changes trigger automatic rebuild
- **Dependency Ordering** - Services start in the right order
- **Environment Management** - Dev/staging/production configs
- **MCP Gateway** - Optional dedicated MCP protocol listener via `--mcp-address`
## Features
### API Gateway
The gateway converts HTTP requests to RPC calls. All API calls require authentication:
```bash
# Log in at http://localhost:8080 with admin/micro to get a session
# Or use a token for programmatic access:
curl -X POST http://localhost:8080/api/helloworld/Say.Hello \
-H "Authorization: Bearer <token>" \
-d '{"name": "World"}'
# Response
{"message": "Hello World"}
```
Create tokens at `/auth/tokens`. The default admin token has `*` scope (full access).
### Agent Playground
The agent playground at `/agent` lets you interact with your services using AI. Your services are automatically exposed as MCP (Model Context Protocol) tools — no configuration needed.
1. Open http://localhost:8080/agent
2. Configure your API key in Agent Settings (supports OpenAI and Anthropic)
3. Chat with the AI agent — it can discover and call your services as tools
The MCP tools API is available at:
- `/mcp/tools` — list all services as AI-callable tools
- `/mcp/call` — invoke a tool (service endpoint) by name
For a dedicated MCP protocol listener (for external AI clients), use:
```bash
micro run --mcp-address :3000
```
### Hot Reload
By default, `micro run` watches for `.go` file changes and automatically rebuilds and restarts affected services.
```bash
micro run # Hot reload enabled (default)
micro run --no-watch # Disable hot reload
```
Changes are debounced (300ms) to handle rapid saves from editors.
### Configuration File
For multi-service projects, create a `micro.mu` file to define services, dependencies, and environments.
#### micro.mu (Recommended)
```
# Service definitions
service users
path ./users
port 8081
service posts
path ./posts
port 8082
depends users
service web
path ./web
port 8089
depends users posts
# Environment configurations
env development
STORE_ADDRESS file://./data
DEBUG true
env production
STORE_ADDRESS postgres://localhost/db
DEBUG false
```
#### micro.json (Alternative)
```json
{
"services": {
"users": {
"path": "./users",
"port": 8081
},
"posts": {
"path": "./posts",
"port": 8082,
"depends": ["users"]
}
},
"env": {
"development": {
"STORE_ADDRESS": "file://./data"
}
}
}
```
### Service Properties
| Property | Required | Description |
|----------|----------|-------------|
| `path` | Yes | Directory containing the service (with main.go) |
| `port` | No | Port the service listens on (enables health check waiting) |
| `depends` | No | Services that must start first (space-separated in .mu, array in .json) |
### Dependency Ordering
When `depends` is specified, services start in topological order:
1. Services with no dependencies start first
2. Each service waits for its dependencies to be ready
3. If a service has a `port`, we wait for `/health` to return 200
4. Circular dependencies are detected and reported as errors
### Environment Management
```bash
micro run # Uses 'development' (default)
micro run --env production # Uses 'production'
micro run --env staging # Uses 'staging'
MICRO_ENV=test micro run # Environment variable override
```
Environment variables from the config are injected into each service's environment.
### Graceful Shutdown
On SIGINT (Ctrl+C) or SIGTERM:
1. Services stop in reverse dependency order
2. SIGTERM is sent first (graceful)
3. After 5 seconds, SIGKILL if still running
4. PID files are cleaned up
## Without Configuration
If no `micro.mu` or `micro.json` exists:
1. All `main.go` files are discovered recursively
2. Each is built and run
3. No dependency ordering
4. Hot reload still works
## Logs
Service logs are written to:
- Terminal: Colorized with service name prefix
- File: `~/micro/logs/{service}-{hash}.log`
View logs:
```bash
micro logs # List available logs
micro logs users # Show logs for 'users' service
```
## Process Management
```bash
micro status # Show running services
micro stop users # Stop a specific service
```
## Example: micro/blog
The [micro/blog](https://github.com/micro/blog) project demonstrates a multi-service setup:
```
# micro.mu
service users
path ./users
port 8081
service posts
path ./posts
port 8082
depends users
service comments
path ./comments
port 8083
depends users posts
service web
path ./web
port 8089
depends users posts comments
```
Run it:
```bash
micro run github.com/micro/blog
```
## Options
```bash
micro run # Gateway on :8080, hot reload
micro run --address :3000 # Custom gateway port
micro run --no-gateway # Services only, no HTTP gateway
micro run --no-watch # Disable hot reload
micro run --env production # Use production environment
micro run --mcp-address :3000 # Enable MCP protocol gateway for AI clients
```
## Tips
1. **Browse First**: Open http://localhost:8080 to explore your services
2. **Try the Agent**: Open http://localhost:8080/agent to chat with your services via AI
3. **Port Configuration**: Set `port` for services to enable health check waiting
4. **Health Endpoint**: Implement `/health` returning 200 for reliable startup sequencing
5. **Environment Separation**: Keep secrets in production env, use file:// paths for development
6. **Hot Reload Scope**: Only `.go` files trigger rebuilds; static assets don't
@@ -0,0 +1,165 @@
---
layout: default
title: Add MCP to Existing Services
---
# Add MCP to Existing Services
You have a working go-micro service and want to make it accessible to AI agents via MCP. This guide covers the three approaches, from simplest to most flexible.
## Option 1: One-Line Setup (Recommended)
Add a single option to your service constructor:
```go
import "go-micro.dev/v6/gateway/mcp"
func main() {
service := micro.NewService("myservice",
mcp.WithMCP(":3001"), // Add this line
)
service.Init()
// ... register handlers as before
service.Run()
}
```
That's it. Your service now exposes all registered handlers as MCP tools at `http://localhost:3001/mcp/tools`.
## Option 2: Standalone MCP Gateway
If you want the MCP gateway to run separately from your services (e.g., in production with multiple services):
```go
import "go-micro.dev/v6/gateway/mcp"
// Start MCP gateway alongside your service
go mcp.ListenAndServe(":3001", mcp.Options{
Registry: service.Options().Registry,
})
```
This discovers all services in the registry and exposes them as tools.
## Option 3: CLI (No Code Changes)
If you don't want to modify your service code at all:
```bash
# Start your service normally
go run .
# In another terminal, start the MCP gateway
micro mcp serve --address :3001
```
The CLI approach uses the same registry to discover running services.
## Improving Agent Experience
Once MCP is enabled, improve how agents interact with your service by adding documentation.
### Step 1: Add Doc Comments
Before:
```go
func (s *Users) Get(ctx context.Context, req *GetRequest, rsp *GetResponse) error {
```
After:
```go
// Get retrieves a user by their unique ID. Returns the full user profile
// including email, display name, and account status.
//
// @example {"id": "user-123"}
func (s *Users) Get(ctx context.Context, req *GetRequest, rsp *GetResponse) error {
```
The MCP gateway automatically extracts these comments and presents them to agents as tool descriptions.
### Step 2: Add Struct Tag Descriptions
```go
type GetRequest struct {
ID string `json:"id" description:"User ID in UUID format"`
}
type GetResponse struct {
Name string `json:"name" description:"Display name"`
Email string `json:"email" description:"Primary email address"`
Active bool `json:"active" description:"Whether the account is active"`
}
```
### Step 3: Add Auth Scopes (Optional)
Restrict which agents can call which endpoints:
```go
handler := service.Server().NewHandler(
new(Users),
server.WithEndpointScopes("Users.Delete", "users:admin"),
server.WithEndpointScopes("Users.Get", "users:read"),
)
```
Then configure the MCP gateway with auth:
```go
mcp.ListenAndServe(":3001", mcp.Options{
Registry: service.Options().Registry,
Auth: authProvider,
Scopes: map[string][]string{
"myservice.Users.Delete": {"users:admin"},
"myservice.Users.Get": {"users:read"},
},
})
```
## Using with Claude Code
Once your service is running with MCP, connect it to Claude Code:
```bash
# Option A: stdio transport (recommended for local dev)
micro mcp serve
# Option B: Add to Claude Code settings
```
```json
{
"mcpServers": {
"my-services": {
"command": "micro",
"args": ["mcp", "serve"]
}
}
}
```
## Verify It Works
```bash
# List all tools the MCP gateway exposes
curl http://localhost:3001/mcp/tools | jq
# Test a specific tool
curl -X POST http://localhost:3001/mcp/call \
-H 'Content-Type: application/json' \
-d '{"tool": "myservice.Users.Get", "arguments": {"id": "user-123"}}'
```
## What Doesn't Need to Change
- **Handler signatures** - No changes needed to your RPC handlers
- **Proto definitions** - Existing protos work as-is
- **Client code** - Services calling each other still use the normal RPC client
- **Tests** - Existing tests continue to work
- **Deployment** - Add a port for MCP, everything else stays the same
## Next Steps
- [Tool Descriptions Guide](../tool-descriptions.md) - Write better descriptions for agents
- [MCP Security Guide](../mcp-security.md) - Auth, scopes, and audit logging
- [Agent Patterns](../agent-patterns.md) - Architecture patterns for agent integration
@@ -0,0 +1,413 @@
---
layout: default
---
# Migrating from gRPC
Step-by-step guide to migrating existing gRPC services to Go Micro.
## Why Migrate?
Go Micro adds:
- Built-in service discovery
- Client-side load balancing
- Pub/sub messaging
- Multiple transport options
- Unified tooling
You keep:
- Your proto definitions
- gRPC performance (via gRPC transport)
- Type safety
- Streaming support
## Migration Strategy
### Phase 1: Parallel Running
Run Go Micro alongside existing gRPC services
### Phase 2: Gradual Migration
Migrate services one at a time
### Phase 3: Complete Migration
All services on Go Micro
## Step-by-Step Migration
### 1. Existing gRPC Service
```protobuf
// proto/hello.proto
syntax = "proto3";
package hello;
option go_package = "./proto;hello";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
```
```go
// Original gRPC server
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "myapp/proto"
)
type server struct {
pb.UnimplementedGreeterServer
}
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + req.Name}, nil
}
func main() {
lis, _ := net.Listen("tcp", ":50051")
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Fatal(s.Serve(lis))
}
```
### 2. Generate Go Micro Code
Update your proto generation:
```bash
# Install protoc-gen-micro
go install go-micro.dev/v6/cmd/protoc-gen-micro@latest
# Generate both gRPC and Go Micro code
protoc --proto_path=. \
--go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
--micro_out=. --micro_opt=paths=source_relative \
proto/hello.proto
```
This generates:
- `hello.pb.go` - Protocol Buffers types
- `hello_grpc.pb.go` - gRPC client/server (keep for compatibility)
- `hello.pb.micro.go` - Go Micro client/server (new)
### 3. Migrate Server to Go Micro
```go
// Go Micro server
package main
import (
"context"
"go-micro.dev/v6"
"go-micro.dev/v6/server"
pb "myapp/proto"
)
type Greeter struct{}
func (s *Greeter) SayHello(ctx context.Context, req *pb.HelloRequest, rsp *pb.HelloReply) error {
rsp.Message = "Hello " + req.Name
return nil
}
func main() {
svc := micro.NewService("greeter",
)
svc.Init()
pb.RegisterGreeterHandler(svc.Server(), new(Greeter))
if err := svc.Run(); err != nil {
log.Fatal(err)
}
}
```
**Key differences:**
- No manual port binding (Go Micro handles it)
- Automatic service registration
- Returns error, response via pointer parameter
### 4. Migrate Client
**Original gRPC client:**
```go
conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
defer conn.Close()
client := pb.NewGreeterClient(conn)
rsp, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "John"})
```
**Go Micro client:**
```go
svc := micro.NewService("client")
svc.Init()
client := pb.NewGreeterService("greeter", svc.Client())
rsp, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "John"})
```
**Benefits:**
- No hardcoded addresses
- Automatic service discovery
- Client-side load balancing
- Automatic retries
### 5. Keep gRPC Transport (Optional)
Use gRPC as the underlying transport:
```go
import (
"go-micro.dev/v6"
"go-micro.dev/v6/client"
"go-micro.dev/v6/server"
grpcclient "go-micro.dev/v6/client/grpc"
grpcserver "go-micro.dev/v6/server/grpc"
)
svc := micro.NewService("greeter",
micro.Client(grpcclient.NewClient()),
micro.Server(grpcserver.NewServer()),
)
```
This gives you:
- gRPC performance
- Go Micro features (discovery, load balancing)
- Compatible with existing gRPC clients
## Streaming Migration
### Original gRPC Streaming
```protobuf
service Greeter {
rpc StreamHellos (stream HelloRequest) returns (stream HelloReply) {}
}
```
```go
func (s *server) StreamHellos(stream pb.Greeter_StreamHellosServer) error {
for {
req, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
stream.Send(&pb.HelloReply{Message: "Hello " + req.Name})
}
}
```
### Go Micro Streaming
```go
func (s *Greeter) StreamHellos(ctx context.Context, stream server.Stream) error {
for {
var req pb.HelloRequest
if err := stream.Recv(&req); err != nil {
return err
}
if err := stream.Send(&pb.HelloReply{Message: "Hello " + req.Name}); err != nil {
return err
}
}
}
```
## Service Discovery Migration
### Before (gRPC with Consul)
```go
// Manually register with Consul
config := api.DefaultConfig()
config.Address = "consul:8500"
client, _ := api.NewClient(config)
reg := &api.AgentServiceRegistration{
ID: "greeter-1",
Name: "greeter",
Address: "localhost",
Port: 50051,
}
client.Agent().ServiceRegister(reg)
// Cleanup on shutdown
defer client.Agent().ServiceDeregister("greeter-1")
```
### After (Go Micro)
```go
import "go-micro.dev/v6/registry/consul"
reg := consul.NewConsulRegistry()
svc := micro.NewService("greeter",
micro.Registry(reg),
)
// Registration automatic on Run()
// Deregistration automatic on shutdown
svc.Run()
```
## Load Balancing Migration
### Before (gRPC with custom LB)
```go
// Need external load balancer or custom implementation
// Example: round-robin DNS, Envoy, nginx
```
### After (Go Micro)
```go
import "go-micro.dev/v6/selector"
// Client-side load balancing built-in
svc := micro.NewService("greeter",
micro.Selector(selector.NewSelector(
selector.SetStrategy(selector.RoundRobin),
)),
)
```
## Gradual Migration Path
### 1. Start with New Services
New services use Go Micro, existing services stay on gRPC.
```go
// New Go Micro service can call gRPC services
// Configure gRPC endpoints directly
grpcConn, _ := grpc.Dial("old-service:50051", grpc.WithInsecure())
oldClient := pb.NewOldServiceClient(grpcConn)
```
### 2. Migrate Read-Heavy Services First
Services with many clients benefit most from service discovery.
### 3. Migrate Services with Fewest Dependencies
Leaf services are easier to migrate.
### 4. Add Adapters if Needed
```go
// gRPC adapter for Go Micro service
type GRPCAdapter struct {
microClient pb.GreeterService
}
func (a *GRPCAdapter) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
return a.microClient.SayHello(ctx, req)
}
// Register adapter as gRPC server
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &GRPCAdapter{microClient: microClient})
```
## Checklist
- [ ] Update proto generation to include `--micro_out`
- [ ] Convert handler signatures (response via pointer)
- [ ] Replace `grpc.Dial` with Go Micro client
- [ ] Configure service discovery (Consul, Etcd, etc)
- [ ] Update deployment (remove hardcoded ports)
- [ ] Update monitoring (Go Micro metrics)
- [ ] Test service-to-service communication
- [ ] Update documentation
- [ ] Train team on Go Micro patterns
## Common Issues
### Port Already in Use
**gRPC**: Manual port management
```go
lis, _ := net.Listen("tcp", ":50051")
```
**Go Micro**: Automatic or explicit
```go
// Let Go Micro choose
svc := micro.NewService("greeter")
// Or specify
svc := micro.NewService("greeter",
micro.Address(":50051"),
)
```
### Service Not Found
Check registry:
```bash
# Consul
curl http://localhost:8500/v1/catalog/services
# Or use micro CLI
micro services
```
### Different Serialization
gRPC uses protobuf by default. Go Micro supports multiple codecs.
Ensure both use protobuf:
```go
import "go-micro.dev/v6/codec/proto"
svc := micro.NewService("greeter",
micro.Codec("application/protobuf", proto.Marshaler{}),
)
```
## Performance Comparison
| Scenario | gRPC | Go Micro (HTTP) | Go Micro (gRPC) |
|----------|------|----------------|-----------------|
| Simple RPC | ~25k req/s | ~20k req/s | ~24k req/s |
| With Discovery | N/A | ~18k req/s | ~22k req/s |
| Streaming | ~30k msg/s | ~15k msg/s | ~28k msg/s |
*Go Micro with gRPC transport performs similarly to pure gRPC*
## Next Steps
- Read [Go Micro Architecture](../architecture.md)
- Explore [Plugin System](../plugins.md)
- Check [Production Patterns](../examples/realworld/)
## Need Help?
- [Examples](../examples/)
- [GitHub Issues](https://github.com/micro/go-micro/issues)
- [API Documentation](https://pkg.go.dev/go-micro.dev/v6)
@@ -0,0 +1,37 @@
---
layout: default
---
# Migration Guides
Step-by-step guides for migrating to Go Micro from other frameworks.
## Available Guides
- [v5 to v6](v5-to-v6.md) - Upgrade to v6: new module path, TLS secure by default, `NewService`
- [Add MCP to Existing Services](add-mcp.md) - Make your services AI-accessible in 5 minutes
- [From gRPC](from-grpc.md) - Migrate from gRPC to Go Micro with minimal code changes
## Coming Soon
We're working on additional migration guides:
- **From go-kit** - Migrate from Go kit microservices framework
- **From Standard Library** - Upgrade from net/http and net/rpc
- **From Gin/Echo** - Transition from HTTP-only frameworks
- **From Micro v3** - Upgrade from older Go Micro versions
## Why Migrate to Go Micro?
- **Pluggable Architecture** - Swap components without changing code
- **Zero Configuration** - Works out of the box with sensible defaults
- **Progressive Enhancement** - Start simple, add complexity when needed
- **Unified Abstractions** - Registry, transport, broker, store all integrated
- **Active Development** - Regular updates and community support
## Need Help?
- Check the [Framework Comparison](../comparison.md) guide
- Review [Architecture Decisions](../../architecture/index.md) to understand design choices
- Ask questions in [GitHub Discussions](https://github.com/micro/go-micro/discussions)
- See the [Contributing Guide](../../contributing.md) to contribute new migration guides
@@ -0,0 +1,71 @@
---
layout: default
---
# Migrating from v5 to v6
v6 is a small, mechanical upgrade. The bulk of it is the Go module path; the
behavioral changes are two, both with a one-line fix.
## 1. Module path: `go-micro.dev/v6`
Go puts the major version in the import path, so every import changes:
```go
// before
import "go-micro.dev/v5"
import "go-micro.dev/v5/server"
// after
import "go-micro.dev/v6"
import "go-micro.dev/v6/server"
```
A repo-wide find/replace does it:
```bash
grep -rl 'go-micro.dev/v5' --include='*.go' . \
| xargs sed -i 's|go-micro.dev/v5|go-micro.dev/v6|g'
go mod tidy
```
Update the CLI too:
```bash
go install go-micro.dev/v6/cmd/micro@latest
```
## 2. TLS is verified by default
In v5, TLS certificate verification was **off** by default (you opted in with
`MICRO_TLS_SECURE=true`). In v6 it is **on** by default — the safe choice now
that an agent, not just a human on a trusted network, can reach an endpoint.
- **Production:** nothing to do. Verification is on.
- **`MICRO_TLS_SECURE` is gone** — remove it; it's the default now.
- **Self-signed certs (local/dev):** opt out with `MICRO_TLS_INSECURE=true`, or
call `tls.InsecureConfig()` directly.
## 3. `NewService` is the service constructor
The service constructor is now symmetric with `NewAgent` and `NewFlow`:
```go
service := micro.NewService("greeter", micro.Address(":8080"))
agent := micro.NewAgent("task-mgr", micro.AgentServices("task"))
flow := micro.NewFlow("onboard", micro.FlowTrigger("events.user.created"))
```
- `micro.New("greeter", ...)` still works as a **deprecated alias** — no rush,
but prefer `NewService`.
- The old name-less form `micro.NewService(micro.Name("greeter"), ...)` is
**removed**; pass the name positionally: `micro.NewService("greeter", ...)`.
Generated services already use `NewService` — re-running `micro new` or
`micro run --prompt` emits the v6 form.
## That's it
No other API changed. Agents, services, flows, the registry/broker/store
interfaces, MCP, A2A, and x402 all work as they did — just under
`go-micro.dev/v6` and secure by default.

Some files were not shown because too many files have changed in this diff Show More