Files
张威33321 1a6ba86f04 0908
2026-09-08 20:36:25 +08:00
..
2026-09-08 20:36:25 +08:00
2026-09-08 20:36:25 +08:00
2026-08-27 20:38:13 +08:00
2026-08-27 20:38:13 +08:00
2026-08-27 20:38:13 +08:00
2026-09-08 20:36:25 +08:00
2026-09-08 19:10:54 +08:00

aio-mcp

aio-system 的自定义 MCP 服务器Go。作为 AI Agent 的「手」,以 MCP 协议对外暴露工具;后续的 SQLite / 文件 / 打印 / 检测等自定义工具都往这里加。

基于官方 SDKgithub.com/modelcontextprotocol/go-sdkv1.7.0)。

目录结构

aio-mcp/
├── cmd/
│   └── aio-mcp/
│       └── main.go            # 入口:解析配置 → 初始化日志 → 装配并启动服务器
└── internal/
    ├── config/
    │   └── config.go          # 启动配置transport / addr / log-level / data-dir
    ├── logging/
    │   └── logging.go         # 日志初始化(固定输出 stderr详见注意事项
    ├── deps/
    │   └── deps.go            # 工具共享依赖容器(数据目录,后续 SQLite 连接等)
    ├── server/
    │   ├── server.go          # 服务器装配:创建实例 + 注册工具 + 注入依赖
    │   └── serve.go           # 传输层stdio / streamable HTTP 的启动逻辑
    └── tools/
        ├── registry.go        # 工具注册表:按业务域分组,统一 RegisterAll
        └── system/            # 工具组:系统类
            ├── system.go      # 组内注册入口
            ├── ping.go        # 健康检查(无参数)
            └── echo.go        # 参数传递演示(有参数)

分层约定:

  • cmd/aio-mcp:只有一个可执行文件,所有编排逻辑放在 internal/ 下,main.go 保持极薄。
  • internal/config / internal/logging:与 MCP 无关的横切能力,独立成包便于加测试。
  • internal/deps共享依赖不放在 internal/tools——工具子包要 import 它,若放在 tools 会与 registry.go 形成循环依赖。
  • internal/tools/<组>:一个业务域一个子包,组内工具各自一个文件;注册入口统一叫 Register(s *mcp.Server, d *deps.Deps)
  • 暂无 pkg/:当前没有需要对外复用的公共 API。若将来要被 membank 其他 Go 项目(如 auto-check直接 import再新增 pkg/ 并把稳定接口下沉过去。

运行

go build -o aio-mcp.exe ./cmd/aio-mcp    # 构建

# http 模式默认streamable HTTP远程客户端访问
aio-mcp.exe
aio-mcp.exe -transport http -addr localhost:8000   # 同上,显式指定监听地址

# stdio 模式(本地 Agent 直接拉起进程时使用)
aio-mcp.exe -transport stdio

# 其他参数
aio-mcp.exe -log-level debug -data-dir D:\data\aio-mcp

服务以 streamable HTTP 为主要对外方式POST /mcp,请求头需同时接受 application/jsontext/event-stream,响应为 SSEevent: message + data: {...})。默认监听 localhost:8000

依赖拉取走内网代理,若需直连官方源:go env -w GOPROXY=https://proxy.golang.org,direct

新增一个工具

  1. 在对应业务域子包(如 internal/tools/sqlite/)新建文件 query.go,用参数 struct + 泛型 mcp.AddTool 注册:

    package sqlite
    
    import (
        "context"
        "github.com/modelcontextprotocol/go-sdk/mcp"
    )
    
    type queryArgs struct {
        SQL string `json:"sql" jsonschema:"要执行的 SELECT 语句,必填"`
    }
    
    func registerQuery(s *mcp.Server, d *deps.Deps) {
        mcp.AddTool(s, &mcp.Tool{
            Name:        "sqlite_query",
            Description: "在指定库上执行只读 SQL 查询。",
        }, func(_ context.Context, _ *mcp.CallToolRequest, args queryArgs) (*mcp.CallToolResult, any, error) {
            // ...业务逻辑,用到共享依赖时取 d.DataDir / d.DB...
            return &mcp.CallToolResult{
                Content: []mcp.Content{&mcp.TextContent{Text: "..."}},
            }, nil, nil
        })
    }
    
  2. 在该组的 Register(如 sqlite/sqlite.go)中追加一行:registerQuery(s, d)

  3. 若是一个新的业务域,再在 internal/tools/registry.goRegisterAll 中追加一行 sqlite.Register(s, d)

工具需要共享状态SQLite 连接、HTTP client 等)时,在 internal/deps/deps.go 加字段并在 deps.New 中构造,各工具从 *deps.Deps 取用,不要自建。

冒烟测试

HTTP 模式(推荐):initialize 响应头里的 Mcp-Session-Id 要带回后续请求, 否则会报 method "tools/list" is invalid during session initialization

# 1) 初始化,取会话 ID
curl -s -i -X POST http://localhost:8000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0.0.1"}}}'
# => Mcp-Session-Id: xxxxx ;响应体为 SSEevent: message / data: {...}

SID=<上一步的会话 ID>
A="Content-Type: application/json"; B="Accept: application/json, text/event-stream"

# 2) 通知已初始化(返回 202
curl -s -X POST http://localhost:8000/mcp -H "$A" -H "$B" -H "Mcp-Session-Id: $SID" \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'

# 3) 列工具
curl -s -X POST http://localhost:8000/mcp -H "$A" -H "$B" -H "Mcp-Session-Id: $SID" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

# 4) 调工具
curl -s -X POST http://localhost:8000/mcp -H "$A" -H "$B" -H "Mcp-Session-Id: $SID" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"echo","arguments":{"message":"你好","times":2}}}'

stdio 模式bash/WSL 下用换行分隔的 JSON-RPC 直接喂协议消息):

printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"test","version":"0.0.1"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
'{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"echo","arguments":{"message":"你好","times":2}}}' \
| aio-mcp.exe -transport stdio

注意Windows PowerShell 的管道会给字节流加 BOM/转码,导致服务端报 invalid character 'ï' looking for beginning of valuestdio 自检请用 bash/WSL 或直接用 MCP 客户端。

接入 Agent以 Reasonix / Claude 为例)

HTTP 模式:先启动服务(或交由 systemd / 容器守护),再在客户端 MCP 配置中指向地址:

{ "mcpServers": { "aio-mcp": { "url": "http://localhost:8000/mcp" } } }

stdio 模式(客户端自行拉起进程):

{ "mcpServers": { "aio-mcp": { "command": "D:\\workspace\\membank\\aio-mcp\\aio-mcp.exe", "args": ["-transport", "stdio"] } } }

注意事项

  • stdio 模式下 stdout 是协议通道:日志已固定输出到 stderr业务代码请勿往 stdout 打印。
  • HTTP 模式下客户端必须带 Accept: application/json, text/event-stream,否则服务端返回 400。
  • 工具名只允许 a-z A-Z 0-9 _ - .