Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5ef4ab6
test: add reproducer for H-1 username/snapshot_id path injection (RED)
terry90918 May 26, 2026
61d6756
fix(security): add regex allowlist to username and snapshot_id URL pa…
terry90918 May 26, 2026
c6dcfaa
fix(security): force fast-uri@3.1.2 via overrides to resolve HIGH CVE…
terry90918 May 26, 2026
e9f5cd7
test: add reproducer for M-2 password complexity not enforced in sche…
terry90918 May 26, 2026
a9bc3e1
fix(security): enforce password complexity policy in schema validatio…
terry90918 May 26, 2026
da1f717
fix(security): add overrides to eliminate all remaining CVEs (M-3)
terry90918 May 26, 2026
bcf02bd
docs(security): document StrictHostKeyChecking=accept-new trust model…
terry90918 May 26, 2026
19b5dd1
fix: address Copilot and Claude bot review feedback on PR #32
terry90918 May 26, 2026
23aad43
fix(security): replace snapshot_id denylist with allowlist regex to b…
terry90918 May 26, 2026
1cbae60
fix(security): add allowlist regex to rollback snapshot parameter (H-1b)
terry90918 May 26, 2026
faf92e3
fix(security): validate IPv4 format before passing to SSH execFile (M-1)
terry90918 May 26, 2026
d2bdb1b
fix(security): explicitly serialize only storage_box+action in create…
terry90918 May 26, 2026
fa28488
fix(security): escape HTML special chars in label key/value Markdown …
terry90918 May 26, 2026
2f6ad02
fix(security): add slug regex to POST body fields server_type/image/s…
terry90918 May 26, 2026
8f96ba6
fix(api): tighten __resetClientsForTesting guard to !== "test"
terry90918 May 26, 2026
60d4b66
test: add reproducers for L-2b HTML escaping gaps in non-label format…
terry90918 May 26, 2026
fd5baee
fix(security): apply escapeHtml to non-label fields in all format fun…
terry90918 May 26, 2026
9d44b28
test: add reproducer for IPv4 octet range validation gap (999.0.0.1 p…
terry90918 May 26, 2026
fe16c7e
fix(security): tighten IPv4 validation regex to enforce octet range 0…
terry90918 May 26, 2026
c0e8552
test: add reproducer for image regex rejecting uppercase custom image…
terry90918 May 26, 2026
099346c
fix: relax image regex to allow uppercase for custom/snapshot image n…
terry90918 May 26, 2026
2b3f5fd
docs: rewrite README to reflect 40-tool codebase
terry90918 May 26, 2026
cf7bff4
docs: remove Kamal comparison section from README
terry90918 May 26, 2026
c88111b
docs: rewrite README — focused, accurate, developer-first
terry90918 May 26, 2026
d509daa
Merge remote-tracking branch 'origin/main' into develop
terry90918 May 26, 2026
9b7649e
feat: add GitHub Pages landing page (docs/index.html)
terry90918 May 26, 2026
3658333
feat: add i18n support (繁體中文, 日本語, 한국어)
terry90918 May 26, 2026
22e0e5c
Merge remote-tracking branch 'origin/main' into develop
terry90918 May 26, 2026
bbfd885
docs: explain ⚠ legend in README and landing page (all 4 languages)
terry90918 May 26, 2026
1107e8a
Merge remote-tracking branch 'origin/main' into develop
terry90918 May 26, 2026
90f1bb8
fix(security): address 9 security findings from automated audit (HIGH…
terry90918 May 26, 2026
86e1512
fix: address 5 code-review findings in PR #42
terry90918 May 26, 2026
b8334f6
refactor: centralise escapeHtml in src/utils.ts; improve error message
terry90918 May 26, 2026
efada08
ci: 移除 claude-code-review.yml
terry90918 Jun 2, 2026
62ed41a
ci: 移除 claude.yml
terry90918 Jun 2, 2026
bd10cc4
Merge pull request #44 from jurislm/ci-remove-claude-review-workflows
terry90918 Jun 2, 2026
4c23c35
feat(storage-boxes): 新增 hetzner_get_storage_box_stats 與 hetzner_asser…
terry90918 Jun 25, 2026
20ee2ee
fix(servers): 改用 location 取代已被 Hetzner 移除的 datacenter 欄位
terry90918 Jul 8, 2026
0ca4736
chore: skip CodeRabbit review on release-please PRs
terry90918 Jul 8, 2026
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
10 changes: 10 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# yaml-language-server: $schema=https://storage.googleapis.com/coderabbit_public_assets/schema.v2.json
language: "zh"
tone_instructions: "一律使用繁體中文(台灣正體用語)撰寫審查意見與回覆。"
reviews:
auto_review:
base_branches:
- "^main$"
# release-please 自動開的 release PR(標題固定 "chore(main): release X.Y.Z")純版號/CHANGELOG,跳過審查
ignore_title_keywords:
- "chore(main): release"
200 changes: 0 additions & 200 deletions .github/workflows/claude-code-review.yml

This file was deleted.

39 changes: 0 additions & 39 deletions .github/workflows/claude.yml

This file was deleted.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Storage Box tools call `api.hetzner.com/v1` (unified API), while all other tools

## Available Tools (40 total)

⚠️ marks destructive or hard-to-reverse operations.

### Servers (7)

| Tool | Description | ⚠️ |
Expand Down
5 changes: 5 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ <h1>Hetzner <span>MCP</span> Server</h1>

<div class="section">
<h2 data-i18n="section-tools">Tools</h2>
<p style="font-size:13px;color:var(--muted);margin:-8px 0 16px;" data-i18n="tools-note">⚠ marks destructive or hard-to-reverse operations.</p>
<div class="categories">

<div class="category">
Expand Down Expand Up @@ -373,6 +374,7 @@ <h2 data-i18n="section-setup">Quick Setup</h2>
"stat-endpoints": "API Endpoints",
"stat-transport": "Transport",
"section-tools": "Tools",
"tools-note": "⚠ marks destructive or hard-to-reverse operations.",
"cat-servers": "Servers",
"cat-servers-desc": "Create, power on/off/reboot, and delete cloud servers.",
"cat-ssh": "SSH Keys",
Expand Down Expand Up @@ -401,6 +403,7 @@ <h2 data-i18n="section-setup">Quick Setup</h2>
"stat-endpoints": "API 端點",
"stat-transport": "傳輸協定",
"section-tools": "工具",
"tools-note": "⚠ 標示破壞性或難以復原的操作。",
"cat-servers": "伺服器",
"cat-servers-desc": "建立、開關機、重新開機、刪除雲端伺服器。",
"cat-ssh": "SSH 金鑰",
Expand Down Expand Up @@ -429,6 +432,7 @@ <h2 data-i18n="section-setup">Quick Setup</h2>
"stat-endpoints": "API エンドポイント",
"stat-transport": "トランスポート",
"section-tools": "ツール",
"tools-note": "⚠ は破壊的または元に戻しにくい操作を示します。",
"cat-servers": "サーバー",
"cat-servers-desc": "クラウドサーバーの作成、電源操作、再起動、削除。",
"cat-ssh": "SSH キー",
Expand Down Expand Up @@ -457,6 +461,7 @@ <h2 data-i18n="section-setup">Quick Setup</h2>
"stat-endpoints": "API 엔드포인트",
"stat-transport": "전송 방식",
"section-tools": "도구",
"tools-note": "⚠ 는 파괴적이거나 되돌리기 어려운 작업을 나타냅니다.",
"cat-servers": "서버",
"cat-servers-desc": "클라우드 서버 생성, 전원 켜기/끄기, 재시작, 삭제.",
"cat-ssh": "SSH 키",
Expand Down
8 changes: 5 additions & 3 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,11 @@ export function createPaginatedFetch(requestFn: PaginatedRequestFn) {
};
}

// I-5: Test-only reset hook for clearing cached clients between tests.
// Throws unless NODE_ENV === "test" — catches both explicit "production" and the
// common MCP production case where NODE_ENV is simply not set.
/**
* @internal
* Test-only reset hook for clearing cached singleton clients between test runs.
* Throws in any non-test environment — do NOT call from production code.
*/
export function __resetClientsForTesting(): void {
if (process.env.NODE_ENV !== "test") {
throw new Error("__resetClientsForTesting must not be called in production");
Expand Down
5 changes: 3 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { registerStorageBoxTools } from "./tools/storage-boxes.js";
import { registerVolumeTools } from "./tools/volumes.js";
import { registerMetricsTools } from "./tools/metrics.js";
import { registerServerSshTools } from "./tools/server-ssh.js";
import { formatStartupError } from "./utils.js";

// Create MCP server instance
const server = new McpServer({
Expand Down Expand Up @@ -54,7 +55,7 @@ async function main(): Promise<void> {
console.error("Hetzner MCP server running via stdio");
}

main().catch((error) => {
console.error("Server error:", error);
main().catch((error: unknown) => {
console.error("Server error:", formatStartupError(error));
process.exit(1);
});
Loading