Skip to content

Commit 36f77cc

Browse files
committed
init alapi.cn mcp
0 parents  commit 36f77cc

File tree

13 files changed

+8642
-0
lines changed

13 files changed

+8642
-0
lines changed

.cursor/rules/mcp.mdc

Lines changed: 8145 additions & 0 deletions
Large diffs are not rendered by default.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
*.exe
3+
build
4+
.vs_code

README.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# ALAPI MCP Server
2+
3+
这是一个基于 [ALAPI](https://www.alapi.cn) 的 MCP (Model Control Protocol) 服务器实现,可以自动将 ALAPI 的 OpenAPI 规范转换为 MCP 工具。
4+
5+
## 功能特点
6+
7+
- 自动加载 ALAPI OpenAPI 规范
8+
- 支持加载全部或指定 API (短视频解析、天气查询、每天60秒读懂世界、企业查询...)
9+
- 符合 MCP 协议标准
10+
- 统一的错误处理和响应格式
11+
- 支持环境变量配置
12+
13+
## 环境要求
14+
15+
- Go 1.24.1 或更高版本
16+
- 有效的 ALAPI Token
17+
- 支持 MCP 的客户端(如 Claude Desktop、Continue、Cursor 等)
18+
19+
## 安装
20+
21+
### 构建安装
22+
```bash
23+
#Github
24+
git clone https://github.com/alapi-sdk/mcp-alapi-cn.git
25+
26+
#CNB 国内加速地址
27+
#git clone https://cnb.cool/alapi/mcp-alapi-cn.git
28+
29+
cd mcp-server
30+
go mod tidy
31+
go build
32+
```
33+
34+
### 下载已构建的软件包
35+
36+
Github:
37+
38+
CNB(国内加速):
39+
40+
41+
42+
## 配置和使用
43+
44+
### 环境变量配置
45+
46+
项目使用以下环境变量:
47+
48+
- `ALAPI_TOKEN`(必需):ALAPI 的认证令牌,在 [token管理](https://www.alapi.cn/dashboard/data/token) 里面创建, **如果不设置,mcp 会启动不了**
49+
- `ALAPI_ID`(可选):指定要加载的 API ID,不设置则加载所有 API(实际的API_id 可通过 [ALAPI](https://www.alapi.cn) 官网查看,可在我的API里面查询)
50+
51+
52+
53+
54+
### Cursor配置方式
55+
56+
1. 打开Cursor设置 > 扩展 > MCP工具
57+
2. 添加新的MCP工具
58+
3. 按照以下格式填写配置:
59+
60+
```json
61+
{
62+
"mcpServers": {
63+
"mcp-alapi-cn": {
64+
"command": "C:\\Users\\Administrator\\实际目录\\mcp-alapi-cn.exe",
65+
"env": {
66+
"ALAPI_TOKEN": "xxxx",
67+
"ALAPI_API_ID": "0"
68+
}
69+
}
70+
}
71+
}
72+
```
73+
74+
### CherryStudio配置方式
75+
1. 打开 CherryStudio 设置 -> MCP 服务器
76+
2. 添加MCP服务器
77+
3. 配置说明:
78+
79+
名称: `MCP-ALAPI-CN`
80+
类型: `STDIO`
81+
命令: `C:\\Users\\Administrator\\实际目录\\mcp-alapi-cn.exe`
82+
环境变量:
83+
```
84+
ALAPI_TOKEN=你的token
85+
```

build.bat

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
@echo off
2+
setlocal
3+
4+
set VERSION=1.0.0
5+
set BUILD_DIR=build
6+
7+
:: 创建构建目录
8+
if not exist %BUILD_DIR% mkdir %BUILD_DIR%
9+
10+
:: 清理旧的构建文件
11+
echo Cleaning build directory...
12+
del /Q %BUILD_DIR%\*
13+
14+
:: Windows 构建
15+
echo Building for Windows/amd64...
16+
set GOOS=windows
17+
set GOARCH=amd64
18+
go build -o "%BUILD_DIR%\mcp-alapi-cn-%VERSION%-windows-amd64.exe" -ldflags="-s -w" main.go
19+
20+
echo Building for Windows/arm64...
21+
set GOOS=windows
22+
set GOARCH=arm64
23+
go build -o "%BUILD_DIR%\mcp-alapi-cn-%VERSION%-windows-arm64.exe" -ldflags="-s -w" main.go
24+
25+
:: Linux 构建
26+
echo Building for Linux/amd64...
27+
set GOOS=linux
28+
set GOARCH=amd64
29+
go build -o "%BUILD_DIR%\mcp-alapi-cn-%VERSION%-linux-amd64" -ldflags="-s -w" main.go
30+
31+
echo Building for Linux/arm64...
32+
set GOOS=linux
33+
set GOARCH=arm64
34+
go build -o "%BUILD_DIR%\mcp-alapi-cn-%VERSION%-linux-arm64" -ldflags="-s -w" main.go
35+
36+
:: macOS 构建
37+
echo Building for macOS/amd64...
38+
set GOOS=darwin
39+
set GOARCH=amd64
40+
go build -o "%BUILD_DIR%\mcp-alapi-cn-%VERSION%-darwin-amd64" -ldflags="-s -w" main.go
41+
42+
echo Building for macOS/arm64...
43+
set GOOS=darwin
44+
set GOARCH=arm64
45+
go build -o "%BUILD_DIR%\mcp-alapi-cn-%VERSION%-darwin-arm64" -ldflags="-s -w" main.go
46+
47+
echo Build complete! Output files are in the build directory.

build.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash
2+
3+
# 版本号
4+
VERSION="1.0.0"
5+
6+
# 创建构建目录
7+
mkdir -p build
8+
9+
# 构建函数
10+
build() {
11+
local GOOS=$1
12+
local GOARCH=$2
13+
local SUFFIX=$3
14+
15+
echo "Building for $GOOS/$GOARCH..."
16+
17+
# 设置输出文件名
18+
local OUTPUT="build/mcp-alapi-cn-${VERSION}-${GOOS}-${GOARCH}${SUFFIX}"
19+
20+
# 执行构建
21+
GOOS=$GOOS GOARCH=$GOARCH go build -o "$OUTPUT" -ldflags="-s -w" main.go
22+
23+
# 检查构建结果
24+
if [ $? -eq 0 ]; then
25+
echo "✓ Successfully built $OUTPUT"
26+
else
27+
echo "✗ Failed to build for $GOOS/$GOARCH"
28+
exit 1
29+
fi
30+
}
31+
32+
# 清理旧的构建文件
33+
echo "Cleaning build directory..."
34+
rm -rf build/*
35+
36+
# Windows 构建
37+
build "windows" "amd64" ".exe"
38+
build "windows" "arm64" ".exe"
39+
40+
# Linux 构建
41+
build "linux" "amd64" ""
42+
build "linux" "arm64" ""
43+
44+
# macOS 构建
45+
build "darwin" "amd64" ""
46+
build "darwin" "arm64" ""
47+
48+
echo "Build complete! Output files are in the build directory."

go.mod

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module mcp-alapi-cn
2+
3+
go 1.24.1
4+
5+
require (
6+
github.com/getkin/kin-openapi v0.131.0
7+
github.com/go-resty/resty/v2 v2.16.5
8+
github.com/mark3labs/mcp-go v0.17.0
9+
)
10+
11+
require (
12+
github.com/go-openapi/jsonpointer v0.21.0 // indirect
13+
github.com/go-openapi/swag v0.23.0 // indirect
14+
github.com/google/uuid v1.6.0 // indirect
15+
github.com/josharian/intern v1.0.0 // indirect
16+
github.com/mailru/easyjson v0.7.7 // indirect
17+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
18+
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
19+
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
20+
github.com/perimeterx/marshmallow v1.1.5 // indirect
21+
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
22+
golang.org/x/net v0.33.0 // indirect
23+
gopkg.in/yaml.v3 v3.0.1 // indirect
24+
)

go.sum

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE=
4+
github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
5+
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
6+
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
7+
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
8+
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
9+
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
10+
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
11+
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
12+
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
13+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
14+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
15+
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
16+
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
17+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
18+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
19+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
20+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
21+
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
22+
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
23+
github.com/mark3labs/mcp-go v0.17.0 h1:5Ps6T7qXr7De/2QTqs9h6BKeZ/qdeUeGrgM5lPzi930=
24+
github.com/mark3labs/mcp-go v0.17.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE=
25+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
26+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
27+
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
28+
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
29+
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
30+
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
31+
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
32+
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
33+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
34+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
35+
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
36+
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
37+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
38+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
39+
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
40+
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
41+
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
42+
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
43+
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
44+
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
45+
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
46+
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
47+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
48+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
49+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
50+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
51+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/config/config.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
"os"
6+
)
7+
8+
type Config struct {
9+
ServerName string
10+
Version string
11+
OpenAPIURL string
12+
BaseURL string
13+
Token string
14+
}
15+
16+
func NewConfig() (*Config, error) {
17+
token := os.Getenv("ALAPI_TOKEN")
18+
if token == "" {
19+
return nil, fmt.Errorf("ALAPI_TOKEN environment variable is required")
20+
}
21+
22+
apiID := os.Getenv("ALAPI_API_ID")
23+
openAPIURL := "https://v3.alapi.cn/openapi.json"
24+
if apiID != "" {
25+
openAPIURL = fmt.Sprintf("https://v3.alapi.cn/openapi/%s.json", apiID)
26+
}
27+
28+
return &Config{
29+
ServerName: "ALAPI MCP Server",
30+
Version: "1.0.0",
31+
OpenAPIURL: openAPIURL,
32+
BaseURL: "https://v3.alapi.cn",
33+
Token: token,
34+
}, nil
35+
}

internal/handler/tool_handler.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package handler
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"mcp-alapi-cn/internal/models"
8+
9+
"github.com/go-resty/resty/v2"
10+
"github.com/mark3labs/mcp-go/mcp"
11+
)
12+
13+
type ToolHandler struct {
14+
baseURL string
15+
token string
16+
client *resty.Client
17+
}
18+
19+
func NewToolHandler(baseURL string, token string) *ToolHandler {
20+
return &ToolHandler{
21+
baseURL: baseURL,
22+
token: token,
23+
client: resty.New(),
24+
}
25+
}
26+
27+
func (h *ToolHandler) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
28+
arguments := request.Params.Arguments
29+
uri := request.Params.Name
30+
31+
marshal, err := json.Marshal(arguments)
32+
if err != nil {
33+
return nil, fmt.Errorf("marshal request failed: %w", err)
34+
}
35+
36+
response, err := h.client.SetBaseURL(h.baseURL).R().
37+
SetHeader("Content-Type", "application/json").
38+
SetHeader("token", h.token).
39+
SetHeader("User-Agent", "ALAPI-SDK/MCP-SERVER v1.0.0").
40+
SetBody(marshal).
41+
Post(uri)
42+
if err != nil {
43+
return nil, fmt.Errorf("API request failed: %w", err)
44+
}
45+
46+
// 解析基础响应
47+
var baseResp models.BaseResponse
48+
if err := json.Unmarshal(response.Body(), &baseResp); err != nil {
49+
return nil, fmt.Errorf("unmarshal response failed: %w", err)
50+
}
51+
if baseResp.Code != 200 {
52+
return nil, fmt.Errorf("api response failed:%s", baseResp.Message)
53+
}
54+
55+
// 只返回 data 部分的数据
56+
dataJSON, err := json.Marshal(baseResp.Data)
57+
if err != nil {
58+
return nil, fmt.Errorf("marshal response data failed: %w", err)
59+
}
60+
61+
return mcp.NewToolResultText(string(dataJSON)), nil
62+
}

internal/models/response.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package models
2+
3+
// BaseResponse 基础响应结构
4+
type BaseResponse struct {
5+
Code int `json:"code"`
6+
RequestID string `json:"request_id"`
7+
Message string `json:"message"`
8+
Time int64 `json:"time"`
9+
Usage int `json:"usage"`
10+
Data interface{} `json:"data"`
11+
}

0 commit comments

Comments
 (0)