feat: call CallMCPTool in parser

This commit is contained in:
lilong.129
2025-05-16 14:30:24 +08:00
parent e333ba380a
commit a58ccffb28
3 changed files with 64 additions and 10 deletions
+1 -1
View File
@@ -1 +1 @@
v5.0.0-beta-2505161414 v5.0.0-beta-2505161430
+40 -5
View File
@@ -1,6 +1,7 @@
package hrp package hrp
import ( import (
"context"
builtinJSON "encoding/json" builtinJSON "encoding/json"
"fmt" "fmt"
"net/url" "net/url"
@@ -18,14 +19,20 @@ import (
"github.com/httprunner/funplugin/fungo" "github.com/httprunner/funplugin/fungo"
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/mcphost"
mcp2 "github.com/mark3labs/mcp-go/mcp"
) )
func NewParser() *Parser { func NewParser() *Parser {
return &Parser{} return &Parser{
ctx: context.Background(),
}
} }
type Parser struct { type Parser struct {
Plugin funplugin.IPlugin // plugin is used to call functions ctx context.Context
Plugin funplugin.IPlugin // plugin is used to call functions
MCPHost *mcphost.MCPHost
} }
func buildURL(baseURL, stepURL string, queryParams url.Values) (fullUrl *url.URL) { func buildURL(baseURL, stepURL string, queryParams url.Values) (fullUrl *url.URL) {
@@ -213,7 +220,7 @@ func (p *Parser) ParseString(raw string, variablesMapping map[string]interface{}
return raw, err return raw, err
} }
result, err := p.callFunc(funcName, parsedArgs.([]interface{})...) result, err := p.CallFunc(funcName, parsedArgs.([]interface{})...)
if err != nil { if err != nil {
log.Error().Str("funcName", funcName).Interface("arguments", arguments). log.Error().Str("funcName", funcName).Interface("arguments", arguments).
Err(err).Msg("call function failed") Err(err).Msg("call function failed")
@@ -275,9 +282,9 @@ func (p *Parser) ParseString(raw string, variablesMapping map[string]interface{}
return parsedString, nil return parsedString, nil
} }
// callFunc calls function with arguments // CallFunc calls function with arguments
// only support return at most one result value // only support return at most one result value
func (p *Parser) callFunc(funcName string, arguments ...interface{}) (interface{}, error) { func (p *Parser) CallFunc(funcName string, arguments ...interface{}) (interface{}, error) {
// call with plugin function // call with plugin function
if p.Plugin != nil { if p.Plugin != nil {
if p.Plugin.Has(funcName) { if p.Plugin.Has(funcName) {
@@ -300,6 +307,34 @@ func (p *Parser) callFunc(funcName string, arguments ...interface{}) (interface{
return fungo.CallFunc(fn, arguments...) return fungo.CallFunc(fn, arguments...)
} }
// CallMCPTool calls a MCP tool on a specific MCP server
func (p *Parser) CallMCPTool(serverName, funcName string, arguments map[string]interface{}) (interface{}, error) {
if p.MCPHost == nil {
return nil, fmt.Errorf("mcphost is not initialized")
}
tools := p.MCPHost.GetTools(p.ctx)
log.Warn().Interface("tools", tools).Msg("tools")
result, err := p.MCPHost.InvokeTool(p.ctx, serverName, funcName, arguments)
if err != nil {
return nil, errors.Wrapf(err, "invoke tool %s/%s failed", serverName, funcName)
}
if result.IsError {
return nil, fmt.Errorf("invoke tool %s/%s failed: %v", serverName, funcName, result.Content)
}
// extract text content
var resultText string
for _, item := range result.Content {
if contentMap, ok := item.(mcp2.TextContent); ok {
resultText += fmt.Sprintf("%v ", contentMap.Text)
}
}
return resultText, nil
}
// merge two variables mapping, the first variables have higher priority // merge two variables mapping, the first variables have higher priority
func mergeVariables(variables, overriddenVariables map[string]interface{}) map[string]interface{} { func mergeVariables(variables, overriddenVariables map[string]interface{}) map[string]interface{} {
if overriddenVariables == nil { if overriddenVariables == nil {
+23 -4
View File
@@ -9,6 +9,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/httprunner/httprunner/v5/internal/version"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -433,26 +434,44 @@ func TestCallBuiltinFunction(t *testing.T) {
parser := NewParser() parser := NewParser()
// call function without arguments // call function without arguments
_, err := parser.callFunc("get_timestamp") _, err := parser.CallFunc("get_timestamp")
assert.Nil(t, err) assert.Nil(t, err)
// call function with one argument // call function with one argument
timeStart := time.Now() timeStart := time.Now()
_, err = parser.callFunc("sleep", 1) _, err = parser.CallFunc("sleep", 1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Greater(t, time.Since(timeStart), time.Duration(1)*time.Second) assert.Greater(t, time.Since(timeStart), time.Duration(1)*time.Second)
// call function with one argument // call function with one argument
result, err := parser.callFunc("gen_random_string", 10) result, err := parser.CallFunc("gen_random_string", 10)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 10, len(result.(string))) assert.Equal(t, 10, len(result.(string)))
// call function with two argument // call function with two argument
result, err = parser.callFunc("max", float64(10), 9.99) result, err = parser.CallFunc("max", float64(10), 9.99)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, float64(10), result.(float64)) assert.Equal(t, float64(10), result.(float64))
} }
func TestCallMCPTool(t *testing.T) {
// Create a new case runner for testing
caseRunner, err := NewCaseRunner(TestCase{
Config: &TConfig{
MCPConfigPath: "pkg/mcphost/testdata/test.mcp.json",
},
}, nil)
require.Nil(t, err)
parser := caseRunner.GetParser()
resp, err := parser.CallMCPTool("filesystem", "read_file",
map[string]interface{}{"path": "internal/version/VERSION"})
assert.Nil(t, err)
t.Logf("resp: %v", resp)
assert.Contains(t, resp, version.VERSION)
}
func TestLiteralEval(t *testing.T) { func TestLiteralEval(t *testing.T) {
testData := []struct { testData := []struct {
expr string expr string