
该文章已被 Model Context Protocol(MCP) 中文教程讲解 收录,欢迎 star 收藏。
📝 前言
随着 MCP
的快速普及和广泛应用,MCP
服务器也层出不穷。大多数开发者使用的 MCP
服务器开发库是官方提供的 typescript-sdk
,而作为 Go
开发者,我们也可以借助优秀的第三方库去开发 MCP
服务器,例如 ThinkInAIXYZ/go-mcp
。
本文将详细介绍如何在 Go
语言中使用 go-mcp
库来开发一个查询 IP
信息的 MCP
服务器。
准备好了吗?准备一杯你最喜欢的咖啡或茶,随着本文一探究竟吧。

🌐 mcp-ip-geo 服务器
mcp-ip-geo
是一个用于查询 IP
信息的 MCP
服务器,项目已开源,仓库地址:mcp-ip-geo。
📁 目录结构说明
csharp
├─cmd
│ └─mcp-ip-geo
└─internal
├─domain
├─server
├─service
└─tools
cmd/mcp-ip-geo
:应用的启动入口目录,包含如main.go
启动文件。internal/domain
:定义项目中的核心数据结构,例如IP API
的响应体等。internal/server
:MCP
服务器的核心逻辑实现。internal/service
:对接第三方服务的实现,如调用IP
解析API
。internal/tools
:MCP
工具的具体实现,支持灵活扩展和注册。
🔍 查询 IP 信息功能实现
代码位于 service
包中,通过 ip-api.com
提供的接口获取 IP
地理位置信息,具体实现如下:
go
package service
import (
"context"
"fmt"
"github.com/chenmingyong0423/mcp-ip-geo/internal/domain"
"net/http"
"time"
httpchain "github.com/chenmingyong0423/go-http-chain"
)
func NewIpApiService() *IpApiService {
return &IpApiService{
host: "http://ip-api.com",
client: httpchain.NewWithClient(&http.Client{
Timeout: time.Second * 10,
}),
}
}
type IIpApiService interface {
GetLocation(ctx context.Context, ip string) (*domain.IpApiResponse, error)
}
var _ IIpApiService = (*IpApiService)(nil)
type IpApiService struct {
host string
client *httpchain.Client
}
func (s *IpApiService) GetLocation(ctx context.Context, ip string) (*domain.IpApiResponse, error) {
var resp domain.IpApiResponse
err := s.client.Get(fmt.Sprintf("%s/json/%s", s.host, ip)).DoAndParse(ctx, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
代码解释:
-
服务初始化(
NewIpApiService
)- 创建一个新的
IpApiService
实例。 - 设置了
API
地址为http://ip-api.com
。 - 使用
httpchain
封装的HTTP
客户端,设置请求超时时间为 10 秒。
- 创建一个新的
-
接口定义(
IIpApiService
)- 定义了服务对外暴露的功能:
GetLocation
方法,用于获取IP
地理位置信息。 - 使用接口有助于后续做依赖注入、
mock
测试等。 var _ IIpApiService = (*IpApiService)(nil)
这行代码用于编译时检查,确保IpApiService
实现了IIpApiService
接口。
- 定义了服务对外暴露的功能:
-
结构体定义(
IpApiService
)- 包含两个字段:
host
:API
的基础地址。client
:封装的HTTP
客户端,类型为*httpchain.Client
。
-
核心方法实现(
GetLocation
)- 根据传入的
IP
构造请求地址:http://ip-api.com/json/{ip}
。 - 使用
httpchain
库发起GET
请求,并将结果解析到domain.IpApiResponse
结构体中。
- 根据传入的
🧰 工具实现
🔧 工具管理
代码位于 tools
包中,用于管理工具,具体实现如下:
go
package tools
import (
"github.com/ThinkInAIXYZ/go-mcp/protocol"
"github.com/ThinkInAIXYZ/go-mcp/server"
)
type ToolFunc func() (tool *protocol.Tool, toolHandler server.ToolHandlerFunc)
func GetToolFuncList() []ToolFunc {
return []ToolFunc{
SingleIpParser,
}
}
代码解释:
-
ToolFunc
类型定义-
定义了一个函数类型
ToolFunc
,返回两个值:*protocol.Tool
:工具的元信息;server.ToolHandlerFunc
:该工具的处理逻辑函数。
-
用这种方式可以将 工具的定义 与 工具的执行逻辑 一并管理,后续在定义工具时都可以通过实现该函数签名进行表示。
-
-
GetToolFuncList
函数- 返回一个
ToolFunc
列表。 - 当前只注册了一个工具:
SingleIpParser
,但这种结构易于扩展,后续只需往列表中添加新的工具函数即可。 - 通过集中注册,应用在初始化时可以统一加载所有工具。
- 返回一个
🌍 查询单个 IP 信息工具的实现
代码位于 tools
包中,用于查询单个 IP
信息,具体实现如下:
go
package tools
import (
"context"
"encoding/json"
"github.com/ThinkInAIXYZ/go-mcp/protocol"
"github.com/ThinkInAIXYZ/go-mcp/server"
"github.com/chenmingyong0423/mcp-ip-geo/internal/service"
)
var singleIpParserTool *protocol.Tool
type ipRequest struct {
Ip string `json:"ip"`
}
func init() {
var err error
singleIpParserTool, err = protocol.NewTool("ip-details", "a tool that provides IP geolocation information", ipRequest{})
if err != nil {
panic(err)
}
}
func SingleIpParser() (*protocol.Tool, server.ToolHandlerFunc) {
ipApiService := service.NewIpApiService()
return singleIpParserTool, func(toolRequest *protocol.CallToolRequest) (*protocol.CallToolResult, error) {
var req ipRequest
if err := protocol.VerifyAndUnmarshal(toolRequest.RawArguments, &req); err != nil {
return nil, err
}
resp, err := ipApiService.GetLocation(context.Background(), req.Ip)
if err != nil {
return nil, err
}
marshal, err := json.Marshal(resp)
if err != nil {
return nil, err
}
return &protocol.CallToolResult{
Content: []protocol.Content{
protocol.TextContent{
Type: "text",
Text: string(marshal),
},
},
}, nil
}
}
代码解释:
-
全局变量声明
singleIpParserTool
:存储工具元信息的协议工具对象ipRequest
:定义工具输入参数结构体,包含ip
字符串字段
-
初始化函数(
init
)- 在包加载时通过
protocol.NewTool
创建工具元信息 - 指定工具标识符
ip-details
,描述信息和输入参数结构体ipRequest{}
- 错误处理采用
panic
,确保工具元信息必须正确初始化
- 在包加载时通过
-
工具注册函数(
SingleIpParser
)-
创建
IpApiService
服务实例用于IP
定位查询 -
返回两个值:
- 预定义的
singleIpParserTool
元信息对象 - 工具处理函数
- 预定义的
-
-
工具处理函数
-
参数验证与解析
- 调用
protocol.VerifyAndUnmarshal
验证请求参数有效性 - 将原始参数反序列化到
ipRequest
结构体
- 调用
-
服务调用
- 使用
ipApiService.GetLocation
获取IP
地理位置信息
- 使用
-
结果处理
- 将服务响应结果序列化为
JSON
字符串并包装为protocol.CallToolResult
结构体返回
- 将服务响应结果序列化为
-
🚀 服务器的创建与启动
代码位于 server
包中,用于初始化服务并启动服务端,具体实现如下:
go
package server
import (
"github.com/ThinkInAIXYZ/go-mcp/server"
"github.com/ThinkInAIXYZ/go-mcp/transport"
"github.com/chenmingyong0423/mcp-ip-geo/internal/tools"
)
func Run(address string) error {
var err error
var ts transport.ServerTransport
if address == "" {
ts = transport.NewStdioServerTransport()
} else {
ts, err = transport.NewSSEServerTransport(address)
if err != nil {
return err
}
}
s, err := server.NewServer(ts)
if err != nil {
return err
}
toolFuncList := tools.GetToolFuncList()
for _, tool := range toolFuncList {
s.RegisterTool(tool())
}
return s.Run()
}
代码解释:
-
传输层初始化
-
根据
address
参数判断运行模式:- 空地址模式 :使用
NewStdioServerTransport
创建标准输入输出传输,适用于命令行工具等场景。 - 指定地址模式 :使用
NewSSEServerTransport
创建SSE
(Server-Sent Events
) 传输,适用于HTTP
长连接服务。
- 空地址模式 :使用
-
-
服务实例化
- 使用
server.NewServer
方法创建服务实例,注入配置好的传输层对象ts
。
- 使用
-
工具注册
-
调用
tools.GetToolFuncList
获取所有预定义的工具函数列表。 -
遍历工具列表,通过
s.RegisterTool(tool())
注册每个工具:tool()
执行后返回元信息*protocol.Tool
和处理函数ToolHandlerFunc
。
-
-
服务启动
- 调用
s.Run()
启动服务,开始监听请求。
- 调用
🧩 主程序入口实现
代码位于 main
包中,作为程序启动入口,具体实现如下:
go
package main
import (
"flag"
"github.com/chenmingyong0423/mcp-ip-geo/internal/server"
)
func main() {
addr := flag.String("address", "", "The host and port to run the sse server")
flag.Parse()
if err := server.Run(*addr); err != nil {
panic(err)
}
}
代码解释:
-
命令行参数解析
-
定义
address
参数:- 参数名称:
-address
- 默认值:空字符串
- 描述:指定
SSE
服务运行的地址和端口
- 参数名称:
-
调用
flag.Parse()
解析命令行参数
-
-
服务启动
- 调用
server.Run(*addr)
启动服务 - 将解析后的
address
参数值传递给服务启动函数
- 调用
✅ 小结
本文将详细介绍 mcp-ip-geo
------ 一个用于查询 IP
信息的 MCP
服务器的实现细节。该服务器目前支持两种数据传输方式:stdio
和 SSE(Server-Sent Events)
。未来还计划支持 Streamable HTTP
传输方式,并持续扩展更多实用的工具(tools
)模块。
你好,我是陈明勇,一名热爱技术、乐于分享的开发者,同时也是开源爱好者。
我专注于分享 Go
语言相关的技术知识,同时也会深入探讨 AI
领域的前沿技术。
成功的路上并不拥挤,有没有兴趣结个伴?
Go
开源库代表作 :go-mongox 、go-optioner。