Skip to content

fix(build): 改用 node runtime 發布,移除對 bun 的執行期依賴 - #22

Closed
terry90918 wants to merge 47 commits into
mainfrom
develop
Closed

fix(build): 改用 node runtime 發布,移除對 bun 的執行期依賴#22
terry90918 wants to merge 47 commits into
mainfrom
develop

Conversation

@terry90918

Copy link
Copy Markdown
Contributor

問題

@jurislm/langfuse-mcp 的 published bin (dist/index.js) 帶有 #!/usr/bin/env bun shebang,build 使用 --target bun

但此套件是透過 npx @jurislm/langfuse-mcp@latest 由 Claude Code 啟動,執行環境不保證有 bun。實測 .bun/bin 僅存在於 ~/.zshrc(互動式 shell),任何 login shell 或 GUI 啟動的行程都會直接失敗:

env: bun: No such file or directory

驗證方式:

d=$(mktemp -d); cd $d && npm pack @jurislm/langfuse-mcp@latest && tar xzf *.tgz && head -1 package/dist/index.js
# → #!/usr/bin/env bun

修法

  • shebang → #!/usr/bin/env node
  • build → bun build --target node
  • engines{ "node": ">=18" }

原始碼完全沒有使用 Bun.* API(grep 驗證為 0 處),無相容性風險。與 hetzner-mcp / coolify-mcp 一致(兩者皆為 node shebang,可正常經 npx 啟動)。

bun install / bun run dev 等開發指令維持不變——bun 仍是本 repo 的開發工具,只有「發布產物的執行期」不該依賴它。

Code review 追加修正(668923a)

  1. 文件仍叫使用者用 bunxREADME.md:111CLAUDE.md:172 的整合範例是 "command": "bunx"bunx 無論 shebang 為何都以 bun runtime 執行,照文件設定的人完全享受不到這次修正。兩處改為 npx -y

  2. 測試守錯 artifact:原測試斷言 src/index.ts 的 shebang,但 npx 實際執行的是 dist/index.js。任何讓 dist 退回 bun 的變更(換 bundler、加 postbuild、bun 預設值改變)都能通過該測試。

    新增 scripts/check-dist-runtime.mjs 直接檢查 dist/index.js 的 shebang 與是否殘留 Bun.* 呼叫,掛在 prepublishOnly——剛好落在產物產生之後、上傳 npm 之前。

驗證

  • 54 tests / typecheck / lint 全綠
  • env -i(僅 HOME + 最小 PATH,無 bun)純 node 執行 dist/index.js:MCP initialize handshake 成功、tools/list 回傳 50 個工具、listPrompts 實打 Langfuse API 回傳 isError: false
  • 守衛反向驗證:竄改 dist shebang 為 bun → exit 1;注入 Bun.fileexit 1;正常產物 → exit 0

Test plan

  • bun run test
  • bun run typecheck
  • bun run lint
  • node scripts/check-dist-runtime.mjs(含反向驗證)
  • 純 node 環境實打 Langfuse API
  • merge 後 release-please 產生 release PR → npm publish
  • 發版後回頭把 jurislm-tools/plugins/langfuse/.mcp.json 比照 coolify/hetzner 套用 login shell + env -i

terry90918 and others added 30 commits April 10, 2026 12:59
- Extract langfuseApi, getAuthHeader, baseUrl to src/lib/api.ts
- Move 6 Prompt tools to src/tools/prompts.ts with registerPromptTools()
- Move 2 Trace tools to src/tools/traces.ts with registerTraceTools()
- Move 2 Observation tools to src/tools/observations.ts with registerObservationTools()
- Move 2 Score tools to src/tools/scores.ts with registerScoreTools()
- Move 1 Session tool to src/tools/sessions.ts with registerSessionTools()
- Simplify src/index.ts to register and initialize tools
- Enhance langfuseApi() to support array query parameters for future Datasets/Metrics tools
- Maintain all 13 existing tools, no functional changes

Prepares codebase for Phase 1: adding Datasets tools

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New tools for dataset-based evaluation workflows:
- listDatasets: List all datasets with search/pagination
- getDataset: Fetch dataset metadata and stats
- createDataset: Create new dataset for evals
- listDatasetItems: List input/output pairs in dataset
- createDatasetItem: Add evaluation samples to dataset
- getDatasetItem: Fetch single dataset item
- listDatasetRuns: List eval executions against dataset
- getDatasetRun: Fetch eval run results

Supports Langfuse eval management workflows.
Total tools now: 21 (13 existing + 8 new)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New tools for usage and performance analytics:
- getDailyMetrics: Query daily metrics (latency, tokens, scores, cost) with filters
- getUsageSummary: Get usage summary for time period

Supports analytics and cost tracking for Langfuse deployments.
Total tools now: 23 (13 existing + 8 datasets + 2 metrics)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New tools for score schema management:
- listScoreConfigs: List all custom score definitions
- getScoreConfig: Fetch score config with data types and categories
- createScoreConfig: Define new numeric or categorical score type
- updateScoreConfig: Update score config (name, categories, description)

Supports custom scoring metrics and eval configurations.
Total tools now: 27 (13 existing + 8 datasets + 2 metrics + 4 score-configs)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…etadata

Phase 4 completion: Project Management tools integrated.
Total tools: 28 (6 prompts + 2 traces + 2 observations + 2 scores + 1 sessions + 8 datasets + 2 metrics + 4 score-configs + 1 project)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Instance Management, Organization Projects, API Keys, and Memberships management tools:
- Instance Management (8 tools): listInstances, getInstance, createInstance, updateInstance, deleteInstance, listInstanceEvents, getInstanceStatus, configureInstanceSettings
- Organization Projects (5 tools): listOrganizationProjects, getOrganizationProject, createOrganizationProject, updateOrganizationProject, deleteOrganizationProject
- Organization API Keys (3 tools): listOrganizationApiKeys, createOrganizationApiKey, deleteOrganizationApiKey
- Organization Memberships (6 tools): listOrganizationMembers, addOrganizationMember, updateOrganizationMember, removeOrganizationMember, listProjectMembers, updateProjectMember

**Architecture changes:**
- Enhanced api.ts with multi-auth support (Basic Auth, Admin Bearer, Org Bearer)
- Added authType and rawPath parameters to langfuseApi()
- New environment variables: LANGFUSE_ADMIN_API_KEY, LANGFUSE_ORG_API_KEY
- Modular tool registration pattern for 4 new tool modules

Total tools expanded from 28 to 50. All existing tools (Prompt, Traces, Observations, Scores, Sessions, Datasets, Metrics, Score Configs, Project) remain unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HIGH-1: Add regex validation to all ID parameters (instanceId, orgId, projectId, memberId, keyId) to prevent path traversal
  Pattern: /^[a-zA-Z0-9_-]+$/ enforces alphanumeric, underscore, and hyphen characters only
  Applied to: instance-management, organization-projects, organization-apikeys, organization-memberships

- HIGH-2: Validate environment variables in getBasicAuthHeader()
  Missing LANGFUSE_PUBLIC_KEY or LANGFUSE_SECRET_KEY now throws explicit error instead of silent authentication failure

- HIGH-3: Move PATCH operation validation from Zod schema to handler
  Resolved MCP SDK ZodEffects incompatibility by moving .refine() validation to runtime checks
  Ensures updateInstance, updateOrganizationProject, updateOrganizationMember, updateProjectMember require at least one field

All changes verified: typecheck ✓, lint ✓, build ✓

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**Schema Validation (MEDIUM severity)**:
- Add datetime validation to metrics queries (fromTimestamp, toTimestamp)
- Add datetime validation to session queries (fromTimestamp, toTimestamp)
- Add min(1) constraint to instance creation/update fields (maxUsers, maxRequests)

**API Testing (Dependency Injection Pattern)**:
- Refactor langfuseApi() to accept optional Fetcher parameter for testability
- Add type-safe Fetcher type to avoid 'any' types in tests
- Implement comprehensive test coverage without any type assertions:
  - Timeout handling with AbortController
  - Content-Type validation (JSON vs text)
  - 204 No Content response handling
  - Error propagation with cause preservation
  - Authorization header validation
  - Failed response handling

**Error Handling**:
- Add cause preservation for thrown errors (ESLint preserve-caught-error)
- Improve timeout error messages

All tests passing: 23 pass, 0 fail
Type checking: ✅ No errors
Linting: ✅ No warnings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add missing tags parameter forwarding in listTraces
- Update server version from 1.0.0 to 1.1.0
- Add .min(1) validation to name fields:
  - createInstance (instance-management.ts)
  - createOrganizationProject (organization-projects.ts)
  - updateOrganizationProject (organization-projects.ts)
  - createOrganizationApiKey (organization-apikeys.ts)
- Update README.md environment variables documentation
  - Add LANGFUSE_ADMIN_API_KEY explanation
  - Add LANGFUSE_ORG_API_KEY explanation
  - Clarify which tools require which API keys

Fixes: #3
- Add claude-code-review.yml (4-phase prompt)
- Add claude.yml (@claude interactive trigger)
- Add .github/copilot-instructions.md (Node/TS template)
- Add .prettierignore with .worktrees/ exclusion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RED — tests fail because LangfuseApiError and withRetry are not yet
exported from api.ts. Covers: instanceof check, status field, retry
on 429, no-retry on non-429, retry exhaustion.
- LangfuseApiError extends Error with status: number field
- withRetry<T> retries on HTTP 429 using configurable delay sequence
- langfuseApi now throws LangfuseApiError instead of plain Error
…eClient

RED — resetLangfuseClient not yet exported from client.ts.
Covers: env validation, singleton pattern, reset behaviour.
Allows test suites to reset the singleton between cases without
reaching into module internals.
RED — registerTraceTools ignores second client argument,
calls real langfuseApi instead of mock. Tests fail with 404.
registerTraceTools now accepts client parameter (default: getLangfuseClient())
enabling typed fetchTraces/fetchTrace calls with SDK retry behaviour.
RED — registerObservationTools ignores second client argument.
registerObservationTools now accepts client parameter (default: getLangfuseClient())
enabling typed fetchObservations/fetchObservation calls.
RED — registerSessionTools ignores second client argument.
registerSessionTools now accepts client parameter (default: getLangfuseClient())
enabling typed fetchSessions calls.
SDK fetchTraces/fetchSessions expect Date | null | undefined for
fromTimestamp/toTimestamp. Also fix bun:test mock.calls tuple
type access via double unknown cast.
- traces/observations/sessions handlers now catch SDK errors and return
  MCP-idiomatic { isError: true, content: [...] } instead of propagating
  unhandled rejections
- Add error-path tests for all three tool families
- Update ToolHandler type to include isError field
- Simplify mock.calls cast to Array<unknown[]> with explicit element cast
# Conflicts:
#	src/lib/api.test.ts
#	src/lib/api.ts
#	src/tools/observations.ts
#	src/tools/sessions.ts
#	src/tools/traces.ts
terry90918 and others added 17 commits April 24, 2026 15:31
… safety

- Wrap langfuseApi() HTTP fetch logic with withRetry() so 429 responses
  automatically retry with configurable delays (default: 1s/2s/4s)
- Add retryDelays option to langfuseApi() opts for caller control
- Fix lastError initialization to Error type, eliminating potential
  non-Error throws from withRetry()
- Add langfuseApi 429-retry integration test; add retryDelays: [] to
  existing error tests to prevent unintended retries
- @v1.0.70 → @v1
- gh pr comment → gh pr review --comment (posts in Reviews section, not comments)
- Add .github/workflows/ci.yml with lint/typecheck/test jobs (bun, concurrency dedup, push-safe if:)
- Upgrade claude-code-review.yml to 6-phase prompt (FETCH→FILTER→TRIAGE→WRITE→Self-check→POST) with profile switch (chill/assertive), pr_ctx step for workflow_dispatch fix, and path filter
- Add packageManager/engines fields to package.json per Bun standard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Floating @v1 tag is mutable and could be redirected to a malicious
commit. Pin to the latest released version for supply chain safety.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Plugin credentials via ${VAR} are user-level (global). This change
lets each project/worktree drop a .env.langfuse file (gitignored)
to override LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY / LANGFUSE_HOST
without touching ~/.zshenv.

Priority: .env.langfuse > Plugin \${VAR} > ~/.zshenv
@jurislm/langfuse-mcp 的 published bin (dist/index.js) 帶有
`#!/usr/bin/env bun` shebang,且 build 使用 `--target bun`。但此套件是透過
`npx @jurislm/langfuse-mcp@latest` 由 Claude Code 啟動,執行環境不保證有 bun
在 PATH 上——實測 `.bun/bin` 僅存在於 ~/.zshrc(互動式 shell),任何 login
shell 或 GUI 啟動的行程都會直接失敗:

  env: bun: No such file or directory

改為 `--target node` + node shebang。原始碼完全沒有使用 Bun.* API(grep 驗證
為 0 處),故無相容性風險。engines 同步由 bun 改為 node >= 18。
與 hetzner-mcp / coolify-mcp 一致(兩者皆為 node shebang,可正常經 npx 啟動)。

新增 src/index.test.ts 防止迴歸:斷言 entrypoint shebang 為 node、build script
target 為 node。已反向驗證——把 shebang 改回 bun 時該測試確實 fail。

驗證:
- 53 + 2 tests / typecheck / lint 全綠
- 以 env -i(僅 HOME + 最小 PATH,無 bun)純 node 執行 dist/index.js:
  MCP initialize handshake 成功、tools/list 回傳 50 個工具、
  listPrompts 實打 Langfuse API 回傳 isError:false 與正常分頁結果
6859c4f。code review 找出兩個讓該修正形同虛設的問題:

1. README.md 與 CLAUDE.md 的整合範例仍叫使用者用 `"command": "bunx"`。
   bunx 無論 shebang 為何都以 bun runtime 執行,照文件設定的人完全享受不到
   node 化的好處;在沒有 bun 的機器上依舊以
   `env: bun: No such file or directory` 失敗。兩處改為 `npx -y`。
   (README 的 `bun install` / `bun run dev` 等開發指令維持不變——bun 仍是
   本 repo 的開發工具,只有「發布產物的執行期」不該依賴它。)

2. src/index.test.ts 守錯了 artifact:它斷言 `src/index.ts` 的 shebang,
   但 npx 實際執行的是 `dist/index.js`。任何讓 dist 退回 bun 的變更
   (換 bundler、加 postbuild、bun 預設值改變)都能通過該測試。
   新增 scripts/check-dist-runtime.mjs 直接檢查 dist/index.js 的 shebang
   與是否殘留 Bun.* 呼叫,掛在 prepublishOnly——剛好落在產物產生之後、
   上傳 npm 之前。原測試簡化為只留原始碼的早期訊號,並移除對
   `pkg.scripts.build` 字串的脆弱斷言(`--target=node` 等價寫法會誤判)。

驗證:
- 54 tests / typecheck / lint 全綠
- 守衛反向驗證:竄改 dist shebang 為 bun → exit 1;注入 Bun.file → exit 1;
  正常產物 → exit 0
Copilot AI review requested due to automatic review settings July 8, 2026 09:35
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 30 seconds

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 92f48abb-938d-430f-9298-25299bef4250

📥 Commits

Reviewing files that changed from the base of the PR and between 8a98345 and 668923a.

📒 Files selected for processing (9)
  • .coderabbit.yaml
  • .github/workflows/claude-code-review.yml
  • .github/workflows/claude.yml
  • CLAUDE.md
  • README.md
  • package.json
  • scripts/check-dist-runtime.mjs
  • src/index.test.ts
  • src/index.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

此 PR 旨在讓 @jurislm/langfuse-mcp 的「發布產物執行期」改為 Node runtime(避免 npx @jurislm/langfuse-mcp 在無 Bun 環境下因 shebang 失敗),並補上 publish 前針對 dist/index.js 的 runtime 守衛,同步更新文件範例的啟動方式。

Changes:

  • 將 entrypoint shebang 改為 #!/usr/bin/env node,並把 bun build target 調整為 nodestart 改用 node dist/index.jsengines 設定為 node >= 18
  • 新增 scripts/check-dist-runtime.mjs 並掛到 prepublishOnly,在 publish 前檢查 dist/index.js shebang 與是否殘留 Bun.* 參照。
  • 更新 README/CLAUDE 文件範例由 bunx 改為 npx -y;移除 Claude 相關 GitHub Actions workflow;新增 CodeRabbit 設定檔。

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/index.ts 將 shebang 切到 node,並加入 .env.langfuse 載入邏輯
src/index.test.ts 新增測試,針對 src/index.ts shebang 提供早期失敗訊號
scripts/check-dist-runtime.mjs 新增 publish 前守衛:檢查 dist/index.js shebang 與 Bun API 參照
package.json build/start/engines 調整為 node 執行期;prepublishOnly 串上 dist 守衛腳本
README.md Claude Code 範例改用 npx -y 啟動
CLAUDE.md 整合範例改用 npx -y 啟動
.github/workflows/claude.yml 移除 Claude Code workflow
.github/workflows/claude-code-review.yml 移除 Claude Code Review workflow
.coderabbit.yaml 新增 CodeRabbit 自動審查語言/語氣設定

Comment thread src/index.ts
Comment on lines +15 to +29
// Load per-project credentials from .env.langfuse (gitignored, no new deps)
import { existsSync, readFileSync } from "fs";
import { join } from "path";
const _envFile = join(process.cwd(), ".env.langfuse");
if (existsSync(_envFile)) {
for (const line of readFileSync(_envFile, "utf-8").split("\n")) {
const t = line.trim();
if (!t || t.startsWith("#")) continue;
const eq = t.indexOf("=");
if (eq === -1) continue;
const key = t.slice(0, eq).trim();
const val = t.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
process.env[key] = val;
}
}
Comment thread src/index.ts
Comment on lines +11 to 12
* 環境變數(優先順序:.env.langfuse > Plugin ${VAR} > ~/.zshenv)
* LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, LANGFUSE_HOST
Comment on lines +26 to +29
const shebang = source.split("\n", 1)[0];
if (shebang !== "#!/usr/bin/env node") {
fail(`dist/index.js shebang is ${JSON.stringify(shebang)}, expected "#!/usr/bin/env node"`);
}
Comment thread README.md
Comment on lines +111 to 113
"command": "npx",
"args": ["-y", "@jurislm/langfuse-mcp@latest"],
"env": {
@terry90918

Copy link
Copy Markdown
Contributor Author

改由 fix/node-runtime-publish 重開:本 PR 的 head develop 落後 main 9 個 commit(含 .env.langfuse 憑證載入與 LANGFUSE_HOST lazy resolve)。新分支直接基於 origin/main,並已在含這些新功能的程式碼上重新驗證(.env.langfuse 為明確 readFileSync,非 bun 自動載入,換 node 不會失效)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants