跳到主要内容

搭建一个 MCP 服务

什么是 MCP 服务

MCP(Model Context Protocol)是 Anthropic 推出的开放标准协议,用于连接 AI 助手与外部数据源和工具。通过 MCP,AI 模型可以安全地访问本地文件、数据库、API 等资源,大大扩展了 AI 的能力边界。

MCP 的核心优势

  • 标准化接口:统一的协议规范,便于集成
  • 安全可控:细粒度的权限管理
  • 可扩展性:支持自定义工具和资源

MCP 架构原理

MCP 采用客户端-服务端架构,通过标准化的消息协议进行通信:

通信流程

快速搭建文件系统 MCP 服务

我们来实现一个简单的文件系统 MCP 服务,支持文件读取和目录列举。

核心结构定义

type MCPServer struct {
rootPath string
allowedExts map[string]bool
}

type MCPRequest struct {
JsonRPC string `json:"jsonrpc"`
ID interface{} `json:"id"`
Method string `json:"method"`
Params interface{} `json:"params,omitempty"`
}

type MCPResponse struct {
JsonRPC string `json:"jsonrpc"`
ID interface{} `json:"id"`
Result interface{} `json:"result,omitempty"`
Error *MCPError `json:"error,omitempty"`
}

资源管理实现

func (s *MCPServer) handleListResources() interface{} {
var resources []Resource

err := filepath.Walk(s.rootPath, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return nil
}

ext := filepath.Ext(path)
if s.allowedExts[ext] {
relPath, _ := filepath.Rel(s.rootPath, path)
resources = append(resources, Resource{
URI: "file://" + relPath,
Name: info.Name(),
Description: fmt.Sprintf("文件大小: %d bytes", info.Size()),
MimeType: getMimeType(ext),
})
}
return nil
})

if err != nil {
return map[string]interface{}{"resources": []Resource{}}
}

return map[string]interface{}{"resources": resources}
}

工具调用处理

func (s *MCPServer) handleCallTool(params map[string]interface{}) interface{} {
toolName, _ := params["name"].(string)
arguments, _ := params["arguments"].(map[string]interface{})

switch toolName {
case "read_file":
return s.readFile(arguments)
case "search_files":
return s.searchFiles(arguments)
default:
return &MCPError{
Code: -32601,
Message: "未知工具: " + toolName,
}
}
}

func (s *MCPServer) readFile(args map[string]interface{}) interface{} {
filePath, _ := args["path"].(string)
fullPath := filepath.Join(s.rootPath, filePath)

// 安全检查:防止路径遍历攻击
if !strings.HasPrefix(fullPath, s.rootPath) {
return &MCPError{Code: -32602, Message: "无效的文件路径"}
}

content, err := os.ReadFile(fullPath)
if err != nil {
return &MCPError{Code: -32603, Message: "读取文件失败: " + err.Error()}
}

return map[string]interface{}{
"content": []map[string]interface{}{
{
"type": "text",
"text": string(content),
},
},
}
}

实际应用场景

数据库查询服务

构建一个 MCP 服务连接数据库,让 AI 助手能够查询和分析数据:

type DatabaseMCP struct {
db *sql.DB
}

func (d *DatabaseMCP) handleSQLQuery(query string) interface{} {
// 安全检查:只允许 SELECT 查询
if !strings.HasPrefix(strings.ToUpper(strings.TrimSpace(query)), "SELECT") {
return &MCPError{Code: -32602, Message: "只允许 SELECT 查询"}
}

rows, err := d.db.Query(query)
if err != nil {
return &MCPError{Code: -32603, Message: err.Error()}
}
defer rows.Close()

// 转换查询结果为结构化数据
return convertRowsToJSON(rows)
}

API 集成服务

将第三方 API 封装为 MCP 工具,统一管理外部服务调用:

func (s *APIServer) callWeatherAPI(args map[string]interface{}) interface{} {
city, _ := args["city"].(string)

resp, err := http.Get(fmt.Sprintf("https://api.weather.com/v1/current?city=%s&key=%s",
city, s.apiKey))
if err != nil {
return &MCPError{Code: -32603, Message: "API 调用失败"}
}
defer resp.Body.Close()

var weatherData map[string]interface{}
json.NewDecoder(resp.Body).Decode(&weatherData)

return map[string]interface{}{
"content": []map[string]interface{}{
{
"type": "text",
"text": fmt.Sprintf("城市 %s 的天气: %v", city, weatherData),
},
},
}
}

部署和配置

服务启动配置

func main() {
server := &MCPServer{
rootPath: "/home/user/documents",
allowedExts: map[string]bool{".txt": true, ".md": true, ".json": true},
}

// 配置 stdio 通信
scanner := bufio.NewScanner(os.Stdin)
encoder := json.NewEncoder(os.Stdout)

for scanner.Scan() {
var request MCPRequest
if err := json.Unmarshal(scanner.Bytes(), &request); err != nil {
continue
}

response := server.handleRequest(&request)
encoder.Encode(response)
}
}

Claude Desktop 集成

claude_desktop_config.json 中配置 MCP 服务:

{
"mcpServers": {
"filesystem": {
"command": "/path/to/your/mcp-server",
"args": ["-root", "/home/user/documents"],
"env": {
"LOG_LEVEL": "info"
}
}
}
}

最佳实践和注意事项

安全考虑

  1. 路径验证:防止目录遍历攻击
  2. 权限控制:限制可访问的文件类型和目录
  3. 输入验证:严格验证所有用户输入

性能优化

// 使用连接池管理数据库连接
type PooledMCP struct {
pool *sql.DB
cache map[string]interface{}
mutex sync.RWMutex
}

func (p *PooledMCP) getCachedResult(key string) (interface{}, bool) {
p.mutex.RLock()
defer p.mutex.RUnlock()

result, exists := p.cache[key]
return result, exists
}

错误处理策略

通过 MCP 服务,我们可以让 AI 助手安全地访问本地资源,实现更强大的自动化能力。关键是要在功能性和安全性之间找到平衡,确保服务既能满足需求又不会带来安全风险。