Skip to content

【功能建议】AK管理-地图相关-增加 Mapbox & MCP ak_verify 的 map 分类增加 Mapbox 地图 Key 验证 #1678

Description

@hhhanxx

【功能建议】AK管理-地图相关-增加 Mapbox & MCP ak_verify 的 map 分类增加 Mapbox 地图 Key 验证

📝 问题描述

ak_verifykind=map 目前仅支持 amap(高德)/tianditu(天地图)/baidu(百度),缺少 Mapbox
Mapbox 是国外主流地图服务(Web/移动端 SDK 大量使用,SRC 资产中常见 pk.xxx/sk.xxx 格式 token),
泄露的 Mapbox token 可导致配额滥用、账单消耗、私有样式/数据集读取。建议在 ak_verify 的 map 分类中
新增 provider=mapbox,或提供独立工具,对齐现有 amap/tianditu/baidu 的参数风格。

🕹 使用环境

  • 目标系统:Windows 10 / 无影版本:v3.4.2
  • 接入方式:MCP(http://127.0.0.1:8088/mcp),Agent 编程调用

💡 建议的 API 设计

方案 A(推荐):扩展现有 ak_verify

参数 类型 必填 说明
kind string map
provider string mapbox(新增,与 amap/tianditu/baidu 并列)
access_key string Mapbox token(pk.xxx 公开 / sk.xxx secret)
secret_key string 不需要,Mapbox 单 token 认证

方案 B:独立工具 mapbox_key_check

参数 类型 必填 说明
token string Mapbox API key(pk.xxxsk.xxx

📦 请求/响应示例

// 调用(方案 A)
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "ak_verify", "arguments": {
      "kind": "map", "provider": "mapbox",
      "access_key": "pk.eyJhbGciOiJSUzI1NiJ9.xxxxx" } } }

// 有效
{ "valid": true, "provider": "mapbox",
  "scopes": ["styles:read", "fonts:read", "datasets:read"],
  "owner": "user@example.com",
  "usage": ["static", "session", "api"],
  "detail": "token 有效,具备 scopes=styles:read,fonts:read,datasets:read" }

// 无效(Mapbox tokens/v2 对无效 token 返回 200 + code=TokenMalformed,注意判定)
{ "valid": false, "provider": "mapbox",
  "code": "TokenMalformed", "detail": "token 无效或格式错误" }

// 限流
{ "valid": "unknown", "provider": "mapbox",
  "status": 429, "detail": "请求被限流,稍后重试" }

🔧 参考实现要点(Go)

// 1. token 类型识别: pk.*(公开) / sk.*(secret)
kind := "public"
if strings.HasPrefix(token, "sk.") { kind = "secret" }

// 2. 权威验证: tokens/v2 官方接口(返回 scopes/owner/usage 权限明细)
url := "https://api.mapbox.com/tokens/v2?access_token=" + token
//    ⚠️ 关键坑: 无效 token 返回 HTTP 200 + {"code":"TokenMalformed"},
//    不能只判 status==200,必须检查 code 字段 + scopes 是否非空
//    有效: {"token":{...}, "scopes":["styles:read",...], "usage":[...]}

// 3. 兜底: geocoding 只读查询(部分 token 无 tokens/v2 权限但可调 API)
//    GET https://api.mapbox.com/geocoding/v5/mapbox.places/beijing.json?limit=1&access_token=<token>
//    200 + features 非空 → 有效

// 4. 判定汇总
//    VALID(有效+scopes) / INVALID(401/code=TokenMalformed) /
//    RATE_LIMITED(429) / UNKNOWN(网络异常)

关键点

  • 只读探测:tokens/v2 与 geocoding 均为查询接口,无副作用、无配额消耗(tokens/v2 本身不计费)
  • 判定陷阱:无效 token 返回 200 + code=TokenMalformed,必须结合 code 字段与 scopes 非空双重判断,不能只依赖 HTTP 状态码(实测踩坑)
  • 有效 token 返回 scopes 权限明细,可进一步判断利用面(styles 读取 / datasets 读写 / 静态图片配额)
  • 验证结果不含 token 本身回传,避免敏感信息落入日志

🧪 测试用例建议

用例 期望
真实有效 pk.xxx token valid: true + 非空 scopes
sk.xxx secret token valid: true(secret 通常权限更全)
格式错误 token(pk.invalid... valid: false + code=TokenMalformed
已吊销/过期 token valid: false + 401 或对应 code
空 token / 超短 token 返回明确错误,不 panic
高并发验证(批量 key 探测) 正常限流处理,429 不误判为无效

⚠️ 注意事项

  1. Mapbox 单 token 认证(无 AK/SK 对),secret_key 参数不需要,避免与 OSS 类混淆
  2. 判定必须区分 429 限流401 无效——限流时 key 可能仍然有效,不应标记 invalid
  3. 若短期不做 MCP,能否先开放该接口的判定逻辑说明,便于外部工具同步(社区已有 Python 参考实现,见下方附注)

附注:社区参考实现(Python,已实测)

本项目已用 Python 快速实现同逻辑验证可行(tscanplus-ext 扩展,mapbox_key_check 工具),
实测无效 token → INVALID (code=TokenMalformed),判定准确。核心逻辑可直接翻译为 Go 集成。

def mapbox_key_check(token: str) -> dict:
    # ① tokens/v2 权威验证
    r1 = request("https://api.mapbox.com/tokens/v2?access_token=" + token)
    if r1.status == 200 and r1.json:
        code = r1.json.get("code", "")
        scopes = r1.json.get("scopes", [])
        if code and code != "TokenValid":      # 无效 token: 200 + code=TokenMalformed
            return {"verdict": "INVALID", "evidence": "code=" + code}
        if not scopes:                          # 无 scopes 且无 code → 兜底 geocoding
            r2 = request("https://api.mapbox.com/geocoding/v5/mapbox.places/beijing.json?limit=1&access_token=" + token)
            if r2.status == 200 and r2.json.get("features"):
                return {"verdict": "VALID", "evidence": "geocoding 200 + features"}
            return {"verdict": "INVALID", "evidence": "tokens/v2 无 scopes"}
        return {"verdict": "VALID", "scopes": scopes, "owner": r1.json.get("token", {}).get("user", "")}
    if r1.status == 429:
        return {"verdict": "RATE_LIMITED"}
    return {"verdict": "UNKNOWN", "evidence": "status=" + str(r1.status)}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions