Skip to content

为 MCP 提供 prompts 能力 https://modelcontextprotocol.io/specification/2025-11-25/server/prompts - #55

Open
endless-bot wants to merge 106 commits into
mainfrom
endless/task-8
Open

为 MCP 提供 prompts 能力 https://modelcontextprotocol.io/specification/2025-11-25/server/prompts#55
endless-bot wants to merge 106 commits into
mainfrom
endless/task-8

Conversation

@endless-bot

Copy link
Copy Markdown

🤖 Auto-generated by Endless task #8.

Initiated by: Huacnlee Li Huashun

背景

MCP(Model Context Protocol)规范在 tools 之外还定义了 prompts 能力,允许服务端向 AI 客户端暴露可复用的工作流模板。客户端(如 Claude Desktop、Cursor)可通过 prompts/list 发现这些模板,再通过 prompts/get 获取填充好参数的消息序列,从而引导 LLM 按步骤调用相关工具并汇总结果。

longbridge-mcp 目前仅实现了 tools 能力,缺少 prompts 能力的声明与实现。这意味着连接该 MCP 服务的客户端无法发现和使用任何预定义的 Longbridge 工作流模板,用户每次都需要手动拼凑复杂的多步提示词。

摘要

  • 解决的问题: 为 longbridge-mcp 添加符合 MCP spec 的 prompts 能力,使 AI 客户端能够发现并调用 5 个预定义的 Longbridge 工作流模板。
  • 做了什么:
    • 新增 src/prompts.rs:使用 rmcp #[prompt_router] + #[prompt] 宏定义 5 个提示词(股票研究、持仓分析、期权筛选、交易执行、市场概览)。
    • 修改 src/main.rs:添加 mod prompts; 将新模块纳入编译。
    • 修改 src/tools/mod.rs:在 impl ServerHandler for Longbridge 上叠加 #[prompt_handler] 宏,并补充对应 model 类型的 use 导入。
  • 为什么这样做:
    • rmcp 1.4 已内置对称的 prompt 宏体系(#[prompt]/#[prompt_router]/#[prompt_handler]),与现有 tool 宏用法完全一致,不需要手动实现 ServerHandler trait 方法。
    • #[tool_handler] 会自动检测同一 impl 块上的 #[prompt_handler] 兄弟属性,在生成的 get_info() 中同时调用 enable_tools()enable_prompts(),无需修改 ServerCapabilities 构造代码。
  • 如何验证: cargo build 无错误,cargo clippy --all-features --all-targets 无警告;本地运行后通过 JSON-RPC 调用 prompts/list(期望返回 5 个提示词)和 prompts/get(期望返回含 user role 的消息)进行功能验证。

修改

文件 改动内容
src/prompts.rs 新增:#[prompt_router] impl 块,含 5 个 #[prompt] 方法及对应参数 struct
src/main.rs 新增 mod prompts; 声明
src/tools/mod.rs 新增 use rmcp::prompt_handler、扩展 model 导入、在 impl ServerHandler 上叠加 #[prompt_handler]

关键决策

#[prompt_handler]#[tool_handler] 叠加在同一 impl ServerHandler 块上:rmcp-macros 中 tool_handler 通过 has_sibling_handler 检测到 prompt_handler 后,会在生成的 get_info() 中同时 enable_tools()enable_prompts();而 prompt_handler 检测到 tool_handler 存在后,会跳过自己生成 get_info() 以避免冲突。这一设计无需改动任何已有代码逻辑,侵入性最小。

huacnlee and others added 30 commits April 16, 2026 17:09
## Summary

- Add a 5-second force-exit fallback in `shutdown_signal()` so
SSE/streaming connections that linger indefinitely don't block port
reuse when restarting the server
- Reduce graceful shutdown timeout from 10s to 5s to align with the
force-exit window

## Test plan

- [ ] Start the server and connect an SSE client
- [ ] Send SIGINT — verify the server exits within 5 seconds even with
an active SSE connection
- [ ] Verify a new instance can bind to the same port immediately after

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Summary

- Add `GET /mcp/tools.json` endpoint (no auth required) that returns all
registered MCP tool names and descriptions as JSON
- Expose `tool_router` with `pub(crate)` visibility via
`#[tool_router(vis = "pub(crate)")]`
- Add `tools::list_tools()` public helper backed by
`ToolRouter::list_all()`
- Log tool count and URL on server startup

## Test plan

- [ ] Start server and verify startup log shows `tools available count=N
url=.../mcp/tools.json`
- [ ] `curl http://localhost:8000/mcp/tools.json` returns JSON with
`tools` array containing name/description for each tool

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Config always enables overnight trading
- Remove LONGBRIDGE_LANGUAGE (determined by Accept-Language header)
- Remove LONGBRIDGE_ENABLE_OVERNIGHT (always on)
- Remove RUST_LOG from README
- McpContext bundles token + language, methods create_config/create_http_client
- Language from Accept-Language header sets SDK Config.language
- enable_overnight always on
- Single struct param makes future extensions backward-compatible
…emium, statement_list, and security_list

- financial_report: add required `kind` param (IS/BS/CF/ALL, default ALL)
- valuation: add required `indicator` (default pe) and `range` (default 1) params
- ah_premium: add `AhPremiumParam` with `period` and `count`; map to `line_type`/`line_num`
- statement_list: replace broken HTTP endpoint with SDK AssetContext.statements()
- security_list: clarify that `category` must be "Overnight" in param doc and tool description
- broker_holding: add period param (rct_1/rct_5/rct_20/rct_60, default rct_1)
- profit_analysis: merge summary + sublist into single response, matching terminal behavior
…trade_sessions, calendar category, alert frequency, and statement sections
hogan-yuan and others added 28 commits May 15, 2026 22:52
## Summary

Improves all 133 Chinese tool descriptions in zh-CN and zh-HK locale
files.

- Average description length: **36 → 90 chars**
- Added return field names in Chinese (最新价, 成交量, 除权日, etc.)
- Added parameter examples and constraints
- zh-HK uses consistent Traditional Chinese (繁體)

## Verification
- `cargo test locales_match_live_tool_schema` ✅

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## Summary

- `today_executions` and `history_executions` now include a `side` field
(Buy/Sell) by concurrently fetching the corresponding orders and joining
by `order_id`
- `order_detail` already serializes the full SDK struct, so `history`
(execution records) is automatically included in the response

## Test plan

- [ ] `today_executions` — verify `side` field appears in the result
- [ ] `history_executions` — verify `side` field appears in the result
- [ ] `order_detail` — verify `history` array is present with
status/time/price/quantity/msg

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…json (#51)

## Summary

### New files
- **`data/tools_app_id.json`** — Maps each MCP tool to its `id` in
`open_api_entries.json`. 111/133 tools matched; 22 WebSocket-only
QuoteContext tools have no HTTP endpoint (app_id: null)
- **`data/scopes.json`** — Moved from root to `data/`

### Changes
- **`/mcp/tools.json`** HTTP endpoint now includes `app_id` per tool
(null for WebSocket-only tools)
- **`src/auth/mod.rs`** — Merges `app_id` from `data/tools_app_id.json`
into the tools.json response at startup
- **`data/scopes.json`** — Added `General` scope (id=0) containing 95
tools not covered by scopes 4/6/10/11

### Scope breakdown
| id | name | tools |
|----|------|-------|
| 0 | General | 95 |
| 4 | Watchlist | 5 |
| 6 | Account & Positions | 16 |
| 10 | Trade Order Lookup | 9 |
| 11 | Trade Execution | 8 |

### Note
`app_id` is only returned by the `/mcp/tools.json` HTTP endpoint. The
MCP protocol `tools/list` response does **not** include `app_id`
(verified).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## Summary

- `calc_indexes` tool was returning raw unscaled Greek values that
differ from what the Longbridge app displays
- Apply the same normalization as `longbridge-terminal` CLI before
returning results:
- **theta**: raw value is annualized (×252) → divide by 252 for
per-trading-day value
- **vega**: raw value is scaled by 100 → divide by 100 for
per-1%-IV-change value
- **rho**: raw value is scaled by 100 → divide by 100 for
per-1%-rate-change value

## Test plan

- [ ] Start MCP server locally and call `calc_indexes` with `theta`,
`vega`, `rho` fields
- [ ] Verify returned values match the Longbridge app display (same as
`longbridge calc-index` CLI output)

Fixes longbridge/developers#1014

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
… top movers, rank (#49)

## Summary

Adds 14 new tools across 5 domains, all verified against live API.

### Shareholders
| Tool | Endpoint |
|------|----------|
| `shareholder_top` | `GET /v1/quote/shareholders/top` |
| `shareholder_detail` | `GET /v1/quote/shareholders/holding` |

### Valuation Comparison
| Tool | Endpoint |
|------|----------|
| `valuation_comparison` | `GET /v1/quote/compare/valuation` |

### Short Selling
| Tool | Endpoint |
|------|----------|
| `short_positions` | `GET /v1/quote/short-positions/hk` + `/us`
(auto-routed) |
| `short_trades` | `GET /v1/quote/short-trades/hk` + `/us` (auto-routed)
|

### Market Data
| Tool | Endpoint |
|------|----------|
| `top_movers` | `POST /v1/quote/market/stock-events` |
| `rank_categories` | `GET /v1/quote/market/rank/categories` |
| `rank_list` | `GET /v1/quote/market/rank/list` |

### Stock Screener
| Tool | Endpoint |
|------|----------|
| `screener_recommend_strategies` | `GET
/v1/quote/screener/strategies/recommend` |
| `screener_user_strategies` | `GET /v1/quote/screener/strategies/mine`
|
| `screener_strategy` | `GET /v1/quote/screener/strategy` |
| `screener_search` | `POST /v1/quote/screener/search` |
| `screener_indicators` | `GET /v1/quote/screener/indicators` |

Also includes:
- `security_list`: remove US-only restriction, add `page`/`count`
pagination, support HK/CN/SG

### screener_search design
Two modes — AI can use either without calling intermediate tools:
- **Mode A** (`strategy_id`): auto-fetches saved strategy and builds
filters
- **Mode B** (`conditions`): `["pettm:10:20", "roe:15:", "divyld:3:"]` —
`filter_` prefix added automatically, `returns[]` auto-built from
condition keys
- `extra_returns`: display-only columns that don't act as filters
(verified: API supports keys in `returns[]` without requiring them in
`filters[]`)
- `sort_by_key` / `sort_order`: key-name sorting instead of opaque
numeric index

### Bugs fixed during review
All found by testing against live API:

| Fix | Detail |
|-----|--------|
| `screener_search` key names | ~half the documented keys were wrong
(`pb`→`pbmrq`, `ps`→`psttm`, `netprofitgrowthrate`→`netincomegrowthyoy`,
`debtassetratio`→`la`, `eps`→`epsttm`, `balance`→`group_balance`,
`close`→`prevclose`, `grossmargin`/`currentratio` don't exist) |
| `marketcap` unit | unit is 亿 for A/HK, documented explicitly |
| `shareholder` position param | missing `position=detail` (was
returning entry-level only) |
| `rank_list` params | missing `market` and `size` params |
| `top_movers` `next_params` | was sending `null`, changed to `{}` |
| `top_movers` timestamps | `events[].timestamp` was raw Unix; now
converted to RFC3339 via `http_post_tool_unix` |
| `shareholder_detail` description | wrong shape (`accum_buy/sell` are
in `tradings[]`, not a separate `holding_summary`) |
| `short_trades` / `short_positions` rate | `rate` is a decimal ratio
(0.4173 = 41.73%), now documented |
| `market` optional in Mode A | `screener_search` `market` changed to
`Option<String>` |
| `security_list` US-only restriction | removed, now supports HK/CN/SG |

### Other
- Sync `data/IX.csv` from longbridge-terminal (648 entries)
- Remove `app_id` from tools metadata and `tools_app_id.json`
- Add Zed `context_servers` config to README
- zh-CN + zh-HK locale entries for all new/changed tools

## Test plan
- [x] `cargo build` ✅
- [x] `cargo clippy --all-features --all-targets` ✅ (0 warnings)
- [x] `cargo test` ✅ (54 passed)
- [x] All 14 new tools called against live API ✅
- [x] `screener_search` full parameter coverage: Mode A, Mode B
(conditions + extra_returns + sort_by_key + sort_order + page + size),
US + HK markets ✅

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## Summary

Version bump `0.3.2` → `0.4.0` following the merge of #49.

Minor version increment (0.3 → 0.4) reflects the addition of 14 new
tools and associated changes.

### Changes
- `Cargo.toml`: `0.3.2` → `0.4.0`
- `server.json`: version, OCI image tag (`ghcr.io/...:0.4.0`), tool
count `133 → 145`, category counts updated, localized descriptions
updated
- `README.md`: tool count `133 → 145`, category count `12 → 13`, add
screener category

### New tool breakdown (133 → 145)

| Category | Before | After | New tools |
|----------|--------|-------|-----------|
| fundamental | 19 | 22 | `shareholder_top`, `shareholder_detail`,
`valuation_comparison` |
| market | 10 | 14 | `short_trades`, `top_movers`, `rank_categories`,
`rank_list` |
| screener | — | 5 | `screener_recommend_strategies`,
`screener_user_strategies`, `screener_strategy`, `screener_search`,
`screener_indicators` |

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
https://modelcontextprotocol.io/specification/2025-11-25/server/prompts

Generated by Endless task #8.

Co-authored-by: Huacnlee Li Huashun <huacnlee@longbridge-inc.com>
…ket param (#56)

## Summary

Migrates all screener MCP tools to `/v1/quote/ai/screener/*` endpoints
per [longbridge-terminal PR
#217](longbridge/longbridge-terminal#217).

### Endpoint changes
| Tool | Old | New |
|------|-----|-----|
| `screener_recommend_strategies` | `GET
/v1/quote/screener/strategies/recommend` | `GET
/v1/quote/ai/screener/strategies/recommend?market=` |
| `screener_user_strategies` | `GET /v1/quote/screener/strategies/mine`
| `GET /v1/quote/ai/screener/strategies/mine?market=` |
| `screener_strategy` | `GET /v1/quote/screener/strategy?id=` | `GET
/v1/quote/ai/screener/strategy/{id}` |
| `screener_search` (Mode A internal) | `GET
/v1/quote/screener/strategy?id=` | `GET
/v1/quote/ai/screener/strategy/{id}` |
| `screener_search` (POST) | `POST /v1/quote/screener/search` | `POST
/v1/quote/ai/screener/search` |
| `screener_indicators` | `GET /v1/quote/screener/indicators` | `GET
/v1/quote/ai/screener/indicators` |

### New parameters
- `screener_recommend_strategies` / `screener_user_strategies`: optional
`market` param (US/HK/CN/SG, default US)

### Critical fix included
The AI strategy endpoint changed response shape. Without the fix, Mode A
silently returns all stocks unfiltered:
- **Old**: `{groups: [{indicators: [{key, min, max, id}]}]}`
- **New**: `{market, filter: {filters: [{key, min, max, tech_values}]}}`

Mode A now reads `strategy["filter"]["filters"][]` and preserves
`tech_values` from the API response.

### Other
- `page` changed to 0-indexed (match terminal PR #217)
- `screener_strategy` description updated to reflect new response shape
- Removed implementation-detail wording ("AI endpoint") from user-facing
descriptions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## Improvements

### Stock Screener
- **`screener_recommend_strategies`** / **`screener_user_strategies`**:
added `market` param (US/HK/CN/SG, default US)
- **`screener_search`** Mode B: `conditions[]` accepts filter objects
`{key, min, max, tech_values}` — tech_values supported for technical
indicators (MACD/RSI/KDJ/BOLL)
- **`screener_search`**: every result now includes 7 default columns —
`prevclose`, `prevchg`, `marketcap`, `salesgrowthyoy`, `pettm`, `pbmrq`,
`industry`
- **`screener_indicators`**: exposes `tech_values` schema per technical
indicator (e.g. `macd_day`: category = goldenfork/deadcross/upzero,
period = day/week)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## Summary
- `next_params` was typed as `Option<serde_json::Value>`, causing the
JSON Schema to emit only a
  description with no `type` field
- Inspected the real API response: `next_params` is always `{"visited":
["id1", "id2", ...]}`, so
defined a concrete `TopMoversNextParams { visited: Vec<String> }` struct
- Schema now correctly emits `type: object` with a fully described
`visited: string[]` property

  ## Test plan
  - [ ] `cargo build` passes
- [ ] `GET /mcp/tools.json` shows `$defs/TopMoversNextParams` with
`type: object` and `visited` field
- [ ] Pagination works correctly when passing `next_params` from a
previous response

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Bump version from 0.4.1 to 0.4.2.

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## Summary
- Bump version from 0.4.2 to 0.4.3 in `Cargo.toml` and `server.json`
- Fix `top_movers` `next_params` schema: replace `$ref`/`anyOf` with an
inline open-object (`type: object, additionalProperties: true`)
- Add Chinese parameter descriptions (`properties`) for 9 tools that
were missing them in `zh-CN` and `zh-HK` locales: `rank_list`,
`screener_indicators`, `screener_strategy`, `screener_user_strategies`,
`shareholder_detail`, `shareholder_top`, `short_positions`,
`short_trades`, `valuation_comparison`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Combines #61 and #62 with CI fixes.

## Changes

### fix(schema): strip null from input schema type arrays
schemars generates `["string", "null"]` for `Option<T>` fields. MCP
convention expresses optionality via absence from `required`, not
nullable types. Post-process all tool `inputSchema`s to convert
`["string","null"]` → `"string"` etc. Override
`ServerHandler::list_tools()` so both the MCP protocol path and
`/mcp/tools.json` go through the same transform.

### fix(tools): anomaly missing size param returns empty results
`/v1/quote/changes` requires a `size` parameter to return data. The MCP
implementation only sent `market` and `category=0`, omitting `size` —
causing the backend to always return `{"changes":[]}`. Default `size=50`
(max 100), matching the CLI. Also adds optional `symbol` filtering and
corrects the description (`changes[]` not `items[]`).

### fix: clippy
- Collapse nested `if let` in `strip_null_from_type_arrays`
(collapsible_if)
- Remove unused `MarketParam` from `market.rs` (replaced by
`AnomalyParam`)

## Closes
- #61
- #62

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…om Host header (#64)

## Summary

Two related fixes for multi-domain / path-flexible deployments:

### 1. Serve MCP at root path (`/`) in addition to `/mcp`

Deployments where the gateway routes the domain root directly (e.g.
`https://mcp.example.com/`) received HTTP 404 because the MCP handler
was only mounted at `/mcp`. A second auth-wrapped
`StreamableHttpService` is now registered via `fallback_service` so both
`/mcp` and `/` work. Existing explicit routes (`/.well-known/*`,
`/health`, `/metrics`, `/mcp/tools.json`) continue to take precedence.

### 2. Dynamic `resource` URL derived from request `Host` header

The `/.well-known/oauth-protected-resource` `resource` field and
`WWW-Authenticate` header previously returned the fixed `--base-url`
value, which broke when the same binary is reachable via multiple
domains. Now `resource_url_from_headers()` reads:
- `Host` header → hostname
- `X-Forwarded-Proto` header → scheme (falls back to the scheme in
`--base-url` so local HTTP deployments without a proxy still work)

### 3. `data/scopes.json`: add `key` field + trailing newline

## Test plan
- [ ] `POST /mcp` → 401 (endpoint exists, auth required)
- [ ] `POST /` → 401 (endpoint exists, auth required)
- [ ] `GET /.well-known/oauth-protected-resource` → 200, `resource`
matches request Host
- [ ] `GET /health` → 200 (not captured by fallback)
- [ ] Claude Code connects to `https://mcp.longbridge.xyz/` without 404

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Bump version from 0.4.4 to 0.4.5.

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## Problem

When a reverse proxy routes `openapi.longbridge.xyz/mcp` to the backend
at `mcp.longbridge.xyz/`, the `Host` header seen by the backend is
`mcp.longbridge.xyz` (internal), not `openapi.longbridge.xyz`
(external). The `resource` field in
`/.well-known/oauth-protected-resource` returned the internal hostname,
causing MCP clients to reject the OAuth flow with:

```
Protected resource https://mcp.longbridge.xyz does not match expected https://openapi.longbridge.xyz/mcp
```

## Fix

Check `X-Forwarded-Host` before `Host` when building the resource URL.
The reverse proxy sets `X-Forwarded-Host: openapi.longbridge.xyz` to
communicate the original external hostname.

| Header present | Result |
|----------------|--------|
| `X-Forwarded-Host: openapi.longbridge.xyz` |
`https://openapi.longbridge.xyz` |
| `Host: mcp.longbridge.xyz` only | `https://mcp.longbridge.xyz` |
| Neither | falls back to `--base-url` |

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## Summary

`/v1/quote/finance_calendar` is paginated — a multi-day query only
returns the first page plus a `next_date` cursor. The previous
implementation made a single request, silently dropping all events on
pages 2+. This caused real data loss: e.g. a `report / US / 2026-05-23 →
2026-05-30` query was missing CRM.US, PDD.US, MRVL.US (reported
externally in longbridge/developers#1045).

**Changes:**
- Auto-follow `next_date` cursor: use the returned `next_date` as the
new `date` parameter on subsequent requests (keeping `date_end`
unchanged), matching CLI behaviour
- Pass `next=later`, `count=100`, `offset=0` on every request — aligned
with CLI
- Merge all pages with dedup by event `id` (`datetime+market` fallback
for id-less entries); date buckets sorted chronologically
- Hard cap of 20 pages to prevent runaway loops (same limit as CLI)
- Add same-cursor guard to break if `next_date` doesn't advance
- Create a fresh HTTP client per page to avoid `SendRequest` errors from
connection reuse
- Tool description (EN / zh-CN / zh-HK) now advises keeping date range ≤
2 weeks
- Bump version to 0.4.7

## Test plan

- [x] 12 unit tests: `next_date_of` edge cases, `merge_pages` dedup +
date sorting, pagination boundary conditions
- [x] `cargo test` — 70 tests pass
- [x] `cargo clippy --all-features --all-targets` — no warnings
- [x] Local smoke-test: `report / US / 2026-06-02 → 2026-06-06` returns
4 unique date buckets, no duplicates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## What

Make MCP-originated upstream requests identifiable by the Longbridge
backend via a standards-compliant `User-Agent`, and pin the SDK to a
published crate version.

## Why

The backend previously could not tell that a request came from this MCP
server, nor which client (Claude Code, Codex, …) drove it. CLI already
tags itself with `longbridge-cli/<version>`; MCP had no equivalent.

## How

- **`McpContext::user_agent()`** builds an RFC 9110 product-token chain:
`<client-ua> longbridge-mcp/<version>`, e.g. `claude-code/2.1.89 (cli)
longbridge-mcp/0.4.6`. Acting as a proxy, we append our token to the
client's UA rather than inventing a custom header, so a single standard
field carries both identities. Falls back to just
`longbridge-mcp/<version>` when the client sends no UA (e.g. Codex
currently sends none).
- Applied on both paths: `create_http_client` (REST) and `create_config`
(Context / WebSocket upgrades), mirroring `longbridge-cli`.
- The client's raw `User-Agent` is no longer forwarded as-is
(`SKIP_FORWARD_HEADERS`); it is captured in `extract_context` and folded
into the chain.
- **Dependency**: switch `longbridge` from a git dependency to the
published crate **`4.2.2`**, which provides the `Config::header` builder
used above.

## Verification

- `cargo clippy --all-features --all-targets`: no issues.
- `cargo test`: 58 passed, including a new end-to-end test
(`upstream_request_carries_synthesized_user_agent`) that redirects the
SDK base URL to a local echo server via `LONGBRIDGE_HTTP_URL` and
asserts the real on-the-wire `User-Agent`.
- Impact of the SDK bump (4.0.5 → 4.2.2) is contained: most tools use
the REST path (`create_http_client`) and are unaffected by the
typed-struct changes; only quote/trade/content/statement use high-level
Contexts and all compile + test green. `alert` uses REST, so the 4.1.0
`AlertContext` breaking change does not affect it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tool names

Revert all changes to src/tools/mod.rs that were not part of task #8:
- Re-add mod atm, mod ipo, mod search declarations
- Restore 24 deleted tool handler functions
- Restore header-forwarding infrastructure (extra_headers, collect_headers,
  SKIP_FORWARD_HEADERS)
- Restore JWT utility functions (account_channel, decode_jwt_account_channel,
  base64url_decode)
- Restore tool_result structured_content object filter
- Restore all full tool descriptions
- Restore collect_headers tests

The only intentional changes kept from efb5edf are the model type imports
(GetPromptRequestParams, GetPromptResult, ListPromptsResult, PaginatedRequestParams,
prompt_handler) and the #[prompt_handler] attribute on impl ServerHandler.

Also fix five incorrect tool names in src/prompts.rs prompt text strings:
- financial_summary -> financial_report + valuation
- option_chain -> option_chain_expiry_date_list + option_chain_info_by_date
- estimate_max_buy_quantity -> estimate_max_purchase_quantity
- market_indices -> quote
- market_turnover_anomaly -> anomaly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…raded) (#70)

## What

The `quote` tool now cleans up its extended-hours (pre/post/overnight)
output.

## Why

`quote` serialized the SDK `SecurityQuote` verbatim. For sessions that
have not traded, the upstream returns a **zero-filled** object
(`last_done == 0`), which AI callers read as a real **-100%** move —
exactly the kind of after-close misjudgement we want to avoid for the
most-called tool. The keys were also verbose (`*_quote`).

## How

`normalize_extended_sessions` post-processes the serialized value:

- **Drop the `_quote` suffix**: `post_market` / `overnight` /
`pre_market` (in that order).
- **Null out untraded sessions**: a session with `last_done <= 0` (or
missing) becomes `null` instead of a zero-filled object. Handles both
string- and number-serialized `Decimal`.

This mirrors the corresponding CLI change (longbridge-terminal#228).

## Verification

`cargo clippy --all-features --all-targets`: clean. Two unit tests cover
the rename, the null-out, key ordering, and both `Decimal` serialization
shapes:

```
tools::quote::tests::normalizes_sessions_string_last_done ... ok
tools::quote::tests::normalizes_sessions_numeric_last_done ... ok
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The attribute was dropped when resolving merge conflicts in
src/tools/mod.rs during 'Merge branch main into endless/task-8'.
The import (use rmcp::prompt_handler) remained but the attribute
itself was missing, so list_prompts/get_prompt were never wired up
and the prompts capability was not advertised in get_info().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace 5 generic prompts with 10 prompts organised by capability domain:
market_data, derivatives, fundamentals, research, portfolio_account,
orders_trading, ipo, quant, watchlist, content.

Each prompt covers the key tools for its domain and references only
registered tool names. Shared argument types (SymbolArgs, TradeArgs,
MarketArgs, QuantArgs) replace one-off structs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@hogan-yuan
hogan-yuan force-pushed the main branch 2 times, most recently from 8284189 to c33ad40 Compare July 6, 2026 02:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants