# @yangzhe1991/dsh-futu-mcp

[English](README.md) | [中文](README.zh.md)

[![npm version](https://img.shields.io/npm/v/@yangzhe1991/dsh-futu-mcp)](https://www.npmjs.com/package/@yangzhe1991/dsh-futu-mcp)
[![npm downloads](https://img.shields.io/npm/dt/@yangzhe1991/dsh-futu-mcp)](https://www.npmjs.com/package/@yangzhe1991/dsh-futu-mcp)
[![license](https://img.shields.io/npm/l/@yangzhe1991/dsh-futu-mcp)](LICENSE)
[![dsh-plugin](https://img.shields.io/badge/dsh-plugin-blue)](https://github.com/deepseek-ai/deepseek-harness)

DSH(DeepSeek Harness)插件:把 agent 接入富途官方 [MCP 服务器](https://open.futunn.com/zh-cn/mcp-docs/overview),其工具以 `mcp__futu__<工具名>` 注册到 `ctx.tools`。

## 功能

- 通过 MCP Streamable HTTP 传输连接 `https://mcp.futunn.com/mcp`,把服务端工具作为可调用工具发布给 agent(`mcp__futu__*`)。
- 完整内嵌富途 **OAuth 2.1** 授权流程:
  - RFC 9728 / RFC 8414 元数据发现、动态客户端注册(RFC 7591)、PKCE S256、refresh token 刷新;
  - 首次使用自动打开浏览器跳转富途授权页;授权后令牌落盘,之后静默复用(自动刷新;刷新失败才重新弹授权页)。
- **授权范围严格受限**:默认只读 `quote:read` + `trade:read`。插件会拦截服务端受保护资源元数据(它声明全部 scope,含下单用的 `trade:write`),裁剪为用户配置的范围 —— 客户端注册与授权请求都绝不可能超出你批准的范围。
- **延迟授权 —— 不碰富途就零流量**:插件加载只从凭证缓存注册上次同步的工具清单(agent 能看到 `mcp__futu__*`),【不连接、不刷 token、不弹窗】;与股票无关的对话全程零富途流量。真正调用富途工具的那一刻才连接;令牌过期则静默刷新(只有刷新令牌本身过期/被作废 —— 14 天周期 —— 才弹浏览器授权页,且同一调用会在授权后自动重试成功)。唯一例外:首次使用还没有工具清单缓存,第一条消息会触发连接+登录;授权后缓存生成,从此全部改为调用时触发。

## 行为一览

| 场景 | 行为 |
|---|---|
| `dsh web` 启动 / 插件加载 | 只读凭证文件:缓存工具清单注册上(agent 可见 `mcp__futu__*`)。**无网络、无刷新、无弹窗** |
| 聊与股票无关的内容 | 什么都不做(零富途流量) |
| 真正调用富途工具 | 此刻才连接+同步;令牌过期 → 静默刷新(无打扰) |
| 刷新令牌过期/被作废(14 天)时调用工具 | 就在那一刻弹授权页;你授权后同一调用重试成功 |
| 首次使用(无缓存清单) | 第一条消息触发连接+登录;授权后清单缓存,之后全程改走调用时触发 |

## 令牌存储(安全、与项目无关)

OAuth 令牌、注册的 `client_id` 与(非敏感的)发现缓存持久化在 **workspace/项目目录之外**的凭证文件里:

- 默认:`~/.dsh/credentials/futu-mcp.json`(即 `$DSH_HOME/credentials/`),文件权限 `0600`、目录权限 `0700`;
- 可通过配置 `credentialFile` 覆盖(支持 `~` 展开;请放在用户目录内,不要放项目目录)。

Access Token 有效期约 2 小时,自动刷新;Refresh Token 最长 14 天,过期后重新弹授权页。可随时在[富途 OpenAPI 控制台](https://open.futunn.com/)作废 Token。

## 安装

```bash
dsh plugin --profile web add @yangzhe1991/dsh-futu-mcp
```

然后重启 `dsh web`。首次查询行情时浏览器会打开富途授权页 —— 登录并批准请求的权限即可。官方推荐验证:让 agent「查一下腾讯的实时股价」。

## 配置

在 profile 的 `cordis.patch.yml` 中按 id 覆盖:

```yaml
- id: futu-mcp
  config:
    # 授权范围:只读 = quote:read + trade:read(默认)。
    # 需要自选管理/下单撤单时追加 quote:write / trade:write 并重新授权。
    scopes: [quote:read, trade:read]
    # 凭证文件(默认 ~/.dsh/credentials/futu-mcp.json,0600)。
    credentialFile: ~/.dsh/credentials/futu-mcp.json
    # 单次工具调用超时(ms)
    toolCallTimeoutMs: 60000
    # 授权页打开后的提醒间隔(ms)
    authTimeoutMs: 300000
    # 本地回调服务器端口(0 = 自动)
    callbackServerPort: 0
    reconnect:
      enabled: true
      initialDelayMs: 500
      maxDelayMs: 30000
      maxAttempts: 10
```

## 本地开发

开发态(link)—— 修改 profile 的 `package.json`:

```json
"dependencies": {
  "@yangzhe1991/dsh-futu-mcp": "link:/path/to/dsh-futu-mcp"
}
```

```bash
pnpm install   # 在 profile 目录执行
npm run build  # 在插件目录执行,然后硬刷新 / 重启 dsh web
```

`dsh.profile.bundles` 需包含 `@yangzhe1991/dsh-futu-mcp`(用 `dsh plugin add` 同步,或与其他插件一样手动加)。

## 许可证

MIT
