dsh-futu-mcp
DSH plugin: connect to Futu (富途) MCP via OAuth 2.1 with deferred authorization; tokens stored securely outside the workspace (~/.dsh, 0600)
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 31, 2026
- Updated
- Aug 31, 2026
Introduction
@yangzhe1991/dsh-futu-mcp
A DSH (DeepSeek Harness) plugin that connects the agent to the official Futu (富途) MCP server and registers its tools on ctx.tools under mcp__futu__<toolName>.
What it does
- Connects to
https://mcp.futunn.com/mcpusing the pagination-aware MCP Streamable HTTP transport 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 includingtrade:writeorder 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 mode0600, directory mode0700. - Override with
credentialFilein 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.
Install
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:
- 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:
"dependencies": {
"@yangzhe1991/dsh-futu-mcp": "link:/path/to/dsh-futu-mcp"
}
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