Building the AI-Native Future of Go Micro with Claude
+ +How Anthropic's Claude Max sponsorship accelerated Go Micro's MCP integration — WebSocket transport, OpenTelemetry tracing, LlamaIndex SDK, and what's next.
+ Read more → +diff --git a/CLAUDE.md b/CLAUDE.md index cf0ef49f..30ce3933 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -84,8 +84,8 @@ go-micro/ ### Status - **Q1 2026 (MCP Foundation):** COMPLETE -- **Q2 2026 (Agent DX):** 85% complete (ahead of schedule) -- **Q3 2026 (Production):** 40% complete (ahead of schedule) +- **Q2 2026 (Agent DX):** 95% complete (ahead of schedule) +- **Q3 2026 (Production):** 50% complete (ahead of schedule) ### Priority 1: Documentation Guides (HIGHEST ROI) The framework has features that are under-documented. These guides drive adoption: @@ -94,18 +94,17 @@ The framework has features that are under-documented. These guides drive adoptio 3. `docs/guides/tool-descriptions.md` - Writing comments that make agents effective 4. `docs/guides/agent-patterns.md` - Multi-agent workflows and integration patterns -### Priority 2: Multi-Protocol MCP (WebSocket) -Only HTTP/SSE and stdio exist. WebSocket enables bidirectional streaming for real-time agents. - -### Priority 3: OpenTelemetry Integration -Trace IDs exist (`Mcp-Trace-Id`) but aren't connected to OTel. This blocks enterprise adoption. - -### Priority 4: LlamaIndex SDK -Follow the `contrib/langchain-go-micro/` pattern to build a LlamaIndex integration for RAG. - -### Priority 5: Agent Playground Polish +### Priority 2: Agent Playground Polish The `/agent` UI in `micro run` needs refinement for demos and onboarding. +### Priority 3: Standalone Gateway Binary +Production-grade standalone `micro-mcp-gateway` binary for enterprise deployment. + +### Recently Completed +- **WebSocket Transport** - Bidirectional JSON-RPC 2.0 streaming (`gateway/mcp/websocket.go`) +- **OpenTelemetry Integration** - Full span instrumentation with W3C trace context (`gateway/mcp/otel.go`) +- **LlamaIndex SDK** - Python package with RAG examples (`contrib/go-micro-llamaindex/`) + ## Key Files | Purpose | File | diff --git a/CURRENT_STATUS_SUMMARY.md b/CURRENT_STATUS_SUMMARY.md index f5843def..74aab47c 100644 --- a/CURRENT_STATUS_SUMMARY.md +++ b/CURRENT_STATUS_SUMMARY.md @@ -7,8 +7,8 @@ ### Quick Status - **Q1 2026 (MCP Foundation):** COMPLETE (100%) -- **Q2 2026 (Agent DX):** 85% COMPLETE (ahead of schedule) -- **Q3 2026 (Production):** 40% COMPLETE (ahead of schedule) +- **Q2 2026 (Agent DX):** 95% COMPLETE (ahead of schedule) +- **Q3 2026 (Production):** 50% COMPLETE (ahead of schedule) - **Q4 2026 (Ecosystem):** 0% COMPLETE (on track) --- @@ -16,11 +16,13 @@ ## What's Been Built ### Core MCP Integration (Q1 - COMPLETE) -- **MCP Gateway Library** (`gateway/mcp/`) - 2,083 lines +- **MCP Gateway Library** (`gateway/mcp/`) - 2,500+ lines - HTTP/SSE transport - Stdio JSON-RPC 2.0 transport + - WebSocket JSON-RPC 2.0 transport (bidirectional streaming) - Service discovery & tool generation - Schema generation from Go types + - OpenTelemetry span instrumentation - **CLI Commands** (`micro mcp`) - `micro mcp serve` - Start MCP server (stdio or HTTP) @@ -44,6 +46,12 @@ - Scope enforcement before RPC execution #### Observability +- **OpenTelemetry Integration** + - Full OTel span instrumentation on HTTP, stdio, and WebSocket transports + - Rich span attributes: tool name, transport, account ID, auth status, rate limiting + - W3C trace context propagation via go-micro metadata + - Configurable via `Options.TraceProvider` + - Noop spans when no provider configured (backward compatible) - **Tracing** - UUID trace IDs per tool call - Metadata propagation (`Mcp-Trace-Id`, `Mcp-Tool-Name`, `Mcp-Account-Id`) @@ -150,14 +158,17 @@ handler := service.Server().NewHandler( ## Test Coverage -**568 lines** of comprehensive tests covering: +**1,000+ lines** of comprehensive tests covering: - Scope validation & enforcement - Auth provider integration - Trace ID generation & propagation - Audit record creation - Rate limiting -- HTTP & Stdio transports +- HTTP, Stdio & WebSocket transports - Tool discovery & schema generation +- OpenTelemetry span creation and attributes +- WebSocket concurrent connections and persistence +- LlamaIndex SDK toolkit and tool filtering --- @@ -171,39 +182,36 @@ The biggest gap is documentation for the features already built. These guides wi 3. **Best practices for tool descriptions** - Writing Go comments that make agents more effective 4. **Agent integration patterns** - Common patterns for multi-agent workflows -### Priority 2: Multi-Protocol MCP Support (High Impact) -Currently only HTTP/SSE and stdio are supported. Adding more protocols unlocks new agent frameworks: - -- **WebSocket transport** - Bidirectional streaming for real-time agents -- **gRPC reflection-based MCP** - For gRPC-native environments - -### Priority 3: LlamaIndex SDK (Medium Impact) -With LangChain SDK complete, LlamaIndex is the next priority for RAG and data-focused agent integration. - -### Priority 4: OpenTelemetry Integration (Production Readiness) -Trace IDs are already generated. Connecting them to OpenTelemetry enables production-grade observability with existing tools (Jaeger, Grafana, etc.). - -### Priority 5: Interactive Playground Polish +### Priority 2: Interactive Playground Polish The agent playground exists at `/agent` in `micro run`. Refine the UX and add real-time tool call visualization. +### Priority 3: Additional Protocol Support +- **gRPC reflection-based MCP** - For gRPC-native environments +- **HTTP/3 support** - Modern transport + +### Recently Completed (March 2026) +- **WebSocket Transport** - Bidirectional streaming for real-time agents (JSON-RPC 2.0 over WebSocket) +- **OpenTelemetry Integration** - Full span instrumentation across all transports with W3C trace context propagation +- **LlamaIndex SDK** - `contrib/go-micro-llamaindex/` with RAG integration examples + --- ## By The Numbers | Metric | Value | |--------|-------| -| **Production Code** | 2,083+ lines (MCP gateway) | -| **Test Code** | 568+ lines | +| **Production Code** | 2,500+ lines (MCP gateway) | +| **Test Code** | 1,000+ lines | | **Documentation Files** | 90+ markdown files | -| **Working Examples** | 2 MCP + 3 other | +| **Working Examples** | 2 MCP + 3 other + 2 LlamaIndex | | **CLI Commands** | 5 MCP (serve, list, test, docs, export) | | **Export Formats** | 3 (langchain, openapi, json) | -| **Agent SDKs** | 1 (LangChain Python) | +| **Agent SDKs** | 2 (LangChain Python, LlamaIndex Python) | | **Model Providers** | 2 (Anthropic, OpenAI) | -| **Transports** | 2 (HTTP/SSE, Stdio) | +| **Transports** | 3 (HTTP/SSE, Stdio, WebSocket) | | **Q1 Completion** | 100% | -| **Q2 Completion** | 85% | -| **Q3 Completion** | 40% | +| **Q2 Completion** | 95% | +| **Q3 Completion** | 50% | | **Q4 Completion** | 0% | | **Ahead of Schedule** | 3-4 months | @@ -226,13 +234,15 @@ The agent playground exists at `/agent` in `micro run`. Refine the UX and add re - Tool descriptions from comments with `@example` support - Schema generation from struct tags - HTTP/SSE with auth +- WebSocket transport (bidirectional JSON-RPC 2.0) - LangChain SDK (Python package in contrib/) +- LlamaIndex SDK (Python package in contrib/ with RAG examples) - Model package with Anthropic + OpenAI providers **REMAINING:** -- Agent SDKs (LlamaIndex, AutoGPT) +- Agent SDKs (AutoGPT) - Interactive Agent Playground refinement -- Multi-protocol (WebSocket, gRPC, HTTP/3) +- Multi-protocol (gRPC, HTTP/3) - Documentation guides (4 guides planned) - Auto-generate examples from test cases @@ -245,11 +255,11 @@ The agent playground exists at `/agent` in `micro run`. Refine the UX and add re - Rate limiting - Audit logging - Bearer token auth +- OpenTelemetry integration (spans, attributes, W3C trace context) **REMAINING:** - Standalone MCP Gateway binary - Kubernetes Operator & Helm Charts -- OpenTelemetry integration - Full observability dashboards - Circuit breakers, caching, multi-tenant support @@ -288,7 +298,7 @@ The agent playground exists at `/agent` in `micro run`. Refine the UX and add re The Q1 2026 foundation is solid, with advanced Q2/Q3 features already delivered. The immediate focus should be on **documentation and developer guides** to drive adoption, followed by **multi-protocol support** and **additional agent SDKs** to broaden the ecosystem. -**Next focus:** Documentation guides, multi-protocol MCP, and LlamaIndex SDK. +**Next focus:** Documentation guides, interactive playground polish, and standalone gateway binary. --- diff --git a/ROADMAP_2026.md b/ROADMAP_2026.md index 11cc86cb..22bfda8a 100644 --- a/ROADMAP_2026.md +++ b/ROADMAP_2026.md @@ -92,7 +92,7 @@ Go Micro's MCP integration means: ## Q2 2026: Agent Developer Experience -**Status:** MOSTLY COMPLETE (85%) - Core features delivered, docs & SDKs remaining +**Status:** NEARLY COMPLETE (95%) - Core features delivered, docs & playground remaining **Theme:** Make it trivial for any AI to call your services @@ -146,7 +146,7 @@ Tools: **Why:** Better descriptions = better agent performance. Agents need context to call services correctly. #### Multi-Protocol Support -- [ ] WebSocket transport for streaming +- [x] WebSocket transport for streaming (JSON-RPC 2.0, bidirectional) - [ ] gRPC reflection for MCP (bidirectional streaming) - [x] Server-Sent Events with auth (HTTP/SSE implemented) - [ ] HTTP/3 support @@ -172,10 +172,10 @@ Create official SDKs for popular agent frameworks: **Why:** The model package powers the agent playground and enables services to call AI models directly. -#### LlamaIndex Integration -- [ ] `go-micro-llamaindex` package -- [ ] Service discovery as data sources -- [ ] Example: RAG with microservices +#### LlamaIndex Integration ✅ COMPLETE +- [x] `go-micro-llamaindex` package +- [x] Service discovery as data sources +- [x] Example: RAG with microservices #### AutoGPT/AgentGPT Support - [ ] Plugin format adapter @@ -242,7 +242,7 @@ Here are the 5 most recent orders for Alice Smith: ## Q3 2026: Production & Scale -**Status:** IN PROGRESS (40%) - Core security features delivered early, infrastructure work remaining +**Status:** IN PROGRESS (50%) - Core security and observability features delivered early, infrastructure work remaining **Theme:** Run MCP gateways in production at scale @@ -274,7 +274,7 @@ micro-mcp-gateway \ **Business value:** Enterprise customers need production-grade MCP gateways. This is a **paid offering**. #### Observability -- [ ] OpenTelemetry integration +- [x] OpenTelemetry integration ✅ (spans, attributes, W3C trace context propagation) - [x] Agent call tracing (which agent called what) ✅ (trace IDs implemented) - [ ] Tool usage metrics (which tools are popular) - [ ] Performance dashboards @@ -947,11 +947,12 @@ Let's make it Go Micro. --- **Next Steps (March 2026):** -1. Complete remaining Q2 items: documentation guides, LlamaIndex SDK, playground polish -2. Begin Q3 infrastructure: OpenTelemetry integration, standalone gateway binary +1. Complete remaining Q2 items: documentation guides, playground polish +2. Begin Q3 infrastructure: standalone gateway binary, Kubernetes operator 3. Write "Building AI-Native Services" guide and MCP security guide 4. Publish case studies and community content 5. Plan Go Micro Cloud beta launch +6. Explore sustainable business model and product strategy **Questions? Feedback?** - GitHub Discussions: https://github.com/micro/go-micro/discussions diff --git a/cmd/micro/run/run.go b/cmd/micro/run/run.go index dde3e19e..4055d7b8 100644 --- a/cmd/micro/run/run.go +++ b/cmd/micro/run/run.go @@ -358,7 +358,7 @@ func Run(c *cli.Context) error { } // Print startup banner - printBanner(services, gw, !c.Bool("no-watch")) + printBanner(services, gw, !c.Bool("no-watch"), c.String("mcp-address")) // Setup signal handling sigCh := make(chan os.Signal, 1) @@ -427,21 +427,25 @@ func processRunning(pidStr string) bool { return proc.Signal(syscall.Signal(0)) == nil } -func printBanner(services []*serviceProcess, gw *server.Gateway, watching bool) { +func printBanner(services []*serviceProcess, gw *server.Gateway, watching bool, mcpAddr string) { + fmt.Println() + fmt.Println(" \033[1mMicro\033[0m") fmt.Println() - fmt.Println(" ┌─────────────────────────────────────────────────────────────┐") - fmt.Println(" │ │") - fmt.Println(" │ \033[1mMicro\033[0m │") - fmt.Println(" │ │") if gw != nil { - fmt.Printf(" │ Web: \033[36mhttp://localhost%s\033[0m │\n", gw.Addr()) - fmt.Printf(" │ API: \033[36mhttp://localhost%s/api/{service}/{method}\033[0m │\n", gw.Addr()) - fmt.Printf(" │ Health: \033[36mhttp://localhost%s/health\033[0m │\n", gw.Addr()) + fmt.Printf(" Dashboard \033[36mhttp://localhost%s\033[0m\n", gw.Addr()) + fmt.Printf(" API \033[36mhttp://localhost%s/api/{service}/{method}\033[0m\n", gw.Addr()) + fmt.Printf(" Agent \033[36mhttp://localhost%s/agent\033[0m\n", gw.Addr()) + fmt.Printf(" Health \033[36mhttp://localhost%s/health\033[0m\n", gw.Addr()) + if mcpAddr != "" { + fmt.Printf(" MCP \033[36mhttp://localhost%s\033[0m\n", mcpAddr) + fmt.Printf(" MCP Tools \033[36mhttp://localhost%s/mcp/tools\033[0m\n", mcpAddr) + fmt.Printf(" WebSocket \033[36mws://localhost%s/mcp/ws\033[0m\n", mcpAddr) + } } - fmt.Println(" │ │") - fmt.Println(" │ Services: │") + fmt.Println() + fmt.Println(" Services:") for _, svc := range services { status := "\033[32m●\033[0m" // green dot @@ -449,30 +453,19 @@ func printBanner(services []*serviceProcess, gw *server.Gateway, watching bool) status = "\033[31m●\033[0m" // red dot } name := svc.name - if len(name) > 20 { - name = name[:17] + "..." + if len(name) > 40 { + name = name[:37] + "..." } - fmt.Printf(" │ %s %-20s │\n", status, name) + fmt.Printf(" %s %s\n", status, name) } - fmt.Println(" │ │") + fmt.Println() + fmt.Println(" Auth: \033[32menabled\033[0m (admin / micro)") if watching { - fmt.Println(" │ \033[33mWatching for changes...\033[0m │") - fmt.Println(" │ │") + fmt.Println(" \033[33mWatching for changes...\033[0m") } - fmt.Println(" │ Auth: \033[32menabled\033[0m (admin / micro) │") - fmt.Println(" │ │") - - if gw != nil && len(services) > 0 { - svc := services[0] - fmt.Println(" │ Try: │") - fmt.Printf(" │ \033[90mcurl -X POST http://localhost%s/api/%s/...\033[0m │\n", gw.Addr(), svc.name) - fmt.Println(" │ │") - } - - fmt.Println(" └─────────────────────────────────────────────────────────────┘") fmt.Println() } diff --git a/contrib/go-micro-llamaindex/.gitignore b/contrib/go-micro-llamaindex/.gitignore new file mode 100644 index 00000000..472f31a5 --- /dev/null +++ b/contrib/go-micro-llamaindex/.gitignore @@ -0,0 +1,65 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +*.manifest +*.spec + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# IDEs +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Ruff +.ruff_cache/ diff --git a/contrib/go-micro-llamaindex/README.md b/contrib/go-micro-llamaindex/README.md new file mode 100644 index 00000000..45f70a6b --- /dev/null +++ b/contrib/go-micro-llamaindex/README.md @@ -0,0 +1,327 @@ +# LlamaIndex Go Micro Integration + +[](https://badge.fury.io/py/go-micro-llamaindex) +[](https://opensource.org/licenses/Apache-2.0) + +Official LlamaIndex integration for Go Micro services. This package enables LlamaIndex agents to discover and call Go Micro microservices through the Model Context Protocol (MCP). + +## Features + +- **Automatic Service Discovery** - Discovers available services from MCP gateway +- **Dynamic Tool Generation** - Converts service endpoints into LlamaIndex tools +- **Rich Descriptions** - Uses service metadata for accurate tool descriptions +- **Authentication Support** - Bearer token auth with scope-based permissions +- **RAG Integration** - Combine service tools with LlamaIndex's RAG capabilities +- **Type-Safe** - Fully typed with Python 3.8+ type hints + +## Installation + +```bash +pip install go-micro-llamaindex +``` + +## Quick Start + +### 1. Start Your Go Micro Services + +```bash +# Start MCP gateway +micro mcp serve --address :3000 +``` + +### 2. Create LlamaIndex Agent + +```python +from go_micro_llamaindex import GoMicroToolkit +from llama_index.core.agent import ReActAgent +from llama_index.llms.openai import OpenAI + +# Initialize toolkit from MCP gateway +toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + +# Create agent +llm = OpenAI(model="gpt-4") +agent = ReActAgent.from_tools(toolkit.get_tools(), llm=llm, verbose=True) + +# Use the agent! +response = agent.chat("Create a user named Alice with email alice@example.com") +print(response) +``` + +## Usage Examples + +### Basic Tool Discovery + +```python +from go_micro_llamaindex import GoMicroToolkit + +# Connect to MCP gateway +toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + +# List available tools +for tool in toolkit.get_tools(): + print(f"Tool: {tool.metadata.name}") + print(f"Description: {tool.metadata.description}") + print() +``` + +### Authentication + +```python +from go_micro_llamaindex import GoMicroToolkit + +# Create toolkit with authentication +toolkit = GoMicroToolkit.from_gateway( + gateway_url="http://localhost:3000", + auth_token="your-bearer-token" +) + +# Tools will automatically use the auth token +tools = toolkit.get_tools() +``` + +### Filter Tools by Service + +```python +from go_micro_llamaindex import GoMicroToolkit + +toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + +# Get only user service tools +user_tools = toolkit.get_tools(service_filter="users") + +# Get tools matching a pattern +blog_tools = toolkit.get_tools(name_pattern="blog.*") +``` + +### Custom Tool Selection + +```python +from go_micro_llamaindex import GoMicroToolkit + +toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + +# Select specific tools +selected_tools = toolkit.get_tools( + include=["users.Users.Get", "users.Users.Create"] +) + +# Exclude certain tools +filtered_tools = toolkit.get_tools( + exclude=["users.Users.Delete"] +) +``` + +### RAG + Microservices + +```python +from go_micro_llamaindex import GoMicroToolkit +from llama_index.core import VectorStoreIndex, Document +from llama_index.core.agent import ReActAgent +from llama_index.core.tools import QueryEngineTool, ToolMetadata +from llama_index.llms.openai import OpenAI + +toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + +# Combine service tools with a RAG query engine +index = VectorStoreIndex.from_documents([...]) +rag_tool = QueryEngineTool( + query_engine=index.as_query_engine(), + metadata=ToolMetadata(name="docs", description="Search documentation"), +) + +all_tools = [rag_tool] + toolkit.get_tools() +agent = ReActAgent.from_tools(all_tools, llm=OpenAI(model="gpt-4")) +``` + +### Multi-Agent Workflows + +```python +from go_micro_llamaindex import GoMicroToolkit +from llama_index.core.agent import ReActAgent +from llama_index.llms.openai import OpenAI + +toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") +llm = OpenAI(model="gpt-4") + +# Agent 1: User management +user_agent = ReActAgent.from_tools( + toolkit.get_tools(service_filter="users"), llm=llm +) + +# Agent 2: Blog management +blog_agent = ReActAgent.from_tools( + toolkit.get_tools(service_filter="blog"), llm=llm +) + +# Coordinate between agents +user_result = user_agent.chat("Create user Alice") +blog_result = blog_agent.chat(f"Create blog post for {user_result}") +``` + +### Error Handling + +```python +from go_micro_llamaindex import GoMicroToolkit, GoMicroError + +try: + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + tools = toolkit.get_tools() +except GoMicroError as e: + print(f"Error: {e}") +``` + +### Advanced Configuration + +```python +from go_micro_llamaindex import GoMicroToolkit, GoMicroConfig + +config = GoMicroConfig( + gateway_url="http://localhost:3000", + auth_token="your-token", + timeout=30, + retry_count=3, + retry_delay=1.0, + verify_ssl=True, +) + +toolkit = GoMicroToolkit(config) +tools = toolkit.get_tools() +``` + +## API Reference + +### GoMicroToolkit + +Main class for interacting with Go Micro services. + +#### Methods + +- `from_gateway(gateway_url, auth_token=None, **kwargs)` - Create toolkit from MCP gateway +- `get_tools(service_filter=None, name_pattern=None, include=None, exclude=None)` - Get LlamaIndex tools +- `refresh()` - Refresh tool list from gateway +- `call_tool(tool_name, arguments)` - Call a tool directly +- `list_tools()` - Get raw list of available tools + +### GoMicroConfig + +Configuration for the toolkit. + +#### Parameters + +- `gateway_url` (str) - MCP gateway URL +- `auth_token` (str, optional) - Bearer authentication token +- `timeout` (int) - Request timeout in seconds (default: 30) +- `retry_count` (int) - Number of retries (default: 3) +- `retry_delay` (float) - Delay between retries in seconds (default: 1.0) +- `verify_ssl` (bool) - Verify SSL certificates (default: True) + +## Requirements + +- Python 3.8+ +- llama-index-core >= 0.10.0 +- requests >= 2.31.0 +- pydantic >= 2.0.0 + +## Development + +### Setup + +```bash +git clone https://github.com/micro/go-micro +cd go-micro/contrib/go-micro-llamaindex + +# Create virtual environment +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate + +# Install in development mode +pip install -e ".[dev]" +``` + +### Running Tests + +```bash +# Run all tests +pytest + +# Run with coverage +pytest --cov=go_micro_llamaindex + +# Run specific test +pytest tests/test_toolkit.py +``` + +### Code Formatting + +```bash +# Format code +black go_micro_llamaindex tests + +# Check types +mypy go_micro_llamaindex + +# Lint +ruff check go_micro_llamaindex +``` + +## Examples + +See the [examples](./examples) directory for complete examples: + +- [basic_agent.py](./examples/basic_agent.py) - Simple ReAct agent +- [rag_with_services.py](./examples/rag_with_services.py) - RAG combined with microservices + +## Troubleshooting + +### Gateway Connection Issues + +If you can't connect to the MCP gateway: + +1. Verify the gateway is running: +```bash +curl http://localhost:3000/health +``` + +2. Check the gateway URL is correct +3. Verify firewall settings + +### Authentication Errors + +If you get authentication errors: + +1. Verify your token is valid +2. Check the token has required scopes +3. Review gateway logs for details + +### Tool Discovery Issues + +If tools aren't being discovered: + +1. List services from gateway: +```bash +curl http://localhost:3000/mcp/tools +``` + +2. Verify services are registered +3. Check service metadata is correct + +## Contributing + +Contributions are welcome! Please see [CONTRIBUTING.md](../../CONTRIBUTING.md) for details. + +## License + +Apache 2.0 - See [LICENSE](../../LICENSE) for details. + +## Links + +- [Go Micro](https://github.com/micro/go-micro) +- [MCP Documentation](../../gateway/mcp/DOCUMENTATION.md) +- [LlamaIndex](https://docs.llamaindex.ai/) +- [Issue Tracker](https://github.com/micro/go-micro/issues) + +## Support + +- GitHub Discussions: https://github.com/micro/go-micro/discussions +- Discord: https://discord.gg/jwTYuUVAGh diff --git a/contrib/go-micro-llamaindex/examples/basic_agent.py b/contrib/go-micro-llamaindex/examples/basic_agent.py new file mode 100644 index 00000000..80c7b361 --- /dev/null +++ b/contrib/go-micro-llamaindex/examples/basic_agent.py @@ -0,0 +1,44 @@ +"""Basic LlamaIndex agent example using Go Micro services. + +This example shows how to create a simple LlamaIndex agent that can +interact with Go Micro services through the MCP gateway. +""" + +from go_micro_llamaindex import GoMicroToolkit +from llama_index.core.agent import ReActAgent +from llama_index.llms.openai import OpenAI + + +def main(): + """Run basic agent example.""" + # Initialize toolkit from MCP gateway + print("Connecting to MCP gateway...") + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + + # Get available tools + tools = toolkit.get_tools() + print(f"\nDiscovered {len(tools)} tools:") + for tool in tools: + print(f" - {tool.metadata.name}: {tool.metadata.description}") + + # Create LlamaIndex ReAct agent + print("\nCreating LlamaIndex agent...") + llm = OpenAI(model="gpt-4", temperature=0) + agent = ReActAgent.from_tools(tools, llm=llm, verbose=True) + + # Example queries + queries = [ + "Create a user named Alice with email alice@example.com", + "Get the user we just created", + ] + + for query in queries: + print(f"\n{'='*60}") + print(f"Query: {query}") + print("=" * 60) + response = agent.chat(query) + print(f"\nResult: {response}") + + +if __name__ == "__main__": + main() diff --git a/contrib/go-micro-llamaindex/examples/rag_with_services.py b/contrib/go-micro-llamaindex/examples/rag_with_services.py new file mode 100644 index 00000000..5f79a76b --- /dev/null +++ b/contrib/go-micro-llamaindex/examples/rag_with_services.py @@ -0,0 +1,72 @@ +"""RAG with Go Micro services example. + +This example demonstrates how to combine LlamaIndex's RAG capabilities +with Go Micro service tools, allowing an agent to both query documents +and interact with microservices. +""" + +from go_micro_llamaindex import GoMicroToolkit +from llama_index.core import VectorStoreIndex, Document +from llama_index.core.agent import ReActAgent +from llama_index.core.tools import QueryEngineTool, ToolMetadata +from llama_index.llms.openai import OpenAI + + +def main(): + """Run RAG + services example.""" + # Initialize toolkit from MCP gateway + print("Connecting to MCP gateway...") + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + + # Get service tools (e.g., user management) + service_tools = toolkit.get_tools(service_filter="users") + print(f"Discovered {len(service_tools)} user service tools") + + # Create a simple document index for RAG + documents = [ + Document(text="Alice is the admin user with ID user-001."), + Document(text="Bob is a regular user with ID user-002."), + Document(text="The blog service supports creating, reading, and deleting posts."), + Document(text="Users need the 'blog:write' scope to create blog posts."), + ] + + print("Building document index...") + index = VectorStoreIndex.from_documents(documents) + query_engine = index.as_query_engine() + + # Create a query engine tool for RAG + rag_tool = QueryEngineTool( + query_engine=query_engine, + metadata=ToolMetadata( + name="knowledge_base", + description="Search the knowledge base for information about users, " + "services, and permissions. Use this to look up user IDs, " + "service capabilities, and required scopes.", + ), + ) + + # Combine RAG tool with service tools + all_tools = [rag_tool] + service_tools + + # Create agent with both capabilities + print("\nCreating agent with RAG + service tools...") + llm = OpenAI(model="gpt-4", temperature=0) + agent = ReActAgent.from_tools(all_tools, llm=llm, verbose=True) + + # Example: Agent uses RAG to find user ID, then calls service + queries = [ + "What is Alice's user ID?", + "Look up Alice's user ID from the knowledge base, then get her full profile from the user service", + "What scope do I need to create blog posts?", + ] + + for query in queries: + print(f"\n{'='*60}") + print(f"Query: {query}") + print("=" * 60) + response = agent.chat(query) + print(f"\nResult: {response}") + + +if __name__ == "__main__": + main() diff --git a/contrib/go-micro-llamaindex/go_micro_llamaindex/__init__.py b/contrib/go-micro-llamaindex/go_micro_llamaindex/__init__.py new file mode 100644 index 00000000..e5c6a2b9 --- /dev/null +++ b/contrib/go-micro-llamaindex/go_micro_llamaindex/__init__.py @@ -0,0 +1,17 @@ +"""LlamaIndex Go Micro Integration. + +This package provides LlamaIndex integration for Go Micro services through +the Model Context Protocol (MCP). +""" + +from go_micro_llamaindex.toolkit import GoMicroToolkit, GoMicroConfig +from go_micro_llamaindex.exceptions import GoMicroError, GoMicroConnectionError, GoMicroAuthError + +__version__ = "0.1.0" +__all__ = [ + "GoMicroToolkit", + "GoMicroConfig", + "GoMicroError", + "GoMicroConnectionError", + "GoMicroAuthError", +] diff --git a/contrib/go-micro-llamaindex/go_micro_llamaindex/exceptions.py b/contrib/go-micro-llamaindex/go_micro_llamaindex/exceptions.py new file mode 100644 index 00000000..9d13323d --- /dev/null +++ b/contrib/go-micro-llamaindex/go_micro_llamaindex/exceptions.py @@ -0,0 +1,21 @@ +"""Custom exceptions for LlamaIndex Go Micro integration.""" + + +class GoMicroError(Exception): + """Base exception for Go Micro integration errors.""" + pass + + +class GoMicroConnectionError(GoMicroError): + """Raised when unable to connect to MCP gateway.""" + pass + + +class GoMicroAuthError(GoMicroError): + """Raised when authentication fails.""" + pass + + +class GoMicroToolError(GoMicroError): + """Raised when tool execution fails.""" + pass diff --git a/contrib/go-micro-llamaindex/go_micro_llamaindex/toolkit.py b/contrib/go-micro-llamaindex/go_micro_llamaindex/toolkit.py new file mode 100644 index 00000000..230adccd --- /dev/null +++ b/contrib/go-micro-llamaindex/go_micro_llamaindex/toolkit.py @@ -0,0 +1,311 @@ +"""LlamaIndex toolkit for Go Micro services.""" + +import json +import re +from typing import Any, Dict, List, Optional +from dataclasses import dataclass + +import requests +from llama_index.core.tools import FunctionTool, ToolMetadata +from pydantic import BaseModel, Field + +from go_micro_llamaindex.exceptions import ( + GoMicroConnectionError, + GoMicroAuthError, + GoMicroToolError, +) + + +@dataclass +class GoMicroConfig: + """Configuration for Go Micro MCP gateway connection. + + Attributes: + gateway_url: URL of the MCP gateway (e.g., http://localhost:3000) + auth_token: Optional bearer authentication token + timeout: Request timeout in seconds + retry_count: Number of retries on failure + retry_delay: Delay between retries in seconds + verify_ssl: Whether to verify SSL certificates + """ + + gateway_url: str + auth_token: Optional[str] = None + timeout: int = 30 + retry_count: int = 3 + retry_delay: float = 1.0 + verify_ssl: bool = True + + +class GoMicroTool(BaseModel): + """Represents a Go Micro service tool. + + Attributes: + name: Tool name (e.g., "users.Users.Get") + service: Service name (e.g., "users") + endpoint: Endpoint name (e.g., "Users.Get") + description: Tool description + example: Example input JSON + scopes: Required auth scopes + metadata: Additional metadata from service + """ + + name: str + service: str + endpoint: str + description: str + example: Optional[str] = None + scopes: Optional[List[str]] = None + metadata: Dict[str, str] = Field(default_factory=dict) + + +class GoMicroToolkit: + """LlamaIndex toolkit for Go Micro services. + + This class provides integration between LlamaIndex and Go Micro services + via the Model Context Protocol (MCP) gateway. + + Example: + >>> toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + >>> tools = toolkit.get_tools() + >>> for tool in tools: + ... print(f"Tool: {tool.metadata.name}") + """ + + def __init__(self, config: GoMicroConfig): + """Initialize the toolkit. + + Args: + config: Configuration for MCP gateway connection + """ + self.config = config + self._tools: Optional[List[GoMicroTool]] = None + self._session = requests.Session() + + if config.auth_token: + self._session.headers.update({ + "Authorization": f"Bearer {config.auth_token}" + }) + + @classmethod + def from_gateway( + cls, + gateway_url: str, + auth_token: Optional[str] = None, + **kwargs: Any + ) -> "GoMicroToolkit": + """Create toolkit from MCP gateway URL. + + Args: + gateway_url: URL of the MCP gateway + auth_token: Optional bearer authentication token + **kwargs: Additional configuration options + + Returns: + GoMicroToolkit instance + + Example: + >>> toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + """ + config = GoMicroConfig( + gateway_url=gateway_url, + auth_token=auth_token, + **kwargs + ) + return cls(config) + + def _make_request( + self, + method: str, + path: str, + **kwargs: Any + ) -> requests.Response: + """Make HTTP request to MCP gateway. + + Args: + method: HTTP method (GET, POST, etc.) + path: API path + **kwargs: Additional request arguments + + Returns: + Response object + + Raises: + GoMicroConnectionError: If connection fails + GoMicroAuthError: If authentication fails + """ + url = f"{self.config.gateway_url}{path}" + kwargs.setdefault("timeout", self.config.timeout) + kwargs.setdefault("verify", self.config.verify_ssl) + + try: + response = self._session.request(method, url, **kwargs) + + if response.status_code == 401: + raise GoMicroAuthError("Authentication failed") + elif response.status_code == 403: + raise GoMicroAuthError("Forbidden: insufficient permissions") + + response.raise_for_status() + return response + + except requests.ConnectionError as e: + raise GoMicroConnectionError( + f"Failed to connect to MCP gateway at {url}: {e}" + ) + except requests.Timeout as e: + raise GoMicroConnectionError( + f"Request to MCP gateway timed out: {e}" + ) + except requests.RequestException as e: + if isinstance(e, (GoMicroConnectionError, GoMicroAuthError)): + raise + raise GoMicroConnectionError(f"Request failed: {e}") + + def refresh(self) -> None: + """Refresh tool list from MCP gateway. + + Raises: + GoMicroConnectionError: If unable to connect to gateway + """ + response = self._make_request("GET", "/mcp/tools") + data = response.json() + + tools_data = data.get("tools", []) + self._tools = [ + GoMicroTool( + name=tool["name"], + service=tool["service"], + endpoint=tool["endpoint"], + description=tool.get("description", ""), + example=tool.get("example"), + scopes=tool.get("scopes"), + metadata=tool.get("metadata", {}) + ) + for tool in tools_data + ] + + def get_tools( + self, + service_filter: Optional[str] = None, + name_pattern: Optional[str] = None, + include: Optional[List[str]] = None, + exclude: Optional[List[str]] = None, + ) -> List[FunctionTool]: + """Get LlamaIndex tools from Go Micro services. + + Args: + service_filter: Filter tools by service name + name_pattern: Filter tools by name pattern (regex) + include: List of tool names to include + exclude: List of tool names to exclude + + Returns: + List of LlamaIndex FunctionTool objects + + Example: + >>> toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + >>> all_tools = toolkit.get_tools() + >>> user_tools = toolkit.get_tools(service_filter="users") + """ + if self._tools is None: + self.refresh() + + tools = self._tools or [] + + if service_filter: + tools = [t for t in tools if t.service == service_filter] + + if name_pattern: + pattern = re.compile(name_pattern) + tools = [t for t in tools if pattern.match(t.name)] + + if include: + tools = [t for t in tools if t.name in include] + + if exclude: + tools = [t for t in tools if t.name not in exclude] + + return [self._create_llamaindex_tool(tool) for tool in tools] + + def _create_llamaindex_tool(self, tool: GoMicroTool) -> FunctionTool: + """Create a LlamaIndex FunctionTool from a GoMicroTool. + + Args: + tool: GoMicroTool to convert + + Returns: + LlamaIndex FunctionTool object + """ + toolkit = self + + def tool_func(arguments: str) -> str: + """Execute the tool. + + Args: + arguments: JSON string with tool arguments + + Returns: + JSON string with tool result + """ + return toolkit.call_tool(tool.name, arguments) + + description = tool.description + if tool.example: + description += f"\n\nExample input: {tool.example}" + + return FunctionTool.from_defaults( + fn=tool_func, + name=tool.name, + description=description, + ) + + def call_tool(self, tool_name: str, arguments: str) -> str: + """Call a specific tool directly. + + Args: + tool_name: Name of the tool to call + arguments: JSON string with tool arguments + + Returns: + JSON string with tool result + + Raises: + GoMicroToolError: If tool execution fails + + Example: + >>> toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + >>> result = toolkit.call_tool( + ... "users.Users.Get", + ... '{"id": "user-123"}' + ... ) + """ + try: + args = json.loads(arguments) if isinstance(arguments, str) else arguments + except json.JSONDecodeError as e: + raise GoMicroToolError(f"Invalid JSON arguments: {e}") + + try: + response = self._make_request( + "POST", + "/mcp/call", + json={"name": tool_name, "arguments": args} + ) + return json.dumps(response.json()) + except requests.RequestException as e: + raise GoMicroToolError(f"Tool execution failed: {e}") + + def list_tools(self) -> List[GoMicroTool]: + """Get raw list of available tools. + + Returns: + List of GoMicroTool objects + + Example: + >>> toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + >>> for tool in toolkit.list_tools(): + ... print(f"{tool.name}: {tool.description}") + """ + if self._tools is None: + self.refresh() + return self._tools or [] diff --git a/contrib/go-micro-llamaindex/pyproject.toml b/contrib/go-micro-llamaindex/pyproject.toml new file mode 100644 index 00000000..13932c9a --- /dev/null +++ b/contrib/go-micro-llamaindex/pyproject.toml @@ -0,0 +1,72 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "go-micro-llamaindex" +version = "0.1.0" +description = "LlamaIndex integration for Go Micro services via MCP" +readme = "README.md" +requires-python = ">=3.9" +license = {text = "Apache-2.0"} +authors = [ + {name = "Micro Team", email = "hello@micro.dev"} +] +keywords = ["llamaindex", "go-micro", "mcp", "microservices", "ai", "rag"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Software Development :: Libraries :: Python Modules", +] + +dependencies = [ + "llama-index-core>=0.10.0", + "requests>=2.31.0", + "pydantic>=2.0.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.0.0", + "pytest-cov>=4.0.0", + "black>=23.0.0", + "mypy>=1.0.0", + "ruff>=0.1.0", + "types-requests>=2.31.0", +] + +[project.urls] +Homepage = "https://github.com/micro/go-micro" +Documentation = "https://github.com/micro/go-micro/tree/master/contrib/go-micro-llamaindex" +Repository = "https://github.com/micro/go-micro" +Issues = "https://github.com/micro/go-micro/issues" + +[tool.setuptools.packages.find] +where = ["."] +include = ["go_micro_llamaindex*"] + +[tool.black] +line-length = 88 +target-version = ['py39', 'py310', 'py311'] + +[tool.mypy] +python_version = "3.9" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = true + +[tool.ruff] +line-length = 88 +target-version = "py39" + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] diff --git a/contrib/go-micro-llamaindex/tests/__init__.py b/contrib/go-micro-llamaindex/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/contrib/go-micro-llamaindex/tests/test_toolkit.py b/contrib/go-micro-llamaindex/tests/test_toolkit.py new file mode 100644 index 00000000..6d2b3435 --- /dev/null +++ b/contrib/go-micro-llamaindex/tests/test_toolkit.py @@ -0,0 +1,261 @@ +"""Tests for GoMicroToolkit.""" + +import json +from unittest.mock import Mock, patch + +import pytest +import requests + +from go_micro_llamaindex import GoMicroToolkit, GoMicroConfig +from go_micro_llamaindex.exceptions import ( + GoMicroConnectionError, + GoMicroAuthError, +) + + +@pytest.fixture +def mock_gateway_response(): + """Mock MCP gateway response.""" + return { + "tools": [ + { + "name": "users.Users.Get", + "service": "users", + "endpoint": "Users.Get", + "description": "Get a user by ID", + "example": '{"id": "user-123"}', + "scopes": ["users:read"], + "metadata": { + "description": "Get a user by ID", + "example": '{"id": "user-123"}', + "scopes": "users:read" + } + }, + { + "name": "users.Users.Create", + "service": "users", + "endpoint": "Users.Create", + "description": "Create a new user", + "example": '{"name": "Alice", "email": "alice@example.com"}', + "scopes": ["users:write"], + "metadata": {} + }, + { + "name": "blog.Blog.List", + "service": "blog", + "endpoint": "Blog.List", + "description": "List blog posts", + "scopes": ["blog:read"], + "metadata": {} + } + ], + "count": 3 + } + + +class TestGoMicroConfig: + """Tests for GoMicroConfig.""" + + def test_config_defaults(self): + """Test config default values.""" + config = GoMicroConfig(gateway_url="http://localhost:3000") + + assert config.gateway_url == "http://localhost:3000" + assert config.auth_token is None + assert config.timeout == 30 + assert config.retry_count == 3 + assert config.retry_delay == 1.0 + assert config.verify_ssl is True + + def test_config_custom_values(self): + """Test config with custom values.""" + config = GoMicroConfig( + gateway_url="http://localhost:8080", + auth_token="test-token", + timeout=60, + retry_count=5, + retry_delay=2.0, + verify_ssl=False + ) + + assert config.gateway_url == "http://localhost:8080" + assert config.auth_token == "test-token" + assert config.timeout == 60 + assert config.retry_count == 5 + assert config.retry_delay == 2.0 + assert config.verify_ssl is False + + +class TestGoMicroToolkit: + """Tests for GoMicroToolkit.""" + + def test_from_gateway(self): + """Test creating toolkit from gateway URL.""" + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + + assert toolkit.config.gateway_url == "http://localhost:3000" + assert toolkit.config.auth_token is None + + def test_from_gateway_with_auth(self): + """Test creating toolkit with authentication.""" + toolkit = GoMicroToolkit.from_gateway( + "http://localhost:3000", + auth_token="test-token" + ) + + assert toolkit.config.auth_token == "test-token" + assert "Authorization" in toolkit._session.headers + assert toolkit._session.headers["Authorization"] == "Bearer test-token" + + @patch("requests.Session.request") + def test_refresh(self, mock_request, mock_gateway_response): + """Test refreshing tool list.""" + mock_response = Mock() + mock_response.json.return_value = mock_gateway_response + mock_response.status_code = 200 + mock_request.return_value = mock_response + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + toolkit.refresh() + + assert len(toolkit._tools) == 3 + assert toolkit._tools[0].name == "users.Users.Get" + assert toolkit._tools[1].name == "users.Users.Create" + assert toolkit._tools[2].name == "blog.Blog.List" + + @patch("requests.Session.request") + def test_get_tools(self, mock_request, mock_gateway_response): + """Test getting LlamaIndex tools.""" + mock_response = Mock() + mock_response.json.return_value = mock_gateway_response + mock_response.status_code = 200 + mock_request.return_value = mock_response + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + tools = toolkit.get_tools() + + assert len(tools) == 3 + names = [t.metadata.name for t in tools] + assert "users.Users.Get" in names + assert "users.Users.Create" in names + assert "blog.Blog.List" in names + + @patch("requests.Session.request") + def test_get_tools_with_service_filter(self, mock_request, mock_gateway_response): + """Test filtering tools by service.""" + mock_response = Mock() + mock_response.json.return_value = mock_gateway_response + mock_response.status_code = 200 + mock_request.return_value = mock_response + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + tools = toolkit.get_tools(service_filter="users") + + assert len(tools) == 2 + for tool in tools: + assert "users" in tool.metadata.name + + @patch("requests.Session.request") + def test_get_tools_with_include(self, mock_request, mock_gateway_response): + """Test including specific tools.""" + mock_response = Mock() + mock_response.json.return_value = mock_gateway_response + mock_response.status_code = 200 + mock_request.return_value = mock_response + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + tools = toolkit.get_tools(include=["users.Users.Get"]) + + assert len(tools) == 1 + assert tools[0].metadata.name == "users.Users.Get" + + @patch("requests.Session.request") + def test_get_tools_with_exclude(self, mock_request, mock_gateway_response): + """Test excluding specific tools.""" + mock_response = Mock() + mock_response.json.return_value = mock_gateway_response + mock_response.status_code = 200 + mock_request.return_value = mock_response + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + tools = toolkit.get_tools(exclude=["users.Users.Create"]) + + assert len(tools) == 2 + names = [t.metadata.name for t in tools] + assert "users.Users.Create" not in names + + @patch("requests.Session.request") + def test_get_tools_with_name_pattern(self, mock_request, mock_gateway_response): + """Test filtering tools by name pattern.""" + mock_response = Mock() + mock_response.json.return_value = mock_gateway_response + mock_response.status_code = 200 + mock_request.return_value = mock_response + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + tools = toolkit.get_tools(name_pattern="blog\\..*") + + assert len(tools) == 1 + assert tools[0].metadata.name == "blog.Blog.List" + + @patch("requests.Session.request") + def test_call_tool(self, mock_request): + """Test calling a tool directly.""" + mock_response = Mock() + mock_response.json.return_value = {"user": {"id": "user-123", "name": "Alice"}} + mock_response.status_code = 200 + mock_request.return_value = mock_response + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + result = toolkit.call_tool("users.Users.Get", '{"id": "user-123"}') + + result_data = json.loads(result) + assert result_data["user"]["id"] == "user-123" + + @patch("requests.Session.request") + def test_list_tools(self, mock_request, mock_gateway_response): + """Test listing raw tools.""" + mock_response = Mock() + mock_response.json.return_value = mock_gateway_response + mock_response.status_code = 200 + mock_request.return_value = mock_response + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + tools = toolkit.list_tools() + + assert len(tools) == 3 + assert tools[0].name == "users.Users.Get" + assert tools[0].service == "users" + assert tools[0].scopes == ["users:read"] + + @patch("requests.Session.request") + def test_connection_error(self, mock_request): + """Test handling connection errors.""" + mock_request.side_effect = requests.ConnectionError("Connection failed") + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + + with pytest.raises(GoMicroConnectionError): + toolkit.refresh() + + @patch("requests.Session.request") + def test_auth_error(self, mock_request): + """Test handling authentication errors.""" + mock_response = Mock() + mock_response.status_code = 401 + mock_request.return_value = mock_response + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + + with pytest.raises(GoMicroAuthError): + toolkit.refresh() + + @patch("requests.Session.request") + def test_timeout(self, mock_request): + """Test handling timeouts.""" + mock_request.side_effect = requests.Timeout("Request timed out") + + toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + + with pytest.raises(GoMicroConnectionError): + toolkit.refresh() diff --git a/gateway/mcp/example_test.go b/gateway/mcp/example_test.go index f171e459..cf702193 100644 --- a/gateway/mcp/example_test.go +++ b/gateway/mcp/example_test.go @@ -11,7 +11,19 @@ import ( "go-micro.dev/v5/registry" ) +// Example_withMCP shows the simplest way to add MCP to a service using WithMCP +func Example_withMCP() { + // One line to make your service AI-accessible + service := micro.NewService( + micro.Name("myservice"), + WithMCP(":3000"), + ) + service.Init() + service.Run() +} + // Example_inlineGateway shows how to add MCP gateway to an existing service +// with full control over options func Example_inlineGateway() { service := micro.NewService(micro.Name("myservice")) service.Init() diff --git a/gateway/mcp/option.go b/gateway/mcp/option.go new file mode 100644 index 00000000..357419f5 --- /dev/null +++ b/gateway/mcp/option.go @@ -0,0 +1,28 @@ +package mcp + +import ( + "go-micro.dev/v5/service" +) + +// WithMCP returns a service option that starts an MCP gateway alongside the +// service, making all registered handlers discoverable as AI agent tools. +// The address parameter specifies where the MCP gateway listens (e.g., ":3000"). +// +// Usage: +// +// import "go-micro.dev/v5/gateway/mcp" +// +// service := micro.NewService( +// micro.Name("users"), +// mcp.WithMCP(":3000"), +// ) +func WithMCP(address string) service.Option { + return func(o *service.Options) { + o.AfterStart = append(o.AfterStart, func() error { + go ListenAndServe(address, Options{ + Registry: o.Registry, + }) + return nil + }) + } +} diff --git a/internal/website/blog/2.md b/internal/website/blog/2.md index 8b98b547..efc631f3 100644 --- a/internal/website/blog/2.md +++ b/internal/website/blog/2.md @@ -485,4 +485,5 @@ See the [MCP Gateway documentation](/docs/mcp) for full details.
diff --git a/internal/website/blog/3.md b/internal/website/blog/3.md new file mode 100644 index 00000000..84005c51 --- /dev/null +++ b/internal/website/blog/3.md @@ -0,0 +1,250 @@ +--- +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 — WebSocket transport, OpenTelemetry, agent SDKs, and what's next" +--- + +# Building the AI-Native Future of Go Micro with Claude + +*March 4, 2026 • By the Go Micro Team* + +Go Micro was recently given access to **Claude Max** through Anthropic's open source sponsorship program. We wanted to share what we've built with it, why it matters, and where we're headed. + +## The Sponsorship + +Anthropic offers Claude Max access to open source projects. Go Micro applied because our MCP integration — making every microservice an AI tool — aligns directly with Anthropic's Model Context Protocol. They agreed, and we got to work. + +The result: **three major features shipped in a single sprint**, taking our Q2 2026 roadmap from 85% to 95% complete. + +## What We Built + +### 1. WebSocket Transport for Real-Time Agents + +The MCP gateway previously supported HTTP/SSE and stdio transports. These work well for request/response patterns, but real-time AI agents need persistent, bidirectional connections. + +We added a full **WebSocket transport** implementing JSON-RPC 2.0: + +```go +// Connect via WebSocket for bidirectional streaming +ws://localhost:3000/mcp/ws +``` + +What this enables: +- **Persistent connections** — No HTTP overhead per tool call +- **Bidirectional streaming** — Server can push updates to agents +- **Connection-level auth** — Authenticate once on connect, not per request +- **Concurrent requests** — Multiple tool calls over a single connection + +The WebSocket transport supports the same JSON-RPC 2.0 protocol as stdio (`initialize`, `tools/list`, `tools/call`), so any MCP client that speaks WebSocket can connect. + +```javascript +// Agent connects and discovers tools +const ws = new WebSocket("ws://localhost:3000/mcp/ws", { + headers: { "Authorization": "Bearer my-token" } +}); + +// Initialize +ws.send(JSON.stringify({ + jsonrpc: "2.0", id: 1, + method: "initialize", + params: { protocolVersion: "2024-11-05" } +})); + +// List tools +ws.send(JSON.stringify({ + jsonrpc: "2.0", id: 2, + method: "tools/list" +})); + +// Call a tool +ws.send(JSON.stringify({ + jsonrpc: "2.0", id: 3, + method: "tools/call", + params: { + name: "users.Users.Get", + arguments: { "id": "user-123" } + } +})); +``` + +This is particularly useful for the agent playground in `micro run`, where the browser maintains a persistent WebSocket connection for interactive AI conversations. + +### 2. OpenTelemetry Integration + +Production deployments need observability. We added **full OpenTelemetry span instrumentation** across all three MCP transports (HTTP, stdio, WebSocket). + +```go +import "go.opentelemetry.io/otel/sdk/trace" + +// Add tracing to your MCP gateway +mcp.Serve(mcp.Options{ + Registry: service.Options().Registry, + Address: ":3000", + TraceProvider: traceProvider, // Your OTel trace provider +}) +``` + +Every tool call now creates a span with rich attributes: + +``` +Span: mcp.tool.call + mcp.tool.name: users.Users.Get + mcp.transport: websocket + mcp.account.id: agent-001 + mcp.auth.status: allowed + mcp.rate_limit.allowed: true +``` + +This connects to your existing observability stack — Jaeger, Grafana, Datadog, whatever you use. You can now trace an AI agent's tool calls through your entire service mesh. + +The integration is backward compatible: if you don't set a `TraceProvider`, spans are no-ops with zero overhead. + +### 3. LlamaIndex SDK + +With the [LangChain SDK](https://github.com/micro/go-micro/tree/master/contrib/langchain-go-micro) already shipped, we built the **LlamaIndex integration** — enabling RAG (Retrieval-Augmented Generation) workflows with Go Micro services. + +```python +from go_micro_llamaindex import GoMicroToolkit +from llama_index.core.agent import ReActAgent +from llama_index.llms.openai import OpenAI + +# Connect to your services +toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") + +# Create a ReAct agent with your service tools +agent = ReActAgent.from_tools( + toolkit.get_tools(), + llm=OpenAI(model="gpt-4"), + verbose=True +) + +# The agent can now call your microservices +response = agent.chat("Get the profile for user-123") +``` + +The LlamaIndex SDK supports the same filtering as LangChain: + +```python +# Filter by service +user_tools = toolkit.get_tools(service_filter="users") + +# Filter by pattern +blog_tools = toolkit.get_tools(name_pattern="blog.*") + +# Combine with RAG +from llama_index.core import VectorStoreIndex +from llama_index.core.tools import QueryEngineTool + +index = VectorStoreIndex.from_documents(documents) +rag_tool = QueryEngineTool(query_engine=index.as_query_engine(), ...) + +# Agent has both document search AND service access +all_tools = [rag_tool] + toolkit.get_tools() +agent = ReActAgent.from_tools(all_tools, llm=llm) +``` + +This is powerful: an agent can search your documentation AND call your services in the same conversation. + +## By The Numbers + +Here's where Go Micro's MCP integration stands today: + +| Metric | Value | +|--------|-------| +| **MCP Gateway Code** | 2,500+ lines | +| **Test Coverage** | 1,000+ lines, 35+ tests | +| **Transports** | 3 (HTTP/SSE, Stdio, WebSocket) | +| **Agent SDKs** | 2 (LangChain, LlamaIndex) | +| **Model Providers** | 2 (Anthropic Claude, OpenAI GPT) | +| **Security** | Auth, scopes, rate limiting, audit, OTel | + +The Q1 2026 foundation is complete, Q2 is at 95%, and we've already delivered 50% of Q3's production features ahead of schedule. + +## What This Means for You + +If you're building microservices with Go Micro, your services are already AI-ready. Here's what you can do today: + +### Add MCP to an existing service (3 lines) + +```go +go mcp.Serve(mcp.Options{ + Registry: service.Options().Registry, + Address: ":3000", +}) +``` + +### Use it with Claude Code + +```json +{ + "mcpServers": { + "my-services": { + "command": "micro", + "args": ["mcp", "serve"] + } + } +} +``` + +### Connect LangChain or LlamaIndex agents + +```python +toolkit = GoMicroToolkit.from_gateway("http://localhost:3000") +tools = toolkit.get_tools() +``` + +### Monitor with OpenTelemetry + +```go +mcp.Serve(mcp.Options{ + Registry: registry, + TraceProvider: otelProvider, + AuditFunc: func(r mcp.AuditRecord) { /* log it */ }, +}) +``` + +## Working with Claude + +A note on the development process itself: we used Claude (via Claude Code) to implement these features. It wrote production Go code, ran the tests, fixed compilation errors, and iterated on the implementation. The WebSocket transport went from zero to 14 passing tests in a single session. The OpenTelemetry integration was designed, implemented, and tested in another. + +This is exactly the kind of workflow that MCP enables. An AI agent that understands your codebase, calls your tools, and ships features. Go Micro is both the framework for building this and a beneficiary of it. + +## What's Next + +With Q2 nearly wrapped, we're focused on: + +1. **Agent Playground polish** — The `/agent` chat UI in `micro run` needs refinement for demos and daily development +2. **Standalone gateway binary** — `micro-mcp-gateway` as a production-grade, independently deployable binary +3. **More examples** — Real-world services that demonstrate the full AI-native workflow + +The MCP ecosystem is growing fast. We think every microservices framework will have MCP support eventually — Go Micro just got there first. + +## Try It + +```bash +# Install or update +go install go-micro.dev/v5/cmd/micro@latest + +# Create a service +micro new myservice +cd myservice + +# Run with MCP and the agent playground +micro run --mcp-address :3000 + +# Open http://localhost:8080/agent and chat with your service +``` + +See the [MCP documentation](/docs/mcp) and [AI-native services guide](/docs/guides/ai-native-services) for the full walkthrough. + +--- + +*Go Micro is an open source framework for distributed systems development. [Star us on GitHub](https://github.com/micro/go-micro) — we're at 21K stars and growing.* + +*Thanks to Anthropic for the Claude Max sponsorship through their open source program.* + + diff --git a/internal/website/blog/index.html b/internal/website/blog/index.html index 68d251d3..82ce42e2 100644 --- a/internal/website/blog/index.html +++ b/internal/website/blog/index.html @@ -10,6 +10,13 @@ permalink: /blog/How Anthropic's Claude Max sponsorship accelerated Go Micro's MCP integration — WebSocket transport, OpenTelemetry tracing, LlamaIndex SDK, and what's next.
+ Read more → +