Reported issues for TWSE Open Data (Taiwan Stock Exchange)
Pod holds 16 of 16 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.
Back to TWSE Open Data (Taiwan Stock Exchange).
Most discussed
P3|上游空資料集視為取得失敗
Parent
#66
What to build
已知必定有資料的資料集回一個格式正確的空陣列 [](上游維護、擋流量、前置 nginx 出事時會發生)時,服務不再把它讀成「查無此標的」。目前 fetchJson 只在 body 無法 JSON.parse 時大聲失敗(那是為了擋 2xx + HTML 的上游故障頁),一個合法的 [] 通過所有檢查,於是 twse_etf_snapshot("0050") 回 is_etf: false——而這個錯誤答案會被 cf.cacheTtl 釘在邊緣一小時,期間每個使用者都拿到它。
決策:對 twse_etf_snapshot 依賴的三個資料集常數(DS_FUND/DS_DAY/DS_RANK),0 筆視為取得失敗,fetchDataset 拋錯。既有的 Promise.allSettled → errors → failed() → 第三態機制自動接手,不必新增第二套錯誤路徑。拋錯的路徑不回傳資料,因此錯誤結果不會進邊緣快取——這是選「拋錯」而非「回空陣列
Read the thread · 2026-08-17 · open · outside contributor · 1 comment
P2|定期定額排行抓失敗時不做否定陳述
Parent
#66
What to build
ETFReport/ETFRank(定期定額排行)抓失敗時,twse_etf_snapshot 不再說「不在定期定額排行榜上」。目前那一段是裸 else,而它的兩個兄弟分支——基金基本資料與日成交資訊——都已經用 failed(label) 判斷過再決定要不要做否定陳述。比照日成交資訊那段:抓失敗時改推一句表達「因為上游取得失敗,無法判斷」的 caveat,語氣比照基金基本資料那段。
這是同一類「上游掛了 vs 查無此標的」錯誤的第三次(前兩次的紀錄與動機見 src/core.ts:466-470 的註解)。修的時候把三個分支寫成看得出對稱的形狀——這個 bug 活下來正是因為兩個分支有守衛、第三個沒有,而那件事用讀的看不出來。
不要把「查無」整個拿掉。 一檔 ETF 真的不在榜上是常見且有意義的答案(該資料集只收前段班)。要拿掉的只有「沒查到卻說沒有」。
Acceptance criteria
- 定期定額排行抓失敗時,caveats 不含「不在定期定額排行榜
Read the thread · 2026-08-17 · open · outside contributor · 1 comment
2026-08-23:判讀探針資料、決定 era 收斂、移除探針
Parent
#34(接續 #36)
What to build
讀 #36 那支臨時探針累積的資料,決定要不要做 era 收斂,然後把探針移除。
計時起點:2026-08-09T08:31:41Z(第一筆確證落地的記錄)。
線上自該時刻起跑的是 e7f8275,已含 #42 的 415 gate,所以資料是乾淨的。
⚠️ 判讀規則——動手前先讀完這段
不要數 protocolVersion 是不是 null。
這條規則第一版就寫錯了,而且錯誤的版本一度被測試鎖住(修正見 91a3843)。原本的假設是
「legacy client 不送協定版本標頭」——那是錯的。2025-era 的 streamable-HTTP client
依規範必須在每個請求送 MCP-Protocol-Version,值是 2025-06-18 之類。它們是 legacy,
但這個欄位不是 null。
照 null 去數,會把一整批合規的 2025 client 讀成「零 legacy 流量」,然後盲切
Read the thread · 2026-08-09 · open · outside contributor · 1 comment
量測 client 分佈:臨時探針 + observability 改為文件化寫法
Parent
#34
What to build
維護者現在無法回答「還有沒有只會講 legacy 的 client 在用這個服務」,因此不敢做 era 收斂, 也沒有任何數據能支持那個決定。這張票裝上一支臨時探針開始累積證據。
一、平台設定。 現行 observability 是頂層開關關閉、logs 子項開啟。這個組合的語意
未被 Cloudflare 文件規定——文件那一節只記載頂層的兩個欄位,完全沒有提到 logs /
traces 子物件,也沒有任何一頁說明優先順序。Cloudflare 自家實作有三處獨立證據一致指向
巢狀值勝出、log 確實會落地,但那是未文件化的行為,最終裁決者是閉源的控制平面。改成
頂層明確開啟、traces 維持關閉——同樣的意圖(只開 logs、traces 對這個唯讀服務用不到),
但用文件化的寫法。設定檔註解裡「與儀表板的實際設定對齊、避免每次部署互相覆蓋」這個理由
不適用於本欄位(wrangler 的遠端/本地設定比對刻意排除 observability,因為它的遠端
預設行為與 wrang
Read the thread · 2026-08-08 · closed · outside contributor · 3 comments
協定 era:確認 dual-era 合規、設定快取提示、量測 legacy lane 用量
Problem Statement
證交所 MCP server 上線後,MCP 規範發布了 2026-07-28 修訂版。這是一次破壞性改版:
取消 initialize 交握、取消 session、改由每個請求自帶 _meta envelope,並新增一批
server 端的強制義務(server/discover、每個結果帶 resultType、可快取結果帶
ttlMs/cacheScope)。
維護者的處境是:不知道自己站在哪裡。專案的相依在 PR #32 換成 GA 版 SDK 之後,
協定行為可能已經整個換了一代,但 repo 裡沒有任何一行程式碼、測試或文件提到協定 era,
README 也不宣稱任何修訂版。於是有三個問題同時懸著:
- 合規性未知 —— 服務到底有沒有符合
2026-07-28的強制義務?如果沒有, 對外會表現成「宣稱支援、實際回應壞掉」,失敗模式從明確拒絕退化成安靜地錯。 - 測試看不到 modern lane —— 既有 20 個主 seam 測試送的都是
Read the thread · 2026-08-08 · closed · outside contributor · 2 comments
catalog: 證交所目錄有變動待審
每週自動刷新發現證交所的 swagger 有變動,已把新的 src/catalog.generated.json 推到 chore/catalog-refresh。
已驗證:typecheck、測試、wrangler deploy --dry-run 全數通過。
diff 會顯示證交所新增/移除了哪些資料集或欄位——這就是我們把 catalog 簽入版控的用意。
由 refresh catalog 自動產生(run #2)
Read the thread · 2026-07-31 · closed · external user · 2 comments
Unlock realtime_quote + fix is_etf detection (post-egress-probe)
Follow-up to #1 / PR #2, triggered by the post-deploy egress probe.
Problem Statement
Two things surfaced after deploying v1 and probing the fragile mis.twse.com.tw egress from the real Cloudflare edge:
- The realtime path works.
etf_snapshot("0056", include_realtime=true)returned live quotes (last: 48.19 @ 13:30:00) andwrangler tailshowed every requestOkwith no fetch exceptions. The uncertainty that made the spec defer the standalonetwse_realtime_quotetool is now r
Read the thread · 2026-07-28 · closed · outside contributor · 1 comment
Spec: 重構為 Cloudflare Workers 遠端 MCP server (TypeScript)
Spec: twse-mcp 重構為 Cloudflare Workers 遠端 MCP server
狀態:✅ 已實作並合併(PR #2,squash 到
main)。本內文已同步至 repo 最新 specdocs/spec-workers-migration.md。 由/grill-me+/to-spec綜合對話共識產出;上線後的 realtime 解禁與is_etf修正見 #3。
Problem Statement
現在的 twse-mcp 是本機 stdio server,使用者要用它得先 git clone、建 venv、pip install -e .,再用絕對路徑註冊進 Claude Code/Desktop。散佈摩擦高、每台機器都要重來一遍、絕對路徑一錯就連不上。對想「開箱即用查證交所開放資料」的人來說,安裝這關就先勸退一半。維護者也想驗證這個服務能否用最低維運成本長駐上線給多人共用。
Solution
把 server 改寫成 **TypeScript + Cloudflare
Read the thread · 2026-07-28 · closed · outside contributor · 1 comment
Most recent
三處「安靜地給錯答案」:代號大小寫、定期定額排行的裸 else、上游空資料集
Problem Statement
使用者問一檔真實存在的 ETF,服務會肯定地回答一件錯的事。
三種情形,症狀相同——都不是壞掉,是安靜地給錯答案:
- 使用者(或模型)把代號打成小寫。問
00679b(元大美債20年,真的在上市),twse_etf_snapshot回is_etf: false、name: null、價量全部 null, caveats 還補一句「也可能單純是代號有誤,或該標的不是基金」。 同一個代號用twse_get_dataset查卻查得到。 - 定期定額排行的資料集抓失敗時,服務會說「0050 不在定期定額排行榜上」。 0050 是那張榜上最大的一檔。真相是那份資料集根本沒讀到。
- 上游回一個格式完全正確的空陣列
[](維護、擋流量、或前置 nginx 出事時會發生), 服務會把它讀成「查無此標的」,於是同樣回is_etf: false——而這個錯誤答案 會被邊緣快取釘住一小時,期間每一個使用者都拿到它。
這三件事對呼叫端來說長得一模一樣:
Read the thread · 2026-08-17 · open · outside contributor · 0 comments
catalog: 每週刷新執行失敗
每週的 catalog 刷新執行失敗了,這代表目錄漂移偵測目前是停擺的—— 在修好之前,證交所若新增/移除資料集或欄位,我們不會收到通知。
最常見的原因是上游暫時回了非 JSON(證交所前面的 nginx 擋流量或維護時, 會用 2xx 狀態碼送出一張 HTML 錯誤頁)。腳本已內建三次退避重試, 所以連重試都失敗通常表示問題不是瞬時的。錯誤訊息會附上狀態碼、 content-type 與 body 開頭,先看那三項。
確認上游恢復後,可用 workflow_dispatch 手動重跑,不必等下週一。
由 refresh catalog 自動產生(run #6)
Read the thread · 2026-08-17 · open · external user · 0 comments
catalog: 證交所目錄有變動待審
每週自動刷新發現證交所的 swagger 有變動,已把新的 src/catalog.generated.json 推到 chore/catalog-refresh。
已驗證:catalog 健檢(規模、結構、twse_etf_snapshot 依賴的資料集仍在)、
typecheck、測試(其中有數項直接對真實目錄斷言)、wrangler deploy --dry-run 全數通過。
diff 會顯示證交所新增/移除了哪些資料集或欄位——這就是我們把 catalog 簽入版控的用意。
⚠️ chore/catalog-refresh 是機器人專用分支,每次執行都會被覆寫。要修改請另開分支,別直接推到它上面。
由 refresh catalog 自動產生(run #5)
Read the thread · 2026-08-10 · open · external user · 0 comments
fix: 量測探針對 415 的請求仍寫下全 null 記錄
Parent
#36
What to build
量測探針的 gate 只看 POST + 路徑,所以後續被 415 擋掉的請求仍然會寫下一筆四欄皆 null
的記錄,和真正的裸 legacy 請求位元組相同——正是加這個 gate 想消除的污染。
對真實 workerd 實測:
| 請求 | 狀態 | 是否寫記錄 |
|---|---|---|
POST /mcp,Content-Type: text/plain |
415 | 會,且四欄全 null |
POST /mcp,Origin: https://evil.example.com |
403 | 會,但 origin 有值 → 濾得掉 |
GET /mcp |
405 | 不會 ✓ |
POST /nope |
404 | 不會 ✓ |
正常 POST /mcp |
200 | 會 ✓ |
所以問題只剩 415 那一條路徑。403 那條已經被現有機制處理掉了——探針記 origin 的用意
就是讓瀏覽器來
Read the thread · 2026-08-09 · closed · outside contributor · 0 comments
fix: twse_search_datasets 的 limit 缺下界,負數會靜靜少回一筆
What to build
twse_search_datasets 的 limit 沒有下界也沒有 clamp,於是負數會靜靜地少回一筆。
實測(對真正的進入點、真實的 143 筆目錄):
limit |
回傳筆數 |
|---|---|
| 未給 | 25(預設) |
0 |
0 |
-1 |
142(143 筆少最後一筆) |
-2 |
141 |
-999999 |
0 |
999999 |
143 |
原因是 src/server.ts 的 schema 是 z.number().int().default(25)——沒有 .min()——而
src/core.ts 直接 out.slice(0, limit),負數走的是 JS 的「從尾端算」語意。
這不是安全問題,是正確性問題。 姊妹工具 twse_get_dataset 承諾了「硬上限 200」,
run-1 的負數 limit 之所以是漏洞,是因為它繞過了那個對呼叫端可見的承諾;
`twse_se
Read the thread · 2026-08-09 · closed · outside contributor · 0 comments
把協定 era 的決策寫成 ADR
Parent
#34
What to build
未來讀者看到本專案「跨了一次破壞性的協定改版,卻幾乎沒動程式碼」時,在原始碼裡 找不到任何解釋。這張票補上本專案第一份 ADR。
一、為什麼零協定程式碼就已合規。 2026-07-28 的 server 端強制義務——
server/discover、每個結果的型別欄位、可快取結果的必填快取欄位、capability 宣告——
全部由 SDK 自動履行;era 判定是純 claim-based(請求的 _meta 裡有沒有協定版本這個
保留鍵,就這一個訊號);lane 策略完全由服務進入點所屬的那層相依決定,它強制底層 SDK
進入 modern-only 模式再自行實作 legacy lane。這些在本專案的原始碼裡完全看不出來。
二、公開快取範圍的依據,與它的失效觸發條件。 服務目前公開、不認證,所以公開範圍 是誠實的描述。一旦導入認證,這個值就從誠實變成錯誤,而且是安靜地錯——共享快取會 跨授權情境重用回應。這是三件事裡唯一有安全後果的,也是這份 ADR 存在的主要
Read the thread · 2026-08-08 · closed · outside contributor · 0 comments
工具清單可被快取:設定 modern era 的快取提示
Parent
#34
What to build
工具清單目前以 SDK 最保守的快取提示對外:不可快取、僅限單一 client。但這份清單 執行期永不改變——五個工具寫死在程式碼裡,只有重新部署才會變。查詢者因此每次對話都在 重拉一份不會變的東西,LLM 的 prompt cache 也拿不到任何好處。
改成公開範圍、1 小時效期,涵蓋 tools/list 與 server/discover 兩個可快取操作。
公開範圍的依據:服務公開、不認證、所有請求者拿到同一份工具清單,這是對真實可見度的 誠實描述。規範明訂這個欄位不得當作存取控制使用,此處也不作此用。
1 小時的依據:清單執行期永不改變,理論上可以設得更長;但工具描述是本專案最常微調的 東西,把「線上說法與 repo 不一致」的窗口壓在 1 小時內,比多拿一點快取效益更值得。
只影響 modern era——legacy 的編碼路徑沒有快取欄位,回應完全不變。
Acceptance criteria
- modern 的
tools/list回傳公開範圍
Read the thread · 2026-08-08 · closed · outside contributor · 0 comments
鎖定現況:雙 era 測試覆蓋 + 釘住決定 lane 策略的相依
Parent
#34
What to build
modern lane 目前零測試覆蓋——既有主 seam 送的都是不帶 envelope 的裸 JSON-RPC,
全部被歸類為 legacy。這張票讓「本服務符合 2026-07-28」這句話由 CI 保證,
而不是由閱讀相依套件的原始碼來保證。
既有主 seam 的請求建構參數化為雙 era:legacy 維持現行形狀;modern 補上 _meta
envelope 的兩個必填保留鍵(協定版本、client capabilities)以及必填的協定版本標頭
與方法標頭。五個工具的既有行為斷言兩個 era 各跑一遍,確保兩條 lane 不會偷偷分岔。
同時把決定 lane 策略的相依從 caret range 釘成確切版本。lane 策略完全由這一層相依決定 (它會強制底層 SDK 進入 modern-only 模式,再自行實作 legacy lane),不該被一次例行 安裝換掉。
這張票不改變任何對外行為。 它的價值在於把改動前的狀態釘死——包含 SDK 目前回的 保守快取
Read the thread · 2026-08-08 · closed · outside contributor · 0 comments
The remaining reports are on the project's issue tracker.