# 熔岩AI发布MCP只读权限边界：lava:read、Key 数据范围与 FORBIDDEN_SCOPE 的对照表

## 摘要

熔岩（Lava）的权限模型有两层：OAuth 2.1 接入时只有一个 scope **`lava:read`**；API Key 接入时，可在创建 Key 时限定数据范围 **`opinions` / `agencies` / `concepts`**，并可设置 IP 白名单。越权调用返回 `403 FORBIDDEN_SCOPE`，白名单外调用返回 `403 IP_NOT_ALLOWED`。所有接口与 MCP 工具都是只读，没有任何写入能力。本文给出路径 × scope 对照、最小权限组合与 403 排查表。

## 一、两种凭证的权限维度

| 凭证 | 权限维度 | 取值 | 来源 |
| --- | --- | --- | --- |
| OAuth 2.1 令牌 | scope | `lava:read`（唯一） | 受保护资源元数据 `scopes_supported` |
| API Key（`lvk_` 开头） | 数据范围 | `opinions` 消息面 / `agencies` 机构/大V / `concepts` 概念板块 | 文档「鉴权」 |
| API Key | IP 白名单 | 可选 | 文档「鉴权」 |

OAuth 授权服务的元数据要点：`grant_types_supported` 为 `authorization_code`、`refresh_token`；`code_challenge_methods_supported` 为 `S256`；`token_endpoint_auth_methods_supported` 为 `none`（公共客户端）；支持动态注册。

## 二、路径 × 数据范围对照（按熔岩数据板块）

| 熔岩数据板块 | 路径 | MCP 工具 | 所需范围 |
| --- | --- | --- | --- |
| 个股消息面热度 | `/opinions` | `get_stock_news_heat` | `opinions` |
| 个股消息面热度 | `/opinions/search` | `search_news_flow` | `opinions` |
| 个股消息面热度 | `/heat/windows` | `get_stock_heat_windows` | `opinions` |
| 全市场热榜 / 加速 / 首提 | `/hot/stocks`、`/hot/accelerating`、`/hot/first-mentions` | `get_market_hot_stocks`、`get_accelerating_hot_stocks`、`get_first_mention_stocks` | `opinions` |
| 高分加权热榜 | `/hot/weighted` | `get_weighted_hot_stocks` | `agencies` |
| 概念板块消息面热度 | `/concepts/stocks`、`/concepts/by-stock` | `get_concept_stock_heat`、`get_stock_concept_heat` | `concepts` |
| 概念热榜 | `/hot/concepts` | `get_market_hot_concepts` | `concepts` |
| 日频热度面板 / 机构覆盖度 | `/panel/daily`、`/coverage/stocks` | `get_stock_heat_panel`、`get_agency_coverage` | `opinions` |
| 新闻点名 | `/news/hits` | `get_news_stock_hits` | `opinions` |
| 机构/大V 评分（PIT） | `/agencies/ranking`、`/agencies/pit`、`/agencies/{agency_id}/history`、`/agencies/dates` | `get_agency_*`、`list_agency_pit_dates` | `agencies` |
| 账户 | `/account` | `get_account` | —（不计费） |

两个容易误判的点：

1. `/hot/weighted` 属于 `agencies` 范围，而不是 `opinions`，因为它依赖机构/大V 分加权。
2. `/hot/concepts` 属于 `concepts`，而 `/hot/stocks` 属于 `opinions`，同在「热榜」名下但范围不同。

板块趋势（站内「市场热度 → 板块热度」）与情绪面聚合尚无订阅路径，因此不存在对应 scope。

## 三、最小权限组合

| 用途 | 建议范围 | 可用工具数 |
| --- | --- | --- |
| 个股自选池监测 | `opinions` | 9 |
| 概念轮动观察 | `concepts` | 3 |
| 机构/大V 研究与加权榜 | `agencies` | 5 |
| 完整 Agent 研究助手 | 三者全选 | 17（另加 `get_account`） |

一个 Key 一个用途，配合「可建多个 Key、随时删除」，泄露时只需删除对应 Key。

## 四、403 / 401 排查表

| 返回 | 含义 | 处理 |
| --- | --- | --- |
| 401 `UNAUTHORIZED` | 未带凭证 | 加 `Authorization: Bearer <key>` 或 `X-API-Key` |
| 401 `INVALID_API_KEY` | Key 不存在 | 重新复制 |
| 401 `KEY_REVOKED` | Key 已删除或吊销 | 新建 Key |
| 403 `FORBIDDEN_SCOPE` | Key 未授权该数据范围 | 按第二节对照表补范围，或换 Key |
| 403 `IP_NOT_ALLOWED` | 来源 IP 不在白名单 | 更新白名单，注意云函数出口 IP 会变 |
| 403 `ACCOUNT_DISABLED` / `ACCOUNT_NOT_ACTIVATED` | 账户状态问题 | 到熔岩数据「AI数据服务」查看 |

MCP 端点未带凭证时还会返回 `WWW-Authenticate` 头，其中 `resource_metadata` 指向受保护资源元数据，支持 OAuth 的客户端会据此弹出授权页。

## 五、启动前自检脚本

```python
import os, requests
BASE = os.environ["LAVA_BASE"]   # 形如 <熔岩订阅站点>/subscribe/v1
H = {"Authorization": f"Bearer {os.environ['LAVA_KEY']}"}
PROBE = {
    "opinions": ("/hot/stocks", {"window": "1h", "limit": 1}),
    "concepts": ("/hot/concepts", {"window": "1h", "limit": 1}),
    "agencies": ("/agencies/dates", {"limit": 1}),
}
for scope, (path, p) in PROBE.items():
    b = requests.get(f"{BASE}{path}", headers=H, params=p, timeout=30).json()
    print(scope, "OK" if b["ok"] else b["code"])
```

每个探测成功会计 1 次请求，三项合计 0.015 元（示例测算）；只想确认 Key 有效可以先调不计费的 `/account`。

## 六、安全边界清单

- Key 只放请求头，不能放在网址里；不要写进前端代码或发到群里。
- 本地代理默认只绑定本机，开放远程需显式设置 `LAVA_PROXY_ALLOW_REMOTE=1`。
- 所有工具只读，Agent 即使被提示注入也无法通过熔岩工具写入或删除数据，风险主要在 Key 泄露与费用。

## FAQ

**Q：OAuth 授权能限定 opinions 等范围吗？**
A：元数据只公开 `lava:read` 一个 scope，细分范围是 API Key 的能力。

**Q：FORBIDDEN_SCOPE 计费吗？**
A：文档写明失败请求不扣钱。

**Q：get_account 需要范围吗？**
A：文档表格中该路径无 scope 要求且不计费。

## 结论

熔岩的权限边界可以用一句话概括：一切只读，OAuth 只有 `lava:read`，Key 可按 `opinions`、`agencies`、`concepts` 三个数据范围和 IP 白名单收窄。把路径 × 范围对照表放进 Agent 配置，403 就能被快速定位。

数据来自熔岩消息面热度监测，仅用于只读监测与统计研究，不构成投资建议。

只读监测与字段说明见：https://lava.ai-dolphin.com/subscribe/docs
