手把手带你理解MCP是什么、怎么用、如何开发,每个步骤都有详细说明

写在前面

很多朋友看完MCP的介绍还是一头雾水:“这到底是什么?跟我有什么关系?我该怎么用?”

别急,这篇文章我会用最通俗的方式一步一步带你搞懂MCP。每个概念都会配上生活化的例子,每个操作都有完整的代码


第一部分:先搞懂“为什么” - 需求分析

问题1:现在的AI有什么痛点?

假设你有一个AI助手,你想让它帮你:

  • 读取你电脑上的Excel文件

  • 搜索网络上的图片

  • 调用公司内部的API

现在的问题是:每个AI模型都要单独适配,ChatGPT写一套代码,Claude写另一套,本地模型又不一样。太麻烦了!

问题2:MCP怎么解决?

MCP就像一个万能插座

text

之前:每个电器都要自己的专属插座 → 混乱
现在:统一标准,什么电器都能插 → 方便

MCP就是AI界的USB-C接口,让所有AI模型都能用统一的方式调用工具、获取数据。


第二部分:彻底搞懂MCP - 必知必会

什么是MCP?(用大白话说)

官方定义:MCP(Model Context Protocol)是一个开放协议,用于标准化应用程序向大型语言模型提供上下文的方式。

大白话解释:MCP是一套通用规则,规定了:

  • AI怎么“伸手要东西”(请求格式)

  • 工具怎么“把东西给AI”(响应格式)

  • 双方怎么“对话”(通信方式)

MCP架构(用餐厅比喻)

1. 宏观架构

把MCP想象成餐厅点餐

角色 餐厅例子 MCP世界
顾客 AI模型
服务员 服务员 MCP客户端
厨房 厨房 MCP服务端
菜单 菜单 工具列表

流程

  1. 顾客(AI)说要吃什么 →

  2. 服务员(MCP客户端)记录并传给厨房 →

  3. 厨房(MCP服务端)做菜并返回

2. SDK 3层架构(通俗版)

text

┌─────────────────────────────────────┐
│  应用层(你写代码的地方)              │  ← 你只需要关心这层
│  @McpTool、@McpResource              │
├─────────────────────────────────────┤
│  协议层(MCP自己处理)                │  ← 不用你操心
│  消息格式、交互规则                   │
├─────────────────────────────────────┤
│  传输层(MCP自己处理)                │  ← 不用你操心  
│  stdio、SSE、HTTP                    │
└─────────────────────────────────────┘
3. MCP客户端(谁在用?)

客户端 = “请求发起方”

实际例子:

  • Cursor编辑器

  • Claude Desktop

  • 你自己写的Java程序

  • VS Code插件

4. MCP服务端(谁提供工具?)

服务端 = “提供能力的一方”

实际例子:

  • 文件系统服务(让AI读写文件)

  • 数据库服务(让AI查询数据)

  • 图片搜索服务(让AI搜图)

MCP核心概念(图解)

text

┌─────────────────────────────────────────────┐
│                  MCP服务端                    │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐      │
│  │  Tool   │  │Resource │  │ Prompt  │      │
│  │  工具   │  │  资源   │  │  提示词 │      │
│  └─────────┘  └─────────┘  └─────────┘      │
│     ↓             ↓             ↓            │
│  可执行操作    可读取数据    可用的模板        │
│  (动词)        (名词)        (配方)          │
└─────────────────────────────────────────────┘

详细解释

概念 通俗理解 代码示例
Tool(工具) AI能“做”的事情 searchImages()sendEmail()
Resource(资源) AI能“看”的数据 文件、数据库记录、API响应
Prompt(提示词) 预设的“对话模板” “你是一个图片搜索专家...”

第三部分:开始使用MCP(实操从这开始)

第一步:环境准备(5分钟搞定)

1.1 安装Node.js(MCP工具依赖)

bash

# Windows: 去官网下载安装包 https://nodejs.org/
# Mac: 
brew install node

# Linux:
sudo apt install nodejs npm

# 验证安装
node -v   # 应该显示 v18.0.0 或更高
npm -v    # 显示版本号
1.2 安装MCP命令行工具

bash

# 全局安装MCP CLI
npm install -g @modelcontextprotocol/cli

# 验证安装
mcp --version

第二步:在Cursor编辑器中使用MCP

Cursor是什么? 一个集成了AI的代码编辑器,类似VS Code。

详细步骤

  1. 下载Cursor:访问 https://cursor.sh/ 下载安装

  2. 打开设置

    • Windows/Linux: Ctrl + ,

    • Mac: Cmd + ,

  3. 找到MCP配置

    • 在设置搜索框输入 "MCP"

    • 找到 "MCP Servers" 配置项

  4. 添加服务

json

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/folder"]
    }
  }
}
  1. 重启Cursor,就可以在AI对话中使用MCP工具了!

第三步:测试MCP是否工作

bash

# 1. 测试MCP连接
mcp ping

# 成功返回: pong

# 2. 启动一个简单的MCP服务(作为测试)
npx -y @modelcontextprotocol/server-filesystem /tmp

# 3. 在另一个终端列出工具
mcp tools list --server http://localhost:3000

第四步:在程序中使用MCP(Python示例)

python

# 1. 安装Python SDK
pip install mcp

# 2. 创建测试脚本 test_mcp.py
import asyncio
from mcp import ClientSession, StdioServerParameters

async def main():
    # 连接到MCP服务
    server_params = StdioServerParameters(
        command="npx",
        args=["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
    )
    
    async with ClientSession(server_params) as session:
        # 初始化连接
        await session.initialize()
        
        # 列出所有可用工具
        tools = await session.list_tools()
        print("可用工具:", [tool.name for tool in tools])
        
        # 调用一个工具
        result = await session.call_tool(
            "read_file",
            arguments={"path": "/tmp/test.txt"}
        )
        print("结果:", result)

# 运行
asyncio.run(main())

bash

# 执行脚本
python test_mcp.py

第四部分:Spring AI MCP开发(Java/Spring Boot)

如果你用的是Java技术栈,这部分是你的重点

MCP客户端开发(调用别人的服务)

步骤1:创建Spring Boot项目

在 start.spring.io 创建项目,选择:

  • Spring Web

  • 添加MCP依赖

步骤2:添加依赖(pom.xml)

xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
</parent>

<dependencies>
    <!-- Spring Boot基础 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- MCP客户端 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
        <version>1.0.0-M1</version>
    </dependency>
</dependencies>
步骤3:配置文件(application.yml)

yaml

spring:
  ai:
    mcp:
      client:
        enabled: true
        # 要连接的MCP服务地址
        server-url: http://localhost:8080
        # 连接超时(毫秒)
        connect-timeout: 30000
        # 请求超时(毫秒)
        request-timeout: 60000

logging:
  level:
    org.springframework.ai: DEBUG
步骤4:编写客户端代码

java

package com.example.mcpclient;

import org.springframework.ai.mcp.McpClient;
import org.springframework.ai.mcp.model.McpCallToolRequest;
import org.springframework.ai.mcp.model.McpCallToolResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/mcp")
public class McpClientController {
    
    @Autowired
    private McpClient mcpClient;
    
    @GetMapping("/search")
    public String searchImages(@RequestParam String keyword) {
        // 1. 构造请求
        McpCallToolRequest request = new McpCallToolRequest();
        request.setName("image-search");  // 要调用的工具名
        
        Map<String, Object> args = new HashMap<>();
        args.put("keyword", keyword);
        args.put("limit", 5);
        request.setArguments(args);
        
        // 2. 发送请求
        McpCallToolResult result = mcpClient.callTool(request);
        
        // 3. 返回结果
        return result.getContent().toString();
    }
    
    @GetMapping("/tools")
    public List<String> listAvailableTools() {
        // 获取所有可用工具
        return mcpClient.listTools()
            .stream()
            .map(tool -> tool.getName())
            .collect(Collectors.toList());
    }
}

MCP服务端开发(提供给别人调用)

步骤1:创建服务端项目

同样的方式创建Spring Boot项目,添加服务端依赖:

xml

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
    <version>1.0.0-M1</version>
</dependency>
步骤2:配置文件

yaml

spring:
  ai:
    mcp:
      server:
        name: my-image-search-service
        version: 1.0.0
        # 传输方式:stdio(标准输入输出)或 sse(HTTP)
        transport-type: sse
        # HTTP端口(使用SSE时)
        port: 8080

server:
  port: 8080
步骤3:启用MCP服务

java

package com.example.mcpserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.ai.mcp.server.annotation.EnableMcpServer;

@SpringBootApplication
@EnableMcpServer  // 这个注解启用MCP服务能力
public class McpServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(McpServerApplication.class, args);
        System.out.println("MCP服务已启动,地址: http://localhost:8080");
    }
}
步骤4:开发MCP工具

java

package com.example.mcpserver;

import org.springframework.ai.mcp.server.annotation.McpTool;
import org.springframework.ai.mcp.server.annotation.McpToolParam;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.ArrayList;

@Service
@Slf4j
public class ImageSearchService {
    
    /**
     * 搜索图片的工具
     * @McpTool 注解把这个方法暴露为MCP工具
     */
    @McpTool(
        name = "image-search",
        description = "根据关键词搜索图片,返回图片URL列表"
    )
    public List<ImageInfo> searchImages(
        @McpToolParam(description = "搜索关键词,比如:风景、猫、汽车") 
        String keyword,
        
        @McpToolParam(description = "返回图片数量,默认10张,最大50") 
        Integer limit
    ) {
        log.info("收到图片搜索请求: keyword={}, limit={}", keyword, limit);
        
        // 限制最大数量
        if (limit == null || limit > 50) {
            limit = 10;
        }
        
        // 这里是示例数据,实际可以调用Unsplash、Pexels等API
        List<ImageInfo> results = new ArrayList<>();
        for (int i = 0; i < limit; i++) {
            ImageInfo image = new ImageInfo();
            image.setId(String.valueOf(i));
            image.setTitle(keyword + " - 图片" + (i+1));
            image.setUrl("https://example.com/images/" + keyword + "_" + i + ".jpg");
            image.setThumbnail("https://example.com/thumbnails/" + keyword + "_" + i + ".jpg");
            results.add(image);
        }
        
        return results;
    }
    
    /**
     * 获取最近上传的图片
     * @McpResource 注解暴露资源
     */
    @McpResource(uri = "image://recent/{count}")
    public List<ImageInfo> getRecentImages(@McpResourcePath String count) {
        int num = Integer.parseInt(count);
        return searchImages("recent", num);
    }
}

// 数据类
class ImageInfo {
    private String id;
    private String title;
    private String url;
    private String thumbnail;
    // getter/setter 省略
}
步骤5:运行服务

bash

# 打包
mvn clean package

# 运行
java -jar target/mcp-server-1.0.0.jar

服务启动后,访问 http://localhost:8080 可以看到MCP服务信息。


第五部分:完整实战 - 图片搜索服务(复制就能用)

服务端完整代码

ImageSearchService.java

java

package com.example.imagesearch;

import org.springframework.ai.mcp.server.annotation.McpTool;
import org.springframework.ai.mcp.server.annotation.McpToolParam;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.*;

@Service
public class ImageSearchService {
    
    private final RestTemplate restTemplate = new RestTemplate();
    
    @McpTool(name = "search-images", description = "从Unsplash搜索免费图片")
    public List<Map<String, String>> searchImages(
        @McpToolParam(description = "搜索关键词,如:mountain, cat, car") String keyword,
        @McpToolParam(description = "返回数量,默认5") Integer limit
    ) {
        
        // 调用Unsplash API(免费图片API)
        String url = "https://api.unsplash.com/search/photos?query=" + keyword 
                    + "&per_page=" + (limit == null ? 5 : limit)
                    + "&client_id=YOUR_UNSPLASH_ACCESS_KEY";
        
        try {
            JsonNode response = restTemplate.getForObject(url, JsonNode.class);
            List<Map<String, String>> results = new ArrayList<>();
            
            for (JsonNode photo : response.get("results")) {
                Map<String, String> image = new HashMap<>();
                image.put("id", photo.get("id").asText());
                image.put("description", photo.get("description") != null ? 
                         photo.get("description").asText() : keyword);
                image.put("url", photo.get("urls").get("regular").asText());
                image.put("thumbnail", photo.get("urls").get("thumb").asText());
                image.put("author", photo.get("user").get("name").asText());
                results.add(image);
            }
            
            return results;
        } catch (Exception e) {
            // 返回模拟数据
            return getMockData(keyword, limit);
        }
    }
    
    private List<Map<String, String>> getMockData(String keyword, Integer limit) {
        List<Map<String, String>> results = new ArrayList<>();
        for (int i = 0; i < (limit == null ? 5 : limit); i++) {
            Map<String, String> image = new HashMap<>();
            image.put("id", "mock_" + i);
            image.put("description", keyword + " 图片 " + (i+1));
            image.put("url", "https://picsum.photos/id/" + (i+10) + "/800/600");
            image.put("thumbnail", "https://picsum.photos/id/" + (i+10) + "/200/150");
            results.add(image);
        }
        return results;
    }
}

客户端完整代码

ImageSearchController.java

java

package com.example.imageclient;

import org.springframework.ai.mcp.McpClient;
import org.springframework.ai.mcp.model.McpCallToolRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;

@RestController
@RequestMapping("/api/images")
public class ImageSearchController {
    
    @Autowired
    private McpClient mcpClient;
    
    @GetMapping("/search")
    public Map<String, Object> search(@RequestParam String q, 
                                      @RequestParam(defaultValue = "5") int limit) {
        
        Map<String, Object> response = new HashMap<>();
        response.put("keyword", q);
        response.put("limit", limit);
        
        try {
            // 调用MCP服务
            McpCallToolRequest request = new McpCallToolRequest();
            request.setName("search-images");
            
            Map<String, Object> args = new HashMap<>();
            args.put("keyword", q);
            args.put("limit", limit);
            request.setArguments(args);
            
            Object result = mcpClient.callTool(request);
            response.put("success", true);
            response.put("data", result);
            response.put("message", "搜索成功");
            
        } catch (Exception e) {
            response.put("success", false);
            response.put("message", "搜索失败: " + e.getMessage());
        }
        
        return response;
    }
}

第六部分:最佳实践(避坑指南)

1. 工具设计原则

java

// ❌ 错误:一个工具做太多事
@McpTool(name = "doEverything")
public String doEverything(String action, String param1, String param2, ...)

// ✅ 正确:每个工具职责单一
@McpTool(name = "searchImages")
public List<Image> searchImages(String keyword)

@McpTool(name = "downloadImage")  
public String downloadImage(String imageId)

@McpTool(name = "getImageInfo")
public ImageInfo getImageInfo(String imageId)

2. 参数设计

java

// ❌ 错误:参数含义模糊
@McpTool(name = "process")
public String process(String p1, String p2)

// ✅ 正确:参数清晰且有描述
@McpTool(name = "searchImages", description = "搜索图片")
public List<Image> searchImages(
    @McpToolParam(description = "搜索关键词,如:风景、猫") String keyword,
    @McpToolParam(description = "返回数量,1-50,默认10") Integer limit,
    @McpToolParam(description = "排序方式:relevance/popularity/latest") String sortBy
)

3. 错误处理

java

@McpTool(name = "searchImages")
public List<Image> searchImages(String keyword, Integer limit) {
    // 参数校验
    if (keyword == null || keyword.trim().isEmpty()) {
        throw new IllegalArgumentException("关键词不能为空");
    }
    
    if (limit != null && (limit < 1 || limit > 100)) {
        throw new IllegalArgumentException("数量必须在1-100之间");
    }
    
    try {
        // 业务逻辑
        return doSearch(keyword, limit);
    } catch (Exception e) {
        log.error("搜索失败", e);
        // 返回空列表而不是抛异常,让AI能继续工作
        return Collections.emptyList();
    }
}

第七部分:部署方案

方案1:本地运行(开发调试)

bash

# 直接运行JAR
java -jar mcp-server-1.0.0.jar

# 或使用Maven
mvn spring-boot:run

方案2:Docker部署

创建 Dockerfile

dockerfile

FROM openjdk:17-jdk-slim
COPY target/mcp-server-1.0.0.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

构建和运行:

bash

# 构建镜像
docker build -t mcp-image-search .

# 运行容器
docker run -d -p 8080:8080 --name mcp-service mcp-image-search

# 查看日志
docker logs mcp-service

方案3:云平台部署(Railway示例)

  1. 注册 Railway.app

  2. 连接你的GitHub仓库

  3. Railway会自动检测Spring Boot项目并部署

  4. 获得公网URL:https://your-service.up.railway.app


第八部分:安全和常见问题

安全问题及解决方案

风险 说明 解决方案
命令注入 恶意参数执行危险命令 严格校验输入,白名单限制
越权访问 访问不该访问的资源 实现权限校验
信息泄露 返回敏感数据 数据脱敏,最小化返回
DoS攻击 大量请求消耗资源 限流,熔断

安全配置示例

java

@Configuration
public class McpSecurityConfig {
    
    @Bean
    public McpSecurityInterceptor securityInterceptor() {
        return new McpSecurityInterceptor() {
            @Override
            public boolean preHandle(String toolName, Map<String, Object> args) {
                // 1. 参数长度限制
                String keyword = (String) args.get("keyword");
                if (keyword != null && keyword.length() > 100) {
                    throw new SecurityException("参数过长");
                }
                
                // 2. 敏感词过滤
                if (containsSensitiveWords(keyword)) {
                    throw new SecurityException("包含敏感词");
                }
                
                // 3. 频率限制
                if (isRateLimitExceeded()) {
                    throw new SecurityException("请求过于频繁");
                }
                
                return true;
            }
        };
    }
}
Logo

更多推荐