MCP 原理初探及基于go语言开发MCP服务的实现
本文系统介绍了 MCP(Model Context Protocol)协议的核心概念、通信机制与关键组件,并结合 Go 语言给出具体的 MCP 服务实现示例。此外,还详细讲解了调试工具 MCP Inspector 的基本使用方法。
一、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-server: https://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协议,以更好地与外部世界建立连接!

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