大模型LLM(五)--大模型MCP原理及实现(SpringAI+MCP+SSE)

1、大模型MCP原理

MCP(Model Context Protocol,模型上下文协议),由 Anthropic 推出的一种开放标准,旨在统一大模型与外部数据源和工具之间的通信协议。
图中就是MCP的工作原理图,从第1步到第7步。其中第4和第5步就是MCP Client和MCP Server通过MCP Protocol进行通信。


MCP的原理图

第1步提问“今天南京的天气”

提问问题

第2步AIAgent获取MCP Server的工具列表,第3步返回工具查询结果

POST /mcp/messages HTTP/1.1
request
{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "id": "3e5f6a3c-7",
  "params": {
    
  }
}
response
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": [
    {
      "type": "function",
      "function": {
        "description": "获取中国城市的当前天气信息。输入为城市名称(例如:杭州、上海)",
        "name": "getWeather",
        "parameters": {
          "$schema": "https://json-schema.org/draft/2020-12/schema",
          "additionalProperties": false,
          "type": "object",
          "properties": {
            "arg0": {
              "type": "string"
            }
          },
          "required": [
            "arg0"
          ]
        }
      }
    }
  ]
}

第4步构建prompt后发起的大模型请求如下,其中tools对象即为工具列表,getWeather是我们使用定义好的MCP工具方法,并使用注解进行标注。

{
  "messages": [
    {
      "content": "今天南京的天气",
      "role": "user"
    }
  ],
  "model": "Qwen3-14B",
  "stream": true,
  "temperature": 0.7,
  "tools": [
    {
      "type": "function",
      "function": {
        "description": "获取中国城市的当前天气信息。输入为城市名称(例如:杭州、上海)",
        "name": "getWeather",
        "parameters": {
          "$schema": "https://json-schema.org/draft/2020-12/schema",
          "additionalProperties": false,
          "type": "object",
          "properties": {
            "arg0": {
              "type": "string"
            }
          },
          "required": [
            "arg0"
          ]
        }
      }
    }
  ]
}

第5步返回选中的参数和提取的参数

{
  "id": "chatcmpl-1758595028.402406",
  "object": "chat.completion.chunk",
  "created": 1758595028,
  "model": "qwen",
  "choices": [
    {
      "index": 0,
      "delta": {
        "tool_calls": {
          "type": "function",
          "function": {
            "name": "getWeather",
            "arguments": {
              "arg0": "南京"
            }
          },
          "id": "call_702672"
        },
        "content": "<tool_call>"
      },
      "finish_reason": "<tool_call>"
    }
  ]
}

第6步和第8步,MCP Client调用MCP Server,即为MCP协议。MCP 协议有两种通信机制:
1、基于标准输入输出的本地通信
2、基于[SSE]
这两种机制都使用 [JSON-RPC 2.0]
JSON-RPC2.0的通信示例如下所示:

POST /mcp/messages HTTP/1.1
request
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "id": "3e5f6a3c-8",
  "params": {
    "name": "getWeather",
    "arguments": {
      "arg0": "南京"
    }
  }
}
response
{
"jsonrpc": "2.0",
"result": {
"content": "\"城市: 南京\\n天气情况: Overcast\\n气压: 1014(mb)\\n温度: 24°C (Feels like: null°C)\\n湿度: 83%\\n降水量:0.0 (mm)\\n风速: 15 km/h (E)\\n能见度: 10 公里\\n紫外线指数: 0\\n观测时间: 2025-09-19 07:41 AM\\n\""
},
"id": 1
}

第9步将调用MCP Server的结果和问题一起构建大模型请求

{
  "messages": [
    {
      "content": "南京今天的天气",
      "role": "user"
    },
    {
      "content": "<tool_call>",
      "role": "assistant",
      "tool_calls": [
        {
          "id": "call_803714",
          "type": "function",
          "function": {
            "name": "getWeather",
            "arguments": "{\"arg0\": \"南京\"}"
          }
        }
      ]
    },
    {
      "content": "\"城市: 南京\\n天气情况: Overcast\\n气压: 1014(mb)\\n温度: 24°C (Feels like: null°C)\\n湿度: 83%\\n降水量:0.0 (mm)\\n风速: 15 km/h (E)\\n能见度: 10 公里\\n紫外线指数: 0\\n观测时间: 2025-09-19 07:41 AM\\n\"",
      "role": "tool",
      "name": "getWeather",
      "tool_call_id": "call_803714"
    }
  ],
  "model": "Qwen3-14B",
  "stream": true,
  "temperature": 0.7,
  "tools": [
    {
      "type": "function",
      "function": {
        "description": "获取中国城市的当前天气信息。输入为城市名称(例如:杭州、上海)",
        "name": "getWeather",
        "parameters": {
          "$schema": "https://json-schema.org/draft/2020-12/schema",
          "additionalProperties": false,
          "type": "object",
          "properties": {
            "arg0": {
              "type": "string"
            }
          },
          "required": [
            "arg0"
          ]
        }
      }
    }
  ]
}

第10步大模型返回答案
大模型返回的流式响应

{"id": "chatcmpl-1758183786.445879", "object": "chat.completion.chunk", "created": 1758183786, "model": "qwen", "choices": [{"index": 0, "delta": {"content": "{\"name\": "}, "finish_reason": null}]}
{"id": "chatcmpl-1758183786.546777", "object": "chat.completion.chunk", "created": 1758183786, "model": "qwen", "choices": [{"index": 0, "delta": {"content": "\"getWeather\", "}, "finish_reason": null}]}
{"id": "chatcmpl-1758183786.6477", "object": "chat.completion.chunk", "created": 1758183786, "model": "qwen", "choices": [{"index": 0, "delta": {"content": "\"arguments\": "}, "finish_reason": null}]}
{"id": "chatcmpl-1758183786.748445", "object": "chat.completion.chunk", "created": 1758183786, "model": "qwen", "choices": [{"index": 0, "delta": {"content": "{\"arg0\": "}, "finish_reason": null}]}
{"id": "chatcmpl-1758183786.849172", "object": "chat.completion.chunk", "created": 1758183786, "model": "qwen", "choices": [{"index": 0, "delta": {"content": "\"\u5357\u4eac"}, "finish_reason": null}]}
{"id": "chatcmpl-1758183786.94977", "object": "chat.completion.chunk", "created": 1758183786, "model": "qwen", "choices": [{"index": 0, "delta": {"content": "\"}}\n"}, "finish_reason": null}]}

处理后的前台响应


大模型返回答案

2、项目地址

github开源的地址:
https://github.com/xujinhelaw/chat-bot-ananas.git
gittee开源的地址:
https://gitee.com/xuhelaw/chat-bot-ananas.git
项目结构如下

└── mcp-server/ (mcp服务端模块)
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   └── org/
│   │   │   │       └── ananas/
│   │   │   │           └── mcpserver/
│   │   │   │           │    └── config/
│   │   │   │           │        └── ToolCallbackProviderConfig.java(mcp服务端配置文件)
│   │   │   │           │    ├── model/
│   │   │   │           │        ├── CurrentCondition.java(天气情况对象)
│   │   │   │           │        ├── WeatherDesc.java(天气描述对象)
│   │   │   │           │        └── WeatherResponse.java(天气情况响应对象)
│   │   │   │           │    ├── service/
│   │   │   │           │        ├── WeatherService.java(天气mcp服务接口)
│   │   │   │           │        └── WeatherServiceImpl.java(天气mcp服务)
│   │   │   │           │    └── McpServerApplication.java(mcp服务后端应用启动代码)
│   │   │   └── resources/
│   │   │       └── application.yml(mcp服务后端配置文件)

MCP实现的效果如下:
未开启mcp,无法返回当前的天气情况

未开启mcp

开启mcp,返回当前的天气情况
开启mcp

3、MCP Server 服务端实现

3.1、 将大模型从Qwen2-7B升级为Qwen3-14B

升级如下依赖软件的版本

    python=3.8.10 --> 3.9.15
    bitsandbytes==0.41.1 -> 0.47.0
    accelerate==1.0.1 -> 1.10.1
    modelscope==1.9.5 --> 1.28.0
    transformers==4.34.0 --> 4.53.0
    sentence-transformers==3.0.0 -->  5.1.0

完整的environment.yaml如下所示:

name: qwen
channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
dependencies:
  - python=3.9.15
  - pip=24.2
  - pip:
    - -i https://pypi.tuna.tsinghua.edu.cn/simple/ # 这里是pip访问国外仓被墙,添加指定国内仓
    - torch==2.7.0
    - modelscope==1.28.0
    - tiktoken==0.7.0
    - accelerate==1.10.1
    - fastapi==0.104.1
    - uvicorn==0.24.0.post1
    - requests==2.32.4
    - modelscope==1.9.5
    - transformers==4.53.0
    - transformers_stream_generator==0.0.4
    - peft==0.4.0
    - datasets==2.10.1
    - scipy==1.10.1
    - bitsandbytes==0.47.0
    - accelerate==1.10.1
    - pip==24.2
    - sentence_transformers==5.1.0
    - pyarrow==14.0.0

3.2、 Maven依赖

        <!-- Spring AI Start -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-core</artifactId>
        </dependency>
        <!-- Spring AI End -->
        <!-- LLM MCP -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.modelcontextprotocol.sdk</groupId>
                    <artifactId>mcp-spring-webmvc</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--解决 SSE 连接将在30秒后终止bug,引入0.8.1版本-->
        <dependency>
            <groupId>io.modelcontextprotocol.sdk</groupId>
            <artifactId>mcp-spring-webmvc</artifactId>
            <version>0.8.1</version>
        </dependency>
        <!-- LLM MCP End-->

3.2、 代码实现

application.yml配置文件的配置

server:
  port: 8090

spring:
  application:
    name: mcp-server
  ai:
    mcp:
      server:
        name: mcp-server
        version: 1.0.0
        type: SYNC
        sse-message-endpoint: /mcp/messages

WeatherServiceImpl.java 天气mcp服务的实现,主要使用@Tool来标注mcp的工具方法

@Service
public class WeatherServiceImpl implements WeatherService {

    private static final String BASE_URL = "https://wttr.in";

    private final RestClient restClient;

    public WeatherServiceImpl() {
        this.restClient = RestClient.builder()
                .baseUrl(BASE_URL)
                .defaultHeader("Accept", "application/geo+json")
                .defaultHeader("User-Agent", "WeatherApiClient/1.0 (your@email.com)")
                .build();
    }

    @Override
    @Tool(description = "获取中国城市的当前天气信息。输入为城市名称(例如:杭州、上海)")
    public String getWeather(@RequestParam("arg0")String cityName) {
        System.out.println("调用天气mcp服务WeatherServiceImpl!");
        WeatherResponse response = restClient.get()
                .uri("/{city_name}?format=j1", cityName)
                .retrieve()
                .body(WeatherResponse.class);
        if (response != null && response.getCurrent_condition() != null && !response.getCurrent_condition().isEmpty()) {
            CurrentCondition currentCondition = response.getCurrent_condition().get(0);
            String result = String.format("""
                            城市: %s
                            天气情况: %s
                            气压: %s(mb)
                            温度: %s°C (Feels like: %s°C)
                            湿度: %s%%
                            降水量:%s (mm)
                            风速: %s km/h (%s)
                            能见度: %s 公里
                            紫外线指数: %s
                            观测时间: %s
                            """,
                    cityName,
                    currentCondition.getWeatherDesc().get(0).getValue(),
                    currentCondition.getPressure(),
                    currentCondition.getTemp_C(),
                    currentCondition.getFeelsLikeC(),
                    currentCondition.getHumidity(),
                    currentCondition.getPrecipMM(),
                    currentCondition.getWindspeedKmph(),
                    currentCondition.getWinddir16Point(),
                    currentCondition.getVisibility(),
                    currentCondition.getUvIndex(),
                    currentCondition.getLocalObsDateTime()
            );
            return result;
        } else {
            return "无法获取天气信息,请检查城市名称是否正确或稍后重试。";
        }
    }

在ToolCallbackProviderConfig.java配置文件中,实例化ToolCallbackProvider对象

@Configuration
public class ToolCallbackProviderConfig {
    @Bean
    public ToolCallbackProvider weatherTools(WeatherService weatherService) {
        return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
    }
}

3.3、 前台代码实现没有太多变化,请自行参见代码仓的ChatView.vue源码

4、MCP Client客户端的实现

4.1、 Maven依赖

        <!-- LLM MCP -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
        </dependency>
        <!-- LLM MCP End--> 

引入mcp-client的starter后,会自动注入mcp client所需要的类

4.2、 代码实现

application.yml配置文件的配置

spring:
  # AI 配置
  ai:
    openai:
      api-key: your-api-key-here # 替换为您的 API Key,如果是本地大模型可以不修改
      base-url: http://127.0.0.1:6006 # 大模型的访问地址
      chat:
        options:
          model: Qwen3-14B
          # temperature参数用于控制生成文本的多样性
          #值越高,生成的文本越多样化,但也可能包含更多的随机性和不可预测的内容
          #值越低,生成的文本越接近于确定性的结果,即生成的文本会更加一致和可预测
          temperature: 0.7
      # 嵌入模型配置
      embedding:
        options:
          model: bge-large-zh-v1.5 # 模型名在这里
          dimensions: 1024 # 向量维度
    retry:
      maxAttempts: 3
    mcp:
      client:
        enabled: true # 是否启用MCP客户端
        request-timeout: 60s # MCP客户端请求超时时间
        type: SYNC # MCP客户端类型
        sse:
          connections:
            server1:
              url: http://127.0.0.1:8090

在ChatController.java 中通过mcp协议跟mcp server进行通信,主要是ToolCallbackProvider的使用

@RestController
// 允许前端跨域访问,生产环境应配置具体域名
@CrossOrigin(origins = "*")
public class ChatController {
    private static final Logger log = LoggerFactory.getLogger(ChatController.class);

    @Autowired
    private ChatClient chatClient; // Spring AI 自动配置的客户端

    @Autowired
    private ToolCallbackProvider toolCallbackProvider; // MCP Provider

    /**
     * 处理聊天消息 (流式响应 - 推荐用于聊天界面)
     */
    @PostMapping(value = "/api/chat-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> chatStream(@RequestBody Map<String, String> request) {
        String userMessage = constructPrompt(request.get("message"));
        Flux<String> flux = chatClient.prompt()
                .user(userMessage)
                .tools(toolCallbackProvider.getToolCallbacks())//设置mcpclient
                .stream()
                .content()
                .doOnNext(chunk -> log.info("Emitting chunk: #{}#", chunk)) 
                .doOnSubscribe(s -> log.info("Subscription started"))
                .doOnComplete(() -> log.info("Flux completed"));
        return flux;
    }

    private String constructPrompt(String userMessage) {
        String augmentedPrompt = """
                你是一个智能助手,请回答下面的问题。
                
                问题:
                %s
                """.formatted(userMessage);
        System.out.println("augmentedPrompt is :" + augmentedPrompt);
        return augmentedPrompt;
    }

}

附录
springai实现的逻辑简单示意如下:

[用户] "今天南京天气如何?"
   ↓
[模型流式输出...] → 生成 tool_call:
                    {"name": "getWeather", "arguments": {"arg0": "南京"}}
   ↓
internalStream.detect(tool_call) → hasToolCalls() = true
   ↓
toolCallingManager.execute("getWeather", {"arg0": "南京"})
   ↓
找到 @FunctionCallback("getWeather") 方法
   ↓
调用 getWeather("南京") → 返回 "晴,25°C"
   ↓
生成 ToolExecutionResult
   ↓
if (returnDirect) → 直接返回结果
else → 递归 internalStream(新prompt含结果)
   ↓
[最终输出] "南京今天晴,25°C"(或模型润色后的句子)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容