一、MCP 的基本概念与背景

1.1 背景

想象一个场景:国庆假期我们准备去XX旅行,在AI技术普及前,我们通常的做法是在多个独立平台(如马蜂窝、小红书、B站)搜集碎片化信息,再切换至地图APP规划行程,随后在购票APP反复对比航班,临行前还需分别查询天气APP和美食APP,并将所有信息手动整合到Word或图片中。这个过程高度碎片化且耗时,等旅行攻略做完,就已经累了,不想出门了。

而 AI 技术成熟后,我们只需要告诉大模型:旅行目的地、时间以及预算,大模型即可自主调用多智能体系统——通过MCP协议实时连接天气、地图、购票等外部工具获取数据,并通过A2A协议协调智能体间协作(如天气Agent与路线Agent联动优化行程),最终生成结构化攻略文档并自动保存至本地,全程无需人工干预。

在 AI 领域,大模型连接天气APP或API独立执行任务可称为一个智能体,多个智能体相互协助,完成复杂任务。

为了解决 AI 智能体之间的交流与协作问题,两个重要的协议标准应运而生:Google 的 A2A(Agent-to-Agent)协议 和 Anthropic 的 MCP(Model Context Protocol)协议

A2A 协议:定义智能体之间的通信标准,解决智能体之间如何互相交互(水平集成)

MCP 协议:定义大模型与外部工具的通信标准,解决智能体与外部工具如何交互(垂直集成)

本篇重点介绍 MCP 协议的原理及基于go语言开发MCP服务的实践!

1.2 概念

MCP(Model Context Protocol,模型上下文协议)是由 Anthropic ([ænˈθrɒpɪk]人类的)公司于 2024 年 11 月开源的一种通信协议,旨在解决大型语言模型(LLM)与外部数据源及应用程序之间的通信。简单来说,MCP 就像给 AI 装上了一个"万能接口",让 AI 能够与各种外部系统和数据源实现标准化的双向通信,被称为“AI 领域的 USB 接口”。

MCP中文站:MCP中文简介 – MCP 中文站(Model Context Protocol 中文)

官网地址(英文版):Introduction - Model Context Protocol

ClaudeMcp 社区:Claude MCP 社区

github 地址:https://github.com/modelcontextprotocol

1.3 LLM 在 MCP 出现前后的能力对比

MCP出现之前 MCP出现之后
无法获取实时数据,如最新天气信息 访问最新的网络数据和信息
无法访问本地数据源,比如本地文件、数据库等 访问本地数据源
无法与外部应用程序交互 调用各种专业工具和服务

1.4 效果演示

效果一:在 cherry studio 中添加生成流程图的MCP服务,生成流程图图片及可编辑的ppt文件等

效果二:在 cherry studio 中添加文件系统的MCP服务,生成产品发布流程的txt文档,并保存到我的电脑桌面

二、MCP 核心架构

从本质上讲,MCP 遵循客户端-服务器架构,其中主机应用程序可以连接到多个服务器:

用户、大模型及MCP服务的交互流程:

三、MCP 消息格式及通信方式

3.1 消息格式

MCP 的通信基于 JSON-RPC 2.0,支持请求、响应和通知三种消息类型,确保通信的标准化和一致性。

3.1.1 请求

// 问号表示 params 参数非必须
{
  jsonrpc: "2.0";
  id: string | number;
  method: string;
  params?: {
    [key: string]: unknown;
  };
}


3.1.2 响应

当响应成功时,result 必须包含,error 必须不包含;当响应错误时,result 必须不包含,error 必须包含。

{
  jsonrpc: "2.0";
  id: string | number;
  result?: {
    [key: string]: unknown;
  }
  error?: {
    code: number;
    message: string;
    data?: unknown;
  }
}

3.1.3 通知

没有包含“id”成员的请求对象为通知,作为通知的请求对象表明客户端对相应的响应对象并不感兴趣,本身也没有响应对象需要返回给客户端。params 参数非必须。

{
  jsonrpc: "2.0";
  method: string;
  params?: {
    [key: string]: unknown;
  };
}


3.2 传输类型

通信方式 传输机制 应用场景 优点 缺点
STDIO(标准输入输出) 通过操作系统的标准输入(stdin)和标准输出(stdout)进行进程间通信,数据以换行符分隔的UTF-8编码JSON-RPC消息传输 本地开发与调试、单机器内的工具集成(如桌面应用嵌入插件)、轻量级本地代理(如对接本地数据库) 无外部依赖,实现简单;本地通信延迟极低,适合高频交互 单进程通信,无法并行处理多个客户端请求;进程通信资源开销大,难以支持大量服务
SSE(Server-Sent Events) 基于HTTP协议的单向数据流传输,客户端通过HTTP GET请求建立长连接,服务器以流式方式持续推送数据 实时推送应用(如新闻更新、社交媒体通知)、Web环境集成(如浏览器调用后端MCP服务) 基于HTTP协议,兼容性好;连接中断时自动重连,保证数据可靠性 单向传输,无法满足双向复杂交互需求;受网络带宽和延迟影响,适合低频次或跨设备通信
Streamable HTTP 基于HTTP协议的双向流式传输,客户端通过HTTP POST发送请求并接收流式响应,支持长连接中的实时数据推送和分块传输 高并发、低延迟的分布式系统(如实时流媒体、在线游戏)、大文件流式传输(如视频、音频分段传输) 最灵活、最强大,适用于大规模并发和高度交互场景;支持分块传输,降低延迟,提高用户体验 实现复杂,需处理网络连接、端口开放、协议解析等;需考虑网络安全(如加密、认证、防火墙配置)

3.3 通信流程示例

以 SSE 通信方式为例, MCP 客户端连接 MCP 服务端并执行任务的整个通信流程及消息内容如下:

四、MCP 核心组件

4.1 资源(Resources)

资源表示 MCP 服务器想要向客户端提供的任何类型数据,可包括:

  • 文件内容
  • 数据库记录
  • API 响应
  • 实时系统数据
  • 截图和图片
  • 日志文件

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

4.2 提示(Prompts)

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

  • 接受动态参数
  • 上下文 链接多个交互
  • 指导特定工作流程
  • 表面作为 UI 元素(如斜线命令)

4.3 工具(Tools)

MCP 中的工具允许服务器公开可由客户端调用的可执行函数。工具的关键方面包括:

  • 发现(tools/list):客户端可以列出可用的工具
  • 调用(tools/call):服务器执行请求的操作并返回结果
  • 灵活性:工具范围从简单的计算到复杂的 API 交互

4.4 采样(Sampling)

采样是 MCP 的一项强大功能,允许服务器通过客户端请求 LLM 完成,从而实现复杂的代理行为,同时保持安全性和隐私性。这种人机交互设计确保用户可以控制 LLM 所看到和生成的内容。

五、基于 go 实现 MCP 服务

5.1 开发流程

5.2 安装 mcp-go 组件

此处使用支持mcp的开源组件 mcp-go,github 地址为:https://github.com/mark3labs/mcp-go

go get github.com/mark3labs/mcp-go

 注意:官方支持 mcp 的 go 语言 sdk 为:https://github.com/modelcontextprotocol/go-sdk

5.3 代码实现

5.3.1 创建 MCPServer

// Create a new MCP server
s := server.NewMCPServer(
	"Demo 🚀",
	"1.0.0",
	server.WithToolCapabilities(false),
)

5.3.2 添加工具

// Add tool
tool := mcp.NewTool("hello_world",
	mcp.WithDescription("Say hello to someone"),
	mcp.WithString("name",
		mcp.Required(),
		mcp.Description("Name of the person to greet"),
	),
)

5.3.3 添加工具处理函数

// Add tool handler
s.AddTool(tool, helloHandler)

func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	name, err := request.RequireString("name")
	if err != nil {
		return mcp.NewToolResultError(err.Error()), nil
	}

	return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil
}

5.3.4 启动服务

按通信方式,可以启动为三种MCP服务:stdio、sse、streamableHttp

// 启动为 stdio 服务
if err := server.ServeStdio(s); err != nil {
	log.Fatalf("Server error: %v", err)
}

// 启动为 sse 服务
sseSrv := server.NewSSEServer(s)
if err := sseSrv.Start(":8081"); err != nil {
	fmt.Printf("Server error: %v\n", err)
}

// 启动为 streamableHttp 服务
srv := server.NewStreamableHTTPServer(s)
if err := srv.Start(":8081"); err != nil {
	fmt.Printf("Server error: %v\n", err)
}

5.3.5 完整代码

package main

import (
	"context"
	"fmt"

	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)

func main() {
	// Create a new MCP server
	s := server.NewMCPServer(
		"Demo 🚀",
		"1.0.0",
		server.WithToolCapabilities(false),
	)

	// Add tool
	tool := mcp.NewTool("hello_world",
		mcp.WithDescription("Say hello to someone"),
		mcp.WithString("name",
			mcp.Required(),
			mcp.Description("Name of the person to greet"),
		),
	)

	// Add tool handler
	s.AddTool(tool, helloHandler)

	// Start the stdio server
	// sseSrv := server.NewStreamableHTTPServer(s)
	sseSrv := server.NewSSEServer(s)
	if err := sseSrv.Start(":8081"); err != nil {
		fmt.Printf("Server error: %v\n", err)
	}
}

func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	name, err := request.RequireString("name")
	if err != nil {
		return mcp.NewToolResultError(err.Error()), nil
	}

	return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil
}

5.3.6 指定自定义httpserver

MCP服务默认只有/sse、/message、/mcp 路由,在实际开发场景中,往往需要在MCP服务中新增自定义路由,比如服务健康状态检查等,此时,需要先自定义 http.Server 并添加路由,然后在创建 MCPServer 时绑定自定义 http.Server。

// 创建 http 路由
mux := http.NewServeMux()
mux.HandleFunc("/management/health", func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	w.Write([]byte(`{"status":{"http":{"working":true,"message":"conn ok."}}}`))
})

// 创建 StreamableHttp 服务器,并绑定自定义 httpServer
srv := server.NewStreamableHTTPServer(
	s,
	server.WithStreamableHTTPServer(&http.Server{Handler: mux}),
)
// 使用自定义 httpServer 后,需要将显示指定 /mcp 路由
mux.Handle("/mcp", srv)

// 启动 mcp 服务
if err := srv.Start(":8085"); err != nil {
	fmt.Printf("Server error: %v\n", err)
}

完整代码如下:

package main

import (
	"context"
	"fmt"
	"net/http"

	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)

func main() {
	// Create a new MCP server
	s := server.NewMCPServer(
		"Demo 🚀",
		"1.0.0",
		server.WithToolCapabilities(false),
	)

	// Add tool
	tool := mcp.NewTool("hello_world",
		mcp.WithDescription("Say hello to someone"),
		mcp.WithString("name",
			mcp.Required(),
			mcp.Description("Name of the person to greet"),
		),
	)

	// Add tool handler
	s.AddTool(tool, helloHandler)

// 创建 http 路由
mux := http.NewServeMux()
mux.HandleFunc("/management/health", func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	w.Write([]byte(`{"status":{"http":{"working":true,"message":"conn ok."}}}`))
})

// 创建 StreamableHttp 服务器,并绑定自定义 httpServer
srv := server.NewStreamableHTTPServer(
	s,
	server.WithStreamableHTTPServer(&http.Server{Handler: mux}),
)
// 使用自定义 httpServer 后,需要将显示指定 /mcp 路由
mux.Handle("/mcp", srv)

// 启动 mcp 服务
if err := srv.Start(":8085"); err != nil {
	fmt.Printf("Server error: %v\n", err)
}
}

func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	name, err := request.RequireString("name")
	if err != nil {
		return mcp.NewToolResultError(err.Error()), nil
	}

	return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil
}

运行mcp服务,在 MCP Inspector(下文有介绍) 调试工具中的效果演示:

在浏览器中输入自定义路由,访问成功:

5.4 优秀开源mcp项目参考

github-mcp-serverhttps://github.com/github/github-mcp-server

六、 MCP 调试工具

6.1 调试工具概述

  • MCP Inspector:交互式调试界面,直接服务器测试

  • Claude for Desktop:集成测试,日志收集

  • 服务器日志:自定义服务日志

6.2 MCP Inspector 简介

MCP Inspector是一款用于测试和调试 MCP 服务器的交互式开发者工具。

官方说明文档:https://modelcontextprotocol.io/docs/tools/inspector

6.3 MCP Inspector 使用方法

6.3.1 运行 Inspector

需要先安装 npx ,然后直接在命令行中运行 Inspector

npx @modelcontextprotocol/inspector

6.3.2 浏览器中打开交互式调试界面

将上图中的url地址复制到浏览器,打开,如图所示:

如果在浏览器中仅输入:http://localhost:6274,则需要Configration中配置 Proxy Session Token,值为步骤1中运行命令 npx @modelcontextprotocol/inspector,生成的 Session Token。

6.3.3 配置命令及选项

选择协议类型(STDIO/SSE/Streamable HTTP),输入命令、参数、环境变量,如图:

如果 Transport Type 选择 SSE 或 Streamable HTTP,URL中填入MCP服务器远程地址即可。

6.3.4 测试连接

连接成功后,可显示 MCP 服务对应的资源列表、工具列表等,并可对工具进行测试。

七、总结

MCP 拓展了 AI 大模型的能力边界,我们的人生亦如此,要不断去探索属于自己的MCP协议,以更好地与外部世界建立连接!

Logo

为武汉地区的开发者提供学习、交流和合作的平台。社区聚集了众多技术爱好者和专业人士,涵盖了多个领域,包括人工智能、大数据、云计算、区块链等。社区定期举办技术分享、培训和活动,为开发者提供更多的学习和交流机会。

更多推荐