深入浅出MCP:从零开始的完整学习指南(保姆级教程)
手把手带你理解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服务端 |
| 菜单 | 菜单 | 工具列表 |
流程:
顾客(AI)说要吃什么 →
服务员(MCP客户端)记录并传给厨房 →
厨房(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。
详细步骤:
下载Cursor:访问 https://cursor.sh/ 下载安装
打开设置:
Windows/Linux:
Ctrl + ,Mac:
Cmd + ,
找到MCP配置:
在设置搜索框输入 "MCP"
找到 "MCP Servers" 配置项
添加服务:
json
{ "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/folder"] } } }重启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示例)
注册 Railway.app
连接你的GitHub仓库
Railway会自动检测Spring Boot项目并部署
获得公网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; } }; } }