MCP

MCP (Model Context Protocol) 是一种标准化协议,允许语言模型与外部工具和服务进行交互。通过 MCP,模型可以:

  • 访问文件系统
  • 执行命令行操作
  • 调用外部 API
  • 与其他服务进行交互

1. MCP Tool

https://github.com/cloudwego/eino-ext项目中的components/tool/mcp中实现了一个MCP Tool,能够与Eino框架的工具系统轻松集成,支持获取和调用MCP工具

1.1 安装

go get github.com/cloudwego/eino-ext/components/tool/mcp@latest
go get github.com/mark3labs/mcp-go
1
2

1.2 创建MCP服务器

这里我们先自己创建一个MCP服务器,来提供一个天气查询的MCP服务。

package main

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"

	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)

func main() {
	mcpServer := server.NewMCPServer("weather", mcp.LATEST_PROTOCOL_VERSION)
	tool := WeatherTool()
	mcpServer.AddTool(tool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
		//解析输入参数
		params := request.GetArguments()
		city, ok := params["city"].(string)
		if !ok || city == "" {
			return nil, fmt.Errorf("city is required")
		}
		//构建API请求URL
		baseURL := "https://restapi.amap.com/v3/weather/weatherInfo"
		queryParams := url.Values{}
		queryParams.Set("city", city)
		queryParams.Set("key", "you api key")
		//设置可选参数
		if extensions, ok := params["extensions"].(string); ok {
			queryParams.Set("extensions", extensions)
		} else {
			// 默认查询实况天气
			queryParams.Set("extensions", "base")
		}
		//设置返回格式为JSON
		queryParams.Set("output", "JSON")
		fullURL := fmt.Sprintf("%s?%s", baseURL, queryParams.Encode())
		//发送HTTP请求
		req, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil)
		if err != nil {
			return nil, fmt.Errorf("failed to create request: %w", err)
		}
		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil {
			return nil, fmt.Errorf("failed to send request: %w", err)
		}
		defer resp.Body.Close()
		//读取响应
		body, err := io.ReadAll(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("failed to read response: %w", err)
		}
		//检查HTTP状态码
		if resp.StatusCode != http.StatusOK {
			return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body))
		}
		return mcp.NewToolResultText(string(body)), nil
	})

	err := server.NewSSEServer(mcpServer).Start("localhost:12345")
	if err != nil {
		panic(err)
	}
}

func WeatherTool() mcp.Tool {
	tool := mcp.NewTool("get_weather",
		mcp.WithDescription("Get weather information for a given city"),
		mcp.WithString("city", mcp.Required(), mcp.Description("城市名称")),
		mcp.WithString("extensions",
			mcp.Required(),
			mcp.Enum("base", "all"),
			mcp.Description("返回数据类型,base为实况天气,all为预报天气"),
			mcp.DefaultString("base"),
		),
	)
	return tool
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

1.3 MCP客户端

package main

import (
	"context"

	"github.com/cloudwego/eino-ext/components/model/ollama"
	mcpTool "github.com/cloudwego/eino-ext/components/tool/mcp"
	"github.com/cloudwego/eino/components/tool"
	"github.com/cloudwego/eino/compose"
	"github.com/cloudwego/eino/flow/agent/react"
	"github.com/cloudwego/eino/schema"
	"github.com/mark3labs/mcp-go/client"
	"github.com/mark3labs/mcp-go/mcp"
)

func main() {
	ctx := context.Background()
	chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
		BaseURL: "http://127.0.0.1:11434",
		Model:   "modelscope.cn/Qwen/Qwen3-32B-GGUF:latest",
	})
	if err != nil {
		panic(err)
	}
	mcpTools, err := getMcpTools()
	if err != nil {
		panic(err)
	}
	agent, err := react.NewAgent(ctx, &react.AgentConfig{
		ToolCallingModel: chatModel,
		ToolsConfig: compose.ToolsNodeConfig{
			Tools: mcpTools,
		},
	})
	if err != nil {
		panic(err)
	}
	msg := []*schema.Message{
		schema.SystemMessage("请根据提供的天气查询工具,查询天气情况"),
		schema.UserMessage("查询北京今天的天气"),
	}
	result, err := agent.Generate(ctx, msg)
	if err != nil {
		panic(err)
	}

	println(result.Content)
}

func getMcpTools() ([]tool.BaseTool, error) {
	ctx := context.Background()
	// 创建SSE客户端连接到MCP服务器
	cli, err := client.NewSSEMCPClient("http://localhost:12345/sse")
	if err != nil {
		panic(err)
	}
	// 启动客户端
	err = cli.Start(ctx)
	if err != nil {
		panic(err)
	}
	initializeRequest := mcp.InitializeRequest{}
	initializeRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
	initializeRequest.Params.ClientInfo = mcp.Implementation{
		Name:    "weather-tool",
		Version: "0.0.1",
	}
	_, err = cli.Initialize(ctx, initializeRequest)
	if err != nil {
		return nil, err
	}
	tools, err := mcpTool.GetTools(ctx, &mcpTool.Config{
		Cli: cli,
	})
	return tools, err
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77