Bundle
@yangzhe1991/dsh-futu-mcp
DSH plugin: connect to Futu (富途) MCP via OAuth 2.1 and expose its tools to the agent; tokens persisted outside the workspace
- Source
- yangzhe1991
- License
- MIT
- Updated
- Updated 13 days ago
Readme
# @yangzhe1991/dsh-futu-mcp
[English](README.md) | [中文](README.zh.md)
[](https://www.npmjs.com/package/@yangzhe1991/dsh-futu-mcp)
[](https://www.npmjs.com/package/@yangzhe1991/dsh-futu-mcp)
[](LICENSE)
[](https://github.com/deepseek-ai/deepseek-harness)
A DSH (DeepSeek Harness) plugin that connects the agent to the official [Futu (富途) MCP server](https://open.futunn.com/zh-cn/mcp-docs/overview) and registers its tools on `ctx.tools` under `mcp__futu__<toolName>`.
## What it does
- Connects to `https://mcp.futunn.com/mcp` using the [pagination-aware MCP Streamable HTTP transport](https://open.futunn.com/zh-cn/mcp-docs/quick-start) and publishes server tools to the agent as callable tools (`mcp__futu__*`).
- Handles Futu's **OAuth 2.1** authorization completely in-process:
- RFC 9728 / RFC 8414 discovery, dynamic client registration (RFC 7591), PKCE S256, refresh-token rotation.
- On first use your browser opens the Futu authorization page; after you authorize, tokens are saved and reused silently afterwards (automatic refresh; a new browser prompt appears only when refresh fails).
- **Scopes are strictly capped to what you configure** (default: read-only `quote:read` + `trade:read`). The plugin intercepts the server's protected-resource metadata (which advertises every scope including `trade:write` order placement) and narrows it to your configured set, so neither client registration nor the authorization request can ever request more than you approved.
- **Deferred authorization — zero Futu traffic until you actually use it**: plugin load only registers the previously synced tool list from the credentials cache (so the agent can see `mcp__futu__*` tools) with **no network, no token refresh, no browser popup**. A conversation that never touches Futu costs nothing. The moment a Futu tool is actually called, the plugin connects; if the token is stale it refreshes silently (only when the refresh token itself is expired/revoked — a 14-day cycle — does the browser login page open, and the same call then retries and succeeds). The one exception: on very first use there is no cached tool list yet, so the first sent message triggers the connect + login, after which the cache exists and everything is call-time-only.
## Behavior
| When | What happens |
|---|---|
| `dsh web` starts / plugin loads | Only reads the credentials file: cached tool list is registered (agent sees `mcp__futu__*`). **No network, no token refresh, no browser.** |
| You chat about anything unrelated | Nothing (zero Futu activity). Tool calls never reach Futu. |
| A Futu tool is actually called | Connect + sync happens now; stale token → silent refresh (no UI). |
| Refresh token expired/revoked (≤14 days) and a Futu tool is called | Login page opens at that moment; after you authorize, the same call retries and succeeds. |
| Very first use (no cached tool list) | Your first sent message triggers connect + login; after authorizing, the tool list is cached for all future starts. |
## Token storage (secure, project-independent)
OAuth tokens, the registered `client_id` and the (non-sensitive) discovery cache are persisted to a credentials file **outside any workspace or project directory**:
- Default: `~/.dsh/credentials/futu-mcp.json` (i.e. `$DSH_HOME/credentials/`), file mode `0600`, directory mode `0700`.
- Override with `credentialFile` in the plugin config (the path is resolved with `~` expansion; keep it inside your home directory, never inside a project).
The access token is short-lived (~2h) and refreshed automatically; the refresh token lives up to 14 days, after which the browser prompt appears again. You can revoke tokens at any time from the [Futu OpenAPI Dashboard](https://open.futunn.com/).
## Install
```bash
dsh plugin --profile web add @yangzhe1991/dsh-futu-mcp
```
Then restart `dsh web`. On the first quote query the browser will open the Futu authorization page — log in and approve the requested scopes. Tip from the official docs: try asking the agent "查一下腾讯的实时股价" to verify.
## Config
Override configuration in your profile's `cordis.patch.yml`:
```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
```
## Local development
Dev mode (link) — edit your profile's `package.json`:
```json
"dependencies": {
"@yangzhe1991/dsh-futu-mcp": "link:/path/to/dsh-futu-mcp"
}
```
```bash
pnpm install # in the profile directory
npm run build # in the plugin directory, then hard-refresh / restart dsh web
```
`dsh.profile.bundles` must include `@yangzhe1991/dsh-futu-mcp` (run `dsh plugin add` to reconcile, or add it manually the same way as other plugins).
## License
MIT
Install
dsh plugin --profile web add github:yangzhe1991/dsh-futu-mcp
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install yangzhe1991-dsh-futu-mcp from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.