Files
sanguo_vnpy_v2/docs/archive/design/provider-mcp-expose-design.md
T
claude_dev f6ca85e937 docs(audit): 文档审计吸收批——P0×2+P1×4 修复 [nas]
- P0-2 触及面矩阵修正(three-env §12): sanguo_api+frontend→🔴——VPS 常驻
  sanguo-api 生产控制台(runbook §7.1/§7.6 09-25 勘误「本就常驻跑纯 API」,
  vps-deploy.yml 每次部署重启 sanguo-api 实证)+公网前端 dist; sanguo_web=legacy
  平行后端非「前端」; orchestrator→🟡 随 api 常驻加载(app.py:15 import 实证)。
  同病 .claude/CLAUDE.md 判定行+reload 节一并修正(两处)
- P0-1 README 入口: 生产命令勘误(勿起 sanguo_web legacy 后端,架构审计 P0-3
  定性危险)+admin123 凭据指引删除+api/user_guide 断链节改指文档中心
- P1 reload 旧任务名 sanguo-bs-eod→sanguo-bs-daily(three-env §4+session-guide
  §6, 2026-08-20 重组遗留)
- P1 session-guide: smoke_e2e.py 死引用(da32cca 审计卫生批已删)→CI nas-verify
  手动等效探针(免凭据版);dispatch 样例补 confirm 必填字段
- P1 nas-deploy-plan §四: docker run 补 --init+数据挂载/删 8080(现役容器
  docker inspect 实证 Init=true 仅 8000 双挂载);§五外网链路标废(首尔入口 502)
- env-version-matrix VPS 角色+ops-README BRIDGE_URL: miniQMT/FastAPI-bridge
  退役标注(09-08)
- three-env §1 NAS 路径 stock→homes/admin(promote.sh:19 实证)+§3.1 模块
  计数 13→15

审计源: audit/20261001_docs_audit/(P0-1/P0-2/P1-1~4) +
audit/20261001_architecture_code_audit/(P0-1)

Co-Authored-By: Claude Code <notify@anthropic.com>
2026-10-01 09:08:18 +08:00

7.1 KiB

设计笔记:用 MCP 直接暴露 LocalUnifiedProvider 给 Claude Code

设计日期 2026-07-30。配套 provider-tet-design.md + docs/research/openbb-platform-research.md。

一句话定位:给 AI 装一只「直接读本项目本地数据」的手——把 LocalUnifiedProvider 的取数方法注册成 Claude Code 可调的 MCP 工具,从「请你跑数据贴给我」变成「我自己拉数据、算指标、给结论」。


一、为什么是「直接暴露」而非照搬 OpenBB

OpenBB 的链路(它需要 REST + Workspace,MCP 是副产品):
  provider方法 → @router.command → FastAPI端点 → fastmcp 从 OpenAPI 派生 → MCP工具

本项目(不需要 REST/Workspace,跳过中间层):
  provider方法 → 直接 @tool 注册成 MCP工具

OpenBB 建了 FastAPI 是为了给 Workspace 前端和第三方 REST 用,MCP 顺手派生。本项目策略层直调 provider、不给第三方 REST,纯为 MCP 去建一整套 FastAPI + Router 命令树是过度工程(YAGNI)。所以直接拿 MCP server 框架(fastmcp / mcp python sdk),把 provider 方法 @tool 注册即可。


二、解决的本项目痛点(三个实证)

痛点 现状 MCP 暴露后
人肉割裂 你跑脚本 → 贴数据 → 我分析;或我用 ! 让你跑命令,每查一次打断一次 我直接调工具拿数据,你当中转的环节消失
手搓查询错(feedback-verify-via-provider-exact-query) 我手搓 SQL symbol='000534' 查 dbbardata(双列)误报「19 股无日线」,实际 provider 数据完整 我调用的就是 provider 确切方法,口径天然一致,不可能手搓错
AI 不能主动取数 你问「000001 走势如何」,我没法直接拉数据,得绕几道 我直接 get_price_panel("000001") → 算指标 → 给结论,一句话进一个分析出

三、暴露哪些方法(基于真实公开方法清单)

源:sanguo_portfolio/providers/local_unified_provider.py 的 LocalUnifiedProvider 14 个公开方法。按暴露优先级分组:

P0 — 高频取数(优先暴露,策略/回测核心)

方法 行号 用途 备注
get_price 170 行情(日线/15min,支持复权) 最高频
get_closes_panel 272 多股票收盘价面板 回测核心,已优化 fq+UNION ALL
get_constituent 512 指数成份股(治偏差版) 选股池
get_fundamentals_df 582 基本面(已修前视偏差) 基本面过滤

P1 — 估值/风控

方法 行号 用途
get_value_metrics_batch 639 估值(pe/pb)批量
get_limit_status_batch 868 涨跌停状态(已优化 UNION ALL)

P2 — 元信息/日历

方法 行号 用途
get_security_info / get_security_info_batch 746 / 777 证券元信息
get_all_securities 998 全部证券列表
get_trade_days 723 交易日历
get_split_dividend 967 除权除息

暂不暴露(与 Vibe-Research 重叠或低频)

方法 原因
get_current_tick(947) 实时 tick,Vibe-Research 的 query_quote 已覆盖在线实时
get_index_stocks(479) 与 get_constituent 重叠,后者是治偏差权威版,暴露后者即可

四、怎么注册(薄 MCP server)

核心:server 只做「注册 + 序列化」,不写业务逻辑(业务全在 provider)。

# 伪代码:sanguo_mcp/server.py
from mcp.server.fastmcp import FastMCP
from sanguo_portfolio.providers.local_unified_provider import LocalUnifiedProvider

mcp = FastMCP("sanguo")
provider = LocalUnifiedProvider(...)   # 读本地 dbbardata/parquet

@mcp.tool()
def sanguo_get_price(symbol: str, start: str, end: str, interval: str = "1d") -> list[dict]:
    """获取 A 股行情(日线/15min,前复权)。symbol 如 '000001.SZ'。"""
    df = provider.get_price(symbol, start, end, interval=interval)
    return df.to_dict(orient="records")   # JSON records,LLM 友好

@mcp.tool()
def sanguo_get_closes_panel(symbols: list[str], start: str, end: str) -> dict:
    """多股票收盘价面板(回测用)。"""
    return provider.get_closes_panel(symbols, start, end).to_dict()
# ... 其余方法同理

关键约定

  • 工具命名前缀 sanguo_:和已挂载的 vibe-research__* 区分,一看就知道是本地数据
  • 参数 = provider 标准字段(symbol/start/end/interval),和 TET 笔记的 standard/extra 拆分一致
  • 返回 JSON records:参考 OpenBB OBBject.to_llm()(JSON records 格式),LLM 友好;大表注意别一次返回几万行(加 limit 或采样)
  • 数据本地读:工具内部仍走 provider-local-data-only 铁律,读 dbbardata/parquet,不打网络

五、和 Vibe-Research 的分工(互补,不重叠)

数据来源 能查什么 工具前缀
Vibe-Research(已有 5 工具) 在线实时 当前价、新闻、研报、估值、全球股 vibe-research__ / mcp__vibe-research__
本项目 MCP(本设计) 本地 dbbardata/parquet 历史日线/15min、成份股、基本面历史、涨跌停、回测取数 sanguo_

互补关系:实时用 Vibe-Research,历史/回测用 sanguo。例如「000001 现在多少钱」用 query_quote,「000001 过去 5 年回测数据」用 sanguo_get_price。


六、和 TET 的配套(顺序很重要)

阶段 做什么 为什么
先 TET 化 provider(见 provider-tet-design.md) transform_data pydantic 校验,治兜底会乱,保证取数质量
后 MCP 暴露 让 AI 消费的是校验过的干净标准数据

顺序不能反:如果 provider 兜底还在(补 NaN 当停牌),MCP 暴露出去的也是脏数据,AI 拿着错数据做分析,结论全错。先治兜底,再暴露。


七、设计原则

  1. 跳过 FastAPI/Router(YAGNI):本项目不给第三方 REST,纯为 MCP 建它们是过度工程
  2. 暴露 provider 确切方法:根治「手搓查询口径错」,我调用的就是策略层用的同一套 API
  3. 薄 server:只注册 + 序列化,业务逻辑零下沉(全留 provider),server 永远是薄薄的胶水层
  4. 本地只读:工具内部读 dbbardata/parquet,遵守 provider-local-data-only 铁律
  5. 大表克制:MCP 工具面向「对话式查询」,默认带 limit/采样,几万行回测数据不该一次性灌给 LLM

八、落地步骤

  1. 选 MCP 框架(mcp python sdk 或 fastmcp),起 sanguo_mcp server
  2. 优先暴露 4 个 P0 方法(get_price / get_closes_panel / get_constituent / get_fundamentals_df)
  3. 参数用标准字段,返回 JSON records,大表加 limit
  4. 挂到 Claude Code MCP 配置(参考 Vibe-Research 的挂载方式)
  5. 验证:对话里调 sanguo_get_price("000001.SZ", ...),确认数据与直接调 provider 一致
  6. 再逐步暴露 P1/P2

相关

  • 配套设计:provider-tet-design.md(先治兜底,再暴露)
  • 调研:docs/research/openbb-platform-research.md
  • Vibe-Research 接入先例:a-stock-data-integration
  • provider 本地只读铁律:provider-local-data-only
  • 手搓查询错教训:feedback-verify-via-provider-exact-query