refactor: migrate MCP SDK to main repo (#3516)

This commit is contained in:
澄潭
2026-02-16 23:39:18 +08:00
committed by GitHub
parent 87c6cc9c9f
commit 9346f1340b
75 changed files with 10117 additions and 3392 deletions

View File

@@ -1,10 +1,14 @@
module jsonrpc-converter
go 1.24.3
go 1.24.1
replace github.com/alibaba/higress/plugins/wasm-go/pkg/mcp => ../../pkg/mcp
require (
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250822030947-8345453fddd0
github.com/higress-group/wasm-go v1.0.4
github.com/alibaba/higress/plugins/wasm-go/pkg/mcp v0.0.0
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20251103120604-77e9cce339d2
github.com/higress-group/wasm-go v1.0.10-0.20260115123534-84ef43c39dc9
github.com/stretchr/testify v1.9.0
github.com/tidwall/gjson v1.18.0
)
@@ -15,6 +19,7 @@ require (
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/higress-group/gjson_template v0.0.0-20250413075336-4c4161ed428b // indirect
github.com/huandu/xstrings v1.5.0 // indirect
@@ -22,8 +27,10 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/tetratelabs/wazero v1.7.2 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/resp v0.1.1 // indirect

View File

@@ -20,10 +20,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/higress-group/gjson_template v0.0.0-20250413075336-4c4161ed428b h1:rRI9+ThQbe+nw4jUiYEyOFaREkXCMMW9k1X2gy2d6pE=
github.com/higress-group/gjson_template v0.0.0-20250413075336-4c4161ed428b/go.mod h1:rU3M+Tq5VrQOo0dxpKHGb03Ty0sdWIZfAH+YCOACx/Y=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250822030947-8345453fddd0 h1:YGdj8KBzVjabU3STUfwMZghB+VlX6YLfJtLbrsWaOD0=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250822030947-8345453fddd0/go.mod h1:tRI2LfMudSkKHhyv1uex3BWzcice2s/l8Ah8axporfA=
github.com/higress-group/wasm-go v1.0.4 h1:/GqbzCw4oWqJc8UbKEfF94E3/+4CPZGbzxpKo2L3Ldk=
github.com/higress-group/wasm-go v1.0.4/go.mod h1:B8C6+OlpnyYyZUBEdUXA7tYZYD+uwZTNjfkE5FywA+A=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20251103120604-77e9cce339d2 h1:NY33OrWCJJ+DFiLc+lsBY4Ywor2Ik61ssk6qkGF8Ypo=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20251103120604-77e9cce339d2/go.mod h1:tRI2LfMudSkKHhyv1uex3BWzcice2s/l8Ah8axporfA=
github.com/higress-group/wasm-go v1.0.10-0.20260115123534-84ef43c39dc9 h1:sUuUXZwr50l3W1St7MESlFmxmUAu+QUNNfJXx4P6bas=
github.com/higress-group/wasm-go v1.0.10-0.20260115123534-84ef43c39dc9/go.mod h1:uKVYICbRaxTlKqdm8E0dpjbysxM8uCPb9LV26hF3Km8=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
@@ -49,6 +49,8 @@ github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tetratelabs/wazero v1.7.2 h1:1+z5nXJNwMLPAWaTePFi49SSTL0IMx/i3Fg8Yc25GDc=
github.com/tetratelabs/wazero v1.7.2/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=

View File

@@ -9,8 +9,8 @@ import (
"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/mcp"
"github.com/higress-group/wasm-go/pkg/mcp/utils"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/utils"
"github.com/higress-group/wasm-go/pkg/wrapper"
"github.com/tidwall/gjson"
)

View File

@@ -1,9 +1,15 @@
package main
import (
"encoding/json"
"testing"
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
"github.com/higress-group/wasm-go/pkg/test"
"github.com/stretchr/testify/require"
)
// TestTruncateString tests the truncateString function
func TestTruncateString(t *testing.T) {
tests := []struct {
name string
@@ -14,6 +20,8 @@ func TestTruncateString(t *testing.T) {
{"Short String", "Higress Is an AI-Native API Gateway", 1000, "Higress Is an AI-Native API Gateway"},
{"Exact Length", "Higress Is an AI-Native API Gateway", 35, "Higress Is an AI-Native API Gateway"},
{"Truncated String", "Higress Is an AI-Native API Gateway", 20, "Higress Is...(truncated)...PI Gateway"},
{"Empty String", "", 10, ""},
{"Single Char", "A", 10, "A"},
}
for _, tt := range tests {
@@ -26,3 +34,248 @@ func TestTruncateString(t *testing.T) {
})
}
}
// TestIsPreRequestStage tests the isPreRequestStage function
func TestIsPreRequestStage(t *testing.T) {
config := McpConverterConfig{Stage: ProcessRequest}
require.True(t, isPreRequestStage(config))
config = McpConverterConfig{Stage: ProcessResponse}
require.False(t, isPreRequestStage(config))
}
// TestIsPreResponseStage tests the isPreResponseStage function
func TestIsPreResponseStage(t *testing.T) {
config := McpConverterConfig{Stage: ProcessResponse}
require.True(t, isPreResponseStage(config))
config = McpConverterConfig{Stage: ProcessRequest}
require.False(t, isPreResponseStage(config))
}
// TestIsMethodAllowed tests the isMethodAllowed function
func TestIsMethodAllowed(t *testing.T) {
config := McpConverterConfig{AllowedMethods: []string{MethodToolList, MethodToolCall}}
require.True(t, isMethodAllowed(config, MethodToolList))
require.True(t, isMethodAllowed(config, MethodToolCall))
require.False(t, isMethodAllowed(config, "invalid/method"))
}
// TestConstants tests the constant values
func TestConstants(t *testing.T) {
require.Equal(t, "x-envoy-jsonrpc-id", JsonRpcId)
require.Equal(t, "x-envoy-jsonrpc-method", JsonRpcMethod)
require.Equal(t, "x-envoy-jsonrpc-params", JsonRpcParams)
require.Equal(t, "x-envoy-jsonrpc-result", JsonRpcResult)
require.Equal(t, "x-envoy-jsonrpc-error", JsonRpcError)
require.Equal(t, "x-envoy-mcp-tool-name", McpToolName)
require.Equal(t, "x-envoy-mcp-tool-arguments", McpToolArguments)
require.Equal(t, "x-envoy-mcp-tool-response", McpToolResponse)
require.Equal(t, "x-envoy-mcp-tool-error", McpToolError)
require.Equal(t, 4000, DefaultMaxHeaderLength)
require.Equal(t, "tools/list", MethodToolList)
require.Equal(t, "tools/call", MethodToolCall)
require.Equal(t, ProcessStage("request"), ProcessRequest)
require.Equal(t, ProcessStage("response"), ProcessResponse)
}
// TestMcpConverterConfigDefaults tests config default values
func TestMcpConverterConfigDefaults(t *testing.T) {
config := McpConverterConfig{}
require.Equal(t, 0, config.MaxHeaderLength)
require.Equal(t, ProcessStage(""), config.Stage)
require.Nil(t, config.AllowedMethods)
}
// TestProcessStage tests ProcessStage type
func TestProcessStage(t *testing.T) {
require.Equal(t, ProcessStage("request"), ProcessRequest)
require.Equal(t, ProcessStage("response"), ProcessResponse)
}
// TestRemoveJsonRpcHeadersFunction tests removeJsonRpcHeaders function logic
func TestRemoveJsonRpcHeadersFunction(t *testing.T) {
headersToRemove := []string{
JsonRpcId,
JsonRpcMethod,
JsonRpcParams,
JsonRpcResult,
McpToolName,
McpToolArguments,
McpToolResponse,
McpToolError,
}
require.Len(t, headersToRemove, 8)
}
// TestTruncateStringLong tests truncation of very long strings
func TestTruncateStringLong(t *testing.T) {
longString := ""
for i := 0; i < 5000; i++ {
longString += "a"
}
config := McpConverterConfig{MaxHeaderLength: 1000}
result := truncateString(longString, config)
require.Contains(t, result, "...(truncated)...")
require.LessOrEqual(t, len(result), 1020)
}
// TestTruncateStringWithSmallMaxLength tests truncation with small max length
func TestTruncateStringWithSmallMaxLength(t *testing.T) {
config := McpConverterConfig{MaxHeaderLength: 10}
result := truncateString("This is a very long string", config)
require.Contains(t, result, "...(truncated)...")
}
// TestPluginInit tests plugin initialization
func TestPluginInit(t *testing.T) {
configBytes, _ := json.Marshal(McpConverterConfig{
Stage: ProcessRequest,
MaxHeaderLength: DefaultMaxHeaderLength,
AllowedMethods: []string{MethodToolList, MethodToolCall},
})
host, status := test.NewTestHost(configBytes)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
}
// TestProcessJsonRpcRequest tests processJsonRpcRequest function
func TestProcessJsonRpcRequest(t *testing.T) {
configBytes, _ := json.Marshal(McpConverterConfig{
Stage: ProcessRequest,
MaxHeaderLength: DefaultMaxHeaderLength,
AllowedMethods: []string{MethodToolList, MethodToolCall},
})
host, status := test.NewTestHost(configBytes)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
host.InitHttp()
host.CallOnHttpRequestHeaders([][2]string{
{":authority", "mcp-server.example.com"},
{":method", "POST"},
{":path", "/mcp"},
{"content-type", "application/json"},
})
toolsListRequest := `{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}`
action := host.CallOnHttpRequestBody([]byte(toolsListRequest))
require.Equal(t, types.ActionContinue, action)
host.CompleteHttp()
}
// TestProcessToolCallRequest tests processToolCallRequest function
func TestProcessToolCallRequest(t *testing.T) {
configBytes, _ := json.Marshal(McpConverterConfig{
Stage: ProcessRequest,
MaxHeaderLength: DefaultMaxHeaderLength,
AllowedMethods: []string{MethodToolCall},
})
host, status := test.NewTestHost(configBytes)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
host.InitHttp()
host.CallOnHttpRequestHeaders([][2]string{
{":authority", "mcp-server.example.com"},
{":method", "POST"},
{":path", "/mcp"},
{"content-type", "application/json"},
})
toolCallRequest := `{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "test_tool",
"arguments": {"arg1": "value1"}
}
}`
action := host.CallOnHttpRequestBody([]byte(toolCallRequest))
require.Equal(t, types.ActionContinue, action)
host.CompleteHttp()
}
// TestProcessJsonRpcResponse tests processJsonRpcResponse function
func TestProcessJsonRpcResponse(t *testing.T) {
configBytes, _ := json.Marshal(McpConverterConfig{
Stage: ProcessResponse,
MaxHeaderLength: DefaultMaxHeaderLength,
AllowedMethods: []string{MethodToolList, MethodToolCall},
})
host, status := test.NewTestHost(configBytes)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
host.InitHttp()
host.CallOnHttpRequestHeaders([][2]string{
{":authority", "mcp-server.example.com"},
{":method", "POST"},
{":path", "/mcp"},
{"content-type", "application/json"},
})
responseBody := `{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [{"name": "test_tool"}]
}
}`
host.CallOnHttpResponseHeaders([][2]string{
{":status", "200"},
{"content-type", "application/json"},
})
host.CallOnHttpResponseBody([]byte(responseBody))
host.CompleteHttp()
}
// TestProcessToolListResponse tests processToolListResponse function
func TestProcessToolListResponse(t *testing.T) {
configBytes, _ := json.Marshal(McpConverterConfig{
Stage: ProcessResponse,
MaxHeaderLength: DefaultMaxHeaderLength,
AllowedMethods: []string{MethodToolList},
})
host, status := test.NewTestHost(configBytes)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
host.InitHttp()
host.CallOnHttpRequestHeaders([][2]string{
{":authority", "mcp-server.example.com"},
{":method", "POST"},
{":path", "/mcp"},
{"content-type", "application/json"},
})
responseBody := `{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [{"name": "test_tool"}]
}
}`
host.CallOnHttpResponseHeaders([][2]string{
{":status", "200"},
{"content-type", "application/json"},
})
host.CallOnHttpResponseBody([]byte(responseBody))
host.CompleteHttp()
}