MCP (Model Context Protocol) 完全指南:架构解析与实战入门

665 阅读14分钟

2025年5月12日

MCP简介

MCP(Model Context Protocol)是一个开放协议,旨在标准化应用程序向大语言模型(LLMs)提供上下文的方式。您可以将 MCP 想象成 AI 应用的 USB-C 接口——正如 USB-C 为设备连接各类外设提供了统一标准,MCP 也为 AI 模型连接不同数据源和工具建立了标准化桥梁。

为什么选择 MCP?

MCP 帮助在 LLMs 之上构建Agent和复杂工作流。由于 LLMs 需要频繁对接数据和工具,MCP 提供:

  • 持续增长的预构建集成库,实现 LLM 即插即用

  • 灵活切换不同 LLM 服务商的能力

与Function Calling的区别

Function Calling是模型API的能力。模型API内部根据自家模型的微调数据,规定了描述工具的格式、模型API请求工具调用的格式、给模型API工具返回结果的格式。

MCP规范了 工具的发现、工具的描述、工具的调用、工具的返回结构。

sequenceDiagram
  participant User
  participant Agent
  participant ModelAPI
  participant Tool

  User ->> Agent: 查询今天杭州的天气
  Agent ->> Agent: 提示词工程

  rect rgba(0, 150, 0, 0.1)
    note right of Agent: MCP
    Agent ->> Agent: 获取可用工具列表
  end

  Agent ->> Agent: 把可用工具的描述信息<br>转换为模型API规定的格式

  rect rgba(0, 0, 255, 0.1)
    note right of Agent: Function Calling
    Agent ->> ModelAPI: 工具列表+提示词
    ModelAPI ->> ModelAPI: 把工具信息和用户问题<br>转换为文本,调用模型
    ModelAPI -->> Agent: 选取工具+参数schema
  end

  Agent ->> Agent: 把参数schema转换为工具参数

  rect rgba(0, 150, 0, 0.1)
    note right of Agent: MCP
    Agent ->> Tool: 调用工具函数
    Tool -->> Agent: 返回结果
  end

  Agent ->> Agent: 把工具返回结果<br>转换为模型API规定的格式

  rect rgba(0, 0, 255, 0.1)
    note right of Agent: Function Calling
    Agent ->> ModelAPI: 工具返回结果
    ModelAPI -->> Agent: 杭州天气是xxxx
  end

  Agent -->> User: 杭州天气是xxxx

OpenAI对Funciton Calling的描述:platform.openai.com/docs/guides…

OpenAI Funciton Calling的工具定义格式“恰好”和MCP协议中的工具定义格式非常非常相似。

mcp-study-openai-function-calling.png

核心架构

重点理解MCP的CS架构设计传输层规范。

模型上下文协议 (MCP) 建立在灵活、可扩展的架构之上,可实现 LLM 应用程序和集成之间的无缝通信。

概述

MCP 采用客户端-服务器架构,支持宿主程序连接多个服务器:

  • MCP 宿主应用(MCP Hosts):如 Claude Desktop、IDE 或 AI 工具等需要通过 MCP 获取数据的应用程序

  • MCP 客户端(MCP Clients):运行在宿主应用中,与服务器保持 1:1 连接的协议客户端

  • MCP 服务器(MCP Servers):通过标准化 Model Context Protocol 暴露特定能力的轻量级程序,向客户端提供上下文、工具和提示词

  • 本地数据源(Local Data Sources):MCP 服务器可安全访问的计算机本地文件、数据库和服务

  • 远程服务(Remote Services):MCP 服务器可通过互联网(如 API)连接的外部系统

flowchart LR
    subgraph "Your Computer"
        Host["Host with MCP Client\n(Claude, IDEs, Tools)"]
        S1["MCP Server A"]
        S2["MCP Server B"]
        S3["MCP Server C"]
        Host <-->|"MCP Protocol"| S1
        Host <-->|"MCP Protocol"| S2
        Host <-->|"MCP Protocol"| S3
        S1 <--> D1[("Local\nData Source A")]
        S2 <--> D2[("Local\nData Source B")]
    end
    subgraph "Internet"
        S3 <-->|"Web APIs"| D3[("Remote\nService C")]
    end
flowchart LR
    subgraph "Host"
        client1[MCP Client]
        client2[MCP Client]
    end
    subgraph "Server Process"
        server1[MCP Server]
    end
    subgraph "Server Process"
        server2[MCP Server]
    end

    client1 <-->|Transport Layer| server1
    client2 <-->|Transport Layer| server2

核心组件

核心组件包括:协议层、传输层、消息类型

协议层

协议层负责消息封装、请求/响应关联及上层的通信模式

// 处理client和server通信的MCP session
interface McpSession {

	// 向模型对应方发送请求,并期望获得类型为 T 的响应。
	<T> Mono<T> sendRequest(String method, Object requestParams, TypeReference<T> typeRef);

	// 向模型client或server发送不带参数的通知
	default Mono<Void> sendNotification(String method) {
		return sendNotification(method, null);
	}

	// 向模型client或server发送带参数的通知
	Mono<Void> sendNotification(String method, Object params);

	// 关闭session并异步释放全部关联的资源
	Mono<Void> closeGracefully();

	// 关闭session并释放全部关联的资源
	void close();
}

传输层

传输层负责客户端与服务端的实际通信。

MCP 支持多种传输机制: 

  1. 标准输入输出传输(Stdio)
  • 使用标准输入/输出进行通信

  • 适用于本地进程间通信 

  1. HTTP SSE 传输
  • 使用服务器推送事件(SSE)实现服务端到客户端通信

  • 使用 HTTP POST 实现客户端到服务端通信 

所有传输均采用JSON-RPC 2.0 协议交换消息。

JSON-RPC:www.jsonrpc.org/

JSON-RPC示例:

服务端请求 --> {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}
客户端接收 <-- {"jsonrpc": "2.0", "result": 19, "id": 1}

服务端请求 --> {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 2}
客户端接收 <-- {"jsonrpc": "2.0", "result": 19, "id": 2}

消息类型

MCP 定义以下主要消息类型: 

1. **请求(Requests)**需要对方响应

record JSONRPCRequest (
  // ...
  String method,  // 方法标识
  Object params   // 请求参数
)

2. **结果(Results)**表示请求成功响应

record JSONRPCResponse (
  // ...
  Object result,  // 结果数据
  JSONRPCError error
)

3. **错误(Errors)**表示请求失败:

record JSONRPCError (
  int code,        // 错误代码
  String message,  // 错误描述
  Object data
)

4. **通知(Notifications)**无需响应的单向消息

record JSONRPCNotification (
  // ...
  String method,
  Object params
)

连接生命周期

一、初始化阶段

  1. 客户端发送包含协议版本与能力的initialize请求

  2. 服务端返回协议版本与能力声明

  3. 客户端发送initialized通知确认

  4. 开始正常消息交换

sequenceDiagram
    participant Client
    participant Server

    Client->>Server: initialize request
    Server->>Client: initialize response
    Client->>Server: initialized notification

    Note over Client,Server: Connection ready for use

二、消息交换阶段

初始化完成后支持以下模式:

  • 请求-响应:客户端或服务端发送请求,对方响应

  • 通知:任意一方发送单向消息

三、终止阶段

连接可通过以下方式终止:

  • 通过close()方法优雅关闭

  • 传输层断开连接

  • 错误状态触发断开

错误处理

MCP 定义标准错误代码:

class ErrorCodes {
    // Standard JSON-RPC error codes
    int PARSE_ERROR = -32700;
    int INVALID_REQUEST = -32600;
    int METHOD_NOT_FOUND = -32601;
    int INVALID_PARAMS = -32602;
    int INTERNAL_ERROR = -32603;
}

SDK 与应用可自定义高于 -32000 的错误代码。

错误通过以下方式传递:

  • 请求的错误响应

  • 传输层的错误事件

  • 协议层的错误处理

实现示例

基础 MCP 服务端实现示例:

var transport = new StdioServerTransportProvider();

// Configure server capabilities with resource support
var capabilities = McpSchema.ServerCapabilities.builder()
        .resources(false, true) // No subscribe support, but list changes notifications
        .tools(true) // Tool support with list changes notifications
        .prompts(true) // Prompt support with list changes notifications
        .logging() // Logging support
        .build();

// Create the server with both tool and resource capabilities
var server = McpServer.async(transport)
        .serverInfo("MCP Demo Server", "1.0.0")
        .capabilities(capabilities)
        .resources(Collections.emptyList())
        .prompts(Collections.emptyList())
        .tools(Collections.emptyList())
        .build();

最佳实践

  1. 本地通信
  • 使用 stdio 传输协议

  • 适用于同机进程通信

  • 简化进程管理

  1. 远程通信
  • 使用 SSE 传输协议

  • 需考虑身份验证与授权等安全因素

基本元素

MCP的基本元素包括:

  • Resource

  • Prompt

  • Tool

  • Sampling

  • Root

其中,以Resource、Prompt、Tool为核心元素。

资源(Resources)

资源是模型上下文协议(Model Context Protocol, MCP)中的核心基本元素,它允许服务器暴露数据和内容,供客户端读取并用作LLM交互的上下文。

概述

资源代表MCP服务器希望提供给客户端的任何类型的数据。这可以包括:

  • 文件内容

  • 数据库记录

  • API响应

  • 实时系统数据

  • 屏幕截图和图像

  • 日志文件

  • 等等

每个资源都由唯一的URI标识,可以包含文本或二进制数据。

资源URI

资源使用以下格式的URI进行标识:

[协议]://[主机]/[路径]

例如:

  • file:///home/user/documents/report.pdf

  • postgres://database/customers/schema

  • screen://localhost/display1

协议和路径结构由MCP服务器实现定义。服务器可以定义自己的自定义URI方案。

资源类型

资源可以包含两种类型的内容:

文本资源

文本资源包含UTF-8编码的文本数据。例如:

  • 源代码

  • 配置文件

  • 日志文件

  • JSON/XML数据

  • 纯文本

二进制资源

二进制资源包含以base64编码的原始二进制数据。例如:

  • 图像

  • PDF文件

  • 音频文件

  • 视频文件

  • 其他非文本格式

资源发现

客户端(Client)可以通过两种主要方式发现可用资源

直接资源

服务器通过resources/list端点暴露具体资源的列表。

record Resource(
    String uri,             // 资源的唯一标识符
    String name,            // 人类可读的名称
    String description,     // 可选描述
    String mimeType,        // 可选MIME类型
    Annotations annotations // 可选的注解信息
) implements Annotated {}
资源模版

对于动态资源,服务器可以暴露URI模板,客户端可以用它来构建有效的资源URI:

record ResourceTemplate(
    String uriTemplate,    // 遵循RFC6570的URI模板,例如:/user/{userId}
    String name,
    String description,
    String mimeType,
    Annotations annotations
) implements Annotated {}

读取资源

要读取资源,客户端需发送带有资源URI的resources/read请求。

服务器以资源内容列表响应:

// 返回的资源结构
record ReadResourceResult(
    List<ResourceContents> contents    // 资源列表
) {}
// 文本资源内容
record TextResourceContents(
    String uri,
    String mimeType,
    String text    // 文本数据
) implements ResourceContents {}
// 二进制资源内容
record BlobResourceContents(
    String uri,
    String mimeType,
    String blob    // 二进制数据
) implements ResourceContents {}

资源更新通知

MCP通过两种机制支持资源的实时更新:

列表变更

服务器可以通过notifications/resources/list_changed通知客户端其可用资源列表的变更。

内容变更

客户端可以订阅特定资源的更新:

  1. 客户端发送带有资源URI的resources/subscribe

  2. 当资源变更时,服务器发送notifications/resources/updated

  3. 客户端可以通过resources/read获取最新内容

  4. 客户端可以通过resources/unsubscribe取消订阅

最佳实践

  1. 为动态内容实现资源模板

  2. 对频繁变更的资源使用订阅

  3. 考虑对大型资源列表进行分页

  4. 适当时缓存资源内容

提示词(Prompts)

提示词模板使服务器能够定义可重用的提示模板和工作流程,客户端可以轻松地向用户和LLM呈现这些内容。它们提供了一种强大的方式来标准化和共享常见的LLM交互。

Tips:提示模板设计为用户控制的,这意味着它们从服务器暴露给客户端时,目的是让用户能够明确地选择使用它们。

概述

MCP中的提示模板是预定义的模板,可以:

  • 接受动态参数

  • 包含来自资源的上下文

  • 链接多个交互

  • 指导特定的工作流程

  • 作为UI元素呈现(如斜杠命令)

提示词模版结构

// 提示词模版结构
record Prompt(
    String name,
    String description,
    List<PromptArgument> arguments    // 可选的参数列表
) {}
// 提示词模版参数
record PromptArgument(
    String name,
    String description,
    Boolean required    // 参数是否必填
) {}

发现提示词模版

客户端可以通过prompts/list端点发现可用的提示模板

服务端响应:

record ListPromptsResult(
    List<Prompt> prompts,    // 提示词模版列表
    String nextCursor        // 分页查询使用的游标
) {}

使用提示词模版

要使用提示模板,客户端需发送prompts/get请求

record GetPromptRequest(
    String name,
    Map<String, Object> arguments
) implements Request {}

响应的提示词内容有三种类型:

  • text:普通文本

  • image:图片

  • resource:资源(文本或二进制)

// 请求
{
  method: "prompts/get",
  params: {
    name: "analyze-code",
    arguments: {
      language: "python"
    }
  }
}

// 响应
{
  description: "分析Python代码以寻找潜在的改进",
  messages: [
    {
      role: "user",
      content: {
        type: "text",
        text: "请分析以下Python代码以寻找潜在的改进:\n\n```python\ndef calculate_sum(numbers):\n    total = 0\n    for num in numbers:\n        total = total + num\n    return total\n\nresult = calculate_sum([1, 2,3, 4, 5])\nprint(result)\n```"
      }
    }
  ]
}

工具(Tools)

工具是MCP中的一种强大的基本元素,它使服务器能够向客户端暴露可执行的功能。通过工具,LLM可以与外部系统交互,执行计算,并在现实世界中采取行动。

工具被设计为由模型控制,这意味着工具从服务器暴露给客户端时,其目的是让AI模型能够自动调用它们。

概述

MCP中的工具允许服务器暴露可执行函数,这些函数可被客户端调用并由LLM使用来执行操作。工具的关键方面包括:

  • 发现:客户端可以通过tools/list端点列出可用的工具

  • 调用:工具通过tools/call端点被调用,服务器执行请求的操作并返回结果

  • 灵活性:工具可以从简单的计算到复杂的API交互不等

与资源类似,工具由唯一名称标识,并可以包含描述来指导其使用。然而,与资源不同,工具代表可以修改状态或与外部系统交互的动态操作。

工具定义结构

{
  name: string;          // 工具的唯一标识符
  description?: string;  // 人类可读的描述
  inputSchema: {         // 工具参数的JSON Schema
    type: "object",
    properties: { ... }  // 工具特定的参数
  },
  annotations?: {        // 关于工具行为的可选提示
    title?: string;      // 工具的人类可读标题
    readOnlyHint?: boolean;    // 如果为true,工具不会修改其环境
    destructiveHint?: boolean; // 如果为true,工具可能执行破坏性更新
    idempotentHint?: boolean;  // 如果为true,使用相同参数重复调用不会产生额外效果
    openWorldHint?: boolean;   // 如果为true,工具与外部实体交互
  }
}

inputSchema结构:

record JsonSchema(
    String type,                       // 数据类型
    Map<String, Object> properties,    // key:参数名称, value:参数的描述Schema
    List<String> required,             // 必填的参数名称集合
    Boolean additionalProperties,
    Map<String, Object> defs,
    Map<String, Object> definitions
) {}

工具示例

以下是服务器可以提供的工具类型示例:

系统操作

与本地系统交互的工具:

{
  name: "execute_command",
  description: "运行shell命令",
  inputSchema: {
    type: "object",
    properties: {
      command: { type: "string" },
      args: { type: "array", items: { type: "string" } }
    }
  }
}
API集成

封装外部API的工具:

{
  name: "github_create_issue",
  description: "创建GitHub issue",inputSchema: {
    type: "object",
    properties: {
      title: { type: "string" },
      body: { type: "string" },
      labels: { type: "array", items: { type: "string" } }
    }
  }
}
数据处理

转换或分析数据的工具:

{
  name: "analyze_csv",
  description: "分析CSV文件",
  inputSchema: {
    type: "object",
    properties: {
      filepath: { type: "string" },
      operations: {
        type: "array",
        items: {
          enum: ["sum", "average", "count"]
        }
      }}
  }
}

工具发现和更新

MCP支持动态工具发现:

  1. 客户端可以随时列出可用工具

  2. 服务器可以使用notifications/tools/list_changed通知客户端工具变更

  3. 工具可以在运行时添加或移除

  4. 工具定义可以更新(但应谨慎进行)

错误处理

工具错误应在结果对象中报告,而不是作为MCP协议级别的错误。这允许LLM查看并可能处理错误。当工具遇到错误时:

  1. 在结果中设置isErrortrue

  2. content数组中包含错误详情

try {
  // Tool operation
  const result = performOperation();
  return {
    content: [
      {
        type: "text",
        text: `Operation successful: ${result}`
      }
    ]
  };
} catch (error) {
  return {
    isError: true,
    content: [
      {
        type: "text",
        text: `Error: ${error.message}`
      }
    ]
  };
}

最佳实践

  1. 提供清晰、描述性的名称和描述

  2. 为参数使用详细的JSON Schema定义

  3. 在工具描述中包含示例,以演示模型应如何使用它们

  4. 让工具的功能更聚焦和原子

采样(Sampling)

采样是MCP的一项强大功能,它允许服务器通过客户端请求LLM补全内容,从而实现复杂的智能代理行为,同时保持安全性和隐私性。

采样工作原理

采样流程遵循以下步骤:

  1. 服务器向客户端发送sampling/createMessage请求

  2. 客户端审核请求并可以修改它

  3. 客户端使用LLM生成补全内容

  4. 客户端审核LLM生成的内容

  5. 客户端将结果返回给服务器

这种的设计确保用户能够控制LLM看到和生成的内容。

根源(Roots)

Roots(根源)是MCP中定义服务器可操作边界的概念。它提供了一种方式,使得客户端能够告知服务器相关资源及其位置。

Roots是客户端建议服务器应关注的URI。当客户端连接到服务器时,它会声明服务器应该使用哪些根源。虽然主要用于文件系统路径,但根源可以是任何有效的URI,包括HTTP URL。例如:

file:///home/user/projects/myapp
https://api.example.com/v1

示例

{
  "roots": [
    {
      "uri": "file:///home/user/projects/frontend",
      "name": "前端代码库"
    },
    {
      "uri": "https://api.example.com/v1",
      "name": "API端点"
    }
  ]
}

MCP实践

自己编写一个MCP Server

官方提供了多种语言的SDK:

但是不同语言的SDK的质量差距较大,例如java-sdk相比python-sdk就垃圾不少。推荐使用python-sdk。

第一步,安装python项目的环境管理工具uv

curl -LsSf https://astral.sh/uv/install.sh | sh

# 或 通过 brew 安装
brew install uv

第二步,设置python项目环境

# 为我们的项目创建一个新目录
uv init mcp-server-demo
cd mcp-server-demo

# 创建虚拟环境并激活它
uv venv
source .venv/bin/activate

# 安装依赖
uv add "mcp[cli]"

# 创建我们的MCP服务器文件
touch python-mcp-test.py

第三步,编写server代码,代码示例如下

# python-mcp-test.py
from mcp.server.fastmcp import FastMCP

# 创建一个 Server
mcp = FastMCP("python-mcp-test")


# 添加一个工具
@mcp.tool(description="整数加法计算器")
def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b


# 添加一个资源
@mcp.resource("files:///documents/QLExpress.md")
def get_ql_express_knowledge() -> str:
    """QL express knowledge"""
    with open('resources/QLExpress.md', 'r') as f:
        return f.read()


# 添加一个提示词
@mcp.prompt(description="查询部门")
def query_dept(dept_no: str) -> str:
    """Query department"""
    return f"查询编号为{dept_no}的部门信息"


if __name__ == "__main__":
    mcp.run()

第四步,调试server

运行命令,然后在浏览器页面进行调试

uv run mcp dev python-mcp-test.py

image

以HSF服务作为MCP Server

请参考文章:ata.atatech.org/articles/11…

以Cline作为MCP Host

第一步,安装VS Code,并在VS Code上下载Cline

mcp-study-install-cline.png

第二步,开启MCP功能

第三步,连接LLM,以连接DeepSeek为例

第四步,配置MCP Server的连接信息。此处以连接自定义MCP Server为例,也可以使用市场上现成的MCP Server。

mcp-study-cline-config-mcp-server.png

示例:

Java MCP Server:

{
  "mcpServers": {
    "spring-ai-mcp-rpc": {
      "command": "java",
      "args": [
        "-Dspring.ai.mcp.server.stdio=true",
        "-jar",
        "// todo jar包文件绝对路径,例如:/Users/yuguo/tools/mvn_repository/org/example/test-demo/0.0.1-SNAPSHOT/test-demo-0.0.1-SNAPSHOT.jar"
      ],
      "transportType": "stdio"
    }
  }
}

Python MCP Server:

{
  "mcpServers": {
    "python-mcp-test": {
      "command": "/opt/homebrew/bin/uv",
      "args": [
        "--directory",
        "// todo python文件的文件夹路径,例如:/Users/yuguo/PycharmProjects/mcp-server-demo",
        "run",
        "// todo python文件名称,例如:python-mcp-test.py"
      ],
      "transportType": "stdio"
    }
  }
}

HSF SSE:

{
  "mcpServers": {
    "// todo 你的HSF mcp server名称": {
      "url": "// todo 从HSF控制台复制过来的url"
    }
  }
}

最终效果:

CLINE自动调用“部门查询”的HSF服务查到了M3775是淘天。

参考文档

modelcontextprotocol.io/introductio…