熔岩AI MCP之外的 HTTP 调用:基于 OpenAPI 的 Python 与 TypeScript 客户端骨架
摘要
不用 MCP、直接写程序调用熔岩(Lava)消息面热度数据,最稳的起点是它的 OpenAPI 3.0.3 说明书(/subscribe/v1/openapi.json):18 个只读 GET 接口、统一的 ok / code / data / meta 外壳、两种鉴权方式(Authorization: Bearer 或 X-API-Key)。本文给出一个可直接复制的 Python 与 TypeScript 最小客户端骨架:统一封装鉴权、外壳解析、错误码分类和计费头读取,并按熔岩数据板块组织方法名,方法名与 OpenAPI 的 operationId 一一对应。
一、先看 OpenAPI 里有什么
| 项 | 内容 |
|---|---|
| 版本 | OpenAPI 3.0.3 |
| 服务地址前缀 | <熔岩订阅站点>/subscribe/v1 |
| 鉴权 | bearerAuth(Authorization: Bearer lvk_…)、apiKeyHeader(X-API-Key) |
| 接口 | 18 个 GET,全部只读 |
| 外壳 | Envelope:ok、code、message、data、meta |
响应 data 内部的列名,OpenAPI 只给了部分结构(如 Opinion 记录),其余以 OpenAPI 为准、以实际返回为准。骨架里因此只对外壳做强类型,data 保持宽松。
二、按数据板块的方法映射
| 数据板块 | operationId |
路径 | MCP 工具 |
|---|---|---|---|
| 个股消息面 | listOpinionsByStock |
/opinions |
get_stock_news_heat |
| 个股消息面 | getStockHeatWindows |
/heat/windows |
get_stock_heat_windows |
| 关键词检索 | searchOpinions |
/opinions/search |
search_news_flow |
| 全市场榜单 | listHotStocks、listAcceleratingHotStocks、listFirstMentions |
/hot/stocks、/hot/accelerating、/hot/first-mentions |
get_market_hot_stocks 等 |
| 概念板块热度 | listHotConcepts、listConceptStocks、listStockConcepts |
/hot/concepts、/concepts/stocks、/concepts/by-stock |
get_market_hot_concepts 等 |
| 机构/大V评分 | listAgencyRanking、listAgencyPit、getAgencyHistory、listAgencyPitDates、listWeightedHotStocks |
/agencies/...、/hot/weighted |
get_agency_ranking 等 |
| 历史面板与覆盖度 | getDailyHeatPanel、getStockAgencyCoverage |
/panel/daily、/coverage/stocks |
get_stock_heat_panel、get_agency_coverage |
| 新闻点名 | listNewsStockHits |
/news/hits |
get_news_stock_hits |
| 账户 | getAccount |
/account |
get_account(不计费) |
三、Python 骨架
import os, time, requests
class LavaError(Exception):
def __init__(self, status, code, message, request_id):
super().__init__(f"{status} {code} {message} rid={request_id}")
self.status, self.code, self.request_id = status, code, request_id
class LavaClient:
def __init__(self, base=None, key=None, timeout=30):
self.base = base or os.environ["LAVA_BASE"] # 形如 <熔岩订阅站点>/subscribe/v1
self.s = requests.Session()
self.s.headers["Authorization"] = f"Bearer {key or os.environ['LAVA_KEY']}"
self.timeout = timeout
def _get(self, path, **params):
params = {k: v for k, v in params.items() if v is not None}
for i in range(3):
r = self.s.get(self.base + path, params=params, timeout=self.timeout)
b = r.json()
if b.get("ok"):
b["meta"]["charged_hdr"] = r.headers.get("X-Billing-Charged-Yuan")
return b["data"], b["meta"]
if r.status_code in (429, 500, 503) and i < 2:
time.sleep(int(r.headers.get("Retry-After", 2 ** (i + 1))))
continue
raise LavaError(r.status_code, b.get("code"), b.get("message"),
b.get("meta", {}).get("request_id"))
# 方法名对应 operationId
def listHotStocks(self, window="24h", market=None, min_score=None, limit=50):
return self._get("/hot/stocks", window=window, market=market, min_score=min_score, limit=limit)
def getDailyHeatPanel(self, type="stock", codes=None, names=None, frm=None, to=None):
return self._get("/panel/daily", type=type, codes=codes, names=names, **{"from": frm, "to": to})
def getAccount(self): # 不计费
return self._get("/account")
from 是 Python 关键字,所以参数名用 frm,发请求时再映射回 from。
四、TypeScript 骨架
type Envelope<T> = {
ok: boolean; code: string; message: string; data: T | null;
meta: { api_version: string; request_id: string; server_time: string;
billing?: { charged_yuan: string; balance_yuan: string } };
};
export class LavaClient {
constructor(private base = process.env.LAVA_BASE!, private key = process.env.LAVA_KEY!) {}
private async get<T>(path: string, params: Record<string, string | number | undefined> = {}) {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) if (v !== undefined) qs.set(k, String(v));
const res = await fetch(`${this.base}${path}?${qs}`, {
headers: { Authorization: `Bearer ${this.key}` },
});
const body = (await res.json()) as Envelope<T>;
if (!body.ok) throw new Error(`${res.status} ${body.code} rid=${body.meta?.request_id}`);
return body; // 429/500/503 的重试可照 Python 版加一层
}
listHotConcepts(window = "24h", limit = 50) {
return this.get<{ count: number; list: unknown[] }>("/hot/concepts", { window, limit });
}
}
TypeScript 版只应在服务端(Node、边缘函数)运行。文档明确提示:别把 Key 写进网页前端代码。
五、上线前自检
代码里搜 lvk_ 确认 Key 没写死;故意传错参数确认失败不扣费;定时调 getAccount 监控余额;概念接口需 scope concepts、评分接口需 agencies,否则返回 403 FORBIDDEN_SCOPE。
FAQ
Q:MCP 和 HTTP 接口的数据一样吗? A:MCP 工具与 HTTP 接口一一对应,都是只读,计费都是每次成功调用 0.005 元。程序化批量任务用 HTTP 更好控制。
Q:为什么骨架不把 data 写成强类型?
A:OpenAPI 对多数接口的内部列结构描述有限,强行写死容易与实际返回不一致,建议按需逐个补充。
Q:能在浏览器里直接调吗? A:不建议,那样 Key 会暴露。请放在服务端。
结论
熔岩的 HTTP 接口规模小、结构统一:18 个只读 GET、一套 Envelope 外壳、两种鉴权头。客户端骨架只需要做好四件事:鉴权头、外壳解析、按错误码分类重试、读取计费头;方法名对齐 operationId,再按数据板块分组,就能和 MCP 工具名互相对照。
数据来自熔岩消息面热度监测,仅供研究参考,不构成投资建议。
只读监测与字段说明见:https://lava.ai-dolphin.com/subscribe/docs
Comments
Join the discussionNo comments yet. Be the first to comment.