什么是MCP Server
理解 MCP 服务器(Model Context Protocol Server)的关键在于其旨在解决大型语言模型(LLM)与外部系统(如数据库、API、工具等)之间交互的“M×N 集成问题”。简单来说,它就像一个“AI 的 USB 接口”,为 AI 应用提供了一个标准化、安全且可扩展的方式来访问和利用外部世界的信息和能力。
核心原理
MCP 服务器的核心原理可以概括为以下几点:
标准化协议 (Standardized Protocol):
- MCP 定义了一套通用的通信协议,通常基于 JSON-RPC 2.0,用于 AI 应用(客户端)和 MCP 服务器之间进行消息交换。
- 这套协议规定了消息的格式(请求、响应、通知、错误)、数据类型以及通信流程,确保不同厂商的 AI 应用和 MCP 服务器之间可以无缝协作。
- 它类似于 HTTP 协议之于 Web 浏览器,提供了一个通用的语言让 AI 系统能理解和交互。
客户端-服务器架构 (Client-Server Architecture):
- 宿主 (Host): 通常是用户直接交互的 AI 应用,例如 Claude Desktop、AI 驱动的 IDE (如 Cursor, VS Code Copilot) 等。宿主内部包含 MCP 客户端。
- 客户端 (Client): 维护与 MCP 服务器的 1:1 连接,负责将宿主的请求通过 MCP 协议发送给服务器,并将服务器的响应返回给宿主。
- 服务器 (Server): 是轻量级的程序,专注于暴露特定的能力。它可以是本地进程,也可以是远程服务。每个服务器提供一组“工具 (Tools)”、“资源 (Resources)”和“提示 (Prompts)”。
- 工具 (Tools): 是 AI 模型可以调用的函数或操作,例如查询数据库、发送消息、执行网页抓取等。它们是 AI 执行特定任务的关键。
- 资源 (Resources): 是 AI 模型可以访问的数据源,类似于 REST API 中的 GET 端点,提供数据但通常不执行复杂的计算。
- 提示 (Prompts): 是预定义的指令或模板,旨在指导 AI 模型以最优方式使用工具或资源。
动态工具发现和执行 (Dynamic Tool Discovery and Execution):
- MCP 允许 AI 模型在运行时动态地发现和利用可用的工具。而不是预先硬编码一套功能,AI 可以查询 MCP 服务器来获取当前可用的工具列表,并根据当前上下文决定如何使用它们。
- 当 AI 应用需要某个功能时,它会连接到相应的 MCP 服务器,服务器会向客户端暴露其支持的工具、资源和提示。AI 模型可以根据自己的判断,通过 MCP 协议调用这些工具来完成任务。
上下文管理 (Context Management):
- MCP 允许将数据分解成可管理的部分,这有助于提高 AI 处理的效率。
- 服务器只接收必要的上下文信息,完整的对话历史通常保留在宿主端,以确保数据隔离和安全性。
安全性与访问控制 (Security and Access Control):
- 安全性是 MCP 的核心设计原则。宿主(AI 模型所在的地方)控制客户端连接的权限,允许用户和组织严格管理 AI 助手可以访问的内容。
- MCP 服务器通常需要遵循最小权限原则,确保服务器只能访问其合法需要的特定数据和功能,从而限制潜在安全漏洞的影响。
- 例如,敏感操作(如修改操作系统状态、数据和凭据访问)通常需要用户的明确批准。
双向通信 (Two-Way Communication):
- MCP 支持双向通信。这意味着 AI 模型不仅可以接收信息,还可以触发外部系统中的操作,从而实现更动态和交互式的应用程序。
可组合性和可扩展性 (Composability and Extensibility):
- MCP 服务器被设计为高度可组合。每个服务器提供隔离的、专注的功能。
- 多个服务器可以无缝组合,共享协议确保了互操作性。这种模块化设计支持未来的可扩展性,并且可以独立地更新、测试和扩展工具和资源。
- MCP 协议是相对复杂的,完整实现需要处理 JSON-RPC 2.0 的请求/响应、错误处理、通知、会话管理以及工具/资源/提示的注册和调用。
- 本示例是一个非常简化的版本,仅用于演示基本概念:如何创建一个监听器,接收 Cursor 的连接请求,并返回一个基本的“Hello World”工具。它不包含完整的 MCP 协议实现,例如工具调用、上下文管理等。
- Cursor 的 MCP 集成功能还在不断发展中。 本文提供的是基于现有文档和社区实践的指导,未来可能会有变化。
Golang MCP Server 示例 (简化版)
这个示例将创建一个简单的 HTTP 服务器,模拟 MCP 服务器,它会暴露一个名为 myTool.sayHello
的虚拟工具。当 Cursor 尝试连接时,我们会返回一些预设的工具定义。
mcp_server.go
文件:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
// MCP 响应结构 (简化)
type MCPResponse struct {
Jsonrpc string `json:"jsonrpc"`
ID *json.RawMessage `json:"id,omitempty"` // ID 可以是字符串或数字
Result interface{} `json:"result,omitempty"`
Error *MCPError `json:"error,omitempty"`
}
// MCP 错误结构 (简化)
type MCPError struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// MCP 请求结构 (简化)
type MCPRequest struct {
Jsonrpc string `json:"jsonrpc"`
ID *json.RawMessage `json:"id,omitempty"` // ID 可以是字符串或数字
Method string `json:"method"`
Params json.RawMessage `json:"params,omitempty"`
}
// 工具定义结构 (简化)
type Tool struct {
Name string `json:"name"`
Description string `json:"description"`
// 更完整的 MCP 工具定义还包括 'parameters' (JSON Schema), 'return' 等
}
// 资源定义结构 (简化)
type Resource struct {
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"` // "json" or "text" or "markdown" etc.
// 更完整的 MCP 资源定义还包括 'schema'
}
// 提示定义结构 (简化)
type Prompt struct {
Name string `json:"name"`
Description string `json:"description"`
Content string `json:"content"`
// 更完整的 MCP 提示定义还包括 'context'
}
// 用于响应 'host.initialize' 方法的结构
type InitializeResult struct {
Tools []Tool `json:"tools"`
Resources []Resource `json:"resources"`
Prompts []Prompt `json:"prompts"`
}
func main() {
http.HandleFunc("/", mcpHandler)
port := ":8080" // 监听端口
fmt.Printf("MCP Server listening on http://localhost%s\n", port)
log.Fatal(http.ListenAndServe(port, nil))
}
func mcpHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
return
}
var req MCPRequest
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&req); err != nil {
sendError(w, r, -32700, "Parse error", nil) // JSON-RPC parse error
return
}
fmt.Printf("Received MCP request: Method=%s, ID=%s\n", req.Method, string(*req.ID))
// 模拟处理 MCP 请求
var resp MCPResponse
resp.Jsonrpc = "2.0"
resp.ID = req.ID // 响应的 ID 必须与请求的 ID 相同
switch req.Method {
case "host.initialize":
// 当 Cursor 连接时,它会发送 host.initialize 请求来获取可用的工具、资源和提示
result := InitializeResult{
Tools: []Tool{
{
Name: "myTool.sayHello",
Description: "Says hello to the given name.",
// 完整的工具定义会有 parameters, return type 等
},
},
Resources: []Resource{
{
Name: "myResource.currentTime",
Description: "Returns the current time.",
Type: "text",
},
},
Prompts: []Prompt{
{
Name: "myPrompt.greeting",
Description: "A prompt for greetings.",
Content: "Hello, how can I assist you today?",
},
},
}
resp.Result = result
case "myTool.sayHello":
// 模拟工具调用,假设工具参数是 { "name": "World" }
var params map[string]interface{}
json.Unmarshal(req.Params, ¶ms)
name, ok := params["name"].(string)
if !ok {
name = "World"
}
resp.Result = fmt.Sprintf("Hello, %s from Go MCP Server!", name)
default:
// 未知方法
sendError(w, r, -32601, "Method not found", fmt.Sprintf("Method '%s' is not supported.", req.Method))
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Error sending response: %v\n", err)
}
}
// 辅助函数:发送 JSON-RPC 错误响应
func sendError(w http.ResponseWriter, r *http.Request, code int, message string, data interface{}) {
resp := MCPResponse{
Jsonrpc: "2.0",
Error: &MCPError{
Code: code,
Message: message,
Data: data,
},
}
// 如果请求有 ID,响应也应包含 ID
var req MCPRequest
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&req); err == nil {
resp.ID = req.ID
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) // JSON-RPC 错误通常返回 200 OK
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Error sending error response: %v\n", err)
}
}
运行 MCP Server
- 将上述代码保存为
mcp_server.go
。 - 打开终端,导航到该文件所在目录。
- 运行命令:
go run mcp_server.go
- 你会看到输出:
MCP Server listening on http://localhost:8080
,表示服务器已成功启动。
如何介入 Cursor
Cursor 通过其“Tools”或“MCP”功能来连接外部 MCP 服务器。以下是操作步骤:
打开 Cursor 设置:
- 在 Cursor 中,按下
Ctrl + ,
(Windows/Linux) 或Cmd + ,
(macOS) 打开设置。 - 或者通过菜单栏
File
->Settings
->Settings
。
- 在 Cursor 中,按下
搜索 "MCP" 或 "Tools":
- 在设置搜索框中输入 "MCP" 或 "Tools"。
- 找到一个类似于 "Cursor > Tools: Enable MCP" 或 "Cursor: MCP Server Endpoints" 的选项。
配置 MCP Server Endpoint:
- Cursor 通常提供一个 JSON 数组来配置 MCP 服务器的端点。
- 找到一个名为
cursor.mcpServerEndpoints
或类似名称的设置项。 - 点击 "Edit in settings.json" 或 "Add Item"。
添加你的服务器地址:
- 将你的 Golang MCP 服务器地址添加到这个 JSON 数组中。对于我们上面的例子,地址是
http://localhost:8080
。
你的
settings.json
文件中cursor.mcpServerEndpoints
部分可能看起来像这样:{ // ... 其他设置 ... "cursor.mcpServerEndpoints": [ "http://localhost:8080" ] // ... 其他设置 ... }
- 将你的 Golang MCP 服务器地址添加到这个 JSON 数组中。对于我们上面的例子,地址是
重启 Cursor:
- 保存
settings.json
文件。 - 重要:重启 Cursor 以使设置生效,并让 Cursor 尝试连接你的 MCP 服务器。
- 保存
验证连接:
当 Cursor 启动并尝试连接时,你会在运行 Go 服务器的终端中看到类似
Received MCP request: Method=host.initialize, ID="1"
的输出。这表明 Cursor 正在尝试发现你的工具。在 Cursor 的 AI Chat 面板中,尝试询问关于你的工具的问题。例如:
- "What tools do I have?"
- "Can you say hello?"
- "Use the
myTool.sayHello
tool."
预期行为: 如果一切顺利,Cursor 应该能识别出你服务器提供的
myTool.sayHello
工具,并可能尝试调用它。由于我们服务器的myTool.sayHello
方法只是一个简单的字符串返回,你可能在 Cursor 的回复中看到类似 "Hello, World from Go MCP Server!" 的内容(如果它尝试了工具调用)。
深入理解与局限性
- JSON-RPC 2.0: MCP 协议通常基于 JSON-RPC 2.0 规范。这意味着请求和响应都遵循特定的 JSON 格式,包含
jsonrpc
版本、method
、params
、id
等字段。我们的示例已经包含了这些基本字段。 - 工具调用: 一个完整的 MCP 服务器需要能够接收工具调用请求(即
method
对应你的工具名称),并根据params
执行相应的逻辑。在我们的简化示例中,myTool.sayHello
只是简单地返回一个字符串,没有真正的逻辑处理。 - 异步操作: 真实的工具可能需要耗时操作,MCP 协议也支持异步处理(例如,通过返回一个
TaskID
,然后客户端可以通过另一个方法查询任务状态)。 - 上下文和数据流: MCP 允许 AI 模型获取外部资源(例如,
myResource.currentTime
)。一个完整的实现需要根据 AI 的请求返回实际的数据。 - 安全性: 在生产环境中,你需要考虑认证、授权、传输加密 (HTTPS) 等安全措施。
- 错误处理: 强大的错误处理机制对于健壮的 MCP 服务器至关重要,需要遵循 JSON-RPC 2.0 的错误码规范。
为什么这个示例是简化的?
MCP 协议的设计目标是让 AI 模型能够与外部系统进行复杂的交互,这涉及到:
- 详细的工具定义: 包括工具的参数 (
parameters
,通常用 JSON Schema 定义)、返回值 (return
)、副作用等。 - 资源的数据格式: 资源的
type
(如json
,text
,markdown
) 以及其schema
。 - 提示的上下文: 提示可以包含动态内容,AI 需要知道如何填充。
- 流式输出和事件: 对于长时间运行的操作,MCP 可能支持流式输出或事件通知。
- 会话管理: 保持客户端和服务器之间的状态。
尽管如此,这个 Golang 示例为你提供了一个起点,展示了如何构建一个最基本的 MCP 服务器,并使其能够与 Cursor 进行初步的通信。要构建一个功能完备的 MCP 服务器,你将需要深入研究 MCP 协议的详细规范,并根据你的具体需求实现复杂的逻辑。