Files
membank/aio-mcp/README.md
张威33321 1a6ba86f04 0908
2026-09-08 20:36:25 +08:00

157 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# aio-mcp
aio-system 的自定义 MCP 服务器Go。作为 AI Agent 的「手」,以 MCP 协议对外暴露工具;后续的 SQLite / 文件 / 打印 / 检测等自定义工具都往这里加。
基于官方 SDK[github.com/modelcontextprotocol/go-sdk](https://github.com/modelcontextprotocol/go-sdk)v1.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/` 并把稳定接口下沉过去。
## 运行
```bash
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/json``text/event-stream`,响应为 SSE`event: message` + `data: {...}`)。默认监听 `localhost:8000`
依赖拉取走内网代理,若需直连官方源:`go env -w GOPROXY=https://proxy.golang.org,direct`
## 新增一个工具
1. 在对应业务域子包(如 `internal/tools/sqlite/`)新建文件 `query.go`,用参数 struct + 泛型 `mcp.AddTool` 注册:
```go
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.go` 的 `RegisterAll` 中追加一行 `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`。
```bash
# 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 直接喂协议消息):
```bash
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 value`stdio 自检请用 bash/WSL 或直接用 MCP 客户端。
## 接入 Agent以 Reasonix / Claude 为例)
HTTP 模式:先启动服务(或交由 systemd / 容器守护),再在客户端 MCP 配置中指向地址:
```json
{ "mcpServers": { "aio-mcp": { "url": "http://localhost:8000/mcp" } } }
```
stdio 模式(客户端自行拉起进程):
```json
{ "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 _ - .`。