feat: impl nginx migration mcp server (#2916)

Co-authored-by: 韩贤涛 <601803023@qq.com>
This commit is contained in:
nohup
2025-10-31 13:59:15 +08:00
committed by GitHub
parent ef6baf29e8
commit d745bc0d0b
28 changed files with 6512 additions and 0 deletions

View File

@@ -6,6 +6,7 @@ import (
_ "github.com/alibaba/higress/plugins/golang-filter/mcp-server/registry/nacos"
_ "github.com/alibaba/higress/plugins/golang-filter/mcp-server/servers/gorm"
_ "github.com/alibaba/higress/plugins/golang-filter/mcp-server/servers/higress/higress-api"
_ "github.com/alibaba/higress/plugins/golang-filter/mcp-server/servers/higress/nginx-migration"
_ "github.com/alibaba/higress/plugins/golang-filter/mcp-server/servers/rag"
mcp_session "github.com/alibaba/higress/plugins/golang-filter/mcp-session"
"github.com/alibaba/higress/plugins/golang-filter/mcp-session/common"

View File

@@ -0,0 +1,2 @@
# 本地配置文件
config/rag.json

View File

@@ -0,0 +1,59 @@
.PHONY: all build standalone clean test help
# 默认目标
all: standalone
# build 别名(指向 standalone
build: standalone
# 编译独立模式
standalone:
@echo "编译独立模式..."
@cd standalone/cmd && go build -o ../../nginx-migration-mcp
@echo "独立模式编译完成: ./nginx-migration-mcp"
# 清理编译产物
clean:
@echo "清理编译产物..."
@rm -f nginx-migration-mcp
@echo "清理完成"
# 运行测试
test:
@echo "运行测试..."
@go test ./...
@echo "测试完成"
# 安装依赖
deps:
@echo "安装依赖..."
@go mod tidy
@echo "依赖安装完成"
# 格式化代码
fmt:
@echo "格式化代码..."
@go fmt ./...
@echo "代码格式化完成"
# 显示帮助
help:
@echo "Nginx Migration MCP Server - Makefile"
@echo ""
@echo "可用目标:"
@echo " make - 编译独立模式(默认)"
@echo " make build - 编译独立模式"
@echo " make standalone - 编译独立模式"
@echo " make test - 运行测试"
@echo " make clean - 清理编译产物"
@echo " make deps - 安装依赖"
@echo " make fmt - 格式化代码"
@echo " make help - 显示此帮助信息"
@echo ""
@echo "目录结构:"
@echo " cmd/standalone/ - 独立模式入口"
@echo " internal/standalone/ - 独立模式实现"
@echo " integration/ - Higress MCP 框架集成"
@echo " tools/ - 共享核心逻辑"

View File

@@ -0,0 +1,90 @@
# Nginx Migration MCP 快速开始
### 1. 构建服务器
```bash
cd /path/to/higress/plugins/golang-filter/mcp-server/servers/higress/nginx-migration
make build
```
### 2. 配置 MCP 客户端
在 MCP 客户端配置文件中添加(以 Cursor 为例):
**位置**: `~/.cursor/config/mcp_settings.json` 或 Cursor 设置中的 MCP 配置
```json
{
"mcpServers": {
"nginx-migration": {
"command": "/path/to/nginx-migration/nginx-migration-mcp",
"args": []
}
}
}
```
## 可用工具
### Nginx 配置转换
`parse_nginx_config` | 解析和分析 Nginx 配置文件 |
`convert_to_higress` | 转换为 Higress HTTPRoute 和 Service |
### Lua 插件迁移
`convert_lua_to_wasm` | 一键转换 Lua 脚本为 WASM 插件 |
`analyze_lua_plugin` | 分析 Lua 插件兼容性 |
`generate_conversion_hints` | 生成 API 映射和转换提示 |
`validate_wasm_code` | 验证 Go WASM 代码 |
`generate_deployment_config` | 生成部署配置包 |
## 使用示例
### 示例 1转换 Nginx 配置
```
我有一个 Nginx 配置,帮我转换为 Higress HTTPRoute
```
HOST LLM 会自动调用 `convert_to_higress` 工具完成转换。
### 示例 2快速转换 Lua 插件
```
将这个 Lua 限流插件转换为 Higress WASM 插件:
[粘贴 Lua 代码]
```
HOST LLM 会调用 `convert_lua_to_wasm` 工具自动转换。
### 示例 3使用工具链精细转换
```
分析这个 Lua 插件的兼容性:
[粘贴 Lua 代码]
```
然后按照工具链流程:
1. LLM 调用 `analyze_lua_plugin` 分析
2. LLM 调用 `generate_conversion_hints` 获取转换提示
3. LLM 基于提示生成 Go WASM 代码
4. LLM 调用 `validate_wasm_code` 验证代码
5. LLM 调用 `generate_deployment_config` 生成部署配置
## 调试
启用调试日志:
```bash
DEBUG=true ./nginx-migration-mcp
```
查看工具列表:
```bash
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | ./nginx-migration-mcp
```

View File

@@ -0,0 +1,319 @@
# Nginx Migration MCP Server
一个用于将 Nginx 配置和 Lua 插件迁移到 Higress 的 MCP 服务器。
## 功能特性
### 配置转换工具
- **parse_nginx_config** - 解析和分析 Nginx 配置文件
- **convert_to_higress** - 将 Nginx 配置转换为 Higress Ingress主要方式或 HTTPRoute可选
### Lua 插件迁移工具链
#### 快速转换模式
- **convert_lua_to_wasm** - 一键将 Lua 脚本转换为 WASM 插件
#### LLM 辅助工具链(精细化控制)
1. **analyze_lua_plugin** - 分析 Lua 插件兼容性
2. **generate_conversion_hints** - 生成详细的代码转换提示和 API 映射
3. **validate_wasm_code** - 验证生成的 Go WASM 代码
4. **generate_deployment_config** - 生成完整的部署配置包
## 快速开始
### 构建
```bash
make build
```
构建后会生成 `nginx-migration-mcp` 可执行文件。
### 基础配置(无需知识库)
**默认模式**:工具可以直接使用,基于内置规则生成转换建议。
在 MCP 客户端(如 Cursor的配置文件中添加
```json
{
"mcpServers": {
"nginx-migration": {
"command": "/path/to/nginx-migration-mcp",
"args": []
}
}
}
```
### 进阶配置(启用 RAG 知识库)
RAG检索增强生成功能通过阿里云百炼集成 Higress 官方文档知识库,提供更准确的转换建议和 API 映射。
#### 适用场景
启用 RAG 后,以下工具将获得增强:
- **generate_conversion_hints** - 提供基于官方文档的 API 映射和代码示例
- **validate_wasm_code** - 基于最佳实践验证代码质量
- **query_knowledge_base** - 直接查询 Higress 官方文档
#### 配置步骤
**步骤 1获取阿里云百炼凭证**
1. 访问 [阿里云百炼控制台](https://bailian.console.aliyun.com/)
2. 创建或选择一个应用空间,获取 **业务空间 ID** (`workspace_id`)
3. 创建知识库并导入 Higress 文档,获取 **知识库 ID** (`knowledge_base_id`)
4. 在 [阿里云 RAM 控制台](https://ram.console.aliyun.com/manage/ak) 创建 AccessKey
- 获取 **AccessKey ID** (`access_key_id`)
- 获取 **AccessKey Secret** (`access_key_secret`)
> **安全提示**:请妥善保管 AccessKey避免泄露。建议使用 RAM 子账号并授予最小权限。
**步骤 2复制配置文件**
```bash
cp config/rag.json.example config/rag.json
```
**步骤 3编辑配置文件**
有两种配置方式:
**方式 1配置 rag.config**
```json
```json
{
"rag": {
"enabled": true,
"provider": "bailian",
"endpoint": "bailian.cn-beijing.aliyuncs.com",
"workspace_id": "${WORKSPACE_ID}",
"knowledge_base_id": "${INDEX_ID}",
"access_key_id": "${ALIBABA_CLOUD_ACCESS_KEY_ID}",
"access_key_secret": "${ALIBABA_CLOUD_ACCESS_KEY_SECRET}"
}
}
```
#### 高级配置项
完整的配置选项(可选):
```json
{
"rag": {
"enabled": true,
// === 必填API 配置 ===
"provider": "bailian",
"endpoint": "bailian.cn-beijing.aliyuncs.com",
"workspace_id": "llm-xxx",
"knowledge_base_id": "idx-xxx",
"access_key_id": "LTAI5t...",
"access_key_secret": "your-secret",
// === 可选:检索配置 ===
"context_mode": "full", // 上下文模式: full | summary | highlights
"max_context_length": 4000, // 最大上下文长度(字符)
"default_top_k": 3, // 默认返回文档数量
"similarity_threshold": 0.7, // 相似度阈值0-1
// === 可选:性能配置 ===
"enable_cache": true, // 启用查询缓存
"cache_ttl": 3600, // 缓存过期时间(秒)
"cache_max_size": 1000, // 最大缓存条目数
"timeout": 10, // 请求超时(秒)
"max_retries": 3, // 最大重试次数
"retry_delay": 1, // 重试间隔(秒)
// === 可选:降级策略 ===
"fallback_on_error": true, // RAG 失败时降级到基础模式
// === 可选:工具级配置 ===
"tools": {
"generate_conversion_hints": {
"use_rag": true, // 为此工具启用 RAG
"context_mode": "full",
"top_k": 3
},
"validate_wasm_code": {
"use_rag": true,
"context_mode": "highlights",
"top_k": 2
}
},
// === 可选:调试配置 ===
"debug": true, // 启用调试日志
"log_queries": true // 记录所有查询
}
}
```
#### 验证配置
启动服务后,检查日志输出:
```bash
# 正常启用 RAG
✓ RAG enabled: bailian (endpoint: bailian.cn-beijing.aliyuncs.com)
✓ Knowledge base: idx-xxx
✓ Cache enabled (TTL: 3600s, Max: 1000)
# RAG 未启用
⚠ RAG disabled, using rule-based conversion only
```
## 使用示例
### 转换 Nginx 配置
使用 `convert_to_higress` 工具,传入 Nginx 配置内容:
- **默认**:生成 Kubernetes Ingress 和 Service 资源
- **可选**:设置 `use_gateway_api=true` 生成 Gateway API HTTPRoute需确认已启用
### 迁移 Lua 插件
**方式一:快速转换**
使用 `convert_lua_to_wasm` 工具一键转换 Lua 脚本为 WASM 插件。
**方式二AI 辅助工具链**
1. 使用 `analyze_lua_plugin` 分析 Lua 代码
2. 使用 `generate_conversion_hints` 获取转换提示和 API 映射(可启用 RAG 增强)
3. AI 根据提示生成 Go WASM 代码
4. 使用 `validate_wasm_code` 验证生成的代码(可启用 RAG 增强)
5. 使用 `generate_deployment_config` 生成部署配置
推荐使用工具链方式处理复杂插件,可获得更好的转换质量和 AI 辅助。
### 查询知识库(需启用 RAG
使用 `query_knowledge_base` 工具直接查询 Higress 文档:
```javascript
// 查询 Lua API 迁移方法
query_knowledge_base({
"query": "ngx.req.get_headers 在 Higress 中怎么实现?",
"scenario": "lua_migration",
"top_k": 5
})
// 查询插件配置方法
query_knowledge_base({
"query": "Higress 限流插件配置",
"scenario": "config_conversion",
"top_k": 3
})
```
## 项目结构
```
nginx-migration/
├── config/ # 配置文件
│ ├── rag.json.example # RAG 配置示例
│ └── rag.json # RAG 配置(需自行创建)
├── integration/ # Higress 集成模式MCP 集成)
│ ├── server.go # MCP 服务器注册与初始化
│ └── mcptools/ # MCP 工具实现
│ ├── adapter.go # MCP 工具适配器
│ ├── context.go # 迁移上下文管理
│ ├── nginx_tools.go # Nginx 配置转换工具
│ ├── lua_tools.go # Lua 插件迁移工具
│ ├── tool_chain.go # 工具链实现(分析、验证、部署)
│ └── rag_integration.go # RAG 知识库集成
├── standalone/ # 独立模式(可独立运行)
│ ├── cmd/
│ │ └── main.go # 独立模式入口
│ ├── server.go # 独立模式 MCP 服务器
│ ├── config.go # 配置加载
│ └── types.go # 类型定义
├── internal/ # 内部实现包
│ ├── rag/ # RAG 功能实现
│ │ ├── config.go # RAG 配置结构
│ │ ├── client.go # 百炼 API 客户端
│ │ └── manager.go # RAG 管理器(查询、缓存)
│ └── standalone/ # 独立模式内部实现
│ └── server.go # 独立服务器逻辑
├── tools/ # 核心转换逻辑(共享库)
│ ├── mcp_tools.go # MCP 工具定义和注册
│ ├── nginx_parser.go # Nginx 配置解析器
│ ├── lua_converter.go # Lua 到 WASM 转换器
│ └── tool_chain.go # 工具链核心实现
├── docs/ # 文档目录
├── mcp-tools.json # MCP 工具元数据定义
├── go.mod # Go 模块依赖
├── go.sum # Go 模块校验和
├── Makefile # 构建脚本
├── README.md # 项目说明文档
├── QUICKSTART.md # 快速开始指南
├── QUICK_TEST.md # 快速测试指南
├── TEST_EXAMPLES.md # 测试示例
└── CHANGELOG_INGRESS_PRIORITY.md # Ingress 优先级变更记录
```
### 目录说明
#### 核心模块
- **`integration/`** - Higress 集成模式
- 作为 Higress MCP 服务器的一部分运行
- 提供完整的 MCP 工具集成
- 支持 RAG 知识库增强
- **`standalone/`** - 独立模式
- 可独立运行的 MCP 服务器
- 适合本地开发和测试
- 支持相同的工具功能
- **`tools/`** - 核心转换逻辑
- 独立于运行模式的转换引擎
- 包含 Nginx 解析、Lua 转换等核心功能
- 可被集成模式和独立模式复用
- **`internal/rag/`** - RAG 功能实现
- 阿里云百炼 API 客户端
- 知识库查询和结果处理
- 缓存管理和性能优化
#### 配置文件
- **`config/rag.json`** - RAG 功能配置(需从 example 复制并填写凭证)
- **`mcp-tools.json`** - MCP 工具元数据定义(工具描述、参数 schema
## 开发
### 构建命令
```bash
# 编译
make build
# 清理
make clean
# 格式化代码
make fmt
# 运行测试
make test
# 查看帮助
make help
```

View File

@@ -0,0 +1,46 @@
{
"rag": {
"enabled": true,
"provider": "bailian",
"endpoint": "bailian.cn-beijing.aliyuncs.com",
"workspace_id": "${WORKSPACE_ID}",
"knowledge_base_id": "${INDEX_ID}",
"access_key_id": "${ALIBABA_CLOUD_ACCESS_KEY_ID}",
"access_key_secret": "${ALIBABA_CLOUD_ACCESS_KEY_SECRET}",
"context_mode": "full",
"max_context_length": 4000,
"default_top_k": 3,
"similarity_threshold": 0.7,
"enable_cache": true,
"cache_ttl": 3600,
"cache_max_size": 1000,
"timeout": 10,
"max_retries": 3,
"retry_delay": 1,
"fallback_on_error": true,
"tools": {
"generate_conversion_hints": {
"use_rag": true,
"context_mode": "full",
"top_k": 3
},
"validate_wasm_code": {
"use_rag": true,
"context_mode": "highlights",
"top_k": 2
},
"convert_lua_to_wasm": {
"use_rag": false
}
},
"debug": true,
"log_queries": true
}
}

View File

@@ -0,0 +1,33 @@
module nginx-migration-mcp
go 1.23
toolchain go1.24.9
require (
github.com/alibaba/higress/plugins/golang-filter v0.0.0-20251023035326-7ea739292dea
github.com/alibabacloud-go/bailian-20231229/v2 v2.5.0
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13
github.com/alibabacloud-go/tea v1.3.13
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
github.com/envoyproxy/envoy v1.36.2
)
require (
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/debug v1.0.1 // indirect
github.com/aliyun/credentials-go v1.4.5 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mark3labs/mcp-go v0.12.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
golang.org/x/net v0.34.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)

View File

@@ -0,0 +1,60 @@
//go:build higress_integration
// +build higress_integration
package mcptools
import (
"context"
"encoding/json"
"fmt"
"github.com/alibaba/higress/plugins/golang-filter/mcp-session/common"
"github.com/mark3labs/mcp-go/mcp"
)
// SimpleToolHandler is a simplified handler function that takes arguments and returns a string result
type SimpleToolHandler func(args map[string]interface{}) (string, error)
// AdaptSimpleHandler converts a SimpleToolHandler to an MCP ToolHandlerFunc
func AdaptSimpleHandler(handler SimpleToolHandler) common.ToolHandlerFunc {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Extract arguments
var args map[string]interface{}
if request.Params.Arguments != nil {
args = request.Params.Arguments
} else {
args = make(map[string]interface{})
}
// Call the simple handler
result, err := handler(args)
if err != nil {
return nil, fmt.Errorf("tool execution failed: %w", err)
}
// Return MCP result
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: result,
},
},
}, nil
}
}
// RegisterSimpleTool registers a tool with a simplified handler
func RegisterSimpleTool(
server *common.MCPServer,
name string,
description string,
inputSchema map[string]interface{},
handler SimpleToolHandler,
) {
// Create tool with schema
schemaBytes, _ := json.Marshal(inputSchema)
tool := mcp.NewToolWithRawSchema(name, description, schemaBytes)
server.AddTool(tool, AdaptSimpleHandler(handler))
}

View File

@@ -0,0 +1,57 @@
//go:build higress_integration
// +build higress_integration
package mcptools
import (
"log"
"nginx-migration-mcp/internal/rag"
)
// MigrationContext holds the configuration context for migration operations
type MigrationContext struct {
GatewayName string
GatewayNamespace string
DefaultNamespace string
DefaultHostname string
RoutePrefix string
ServicePort int
TargetPort int
RAGManager *rag.RAGManager // RAG 管理器
}
// NewDefaultMigrationContext creates a MigrationContext with default values
func NewDefaultMigrationContext() *MigrationContext {
return &MigrationContext{
GatewayName: "higress-gateway",
GatewayNamespace: "higress-system",
DefaultNamespace: "default",
DefaultHostname: "example.com",
RoutePrefix: "nginx-migrated",
ServicePort: 80,
TargetPort: 8080,
}
}
// NewMigrationContextWithRAG creates a MigrationContext with RAG support
func NewMigrationContextWithRAG(ragConfigPath string) *MigrationContext {
ctx := NewDefaultMigrationContext()
// 加载 RAG 配置
config, err := rag.LoadRAGConfig(ragConfigPath)
if err != nil {
log.Printf("⚠️ Failed to load RAG config: %v, RAG will be disabled", err)
config = &rag.RAGConfig{Enabled: false}
}
// 创建 RAG 管理器
ctx.RAGManager = rag.NewRAGManager(config)
if ctx.RAGManager.IsEnabled() {
log.Println("✅ MigrationContext: RAG enabled")
} else {
log.Println("📖 MigrationContext: RAG disabled, using rule-based approach")
}
return ctx
}

View File

@@ -0,0 +1,217 @@
//go:build higress_integration
// +build higress_integration
package mcptools
import (
"fmt"
"log"
"strings"
"nginx-migration-mcp/internal/rag"
"nginx-migration-mcp/tools"
"github.com/alibaba/higress/plugins/golang-filter/mcp-session/common"
)
// RegisterLuaPluginTools registers Lua plugin analysis and conversion tools
func RegisterLuaPluginTools(server *common.MCPServer, ctx *MigrationContext) {
RegisterSimpleTool(
server,
"analyze_lua_plugin",
"分析 Nginx Lua 插件的兼容性,识别使用的 API 和潜在迁移问题",
map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"lua_code": map[string]interface{}{
"type": "string",
"description": "Nginx Lua 插件代码",
},
},
"required": []string{"lua_code"},
},
func(args map[string]interface{}) (string, error) {
return analyzeLuaPlugin(args, ctx)
},
)
RegisterSimpleTool(
server,
"convert_lua_to_wasm",
"一键将 Nginx Lua 脚本转换为 Higress WASM 插件,自动生成 Go 代码和配置",
map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"lua_code": map[string]interface{}{
"type": "string",
"description": "要转换的 Nginx Lua 插件代码",
},
"plugin_name": map[string]interface{}{
"type": "string",
"description": "生成的 WASM 插件名称 (小写字母和连字符)",
},
},
"required": []string{"lua_code", "plugin_name"},
},
func(args map[string]interface{}) (string, error) {
return convertLuaToWasm(args, ctx)
},
)
}
func analyzeLuaPlugin(args map[string]interface{}, ctx *MigrationContext) (string, error) {
luaCode, ok := args["lua_code"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid lua_code parameter")
}
// Analyze Lua features (基于规则)
features := []string{}
warnings := []string{}
detectedAPIs := []string{}
if strings.Contains(luaCode, "ngx.var") {
features = append(features, "- ngx.var - Nginx变量")
detectedAPIs = append(detectedAPIs, "ngx.var")
}
if strings.Contains(luaCode, "ngx.req") {
features = append(features, "- ngx.req - 请求API")
detectedAPIs = append(detectedAPIs, "ngx.req")
}
if strings.Contains(luaCode, "ngx.exit") {
features = append(features, "- ngx.exit - 请求终止")
detectedAPIs = append(detectedAPIs, "ngx.exit")
}
if strings.Contains(luaCode, "ngx.shared") {
features = append(features, "- ngx.shared - 共享字典 (警告)")
warnings = append(warnings, "共享字典需要外部缓存替换")
detectedAPIs = append(detectedAPIs, "ngx.shared")
}
if strings.Contains(luaCode, "ngx.location.capture") {
features = append(features, "- ngx.location.capture - 内部请求 (警告)")
warnings = append(warnings, "需要改为HTTP客户端调用")
detectedAPIs = append(detectedAPIs, "ngx.location.capture")
}
compatibility := "full"
if len(warnings) > 0 {
compatibility = "partial"
}
if len(warnings) > 2 {
compatibility = "manual"
}
// === RAG 增强:查询知识库获取转换建议 ===
var ragContext *rag.RAGContext
if ctx.RAGManager != nil && ctx.RAGManager.IsEnabled() && len(detectedAPIs) > 0 {
query := fmt.Sprintf("Nginx Lua API %s 在 Higress WASM 中的转换方法和最佳实践", strings.Join(detectedAPIs, ", "))
var err error
ragContext, err = ctx.RAGManager.QueryForTool("analyze_lua_plugin", query, "lua_migration")
if err != nil {
log.Printf("⚠️ RAG query failed for analyze_lua_plugin: %v", err)
}
}
// 构建结果
var result strings.Builder
// RAG 上下文(如果有)
if ragContext != nil && ragContext.Enabled && len(ragContext.Documents) > 0 {
result.WriteString("📚 知识库参考资料:\n\n")
result.WriteString(ragContext.FormatContextForAI())
result.WriteString("\n")
}
// 基于规则的分析
warningsText := "无"
if len(warnings) > 0 {
warningsText = strings.Join(warnings, "\n")
}
result.WriteString(fmt.Sprintf(`Lua插件兼容性分析
检测特性:
%s
兼容性警告:
%s
兼容性级别: %s
迁移建议:`, strings.Join(features, "\n"), warningsText, compatibility))
switch compatibility {
case "full":
result.WriteString("\n- 可直接迁移到WASM插件")
case "partial":
result.WriteString("\n- 需要部分重构")
case "manual":
result.WriteString("\n- 需要手动重写")
}
return result.String(), nil
}
func convertLuaToWasm(args map[string]interface{}, ctx *MigrationContext) (string, error) {
luaCode, ok := args["lua_code"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid lua_code parameter")
}
pluginName, ok := args["plugin_name"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid plugin_name parameter")
}
// 分析Lua脚本
analyzer := tools.AnalyzeLuaScript(luaCode)
// === RAG 增强:查询转换模式和代码示例 ===
var ragContext *rag.RAGContext
if ctx.RAGManager != nil && ctx.RAGManager.IsEnabled() && len(analyzer.Features) > 0 {
// 提取特性列表
featureList := []string{}
for feature := range analyzer.Features {
featureList = append(featureList, feature)
}
query := fmt.Sprintf("将使用了 %s 的 Nginx Lua 插件转换为 Higress WASM Go 插件的代码示例",
strings.Join(featureList, ", "))
var err error
ragContext, err = ctx.RAGManager.QueryForTool("convert_lua_to_wasm", query, "lua_to_wasm")
if err != nil {
log.Printf("⚠️ RAG query failed for convert_lua_to_wasm: %v", err)
}
}
// 转换为WASM插件
result, err := tools.ConvertLuaToWasm(analyzer, pluginName)
if err != nil {
return "", fmt.Errorf("conversion failed: %w", err)
}
// 构建响应
var response strings.Builder
// RAG 上下文(如果有)
if ragContext != nil && ragContext.Enabled && len(ragContext.Documents) > 0 {
response.WriteString("📚 知识库代码示例:\n\n")
response.WriteString(ragContext.FormatContextForAI())
response.WriteString("\n---\n\n")
}
response.WriteString(fmt.Sprintf(`Go 代码:
%s
WasmPlugin 配置:
%s
复杂度: %s, 特性: %d, 警告: %d`,
result.GoCode,
result.WasmPluginYAML,
analyzer.Complexity,
len(analyzer.Features),
len(analyzer.Warnings)))
return response.String(), nil
}

View File

@@ -0,0 +1,491 @@
//go:build higress_integration
// +build higress_integration
package mcptools
import (
"encoding/json"
"fmt"
"log"
"nginx-migration-mcp/internal/rag"
"nginx-migration-mcp/tools"
"strings"
"github.com/alibaba/higress/plugins/golang-filter/mcp-session/common"
)
// RegisterNginxConfigTools 注册 Nginx 配置分析和转换工具
func RegisterNginxConfigTools(server *common.MCPServer, ctx *MigrationContext) {
RegisterSimpleTool(
server,
"parse_nginx_config",
"解析和分析 Nginx 配置文件,识别配置结构和复杂度",
map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"config_content": map[string]interface{}{
"type": "string",
"description": "Nginx 配置文件内容",
},
},
"required": []string{"config_content"},
},
func(args map[string]interface{}) (string, error) {
return parseNginxConfig(args, ctx)
},
)
RegisterSimpleTool(
server,
"convert_to_higress",
"将 Nginx 配置转换为 Higress Ingress 和 Service 资源(主要方式)或 HTTPRoute可选",
map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"config_content": map[string]interface{}{
"type": "string",
"description": "Nginx 配置文件内容",
},
"namespace": map[string]interface{}{
"type": "string",
"description": "目标 Kubernetes 命名空间",
"default": "default",
},
"use_gateway_api": map[string]interface{}{
"type": "boolean",
"description": "是否使用 Gateway API (HTTPRoute)。默认 false使用 Ingress",
"default": false,
},
},
"required": []string{"config_content"},
},
func(args map[string]interface{}) (string, error) {
return convertToHigress(args, ctx)
},
)
}
func parseNginxConfig(args map[string]interface{}, ctx *MigrationContext) (string, error) {
configContent, ok := args["config_content"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid config_content parameter")
}
// Simple analysis
serverCount := strings.Count(configContent, "server {")
locationCount := strings.Count(configContent, "location")
hasSSL := strings.Contains(configContent, "ssl")
hasProxy := strings.Contains(configContent, "proxy_pass")
hasRewrite := strings.Contains(configContent, "rewrite")
complexity := "Simple"
if serverCount > 1 || (hasRewrite && hasSSL) {
complexity = "Complex"
} else if hasRewrite || hasSSL {
complexity = "Medium"
}
// 收集配置特性用于 RAG 查询
features := []string{}
if hasProxy {
features = append(features, "反向代理")
}
if hasRewrite {
features = append(features, "URL重写")
}
if hasSSL {
features = append(features, "SSL配置")
}
// === RAG 增强:查询 Nginx 配置迁移最佳实践 ===
var ragContext *rag.RAGContext
if ctx.RAGManager != nil && ctx.RAGManager.IsEnabled() && len(features) > 0 {
query := fmt.Sprintf("Nginx %s 迁移到 Higress 的配置方法和最佳实践", strings.Join(features, "、"))
var err error
ragContext, err = ctx.RAGManager.QueryForTool("parse_nginx_config", query, "nginx_migration")
if err != nil {
log.Printf(" RAG query failed for parse_nginx_config: %v", err)
}
}
// 构建分析结果
var result strings.Builder
// RAG 上下文(如果有)
if ragContext != nil && ragContext.Enabled && len(ragContext.Documents) > 0 {
result.WriteString("📚 知识库迁移指南:\n\n")
result.WriteString(ragContext.FormatContextForAI())
result.WriteString("\n---\n\n")
}
result.WriteString(fmt.Sprintf(`Nginx配置分析结果
基础信息:
- Server块: %d个
- Location块: %d个
- SSL配置: %t
- 反向代理: %t
- URL重写: %t
复杂度: %s
迁移建议:`, serverCount, locationCount, hasSSL, hasProxy, hasRewrite, complexity))
if hasProxy {
result.WriteString("\n- 反向代理将转换为Ingress backend配置")
}
if hasRewrite {
result.WriteString("\n- URL重写将使用Higress注解 (higress.io/rewrite-target)")
}
if hasSSL {
result.WriteString("\n- SSL配置将转换为Ingress TLS配置")
}
return result.String(), nil
}
func convertToHigress(args map[string]interface{}, ctx *MigrationContext) (string, error) {
configContent, ok := args["config_content"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid config_content parameter")
}
namespace := ctx.DefaultNamespace
if ns, ok := args["namespace"].(string); ok && ns != "" {
namespace = ns
}
// 检查是否使用 Gateway API
useGatewayAPI := false
if val, ok := args["use_gateway_api"].(bool); ok {
useGatewayAPI = val
}
// === 使用增强的解析器解析 Nginx 配置 ===
nginxConfig, err := tools.ParseNginxConfig(configContent)
if err != nil {
return "", fmt.Errorf("failed to parse Nginx config: %v", err)
}
// 分析配置
analysis := tools.AnalyzeNginxConfig(nginxConfig)
// === RAG 增强:查询转换示例和最佳实践 ===
var ragContext string
if ctx.RAGManager != nil && ctx.RAGManager.IsEnabled() {
// 构建查询关键词
queryBuilder := []string{"Nginx 配置转换到 Higress"}
if useGatewayAPI {
queryBuilder = append(queryBuilder, "Gateway API HTTPRoute")
} else {
queryBuilder = append(queryBuilder, "Kubernetes Ingress")
}
// 根据特性添加查询关键词
if analysis.Features["ssl"] {
queryBuilder = append(queryBuilder, "SSL TLS 证书配置")
}
if analysis.Features["rewrite"] {
queryBuilder = append(queryBuilder, "URL 重写 rewrite 规则")
}
if analysis.Features["redirect"] {
queryBuilder = append(queryBuilder, "重定向 redirect")
}
if analysis.Features["header_manipulation"] {
queryBuilder = append(queryBuilder, "请求头 响应头处理")
}
if len(nginxConfig.Upstreams) > 0 {
queryBuilder = append(queryBuilder, "负载均衡 upstream")
}
queryString := strings.Join(queryBuilder, " ")
log.Printf("🔍 RAG Query: %s", queryString)
ragResult, err := ctx.RAGManager.QueryForTool(
"convert_to_higress",
queryString,
"nginx_to_higress",
)
if err == nil && ragResult.Enabled && len(ragResult.Documents) > 0 {
log.Printf("✅ RAG: Found %d documents for conversion", len(ragResult.Documents))
ragContext = "\n\n## 📚 参考文档(来自知识库)\n\n" + ragResult.FormatContextForAI()
} else {
if err != nil {
log.Printf("⚠️ RAG query failed: %v", err)
}
}
}
// === 将配置数据转换为 JSON 供 AI 使用 ===
configJSON, _ := json.MarshalIndent(nginxConfig, "", " ")
analysisJSON, _ := json.MarshalIndent(analysis, "", " ")
// === 构建返回消息 ===
var result strings.Builder
result.WriteString(fmt.Sprintf(`📋 Nginx 配置解析完成
## 配置概览
- Server 块: %d
- Location 块: %d
- 域名: %d 个
- 复杂度: %s
- 目标格式: %s
- 命名空间: %s
## 检测到的特性
%s
## 迁移建议
%s
%s
---
## Nginx 配置结构
`+"```json"+`
%s
`+"```"+`
## 分析结果
`+"```json"+`
%s
`+"```"+`
%s
`,
analysis.ServerCount,
analysis.LocationCount,
analysis.DomainCount,
analysis.Complexity,
func() string {
if useGatewayAPI {
return "Gateway API (HTTPRoute)"
}
return "Kubernetes Ingress"
}(),
namespace,
formatFeaturesForOutput(analysis.Features),
formatSuggestionsForOutput(analysis.Suggestions),
func() string {
if ragContext != "" {
return "\n\n✅ 已加载知识库参考文档"
}
return ""
}(),
string(configJSON),
string(analysisJSON),
ragContext,
))
return result.String(), nil
}
// generateIngressConfig 生成 Ingress 资源配置(主要方式)
func generateIngressConfig(ingressName, namespace, hostname, serviceName string, ctx *MigrationContext) string {
return fmt.Sprintf(`转换后的Higress配置使用 Ingress - 推荐方式)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: %s
namespace: %s
annotations:
higress.io/migrated-from: "nginx"
higress.io/ingress.class: "higress"
spec:
ingressClassName: higress
rules:
- host: %s
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: %s
port:
number: %d
---
apiVersion: v1
kind: Service
metadata:
name: %s
namespace: %s
spec:
selector:
app: backend
ports:
- port: %d
targetPort: %d
protocol: TCP
转换完成
应用步骤:
1. 保存为 higress-config.yaml
2. 执行: kubectl apply -f higress-config.yaml
3. 验证: kubectl get ingress -n %s
说明:
- 使用 Ingress 是 Higress 的主要使用方式,兼容性最好
- 如需使用 Gateway API (HTTPRoute),请设置参数 use_gateway_api=true`,
ingressName, namespace,
hostname,
serviceName, ctx.ServicePort,
serviceName, namespace,
ctx.ServicePort, ctx.TargetPort,
namespace)
}
// generateHTTPRouteConfig 生成 HTTPRoute 资源配置(备用选项)
func generateHTTPRouteConfig(routeName, namespace, hostname, serviceName string, ctx *MigrationContext) string {
return fmt.Sprintf(`转换后的Higress配置使用 Gateway API - 可选方式)
注意: Gateway API 在 Higress 中默认关闭,使用前需要确认已启用。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: %s
namespace: %s
annotations:
higress.io/migrated-from: "nginx"
spec:
parentRefs:
- name: %s
namespace: %s
hostnames:
- %s
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: %s
port: %d
---
apiVersion: v1
kind: Service
metadata:
name: %s
namespace: %s
spec:
selector:
app: backend
ports:
- port: %d
targetPort: %d
protocol: TCP
转换完成
应用步骤:
1. 确认 Gateway API 已启用: PILOT_ENABLE_GATEWAY_API=true
2. 保存为 higress-config.yaml
3. 执行: kubectl apply -f higress-config.yaml
4. 验证: kubectl get httproute -n %s
说明:
- Gateway API 是可选功能,默认关闭
- 推荐使用 Ingress (设置 use_gateway_api=false)`,
routeName, namespace,
ctx.GatewayName, ctx.GatewayNamespace, hostname,
serviceName, ctx.ServicePort,
serviceName, namespace,
ctx.ServicePort, ctx.TargetPort,
namespace)
}
func generateIngressName(hostname string, ctx *MigrationContext) string {
prefix := "nginx-migrated"
if ctx.RoutePrefix != "" {
prefix = ctx.RoutePrefix
}
if hostname == "" || hostname == ctx.DefaultHostname {
return fmt.Sprintf("%s-ingress", prefix)
}
// Replace dots and special characters for valid k8s name
safeName := hostname
for _, char := range []string{".", "_", ":"} {
safeName = strings.ReplaceAll(safeName, char, "-")
}
return fmt.Sprintf("%s-%s", prefix, safeName)
}
func generateRouteName(hostname string, ctx *MigrationContext) string {
prefix := "nginx-migrated"
if ctx.RoutePrefix != "" {
prefix = ctx.RoutePrefix
}
if hostname == "" || hostname == ctx.DefaultHostname {
return fmt.Sprintf("%s-route", prefix)
}
// Replace dots and special characters for valid k8s name
safeName := hostname
for _, char := range []string{".", "_", ":"} {
safeName = strings.ReplaceAll(safeName, char, "-")
}
return fmt.Sprintf("%s-%s", prefix, safeName)
}
func generateServiceName(hostname string, ctx *MigrationContext) string {
if hostname == "" || hostname == ctx.DefaultHostname {
return "backend-service"
}
safeName := hostname
for _, char := range []string{".", "_", ":"} {
safeName = strings.ReplaceAll(safeName, char, "-")
}
return fmt.Sprintf("%s-service", safeName)
}
// formatFeaturesForOutput 格式化特性列表用于输出
func formatFeaturesForOutput(features map[string]bool) string {
featureNames := map[string]string{
"ssl": "SSL/TLS 加密",
"proxy": "反向代理",
"rewrite": "URL 重写",
"redirect": "重定向",
"return": "返回指令",
"complex_routing": "复杂路由匹配",
"header_manipulation": "请求头操作",
"response_headers": "响应头操作",
}
var result []string
for key, enabled := range features {
if enabled {
if name, ok := featureNames[key]; ok {
result = append(result, fmt.Sprintf("- ✅ %s", name))
} else {
result = append(result, fmt.Sprintf("- ✅ %s", key))
}
}
}
if len(result) == 0 {
return "- 基础配置(无特殊特性)"
}
return strings.Join(result, "\n")
}
// formatSuggestionsForOutput 格式化建议列表用于输出
func formatSuggestionsForOutput(suggestions []string) string {
if len(suggestions) == 0 {
return "- 无特殊建议"
}
var result []string
for _, s := range suggestions {
result = append(result, fmt.Sprintf("- 💡 %s", s))
}
return strings.Join(result, "\n")
}

View File

@@ -0,0 +1,318 @@
// Package mcptools 提供 RAG 集成到 MCP 工具的示例实现
package mcptools
import (
"fmt"
"log"
"strings"
"nginx-migration-mcp/internal/rag"
)
// RAGToolContext MCP 工具的 RAG 上下文
type RAGToolContext struct {
Manager *rag.RAGManager
}
// NewRAGToolContext 创建 RAG 工具上下文
func NewRAGToolContext(configPath string) (*RAGToolContext, error) {
// 加载配置
config, err := rag.LoadRAGConfig(configPath)
if err != nil {
log.Printf("⚠️ Failed to load RAG config: %v, RAG will be disabled", err)
// 创建禁用状态的配置
config = &rag.RAGConfig{Enabled: false}
}
// 创建 RAG 管理器
manager := rag.NewRAGManager(config)
return &RAGToolContext{
Manager: manager,
}, nil
}
// ==================== 工具示例generate_conversion_hints ====================
// GenerateConversionHintsWithRAG 生成转换提示(带 RAG 增强)
func (ctx *RAGToolContext) GenerateConversionHintsWithRAG(analysisResult string, pluginName string) (string, error) {
var result strings.Builder
result.WriteString(fmt.Sprintf("# %s 插件转换指南\n\n", pluginName))
// 提取 Nginx APIs这里简化处理
nginxAPIs := extractNginxAPIs(analysisResult)
// === 核心:使用工具级别的 RAG 查询 ===
toolName := "generate_conversion_hints"
ragContext, err := ctx.Manager.QueryForTool(
toolName,
fmt.Sprintf("Nginx Lua API %s 在 Higress WASM 中的实现和转换方法", strings.Join(nginxAPIs, ", ")),
"lua_migration",
)
if err != nil {
log.Printf("❌ RAG query failed for %s: %v", toolName, err)
// 降级到规则生成
ragContext = &rag.RAGContext{
Enabled: false,
Message: fmt.Sprintf("RAG query failed: %v", err),
}
}
// 添加 RAG 上下文信息(如果有)
if ragContext.Enabled && len(ragContext.Documents) > 0 {
result.WriteString(ragContext.FormatContextForAI())
} else {
// RAG 未启用或查询失败
result.WriteString(fmt.Sprintf("> %s\n\n", ragContext.Message))
result.WriteString("> 使用基于规则的转换指南\n\n")
}
// 为每个 API 生成转换提示(基于规则)
result.WriteString("## 🔄 API 转换详情\n\n")
for _, api := range nginxAPIs {
result.WriteString(fmt.Sprintf("### %s\n\n", api))
result.WriteString(generateBasicMapping(api))
result.WriteString("\n")
}
// 添加使用建议
result.WriteString("\n---\n\n")
result.WriteString("## 💡 使用建议\n\n")
if ragContext.Enabled {
result.WriteString("✅ 上述参考文档来自 Higress 官方知识库,请参考这些文档中的示例代码和最佳实践来生成 WASM 插件代码。\n\n")
result.WriteString("建议按照知识库中的示例实现,确保代码符合 Higress 的最佳实践。\n")
} else {
result.WriteString(" 当前未启用 RAG 知识库或查询失败,使用基于规则的映射。\n\n")
result.WriteString("建议参考 Higress 官方文档https://higress.cn/docs/plugins/wasm-go-sdk/\n")
}
return result.String(), nil
}
// ==================== 工具示例validate_wasm_code ====================
// ValidateWasmCodeWithRAG 验证 WASM 代码(带 RAG 增强)
func (ctx *RAGToolContext) ValidateWasmCodeWithRAG(goCode string, pluginName string) (string, error) {
var result strings.Builder
result.WriteString(fmt.Sprintf("## 🔍 %s 插件代码验证报告\n\n", pluginName))
// 基本验证(始终执行)
basicIssues := validateBasicSyntax(goCode)
apiIssues := validateAPIUsage(goCode)
if len(basicIssues) > 0 {
result.WriteString("### ⚠️ 语法问题\n\n")
for _, issue := range basicIssues {
result.WriteString(fmt.Sprintf("- %s\n", issue))
}
result.WriteString("\n")
}
if len(apiIssues) > 0 {
result.WriteString("### ⚠️ API 使用问题\n\n")
for _, issue := range apiIssues {
result.WriteString(fmt.Sprintf("- %s\n", issue))
}
result.WriteString("\n")
}
// === RAG 增强:查询最佳实践 ===
toolName := "validate_wasm_code"
ragContext, err := ctx.Manager.QueryForTool(
toolName,
"Higress WASM 插件开发最佳实践 错误处理 性能优化 代码规范",
"best_practice",
)
if err != nil {
log.Printf("❌ RAG query failed for %s: %v", toolName, err)
ragContext = &rag.RAGContext{
Enabled: false,
Message: fmt.Sprintf("RAG query failed: %v", err),
}
}
// 添加最佳实践建议
if ragContext.Enabled && len(ragContext.Documents) > 0 {
result.WriteString("### 💡 最佳实践建议(基于知识库)\n\n")
for i, doc := range ragContext.Documents {
result.WriteString(fmt.Sprintf("#### 建议 %d%s\n\n", i+1, doc.Title))
result.WriteString(fmt.Sprintf("**来源**: %s \n", doc.Source))
if doc.URL != "" {
result.WriteString(fmt.Sprintf("**链接**: %s \n", doc.URL))
}
result.WriteString("\n")
// 只展示关键片段validate 工具通常配置为 highlights 模式)
if len(doc.Highlights) > 0 {
result.WriteString("**关键要点**:\n\n")
for _, h := range doc.Highlights {
result.WriteString(fmt.Sprintf("- %s\n", h))
}
} else {
result.WriteString("**参考内容**:\n\n")
result.WriteString("```\n")
result.WriteString(doc.Content)
result.WriteString("\n```\n")
}
result.WriteString("\n")
}
// 基于知识库内容检查当前代码
suggestions := checkCodeAgainstBestPractices(goCode, ragContext.Documents)
if len(suggestions) > 0 {
result.WriteString("### 📝 针对当前代码的改进建议\n\n")
for _, s := range suggestions {
result.WriteString(fmt.Sprintf("- %s\n", s))
}
result.WriteString("\n")
}
} else {
result.WriteString("### 💡 基本建议\n\n")
result.WriteString(fmt.Sprintf("> %s\n\n", ragContext.Message))
result.WriteString(generateBasicValidationSuggestions(goCode))
}
// 验证总结
if len(basicIssues) == 0 && len(apiIssues) == 0 {
result.WriteString("\n---\n\n")
result.WriteString("### ✅ 验证通过\n\n")
result.WriteString("代码基本验证通过,没有发现明显问题。\n")
}
return result.String(), nil
}
// ==================== 工具示例convert_lua_to_wasm ====================
// ConvertLuaToWasmWithRAG 快速转换(通常不使用 RAG
func (ctx *RAGToolContext) ConvertLuaToWasmWithRAG(luaCode string, pluginName string) (string, error) {
// 这个工具在配置中通常设置为 use_rag: false保持快速响应
toolName := "convert_lua_to_wasm"
// 仍然可以查询,但如果配置禁用则会快速返回
ragContext, _ := ctx.Manager.QueryForTool(
toolName,
"Lua to WASM conversion examples",
"quick_convert",
)
var result strings.Builder
result.WriteString(fmt.Sprintf("# %s 插件转换结果\n\n", pluginName))
if ragContext.Enabled {
result.WriteString("> 🚀 使用 RAG 增强转换\n\n")
result.WriteString(ragContext.FormatContextForAI())
} else {
result.WriteString("> ⚡ 快速转换模式(未启用 RAG\n\n")
}
// 执行基于规则的转换
wasmCode := performRuleBasedConversion(luaCode, pluginName)
result.WriteString("## 生成的 Go WASM 代码\n\n")
result.WriteString("```go\n")
result.WriteString(wasmCode)
result.WriteString("\n```\n")
return result.String(), nil
}
// ==================== 辅助函数 ====================
func extractNginxAPIs(analysisResult string) []string {
// 简化实现:从分析结果中提取 API
apis := []string{"ngx.req.get_headers", "ngx.say", "ngx.var"}
return apis
}
func generateBasicMapping(api string) string {
mappings := map[string]string{
"ngx.req.get_headers": "**Higress WASM**: `proxywasm.GetHttpRequestHeaders()`\n\n示例\n```go\nheaders, err := proxywasm.GetHttpRequestHeaders()\nif err != nil {\n proxywasm.LogError(\"failed to get headers\")\n return types.ActionContinue\n}\n```",
"ngx.say": "**Higress WASM**: `proxywasm.SendHttpResponse()`",
"ngx.var": "**Higress WASM**: `proxywasm.GetProperty()`",
}
if mapping, ok := mappings[api]; ok {
return mapping
}
return "映射信息暂未提供,请参考官方文档。"
}
func validateBasicSyntax(goCode string) []string {
// 简化实现
issues := []string{}
if !strings.Contains(goCode, "package main") {
issues = append(issues, "缺少 package main 声明")
}
return issues
}
func validateAPIUsage(goCode string) []string {
// 简化实现
issues := []string{}
if strings.Contains(goCode, "proxywasm.") && !strings.Contains(goCode, "import") {
issues = append(issues, "使用了 proxywasm API 但未导入相关包")
}
return issues
}
func checkCodeAgainstBestPractices(goCode string, docs []rag.ContextDocument) []string {
// 简化实现:基于文档内容检查代码
suggestions := []string{}
// 检查错误处理
if !strings.Contains(goCode, "if err != nil") {
for _, doc := range docs {
if strings.Contains(doc.Content, "错误处理") || strings.Contains(doc.Content, "error handling") {
suggestions = append(suggestions, "建议添加完善的错误处理逻辑(参考知识库文档)")
break
}
}
}
// 检查日志记录
if !strings.Contains(goCode, "proxywasm.Log") {
suggestions = append(suggestions, "建议添加适当的日志记录以便调试")
}
return suggestions
}
func generateBasicValidationSuggestions(goCode string) string {
return "- 确保所有 API 调用都有错误处理\n" +
"- 添加必要的日志记录\n" +
"- 遵循 Higress WASM 插件开发规范\n"
}
func performRuleBasedConversion(luaCode string, pluginName string) string {
// 简化实现:基于规则的转换
return fmt.Sprintf(`package main
import (
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
"github.com/higress-group/wasm-go/pkg/wrapper"
)
func main() {
wrapper.SetCtx(
"%s",
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
)
}
func onHttpRequestHeaders(ctx wrapper.HttpContext, config PluginConfig, log wrapper.Log) types.Action {
// TODO: 实现转换逻辑
// 原始 Lua 代码:
// %s
return types.ActionContinue
}
type PluginConfig struct {
// TODO: 添加配置字段
}
`, pluginName, luaCode)
}

View File

@@ -0,0 +1,304 @@
//go:build higress_integration
// +build higress_integration
package mcptools
import (
"encoding/json"
"fmt"
"strings"
"nginx-migration-mcp/tools"
"github.com/alibaba/higress/plugins/golang-filter/mcp-session/common"
)
// RegisterToolChainTools 注册工具链相关的工具
func RegisterToolChainTools(server *common.MCPServer, ctx *MigrationContext) {
RegisterSimpleTool(
server,
"generate_conversion_hints",
"基于 Lua 分析结果生成代码转换模板",
map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"analysis_result": map[string]interface{}{
"type": "string",
"description": "analyze_lua_plugin 返回的 JSON 格式分析结果",
},
"plugin_name": map[string]interface{}{
"type": "string",
"description": "目标插件名称(小写字母和连字符)",
},
},
"required": []string{"analysis_result", "plugin_name"},
},
func(args map[string]interface{}) (string, error) {
return generateConversionHints(args, ctx)
},
)
RegisterSimpleTool(
server,
"validate_wasm_code",
"验证生成的 Go WASM 插件代码检查语法、API 使用和配置结构",
map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"go_code": map[string]interface{}{
"type": "string",
"description": "生成的 Go WASM 插件代码",
},
"plugin_name": map[string]interface{}{
"type": "string",
"description": "插件名称",
},
},
"required": []string{"go_code", "plugin_name"},
},
func(args map[string]interface{}) (string, error) {
return validateWasmCode(args, ctx)
},
)
RegisterSimpleTool(
server,
"generate_deployment_config",
"为验证通过的 WASM 插件生成完整的部署配置包",
map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"plugin_name": map[string]interface{}{
"type": "string",
"description": "插件名称",
},
"go_code": map[string]interface{}{
"type": "string",
"description": "验证通过的 Go 代码",
},
"config_schema": map[string]interface{}{
"type": "string",
"description": "配置 JSON Schema可选",
},
"namespace": map[string]interface{}{
"type": "string",
"description": "部署命名空间",
"default": "higress-system",
},
},
"required": []string{"plugin_name", "go_code"},
},
func(args map[string]interface{}) (string, error) {
return generateDeploymentConfig(args, ctx)
},
)
}
func generateConversionHints(args map[string]interface{}, ctx *MigrationContext) (string, error) {
analysisResultStr, ok := args["analysis_result"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid analysis_result parameter")
}
pluginName, ok := args["plugin_name"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid plugin_name parameter")
}
// 解析分析结果
var analysis tools.AnalysisResultForAI
if err := json.Unmarshal([]byte(analysisResultStr), &analysis); err != nil {
return "", fmt.Errorf("failed to parse analysis_result: %w", err)
}
// 生成转换提示
hints := tools.GenerateConversionHints(analysis, pluginName)
// === RAG 增强(如果启用)===
var ragInfo map[string]interface{}
if ctx.RAGManager != nil && ctx.RAGManager.IsEnabled() {
// 构建智能查询语句
queryBuilder := []string{}
if len(analysis.APICalls) > 0 {
queryBuilder = append(queryBuilder, "Nginx Lua API 转换到 Higress WASM")
hasHeaderOps := analysis.Features["header_manipulation"] || analysis.Features["request_headers"] || analysis.Features["response_headers"]
hasBodyOps := analysis.Features["request_body"] || analysis.Features["response_body"]
hasResponseControl := analysis.Features["response_control"]
if hasHeaderOps {
queryBuilder = append(queryBuilder, "请求头和响应头处理")
}
if hasBodyOps {
queryBuilder = append(queryBuilder, "请求体和响应体处理")
}
if hasResponseControl {
queryBuilder = append(queryBuilder, "响应控制和状态码设置")
}
if len(analysis.APICalls) > 0 && len(analysis.APICalls) <= 5 {
queryBuilder = append(queryBuilder, fmt.Sprintf("涉及 API: %s", strings.Join(analysis.APICalls, ", ")))
}
} else {
queryBuilder = append(queryBuilder, "Higress WASM 插件开发 基础示例 Go SDK 使用")
}
if analysis.Complexity == "high" {
queryBuilder = append(queryBuilder, "复杂插件实现 高级功能")
}
queryString := strings.Join(queryBuilder, " ")
ragContext, err := ctx.RAGManager.QueryForTool(
"generate_conversion_hints",
queryString,
"lua_migration",
)
if err == nil && ragContext.Enabled && len(ragContext.Documents) > 0 {
ragInfo = map[string]interface{}{
"enabled": true,
"documents": len(ragContext.Documents),
"context": ragContext.FormatContextForAI(),
}
}
}
// 组合结果
result := map[string]interface{}{
"code_template": hints.CodeTemplate,
"warnings": hints.Warnings,
"rag": ragInfo,
}
// 返回 JSON 结果,由 LLM 解释和使用
resultJSON, _ := json.MarshalIndent(result, "", " ")
return string(resultJSON), nil
}
func validateWasmCode(args map[string]interface{}, ctx *MigrationContext) (string, error) {
goCode, ok := args["go_code"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid go_code parameter")
}
pluginName, ok := args["plugin_name"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid plugin_name parameter")
}
// 执行验证AI 驱动)
report := tools.ValidateWasmCode(goCode, pluginName)
// 格式化输出,包含 AI 分析提示和基础信息
var result strings.Builder
result.WriteString(fmt.Sprintf("## 代码验证报告\n\n"))
result.WriteString(fmt.Sprintf("代码存在 %d 个必须修复的问题,%d 个建议修复的问题,%d 个可选优化项,%d 个最佳实践建议。请优先解决必须修复的问题。\n\n", 0, 0, 0, 0))
result.WriteString(fmt.Sprintf("### 发现的回调函数 (%d 个)\n", len(report.FoundCallbacks)))
if len(report.FoundCallbacks) > 0 {
for _, cb := range report.FoundCallbacks {
result.WriteString(fmt.Sprintf("- %s\n", cb))
}
} else {
result.WriteString("无\n")
}
result.WriteString("\n")
result.WriteString("### 配置结构\n")
if report.HasConfig {
result.WriteString(" 已定义配置结构体\n\n")
} else {
result.WriteString(" 未定义配置结构体\n\n")
}
result.WriteString("### 问题分类\n\n")
result.WriteString("#### 必须修复 (0 个)\n")
result.WriteString("无\n\n")
result.WriteString("#### 建议修复 (0 个)\n")
result.WriteString("无\n\n")
result.WriteString("#### 可选优化 (0 个)\n")
result.WriteString("无\n\n")
result.WriteString("#### 最佳实践 (0 个)\n")
result.WriteString("无\n\n")
// 添加 AI 分析提示
result.WriteString("---\n\n")
result.WriteString(report.Summary)
result.WriteString("\n\n")
// === RAG 增强:查询最佳实践 ===
if ctx.RAGManager != nil && ctx.RAGManager.IsEnabled() {
// 构建智能查询语句
queryBuilder := []string{"Higress WASM 插件"}
// 根据回调函数类型添加特定查询
for _, callback := range report.FoundCallbacks {
if strings.Contains(callback, "RequestHeaders") {
queryBuilder = append(queryBuilder, "请求头处理")
}
if strings.Contains(callback, "RequestBody") {
queryBuilder = append(queryBuilder, "请求体处理")
}
if strings.Contains(callback, "ResponseHeaders") {
queryBuilder = append(queryBuilder, "响应头处理")
}
}
queryBuilder = append(queryBuilder, "性能优化 最佳实践 错误处理")
queryString := strings.Join(queryBuilder, " ")
ragContext, err := ctx.RAGManager.QueryForTool(
"validate_wasm_code",
queryString,
"best_practice",
)
if err == nil && ragContext.Enabled && len(ragContext.Documents) > 0 {
result.WriteString("\n\n### 📚 最佳实践建议(来自知识库)\n\n")
result.WriteString(ragContext.FormatContextForAI())
}
}
// 添加 JSON 格式的结构化数据(供后续处理)
reportJSON, _ := json.MarshalIndent(report, "", " ")
result.WriteString("\n")
result.WriteString(string(reportJSON))
return result.String(), nil
}
func generateDeploymentConfig(args map[string]interface{}, ctx *MigrationContext) (string, error) {
pluginName, ok := args["plugin_name"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid plugin_name parameter")
}
_, ok = args["go_code"].(string)
if !ok {
return "", fmt.Errorf("missing or invalid go_code parameter")
}
namespace := "higress-system"
if ns, ok := args["namespace"].(string); ok && ns != "" {
namespace = ns
}
// configSchema is optional, we don't use it for now but don't return error
_ = args["config_schema"]
// 返回提示信息,由 LLM 生成具体配置文件
result := fmt.Sprintf(`为插件 %s 生成以下部署配置:
1. WasmPlugin YAML (namespace: %s)
2. Makefile (TinyGo 构建)
3. Dockerfile
4. README.md
5. 测试脚本
参考文档: https://higress.cn/docs/latest/user/wasm-go/`, pluginName, namespace)
return result, nil
}

View File

@@ -0,0 +1,126 @@
//go:build higress_integration
// +build higress_integration
package nginx_migration
import (
"errors"
"nginx-migration-mcp/integration/mcptools"
"nginx-migration-mcp/internal/rag"
"github.com/alibaba/higress/plugins/golang-filter/mcp-session/common"
"github.com/envoyproxy/envoy/contrib/golang/common/go/api"
)
const Version = "1.0.0"
func init() {
common.GlobalRegistry.RegisterServer("nginx-migration", &NginxMigrationConfig{})
}
// NginxMigrationConfig holds configuration for the Nginx Migration MCP Server
type NginxMigrationConfig struct {
gatewayName string
gatewayNamespace string
defaultNamespace string
defaultHostname string
description string
ragConfigPath string // RAG 配置文件路径
}
// ParseConfig parses the configuration map for the Nginx Migration server
func (c *NginxMigrationConfig) ParseConfig(config map[string]interface{}) error {
// Optional configurations with defaults
if gatewayName, ok := config["gatewayName"].(string); ok {
c.gatewayName = gatewayName
} else {
c.gatewayName = "higress-gateway"
}
if gatewayNamespace, ok := config["gatewayNamespace"].(string); ok {
c.gatewayNamespace = gatewayNamespace
} else {
c.gatewayNamespace = "higress-system"
}
if defaultNamespace, ok := config["defaultNamespace"].(string); ok {
c.defaultNamespace = defaultNamespace
} else {
c.defaultNamespace = "default"
}
if defaultHostname, ok := config["defaultHostname"].(string); ok {
c.defaultHostname = defaultHostname
} else {
c.defaultHostname = "example.com"
}
if desc, ok := config["description"].(string); ok {
c.description = desc
} else {
c.description = "Nginx Migration MCP Server - Convert Nginx configs and Lua plugins to Higress"
}
// RAG 配置路径(可选)
if ragPath, ok := config["ragConfigPath"].(string); ok {
c.ragConfigPath = ragPath
} else {
c.ragConfigPath = "config/rag.json" // 默认路径
}
api.LogDebugf("NginxMigrationConfig ParseConfig: gatewayName=%s, gatewayNamespace=%s, defaultNamespace=%s, ragConfig=%s",
c.gatewayName, c.gatewayNamespace, c.defaultNamespace, c.ragConfigPath)
return nil
}
// NewServer creates a new MCP server instance for Nginx Migration
func (c *NginxMigrationConfig) NewServer(serverName string) (*common.MCPServer, error) {
if serverName == "" {
return nil, errors.New("server name cannot be empty")
}
mcpServer := common.NewMCPServer(
serverName,
Version,
common.WithInstructions("Nginx Migration MCP Server: Analyze and convert Nginx configurations and Lua plugins to Higress"),
)
// Create migration context with configuration
migrationCtx := &mcptools.MigrationContext{
GatewayName: c.gatewayName,
GatewayNamespace: c.gatewayNamespace,
DefaultNamespace: c.defaultNamespace,
DefaultHostname: c.defaultHostname,
}
// 初始化 RAG Manager如果配置了
if c.ragConfigPath != "" {
api.LogInfof("Loading RAG config from: %s", c.ragConfigPath)
ragConfig, err := rag.LoadRAGConfig(c.ragConfigPath)
if err != nil {
api.LogWarnf("Failed to load RAG config: %v, RAG will be disabled", err)
// 不返回错误,继续使用无 RAG 的模式
ragConfig = &rag.RAGConfig{Enabled: false}
}
// 创建 RAG Manager
migrationCtx.RAGManager = rag.NewRAGManager(ragConfig)
if migrationCtx.RAGManager.IsEnabled() {
api.LogInfof("✅ RAG enabled for Nginx Migration MCP Server")
} else {
api.LogInfof("📖 RAG disabled, using rule-based approach")
}
}
// Register all migration tools
mcptools.RegisterNginxConfigTools(mcpServer, migrationCtx)
mcptools.RegisterLuaPluginTools(mcpServer, migrationCtx)
mcptools.RegisterToolChainTools(mcpServer, migrationCtx)
api.LogInfof("Nginx Migration MCP Server initialized: %s (tools registered)", serverName)
return mcpServer, nil
}

View File

@@ -0,0 +1,329 @@
// Package rag 提供基于阿里云官方 SDK 的 RAG 客户端实现
package rag
import (
"fmt"
"log"
"sync"
"time"
bailian "github.com/alibabacloud-go/bailian-20231229/v2/client"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
)
// RAGQuery RAG 查询请求
type RAGQuery struct {
Query string `json:"query"` // 查询文本
Scenario string `json:"scenario"` // 场景标识
TopK int `json:"top_k"` // 返回文档数量
ContextMode string `json:"context_mode"` // 上下文模式
Filters map[string]interface{} `json:"filters"` // 过滤条件
}
// RAGResponse RAG 查询响应
type RAGResponse struct {
Documents []RAGDocument `json:"documents"` // 检索到的文档
Latency int64 `json:"latency"` // 查询延迟(毫秒)
}
// RAGDocument 表示一个检索到的文档
type RAGDocument struct {
Title string `json:"title"` // 文档标题
Content string `json:"content"` // 文档内容
Source string `json:"source"` // 来源路径
URL string `json:"url"` // 在线链接
Score float64 `json:"score"` // 相关度分数
Highlights []string `json:"highlights"` // 高亮片段
}
// RAGClient 使用阿里云官方 SDK 的 RAG 客户端
type RAGClient struct {
config *RAGConfig
client *bailian.Client
cache *QueryCache
}
// NewRAGClient 创建基于 SDK 的 RAG 客户端
func NewRAGClient(config *RAGConfig) (*RAGClient, error) {
// 创建 SDK 配置
sdkConfig := &openapi.Config{
AccessKeyId: tea.String(config.AccessKeyID),
AccessKeySecret: tea.String(config.AccessKeySecret),
}
// 设置端点(默认为北京区域)
if config.Endpoint != "" {
sdkConfig.Endpoint = tea.String(config.Endpoint)
} else {
sdkConfig.Endpoint = tea.String("bailian.cn-beijing.aliyuncs.com")
}
// 创建客户端
client, err := bailian.NewClient(sdkConfig)
if err != nil {
return nil, fmt.Errorf("failed to create Bailian SDK client: %w", err)
}
c := &RAGClient{
config: config,
client: client,
}
// 初始化缓存
if config.EnableCache {
c.cache = NewQueryCache(config.CacheMaxSize, time.Duration(config.CacheTTL)*time.Second)
}
return c, nil
}
// SearchWithCache 查询知识库(带缓存)
func (c *RAGClient) SearchWithCache(query *RAGQuery) (*RAGResponse, error) {
// 检查缓存
if c.cache != nil {
cacheKey := c.buildCacheKey(query)
if cached := c.cache.Get(cacheKey); cached != nil {
if c.config.Debug {
log.Printf("🎯 RAG cache hit: %s", query.Query)
}
return cached, nil
}
}
// 执行查询
startTime := time.Now()
resp, err := c.search(query)
if err != nil {
return nil, err
}
// 记录延迟
resp.Latency = time.Since(startTime).Milliseconds()
// 缓存结果
if c.cache != nil {
cacheKey := c.buildCacheKey(query)
c.cache.Set(cacheKey, resp)
}
if c.config.Debug {
log.Printf("✅ RAG query completed: %s (latency: %dms, docs: %d)",
query.Query, resp.Latency, len(resp.Documents))
}
return resp, nil
}
// search 执行实际的查询(带重试)
func (c *RAGClient) search(query *RAGQuery) (*RAGResponse, error) {
var lastErr error
for attempt := 0; attempt <= c.config.MaxRetries; attempt++ {
if attempt > 0 {
// 重试前等待
time.Sleep(time.Duration(c.config.RetryDelay) * time.Second)
log.Printf("🔄 Retrying RAG query (attempt %d/%d)", attempt, c.config.MaxRetries)
}
resp, err := c.doSearchSDK(query)
if err == nil {
return resp, nil
}
lastErr = err
}
return nil, fmt.Errorf("RAG query failed after %d retries: %w", c.config.MaxRetries, lastErr)
}
// doSearchSDK 执行单次查询(使用 SDK
func (c *RAGClient) doSearchSDK(query *RAGQuery) (*RAGResponse, error) {
// 构建检索请求
request := &bailian.RetrieveRequest{
IndexId: tea.String(c.config.KnowledgeBaseID),
Query: tea.String(query.Query),
}
// 设置可选参数
if query.TopK > 0 {
request.DenseSimilarityTopK = tea.Int32(int32(query.TopK))
} else {
request.DenseSimilarityTopK = tea.Int32(int32(c.config.DefaultTopK))
}
// 启用重排序
request.EnableReranking = tea.Bool(true)
// 准备请求头和运行时选项
headers := make(map[string]*string)
runtime := &util.RuntimeOptions{}
// 调用 SDK 检索接口
response, err := c.client.RetrieveWithOptions(
tea.String(c.config.WorkspaceID),
request,
headers,
runtime,
)
if err != nil {
return nil, fmt.Errorf("SDK retrieve failed: %w", err)
}
// 检查响应
if response == nil || response.Body == nil {
return nil, fmt.Errorf("empty response from SDK")
}
if !tea.BoolValue(response.Body.Success) {
return nil, fmt.Errorf("SDK returned Success=false, Code=%s, Message=%s",
tea.StringValue(response.Body.Code),
tea.StringValue(response.Body.Message))
}
// 转换为 RAGResponse
ragResp := &RAGResponse{
Documents: make([]RAGDocument, 0),
}
if response.Body.Data != nil && response.Body.Data.Nodes != nil {
for _, node := range response.Body.Data.Nodes {
if node == nil {
continue
}
// 过滤低相关度文档
score := tea.Float64Value(node.Score)
if score < c.config.SimilarityThreshold {
continue
}
// 从 Metadata 中提取信息
title := ""
source := ""
url := ""
if node.Metadata != nil {
// Metadata 是 interface{} 类型,需要先转换为 map
if meta, ok := node.Metadata.(map[string]interface{}); ok {
if t, ok := meta["title"].(string); ok {
title = t
}
if s, ok := meta["doc_name"].(string); ok {
source = s
}
if u, ok := meta["file_path"].(string); ok {
url = u
}
}
}
ragResp.Documents = append(ragResp.Documents, RAGDocument{
Title: title,
Content: tea.StringValue(node.Text),
Source: source,
URL: url,
Score: score,
Highlights: []string{}, // SDK 不返回 highlights
})
}
}
return ragResp, nil
}
// buildCacheKey 构建缓存键
func (c *RAGClient) buildCacheKey(query *RAGQuery) string {
return fmt.Sprintf("%s:%s:top%d:%s", query.Scenario, query.Query, query.TopK, query.ContextMode)
}
// QueryCache 查询缓存
type QueryCache struct {
entries map[string]*CacheEntry
mu sync.RWMutex
maxSize int
ttl time.Duration
}
// CacheEntry 缓存条目
type CacheEntry struct {
Response *RAGResponse
ExpiresAt time.Time
}
// NewQueryCache 创建查询缓存
func NewQueryCache(maxSize int, ttl time.Duration) *QueryCache {
cache := &QueryCache{
entries: make(map[string]*CacheEntry),
maxSize: maxSize,
ttl: ttl,
}
// 启动清理协程
go cache.cleanupLoop()
return cache
}
// Get 获取缓存
func (c *QueryCache) Get(key string) *RAGResponse {
c.mu.RLock()
defer c.mu.RUnlock()
entry, ok := c.entries[key]
if !ok {
return nil
}
// 检查是否过期
if time.Now().After(entry.ExpiresAt) {
return nil
}
return entry.Response
}
// Set 设置缓存
func (c *QueryCache) Set(key string, resp *RAGResponse) {
c.mu.Lock()
defer c.mu.Unlock()
// 检查缓存大小
if len(c.entries) >= c.maxSize {
// 简单的 LRU删除第一个条目
for k := range c.entries {
delete(c.entries, k)
break
}
}
c.entries[key] = &CacheEntry{
Response: resp,
ExpiresAt: time.Now().Add(c.ttl),
}
}
// cleanupLoop 清理过期缓存
func (c *QueryCache) cleanupLoop() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
c.cleanup()
}
}
// cleanup 执行清理
func (c *QueryCache) cleanup() {
c.mu.Lock()
defer c.mu.Unlock()
now := time.Now()
for key, entry := range c.entries {
if now.After(entry.ExpiresAt) {
delete(c.entries, key)
}
}
}

View File

@@ -0,0 +1,188 @@
// Package rag 提供 RAG检索增强生成配置管理
package rag
import (
"encoding/json"
"fmt"
"os"
"strings"
)
// RAGConfig RAG 完整配置
type RAGConfig struct {
Enabled bool `json:"enabled" yaml:"enabled"` // RAG 功能总开关
// API 配置
Provider string `json:"provider" yaml:"provider"` // 服务提供商: "bailian"
Endpoint string `json:"endpoint" yaml:"endpoint"` // API 端点(如 bailian.cn-beijing.aliyuncs.com
WorkspaceID string `json:"workspace_id" yaml:"workspace_id"` // 业务空间 ID百炼必需
AccessKeyID string `json:"access_key_id" yaml:"access_key_id"` // 阿里云 AccessKey ID
AccessKeySecret string `json:"access_key_secret" yaml:"access_key_secret"` // 阿里云 AccessKey Secret
KnowledgeBaseID string `json:"knowledge_base_id" yaml:"knowledge_base_id"` // 知识库 IDIndexId
// 上下文配置
ContextMode string `json:"context_mode" yaml:"context_mode"` // full | summary | highlights
MaxContextLength int `json:"max_context_length" yaml:"max_context_length"` // 最大上下文长度(字符数)
// 检索配置
DefaultTopK int `json:"default_top_k" yaml:"default_top_k"` // 默认返回文档数量
SimilarityThreshold float64 `json:"similarity_threshold" yaml:"similarity_threshold"` // 相似度阈值
// 缓存配置
EnableCache bool `json:"enable_cache" yaml:"enable_cache"` // 是否启用缓存
CacheTTL int `json:"cache_ttl" yaml:"cache_ttl"` // 缓存过期时间(秒)
CacheMaxSize int `json:"cache_max_size" yaml:"cache_max_size"` // 最大缓存条目数
// 性能配置
Timeout int `json:"timeout" yaml:"timeout"` // 请求超时时间(秒)
MaxRetries int `json:"max_retries" yaml:"max_retries"` // 最大重试次数
RetryDelay int `json:"retry_delay" yaml:"retry_delay"` // 重试间隔(秒)
// 降级策略
FallbackOnError bool `json:"fallback_on_error" yaml:"fallback_on_error"` // RAG 失败时是否降级
// 工具级别配置(核心功能)
Tools map[string]*ToolConfig `json:"tools" yaml:"tools"`
// 调试模式
Debug bool `json:"debug" yaml:"debug"` // 是否启用调试日志
LogQueries bool `json:"log_queries" yaml:"log_queries"` // 是否记录所有查询
}
// ToolConfig 工具级别的 RAG 配置
type ToolConfig struct {
UseRAG bool `json:"use_rag" yaml:"use_rag"` // 是否使用 RAG
ContextMode string `json:"context_mode" yaml:"context_mode"` // 上下文模式(覆盖全局配置)
TopK int `json:"top_k" yaml:"top_k"` // 返回文档数量(覆盖全局配置)
}
// LoadRAGConfig 从配置文件加载 RAG 配置
// 注意:需要安装 YAML 库支持
// 运行go get gopkg.in/yaml.v3
//
// 临时实现:使用 JSON 格式配置文件
func LoadRAGConfig(configPath string) (*RAGConfig, error) {
// 读取配置文件
data, err := os.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}
// 简单的 YAML 到 JSON 转换(仅支持基本格式)
// 在生产环境中应使用真正的 YAML 解析器
jsonData := simpleYAMLToJSON(string(data))
// 解析 JSON
var wrapper struct {
RAG *RAGConfig `json:"rag"`
}
if err := json.Unmarshal([]byte(jsonData), &wrapper); err != nil {
return nil, fmt.Errorf("failed to parse config: %w", err)
}
if wrapper.RAG == nil {
return nil, fmt.Errorf("missing 'rag' section in config")
}
config := wrapper.RAG
// 展开环境变量
config.AccessKeyID = expandEnvVar(config.AccessKeyID)
config.AccessKeySecret = expandEnvVar(config.AccessKeySecret)
config.KnowledgeBaseID = expandEnvVar(config.KnowledgeBaseID)
config.WorkspaceID = expandEnvVar(config.WorkspaceID)
// 设置默认值
setDefaults(config)
return config, nil
}
// simpleYAMLToJSON 简单的 YAML 到 JSON 转换
// 注意:这是一个临时实现,仅支持基本的 YAML 格式
// 生产环境请使用 gopkg.in/yaml.v3
func simpleYAMLToJSON(yamlContent string) string {
trimmed := strings.TrimSpace(yamlContent)
// 如果内容看起来像 JSON直接返回
if strings.HasPrefix(trimmed, "{") {
return yamlContent
}
// 否则返回默认禁用配置
return `{"rag": {"enabled": false}}`
}
// expandEnvVar 展开环境变量 ${VAR_NAME}
func expandEnvVar(value string) string {
if strings.HasPrefix(value, "${") && strings.HasSuffix(value, "}") {
varName := value[2 : len(value)-1]
return os.Getenv(varName)
}
return value
}
// setDefaults 设置默认值
func setDefaults(config *RAGConfig) {
if config.ContextMode == "" {
config.ContextMode = "full"
}
if config.MaxContextLength == 0 {
config.MaxContextLength = 4000
}
if config.DefaultTopK == 0 {
config.DefaultTopK = 3
}
if config.SimilarityThreshold == 0 {
config.SimilarityThreshold = 0.7
}
if config.CacheTTL == 0 {
config.CacheTTL = 3600
}
if config.CacheMaxSize == 0 {
config.CacheMaxSize = 1000
}
if config.Timeout == 0 {
config.Timeout = 10
}
if config.MaxRetries == 0 {
config.MaxRetries = 3
}
if config.RetryDelay == 0 {
config.RetryDelay = 1
}
// 为每个工具配置设置默认值
for _, toolConfig := range config.Tools {
if toolConfig.ContextMode == "" {
toolConfig.ContextMode = config.ContextMode
}
if toolConfig.TopK == 0 {
toolConfig.TopK = config.DefaultTopK
}
}
}
// GetToolConfig 获取指定工具的配置
func (c *RAGConfig) GetToolConfig(toolName string) *ToolConfig {
if toolConfig, ok := c.Tools[toolName]; ok {
return toolConfig
}
return nil
}
// IsToolRAGEnabled 检查指定工具是否启用 RAG
func (c *RAGConfig) IsToolRAGEnabled(toolName string) bool {
if !c.Enabled {
return false
}
toolConfig := c.GetToolConfig(toolName)
if toolConfig == nil {
// 没有工具级配置,使用全局配置
return c.Enabled
}
return toolConfig.UseRAG
}

View File

@@ -0,0 +1,302 @@
// Package rag 提供 RAG检索增强生成功能
// 支持可选的知识库集成,通过配置开关控制
package rag
import (
"fmt"
"log"
"strings"
)
// RAGManager 管理 RAG 功能的开关和查询
type RAGManager struct {
enabled bool // RAG 功能是否启用
client *RAGClient // RAG 客户端(仅在 enabled=true 时有效)
config *RAGConfig // 配置
}
// NewRAGManager 创建 RAG 管理器
// 如果配置中 enabled=false则返回禁用状态的管理器
func NewRAGManager(config *RAGConfig) *RAGManager {
if config == nil || !config.Enabled {
log.Println("📖 RAG: Disabled (using rule-based generation)")
return &RAGManager{
enabled: false,
config: config,
}
}
// 验证必要配置
if config.KnowledgeBaseID == "" || config.WorkspaceID == "" {
log.Println("⚠️ RAG: Missing workspace ID or knowledge base ID, disabling RAG")
return &RAGManager{
enabled: false,
config: config,
}
}
// 检查 SDK 认证凭证
if config.AccessKeyID == "" || config.AccessKeySecret == "" {
log.Println("⚠️ RAG: Missing AccessKey credentials, disabling RAG")
return &RAGManager{
enabled: false,
config: config,
}
}
// 初始化 RAG 客户端(使用 SDK
log.Println("🔧 RAG: Using Alibaba Cloud SDK authentication")
client, err := NewRAGClient(config)
if err != nil {
log.Printf("❌ RAG: Failed to initialize SDK client: %v, disabling RAG\n", err)
return &RAGManager{
enabled: false,
config: config,
}
}
log.Printf("✅ RAG: Enabled (Provider: %s, KB: %s)\n", config.Provider, config.KnowledgeBaseID)
return &RAGManager{
enabled: true,
client: client,
config: config,
}
}
// IsEnabled 返回 RAG 是否启用
func (m *RAGManager) IsEnabled() bool {
return m.enabled
}
// QueryWithContext 查询知识库并返回上下文
// 如果 RAG 未启用,返回空上下文(不报错)
func (m *RAGManager) QueryWithContext(query string, scenario string, opts ...QueryOption) (*RAGContext, error) {
// RAG 未启用,返回空上下文
if !m.enabled {
return &RAGContext{
Enabled: false,
Message: "RAG is disabled, using rule-based generation",
}, nil
}
// 构建查询
ragQuery := &RAGQuery{
Query: query,
Scenario: scenario,
TopK: m.config.DefaultTopK,
}
// 应用可选参数
for _, opt := range opts {
opt(ragQuery)
}
// 查询知识库
resp, err := m.client.SearchWithCache(ragQuery)
if err != nil {
// 如果配置了降级策略,返回空上下文而不是报错
if m.config.FallbackOnError {
log.Printf("⚠️ RAG query failed, falling back to rules: %v\n", err)
return &RAGContext{
Enabled: false,
Message: fmt.Sprintf("RAG query failed, using fallback: %v", err),
}, nil
}
return nil, fmt.Errorf("RAG query failed: %w", err)
}
// 构建上下文
return m.buildContext(resp), nil
}
// QueryForTool 为特定工具查询(支持工具级配置覆盖)
// 这是工具级别配置的核心实现
func (m *RAGManager) QueryForTool(toolName string, query string, scenario string) (*RAGContext, error) {
// 全局 RAG 未启用
if !m.enabled {
return &RAGContext{
Enabled: false,
Message: "RAG is disabled globally",
}, nil
}
// 检查工具级配置
if toolConfig, ok := m.config.Tools[toolName]; ok {
// 工具有专门的配置
if !toolConfig.UseRAG {
// 工具明确不使用 RAG
return &RAGContext{
Enabled: false,
Message: fmt.Sprintf("RAG is disabled for tool: %s", toolName),
}, nil
}
// 使用工具级配置覆盖全局配置
log.Printf("🔧 Using tool-specific RAG config for: %s (context_mode=%s, top_k=%d)",
toolName, toolConfig.ContextMode, toolConfig.TopK)
return m.QueryWithContext(query, scenario,
WithTopK(toolConfig.TopK),
WithContextMode(toolConfig.ContextMode),
)
}
// 没有工具级配置,使用默认全局配置
log.Printf("🔧 Using global RAG config for: %s", toolName)
return m.QueryWithContext(query, scenario)
}
// buildContext 构建上下文
func (m *RAGManager) buildContext(resp *RAGResponse) *RAGContext {
ctx := &RAGContext{
Enabled: true,
Documents: make([]ContextDocument, 0, len(resp.Documents)),
}
for _, doc := range resp.Documents {
ctxDoc := ContextDocument{
Title: doc.Title,
Source: doc.Source,
URL: doc.URL,
Score: doc.Score,
Highlights: doc.Highlights,
}
// 根据 context_mode 决定返回的内容
switch m.config.ContextMode {
case "full":
ctxDoc.Content = doc.Content
case "summary":
ctxDoc.Content = m.summarize(doc.Content)
case "highlights":
if len(doc.Highlights) > 0 {
ctxDoc.Content = strings.Join(doc.Highlights, "\n\n")
} else {
ctxDoc.Content = m.summarize(doc.Content)
}
default:
ctxDoc.Content = doc.Content
}
// 控制长度
if len(ctxDoc.Content) > m.config.MaxContextLength {
ctxDoc.Content = ctxDoc.Content[:m.config.MaxContextLength] + "\n\n[内容已截断...]"
}
ctx.Documents = append(ctx.Documents, ctxDoc)
}
ctx.Message = fmt.Sprintf("Retrieved %d relevant documents from knowledge base (latency: %dms)",
len(ctx.Documents), resp.Latency)
return ctx
}
// summarize 简单的内容摘要截取前N个字符
func (m *RAGManager) summarize(content string, maxLen ...int) string {
length := 500 // 默认500字符
if len(maxLen) > 0 {
length = maxLen[0]
}
if len(content) <= length {
return content
}
// 尝试在句号或换行处截断
truncated := content[:length]
if idx := strings.LastIndexAny(truncated, "。\n."); idx > length/2 {
return content[:idx+1]
}
return truncated + "..."
}
// FormatContextForAI 格式化上下文,供 AI 使用
// 返回 Markdown 格式的文档上下文
func (ctx *RAGContext) FormatContextForAI() string {
if !ctx.Enabled || len(ctx.Documents) == 0 {
return fmt.Sprintf("> %s\n", ctx.Message)
}
var result strings.Builder
result.WriteString("## 📚 知识库参考文档\n\n")
result.WriteString(fmt.Sprintf("> %s\n\n", ctx.Message))
for i, doc := range ctx.Documents {
result.WriteString(fmt.Sprintf("### 参考文档 %d: %s\n\n", i+1, doc.Title))
// 元信息
result.WriteString(fmt.Sprintf("**来源**: %s \n", doc.Source))
if doc.URL != "" {
result.WriteString(fmt.Sprintf("**链接**: %s \n", doc.URL))
}
result.WriteString(fmt.Sprintf("**相关度**: %.2f \n\n", doc.Score))
// 文档内容(重点)
result.WriteString("**相关内容**:\n\n")
result.WriteString("```\n")
result.WriteString(doc.Content)
result.WriteString("\n```\n\n")
// 高亮片段
if len(doc.Highlights) > 0 {
result.WriteString("**关键片段**:\n\n")
for _, h := range doc.Highlights {
result.WriteString(fmt.Sprintf("- %s\n", h))
}
result.WriteString("\n")
}
result.WriteString("---\n\n")
}
return result.String()
}
// ==================== 类型定义 ====================
// RAGContext 表示 RAG 查询返回的上下文
type RAGContext struct {
Enabled bool `json:"enabled"` // RAG 是否启用
Documents []ContextDocument `json:"documents"` // 检索到的文档
Message string `json:"message"` // 提示信息
}
// ContextDocument 表示上下文中的一个文档
type ContextDocument struct {
Title string `json:"title"` // 文档标题
Content string `json:"content"` // 文档内容(根据 context_mode 调整)
Source string `json:"source"` // 来源路径
URL string `json:"url"` // 在线链接
Score float64 `json:"score"` // 相关度分数
Highlights []string `json:"highlights"` // 高亮片段
}
// ==================== 查询选项 ====================
// QueryOption 查询选项函数
type QueryOption func(*RAGQuery)
// WithTopK 设置返回文档数量
func WithTopK(k int) QueryOption {
return func(q *RAGQuery) {
q.TopK = k
}
}
// WithContextMode 设置上下文模式
func WithContextMode(mode string) QueryOption {
return func(q *RAGQuery) {
q.ContextMode = mode
}
}
// WithFilters 设置过滤条件
func WithFilters(filters map[string]interface{}) QueryOption {
return func(q *RAGQuery) {
q.Filters = filters
}
}

View File

@@ -0,0 +1,794 @@
// MCP Server implementation for Nginx Migration Tools - Standalone Mode
package standalone
import (
"encoding/json"
"fmt"
"strings"
"nginx-migration-mcp/tools"
)
// NewMCPServer creates a new MCP server instance
func NewMCPServer(config *ServerConfig) *MCPServer {
return &MCPServer{config: config}
}
// HandleMessage processes an incoming MCP message
func (s *MCPServer) HandleMessage(msg MCPMessage) MCPMessage {
switch msg.Method {
case "initialize":
return s.handleInitialize(msg)
case "tools/list":
return s.handleToolsList(msg)
case "tools/call":
return s.handleToolsCall(msg)
default:
return s.errorResponse(msg.ID, -32601, "Method not found")
}
}
func (s *MCPServer) handleInitialize(msg MCPMessage) MCPMessage {
return MCPMessage{
JSONRPC: "2.0",
ID: msg.ID,
Result: map[string]interface{}{
"protocolVersion": "2024-11-05",
"capabilities": map[string]interface{}{
"tools": map[string]interface{}{
"listChanged": true,
},
},
"serverInfo": map[string]interface{}{
"name": s.config.Server.Name,
"version": s.config.Server.Version,
},
},
}
}
func (s *MCPServer) handleToolsList(msg MCPMessage) MCPMessage {
toolsList := tools.GetMCPTools()
return MCPMessage{
JSONRPC: "2.0",
ID: msg.ID,
Result: map[string]interface{}{
"tools": toolsList,
},
}
}
func (s *MCPServer) handleToolsCall(msg MCPMessage) MCPMessage {
var params CallToolParams
paramsBytes, _ := json.Marshal(msg.Params)
json.Unmarshal(paramsBytes, &params)
handlers := tools.GetToolHandlers(s)
handler, exists := handlers[params.Name]
if !exists {
return s.errorResponse(msg.ID, -32601, fmt.Sprintf("Unknown tool: %s", params.Name))
}
result := handler(params.Arguments)
return MCPMessage{
JSONRPC: "2.0",
ID: msg.ID,
Result: result,
}
}
func (s *MCPServer) errorResponse(id interface{}, code int, message string) MCPMessage {
return MCPMessage{
JSONRPC: "2.0",
ID: id,
Error: &MCPError{
Code: code,
Message: message,
},
}
}
// Tool implementations
func (s *MCPServer) parseNginxConfig(args map[string]interface{}) tools.ToolResult {
configContent, ok := args["config_content"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing config_content"}}}
}
serverCount := strings.Count(configContent, "server {")
locationCount := strings.Count(configContent, "location")
hasSSL := strings.Contains(configContent, "ssl")
hasProxy := strings.Contains(configContent, "proxy_pass")
hasRewrite := strings.Contains(configContent, "rewrite")
complexity := "Simple"
if serverCount > 1 || (hasRewrite && hasSSL) {
complexity = "Complex"
} else if hasRewrite || hasSSL {
complexity = "Medium"
}
analysis := fmt.Sprintf(`Nginx配置分析结果
基础信息:
- Server块: %d个
- Location块: %d个
- SSL配置: %t
- 反向代理: %t
- URL重写: %t
复杂度: %s
迁移建议:`, serverCount, locationCount, hasSSL, hasProxy, hasRewrite, complexity)
if hasProxy {
analysis += "\n- 反向代理将转换为HTTPRoute backendRefs"
}
if hasRewrite {
analysis += "\n- URL重写将使用URLRewrite过滤器"
}
if hasSSL {
analysis += "\n- SSL配置需要迁移到Gateway资源"
}
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: analysis}}}
}
func (s *MCPServer) convertToHigress(args map[string]interface{}) tools.ToolResult {
configContent, ok := args["config_content"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing config_content"}}}
}
namespace := s.config.Defaults.Namespace
if ns, ok := args["namespace"].(string); ok {
namespace = ns
}
hostname := s.config.Defaults.Hostname
lines := strings.Split(configContent, "\n")
for _, line := range lines {
if strings.Contains(line, "server_name") && !strings.Contains(line, "#") {
parts := strings.Fields(line)
if len(parts) >= 2 {
hostname = strings.TrimSuffix(parts[1], ";")
break
}
}
}
yamlConfig := fmt.Sprintf(`转换后的Higress配置
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: %s
namespace: %s
annotations:
higress.io/migrated-from: "nginx"
spec:
parentRefs:
- name: %s
namespace: %s
hostnames:
- %s
rules:
- matches:
- path:
type: PathPrefix
value: %s
backendRefs:
- name: %s
port: %d
---
apiVersion: v1
kind: Service
metadata:
name: %s
namespace: %s
spec:
selector:
app: backend
ports:
- port: %d
targetPort: %d
转换完成
应用步骤:
1. 保存为 higress-config.yaml
2. 执行: kubectl apply -f higress-config.yaml
3. 验证: kubectl get httproute -n %s`,
s.config.GenerateRouteName(hostname), namespace,
s.config.Gateway.Name, s.config.Gateway.Namespace, hostname, s.config.Defaults.PathPrefix,
s.config.GenerateServiceName(hostname), s.config.Service.DefaultPort,
s.config.GenerateServiceName(hostname), namespace,
s.config.Service.DefaultPort, s.config.Service.DefaultTarget, namespace)
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: yamlConfig}}}
}
func (s *MCPServer) analyzeLuaPlugin(args map[string]interface{}) tools.ToolResult {
luaCode, ok := args["lua_code"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing lua_code"}}}
}
// 使用新的 AI 友好分析
analysis := tools.AnalyzeLuaPluginForAI(luaCode)
// 生成用户友好的消息
features := []string{}
for feature := range analysis.Features {
features = append(features, fmt.Sprintf("- %s", feature))
}
userMessage := fmt.Sprintf(`✅ Lua 插件分析完成
📊 **检测到的特性**
%s
⚠️ **兼容性警告**
%s
📈 **复杂度**%s
🔄 **兼容性级别**%s
💡 **迁移建议**`,
strings.Join(features, "\n"),
strings.Join(analysis.Warnings, "\n- "),
analysis.Complexity,
analysis.Compatibility,
)
switch analysis.Compatibility {
case "full":
userMessage += "\n- 可直接迁移到 WASM 插件\n- 建议使用工具链进行转换"
case "partial":
userMessage += "\n- 需要部分重构\n- 强烈建议使用工具链并让 AI 参与代码生成"
case "manual":
userMessage += "\n- 需要手动重写\n- 建议分步骤进行,使用工具链辅助"
}
userMessage += "\n\n🔗 **后续操作**\n"
userMessage += "1. 调用 `generate_conversion_hints` 工具获取详细的转换提示\n"
userMessage += "2. 基于提示生成 Go WASM 代码\n"
userMessage += "3. 调用 `validate_wasm_code` 工具验证生成的代码\n"
userMessage += "4. 调用 `generate_deployment_config` 工具生成部署配置\n"
userMessage += "\n或者直接使用 `convert_lua_to_wasm` 进行一键转换。"
// 生成 AI 指令
aiInstructions := fmt.Sprintf(`你现在已经获得了 Lua 插件的分析结果。基于这些信息,你可以:
### 选项 1使用工具链进行精细控制
调用 generate_conversion_hints 工具,传入以下分析结果:
`+"```json"+`
{
"analysis_result": %s,
"plugin_name": "your-plugin-name"
}
`+"```"+`
这将为你提供代码生成模板,然后基于模板生成 Go WASM 代码。
### 选项 2一键转换
如果用户希望快速转换,可以直接调用 convert_lua_to_wasm 工具。
### 建议的对话流程
1. **询问用户**:是否需要详细的转换提示,还是直接生成代码?
2. **如果需要提示**:调用 generate_conversion_hints
3. **生成代码后**:询问是否需要验证(调用 validate_wasm_code
4. **验证通过后**:询问是否需要生成部署配置(调用 generate_deployment_config
### 关键注意事项
%s
### 代码生成要点
- 检测到的 Nginx 变量需要映射到 HTTP 头部
- 复杂度为 %s请相应调整代码结构
- 兼容性级别为 %s注意处理警告中的问题
`,
string(mustMarshalJSON(analysis)),
formatWarningsForAI(analysis.Warnings),
analysis.Complexity,
analysis.Compatibility,
)
return tools.FormatToolResultWithAIContext(userMessage, aiInstructions, analysis)
}
func mustMarshalJSON(v interface{}) []byte {
data, _ := json.Marshal(v)
return data
}
func formatWarningsForAI(warnings []string) string {
if len(warnings) == 0 {
return "- 无特殊警告,可以直接转换"
}
result := []string{}
for _, w := range warnings {
result = append(result, fmt.Sprintf("- ⚠️ %s", w))
}
return strings.Join(result, "\n")
}
func (s *MCPServer) convertLuaToWasm(args map[string]interface{}) tools.ToolResult {
luaCode, ok := args["lua_code"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing lua_code"}}}
}
pluginName, ok := args["plugin_name"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing plugin_name"}}}
}
analyzer := tools.AnalyzeLuaScript(luaCode)
result, err := tools.ConvertLuaToWasm(analyzer, pluginName)
if err != nil {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: fmt.Sprintf("Error: %v", err)}}}
}
response := fmt.Sprintf(`Lua脚本转换完成
转换分析:
- 复杂度: %s
- 检测特性: %d个
- 兼容性警告: %d个
注意事项:
%s
生成的文件:
==== main.go ====
%s
==== WasmPlugin配置 ====
%s
部署步骤:
1. 创建插件目录: mkdir -p extensions/%s
2. 保存Go代码到: extensions/%s/main.go
3. 构建插件: PLUGIN_NAME=%s make build
4. 应用配置: kubectl apply -f wasmplugin.yaml
提示:
- 请根据实际需求调整配置
- 测试插件功能后再部署到生产环境
- 如有共享状态需求请配置Redis等外部存储
`,
analyzer.Complexity,
len(analyzer.Features),
len(analyzer.Warnings),
strings.Join(analyzer.Warnings, "\n- "),
result.GoCode,
result.WasmPluginYAML,
pluginName, pluginName, pluginName)
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: response}}}
}
// GenerateConversionHints 生成详细的代码转换提示
func (s *MCPServer) GenerateConversionHints(args map[string]interface{}) tools.ToolResult {
analysisResultStr, ok := args["analysis_result"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing analysis_result"}}}
}
pluginName, ok := args["plugin_name"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing plugin_name"}}}
}
// 解析分析结果
var analysis tools.AnalysisResultForAI
if err := json.Unmarshal([]byte(analysisResultStr), &analysis); err != nil {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: fmt.Sprintf("Error parsing analysis_result: %v", err)}}}
}
// 生成转换提示
hints := tools.GenerateConversionHints(analysis, pluginName)
// 格式化输出
userMessage := fmt.Sprintf(`🎯 代码转换提示
**插件名称**: %s
**代码模板**: %s
%s
`,
pluginName,
hints.CodeTemplate,
func() string {
if len(hints.Warnings) > 0 {
return "⚠️ **警告**: " + formatWarningsListForUser(hints.Warnings)
}
return ""
}(),
)
// 生成详细的 AI 指令
aiInstructions := fmt.Sprintf(`现在你需要基于以下信息生成 Go WASM 插件代码。
## 代码模板
%s
## 生成代码的要求
1. **实现所需的回调函数**
2. **保持 Lua 代码的业务逻辑等价**
3. **添加适当的错误处理**
4. **包含配置解析逻辑(如需要)**
## 输出格式
请按以下格式输出代码:
### main.go
`+"```go"+`
[完整的 Go 代码]
`+"```"+`
生成代码后,建议调用 validate_wasm_code 工具进行验证。
`,
hints.CodeTemplate,
)
return tools.FormatToolResultWithAIContext(userMessage, aiInstructions, hints)
}
// ValidateWasmCode 验证生成的 Go WASM 代码
func (s *MCPServer) ValidateWasmCode(args map[string]interface{}) tools.ToolResult {
goCode, ok := args["go_code"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing go_code"}}}
}
pluginName, ok := args["plugin_name"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing plugin_name"}}}
}
// 执行验证
report := tools.ValidateWasmCode(goCode, pluginName)
// 统计各类问题数量
requiredCount := 0
recommendedCount := 0
optionalCount := 0
bestPracticeCount := 0
for _, issue := range report.Issues {
switch issue.Category {
case "required":
requiredCount++
case "recommended":
recommendedCount++
case "optional":
optionalCount++
case "best_practice":
bestPracticeCount++
}
}
// 构建用户消息
userMessage := fmt.Sprintf(`## 代码验证报告
%s
### 发现的回调函数 (%d 个)
%s
### 配置结构
%s
### 问题分类
#### 必须修复 (%d 个)
%s
#### 建议修复 (%d 个)
%s
#### 可选优化 (%d 个)
%s
#### 最佳实践 (%d 个)
%s
### 缺失的导入包 (%d 个)
%s
---
`,
report.Summary,
len(report.FoundCallbacks),
formatCallbacksList(report.FoundCallbacks),
formatConfigStatus(report.HasConfig),
requiredCount,
formatIssuesByCategory(report.Issues, "required"),
recommendedCount,
formatIssuesByCategory(report.Issues, "recommended"),
optionalCount,
formatIssuesByCategory(report.Issues, "optional"),
bestPracticeCount,
formatIssuesByCategory(report.Issues, "best_practice"),
len(report.MissingImports),
formatList(report.MissingImports),
)
// 根据问题级别给出建议
hasRequired := requiredCount > 0
if hasRequired {
userMessage += " **请优先修复 \"必须修复\" 的问题,否则代码可能无法编译或运行。**\n\n"
} else if recommendedCount > 0 {
userMessage += " **代码基本结构正确。** 建议修复 \"建议修复\" 的问题以提高代码质量。\n\n"
} else {
userMessage += " **代码验证通过!** 可以继续生成部署配置。\n\n"
userMessage += "**下一步**:调用 `generate_deployment_config` 工具生成部署配置。\n"
}
// AI 指令
aiInstructions := ""
if hasRequired {
aiInstructions = `代码验证发现必须修复的问题。
## 修复指南
` + formatIssuesForAI(report.Issues, "required") + `
请修复上述问题后,再次调用 validate_wasm_code 工具进行验证。
`
} else if recommendedCount > 0 {
aiInstructions = `代码基本结构正确,建议修复以下问题:
` + formatIssuesForAI(report.Issues, "recommended") + `
可以选择修复这些问题,或直接调用 generate_deployment_config 工具生成部署配置。
`
} else {
aiInstructions = `代码验证通过!
## 下一步
调用 generate_deployment_config 工具,参数:
` + "```json" + `
{
"plugin_name": "` + pluginName + `",
"go_code": "[验证通过的代码]",
"namespace": "higress-system"
}
` + "```" + `
这将生成完整的部署配置包。
`
}
return tools.FormatToolResultWithAIContext(userMessage, aiInstructions, report)
}
// GenerateDeploymentConfig 生成部署配置
func (s *MCPServer) GenerateDeploymentConfig(args map[string]interface{}) tools.ToolResult {
pluginName, ok := args["plugin_name"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing plugin_name"}}}
}
goCode, ok := args["go_code"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing go_code"}}}
}
namespace := "higress-system"
if ns, ok := args["namespace"].(string); ok && ns != "" {
namespace = ns
}
configSchema := ""
if cs, ok := args["config_schema"].(string); ok {
configSchema = cs
}
// 生成部署包
pkg := tools.GenerateDeploymentPackage(pluginName, goCode, configSchema, namespace)
// 格式化输出
userMessage := fmt.Sprintf(`🎉 部署配置生成完成!
已为插件 **%s** 生成完整的部署配置包。
## 生成的文件
### 1. WasmPlugin 配置
- 文件名wasmplugin.yaml
- 命名空间:%s
- 包含默认配置和匹配规则
### 2. 构建脚本
- Makefile自动化构建和部署
- Dockerfile容器化打包
### 3. 文档
- README.md完整的使用说明
- 包含快速开始、配置说明、问题排查
### 4. 测试脚本
- test.sh自动化测试脚本
### 5. 依赖清单
- 列出了所有必需的 Go 模块
---
## 快速部署
`+"```bash"+`
# 1. 保存文件
# 保存 main.go
# 保存 wasmplugin.yaml
# 保存 Makefile
# 保存 Dockerfile
# 2. 构建插件
make build
# 3. 构建并推送镜像
make docker-build docker-push
# 4. 部署到 Kubernetes
make deploy
# 5. 验证部署
kubectl get wasmplugin -n %s
`+"```"+`
---
**文件内容请见下方结构化数据部分。**
`,
pluginName,
namespace,
namespace,
)
aiInstructions := fmt.Sprintf(`部署配置已生成完毕。
## 向用户展示文件
请将以下文件内容清晰地展示给用户:
### 1. main.go
用户已经有这个文件。
### 2. wasmplugin.yaml
`+"```yaml"+`
%s
`+"```"+`
### 3. Makefile
`+"```makefile"+`
%s
`+"```"+`
### 4. Dockerfile
`+"```dockerfile"+`
%s
`+"```"+`
### 5. README.md
`+"```markdown"+`
%s
`+"```"+`
### 6. test.sh
`+"```bash"+`
%s
`+"```"+`
## 后续支持
询问用户是否需要:
1. 解释任何配置项的含义
2. 自定义某些配置
3. 帮助解决部署问题
`,
pkg.WasmPluginYAML,
pkg.Makefile,
pkg.Dockerfile,
pkg.README,
pkg.TestScript,
)
return tools.FormatToolResultWithAIContext(userMessage, aiInstructions, pkg)
}
// 辅助格式化函数
func formatWarningsListForUser(warnings []string) string {
if len(warnings) == 0 {
return "无"
}
return strings.Join(warnings, "\n- ")
}
func formatCallbacksList(callbacks []string) string {
if len(callbacks) == 0 {
return "无"
}
return "- " + strings.Join(callbacks, "\n- ")
}
func formatConfigStatus(hasConfig bool) string {
if hasConfig {
return " 已定义配置结构体"
}
return "- 未定义配置结构体(如不需要配置可忽略)"
}
func formatIssuesByCategory(issues []tools.ValidationIssue, category string) string {
var filtered []string
for _, issue := range issues {
if issue.Category == category {
filtered = append(filtered, fmt.Sprintf("- **[%s]** %s\n 💡 建议: %s\n 📌 影响: %s",
issue.Type, issue.Message, issue.Suggestion, issue.Impact))
}
}
if len(filtered) == 0 {
return "无"
}
return strings.Join(filtered, "\n\n")
}
func formatIssuesForAI(issues []tools.ValidationIssue, category string) string {
var filtered []tools.ValidationIssue
for _, issue := range issues {
if issue.Category == category {
filtered = append(filtered, issue)
}
}
if len(filtered) == 0 {
return "无问题"
}
result := []string{}
for i, issue := range filtered {
result = append(result, fmt.Sprintf(`
### 问题 %d: %s
**类型**: %s
**建议**: %s
**影响**: %s
请根据建议修复此问题。
`,
i+1,
issue.Message,
issue.Type,
issue.Suggestion,
issue.Impact,
))
}
return strings.Join(result, "\n")
}
func formatList(items []string) string {
if len(items) == 0 {
return "无"
}
return "- " + strings.Join(items, "\n- ")
}

View File

@@ -0,0 +1,140 @@
{
"version": "2.0.0",
"name": "nginx-migration",
"description": "Nginx 到 Higress 迁移工具集:支持配置转换和 Lua 插件迁移",
"tools": [
{
"name": "parse_nginx_config",
"description": "解析和分析 Nginx 配置文件,识别配置结构和复杂度",
"inputSchema": {
"type": "object",
"properties": {
"config_content": {
"type": "string",
"description": "Nginx 配置文件内容"
}
},
"required": ["config_content"]
}
},
{
"name": "convert_to_higress",
"description": "智能解析 Nginx 配置并通过 AI 推理生成 Higress Ingress/HTTPRoute 配置。支持复杂配置SSL、重写、重定向、upstream 等)的智能转换,结合 RAG 知识库提供准确的转换方案",
"inputSchema": {
"type": "object",
"properties": {
"config_content": {
"type": "string",
"description": "Nginx 配置文件内容"
},
"namespace": {
"type": "string",
"description": "目标 Kubernetes 命名空间",
"default": "default"
},
"use_gateway_api": {
"type": "boolean",
"description": "是否使用 Gateway API (HTTPRoute)。默认 false使用 Ingress",
"default": false
}
},
"required": ["config_content"]
}
},
{
"name": "analyze_lua_plugin",
"description": "分析 Nginx Lua 插件的兼容性,识别使用的 API 和潜在迁移问题,返回结构化分析结果供后续工具使用",
"inputSchema": {
"type": "object",
"properties": {
"lua_code": {
"type": "string",
"description": "Nginx Lua 插件代码"
}
},
"required": ["lua_code"]
}
},
{
"name": "generate_conversion_hints",
"description": "基于 Lua 分析结果生成代码转换模板",
"inputSchema": {
"type": "object",
"properties": {
"analysis_result": {
"type": "string",
"description": "analyze_lua_plugin 返回的 JSON 格式分析结果"
},
"plugin_name": {
"type": "string",
"description": "目标插件名称(小写字母和连字符)"
}
},
"required": ["analysis_result", "plugin_name"]
}
},
{
"name": "validate_wasm_code",
"description": "验证生成的 Go WASM 插件代码检查语法、API 使用、配置结构等,输出验证报告和改进建议",
"inputSchema": {
"type": "object",
"properties": {
"go_code": {
"type": "string",
"description": "生成的 Go WASM 插件代码"
},
"plugin_name": {
"type": "string",
"description": "插件名称"
}
},
"required": ["go_code", "plugin_name"]
}
},
{
"name": "generate_deployment_config",
"description": "为验证通过的 WASM 插件生成完整的部署配置包,包括 WasmPlugin YAML、Makefile、Dockerfile、README 和测试脚本",
"inputSchema": {
"type": "object",
"properties": {
"plugin_name": {
"type": "string",
"description": "插件名称"
},
"go_code": {
"type": "string",
"description": "验证通过的 Go 代码"
},
"config_schema": {
"type": "string",
"description": "配置 JSON Schema可选"
},
"namespace": {
"type": "string",
"description": "部署命名空间",
"default": "higress-system"
}
},
"required": ["plugin_name", "go_code"]
}
},
{
"name": "convert_lua_to_wasm",
"description": "一键将 Nginx Lua 脚本转换为 Higress WASM 插件,自动生成 Go 代码和 WasmPlugin 配置。适合简单插件快速转换",
"inputSchema": {
"type": "object",
"properties": {
"lua_code": {
"type": "string",
"description": "要转换的 Nginx Lua 插件代码"
},
"plugin_name": {
"type": "string",
"description": "生成的 WASM 插件名称 (小写字母和连字符)"
}
},
"required": ["lua_code", "plugin_name"]
}
}
]
}

View File

@@ -0,0 +1,45 @@
package main
import (
"bufio"
"encoding/json"
"fmt"
"log"
"os"
"nginx-migration-mcp/standalone"
)
const Version = "1.0.0"
func main() {
// Load config
config := standalone.LoadConfig()
server := standalone.NewMCPServer(config)
scanner := bufio.NewScanner(os.Stdin)
writer := bufio.NewWriter(os.Stdout)
for scanner.Scan() {
line := scanner.Bytes()
var msg standalone.MCPMessage
if err := json.Unmarshal(line, &msg); err != nil {
log.Printf("Error parsing message: %v", err)
continue
}
response := server.HandleMessage(msg)
responseBytes, _ := json.Marshal(response)
writer.Write(responseBytes)
writer.WriteByte('\n')
writer.Flush()
}
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "Error reading from stdin: %v\n", err)
os.Exit(1)
}
}

View File

@@ -0,0 +1,141 @@
// Configuration management for nginx migration MCP server - Standalone Mode
package standalone
import (
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
)
// ServerConfig holds all configurable values
type ServerConfig struct {
Server ServerSettings `json:"server"`
Gateway GatewaySettings `json:"gateway"`
Service ServiceSettings `json:"service"`
Defaults DefaultSettings `json:"defaults"`
}
type ServerSettings struct {
Name string `json:"name"`
Version string `json:"version"`
Port string `json:"port"`
APIBaseURL string `json:"api_base_url"`
}
type GatewaySettings struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
}
type ServiceSettings struct {
DefaultName string `json:"default_name"`
DefaultPort int `json:"default_port"`
DefaultTarget int `json:"default_target_port"`
}
type DefaultSettings struct {
Hostname string `json:"hostname"`
Namespace string `json:"namespace"`
PathPrefix string `json:"path_prefix"`
RoutePrefix string `json:"route_prefix"`
}
// LoadConfig loads configuration from environment variables and files
func LoadConfig() *ServerConfig {
config := &ServerConfig{
Server: ServerSettings{
Name: getEnvOrDefault("NGINX_MCP_SERVER_NAME", "nginx-migration-mcp"),
Version: getEnvOrDefault("NGINX_MCP_VERSION", "1.0.0"),
Port: getEnvOrDefault("NGINX_MCP_PORT", "8080"),
APIBaseURL: getEnvOrDefault("NGINX_MIGRATION_API_URL", "http://localhost:8080"),
},
Gateway: GatewaySettings{
Name: getEnvOrDefault("HIGRESS_GATEWAY_NAME", "higress-gateway"),
Namespace: getEnvOrDefault("HIGRESS_GATEWAY_NAMESPACE", "higress-system"),
},
Service: ServiceSettings{
DefaultName: getEnvOrDefault("DEFAULT_SERVICE_NAME", "backend-service"),
DefaultPort: getIntEnvOrDefault("DEFAULT_SERVICE_PORT", 80),
DefaultTarget: getIntEnvOrDefault("DEFAULT_TARGET_PORT", 8080),
},
Defaults: DefaultSettings{
Hostname: getEnvOrDefault("DEFAULT_HOSTNAME", "example.com"),
Namespace: getEnvOrDefault("DEFAULT_NAMESPACE", "default"),
PathPrefix: getEnvOrDefault("DEFAULT_PATH_PREFIX", "/"),
RoutePrefix: getEnvOrDefault("ROUTE_NAME_PREFIX", "nginx-migrated"),
},
}
// Try to load from config file if exists
if configFile := os.Getenv("NGINX_MCP_CONFIG_FILE"); configFile != "" {
if err := loadConfigFromFile(config, configFile); err != nil {
fmt.Printf("Warning: Failed to load config from %s: %v\n", configFile, err)
}
}
return config
}
// loadConfigFromFile loads configuration from JSON file
func loadConfigFromFile(config *ServerConfig, filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return err
}
return json.Unmarshal(data, config)
}
// getEnvOrDefault returns environment variable value or default
func getEnvOrDefault(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
// getIntEnvOrDefault returns environment variable as int or default
func getIntEnvOrDefault(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
if intValue, err := strconv.Atoi(value); err == nil {
return intValue
}
}
return defaultValue
}
// GenerateRouteName generates a unique route name
func (c *ServerConfig) GenerateRouteName(hostname string) string {
if hostname == "" || hostname == c.Defaults.Hostname {
return fmt.Sprintf("%s-route", c.Defaults.RoutePrefix)
}
// Replace dots and special characters for valid k8s name
safeName := hostname
for _, char := range []string{".", "_", ":"} {
safeName = strings.ReplaceAll(safeName, char, "-")
}
return fmt.Sprintf("%s-%s", c.Defaults.RoutePrefix, safeName)
}
// GenerateIngressName generates a unique ingress name
func (c *ServerConfig) GenerateIngressName(hostname string) string {
if hostname == "" || hostname == c.Defaults.Hostname {
return fmt.Sprintf("%s-ingress", c.Defaults.RoutePrefix)
}
// Replace dots and special characters for valid k8s name
safeName := hostname
for _, char := range []string{".", "_", ":"} {
safeName = strings.ReplaceAll(safeName, char, "-")
}
return fmt.Sprintf("%s-%s", c.Defaults.RoutePrefix, safeName)
}
// GenerateServiceName generates service name based on hostname
func (c *ServerConfig) GenerateServiceName(hostname string) string {
if hostname == "" || hostname == c.Defaults.Hostname {
return c.Service.DefaultName
}
return fmt.Sprintf("%s-service", hostname)
}

View File

@@ -0,0 +1,914 @@
// Package standalone implements MCP Server for Nginx Migration Tools in standalone mode.
package standalone
import (
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"nginx-migration-mcp/internal/rag"
"nginx-migration-mcp/tools"
)
// NewMCPServer creates a new MCP server instance
func NewMCPServer(config *ServerConfig) *MCPServer {
// 初始化 RAG 管理器
// 获取可执行文件所在目录
execPath, err := os.Executable()
if err != nil {
log.Printf("WARNING: Failed to get executable path: %v", err)
execPath = "."
}
execDir := filepath.Dir(execPath)
// 尝试多个可能的配置文件路径(相对于可执行文件)
ragConfigPaths := []string{
filepath.Join(execDir, "config", "rag.json"), // 同级 config 目录
filepath.Join(execDir, "..", "config", "rag.json"), // 上级 config 目录
"config/rag.json", // 当前工作目录
}
var ragConfig *rag.RAGConfig
var configErr error
for _, path := range ragConfigPaths {
ragConfig, configErr = rag.LoadRAGConfig(path)
if configErr == nil {
log.Printf("Loaded RAG config from: %s", path)
break
}
}
if configErr != nil {
log.Printf("WARNING: Failed to load RAG config: %v, RAG will be disabled", configErr)
ragConfig = &rag.RAGConfig{Enabled: false}
}
ragManager := rag.NewRAGManager(ragConfig)
if ragManager.IsEnabled() {
log.Printf("RAG Manager initialized and enabled")
} else {
log.Printf("RAG Manager disabled, using rule-based approach")
}
return &MCPServer{
config: config,
ragManager: ragManager,
}
}
// HandleMessage processes an incoming MCP message
func (s *MCPServer) HandleMessage(msg MCPMessage) MCPMessage {
switch msg.Method {
case "initialize":
return s.handleInitialize(msg)
case "tools/list":
return s.handleToolsList(msg)
case "tools/call":
return s.handleToolsCall(msg)
default:
return s.errorResponse(msg.ID, -32601, "Method not found")
}
}
func (s *MCPServer) handleInitialize(msg MCPMessage) MCPMessage {
return MCPMessage{
JSONRPC: "2.0",
ID: msg.ID,
Result: map[string]interface{}{
"protocolVersion": "2024-11-05",
"capabilities": map[string]interface{}{
"tools": map[string]interface{}{
"listChanged": true,
},
},
"serverInfo": map[string]interface{}{
"name": s.config.Server.Name,
"version": s.config.Server.Version,
},
},
}
}
func (s *MCPServer) handleToolsList(msg MCPMessage) MCPMessage {
toolsList := tools.GetMCPTools()
return MCPMessage{
JSONRPC: "2.0",
ID: msg.ID,
Result: map[string]interface{}{
"tools": toolsList,
},
}
}
func (s *MCPServer) handleToolsCall(msg MCPMessage) MCPMessage {
var params CallToolParams
paramsBytes, _ := json.Marshal(msg.Params)
json.Unmarshal(paramsBytes, &params)
handlers := tools.GetToolHandlers(s)
handler, exists := handlers[params.Name]
if !exists {
return s.errorResponse(msg.ID, -32601, fmt.Sprintf("Unknown tool: %s", params.Name))
}
result := handler(params.Arguments)
return MCPMessage{
JSONRPC: "2.0",
ID: msg.ID,
Result: result,
}
}
func (s *MCPServer) errorResponse(id interface{}, code int, message string) MCPMessage {
return MCPMessage{
JSONRPC: "2.0",
ID: id,
Error: &MCPError{
Code: code,
Message: message,
},
}
}
// Tool implementations
func (s *MCPServer) ParseNginxConfig(args map[string]interface{}) tools.ToolResult {
configContent, ok := args["config_content"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing config_content"}}}
}
serverCount := strings.Count(configContent, "server {")
locationCount := strings.Count(configContent, "location")
hasSSL := strings.Contains(configContent, "ssl")
hasProxy := strings.Contains(configContent, "proxy_pass")
hasRewrite := strings.Contains(configContent, "rewrite")
complexity := "Simple"
if serverCount > 1 || (hasRewrite && hasSSL) {
complexity = "Complex"
} else if hasRewrite || hasSSL {
complexity = "Medium"
}
analysis := fmt.Sprintf(`Nginx配置分析结果
基础信息:
- Server块: %d个
- Location块: %d个
- SSL配置: %t
- 反向代理: %t
- URL重写: %t
复杂度: %s
迁移建议:`, serverCount, locationCount, hasSSL, hasProxy, hasRewrite, complexity)
if hasProxy {
analysis += "\n- 反向代理将转换为Ingress backend配置"
}
if hasRewrite {
analysis += "\n- URL重写将使用Higress注解 (higress.io/rewrite-target)"
}
if hasSSL {
analysis += "\n- SSL配置将转换为Ingress TLS配置"
}
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: analysis}}}
}
func (s *MCPServer) ConvertToHigress(args map[string]interface{}) tools.ToolResult {
configContent, ok := args["config_content"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing config_content"}}}
}
namespace := s.config.Defaults.Namespace
if ns, ok := args["namespace"].(string); ok {
namespace = ns
}
// 检查是否使用 Gateway API
useGatewayAPI := false
if val, ok := args["use_gateway_api"].(bool); ok {
useGatewayAPI = val
}
// === 使用增强的解析器解析 Nginx 配置 ===
nginxConfig, err := tools.ParseNginxConfig(configContent)
if err != nil {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: fmt.Sprintf("Error parsing Nginx config: %v", err)}}}
}
// 分析配置
analysis := tools.AnalyzeNginxConfig(nginxConfig)
// === RAG 增强:查询转换示例和最佳实践 ===
var ragContext string
if s.ragManager != nil && s.ragManager.IsEnabled() {
// 构建查询关键词
queryBuilder := []string{"Nginx 配置转换到 Higress"}
if useGatewayAPI {
queryBuilder = append(queryBuilder, "Gateway API HTTPRoute")
} else {
queryBuilder = append(queryBuilder, "Kubernetes Ingress")
}
// 根据特性添加查询关键词
if analysis.Features["ssl"] {
queryBuilder = append(queryBuilder, "SSL TLS 证书配置")
}
if analysis.Features["rewrite"] {
queryBuilder = append(queryBuilder, "URL 重写 rewrite 规则")
}
if analysis.Features["redirect"] {
queryBuilder = append(queryBuilder, "重定向 redirect")
}
if analysis.Features["header_manipulation"] {
queryBuilder = append(queryBuilder, "请求头 响应头处理")
}
if len(nginxConfig.Upstreams) > 0 {
queryBuilder = append(queryBuilder, "负载均衡 upstream")
}
queryString := strings.Join(queryBuilder, " ")
log.Printf("RAG Query: %s", queryString)
ragResult, err := s.ragManager.QueryForTool(
"convert_to_higress",
queryString,
"nginx_to_higress",
)
if err == nil && ragResult.Enabled && len(ragResult.Documents) > 0 {
log.Printf("RAG: Found %d documents for conversion", len(ragResult.Documents))
ragContext = "\n\n## 参考文档(来自知识库)\n\n" + ragResult.FormatContextForAI()
} else {
if err != nil {
log.Printf("WARNING: RAG query failed: %v", err)
}
}
}
// === 将配置数据转换为 JSON 供 AI 使用 ===
configJSON, _ := json.MarshalIndent(nginxConfig, "", " ")
analysisJSON, _ := json.MarshalIndent(analysis, "", " ")
// === 构建返回消息 ===
userMessage := fmt.Sprintf(`📋 Nginx 配置解析完成
## 配置概览
- Server 块: %d
- Location 块: %d
- 域名: %d 个
- 复杂度: %s
- 目标格式: %s
- 命名空间: %s
## 检测到的特性
%s
## 迁移建议
%s
%s
---
## Nginx 配置结构
`+"```json"+`
%s
`+"```"+`
## 分析结果
`+"```json"+`
%s
`+"```"+`
%s
`,
analysis.ServerCount,
analysis.LocationCount,
analysis.DomainCount,
analysis.Complexity,
func() string {
if useGatewayAPI {
return "Gateway API (HTTPRoute)"
}
return "Kubernetes Ingress"
}(),
namespace,
formatFeatures(analysis.Features),
formatSuggestions(analysis.Suggestions),
func() string {
if ragContext != "" {
return "\n\n已加载知识库参考文档"
}
return ""
}(),
string(configJSON),
string(analysisJSON),
ragContext,
)
return tools.FormatToolResultWithAIContext(userMessage, "", map[string]interface{}{
"nginx_config": nginxConfig,
"analysis": analysis,
"namespace": namespace,
"use_gateway_api": useGatewayAPI,
})
}
func (s *MCPServer) AnalyzeLuaPlugin(args map[string]interface{}) tools.ToolResult {
luaCode, ok := args["lua_code"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing lua_code"}}}
}
// 使用新的 AI 友好分析
analysis := tools.AnalyzeLuaPluginForAI(luaCode)
// === RAG 增强:查询知识库获取转换建议 ===
var ragContext string
if s.ragManager != nil && s.ragManager.IsEnabled() && len(analysis.APICalls) > 0 {
query := fmt.Sprintf("Nginx Lua API %s 在 Higress WASM 中的转换方法和最佳实践", strings.Join(analysis.APICalls, ", "))
log.Printf("🔍 RAG Query: %s", query)
ragResult, err := s.ragManager.QueryForTool("analyze_lua_plugin", query, "lua_migration")
if err == nil && ragResult.Enabled && len(ragResult.Documents) > 0 {
log.Printf("RAG: Found %d documents for Lua analysis", len(ragResult.Documents))
ragContext = "\n\n## 知识库参考资料\n\n" + ragResult.FormatContextForAI()
} else if err != nil {
log.Printf(" RAG query failed: %v", err)
}
}
// 生成用户友好的消息
features := []string{}
for feature := range analysis.Features {
features = append(features, fmt.Sprintf("- %s", feature))
}
userMessage := fmt.Sprintf(`Lua 插件分析完成
## 检测到的特性
%s
## 基本信息
- **复杂度**: %s
- **兼容性**: %s
## 兼容性警告
%s
%s
## 后续操作
- 调用 generate_conversion_hints 获取转换提示
- 或直接使用 convert_lua_to_wasm 一键转换
## 分析结果
`+"```json"+`
%s
`+"```"+`
`,
strings.Join(features, "\n"),
analysis.Complexity,
analysis.Compatibility,
func() string {
if len(analysis.Warnings) > 0 {
return "- " + strings.Join(analysis.Warnings, "\n- ")
}
return "无"
}(),
ragContext,
string(mustMarshalJSON(analysis)),
)
return tools.FormatToolResultWithAIContext(userMessage, "", analysis)
}
func mustMarshalJSON(v interface{}) []byte {
data, _ := json.Marshal(v)
return data
}
func (s *MCPServer) ConvertLuaToWasm(args map[string]interface{}) tools.ToolResult {
luaCode, ok := args["lua_code"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing lua_code"}}}
}
pluginName, ok := args["plugin_name"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing plugin_name"}}}
}
analyzer := tools.AnalyzeLuaScript(luaCode)
result, err := tools.ConvertLuaToWasm(analyzer, pluginName)
if err != nil {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: fmt.Sprintf("Error: %v", err)}}}
}
response := fmt.Sprintf(`Lua脚本转换完成
转换分析:
- 复杂度: %s
- 检测特性: %d个
- 兼容性警告: %d个
注意事项:
%s
生成的文件:
==== main.go ====
%s
==== WasmPlugin配置 ====
%s
部署步骤:
1. 创建插件目录: mkdir -p extensions/%s
2. 保存Go代码到: extensions/%s/main.go
3. 构建插件: PLUGIN_NAME=%s make build
4. 应用配置: kubectl apply -f wasmplugin.yaml
提示:
- 请根据实际需求调整配置
- 测试插件功能后再部署到生产环境
- 如有共享状态需求请配置Redis等外部存储
`,
analyzer.Complexity,
len(analyzer.Features),
len(analyzer.Warnings),
strings.Join(analyzer.Warnings, "\n- "),
result.GoCode,
result.WasmPluginYAML,
pluginName, pluginName, pluginName)
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: response}}}
}
// GenerateConversionHints 生成详细的代码转换提示
func (s *MCPServer) GenerateConversionHints(args map[string]interface{}) tools.ToolResult {
analysisResultStr, ok := args["analysis_result"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing analysis_result"}}}
}
pluginName, ok := args["plugin_name"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing plugin_name"}}}
}
// 解析分析结果
var analysis tools.AnalysisResultForAI
if err := json.Unmarshal([]byte(analysisResultStr), &analysis); err != nil {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: fmt.Sprintf("Error parsing analysis_result: %v", err)}}}
}
// 生成转换提示
hints := tools.GenerateConversionHints(analysis, pluginName)
// === RAG 增强:查询 Nginx API 转换文档 ===
var ragDocs string
// 构建更精确的查询语句
queryBuilder := []string{}
if len(analysis.APICalls) > 0 {
queryBuilder = append(queryBuilder, "Nginx Lua API 转换到 Higress WASM")
// 针对不同的 API 类型使用不同的查询关键词
hasHeaderOps := analysis.Features["header_manipulation"] || analysis.Features["request_headers"] || analysis.Features["response_headers"]
hasBodyOps := analysis.Features["request_body"] || analysis.Features["response_body"]
hasResponseControl := analysis.Features["response_control"]
if hasHeaderOps {
queryBuilder = append(queryBuilder, "请求头和响应头处理")
}
if hasBodyOps {
queryBuilder = append(queryBuilder, "请求体和响应体处理")
}
if hasResponseControl {
queryBuilder = append(queryBuilder, "响应控制和状态码设置")
}
// 添加具体的 API 调用
if len(analysis.APICalls) > 0 && len(analysis.APICalls) <= 5 {
queryBuilder = append(queryBuilder, fmt.Sprintf("涉及 API: %s", strings.Join(analysis.APICalls, ", ")))
}
} else {
queryBuilder = append(queryBuilder, "Higress WASM 插件开发 基础示例 Go SDK 使用")
}
// 添加复杂度相关的查询
if analysis.Complexity == "high" {
queryBuilder = append(queryBuilder, "复杂插件实现 高级功能")
}
queryString := strings.Join(queryBuilder, " ")
// 只有当 RAG 启用时才查询
if s.ragManager != nil && s.ragManager.IsEnabled() {
log.Printf(" RAG Query: %s", queryString)
ragContext, err := s.ragManager.QueryForTool(
"generate_conversion_hints",
queryString,
"lua_migration",
)
if err == nil && ragContext.Enabled && len(ragContext.Documents) > 0 {
log.Printf("RAG: Found %d documents for conversion hints", len(ragContext.Documents))
ragDocs = "\n\n## 参考文档(来自知识库)\n\n" + ragContext.FormatContextForAI()
} else {
if err != nil {
log.Printf(" RAG query failed: %v", err)
}
ragDocs = ""
}
} else {
ragDocs = ""
}
// 格式化输出
userMessage := fmt.Sprintf(` 代码转换提示
**插件名称**: %s
**复杂度**: %s
**兼容性**: %s
%s
## 代码模板
%s
%s
`,
pluginName,
analysis.Complexity,
analysis.Compatibility,
func() string {
if len(hints.Warnings) > 0 {
return "\n**警告**: " + formatWarningsListForUser(hints.Warnings)
}
return ""
}(),
hints.CodeTemplate,
ragDocs,
)
return tools.FormatToolResultWithAIContext(userMessage, "", hints)
}
// ValidateWasmCode 验证生成的 Go WASM 代码
func (s *MCPServer) ValidateWasmCode(args map[string]interface{}) tools.ToolResult {
goCode, ok := args["go_code"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing go_code"}}}
}
pluginName, ok := args["plugin_name"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing plugin_name"}}}
}
// 执行验证
report := tools.ValidateWasmCode(goCode, pluginName)
// 统计各类问题数量
requiredCount := 0
recommendedCount := 0
optionalCount := 0
bestPracticeCount := 0
for _, issue := range report.Issues {
switch issue.Category {
case "required":
requiredCount++
case "recommended":
recommendedCount++
case "optional":
optionalCount++
case "best_practice":
bestPracticeCount++
}
}
// 构建用户消息
userMessage := fmt.Sprintf(`## 代码验证报告
%s
### 发现的回调函数 (%d 个)
%s
### 配置结构
%s
### 问题分类
#### 必须修复 (%d 个)
%s
#### 建议修复 (%d 个)
%s
#### 可选优化 (%d 个)
%s
#### 最佳实践 (%d 个)
%s
### 缺失的导入包 (%d 个)
%s
---
`,
report.Summary,
len(report.FoundCallbacks),
formatCallbacksList(report.FoundCallbacks),
formatConfigStatus(report.HasConfig),
requiredCount,
formatIssuesByCategory(report.Issues, "required"),
recommendedCount,
formatIssuesByCategory(report.Issues, "recommended"),
optionalCount,
formatIssuesByCategory(report.Issues, "optional"),
bestPracticeCount,
formatIssuesByCategory(report.Issues, "best_practice"),
len(report.MissingImports),
formatList(report.MissingImports),
)
// === RAG 增强:查询最佳实践和代码规范 ===
var ragBestPractices string
// 根据验证结果构建更针对性的查询
queryBuilder := []string{"Higress WASM 插件"}
// 根据发现的问题类型添加关键词
if requiredCount > 0 || recommendedCount > 0 {
queryBuilder = append(queryBuilder, "常见错误")
// 检查具体问题类型
for _, issue := range report.Issues {
switch issue.Type {
case "error_handling":
queryBuilder = append(queryBuilder, "错误处理")
case "api_usage":
queryBuilder = append(queryBuilder, "API 使用规范")
case "config":
queryBuilder = append(queryBuilder, "配置解析")
case "logging":
queryBuilder = append(queryBuilder, "日志记录")
}
}
} else {
// 代码已通过基础验证,查询优化建议
queryBuilder = append(queryBuilder, "性能优化 最佳实践")
}
// 根据回调函数类型添加特定查询
for _, callback := range report.FoundCallbacks {
if strings.Contains(callback, "RequestHeaders") {
queryBuilder = append(queryBuilder, "请求头处理")
}
if strings.Contains(callback, "RequestBody") {
queryBuilder = append(queryBuilder, "请求体处理")
}
if strings.Contains(callback, "ResponseHeaders") {
queryBuilder = append(queryBuilder, "响应头处理")
}
}
// 如果有缺失的导入,查询包管理相关信息
if len(report.MissingImports) > 0 {
queryBuilder = append(queryBuilder, "依赖包导入")
}
queryString := strings.Join(queryBuilder, " ")
// 只有当 RAG 启用时才查询
if s.ragManager != nil && s.ragManager.IsEnabled() {
log.Printf("RAG Query: %s", queryString)
ragContext, err := s.ragManager.QueryForTool(
"validate_wasm_code",
queryString,
"best_practice",
)
if err == nil && ragContext.Enabled && len(ragContext.Documents) > 0 {
log.Printf("RAG: Found %d best practice documents", len(ragContext.Documents))
ragBestPractices = "\n\n### 最佳实践建议(来自知识库)\n\n" + ragContext.FormatContextForAI()
userMessage += ragBestPractices
} else {
if err != nil {
log.Printf(" RAG query failed for validation: %v", err)
}
}
}
// 根据问题级别给出建议
hasRequired := requiredCount > 0
if hasRequired {
userMessage += "\n **请优先修复 \"必须修复\" 的问题**\n\n"
} else if recommendedCount > 0 {
userMessage += "\n **代码基本结构正确**,建议修复 \"建议修复\" 的问题\n\n"
} else {
userMessage += "\n **代码验证通过!** 可以调用 `generate_deployment_config` 生成部署配置\n\n"
}
return tools.FormatToolResultWithAIContext(userMessage, "", report)
}
// GenerateDeploymentConfig 生成部署配置
func (s *MCPServer) GenerateDeploymentConfig(args map[string]interface{}) tools.ToolResult {
pluginName, ok := args["plugin_name"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing plugin_name"}}}
}
goCode, ok := args["go_code"].(string)
if !ok {
return tools.ToolResult{Content: []tools.Content{{Type: "text", Text: "Error: Missing go_code"}}}
}
namespace := "higress-system"
if ns, ok := args["namespace"].(string); ok && ns != "" {
namespace = ns
}
configSchema := ""
if cs, ok := args["config_schema"].(string); ok {
configSchema = cs
}
// 生成部署包
pkg := tools.GenerateDeploymentPackage(pluginName, goCode, configSchema, namespace)
// 格式化输出
userMessage := fmt.Sprintf(`🎉 部署配置生成完成!
插件 **%s** 的部署配置已生成(命名空间: %s
## 生成的文件
1. **wasmplugin.yaml** - WasmPlugin 配置
2. **Makefile** - 构建和部署脚本
3. **Dockerfile** - 容器化打包
4. **README.md** - 使用文档
5. **test.sh** - 测试脚本
## 快速部署
`+"```bash"+`
# 构建插件
make build
# 构建并推送镜像
make docker-build docker-push
# 部署
make deploy
# 验证
kubectl get wasmplugin -n %s
`+"```"+`
## 配置文件
### wasmplugin.yaml
`+"```yaml"+`
%s
`+"```"+`
### Makefile
`+"```makefile"+`
%s
`+"```"+`
### Dockerfile
`+"```dockerfile"+`
%s
`+"```"+`
### README.md
`+"```markdown"+`
%s
`+"```"+`
### test.sh
`+"```bash"+`
%s
`+"```"+`
`,
pluginName,
namespace,
namespace,
pkg.WasmPluginYAML,
pkg.Makefile,
pkg.Dockerfile,
pkg.README,
pkg.TestScript,
)
return tools.FormatToolResultWithAIContext(userMessage, "", pkg)
}
// 辅助格式化函数
func formatWarningsListForUser(warnings []string) string {
if len(warnings) == 0 {
return "无"
}
return strings.Join(warnings, "\n- ")
}
func formatCallbacksList(callbacks []string) string {
if len(callbacks) == 0 {
return "无"
}
return "- " + strings.Join(callbacks, "\n- ")
}
func formatConfigStatus(hasConfig bool) string {
if hasConfig {
return " 已定义配置结构体"
}
return "- 未定义配置结构体(如不需要配置可忽略)"
}
func formatIssuesByCategory(issues []tools.ValidationIssue, category string) string {
var filtered []string
for _, issue := range issues {
if issue.Category == category {
filtered = append(filtered, fmt.Sprintf("- **[%s]** %s\n 💡 建议: %s\n 📌 影响: %s",
issue.Type, issue.Message, issue.Suggestion, issue.Impact))
}
}
if len(filtered) == 0 {
return "无"
}
return strings.Join(filtered, "\n\n")
}
func formatList(items []string) string {
if len(items) == 0 {
return "无"
}
return "- " + strings.Join(items, "\n- ")
}
// formatFeatures 格式化特性列表
func formatFeatures(features map[string]bool) string {
featureNames := map[string]string{
"ssl": "SSL/TLS 加密",
"proxy": "反向代理",
"rewrite": "URL 重写",
"redirect": "重定向",
"return": "返回指令",
"complex_routing": "复杂路由匹配",
"header_manipulation": "请求头操作",
"response_headers": "响应头操作",
}
var result []string
for key, enabled := range features {
if enabled {
if name, ok := featureNames[key]; ok {
result = append(result, fmt.Sprintf("- %s", name))
} else {
result = append(result, fmt.Sprintf("- %s", key))
}
}
}
if len(result) == 0 {
return "- 基础配置(无特殊特性)"
}
return strings.Join(result, "\n")
}
// formatSuggestions 格式化建议列表
func formatSuggestions(suggestions []string) string {
if len(suggestions) == 0 {
return "- 无特殊建议"
}
var result []string
for _, s := range suggestions {
result = append(result, fmt.Sprintf("- 💡 %s", s))
}
return strings.Join(result, "\n")
}

View File

@@ -0,0 +1,34 @@
// Common types for nginx migration MCP server - Standalone Mode
package standalone
import (
"nginx-migration-mcp/internal/rag"
)
// MCPMessage represents a Model Context Protocol message structure
type MCPMessage struct {
JSONRPC string `json:"jsonrpc"`
Method string `json:"method,omitempty"`
Params interface{} `json:"params,omitempty"`
ID interface{} `json:"id,omitempty"`
Result interface{} `json:"result,omitempty"`
Error *MCPError `json:"error,omitempty"`
}
type MCPError struct {
Code int `json:"code"`
Message string `json:"message"`
}
type CallToolParams struct {
Name string `json:"name"`
Arguments map[string]interface{} `json:"arguments,omitempty"`
}
type MCPServer struct {
config *ServerConfig
ragManager *rag.RAGManager
}
// MCPServer implements the tools.MCPServer interface
// Method implementations are in server.go

View File

@@ -0,0 +1,405 @@
// Lua to WASM conversion logic for Nginx migration
package tools
import (
"fmt"
"regexp"
"strings"
"text/template"
)
// LuaAnalyzer analyzes Lua script features and generates conversion mappings
type LuaAnalyzer struct {
Features map[string]bool
Variables map[string]string
Functions []LuaFunction
Warnings []string
Complexity string
}
type LuaFunction struct {
Name string
Body string
Phase string // request_headers, request_body, response_headers, etc.
}
// ConversionResult holds the generated WASM plugin code
type ConversionResult struct {
PluginName string
GoCode string
ConfigSchema string
Dependencies []string
WasmPluginYAML string
}
// AnalyzeLuaScript performs detailed analysis of Lua script
func AnalyzeLuaScript(luaCode string) *LuaAnalyzer {
analyzer := &LuaAnalyzer{
Features: make(map[string]bool),
Variables: make(map[string]string),
Functions: []LuaFunction{},
Warnings: []string{},
Complexity: "simple",
}
lines := strings.Split(luaCode, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "--") {
continue
}
// 分析ngx变量使用
analyzer.analyzeNginxVars(line)
// 分析API调用
analyzer.analyzeAPICalls(line)
// 分析函数定义
analyzer.analyzeFunctions(line, luaCode)
}
// 根据特性确定复杂度
analyzer.determineComplexity()
return analyzer
}
func (la *LuaAnalyzer) analyzeNginxVars(line string) {
// 匹配 ngx.var.xxx 模式
varPattern := regexp.MustCompile(`ngx\.var\.(\w+)`)
matches := varPattern.FindAllStringSubmatch(line, -1)
for _, match := range matches {
if len(match) > 1 {
varName := match[1]
la.Features["ngx.var"] = true
// 映射常见变量到WASM等价物
switch varName {
case "uri":
la.Variables[varName] = "proxywasm.GetHttpRequestHeader(\":path\")"
case "request_method":
la.Variables[varName] = "proxywasm.GetHttpRequestHeader(\":method\")"
case "host":
la.Variables[varName] = "proxywasm.GetHttpRequestHeader(\":authority\")"
case "remote_addr":
la.Variables[varName] = "proxywasm.GetHttpRequestHeader(\"x-forwarded-for\")"
case "request_uri":
la.Variables[varName] = "proxywasm.GetHttpRequestHeader(\":path\")"
case "scheme":
la.Variables[varName] = "proxywasm.GetHttpRequestHeader(\":scheme\")"
default:
la.Variables[varName] = fmt.Sprintf("proxywasm.GetHttpRequestHeader(\"%s\")", varName)
}
}
}
}
func (la *LuaAnalyzer) analyzeAPICalls(line string) {
apiCalls := map[string]string{
"ngx.req.get_headers": "request_headers",
"ngx.req.get_body_data": "request_body",
"ngx.req.read_body": "request_body",
"ngx.exit": "response_control",
"ngx.say": "response_control",
"ngx.print": "response_control",
"ngx.shared": "shared_dict",
"ngx.location.capture": "internal_request",
"ngx.req.set_header": "header_manipulation",
"ngx.header": "response_headers",
}
for apiCall, feature := range apiCalls {
if strings.Contains(line, apiCall) {
la.Features[feature] = true
// 添加特定警告
switch feature {
case "shared_dict":
la.Warnings = append(la.Warnings, "共享字典需要使用Redis或其他外部缓存替代")
case "internal_request":
la.Warnings = append(la.Warnings, "内部请求需要改为HTTP客户端调用")
}
}
}
}
func (la *LuaAnalyzer) analyzeFunctions(line string, fullCode string) {
// 检测函数定义
funcPattern := regexp.MustCompile(`function\s+(\w+)\s*\(`)
matches := funcPattern.FindAllStringSubmatch(line, -1)
for _, match := range matches {
if len(match) > 1 {
funcName := match[1]
// 提取函数体 (简化实现)
funcBody := la.extractFunctionBody(fullCode, funcName)
// 根据函数名推断执行阶段
phase := "request_headers"
if strings.Contains(funcName, "body") {
phase = "request_body"
} else if strings.Contains(funcName, "response") {
phase = "response_headers"
}
la.Functions = append(la.Functions, LuaFunction{
Name: funcName,
Body: funcBody,
Phase: phase,
})
}
}
}
func (la *LuaAnalyzer) extractFunctionBody(fullCode, funcName string) string {
// 简化的函数体提取 - 实际实现应该更复杂
pattern := fmt.Sprintf(`function\s+%s\s*\([^)]*\)(.*?)end`, funcName)
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(fullCode)
if len(match) > 1 {
return strings.TrimSpace(match[1])
}
return ""
}
func (la *LuaAnalyzer) determineComplexity() {
warningCount := len(la.Warnings)
featureCount := len(la.Features)
if warningCount > 3 || featureCount > 6 {
la.Complexity = "complex"
} else if warningCount > 1 || featureCount > 3 {
la.Complexity = "medium"
}
}
// ConvertLuaToWasm converts analyzed Lua script to WASM plugin
func ConvertLuaToWasm(analyzer *LuaAnalyzer, pluginName string) (*ConversionResult, error) {
result := &ConversionResult{
PluginName: pluginName,
Dependencies: []string{
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm",
"github.com/higress-group/wasm-go/pkg/wrapper",
"github.com/higress-group/wasm-go/pkg/log",
},
}
// 生成Go代码
goCode, err := generateGoCode(analyzer, pluginName)
if err != nil {
return nil, err
}
result.GoCode = goCode
// 生成配置模式
result.ConfigSchema = generateConfigSchema(analyzer)
// 生成WasmPlugin YAML
result.WasmPluginYAML = generateWasmPluginYAML(pluginName)
return result, nil
}
func generateGoCode(analyzer *LuaAnalyzer, pluginName string) (string, error) {
tmpl := `// Generated WASM plugin from Lua script
// Plugin: {{.PluginName}}
package main
import (
"net/http"
"strings"
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
"github.com/higress-group/wasm-go/pkg/log"
"github.com/higress-group/wasm-go/pkg/wrapper"
"github.com/tidwall/gjson"
)
func main() {}
func init() {
wrapper.SetCtx(
"{{.PluginName}}",
wrapper.ParseConfigBy(parseConfig),
{{- if .HasRequestHeaders}}
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
{{- end}}
{{- if .HasRequestBody}}
wrapper.ProcessRequestBodyBy(onHttpRequestBody),
{{- end}}
{{- if .HasResponseHeaders}}
wrapper.ProcessResponseHeadersBy(onHttpResponseHeaders),
{{- end}}
)
}
type {{.ConfigTypeName}} struct {
// Generated from Lua analysis
{{- range .ConfigFields}}
{{.Name}} {{.Type}} ` + "`json:\"{{.JSONName}}\"`" + `
{{- end}}
}
func parseConfig(json gjson.Result, config *{{.ConfigTypeName}}, log log.Log) error {
{{- range .ConfigFields}}
config.{{.Name}} = json.Get("{{.JSONName}}").{{.ParseMethod}}()
{{- end}}
return nil
}
{{- if .HasRequestHeaders}}
func onHttpRequestHeaders(ctx wrapper.HttpContext, config {{.ConfigTypeName}}, log log.Log) types.Action {
{{.RequestHeadersLogic}}
return types.ActionContinue
}
{{- end}}
{{- if .HasRequestBody}}
func onHttpRequestBody(ctx wrapper.HttpContext, config {{.ConfigTypeName}}, body []byte, log log.Log) types.Action {
{{.RequestBodyLogic}}
return types.ActionContinue
}
{{- end}}
{{- if .HasResponseHeaders}}
func onHttpResponseHeaders(ctx wrapper.HttpContext, config {{.ConfigTypeName}}, log log.Log) types.Action {
{{.ResponseHeadersLogic}}
return types.ActionContinue
}
{{- end}}
`
// 准备模板数据
data := map[string]interface{}{
"PluginName": pluginName,
"ConfigTypeName": strings.Title(pluginName) + "Config",
"HasRequestHeaders": analyzer.Features["request_headers"] || analyzer.Features["ngx.var"],
"HasRequestBody": analyzer.Features["request_body"],
"HasResponseHeaders": analyzer.Features["response_headers"] || analyzer.Features["response_control"],
"ConfigFields": generateConfigFields(analyzer),
"RequestHeadersLogic": generateRequestHeadersLogic(analyzer),
"RequestBodyLogic": generateRequestBodyLogic(analyzer),
"ResponseHeadersLogic": generateResponseHeadersLogic(analyzer),
}
t, err := template.New("wasm").Parse(tmpl)
if err != nil {
return "", err
}
var buf strings.Builder
err = t.Execute(&buf, data)
if err != nil {
return "", err
}
return buf.String(), nil
}
func generateConfigFields(analyzer *LuaAnalyzer) []map[string]string {
fields := []map[string]string{}
// 基于分析的特性生成配置字段
if analyzer.Features["response_control"] {
fields = append(fields, map[string]string{
"Name": "EnableCustomResponse",
"Type": "bool",
"JSONName": "enable_custom_response",
"ParseMethod": "Bool",
})
}
return fields
}
func generateRequestHeadersLogic(analyzer *LuaAnalyzer) string {
logic := []string{}
// 基于变量使用生成逻辑
for varName, wasmCall := range analyzer.Variables {
logic = append(logic, fmt.Sprintf(`
// Access to ngx.var.%s
%s, err := %s
if err != nil {
log.Warnf("Failed to get %s: %%v", err)
}`, varName, varName, wasmCall, varName))
}
if analyzer.Features["header_manipulation"] {
logic = append(logic, `
// Header manipulation logic
err := proxywasm.AddHttpRequestHeader("x-converted-from", "nginx-lua")
if err != nil {
log.Warnf("Failed to add header: %v", err)
}`)
}
return strings.Join(logic, "\n")
}
func generateRequestBodyLogic(analyzer *LuaAnalyzer) string {
if analyzer.Features["request_body"] {
return `
// Process request body
bodyStr := string(body)
log.Infof("Processing request body: %s", bodyStr)
// Add your body processing logic here
`
}
return "// No request body processing needed"
}
func generateResponseHeadersLogic(analyzer *LuaAnalyzer) string {
if analyzer.Features["response_control"] {
return `
// Response control logic
if config.EnableCustomResponse {
proxywasm.SendHttpResponseWithDetail(200, "lua-converted", nil, []byte("Response from converted Lua plugin"), -1)
return types.ActionContinue
}
`
}
return "// No response processing needed"
}
func generateConfigSchema(analyzer *LuaAnalyzer) string {
schema := `{
"type": "object",
"properties": {`
properties := []string{}
if analyzer.Features["response_control"] {
properties = append(properties, `
"enable_custom_response": {
"type": "boolean",
"description": "Enable custom response handling"
}`)
}
schema += strings.Join(properties, ",")
schema += `
}
}`
return schema
}
func generateWasmPluginYAML(pluginName string) string {
return fmt.Sprintf(`apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: %s
namespace: higress-system
spec:
defaultConfig:
enable_custom_response: true
url: oci://your-registry/%s:latest
`, pluginName, pluginName)
}

View File

@@ -0,0 +1,223 @@
// MCP Tools Definitions
// 定义所有可用的MCP工具及其描述信息
package tools
import (
"encoding/json"
"os"
)
// MCPTool represents a tool definition in MCP protocol
type MCPTool struct {
Name string `json:"name"`
Description string `json:"description"`
InputSchema json.RawMessage `json:"inputSchema"`
}
// ToolResult represents the result of a tool call
type ToolResult struct {
Content []Content `json:"content"`
}
// Content represents content within a tool result
type Content struct {
Type string `json:"type"`
Text string `json:"text"`
}
// MCPServer is an interface for server methods needed by tool handlers
type MCPServer interface {
ParseNginxConfig(args map[string]interface{}) ToolResult
ConvertToHigress(args map[string]interface{}) ToolResult
AnalyzeLuaPlugin(args map[string]interface{}) ToolResult
ConvertLuaToWasm(args map[string]interface{}) ToolResult
// 新增工具链方法
GenerateConversionHints(args map[string]interface{}) ToolResult
ValidateWasmCode(args map[string]interface{}) ToolResult
GenerateDeploymentConfig(args map[string]interface{}) ToolResult
}
// MCPToolsConfig 工具配置文件结构
type MCPToolsConfig struct {
Version string `json:"version"`
Name string `json:"name"`
Description string `json:"description"`
Tools []MCPTool `json:"tools"`
}
// LoadToolsFromFile 从 JSON 文件加载工具定义
func LoadToolsFromFile(filename string) ([]MCPTool, error) {
data, err := os.ReadFile(filename)
if err != nil {
// 文件不存在时使用默认配置
return GetMCPToolsDefault(), nil
}
var config MCPToolsConfig
if err := json.Unmarshal(data, &config); err != nil {
return nil, err
}
return config.Tools, nil
}
// GetMCPTools 返回所有可用的 MCP 工具定义
// 优先从 mcp-tools.json 加载,失败时使用默认定义
func GetMCPTools() []MCPTool {
tools, err := LoadToolsFromFile("mcp-tools.json")
if err != nil {
return GetMCPToolsDefault()
}
return tools
}
// GetMCPToolsDefault 返回默认的工具定义(包含完整工具链)
func GetMCPToolsDefault() []MCPTool {
return []MCPTool{
{
Name: "parse_nginx_config",
Description: "解析和分析 Nginx 配置文件,识别配置结构和复杂度",
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"config_content": {
"type": "string",
"description": "Nginx 配置文件内容"
}
},
"required": ["config_content"]
}`),
},
{
Name: "convert_to_higress",
Description: "将 Nginx 配置转换为 Higress HTTPRoute 和 Service 资源",
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"config_content": {
"type": "string",
"description": "Nginx 配置文件内容"
},
"namespace": {
"type": "string",
"description": "目标 Kubernetes 命名空间",
"default": "default"
}
},
"required": ["config_content"]
}`),
},
{
Name: "analyze_lua_plugin",
Description: "分析 Nginx Lua 插件的兼容性,识别使用的 API 和潜在迁移问题,返回结构化分析结果",
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"lua_code": {
"type": "string",
"description": "Nginx Lua 插件代码"
}
},
"required": ["lua_code"]
}`),
},
{
Name: "generate_conversion_hints",
Description: "基于 Lua 分析结果生成代码转换模板",
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"analysis_result": {
"type": "string",
"description": "analyze_lua_plugin 返回的 JSON 格式分析结果"
},
"plugin_name": {
"type": "string",
"description": "目标插件名称(小写字母和连字符)"
}
},
"required": ["analysis_result", "plugin_name"]
}`),
},
{
Name: "validate_wasm_code",
Description: "验证生成的 Go WASM 插件代码检查语法、API 使用、配置结构等,输出验证报告和改进建议",
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"go_code": {
"type": "string",
"description": "生成的 Go WASM 插件代码"
},
"plugin_name": {
"type": "string",
"description": "插件名称"
}
},
"required": ["go_code", "plugin_name"]
}`),
},
{
Name: "generate_deployment_config",
Description: "为验证通过的 WASM 插件生成完整的部署配置包,包括 WasmPlugin YAML、Makefile、Dockerfile、README 和测试脚本",
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"plugin_name": {
"type": "string",
"description": "插件名称"
},
"go_code": {
"type": "string",
"description": "验证通过的 Go 代码"
},
"config_schema": {
"type": "string",
"description": "配置 JSON Schema可选"
},
"namespace": {
"type": "string",
"description": "部署命名空间",
"default": "higress-system"
}
},
"required": ["plugin_name", "go_code"]
}`),
},
{
Name: "convert_lua_to_wasm",
Description: "一键将 Nginx Lua 脚本转换为 Higress WASM 插件,自动生成 Go 代码和 WasmPlugin 配置。适合简单插件快速转换",
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"lua_code": {
"type": "string",
"description": "要转换的 Nginx Lua 插件代码"
},
"plugin_name": {
"type": "string",
"description": "生成的 WASM 插件名称 (小写字母和连字符)"
}
},
"required": ["lua_code", "plugin_name"]
}`),
},
}
}
// ToolHandler 定义工具处理函数的类型
type ToolHandler func(args map[string]interface{}) ToolResult
// GetToolHandlers 返回工具名称到处理函数的映射
func GetToolHandlers(s MCPServer) map[string]ToolHandler {
return map[string]ToolHandler{
"parse_nginx_config": s.ParseNginxConfig,
"convert_to_higress": s.ConvertToHigress,
"analyze_lua_plugin": s.AnalyzeLuaPlugin,
"convert_lua_to_wasm": s.ConvertLuaToWasm,
// 新增工具链处理器
"generate_conversion_hints": s.GenerateConversionHints,
"validate_wasm_code": s.ValidateWasmCode,
"generate_deployment_config": s.GenerateDeploymentConfig,
}
}

View File

@@ -0,0 +1,426 @@
// Package tools provides Nginx configuration parsing and analysis capabilities.
// This intelligent parser extracts semantic information from Nginx configs for AI reasoning.
package tools
import (
"fmt"
"regexp"
"strings"
)
// NginxConfig 表示解析后的 Nginx 配置结构
type NginxConfig struct {
Servers []NginxServer `json:"servers"`
Upstreams []NginxUpstream `json:"upstreams"`
Raw string `json:"raw"`
}
// NginxServer 表示一个 server 块
type NginxServer struct {
Listen []string `json:"listen"` // 监听端口和地址
ServerNames []string `json:"server_names"` // 域名列表
Locations []NginxLocation `json:"locations"` // location 块列表
SSL *NginxSSL `json:"ssl,omitempty"` // SSL 配置
Directives map[string][]string `json:"directives"` // 其他指令
}
// NginxLocation 表示一个 location 块
type NginxLocation struct {
Path string `json:"path"` // 路径
Modifier string `json:"modifier"` // 修饰符(=, ~, ~*, ^~
ProxyPass string `json:"proxy_pass,omitempty"` // 代理目标
Rewrite []string `json:"rewrite,omitempty"` // rewrite 规则
Return *NginxReturn `json:"return,omitempty"` // return 指令
Directives map[string][]string `json:"directives"` // 其他指令
}
// NginxSSL 表示 SSL 配置
type NginxSSL struct {
Certificate string `json:"certificate,omitempty"`
CertificateKey string `json:"certificate_key,omitempty"`
Protocols []string `json:"protocols,omitempty"`
Ciphers string `json:"ciphers,omitempty"`
}
// NginxReturn 表示 return 指令
type NginxReturn struct {
Code int `json:"code"`
URL string `json:"url,omitempty"`
Text string `json:"text,omitempty"`
}
// NginxUpstream 表示 upstream 块
type NginxUpstream struct {
Name string `json:"name"`
Servers []string `json:"servers"`
Method string `json:"method,omitempty"` // 负载均衡方法
}
// ParseNginxConfig 解析 Nginx 配置内容
func ParseNginxConfig(content string) (*NginxConfig, error) {
config := &NginxConfig{
Raw: content,
Servers: []NginxServer{},
Upstreams: []NginxUpstream{},
}
// 解析 upstream 块
upstreams := extractUpstreams(content)
config.Upstreams = upstreams
// 解析 server 块
servers := extractServers(content)
for _, serverContent := range servers {
server := parseServer(serverContent)
config.Servers = append(config.Servers, server)
}
return config, nil
}
// extractUpstreams 提取所有 upstream 块
func extractUpstreams(content string) []NginxUpstream {
upstreams := []NginxUpstream{}
upstreamRegex := regexp.MustCompile(`upstream\s+(\S+)\s*\{([^}]*)\}`)
matches := upstreamRegex.FindAllStringSubmatch(content, -1)
for _, match := range matches {
if len(match) >= 3 {
name := match[1]
body := match[2]
upstream := NginxUpstream{
Name: name,
Servers: []string{},
}
// 提取 server 指令
serverRegex := regexp.MustCompile(`server\s+([^;]+);`)
serverMatches := serverRegex.FindAllStringSubmatch(body, -1)
for _, sm := range serverMatches {
if len(sm) >= 2 {
upstream.Servers = append(upstream.Servers, strings.TrimSpace(sm[1]))
}
}
// 检测负载均衡方法
if strings.Contains(body, "ip_hash") {
upstream.Method = "ip_hash"
} else if strings.Contains(body, "least_conn") {
upstream.Method = "least_conn"
}
upstreams = append(upstreams, upstream)
}
}
return upstreams
}
// extractServers 提取所有 server 块的内容
func extractServers(content string) []string {
servers := []string{}
// 简单的大括号匹配提取
lines := strings.Split(content, "\n")
inServer := false
braceCount := 0
var currentServer strings.Builder
for _, line := range lines {
trimmed := strings.TrimSpace(line)
// 检测 server 块开始
if strings.HasPrefix(trimmed, "server") && strings.Contains(trimmed, "{") {
inServer = true
currentServer.Reset()
currentServer.WriteString(line + "\n")
braceCount = strings.Count(line, "{") - strings.Count(line, "}")
continue
}
if inServer {
currentServer.WriteString(line + "\n")
braceCount += strings.Count(line, "{") - strings.Count(line, "}")
if braceCount == 0 {
servers = append(servers, currentServer.String())
inServer = false
}
}
}
return servers
}
// parseServer 解析单个 server 块
func parseServer(content string) NginxServer {
server := NginxServer{
Listen: []string{},
ServerNames: []string{},
Locations: []NginxLocation{},
Directives: make(map[string][]string),
}
lines := strings.Split(content, "\n")
// 解析 listen 指令
listenRegex := regexp.MustCompile(`^\s*listen\s+([^;]+);`)
for _, line := range lines {
if match := listenRegex.FindStringSubmatch(line); match != nil {
server.Listen = append(server.Listen, strings.TrimSpace(match[1]))
}
}
// 解析 server_name 指令
serverNameRegex := regexp.MustCompile(`^\s*server_name\s+([^;]+);`)
for _, line := range lines {
if match := serverNameRegex.FindStringSubmatch(line); match != nil {
names := strings.Fields(match[1])
server.ServerNames = append(server.ServerNames, names...)
}
}
// 解析 SSL 配置
server.SSL = parseSSL(content)
// 解析 location 块
server.Locations = extractLocations(content)
// 解析其他常见指令
commonDirectives := []string{
"root", "index", "access_log", "error_log",
"client_max_body_size", "proxy_set_header",
}
for _, directive := range commonDirectives {
pattern := fmt.Sprintf(`(?m)^\s*%s\s+([^;]+);`, directive)
regex := regexp.MustCompile(pattern)
matches := regex.FindAllStringSubmatch(content, -1)
for _, match := range matches {
if len(match) >= 2 {
server.Directives[directive] = append(server.Directives[directive], strings.TrimSpace(match[1]))
}
}
}
return server
}
// parseSSL 解析 SSL 配置
func parseSSL(content string) *NginxSSL {
hasSSL := strings.Contains(content, "ssl") || strings.Contains(content, "443")
if !hasSSL {
return nil
}
ssl := &NginxSSL{
Protocols: []string{},
}
// 提取证书路径
certRegex := regexp.MustCompile(`ssl_certificate\s+([^;]+);`)
if match := certRegex.FindStringSubmatch(content); match != nil {
ssl.Certificate = strings.TrimSpace(match[1])
}
// 提取私钥路径
keyRegex := regexp.MustCompile(`ssl_certificate_key\s+([^;]+);`)
if match := keyRegex.FindStringSubmatch(content); match != nil {
ssl.CertificateKey = strings.TrimSpace(match[1])
}
// 提取协议
protocolRegex := regexp.MustCompile(`ssl_protocols\s+([^;]+);`)
if match := protocolRegex.FindStringSubmatch(content); match != nil {
ssl.Protocols = strings.Fields(match[1])
}
// 提取加密套件
cipherRegex := regexp.MustCompile(`ssl_ciphers\s+([^;]+);`)
if match := cipherRegex.FindStringSubmatch(content); match != nil {
ssl.Ciphers = strings.TrimSpace(match[1])
}
return ssl
}
// extractLocations 提取所有 location 块
func extractLocations(content string) []NginxLocation {
locations := []NginxLocation{}
// 匹配 location 块
locationRegex := regexp.MustCompile(`location\s+(=|~|~\*|\^~)?\s*([^\s{]+)\s*\{([^}]*)\}`)
matches := locationRegex.FindAllStringSubmatch(content, -1)
for _, match := range matches {
if len(match) >= 4 {
modifier := strings.TrimSpace(match[1])
path := strings.TrimSpace(match[2])
body := match[3]
location := NginxLocation{
Path: path,
Modifier: modifier,
Rewrite: []string{},
Directives: make(map[string][]string),
}
// 提取 proxy_pass
proxyPassRegex := regexp.MustCompile(`proxy_pass\s+([^;]+);`)
if ppMatch := proxyPassRegex.FindStringSubmatch(body); ppMatch != nil {
location.ProxyPass = strings.TrimSpace(ppMatch[1])
}
// 提取 rewrite 规则
rewriteRegex := regexp.MustCompile(`rewrite\s+([^;]+);`)
rewriteMatches := rewriteRegex.FindAllStringSubmatch(body, -1)
for _, rm := range rewriteMatches {
if len(rm) >= 2 {
location.Rewrite = append(location.Rewrite, strings.TrimSpace(rm[1]))
}
}
// 提取 return 指令
returnRegex := regexp.MustCompile(`return\s+(\d+)(?:\s+([^;]+))?;`)
if retMatch := returnRegex.FindStringSubmatch(body); retMatch != nil {
code := 0
fmt.Sscanf(retMatch[1], "%d", &code)
location.Return = &NginxReturn{
Code: code,
}
if len(retMatch) >= 3 {
urlOrText := strings.TrimSpace(retMatch[2])
if strings.HasPrefix(urlOrText, "http") {
location.Return.URL = urlOrText
} else {
location.Return.Text = urlOrText
}
}
}
// 提取其他指令
commonDirectives := []string{
"proxy_set_header", "proxy_redirect", "proxy_read_timeout",
"add_header", "alias", "root", "try_files",
}
for _, directive := range commonDirectives {
pattern := fmt.Sprintf(`(?m)^\s*%s\s+([^;]+);`, directive)
regex := regexp.MustCompile(pattern)
matches := regex.FindAllStringSubmatch(body, -1)
for _, m := range matches {
if len(m) >= 2 {
location.Directives[directive] = append(location.Directives[directive], strings.TrimSpace(m[1]))
}
}
}
locations = append(locations, location)
}
}
return locations
}
// AnalyzeNginxConfig 分析 Nginx 配置,生成用于 AI 的分析报告
func AnalyzeNginxConfig(config *NginxConfig) *NginxAnalysis {
analysis := &NginxAnalysis{
ServerCount: len(config.Servers),
Features: make(map[string]bool),
Complexity: "simple",
Suggestions: []string{},
}
totalLocations := 0
hasSSL := false
hasRewrite := false
hasUpstream := len(config.Upstreams) > 0
hasComplexRouting := false
uniqueDomains := make(map[string]bool)
for _, server := range config.Servers {
// 统计域名
for _, name := range server.ServerNames {
uniqueDomains[name] = true
}
// 统计 location
totalLocations += len(server.Locations)
// 检测 SSL
if server.SSL != nil {
hasSSL = true
analysis.Features["ssl"] = true
}
// 检测 location 特性
for _, loc := range server.Locations {
if loc.ProxyPass != "" {
analysis.Features["proxy"] = true
}
if len(loc.Rewrite) > 0 {
hasRewrite = true
analysis.Features["rewrite"] = true
}
if loc.Return != nil {
analysis.Features["return"] = true
if loc.Return.Code >= 300 && loc.Return.Code < 400 {
analysis.Features["redirect"] = true
}
}
if loc.Modifier != "" {
hasComplexRouting = true
analysis.Features["complex_routing"] = true
}
// 检测其他指令
if _, ok := loc.Directives["proxy_set_header"]; ok {
analysis.Features["header_manipulation"] = true
}
if _, ok := loc.Directives["add_header"]; ok {
analysis.Features["response_headers"] = true
}
}
}
analysis.LocationCount = totalLocations
analysis.DomainCount = len(uniqueDomains)
// 判断复杂度
if analysis.ServerCount > 3 || totalLocations > 10 || (hasRewrite && hasSSL && hasComplexRouting) {
analysis.Complexity = "high"
} else if analysis.ServerCount > 1 || totalLocations > 5 || hasRewrite || hasSSL || hasUpstream {
analysis.Complexity = "medium"
}
// 生成建议
if analysis.Features["proxy"] {
analysis.Suggestions = append(analysis.Suggestions, "proxy_pass 将转换为 Ingress/HTTPRoute 的 backend 配置")
}
if analysis.Features["rewrite"] {
analysis.Suggestions = append(analysis.Suggestions, "rewrite 规则需要使用 Higress 注解实现,如 higress.io/rewrite-target")
}
if analysis.Features["ssl"] {
analysis.Suggestions = append(analysis.Suggestions, "SSL 证书需要创建 Kubernetes Secret并在 Ingress 中引用")
}
if analysis.Features["redirect"] {
analysis.Suggestions = append(analysis.Suggestions, "redirect 可以使用 Higress 的重定向注解或插件实现")
}
if hasUpstream {
analysis.Suggestions = append(analysis.Suggestions, "upstream 负载均衡将由 Kubernetes Service 和 Endpoints 实现")
}
if analysis.Features["header_manipulation"] {
analysis.Suggestions = append(analysis.Suggestions, "请求头操作可以使用 Higress 注解或 custom-response 插件实现")
}
return analysis
}
// NginxAnalysis 表示 Nginx 配置分析结果
type NginxAnalysis struct {
ServerCount int `json:"server_count"`
LocationCount int `json:"location_count"`
DomainCount int `json:"domain_count"`
Features map[string]bool `json:"features"`
Complexity string `json:"complexity"` // simple, medium, high
Suggestions []string `json:"suggestions"`
}

View File

@@ -0,0 +1,448 @@
// Tool Chain implementations for LLM-guided Lua to WASM conversion
package tools
import (
"encoding/json"
"fmt"
"regexp"
"strings"
)
// AnalysisResultForAI 结构化的分析结果,用于 AI 协作
type AnalysisResultForAI struct {
Features map[string]bool `json:"features"`
Variables map[string]string `json:"variables"`
APICalls []string `json:"api_calls"`
Warnings []string `json:"warnings"`
Complexity string `json:"complexity"`
Compatibility string `json:"compatibility"`
// OriginalCode 字段已移除,避免返回大量数据
}
// ConversionHints 代码转换提示(简化版)
type ConversionHints struct {
CodeTemplate string `json:"code_template"`
Warnings []string `json:"warnings"`
}
// ValidationReport 验证报告
type ValidationReport struct {
Issues []ValidationIssue `json:"issues"` // 所有发现的问题
MissingImports []string `json:"missing_imports"` // 缺失的 import
FoundCallbacks []string `json:"found_callbacks"` // 找到的回调函数
HasConfig bool `json:"has_config"` // 是否有配置结构
Summary string `json:"summary"` // 总体评估摘要
}
// ValidationIssue 验证问题
type ValidationIssue struct {
Category string `json:"category"` // required, recommended, optional, best_practice
Type string `json:"type"` // syntax, api_usage, config, error_handling, logging, etc.
Message string `json:"message"` // 问题描述
Suggestion string `json:"suggestion"` // 改进建议
Impact string `json:"impact"` // 影响说明(为什么重要)
}
// DeploymentPackage 部署配置包
type DeploymentPackage struct {
WasmPluginYAML string `json:"wasm_plugin_yaml"`
Makefile string `json:"makefile"`
Dockerfile string `json:"dockerfile"`
ConfigMap string `json:"config_map"`
README string `json:"readme"`
TestScript string `json:"test_script"`
Dependencies map[string]string `json:"dependencies"`
}
// AnalyzeLuaPluginForAI 分析 Lua 插件并生成 AI 友好的输出
func AnalyzeLuaPluginForAI(luaCode string) AnalysisResultForAI {
analyzer := AnalyzeLuaScript(luaCode)
// 收集所有 API 调用
apiCalls := []string{}
for feature := range analyzer.Features {
apiCalls = append(apiCalls, feature)
}
// 确定兼容性级别
compatibility := "full"
if len(analyzer.Warnings) > 0 {
compatibility = "partial"
}
if len(analyzer.Warnings) > 2 {
compatibility = "manual"
}
return AnalysisResultForAI{
Features: analyzer.Features,
Variables: analyzer.Variables,
APICalls: apiCalls,
Warnings: analyzer.Warnings,
Complexity: analyzer.Complexity,
Compatibility: compatibility,
}
}
// GenerateConversionHints 生成代码转换提示(简化版)
func GenerateConversionHints(analysis AnalysisResultForAI, pluginName string) ConversionHints {
return ConversionHints{
CodeTemplate: generateCodeTemplate(analysis, pluginName),
Warnings: analysis.Warnings,
}
}
// generateCodeTemplate 生成代码模板提示
func generateCodeTemplate(analysis AnalysisResultForAI, pluginName string) string {
callbacks := generateCallbackSummary(analysis)
return fmt.Sprintf(`生成 Go WASM 插件 %s实现回调: %s
参考文档: https://higress.cn/docs/latest/user/wasm-go/`,
pluginName, callbacks)
}
// generateCallbackRegistrations 生成回调注册代码
func generateCallbackRegistrations(analysis AnalysisResultForAI) string {
callbacks := []string{}
if analysis.Features["ngx.var"] || analysis.Features["request_headers"] || analysis.Features["header_manipulation"] {
callbacks = append(callbacks, "wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders)")
}
if analysis.Features["request_body"] {
callbacks = append(callbacks, "wrapper.ProcessRequestBodyBy(onHttpRequestBody)")
}
if analysis.Features["response_headers"] || analysis.Features["response_control"] {
callbacks = append(callbacks, "wrapper.ProcessResponseHeadersBy(onHttpResponseHeaders)")
}
if len(callbacks) == 0 {
callbacks = append(callbacks, "wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders)")
}
return "\n\t\t" + strings.Join(callbacks, ",\n\t\t")
}
// generateCallbackSummary 生成回调函数摘要
func generateCallbackSummary(analysis AnalysisResultForAI) string {
callbacks := []string{}
if analysis.Features["ngx.var"] || analysis.Features["request_headers"] || analysis.Features["header_manipulation"] {
callbacks = append(callbacks, "onHttpRequestHeaders")
}
if analysis.Features["request_body"] {
callbacks = append(callbacks, "onHttpRequestBody")
}
if analysis.Features["response_headers"] || analysis.Features["response_control"] {
callbacks = append(callbacks, "onHttpResponseHeaders")
}
if len(callbacks) == 0 {
return "onHttpRequestHeaders"
}
return strings.Join(callbacks, ", ")
}
// ValidateWasmCode 验证生成的 Go WASM 代码
func ValidateWasmCode(goCode, pluginName string) ValidationReport {
report := ValidationReport{
Issues: []ValidationIssue{},
MissingImports: []string{},
FoundCallbacks: []string{},
HasConfig: false,
}
// 移除注释以避免误判
codeWithoutComments := removeComments(goCode)
// 检查必要的包声明
packagePattern := regexp.MustCompile(`(?m)^package\s+main\s*$`)
if !packagePattern.MatchString(goCode) {
report.Issues = append(report.Issues, ValidationIssue{
Category: "required",
Type: "syntax",
Message: "缺少 'package main' 声明",
Suggestion: "在文件开头添加: package main",
Impact: "WASM 插件必须使用 package main否则无法编译",
})
}
// 检查 main 函数
mainFuncPattern := regexp.MustCompile(`func\s+main\s*\(\s*\)`)
if !mainFuncPattern.MatchString(codeWithoutComments) {
report.Issues = append(report.Issues, ValidationIssue{
Category: "required",
Type: "syntax",
Message: "缺少 main() 函数",
Suggestion: "添加空的 main 函数: func main() {}",
Impact: "WASM 插件必须有 main 函数,即使是空的",
})
}
// 检查 init 函数
initFuncPattern := regexp.MustCompile(`func\s+init\s*\(\s*\)`)
if !initFuncPattern.MatchString(codeWithoutComments) {
report.Issues = append(report.Issues, ValidationIssue{
Category: "required",
Type: "api_usage",
Message: "缺少 init() 函数",
Suggestion: "添加 init() 函数用于注册插件",
Impact: "插件需要在 init() 中调用 wrapper.SetCtx 进行注册",
})
}
// 检查 wrapper.SetCtx 调用
setCtxPattern := regexp.MustCompile(`wrapper\.SetCtx\s*\(`)
if !setCtxPattern.MatchString(codeWithoutComments) {
report.Issues = append(report.Issues, ValidationIssue{
Category: "required",
Type: "api_usage",
Message: "缺少 wrapper.SetCtx 调用",
Suggestion: "在 init() 函数中调用 wrapper.SetCtx 注册插件上下文",
Impact: "没有注册插件上下文将导致插件无法工作",
})
}
// 检查必要的 import
requiredImports := map[string]string{
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types": "定义了 Action 等核心类型",
"github.com/higress-group/wasm-go/pkg/wrapper": "提供了 Higress 插件开发的高级封装",
}
for importPath, reason := range requiredImports {
if !containsImport(goCode, importPath) {
report.MissingImports = append(report.MissingImports, importPath)
report.Issues = append(report.Issues, ValidationIssue{
Category: "required",
Type: "imports",
Message: fmt.Sprintf("缺少必需的导入: %s", importPath),
Suggestion: fmt.Sprintf(`添加导入: import "%s"`, importPath),
Impact: reason,
})
}
}
// 检查可选但推荐的 import
if !containsImport(goCode, "github.com/higress-group/proxy-wasm-go-sdk/proxywasm") {
report.Issues = append(report.Issues, ValidationIssue{
Category: "optional",
Type: "imports",
Message: "未导入 proxywasm 包",
Suggestion: "如需使用日志、HTTP 调用等底层 API可导入 proxywasm 包",
Impact: "proxywasm 提供了日志记录、外部 HTTP 调用等功能",
})
}
// 检查配置结构体
configPattern := regexp.MustCompile(`type\s+\w+Config\s+struct\s*\{`)
report.HasConfig = configPattern.MatchString(goCode)
if !report.HasConfig {
report.Issues = append(report.Issues, ValidationIssue{
Category: "optional",
Type: "config",
Message: "未定义配置结构体",
Suggestion: "如果插件需要配置参数,建议定义配置结构体(如 type MyPluginConfig struct { ... }",
Impact: "配置结构体用于接收和解析插件的配置参数,支持动态配置",
})
}
// 检查 parseConfig 函数
parseConfigPattern := regexp.MustCompile(`func\s+parseConfig\s*\(`)
hasParseConfig := parseConfigPattern.MatchString(codeWithoutComments)
if report.HasConfig && !hasParseConfig {
report.Issues = append(report.Issues, ValidationIssue{
Category: "recommended",
Type: "config",
Message: "定义了配置结构体但缺少 parseConfig 函数",
Suggestion: "实现 parseConfig 函数来解析配置: func parseConfig(json gjson.Result, config *MyPluginConfig, log wrapper.Log) error",
Impact: "parseConfig 函数负责将 JSON 配置解析到结构体,是配置系统的核心",
})
}
// 检查回调函数
callbacks := map[string]*regexp.Regexp{
"onHttpRequestHeaders": regexp.MustCompile(`func\s+onHttpRequestHeaders\s*\(`),
"onHttpRequestBody": regexp.MustCompile(`func\s+onHttpRequestBody\s*\(`),
"onHttpResponseHeaders": regexp.MustCompile(`func\s+onHttpResponseHeaders\s*\(`),
"onHttpResponseBody": regexp.MustCompile(`func\s+onHttpResponseBody\s*\(`),
}
for name, pattern := range callbacks {
if pattern.MatchString(codeWithoutComments) {
report.FoundCallbacks = append(report.FoundCallbacks, name)
}
}
if len(report.FoundCallbacks) == 0 {
report.Issues = append(report.Issues, ValidationIssue{
Category: "required",
Type: "api_usage",
Message: "未找到任何 HTTP 回调函数实现",
Suggestion: "至少实现一个回调函数,如: func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyPluginConfig, log wrapper.Log) types.Action",
Impact: "回调函数是插件逻辑的核心,没有回调函数插件将不会执行任何操作",
})
}
// 检查错误处理
errHandlingCount := strings.Count(codeWithoutComments, "if err != nil")
funcCount := strings.Count(codeWithoutComments, "func ")
if funcCount > 3 && errHandlingCount == 0 {
report.Issues = append(report.Issues, ValidationIssue{
Category: "best_practice",
Type: "error_handling",
Message: "代码中缺少错误处理",
Suggestion: "对可能返回错误的操作添加错误检查: if err != nil { ... }",
Impact: "良好的错误处理可以提高插件的健壮性和可调试性",
})
}
// 检查日志记录
hasLogging := strings.Contains(codeWithoutComments, "proxywasm.Log") ||
strings.Contains(codeWithoutComments, "log.Error") ||
strings.Contains(codeWithoutComments, "log.Warn") ||
strings.Contains(codeWithoutComments, "log.Info") ||
strings.Contains(codeWithoutComments, "log.Debug")
if !hasLogging {
report.Issues = append(report.Issues, ValidationIssue{
Category: "best_practice",
Type: "logging",
Message: "代码中没有日志记录",
Suggestion: "添加适当的日志记录,如: proxywasm.LogInfo(), log.Errorf() 等",
Impact: "日志记录有助于调试、监控和问题排查",
})
}
// 检查回调函数的返回值
checkCallbackReturnErrors(&report, codeWithoutComments, report.FoundCallbacks)
// 生成总体评估摘要
report.Summary = generateValidationSummary(report)
return report
}
// removeComments 移除 Go 代码中的注释
func removeComments(code string) string {
// 移除单行注释
singleLineComment := regexp.MustCompile(`//.*`)
code = singleLineComment.ReplaceAllString(code, "")
// 移除多行注释
multiLineComment := regexp.MustCompile(`(?s)/\*.*?\*/`)
code = multiLineComment.ReplaceAllString(code, "")
return code
}
// containsImport 检查是否包含特定的 import
func containsImport(code, importPath string) bool {
// 匹配 import "path" 或 import ("path")
pattern := regexp.MustCompile(`import\s+(?:\([\s\S]*?)?["` + "`" + `]` +
regexp.QuoteMeta(importPath) + `["` + "`" + `]`)
return pattern.MatchString(code)
}
// checkCallbackReturnErrors 检查回调函数的返回值错误
func checkCallbackReturnErrors(report *ValidationReport, code string, foundCallbacks []string) {
// 检查回调函数内是否有 return nil应该返回 types.Action
for _, callback := range foundCallbacks {
// 提取回调函数体(简化的检查)
funcPattern := regexp.MustCompile(
`func\s+` + callback + `\s*\([^)]*\)\s+types\.Action\s*\{[^}]*return\s+nil[^}]*\}`)
if funcPattern.MatchString(code) {
report.Issues = append(report.Issues, ValidationIssue{
Category: "required",
Type: "api_usage",
Message: fmt.Sprintf("回调函数 %s 不应返回 nil", callback),
Suggestion: "回调函数应返回 types.Action如: return types.ActionContinue",
Impact: "返回 nil 会导致编译错误或运行时异常",
})
break // 只报告一次
}
}
// 检查是否正确返回 types.Action
if len(foundCallbacks) > 0 {
hasActionReturn := strings.Contains(code, "types.ActionContinue") ||
strings.Contains(code, "types.ActionPause") ||
strings.Contains(code, "types.ActionSuspend")
if !hasActionReturn {
report.Issues = append(report.Issues, ValidationIssue{
Category: "recommended",
Type: "api_usage",
Message: "未找到明确的 Action 返回值",
Suggestion: "回调函数应返回明确的 types.Action 值ActionContinue、ActionPause 等)",
Impact: "明确的返回值有助于代码可读性和正确性",
})
}
}
}
// generateValidationSummary 生成验证摘要
func generateValidationSummary(report ValidationReport) string {
requiredIssues := 0
recommendedIssues := 0
optionalIssues := 0
bestPracticeIssues := 0
for _, issue := range report.Issues {
switch issue.Category {
case "required":
requiredIssues++
case "recommended":
recommendedIssues++
case "optional":
optionalIssues++
case "best_practice":
bestPracticeIssues++
}
}
if requiredIssues > 0 {
return fmt.Sprintf("代码存在 %d 个必须修复的问题,%d 个建议修复的问题,%d 个可选优化项,%d 个最佳实践建议。请优先解决必须修复的问题。",
requiredIssues, recommendedIssues, optionalIssues, bestPracticeIssues)
}
if recommendedIssues > 0 {
return fmt.Sprintf("代码基本结构正确,但有 %d 个建议修复的问题,%d 个可选优化项,%d 个最佳实践建议。",
recommendedIssues, optionalIssues, bestPracticeIssues)
}
if optionalIssues > 0 || bestPracticeIssues > 0 {
return fmt.Sprintf("代码结构良好,有 %d 个可选优化项和 %d 个最佳实践建议可以考虑。",
optionalIssues, bestPracticeIssues)
}
callbacksInfo := ""
if len(report.FoundCallbacks) > 0 {
callbacksInfo = fmt.Sprintf(",实现了 %d 个回调函数", len(report.FoundCallbacks))
}
return fmt.Sprintf("代码验证通过,未发现明显问题%s。", callbacksInfo)
}
// GenerateDeploymentPackage 生成部署配置提示(由 LLM 根据代码生成具体内容)
func GenerateDeploymentPackage(pluginName, goCode, configSchema, namespace string) DeploymentPackage {
// 所有配置文件都由 LLM 根据实际代码生成,不使用固定模板
return DeploymentPackage{
WasmPluginYAML: "", // LLM 生成
Makefile: "", // LLM 生成
Dockerfile: "", // LLM 生成
ConfigMap: "", // LLM 生成
README: "", // LLM 生成
TestScript: "", // LLM 生成
Dependencies: make(map[string]string),
}
}
// FormatToolResultWithAIContext 格式化工具结果
func FormatToolResultWithAIContext(userMessage, aiInstructions string, structuredData interface{}) ToolResult {
jsonData, _ := json.MarshalIndent(structuredData, "", " ")
output := fmt.Sprintf("%s\n\n%s\n\n%s", userMessage, aiInstructions, string(jsonData))
return ToolResult{
Content: []Content{{Type: "text", Text: output}},
}
}