mcp大模型的通用工具调用协议

# MCP和Function Call对比

在本次文章开始写之前,要搞明白Function CallMCP的区别。

  graph TD
    A[用户输入] --> B[大模型判断意图]
    B --> C{是否需要调用函数?}
    C -->|是| D[生成结构化函数调用请求<br>(如JSON参数)]
    C -->|否| E[直接生成回答]
    D --> F[开发者解析并执行函数]
    F --> G[获取函数执行结果]
    G --> H[将结果反馈给大模型]
    H --> I[生成最终回答]
    I --> J[输出给用户]
  graph LR
    A[用户请求] --> B[大模型]
    B --> C[MCP服务器]
    C --> D{路由判断}
    D -->|工具调用| E[执行外部工具]
    D -->|数据查询| F[访问数据库]
    E --> C{{MCP服务器}}
    F --> C
    C --> B[大模型]
    B --> G[生成回答]
对比维度 Function Call MCP (Managed Context Protocol)
协议层级 特定厂商(如OpenAI)的功能实现 通用协议标准(类似USB-C接口规范)
标准化程度 依赖厂商定义,灵活性高但碎片化 强制遵循JSON-RPC 2.0标准,统一性强
核心功能 单次函数调用(如天气查询) 上下文管理+工具动态接入(如多工具协作)
控制权归属 大模型主导调用逻辑 应用层通过MCP服务器管理工具与数据源
典型场景 简单API调用(如发送邮件、查询数据库) 企业级复杂系统集成(如医疗数据中台)
扩展性 需手动注册函数 支持动态加载工具和知识库

对比下来,MCP是在大模型和工具之间定义了标准化的协议,只需要和MCP服务器交互即可。那么现在任何大模型都遵循该协议吗?

为此,查看了代码在执行中给大模型实际的数据,可以看到,这只是一段prompt,任何大模型只要能遵循指令,即可实现mcp的调用。

 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
<|im_start|>system
You are Qwen, created by Alibaba Cloud. You are a helpful assistant.

# Tools

You may call one or more functions to assist with the user query.


You are provided with function signatures within <tools></tools> XML tags:

<tools>
{\"type\": \"function\", \"function\": {\"name\":\"calculate\",\"description\":\"Perform basic arithmetic operations\",\"parameters\":{\"type\":\"object\",\"required\":[\"operation\",\"x\",\"y\"],\"properties\":{\"operation\":{\"type\":\"string\",\"description\":\"The operation to perform (add, subtract, multiply, divide)\",\"enum\":[\"add\",\"subtract\",\"multiply\",\"divide\"]},\"x\":{\"type\":\"number\",\"description\":\"First number\"},\"y\":{\"type\":\"number\",\"description\":\"Second number\"}}}}}

</tools>


For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:

<tool_call>

{\"name\": <function-name>, \"arguments\": <args-json-object>}

</tool_call><|im_end|>

<|im_start|>user

我今天买了一斤苹果和一斤梨,苹果是5块一斤,梨是3块一斤,我一共花了多少钱?<|im_end|>

<|im_start|>assistant

# 测试代码

  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
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package main

import (
 "context"
 "fmt"
 "log"
 "time"

 "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"
 "github.com/mark3labs/mcp-go/server"

 "github.com/cloudwego/eino-ext/components/model/ollama"
 mcpp "github.com/cloudwego/eino-ext/components/tool/mcp"
)

func main() {

 startMCPServer()
 time.Sleep(1 * time.Second)
 ctx := context.Background()

 mcpTools := getMCPTool(ctx)

 chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
  BaseURL: "http://localhost:11434",
  Model:   "qwen2.5:7b",
 })
 if err != nil {
  log.Printf("NewChatModel failed, err=%v", err)
  return
 }

 agent, err := react.NewAgent(ctx, &react.AgentConfig{
  Model:       chatModel,
  ToolsConfig: compose.ToolsNodeConfig{Tools: mcpTools},
 })

 fmt.Println(err)

 rsp, err := agent.Generate(ctx, []*schema.Message{
  {
   Role:    schema.User,
   Content: "我今天买了一斤苹果和一斤梨,苹果是5块一斤,梨是3块一斤,我一共花了多少钱?",
  },
 })

 fmt.Println(rsp, err)
}

func getMCPTool(ctx context.Context) []tool.BaseTool {
 cli, err := client.NewSSEMCPClient("http://localhost:12345/sse")
 if err != nil {
  log.Fatal(err)
 }
 err = cli.Start(ctx)
 if err != nil {
  log.Fatal(err)
 }

 initRequest := mcp.InitializeRequest{}
 initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
 initRequest.Params.ClientInfo = mcp.Implementation{
  Name:    "example-client",
  Version: "1.0.0",
 }

 _, err = cli.Initialize(ctx, initRequest)
 if err != nil {
  log.Fatal(err)
 }

 tools, err := mcpp.GetTools(ctx, &mcpp.Config{Cli: cli})
 if err != nil {
  log.Fatal(err)
 }

 return tools
}

func startMCPServer() {
 svr := server.NewMCPServer("demo", mcp.LATEST_PROTOCOL_VERSION)
 svr.AddTool(mcp.NewTool("calculate",
  mcp.WithDescription("Perform basic arithmetic operations"),
  mcp.WithString("operation",
   mcp.Required(),
   mcp.Description("The operation to perform (add, subtract, multiply, divide)"),
   mcp.Enum("add", "subtract", "multiply", "divide"),
  ),
  mcp.WithNumber("x",
   mcp.Required(),
   mcp.Description("First number"),
  ),
  mcp.WithNumber("y",
   mcp.Required(),
   mcp.Description("Second number"),
  ),
 ), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  fmt.Println("Received request:", request)
  op := request.Params.Arguments["operation"].(string)
  x := request.Params.Arguments["x"].(float64)
  y := request.Params.Arguments["y"].(float64)

  var result float64
  switch op {
  case "add":
   result = x + y
  case "subtract":
   result = x - y
  case "multiply":
   result = x * y
  case "divide":
   if y == 0 {
    return mcp.NewToolResultText("Cannot divide by zero"), nil
   }
   result = x / y
  }
  log.Printf("Calculated result: %.2f", result)
  return mcp.NewToolResultText(fmt.Sprintf("%.2f", result)), nil
 })
 go func() {
  defer func() {
   e := recover()
   if e != nil {
    fmt.Println(e)
   }
  }()

  err := server.NewSSEServer(svr, server.WithBaseURL("http://localhost:12345")).Start("localhost:12345")

  if err != nil {
   log.Fatal(err)
  }
 }()
}

执行代码,可以看到是有工具调用的。依托于该基础代码,未来可以封装更多的工具。

1
2
3
4
5
<nil>
Received request: {{tools/call {<nil>}} {calculate map[operation:add x:5 y:3] <nil>}}
2025/03/30 19:07:20 Calculated result: 8.00
assistant: 苹果和梨的单价相加就是总价,所以您一共花了8元钱。
finish_reason: stop <nil>

结尾抛出一个问题,大模型的输入输出是有上限的,当工具达到几百个时,大模型会能正确找到函数吗,如何解决?

Licensed under CC BY-NC-SA 4.0