MCP 的相关实操学习

前言

随着AI模型的发展,对应回答的数据也是越来越丰富和完善了,但是还缺少一块有关第三方数据信息的内容,这一块在往常的方式形式上,我们可能是通过api接口的形式暴露调用其能力。对于AI模型上,有了通用的形式MCP,让所有第三方暴露的信息内容,都按照统一的格式暴露。

Mcp server

现在有很多对应的Mcp server的服务市场,有很多比较成熟的服务,可以快速接入使用 ,可以依照自己的需要选择接入

当然有想法,也可以自己创建对应mcp内容,上传对应的内容。目前支持的pythonts写的会比较多。所以网上提供的大部分都是npx/uvx执行

使用uv来构建本地内容

mac安装uv,通过brew来实现brew add uv安装

使用uv来快速创建一个虚拟环境的项目,这里我们可以先使用3.11版本的python内容环境依赖,创建名称为mcp-project的项目名称, 然后安装 mcp 包,并且额外安装其 cli 这个 extra 所指定的可选依赖

csharp 复制代码
uv init mcp-project
uv venv --seed --python 3.11 .venv //seed相关pip包, 生成.venv配置环境内容
uv add "mcp[cli]"

注意要使用python/pip相关命令,需要source .venv/bin/activate激活环境里的python和pip

对应在github上找到model context protocol项目也就是mcp,里面对应有的python sdk仓库。

仿照对应它的quick start的案例编写属于自己的demo的案例,包括构建Servertool工具信息

python 复制代码
from mcp.server.fastmcp import FastMCP

# Create an MCP server
mcp = FastMCP("Demo")


# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b


# Add a dynamic greeting resource
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
    """Get a personalized greeting"""
    return f"Hello, {name}!"

if __name__ == "__main__":
    mcp.run(transport="stdio")
  • tool : 代表对应的可以执行的工具,允许 LLM 通过您的服务器执行协作,类似于http中的post
    • 参数:包含对应的参数值信息,参数和类型,对应上述方法中的ab,类型为int
    • 标题:包含对应的方法的工具名称标题,对应上述方法标题 add
    • 介绍:包含对这个工具的相关的使用介绍信息, 对应上述方法中的 Add two numbers
    • 返回:包含对应的返回值,对应上述方法的 a+b的结果值
  • resource: 资源是如何向 LLM 公开数据的方式,类似于http中的get
  • trasport:对应的类型,
    • 标准输入输出(stdio)
    • 服务发送事件(sse): 对应的是http请求url的地址,后缀加sse
    • 可流式传输的http(streamableHttp)

使用运行uv run main.py ,就能运行对应这个对应的服务 。 如果想使用Mcp Inspector来进行调试对应的MCP服务是否可用, 使用命令mcp dev main.py 来运行,就会得到对应的请求地址http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=xxxxxxx

less 复制代码
uv run main.py // 单纯运行这个服务
mcp dev main.py // 通过mcp检测调试的方式运行服务

下图就是标识了检测MCP服务是否可以连接用的展示,是执行了mcp dev main.py的结果,注意前面添加依赖的时候使用的是uv add "mcp[cli]"才能执行而不是 uv add mcp

使用ts构建本地内容

创建一个node的项目,申明一个项目名称,然后初始化项目

arduino 复制代码
mkdir simple-mcp // 创建一个名称为simple-mcp的项目
cd simple-mcp
npm init -y // 初始化项目

此时会有对应的package.json文件内容,此时我们可以增加一些mcp,ts,node的相关依赖

bash 复制代码
npm install @modelcontextprotocol/sdk zod  
npm install -D @types/node typescript

创建一些对应的主文件目录src,和对应的文件src/index.ts

bash 复制代码
mkdir src
touch src/index.ts

然后修改对应的package.json文件

  1. 对应的type修改为module,可以使用使用现代 JS 模块语法(import/export
  2. 增加bin ,提供一个终端命令(如 pixabay)来运行这个工具
  3. 修改scripts,使用 TypeScript 编写源码,并通过 tsc 构建到 build/ 目录
  4. 增加files,只发布构建后的代码(build/
  5. 修改main , 改为build/index.js,指向的是编译后的 .js 文件
json 复制代码
{
  "name": "simple-mcp",
  "version": "1.0.0",
  "description": "",
  "main": "build/index.js"
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "bin": {
    "pixabay": "./build/index.js"
  },
  "scripts": {
    "build": "tsc && chmod 755 build/index.js"
    "start": "node build/index.js"
  },
  "files": [
    "build"
  ]
}

在根目录文件下增加 tsconfig.json文件

json 复制代码
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

至此我们已经创建好一个项目了,开始正式创建mcp server内容, 和之前提到的uv一样,也是从typescript sdk仓库中获得mcp的案例进行仿写

php 复制代码
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create an MCP server
const server = new McpServer({
  name: "demo-server",
  version: "1.0.0"
});

// Add an addition tool
server.registerTool("add",
  {
    title: "Addition Tool",
    description: "Add two numbers",
    inputSchema: { a: z.number(), b: z.number() }
  },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }]
  })
);


// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
await server.connect(transport);
  • registerTool : 绑定对应的工具方法
    • title: 标题
    • description: 介绍详情内容
    • inputSchema:对应参数类型,名称

开始执行构建npm run build命令,会生成build文件夹,和它下面的index.js文件 。如果有需要的话执行的话npm start

然后再通过开启的之前提到的uv中的mcp dev xx.py效果一致,也是启动一个端口页面访问测试连接 使用命令npx @modelcontextprotocol/inspector 按照启动调试mcp server是否能联通的依赖包

点开对应携带token的url端口地址,页面中command中输入node ,在Arguments中填入刚刚构建好的build/index.js,本质上也是运行这个脚本

Mcp Client(软件客户端)

现有的client对应有不同形式的很多,我们前面只是通过工具页面 MCP Inspector来检测是否服务是否成功,当然我们可以直接在常见的Cursorcherry studio中直接配置对应的mcp服务器

配置到cherry studio

按照我们前面提到的例子,uv构建的和ts构建的本地的mcp server内容,使用我们的cherry studio来连接使用

  1. 第一种通过uv的 mcp的形式

点击设置-> Mcp服务器-> 创建对应的服务器

类型选择 标准输入/输出(stdio) 命令: uv 参数为 --directory mcp-project项目地址 run main.py

如果你想用sse类型,可以修改代码mcp.run(transport="sse"),然后配置的时候选择 服务发送事件(sse) 和地址url配置上即可

  1. 第二种通过ts的mcp的形式

点击设置-> Mcp服务器-> 创建对应的服务器 类型选择 标准输入/输出(stdio) 命令:node 参数为: index.js的地址

  1. 当然也可以直接通过json的配置内容直接复制进入也是可以的

复制到 "mcpServers"对象中,里面每一个keyvalue都对应一个mcp的服务内容

配置到Cursor

点击对应的chat setting设置,然后选择MCP,可以将对应的json配置直接加入即可,复制到 "mcpServers"对象中,里面每一个keyvalue都对应一个mcp的服务内容

json 复制代码
"GEWXPfNVUk1-stgDGAXxT": {
  "name": "MCP 服务器",
  "type": "stdio",
  "description": "",
  "isActive": true,
  "command": "node",
  "args": [
    "/Users/gjc/workspace/vscodeWorkspace/simple-mcp/build/index.js"
  ]
}

Mcp Client(代码客户端)

使用mcp的原生sdk实现

作为后端比较熟悉java的写法,刚好java也有对应的依赖支持。

xml 复制代码
<dependency>
    <groupId>io.modelcontextprotocol.sdk</groupId>
    <artifactId>mcp</artifactId>
    <version>0.9.0</version>
</dependency>

通过sse形式路径url来构建clientbean内容 ,注意sseServerUrl已经不需要在添加/sse

java 复制代码
@Component
public class McpClientUtil {


    private McpSyncClient mcpClient;
    // 对应的sse的连接地址
    private static final String sseServerUrl = "https://xxxx.yy.com/";


    // 初始化
    @PostConstruct
    public void init(){
        try {
            McpClientTransport transport = new HttpClientSseClientTransport(sseServerUrl);
            mcpClient = McpClient.sync(transport)
                    .requestTimeout(Duration.ofSeconds(20L))
                    .capabilities(McpSchema.ClientCapabilities.builder()
                            .roots(true)
                            .sampling()
                            .build())
                    .build();
            mcpClient.initialize();
        } catch (Exception e) {
            throw new RuntimeException("初始化MCP客户端对象失败", e);
        }
    }


    // 结束销毁
    @PreDestroy
    public void destroy(){
        if (mcpClient != null) {
            mcpClient.closeGracefully();
        }
    }

}

对应的McpClientUtilbean的事先构建,含有McpSyncClient这个属性实例,通过bean的初始化和结束销毁操作 ,也就是对于这个McpSyncClient执行不同的初始化和结束销毁

如果我们想获取对应地址sse的Mcp server中的能力,我们可以在提供一个方法来支持

csharp 复制代码
// 获取对应工具的信息内容
public void getToolList(){
    McpSchema.ListToolsResult toolsResult = mcpClient.listTools();
    for (McpSchema.Tool tool:toolsResult.tools()) {
        System.out.println(tool.name());
        System.out.println(tool.description());
        System.out.println(tool.inputSchema());
    }
}

McpClientUtil这个bean来调用getToolList方法,此时会打印出对应工具的名称,描述信息,方法参数和类型信息。inputSchema还可以直接作为functionCalltools中的对象

如果我们还想直接调用工具的方法的话

scss 复制代码
// 调用工具的方法
public void callTool() {
    McpSchema.ListToolsResult toolsResult = mcpClient.listTools();
    for (McpSchema.Tool tool:toolsResult.tools()) {
        // 入参
        Map<String,Object> parameters = new HashMap<>();
        parameters.put("a", 1);
        parameters.put("b", 1);
        McpSchema.CallToolResult toolResult = mcpClient.callTool(new McpSchema.CallToolRequest("add", parameters));
        System.out.println(extractTextContent(toolResult));
    }
}

// 批量工具调用的结果整合输出
private String extractTextContent(McpSchema.CallToolResult toolResult){
    StringBuilder resultText = new StringBuilder();
    toolResult.content().forEach(content -> {
        if (content instanceof McpSchema.TextContent) {
            resultText.append(((McpSchema.TextContent) content).text());
        }
    });
    return resultText.toString();
}

McpClientUtil这个bean来调用callTool方法 , 指定对应的参数parameters中的内容和我们在Map server中定义的方法入参一致。CallToolRequest对应的构建方法名称和参数封装构建

使用spring ai实现(sse)

我们先直接用大模型来实现操作(不引入mcp),引入相关依赖openai的spring-ai ,不是很清楚依赖版本的,可以通过spring网站直接创建模版项目

xml 复制代码
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>

<dependencyManagement>
<dependencies>
  <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-bom</artifactId>
    <version>${spring-ai.version}</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>
</dependencies>
</dependencyManagement>

虽然我们使用的是openai的相关依赖,但是也不影响通过它来配置deepseek, 因为相关的参数配置接口信息都是遵从的一致,我们来配置下相关的文件信息

yaml 复制代码
spring:
  ai:
    openai:
      base-url: https://api.deepseek.com  // 配置路径
      api-key: sk-xxxxxxx  // 配置密钥
      chat:
        options:
          model: deepseek-chat  // 配置使用的model
        completions-path: /chat/completions

然后我们通过书写下简单的实例调用一下

kotlin 复制代码
@Configuration
public class ChatClientConfig {

    @Autowired
    private OpenAiChatModel chatModel;

    @Bean
    public CommandLineRunner predefinedQuestions(
            ConfigurableApplicationContext context) {
        return args -> {
            // 构建ChatClient,此时不注入任何工具
            var chatClient = ChatClient.builder(chatModel)
                    .build();
            String userInput = "你是谁";
            System.out.println("\n>>> QUESTION: " + userInput);
            System.out.println("\n>>> ASSISTANT: " + chatClient.prompt().user(userInput).call().content());
            context.close();
        };
    }
}

启动的时候,直接就会去构建访问deepseek,下图就是我们执行成功的标识

我们开始配置使用上mcp的功能,先配置对应的mcp server相关信息(sse的形式)

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        name: spring-ai-mcp-client
        type: sync
        toolcallback:
          enabled: true
        request-timeout: 30000
        enabled: true
        sse:
          connections:
            server1:
              url: https://xxxxxx

在原来ChatClientConfig增加调用这个mcp工具

scss 复制代码
@Autowired
private ToolCallbackProvider tools;

// 构建ChatClient,此时增加注入工具
var chatClient = ChatClient.builder(chatModel).defaultToolCallbacks(tools)
                    .build();

使用spring ai实现(stdio)

但是如果我们想通过stdio的形式来执行

StdioSSE配置的区别是将调用方式由显示的服务地址换成npm、java、python等脚本命令直接执行的远程包或本地包

我们复用上面的例子,然后修改mcpsse的形式为stdio,修改对应的配置文件

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        name: spring-ai-mcp-client
        toolcallback:
          enabled: true
        request-timeout: 30000
        enabled: true
        stdio:
          servers-configuration:classpath:/mcp-servers-config.json

创建对应的本地resource下的mcp-servers-config.json内容,编写mcpserver的配置内容,例如我本地的pythonmcpserver将它配置进来

json 复制代码
{
  "mcpServers": {
    "mcp-server-hotnews": {
      "command": "uv",
      "args": [
        "--directory",
        "/Users/gjc/workspace/cursorWorkspace/simple-mcp",
        "run",
        "main.py"
      ]
    }
  }
}

然后再进行修改构建client的地方内容

scss 复制代码
// 构建ChatClient,此时增加注入工具
var chatClient = ChatClient.builder(chatModel).defaultToolCallbacks(tools.getToolCallbacks())
                    .build();

总结

本质上我们通过自己uv/node构建项目,来整合mcp的sdk依赖,来实现本地mcp server的功能,然后也实现了在已有的软件cursorcherry studio中配置连接mcp server。最后通过java项目导入ai的相关依赖来实现配置来连接mcp server

相关推荐
白仑色4 分钟前
Spring Boot 安全登录系统:前后端分离实现
spring boot·后端·安全
国科安芯5 分钟前
车规级ASM1042芯片在汽车无线充电模块中的应用探索
人工智能·单片机·嵌入式硬件·物联网·安全·汽车
error_cn7 分钟前
sort命令的稳定性分析
后端
绵阳的脑内SSD8 分钟前
CMU15445-2024fall-project2踩坑经历
后端
小码编匠8 分钟前
C# 的西门子数控系统 OPCUA 数据采集开发从零入门
后端·数据分析·c#
Victor3569 分钟前
MySQL(136)如何防止SQL注入攻击?
后端
阿里云大数据AI技术11 分钟前
基于MaxCompute MaxFrame 汽车自动驾驶数据预处理最佳实践
大数据·人工智能·自动驾驶
腾讯云开发者13 分钟前
从具身智能到行业应用,腾讯云携业界专家共话 AI 新趋势
人工智能
FE杂志社14 分钟前
全栈开发 → FastAPI碎碎念
后端·python·fastapi
考虑考虑15 分钟前
解决java: java.lang.ExceptionInInitializerError com.sun.tools.javac.code.TypeTag :
java·spring boot·后端