首页 > 教程攻略 > ai资讯 >使用Go开发MCP Server, 太简单了!

使用Go开发MCP Server, 太简单了!

来源:互联网 时间:2026-06-23 13:56:36

快速掌握Go语言开发MCP Server,从入门到实践。

核心内容:

  • Go生态中MCP库的选择与比较
  • 使用mark3labs/mcp-go库快速搭建MCP服务
  • 资源与工具的添加,实现数据暴露与LLM执行

使用Go开发MCP Server, 太简单了!

好,不绕弯子,假设你对MCP(Model Context Protocol)的背景已经心里有数。目前Go生态里有两款比较知名的MCP开发库:一个是mark3labs/mcp-go[1],另一个是metoro-io/mcp-golang[2]。两个用起来都很顺手,这次咱们按star数量选第一个来做演示。

mcp-go 入门

mark3labs/mcp-go这个库提供了丰富的示例代码,而且README里已经把每种MCP Tool类型的写法都列出来了,算是很贴心的入门指南。

启动服务

MCP支持SSE和标准输入输出两种通信方式。平时在自己机器上开发测试,标准输入输出(stdio)是最常见的用法:

// Create a basic server
s := server.NewMCPServer(
    "My Server",  // Server name
    "1.0.0",     // Version
)

// Start the server using stdio
if err := server.ServeStdio(s); err != nil {
    log.Fatalf("Server error: %v", err)
}

几行代码就能跑起一个MCP服务,没什么复杂的。

增加资源

资源是你向LLM暴露数据的方式。它们可以来自任何地方:文件、API响应、数据库查询、系统信息等等。资源分两种:

  • 静态资源(固定URI)
  • 动态资源(使用URI模板)

下面是一个静态资源的例子——暴露一个README文件:

// Static resource example - exposing a README file
resource := mcp.NewResource(
    "docs://readme",
    "Project README",
    mcp.WithResourceDescription("The project's README file"),
    mcp.WithMIMEType("text/markdown"),
)

// Add resource with its handler
s.AddResource(resource, func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
    content, err := os.ReadFile("README.md")
    if err != nil {
        return nil, err
    }

    return []mcp.ResourceContents{
        mcp.TextResourceContents{
            URI:      "docs://readme",
            MIMEType: "text/markdown",
            Text:     string(content),
        },
    }, nil
})

看到没?定义资源的时候指定一个URI和MIME类型,再写一个handler读取真实数据,一个数据暴露通道就打通了。

增加工具

工具让LLM通过你的服务器执行操作。和资源不同,工具预期会进行实际的运算并产生副作用——你可以把它想象成REST API里的POST端点。下面是一个简单的算术工具:

calculatorTool := mcp.NewTool("calculate",
    mcp.WithDescription("Perform basic arithmetic calculations"),
    mcp.WithString("operation",
        mcp.Required(),
        mcp.Description("The arithmetic operation to perform"),
        mcp.Enum("add", "subtract", "multiply", "divide"),
    ),
    mcp.WithNumber("x",
        mcp.Required(),
        mcp.Description("First number"),
    ),
    mcp.WithNumber("y",
        mcp.Required(),
        mcp.Description("Second number"),
    ),
)

s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    op := request.Params.Arguments["operation"].(string)
    x := request.Params.Arguments["x"].(float64)
    y := request.Params.Arguments["y"].(float64)

    var result float64
    switch op {
    case "add":
        result = x + y
    case "subtract":
        result = x - y
    case "multiply":
        result = x * y
    case "divide":
        if y == 0 {
            return nil, errors.New("Division by zero is not allowed")
        }
        result = x / y
    }

    return mcp.FormatNumberResult(result), nil
})

工具的用途远不止计算,它可以做很多事情:

  • 数据库查询
  • 文件操作
  • 外部API调用
  • 计算
  • 系统操作

每个工具设计时都应该注意:

  • 有清晰的描述
  • 验证输入
  • 优雅处理错误
  • 返回结构化的响应
  • 使用适当的结果类型

增加提示

提示(Prompt)是预定义的模板,LLM会按模板和用户进行交互。下面是一个问候提示的例子,它需要一个名字参数,然后返回一段问候语:

// Simple greeting prompt
s.AddPrompt(mcp.NewPrompt("greeting",
    mcp.WithPromptDescription("A friendly greeting prompt"),
    mcp.WithArgument("name",
        mcp.ArgumentDescription("Name of the person to greet"),
    ),
), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
    name := request.Params.Arguments["name"]
    if name == "" {
        name = "friend"
    }

    return mcp.NewGetPromptResult(
        "A friendly greeting",
        []mcp.PromptMessage{
            mcp.NewPromptMessage(
                mcp.RoleAssistant,
                mcp.NewTextContent(fmt.Sprintf("Hello, %s! How can I help you today?", name)),
            ),
        },
    ), nil
})

实战:实现一个查询IP地理信息的MCP Tool

理论知识够用了,来一个真正能用的东西——查询IP地址地理位置的MCP工具。

先写出main函数的主逻辑:

package main

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

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

func main() {
	// Create MCP server
	s := server.NewMCPServer(
		"ip-mcp",
		"1.0.0",
	)

	// Add tool
	tool := mcp.NewTool("ip_query",
		mcp.WithDescription("query geo location of an IP address"),
		mcp.WithString("ip",
			mcp.Required(),
			mcp.Description("IP address to query"),
		),
	)

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

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

这里我们创建了一个名为ip_query的工具,描述很清楚:查询IP地址的地理位置。它需要一个参数:待查询的IP地址。具体逻辑交给ipQueryHandler函数去实现。通信方式选的是标准输入输出,本地测试最方便。

接下来是ipQueryHandler的实现。之前我搭过一个IP地址查询网站 https://ip.rpcx.io,直接调用它的API,封装起来非常简单。

请求到的数据(JSON格式)直接以文本方式返回给MCP Client。Client拿到的是JSON字符串,自己解析就好。

func ipQueryHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	ip, ok := request.Params.Arguments["ip"].(string)
	if !ok {
		return nil, errors.New("ip must be a string")
	}

	parsedIP := net.ParseIP(ip)
	if parsedIP == nil {
		golog.Errorf("invalid IP address: %s", ip)
		return nil, errors.New("invalid IP address")
	}

	resp, err := http.Get("https://ip.rpcx.io/api/ip?ip=" + ip)
	if err != nil {
		golog.Errorf("Error fetching IP information: %v", err)
		return nil, fmt.Errorf("Error fetching IP information: %v", err)
	}
	defer resp.Body.Close()

	data, err := io.ReadAll(resp.Body)
	if err != nil {
		golog.Errorf("Error reading response body: %v", err)
		return nil, fmt.Errorf("Error reading response body: %v", err)
	}

	return mcp.NewToolResultText(string(data)), nil
}

注意参数合法性检查和错误处理这些常规操作。2025年了,这类琐事交给GitHub Copilot这类工具自动补全,一路回车就行。

编译成可执行二进制文件:

go build -o ip-mcp

你可以把它放在系统目录里方便调用,也可以不放在系统目录——后面配置MCP Server时需要指定它的路径。

测试MCP Server

测试工具我们选用deepchat[3],它已经原生支持MCP,配置起来极其简单。

在MCP配置界面,新增一个MCP Server,配置如下(注意把ip-mcp工具的路径换成你本地的实际路径):

然后启动它:

此时在对话框窗口应该能看到运行着的MCP Tool列表,我们刚写的IP查询工具就在其中:

直接打一句“查询一下8.8.8.8的地理位置”试试——

测试正常。而且看起来deepchat还会把工具的返回结果自动交给deepseek进一步处理,体验很流畅。

就这样,一个简单的MCP Server实现了,并且跑通了全流程。可以看到,我们可以非常快速地把已有的API或产品封装成MCP工具——这也是百度地图、高德地图能迅速推出MCP Server的原因。

相关下载