Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,26 @@ jobs:

- name: Catalog vs FinMind-Doc sync check
run: python scripts/check_doc_sync.py --doc ./FinMind-Doc

mcpb:
# Claude Desktop 擴充套件(.mcpb):用官方 CLI 驗證 + 打包,再模擬 host 解壓、
# 以 uv 啟動並走一次 MCP 握手。--dev 讓相依指向本 PR 的原始碼,而非 PyPI 舊版。
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # --dev 會從原始碼 build 本套件,hatch-vcs 需要 tags

- name: Install uv
uses: astral-sh/setup-uv@v5

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Build finmind.mcpb
run: python3 scripts/build_mcpb.py --dev

- name: Install & launch like Claude Desktop
run: python3 scripts/check_mcpb.py
41 changes: 41 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ name: Publish to PyPI
# - git tag v0.1.0
# - git push origin v0.1.0 ← 推 tag 就觸發;hatch-vcs 從 tag 算出版本 0.1.0
# 自動 build → 上傳 PyPI(PyPI 版本號不可重複,所以每次用新 tag)
# - 上 PyPI 之後,mcpb job 會打包 Claude Desktop 擴充套件 finmind.mcpb(釘同版本),
# 附加到該 tag 的 GitHub Release。下載連結固定為
# https://github.com/FinMind/FinMind-MCP/releases/latest/download/finmind.mcpb

on:
push:
Expand Down Expand Up @@ -66,3 +69,41 @@ jobs:

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

mcpb:
# 必須在 PyPI 發佈之後:.mcpb 裡的 pyproject 釘 finmind-mcp==<tag>,
# 使用者安裝時由 Claude Desktop 用 uv 從 PyPI 下載。
needs: pypi-publish
if: github.ref_type == 'tag'
runs-on: ubuntu-latest
permissions:
contents: write # 建立 / 更新 GitHub Release
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Build finmind.mcpb (pinned to this tag)
run: python3 scripts/build_mcpb.py "$GITHUB_REF_NAME"

- name: Install & launch like Claude Desktop (from PyPI)
# PyPI 剛發佈時 index 可能還沒更新,失敗就等一下重試
run: |
for i in 1 2 3 4 5; do
python3 scripts/check_mcpb.py && exit 0
echo "retry $i: waiting for PyPI index"; sleep 60
done
exit 1

- name: Attach to GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1 \
|| gh release create "$GITHUB_REF_NAME" --verify-tag --generate-notes --title "$GITHUB_REF_NAME"
gh release upload "$GITHUB_REF_NAME" dist/finmind.mcpb --clobber
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pipx install finmind-mcp

| Host | 安裝指引 |
|---|---|
| Claude Desktop | [install/claude-desktop.md](install/claude-desktop.md) |
| Claude Desktop | **一鍵安裝**:下載 [finmind.mcpb](https://github.com/FinMind/FinMind-MCP/releases/latest/download/finmind.mcpb) 點兩下 → 詳見 [install/claude-desktop.md](install/claude-desktop.md) |
| Claude Code | [install/claude-code.md](install/claude-code.md) |
| Cursor | [install/cursor.md](install/cursor.md) |
| Windsurf | [install/windsurf.md](install/windsurf.md) |
Expand Down Expand Up @@ -52,6 +52,7 @@ FinMind-MCP/
├── knowledge/ # SSOT markdown(dataset、行為規則、錯誤腳本、Q&A)
├── chatgpt/ # Custom GPT artifacts(OpenAPI + build script)
├── install/ # 各 host 安裝指引
├── mcpb/ # Claude Desktop 擴充套件(.mcpb)的 manifest 與進入點
└── docs/ # 設計 spec 與實作 plan
```

Expand Down Expand Up @@ -79,6 +80,15 @@ git tag v0.1.0
git push origin v0.1.0 # → CI 自動 build + 發 PyPI
```

PyPI 發佈成功後,同一個 workflow 會打包 Claude Desktop 擴充套件 `finmind.mcpb`(`finmind-mcp` 釘在同一版本)並附加到該 tag 的 GitHub Release,下載連結 `releases/latest/download/finmind.mcpb` 會自動指向最新版。本機打包測試:

```bash
python3 scripts/build_mcpb.py --dev # 需要 Node.js;--dev 用本機原始碼
python3 scripts/check_mcpb.py # 模擬 Claude Desktop 解壓、啟動、MCP 握手
```

新增 tool 時記得同步 `mcpb/manifest.json` 的 `tools`(`tests/test_mcpb.py` 會擋)。

`knowledge/` 由 hatch `force-include` 打包進 wheel(`finmind_mcp/_knowledge/`),server runtime 直接讀、不用 compile;發版後用戶 `uvx finmind-mcp` / `pipx install finmind-mcp` 即抓到新版。

> 首次需在 PyPI 設好 trusted publisher(project `finmind-mcp` / repo `FinMind/FinMind-MCP` / workflow `publish.yml` / env `pypi`)。
Expand Down
38 changes: 29 additions & 9 deletions install/claude-desktop.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
# Claude Desktop

在 Claude Desktop 桌面應用程式啟用 FinMind MCP server(本機 stdio 模式),讓 Claude 直接呼叫 FinMind 金融資料。
在 Claude Desktop 桌面應用程式啟用 FinMind MCP server,讓 Claude 直接呼叫 FinMind 金融資料。

## 安裝套件
先依 [token 取得指引](../knowledge/token-guide.md) 取得 FinMind Token,再選擇以下任一方式安裝。

請先安裝 `finmind-mcp`(擇一即可):
## 方式一:一鍵安裝擴充套件(推薦)

不需要安裝 Python、不需要編輯設定檔,Windows / macOS 步驟相同:

1. 下載 **[finmind.mcpb](https://github.com/FinMind/FinMind-MCP/releases/latest/download/finmind.mcpb)**
2. 在下載的檔案上**點兩下**,Claude Desktop 會跳出安裝視窗(若沒有反應:開啟 Claude Desktop →「設定」→「擴充功能(Extensions)」,把檔案拖進視窗)
3. 按「安裝」,在「FinMind Token」欄位貼上 token,確認啟用

第一次使用時 Claude Desktop 會自動下載執行環境,約需數十秒。

> 更新版本:重新下載上面的連結再安裝一次即可。

## 方式二:手動編輯設定檔

適合想自行管理套件版本的使用者。

### 安裝套件

```bash
pipx install finmind-mcp # 推薦:長駐安裝
Expand All @@ -14,7 +30,7 @@ uvx finmind-mcp --help # 用 uv 即時跑,不安裝

> **Windows 使用者**:`pipx`/`uvx` 的安裝方式、token 設定與常見 PATH 問題,請先看 [Windows 安裝指引](windows.md)。

## 設定
### 設定

依您的作業系統,編輯對應的設定檔:

Expand All @@ -39,12 +55,16 @@ uvx finmind-mcp --help # 用 uv 即時跑,不安裝
}
```

## 取得 Token

請先依 [token 取得指引](../knowledge/token-guide.md) 取得 FinMind Token,並設定環境變數 `FINMIND_TOKEN`(或填入上方設定)。

## 驗證

重啟 Claude Desktop,在對話框輸入「列出 FinMind 可用的 dataset」,應該看到工具圖示出現並回傳 dataset 清單。
(方式二需先完全結束並重開 Claude Desktop)在對話框輸入「列出 FinMind 可用的 dataset」,應該看到工具圖示出現並回傳 dataset 清單。

![](../../docs/images/install/claude-desktop.png)

## 常見問題

### 擴充套件顯示無法連線

1. 確認 token 有貼對:「設定」→「擴充功能」→ FinMind →「設定」重新貼上。
2. 第一次啟動需要下載執行環境,網路較慢時可能逾時;等一分鐘後關閉再開啟擴充套件。
3. 仍失敗時,安裝 [uv](https://docs.astral.sh/uv/getting-started/installation/)(Windows:PowerShell 執行 `winget install --id astral-sh.uv -e`),然後從系統匣(工作列右下角)圖示按右鍵**結束** Claude Desktop 再重開。
8 changes: 7 additions & 1 deletion install/windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

各 host 的安裝文件([Claude Desktop](claude-desktop.md)、[Cursor](cursor.md) 等)中的指令以 macOS / Linux 為例。Windows 使用者請先照本頁完成「安裝套件」與「設定 Token」,再回到對應 host 的文件貼設定;本頁最後整理了 Windows 常見問題(尤其是 host 顯示找不到 `finmind-mcp` 指令的狀況)。

> **使用 Claude Desktop?** 直接用[一鍵安裝擴充套件](claude-desktop.md#方式一一鍵安裝擴充套件推薦):下載 `finmind.mcpb` 點兩下、貼上 token 就完成,**不需要本頁任何步驟**。本頁適用於 Cursor、Windsurf 等其他工具,或想手動設定的使用者。

以下指令都在 **PowerShell** 執行(開始功能表搜尋「PowerShell」開啟即可,不需系統管理員權限)。

## 1. 安裝套件
Expand Down Expand Up @@ -77,7 +79,11 @@ setx FINMIND_TOKEN "your-token-here"

### host 啟動 server 失敗(找不到指令 / `ENOENT`)

最常見的 Windows 問題:`finmind-mcp` 或 `uvx` 在 PowerShell 跑得動,但 Claude Desktop 這類**圖形介面應用程式讀到的 PATH 和終端機不同**(例如安裝 uv 之前就已開著、或 PATH 是安裝程式事後才寫入的),host 啟動 MCP server 這個子行程時就找不到指令,顯示連線失敗。解法是把設定裡的 `command` 改成執行檔的**完整路徑**。
最常見的 Windows 問題:`finmind-mcp` 或 `uvx` 在 PowerShell 跑得動,但 Claude Desktop 這類**圖形介面應用程式讀到的 PATH 和終端機不同**(例如安裝 uv 之前就已開著、或 PATH 是安裝程式事後才寫入的),host 啟動 MCP server 這個子行程時就找不到指令,顯示連線失敗。

**先試最簡單的**:多半只是該程式在安裝 uv 之前就開著,還沒讀到新的 PATH。把它**完全結束**(Claude Desktop 要到系統匣圖示按右鍵選結束,見下一題)再重開,通常就好了。

仍然不行,再把設定裡的 `command` 改成執行檔的**完整路徑**:

**① 查實際位置**(在 PowerShell 執行;要打 `where.exe`,單打 `where` 在 PowerShell 是別的指令):

Expand Down
4 changes: 4 additions & 0 deletions mcpb/.mcpbignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.venv/
uv.lock
__pycache__/
*.pyc
Binary file added mcpb/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions mcpb/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"manifest_version": "0.4",
"name": "finmind",
"display_name": "FinMind",
"version": "0.0.0",
"description": "台股/期權/總經/全球金融資料 MCP server(FinMind)",
"long_description": "讓 Claude 直接查詢 FinMind 的台股、期貨選擇權、總體經濟與全球金融資料。安裝時輸入 FinMind Token 即可使用,不需要另外設定 Python 或編輯設定檔。",
"author": {
"name": "FinMind",
"email": "finmind.tw@gmail.com",
"url": "https://finmindtrade.com"
},
"repository": {
"type": "git",
"url": "https://github.com/FinMind/FinMind-MCP"
},
"homepage": "https://github.com/FinMind/FinMind-MCP",
"documentation": "https://github.com/FinMind/FinMind-MCP/blob/master/install/claude-desktop.md",
"support": "https://github.com/FinMind/FinMind-MCP/issues",
"icon": "icon.png",
"server": {
"type": "uv",
"entry_point": "src/server.py",
"mcp_config": {
"command": "uv",
"args": ["run", "--directory", "${__dirname}", "src/server.py"],
"env": {
"FINMIND_TOKEN": "${user_config.finmind_token}"
}
}
},
"tools": [
{ "name": "query_dataset", "description": "查詢 FinMind dataset(股價、財報、籌碼、期權、總經等)" },
{ "name": "list_datasets", "description": "列出可查詢的 dataset 與所需會員等級" },
{ "name": "get_stock_info", "description": "查詢股票代號、名稱與產業別" },
{ "name": "query_trading_daily_report", "description": "查詢個股單日分點進出" },
{ "name": "get_pricing", "description": "查詢 FinMind 會員方案與價格" }
],
"user_config": {
"finmind_token": {
"type": "string",
"title": "FinMind Token",
"description": "到 https://finmindtrade.com/analysis/#/account/user 登入,在「API Token」分頁按「複製」後貼上。",
"required": true,
"sensitive": true
}
},
"compatibility": {
"platforms": ["darwin", "win32", "linux"]
},
"keywords": ["finmind", "finance", "taiwan-stock", "futures", "options"],
"license": "Apache-2.0"
}
10 changes: 10 additions & 0 deletions mcpb/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Claude Desktop 擴充套件(.mcpb)用的執行環境定義。
# Claude Desktop 啟動時以 `uv run` 依此檔自動準備 Python 與相依套件,使用者不用自己裝。
# finmind-mcp 的版本由 scripts/build_mcpb.py 在打包時改成與發版 tag 相同,不要手動改。
[project]
name = "finmind-mcpb"
version = "0.0.0"
requires-python = ">=3.10"
dependencies = [
"finmind-mcp==0.0.0",
]
6 changes: 6 additions & 0 deletions mcpb/src/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Claude Desktop 擴充套件(.mcpb)的進入點:直接啟動 PyPI 上的 finmind-mcp。"""

from finmind_mcp.server import main

if __name__ == "__main__":
main()
82 changes: 82 additions & 0 deletions scripts/build_mcpb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""打包 Claude Desktop 擴充套件 dist/finmind.mcpb。

把 mcpb/ 複製到暫存目錄,將 manifest.json 與 pyproject.toml 的版本改成 VERSION,
再用官方 CLI(@anthropic-ai/mcpb)驗證並打包。需要 Node.js(npx)。

用法:
python scripts/build_mcpb.py 0.0.11 # 指定版本
python scripts/build_mcpb.py # 沒給版本 → 讀環境變數 GITHUB_REF_NAME(發版 tag)
python scripts/build_mcpb.py --dev # CI 用:相依改指向本 repo 原始碼,測尚未發版的改動

finmind-mcp 會被釘在同一個版本,所以該版本必須已經在 PyPI 上
(publish.yml 先上 PyPI 再打包,順序已保證)。
"""

from __future__ import annotations

import json
import os
import re
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
SRC = ROOT / "mcpb"
OUT = ROOT / "dist" / "finmind.mcpb"
MCPB_CLI = "@anthropic-ai/mcpb@2"
VERSION_RE = re.compile(r"^\d+\.\d+\.\d+$")


def resolve_version(args: list[str]) -> str:
raw = args[0] if args else os.environ.get("GITHUB_REF_NAME", "")
version = raw.removeprefix("v")
if not VERSION_RE.match(version):
sys.exit(f"版本需為 X.Y.Z(可帶 v 前綴),收到:{raw!r}")
return version


def stamp(build_dir: Path, version: str, requirement: str) -> None:
manifest_path = build_dir / "manifest.json"
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
manifest["version"] = version
manifest_path.write_text(
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
)

pyproject_path = build_dir / "pyproject.toml"
text = pyproject_path.read_text(encoding="utf-8")
text, n_pin = re.subn(r'"finmind-mcp==[^"]*"', f'"{requirement}"', text)
text, n_ver = re.subn(r'(?m)^version = "[^"]*"$', f'version = "{version}"', text)
if n_pin != 1 or n_ver != 1:
sys.exit("mcpb/pyproject.toml 格式不符,找不到 finmind-mcp 釘版或 version 欄位")
pyproject_path.write_text(text, encoding="utf-8")


def main() -> None:
args = sys.argv[1:]
dev = "--dev" in args
if dev:
version, requirement = "0.0.0", f"finmind-mcp @ {ROOT.as_uri()}"
else:
version = resolve_version(args)
requirement = f"finmind-mcp=={version}"
npx = shutil.which("npx")
if npx is None:
sys.exit("找不到 npx,請先安裝 Node.js")

with tempfile.TemporaryDirectory() as tmp:
build_dir = Path(tmp) / "finmind"
shutil.copytree(SRC, build_dir, ignore=shutil.ignore_patterns(".venv", "uv.lock", "__pycache__"))
stamp(build_dir, version, requirement)
OUT.parent.mkdir(exist_ok=True)
subprocess.run([npx, "-y", MCPB_CLI, "validate", str(build_dir)], check=True)
subprocess.run([npx, "-y", MCPB_CLI, "pack", str(build_dir), str(OUT)], check=True)

print(f"built {OUT.relative_to(ROOT)} ({requirement})")


if __name__ == "__main__":
main()
Loading
Loading