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
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:
@@ -0,0 +1,453 @@
|
||||
# MCP CLI Command Examples
|
||||
|
||||
This document provides examples of using the `micro mcp` commands for AI agent integration.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [List Available Tools](#list-available-tools)
|
||||
- [Test a Tool](#test-a-tool)
|
||||
- [Generate Documentation](#generate-documentation)
|
||||
- [Export to Different Formats](#export-to-different-formats)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You need at least one microservice running with the go-micro framework. The service will automatically be discovered via the registry (mdns by default).
|
||||
|
||||
Example service:
|
||||
```bash
|
||||
cd examples/mcp/hello
|
||||
go run main.go
|
||||
```
|
||||
|
||||
## List Available Tools
|
||||
|
||||
### Human-readable list
|
||||
```bash
|
||||
micro mcp list
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Available MCP Tools:
|
||||
|
||||
Service: greeter
|
||||
• greeter.Greeter.SayHello
|
||||
|
||||
Total: 1 tools
|
||||
```
|
||||
|
||||
### JSON output
|
||||
```bash
|
||||
micro mcp list --json
|
||||
```
|
||||
|
||||
Output:
|
||||
```json
|
||||
{
|
||||
"count": 1,
|
||||
"tools": [
|
||||
{
|
||||
"description": "Call SayHello on greeter service",
|
||||
"endpoint": "Greeter.SayHello",
|
||||
"name": "greeter.Greeter.SayHello",
|
||||
"service": "greeter"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Test a Tool
|
||||
|
||||
### Basic test
|
||||
```bash
|
||||
micro mcp test greeter.Greeter.SayHello '{"name": "Alice"}'
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Testing tool: greeter.Greeter.SayHello
|
||||
Service: greeter
|
||||
Endpoint: Greeter.SayHello
|
||||
Input: {"name": "Alice"}
|
||||
|
||||
✅ Call successful!
|
||||
|
||||
Response:
|
||||
{
|
||||
"message": "Hello Alice!"
|
||||
}
|
||||
```
|
||||
|
||||
### Test with default empty input
|
||||
```bash
|
||||
micro mcp test greeter.Greeter.SayHello
|
||||
```
|
||||
|
||||
This will call the tool with an empty JSON object `{}`.
|
||||
|
||||
## Generate Documentation
|
||||
|
||||
### Markdown documentation (stdout)
|
||||
```bash
|
||||
micro mcp docs
|
||||
```
|
||||
|
||||
Output:
|
||||
```markdown
|
||||
# MCP Tools Documentation
|
||||
|
||||
Generated: 2026-02-13 14:30:00
|
||||
|
||||
Total Tools: 1
|
||||
|
||||
## Service: greeter
|
||||
|
||||
### greeter.Greeter.SayHello
|
||||
|
||||
**Description:** Greets a person by name. Returns a friendly greeting message.
|
||||
|
||||
**Example Input:**
|
||||
\`\`\`json
|
||||
{"name": "Alice"}
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
### Markdown documentation (save to file)
|
||||
```bash
|
||||
micro mcp docs --output mcp-tools.md
|
||||
```
|
||||
|
||||
This creates a `mcp-tools.md` file with the documentation.
|
||||
|
||||
### JSON documentation
|
||||
```bash
|
||||
micro mcp docs --format json
|
||||
```
|
||||
|
||||
Output:
|
||||
```json
|
||||
{
|
||||
"count": 1,
|
||||
"tools": [
|
||||
{
|
||||
"description": "Greets a person by name. Returns a friendly greeting message.",
|
||||
"endpoint": "Greeter.SayHello",
|
||||
"example": "{\"name\": \"Alice\"}",
|
||||
"metadata": {
|
||||
"description": "Greets a person by name. Returns a friendly greeting message.",
|
||||
"example": "{\"name\": \"Alice\"}"
|
||||
},
|
||||
"name": "greeter.Greeter.SayHello",
|
||||
"scopes": null,
|
||||
"service": "greeter"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### JSON documentation (save to file)
|
||||
```bash
|
||||
micro mcp docs --format json --output tools.json
|
||||
```
|
||||
|
||||
## Export to Different Formats
|
||||
|
||||
### Export to LangChain (Python)
|
||||
|
||||
Generate Python code with LangChain tool definitions:
|
||||
|
||||
```bash
|
||||
micro mcp export langchain
|
||||
```
|
||||
|
||||
Output:
|
||||
```python
|
||||
# LangChain Tools for Go Micro Services
|
||||
# Auto-generated from MCP service discovery
|
||||
|
||||
from langchain.tools import Tool
|
||||
import requests
|
||||
import json
|
||||
|
||||
# Configure your MCP gateway endpoint
|
||||
MCP_GATEWAY_URL = 'http://localhost:3000/mcp'
|
||||
|
||||
def call_mcp_tool(tool_name, arguments):
|
||||
"""Call an MCP tool via HTTP gateway"""
|
||||
response = requests.post(
|
||||
f'{MCP_GATEWAY_URL}/call',
|
||||
json={'name': tool_name, 'arguments': arguments}
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
# Define tools
|
||||
tools = []
|
||||
|
||||
def greeter_Greeter_SayHello(arguments: str) -> str:
|
||||
"""Greets a person by name. Returns a friendly greeting message."""
|
||||
args = json.loads(arguments) if isinstance(arguments, str) else arguments
|
||||
return json.dumps(call_mcp_tool('greeter.Greeter.SayHello', args))
|
||||
|
||||
tools.append(Tool(
|
||||
name='greeter.Greeter.SayHello',
|
||||
func=greeter_Greeter_SayHello,
|
||||
description='Greets a person by name. Returns a friendly greeting message.'
|
||||
))
|
||||
|
||||
# Example usage:
|
||||
# from langchain.agents import initialize_agent, AgentType
|
||||
# from langchain.llms import OpenAI
|
||||
#
|
||||
# llm = OpenAI(temperature=0)
|
||||
# agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)
|
||||
# agent.run('Your query here')
|
||||
```
|
||||
|
||||
Save to file:
|
||||
```bash
|
||||
micro mcp export langchain --output langchain_tools.py
|
||||
```
|
||||
|
||||
### Export to OpenAPI 3.0
|
||||
|
||||
Generate an OpenAPI specification:
|
||||
|
||||
```bash
|
||||
micro mcp export openapi
|
||||
```
|
||||
|
||||
Output:
|
||||
```json
|
||||
{
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"bearerAuth": {
|
||||
"scheme": "bearer",
|
||||
"type": "http"
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"description": "Auto-generated OpenAPI spec from MCP service discovery",
|
||||
"title": "Go Micro MCP Services",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"openapi": "3.0.0",
|
||||
"paths": {
|
||||
"/mcp/call/greeter/Greeter/SayHello": {
|
||||
"post": {
|
||||
"description": "Greets a person by name. Returns a friendly greeting message.",
|
||||
"operationId": "greeter_Greeter_SayHello",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Successful response"
|
||||
}
|
||||
},
|
||||
"summary": "greeter.Greeter.SayHello"
|
||||
}
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"description": "MCP Gateway",
|
||||
"url": "http://localhost:3000"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Save to file:
|
||||
```bash
|
||||
micro mcp export openapi --output openapi.json
|
||||
```
|
||||
|
||||
### Export to raw JSON
|
||||
|
||||
Export raw tool definitions:
|
||||
|
||||
```bash
|
||||
micro mcp export json
|
||||
```
|
||||
|
||||
This is similar to `micro mcp docs --format json` but specifically for export purposes.
|
||||
|
||||
Save to file:
|
||||
```bash
|
||||
micro mcp export json --output tools.json
|
||||
```
|
||||
|
||||
## Using with Different Registries
|
||||
|
||||
By default, the commands use mdns registry. You can specify a different registry:
|
||||
|
||||
```bash
|
||||
# Using consul
|
||||
micro mcp list --registry consul --registry_address consul:8500
|
||||
|
||||
# Using etcd
|
||||
micro mcp list --registry etcd --registry_address etcd:2379
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### Using LangChain Export with Claude
|
||||
|
||||
1. Export your tools to LangChain format:
|
||||
```bash
|
||||
micro mcp export langchain --output my_tools.py
|
||||
```
|
||||
|
||||
2. Use in your Python agent:
|
||||
```python
|
||||
from my_tools import tools
|
||||
from langchain.agents import initialize_agent, AgentType
|
||||
from langchain.chat_models import ChatAnthropic
|
||||
|
||||
llm = ChatAnthropic(model="claude-3-sonnet-20240229")
|
||||
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)
|
||||
|
||||
result = agent.run("Greet Alice")
|
||||
print(result)
|
||||
```
|
||||
|
||||
### Using OpenAPI Export with GPT
|
||||
|
||||
1. Export to OpenAPI:
|
||||
```bash
|
||||
micro mcp export openapi --output openapi.json
|
||||
```
|
||||
|
||||
2. Upload to ChatGPT as a custom GPT action or use with OpenAI Assistants API.
|
||||
|
||||
### Documentation for AI Agents
|
||||
|
||||
Generate documentation that AI agents can read to understand your services:
|
||||
|
||||
```bash
|
||||
micro mcp docs --format json --output service-catalog.json
|
||||
```
|
||||
|
||||
This JSON file can be fed to AI agents for service discovery and understanding.
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Piping and Processing
|
||||
|
||||
You can pipe the output to other tools:
|
||||
|
||||
```bash
|
||||
# Count tools per service
|
||||
micro mcp list --json | jq '.tools | group_by(.service) | map({service: .[0].service, count: length})'
|
||||
|
||||
# Extract all tool names
|
||||
micro mcp list --json | jq -r '.tools[].name'
|
||||
|
||||
# Filter tools by service
|
||||
micro mcp list --json | jq '.tools[] | select(.service == "greeter")'
|
||||
```
|
||||
|
||||
### Monitoring and CI/CD
|
||||
|
||||
Use these commands in your CI/CD pipeline:
|
||||
|
||||
```bash
|
||||
# Validate all services are discoverable
|
||||
SERVICE_COUNT=$(micro mcp list --json | jq '.count')
|
||||
if [ "$SERVICE_COUNT" -lt 5 ]; then
|
||||
echo "Error: Expected at least 5 services, found $SERVICE_COUNT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate documentation on each deployment
|
||||
micro mcp docs --output docs/mcp-services.md
|
||||
git add docs/mcp-services.md
|
||||
git commit -m "Update MCP service documentation"
|
||||
```
|
||||
|
||||
### Testing in Development
|
||||
|
||||
Create a script to test all your tools:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# test-all-tools.sh
|
||||
|
||||
TOOLS=$(micro mcp list --json | jq -r '.tools[].name')
|
||||
|
||||
for tool in $TOOLS; do
|
||||
echo "Testing $tool..."
|
||||
micro mcp test "$tool" "{}" || echo "Failed: $tool"
|
||||
done
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No tools found
|
||||
|
||||
If `micro mcp list` shows 0 tools:
|
||||
|
||||
1. Verify services are running:
|
||||
```bash
|
||||
ps aux | grep "your-service"
|
||||
```
|
||||
|
||||
2. Check registry (mdns might need time to discover):
|
||||
```bash
|
||||
# Wait a few seconds and try again
|
||||
sleep 3
|
||||
micro mcp list
|
||||
```
|
||||
|
||||
3. Use a different registry if mdns is unreliable:
|
||||
```bash
|
||||
# Start services with consul
|
||||
micro --registry consul server
|
||||
|
||||
# List with consul
|
||||
micro mcp list --registry consul
|
||||
```
|
||||
|
||||
### Service not responding in tests
|
||||
|
||||
If `micro mcp test` fails:
|
||||
|
||||
1. Verify the tool name is correct:
|
||||
```bash
|
||||
micro mcp list
|
||||
```
|
||||
|
||||
2. Check the JSON input format:
|
||||
```bash
|
||||
# Invalid
|
||||
micro mcp test service.Handler.Method '{invalid}'
|
||||
|
||||
# Valid
|
||||
micro mcp test service.Handler.Method '{"key": "value"}'
|
||||
```
|
||||
|
||||
3. Check service logs for errors.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read the [MCP Documentation](../../gateway/mcp/DOCUMENTATION.md)
|
||||
- Try the [MCP Examples](../../examples/mcp/README.md)
|
||||
- Learn about [Tool Scopes and Security](../../gateway/mcp/DOCUMENTATION.md#authentication-and-scopes)
|
||||
- Explore [Agent SDKs](#) (coming soon)
|
||||
@@ -0,0 +1,846 @@
|
||||
// Package mcp provides the 'micro mcp' command for MCP server management
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go-micro.dev/v6/client"
|
||||
"go-micro.dev/v6/cmd"
|
||||
"go-micro.dev/v6/codec/bytes"
|
||||
"go-micro.dev/v6/gateway/mcp"
|
||||
"go-micro.dev/v6/registry"
|
||||
"go-micro.dev/v6/wrapper/x402"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd.Register(&cli.Command{
|
||||
Name: "mcp",
|
||||
Usage: "MCP server management",
|
||||
Description: `Manage MCP (Model Context Protocol) server for AI agent integration.
|
||||
|
||||
Examples:
|
||||
# Start MCP server (stdio for Claude Code)
|
||||
micro mcp serve
|
||||
|
||||
# Start MCP server with HTTP/SSE
|
||||
micro mcp serve --address :3000
|
||||
|
||||
# List available tools
|
||||
micro mcp list
|
||||
|
||||
# Test a tool
|
||||
micro mcp test users.Users.Get
|
||||
|
||||
The 'micro mcp' command exposes your microservices as AI-accessible tools via the
|
||||
Model Context Protocol (MCP). This enables Claude Code, ChatGPT, and other AI agents
|
||||
to discover and call your services automatically.
|
||||
|
||||
For Claude Code integration, add to your config:
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-services": {
|
||||
"command": "micro",
|
||||
"args": ["mcp", "serve"]
|
||||
}
|
||||
}
|
||||
}`,
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "serve",
|
||||
Usage: "Start MCP server",
|
||||
Description: `Start an MCP server to expose microservices as AI tools.
|
||||
|
||||
By default, uses stdio transport (for Claude Code and local AI tools).
|
||||
Use --address for HTTP/SSE transport (for web-based agents).
|
||||
|
||||
Examples:
|
||||
# Stdio transport (for Claude Code)
|
||||
micro mcp serve
|
||||
|
||||
# HTTP/SSE transport
|
||||
micro mcp serve --address :3000
|
||||
|
||||
# Custom registry
|
||||
micro mcp serve --registry consul --registry_address consul:8500`,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "address",
|
||||
Usage: "HTTP address to listen on (e.g., :3000). If not set, uses stdio.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "registry",
|
||||
Usage: "Registry for service discovery (mdns, consul, etcd)",
|
||||
Value: "mdns",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "registry_address",
|
||||
Usage: "Registry address (e.g., consul:8500)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "x402_pay_to",
|
||||
Usage: "Enable x402 payments for tool calls; the address payments are sent to",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "x402_amount",
|
||||
Usage: "Default amount required per tool call, in the asset's smallest unit (e.g. 10000 = 0.01 USDC)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "x402_network",
|
||||
Usage: "Payment network: base (default), solana, ...",
|
||||
Value: "base",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "x402_facilitator",
|
||||
Usage: "x402 facilitator URL (Coinbase CDP, Alchemy, or self-hosted)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "x402_config",
|
||||
Usage: "Path to an x402 config file (payTo, network, asset, amount, per-tool amounts); overrides the x402_* flags",
|
||||
},
|
||||
},
|
||||
Action: serveAction,
|
||||
},
|
||||
{
|
||||
Name: "list",
|
||||
Usage: "List available tools",
|
||||
Description: `List all tools available via MCP.
|
||||
|
||||
Each service endpoint is exposed as a tool that AI agents can call.
|
||||
|
||||
Example:
|
||||
micro mcp list`,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "registry",
|
||||
Usage: "Registry for service discovery (mdns, consul, etcd)",
|
||||
Value: "mdns",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "registry_address",
|
||||
Usage: "Registry address",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "json",
|
||||
Usage: "Output as JSON",
|
||||
},
|
||||
},
|
||||
Action: listAction,
|
||||
},
|
||||
{
|
||||
Name: "test",
|
||||
Usage: "Test a tool",
|
||||
Description: `Test calling a specific tool.
|
||||
|
||||
Example:
|
||||
micro mcp test users.Users.Get '{"id": "123"}'`,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "registry",
|
||||
Usage: "Registry for service discovery",
|
||||
Value: "mdns",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "registry_address",
|
||||
Usage: "Registry address",
|
||||
},
|
||||
},
|
||||
Action: testAction,
|
||||
},
|
||||
{
|
||||
Name: "docs",
|
||||
Usage: "Generate MCP documentation",
|
||||
Description: `Generate documentation for all available MCP tools.
|
||||
|
||||
The documentation includes tool names, descriptions, parameters, and examples
|
||||
extracted from service metadata and Go comments.
|
||||
|
||||
Examples:
|
||||
# Generate markdown documentation
|
||||
micro mcp docs
|
||||
|
||||
# Generate JSON documentation
|
||||
micro mcp docs --format json
|
||||
|
||||
# Save to file
|
||||
micro mcp docs --output mcp-tools.md`,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "registry",
|
||||
Usage: "Registry for service discovery",
|
||||
Value: "mdns",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "registry_address",
|
||||
Usage: "Registry address",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "Output format (markdown, json)",
|
||||
Value: "markdown",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "Output file (default: stdout)",
|
||||
},
|
||||
},
|
||||
Action: docsAction,
|
||||
},
|
||||
{
|
||||
Name: "export",
|
||||
Usage: "Export tools to different formats",
|
||||
Description: `Export MCP tools to various agent framework formats.
|
||||
|
||||
Supported formats:
|
||||
- langchain: LangChain tool definitions (Python)
|
||||
- openapi: OpenAPI 3.0 specification
|
||||
- json: Raw JSON tool definitions
|
||||
|
||||
Examples:
|
||||
# Export to LangChain format
|
||||
micro mcp export langchain
|
||||
|
||||
# Export to OpenAPI
|
||||
micro mcp export openapi --output openapi.yaml
|
||||
|
||||
# Export raw JSON
|
||||
micro mcp export json`,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "registry",
|
||||
Usage: "Registry for service discovery",
|
||||
Value: "mdns",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "registry_address",
|
||||
Usage: "Registry address",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "Output file (default: stdout)",
|
||||
},
|
||||
},
|
||||
Action: exportAction,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// serveAction starts the MCP server
|
||||
func serveAction(ctx *cli.Context) error {
|
||||
// Get registry
|
||||
reg := registry.DefaultRegistry
|
||||
if regName := ctx.String("registry"); regName != "" {
|
||||
// TODO: Support other registries (consul, etcd)
|
||||
if regName != "mdns" {
|
||||
return fmt.Errorf("registry %s not yet supported, use mdns", regName)
|
||||
}
|
||||
}
|
||||
|
||||
// Create MCP server options
|
||||
opts := mcp.Options{
|
||||
Registry: reg,
|
||||
Address: ctx.String("address"),
|
||||
Context: context.Background(),
|
||||
Logger: log.Default(),
|
||||
}
|
||||
|
||||
// Opt-in x402 payments: a config file (per-tool amounts) or flags.
|
||||
if cfgPath := ctx.String("x402_config"); cfgPath != "" {
|
||||
cfg, err := x402.LoadConfig(cfgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.Payment = cfg
|
||||
} else if payTo := ctx.String("x402_pay_to"); payTo != "" {
|
||||
opts.Payment = &x402.Config{
|
||||
PayTo: payTo,
|
||||
Amount: ctx.String("x402_amount"),
|
||||
Network: ctx.String("x402_network"),
|
||||
FacilitatorURL: ctx.String("x402_facilitator"),
|
||||
}
|
||||
}
|
||||
|
||||
// Handle shutdown gracefully
|
||||
ctx2, cancel := context.WithCancel(opts.Context)
|
||||
opts.Context = ctx2
|
||||
defer cancel()
|
||||
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-sigChan
|
||||
cancel()
|
||||
}()
|
||||
|
||||
// Start MCP server
|
||||
return mcp.Serve(opts)
|
||||
}
|
||||
|
||||
// listAction lists available tools
|
||||
func listAction(ctx *cli.Context) error {
|
||||
// Get registry
|
||||
reg := registry.DefaultRegistry
|
||||
|
||||
// Create temporary MCP server to discover tools
|
||||
opts := mcp.Options{
|
||||
Registry: reg,
|
||||
Context: context.Background(),
|
||||
Logger: log.New(os.Stderr, "", 0), // Log to stderr so stdout is clean
|
||||
}
|
||||
|
||||
// Discover services
|
||||
services, err := opts.Registry.ListServices()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list services: %w", err)
|
||||
}
|
||||
|
||||
if ctx.Bool("json") {
|
||||
// JSON output
|
||||
var tools []map[string]interface{}
|
||||
for _, svc := range services {
|
||||
fullSvcs, err := opts.Registry.GetService(svc.Name)
|
||||
if err != nil || len(fullSvcs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ep := range fullSvcs[0].Endpoints {
|
||||
tools = append(tools, map[string]interface{}{
|
||||
"name": fmt.Sprintf("%s.%s", svc.Name, ep.Name),
|
||||
"service": svc.Name,
|
||||
"endpoint": ep.Name,
|
||||
"description": fmt.Sprintf("Call %s on %s service", ep.Name, svc.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(map[string]interface{}{
|
||||
"tools": tools,
|
||||
"count": len(tools),
|
||||
})
|
||||
}
|
||||
|
||||
// Human-readable output
|
||||
fmt.Println()
|
||||
fmt.Println(" \033[1mmicro mcp tools\033[0m")
|
||||
fmt.Println()
|
||||
toolCount := 0
|
||||
for _, svc := range services {
|
||||
fullSvcs, err := opts.Registry.GetService(svc.Name)
|
||||
if err != nil || len(fullSvcs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf(" \033[1m%s\033[0m\n", svc.Name)
|
||||
for _, ep := range fullSvcs[0].Endpoints {
|
||||
toolName := fmt.Sprintf("%s.%s", svc.Name, ep.Name)
|
||||
fmt.Printf(" \033[32m●\033[0m %s\n", toolName)
|
||||
toolCount++
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
fmt.Printf(" \033[2m%d tools\033[0m\n\n", toolCount)
|
||||
return nil
|
||||
}
|
||||
|
||||
// testAction tests a specific tool
|
||||
func testAction(ctx *cli.Context) error {
|
||||
if ctx.Args().Len() < 1 {
|
||||
return fmt.Errorf("usage: micro mcp test <tool-name> [input-json]")
|
||||
}
|
||||
|
||||
toolName := ctx.Args().First()
|
||||
inputJSON := "{}"
|
||||
if ctx.Args().Len() > 1 {
|
||||
inputJSON = ctx.Args().Get(1)
|
||||
}
|
||||
|
||||
// Validate input JSON
|
||||
var inputData map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(inputJSON), &inputData); err != nil {
|
||||
return fmt.Errorf("invalid JSON input: %w", err)
|
||||
}
|
||||
|
||||
// Get registry
|
||||
reg := registry.DefaultRegistry
|
||||
if regName := ctx.String("registry"); regName != "" {
|
||||
if regName != "mdns" {
|
||||
return fmt.Errorf("registry %s not yet supported, use mdns", regName)
|
||||
}
|
||||
}
|
||||
|
||||
// Create MCP options
|
||||
opts := mcp.Options{
|
||||
Registry: reg,
|
||||
Context: context.Background(),
|
||||
Logger: log.New(os.Stderr, "", 0),
|
||||
}
|
||||
|
||||
// Parse tool name (format: "service.endpoint" or "service.Handler.Method")
|
||||
parts := parseTool(toolName)
|
||||
if len(parts) < 2 {
|
||||
return fmt.Errorf("invalid tool name format. Expected: service.endpoint or service.Handler.Method")
|
||||
}
|
||||
|
||||
serviceName := parts[0]
|
||||
endpointName := parts[1]
|
||||
|
||||
// If tool name has 3 parts, combine last two for endpoint (e.g., Handler.Method)
|
||||
if len(parts) == 3 {
|
||||
endpointName = parts[1] + "." + parts[2]
|
||||
}
|
||||
|
||||
// Discover the tool from registry
|
||||
services, err := opts.Registry.GetService(serviceName)
|
||||
if err != nil || len(services) == 0 {
|
||||
return fmt.Errorf("service %s not found: %w", serviceName, err)
|
||||
}
|
||||
|
||||
// Find the endpoint
|
||||
var endpoint *registry.Endpoint
|
||||
for _, ep := range services[0].Endpoints {
|
||||
if ep.Name == endpointName {
|
||||
endpoint = ep
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if endpoint == nil {
|
||||
return fmt.Errorf("endpoint %s not found in service %s", endpointName, serviceName)
|
||||
}
|
||||
|
||||
// Display test info
|
||||
fmt.Printf("Testing tool: %s\n", toolName)
|
||||
fmt.Printf("Service: %s\n", serviceName)
|
||||
fmt.Printf("Endpoint: %s\n", endpointName)
|
||||
fmt.Printf("Input: %s\n\n", inputJSON)
|
||||
|
||||
// Convert input to JSON bytes for RPC call
|
||||
inputBytes, err := json.Marshal(inputData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal input: %w", err)
|
||||
}
|
||||
|
||||
// Make RPC call using bytes codec
|
||||
c := opts.Client
|
||||
if c == nil {
|
||||
c = client.DefaultClient
|
||||
}
|
||||
|
||||
// Create request with bytes frame
|
||||
req := c.NewRequest(serviceName, endpointName, &bytes.Frame{Data: inputBytes})
|
||||
|
||||
// Make the call
|
||||
var rsp bytes.Frame
|
||||
if err := c.Call(opts.Context, req, &rsp); err != nil {
|
||||
fmt.Printf("❌ Call failed: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse and display response
|
||||
fmt.Println("✅ Call successful!")
|
||||
fmt.Println("\nResponse:")
|
||||
|
||||
// Try to pretty-print JSON response
|
||||
var result interface{}
|
||||
if err := json.Unmarshal(rsp.Data, &result); err == nil {
|
||||
prettyJSON, err := json.MarshalIndent(result, "", " ")
|
||||
if err == nil {
|
||||
fmt.Println(string(prettyJSON))
|
||||
} else {
|
||||
fmt.Println(string(rsp.Data))
|
||||
}
|
||||
} else {
|
||||
// Not JSON, print raw
|
||||
fmt.Println(string(rsp.Data))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseTool splits a tool name into service and endpoint parts
|
||||
func parseTool(toolName string) []string {
|
||||
return strings.Split(toolName, ".")
|
||||
}
|
||||
|
||||
// docsAction generates documentation for MCP tools
|
||||
func docsAction(ctx *cli.Context) error {
|
||||
// Get registry
|
||||
reg := registry.DefaultRegistry
|
||||
|
||||
// Create temporary MCP server to discover tools
|
||||
opts := mcp.Options{
|
||||
Registry: reg,
|
||||
Context: context.Background(),
|
||||
Logger: log.New(os.Stderr, "", 0),
|
||||
}
|
||||
|
||||
// Discover services
|
||||
services, err := opts.Registry.ListServices()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list services: %w", err)
|
||||
}
|
||||
|
||||
format := ctx.String("format")
|
||||
outputFile := ctx.String("output")
|
||||
|
||||
// Prepare output writer
|
||||
writer := os.Stdout
|
||||
if outputFile != "" {
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create output file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
writer = f
|
||||
}
|
||||
|
||||
// Collect all tools with metadata
|
||||
type ToolDoc struct {
|
||||
Name string `json:"name"`
|
||||
Service string `json:"service"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Description string `json:"description"`
|
||||
Example string `json:"example,omitempty"`
|
||||
Scopes []string `json:"scopes,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
var tools []ToolDoc
|
||||
for _, svc := range services {
|
||||
fullSvcs, err := opts.Registry.GetService(svc.Name)
|
||||
if err != nil || len(fullSvcs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ep := range fullSvcs[0].Endpoints {
|
||||
toolDoc := ToolDoc{
|
||||
Name: fmt.Sprintf("%s.%s", svc.Name, ep.Name),
|
||||
Service: svc.Name,
|
||||
Endpoint: ep.Name,
|
||||
Description: fmt.Sprintf("Call %s on %s service", ep.Name, svc.Name),
|
||||
Metadata: ep.Metadata,
|
||||
}
|
||||
|
||||
// Extract description from metadata if available
|
||||
if desc, ok := ep.Metadata["description"]; ok {
|
||||
toolDoc.Description = desc
|
||||
}
|
||||
|
||||
// Extract example from metadata if available
|
||||
if example, ok := ep.Metadata["example"]; ok {
|
||||
toolDoc.Example = example
|
||||
}
|
||||
|
||||
// Extract scopes from metadata if available
|
||||
if scopesStr, ok := ep.Metadata["scopes"]; ok && scopesStr != "" {
|
||||
toolDoc.Scopes = strings.Split(scopesStr, ",")
|
||||
}
|
||||
|
||||
tools = append(tools, toolDoc)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate output based on format
|
||||
switch format {
|
||||
case "json":
|
||||
enc := json.NewEncoder(writer)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(map[string]interface{}{
|
||||
"tools": tools,
|
||||
"count": len(tools),
|
||||
})
|
||||
|
||||
case "markdown":
|
||||
fmt.Fprintf(writer, "# MCP Tools Documentation\n\n")
|
||||
fmt.Fprintf(writer, "Generated: %s\n\n", time.Now().Format("2006-01-02 15:04:05"))
|
||||
fmt.Fprintf(writer, "Total Tools: %d\n\n", len(tools))
|
||||
|
||||
// Group by service
|
||||
serviceMap := make(map[string][]ToolDoc)
|
||||
for _, tool := range tools {
|
||||
serviceMap[tool.Service] = append(serviceMap[tool.Service], tool)
|
||||
}
|
||||
|
||||
for service, serviceTools := range serviceMap {
|
||||
fmt.Fprintf(writer, "## Service: %s\n\n", service)
|
||||
|
||||
for _, tool := range serviceTools {
|
||||
fmt.Fprintf(writer, "### %s\n\n", tool.Name)
|
||||
fmt.Fprintf(writer, "**Description:** %s\n\n", tool.Description)
|
||||
|
||||
if len(tool.Scopes) > 0 {
|
||||
fmt.Fprintf(writer, "**Required Scopes:** %s\n\n", strings.Join(tool.Scopes, ", "))
|
||||
}
|
||||
|
||||
if tool.Example != "" {
|
||||
fmt.Fprintf(writer, "**Example Input:**\n```json\n%s\n```\n\n", tool.Example)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported format: %s (supported: markdown, json)", format)
|
||||
}
|
||||
}
|
||||
|
||||
// exportAction exports tools to different formats
|
||||
func exportAction(ctx *cli.Context) error {
|
||||
if ctx.Args().Len() < 1 {
|
||||
return fmt.Errorf("usage: micro mcp export <format>\nSupported formats: langchain, openapi, json")
|
||||
}
|
||||
|
||||
exportFormat := ctx.Args().First()
|
||||
|
||||
// Get registry
|
||||
reg := registry.DefaultRegistry
|
||||
|
||||
// Create temporary MCP server to discover tools
|
||||
opts := mcp.Options{
|
||||
Registry: reg,
|
||||
Context: context.Background(),
|
||||
Logger: log.New(os.Stderr, "", 0),
|
||||
}
|
||||
|
||||
// Discover services
|
||||
services, err := opts.Registry.ListServices()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list services: %w", err)
|
||||
}
|
||||
|
||||
outputFile := ctx.String("output")
|
||||
|
||||
// Prepare output writer
|
||||
writer := os.Stdout
|
||||
if outputFile != "" {
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create output file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
writer = f
|
||||
}
|
||||
|
||||
switch exportFormat {
|
||||
case "langchain":
|
||||
return exportLangChain(writer, services, opts)
|
||||
case "openapi":
|
||||
return exportOpenAPI(writer, services, opts)
|
||||
case "json":
|
||||
return exportJSON(writer, services, opts)
|
||||
default:
|
||||
return fmt.Errorf("unsupported export format: %s\nSupported: langchain, openapi, json", exportFormat)
|
||||
}
|
||||
}
|
||||
|
||||
// exportLangChain exports tools in LangChain format (Python)
|
||||
func exportLangChain(writer *os.File, services []*registry.Service, opts mcp.Options) error {
|
||||
fmt.Fprintf(writer, "# LangChain Tools for Go Micro Services\n")
|
||||
fmt.Fprintf(writer, "# Auto-generated from MCP service discovery\n\n")
|
||||
fmt.Fprintf(writer, "from langchain.tools import Tool\n")
|
||||
fmt.Fprintf(writer, "import requests\nimport json\n\n")
|
||||
fmt.Fprintf(writer, "# Configure your MCP gateway endpoint\n")
|
||||
fmt.Fprintf(writer, "MCP_GATEWAY_URL = 'http://localhost:3000/mcp'\n\n")
|
||||
|
||||
fmt.Fprintf(writer, "def call_mcp_tool(tool_name, arguments):\n")
|
||||
fmt.Fprintf(writer, " \"\"\"Call an MCP tool via HTTP gateway\"\"\"\n")
|
||||
fmt.Fprintf(writer, " response = requests.post(\n")
|
||||
fmt.Fprintf(writer, " f'{MCP_GATEWAY_URL}/call',\n")
|
||||
fmt.Fprintf(writer, " json={'name': tool_name, 'arguments': arguments}\n")
|
||||
fmt.Fprintf(writer, " )\n")
|
||||
fmt.Fprintf(writer, " response.raise_for_status()\n")
|
||||
fmt.Fprintf(writer, " return response.json()\n\n")
|
||||
|
||||
fmt.Fprintf(writer, "# Define tools\n")
|
||||
fmt.Fprintf(writer, "tools = []\n\n")
|
||||
|
||||
for _, svc := range services {
|
||||
fullSvcs, err := opts.Registry.GetService(svc.Name)
|
||||
if err != nil || len(fullSvcs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ep := range fullSvcs[0].Endpoints {
|
||||
toolName := fmt.Sprintf("%s.%s", svc.Name, ep.Name)
|
||||
description := fmt.Sprintf("Call %s on %s service", ep.Name, svc.Name)
|
||||
|
||||
if desc, ok := ep.Metadata["description"]; ok {
|
||||
description = desc
|
||||
}
|
||||
|
||||
// Generate Python function name (replace dots with underscores)
|
||||
funcName := strings.ReplaceAll(toolName, ".", "_")
|
||||
|
||||
fmt.Fprintf(writer, "def %s(arguments: str) -> str:\n", funcName)
|
||||
fmt.Fprintf(writer, " \"\"\"% s\"\"\"\n", description)
|
||||
fmt.Fprintf(writer, " args = json.loads(arguments) if isinstance(arguments, str) else arguments\n")
|
||||
fmt.Fprintf(writer, " return json.dumps(call_mcp_tool('%s', args))\n\n", toolName)
|
||||
|
||||
fmt.Fprintf(writer, "tools.append(Tool(\n")
|
||||
fmt.Fprintf(writer, " name='%s',\n", toolName)
|
||||
fmt.Fprintf(writer, " func=%s,\n", funcName)
|
||||
fmt.Fprintf(writer, " description='%s'\n", strings.ReplaceAll(description, "'", "\\'"))
|
||||
fmt.Fprintf(writer, "))\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "# Example usage:\n")
|
||||
fmt.Fprintf(writer, "# from langchain.agents import initialize_agent, AgentType\n")
|
||||
fmt.Fprintf(writer, "# from langchain.llms import OpenAI\n")
|
||||
fmt.Fprintf(writer, "#\n")
|
||||
fmt.Fprintf(writer, "# llm = OpenAI(temperature=0)\n")
|
||||
fmt.Fprintf(writer, "# agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)\n")
|
||||
fmt.Fprintf(writer, "# agent.run('Your query here')\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// exportOpenAPI exports tools in OpenAPI 3.0 format
|
||||
func exportOpenAPI(writer *os.File, services []*registry.Service, opts mcp.Options) error {
|
||||
spec := map[string]interface{}{
|
||||
"openapi": "3.0.0",
|
||||
"info": map[string]interface{}{
|
||||
"title": "Go Micro MCP Services",
|
||||
"description": "Auto-generated OpenAPI spec from MCP service discovery",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
"servers": []map[string]interface{}{
|
||||
{
|
||||
"url": "http://localhost:3000",
|
||||
"description": "MCP Gateway",
|
||||
},
|
||||
},
|
||||
"paths": make(map[string]interface{}),
|
||||
}
|
||||
|
||||
paths := spec["paths"].(map[string]interface{})
|
||||
|
||||
for _, svc := range services {
|
||||
fullSvcs, err := opts.Registry.GetService(svc.Name)
|
||||
if err != nil || len(fullSvcs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ep := range fullSvcs[0].Endpoints {
|
||||
toolName := fmt.Sprintf("%s.%s", svc.Name, ep.Name)
|
||||
path := fmt.Sprintf("/mcp/call/%s", strings.ReplaceAll(toolName, ".", "/"))
|
||||
|
||||
description := fmt.Sprintf("Call %s on %s service", ep.Name, svc.Name)
|
||||
if desc, ok := ep.Metadata["description"]; ok {
|
||||
description = desc
|
||||
}
|
||||
|
||||
operation := map[string]interface{}{
|
||||
"summary": toolName,
|
||||
"description": description,
|
||||
"operationId": strings.ReplaceAll(toolName, ".", "_"),
|
||||
"requestBody": map[string]interface{}{
|
||||
"required": true,
|
||||
"content": map[string]interface{}{
|
||||
"application/json": map[string]interface{}{
|
||||
"schema": map[string]interface{}{
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"responses": map[string]interface{}{
|
||||
"200": map[string]interface{}{
|
||||
"description": "Successful response",
|
||||
"content": map[string]interface{}{
|
||||
"application/json": map[string]interface{}{
|
||||
"schema": map[string]interface{}{
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Add scope security if available
|
||||
if scopesStr, ok := ep.Metadata["scopes"]; ok && scopesStr != "" {
|
||||
operation["security"] = []map[string]interface{}{
|
||||
{
|
||||
"bearerAuth": strings.Split(scopesStr, ","),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
paths[path] = map[string]interface{}{
|
||||
"post": operation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add security schemes
|
||||
spec["components"] = map[string]interface{}{
|
||||
"securitySchemes": map[string]interface{}{
|
||||
"bearerAuth": map[string]interface{}{
|
||||
"type": "http",
|
||||
"scheme": "bearer",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(writer)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(spec)
|
||||
}
|
||||
|
||||
// exportJSON exports raw tool definitions as JSON
|
||||
func exportJSON(writer *os.File, services []*registry.Service, opts mcp.Options) error {
|
||||
var tools []map[string]interface{}
|
||||
|
||||
for _, svc := range services {
|
||||
fullSvcs, err := opts.Registry.GetService(svc.Name)
|
||||
if err != nil || len(fullSvcs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ep := range fullSvcs[0].Endpoints {
|
||||
tool := map[string]interface{}{
|
||||
"name": fmt.Sprintf("%s.%s", svc.Name, ep.Name),
|
||||
"service": svc.Name,
|
||||
"endpoint": ep.Name,
|
||||
"metadata": ep.Metadata,
|
||||
}
|
||||
|
||||
if desc, ok := ep.Metadata["description"]; ok {
|
||||
tool["description"] = desc
|
||||
}
|
||||
|
||||
if example, ok := ep.Metadata["example"]; ok {
|
||||
tool["example"] = example
|
||||
}
|
||||
|
||||
if scopesStr, ok := ep.Metadata["scopes"]; ok && scopesStr != "" {
|
||||
tool["scopes"] = strings.Split(scopesStr, ",")
|
||||
}
|
||||
|
||||
tools = append(tools, tool)
|
||||
}
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(writer)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(map[string]interface{}{
|
||||
"tools": tools,
|
||||
"count": len(tools),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseTool(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
toolName string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "simple two-part tool",
|
||||
toolName: "service.endpoint",
|
||||
want: []string{"service", "endpoint"},
|
||||
},
|
||||
{
|
||||
name: "three-part tool (service.Handler.Method)",
|
||||
toolName: "greeter.Greeter.Hello",
|
||||
want: []string{"greeter", "Greeter", "Hello"},
|
||||
},
|
||||
{
|
||||
name: "single part (invalid)",
|
||||
toolName: "service",
|
||||
want: []string{"service"},
|
||||
},
|
||||
{
|
||||
name: "four-part tool",
|
||||
toolName: "users.Users.Get.All",
|
||||
want: []string{"users", "Users", "Get", "All"},
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
toolName: "",
|
||||
want: []string{""},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := parseTool(tt.toolName)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("parseTool(%q) = %v, want %v", tt.toolName, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportFormats(t *testing.T) {
|
||||
// Test that export formats are recognized
|
||||
formats := []string{"langchain", "openapi", "json"}
|
||||
|
||||
for _, format := range formats {
|
||||
t.Run(format, func(t *testing.T) {
|
||||
// This is a basic test to ensure the format strings are defined
|
||||
// The actual export functions are tested through integration tests
|
||||
if format == "" {
|
||||
t.Error("export format should not be empty")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocsFormats(t *testing.T) {
|
||||
// Test that docs formats are recognized
|
||||
formats := []string{"markdown", "json"}
|
||||
|
||||
for _, format := range formats {
|
||||
t.Run(format, func(t *testing.T) {
|
||||
// This is a basic test to ensure the format strings are defined
|
||||
// The actual docs functions are tested through integration tests
|
||||
if format == "" {
|
||||
t.Error("docs format should not be empty")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user