mirror of
https://github.com/alibaba/higress.git
synced 2026-06-09 04:37:31 +08:00
refactor: migrate MCP SDK to main repo (#3516)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user