Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
คำอธิบายจากผู้ทำ (ภาษาอังกฤษตามต้นฉบับ)
Persistence hook auditor: scan repos for auto-exec hooks, flag Shai-Hulud patterns.
สามเรื่องนี้ไม่รวมเป็นคะแนนเดียว เพราะความเห็น การวัด และความนิยม เป็นคนละเรื่องกัน
ตัวนี้ยังไม่ได้เรียบเรียงเป็นภาษาไทย ด้านล่างคือข้อเท็จจริงที่เราอ่านจาก registry
เลือกโปรแกรม AI ที่คุณใช้ แล้วคัดลอกค่าตั้งไปวาง
ต้องเตรียมก่อน
คุณใช้แอปไหน
เลือกแอปด้านบน เราจำไว้ให้ ครั้งหน้าปุ่มจะขึ้นว่า “ติดตั้งลง …”
เปิด Claude Desktop → Settings → Developer → Edit Config แล้ววางค่านี้ในไฟล์ claude_desktop_config.json
{
"mcpServers": {
"hookcheck": {
"command": "npx",
"args": [
"-y",
"@reneturcios/hookcheck",
"mcp"
]
}
}
}พิมพ์คำสั่งนี้ใน Terminal
claude mcp add hookcheck -- npx -y @reneturcios/hookcheck mcpวางในไฟล์ .cursor/mcp.json ของโปรเจกต์ หรือ ~/.cursor/mcp.json
{
"mcpServers": {
"hookcheck": {
"command": "npx",
"args": [
"-y",
"@reneturcios/hookcheck",
"mcp"
]
}
}
}เบราว์เซอร์จะถามก่อนเปิดแอป จากนั้นแอปแสดงค่าตั้งให้ตรวจแล้วกดยืนยัน ช่อง key เว้นว่างไว้ให้คุณใส่เอง
วางในไฟล์ .vscode/mcp.json ของโปรเจกต์
{
"servers": {
"hookcheck": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@reneturcios/hookcheck",
"mcp"
]
}
}
}เบราว์เซอร์จะถามก่อนเปิดแอป จากนั้นแอปแสดงค่าตั้งให้ตรวจแล้วกดยืนยัน ช่อง key เว้นว่างไว้ให้คุณใส่เอง
ตัวนี้รันในเครื่องคุณ แต่ ChatGPT ต่อได้เฉพาะ server บนอินเทอร์เน็ต ลองเลือกแอปอื่น
ตัวเลือกเก็บในคุกกี้บนเครื่องนี้ ไม่ต้องมีบัญชี
ค่าตั้งนี้สร้างจากข้อมูลใน registry ของผู้ทำ ถ้าไม่ตรงกับ README ให้เชื่อ README
รันในเครื่องคุณ รายการเครื่องมือจะขึ้นในแอป AI หลังติดตั้ง
เราตรวจจากข้อมูลที่ใครก็เปิดดูได้ ยังไม่ได้อ่านโค้ด ข้อที่ไม่เกี่ยวกับตัวนี้ไม่นับในตัวหาร · ตรวจเมื่อ 7 พ.ย. 2569
ชื่อใน registry ยืนยันด้วยบัญชี GitHub CloudCorpRecords ซึ่งเป็นเจ้าของ repo
ยังไม่ได้อ่านข้อมูล repo จาก GitHub
ยังไม่ได้อ่านข้อมูล repo จาก GitHub
ตัวนี้รันบนเครื่องคุณ ไม่มี endpoint บนอินเทอร์เน็ต
ตัวนี้รันบนเครื่อง ใช้ค่าที่ตั้งในเครื่องแทน
รันในเครื่องคุณ เราอ่านรายการเครื่องมือได้เฉพาะตัวที่มี endpoint บนอินเทอร์เน็ต
เทียบกับทุก server ที่ยังใช้งานในหมวดนี้ทั้งทะเบียน ดาวนับเฉพาะเมื่อผู้ลงทะเบียนเป็นเจ้าของ repo
จากการตรวจเบื้องต้นของเรา ผ่าน 1 จาก 1 ข้อที่ตรวจได้ เรายังไม่ได้อ่านโค้ด ก่อนใส่กุญแจหรือ token ให้ดูว่า server ขอสิทธิ์อะไร และเลือกกุญแจที่ทำได้เท่าที่จำเป็น
registry ไม่ได้ระบุว่าต้องใช้กุญแจ แต่บางตัวจะขอให้ล็อกอินตอนใช้ครั้งแรก
รันบนเครื่องของคุณ โปรแกรม AI จะเปิด server ขึ้นมาเองเมื่อใช้ ข้อมูลไม่ต้องผ่านเซิร์ฟเวอร์ของผู้ทำ (ยกเว้นตัวที่ไปเรียกบริการออนไลน์ต่อ)
repo บน GitHub มีการแก้โค้ดล่าสุด – และส่งรุ่นล่าสุด (0.1.18) เข้า registry 15 วันที่ผ่านมา
โปรแกรมที่รองรับ MCP เช่น Claude Desktop, Claude Code, Cursor และ VS Code ตัวนี้ต้องมี Node.js, Docker ในเครื่องด้วย
ในหมวดเดียวกันมี Spec Driven Development, Remote Desktop Commander, SuperCollider
server หมวดเดียวกันจากผู้ทำคนอื่น เรียงตามดาว
อ่านการ์ด:★ ดาว GitHubiสามเสา: รีวิว · ผลตรวจ · การใช้งานiอัปเดต = วันที่ส่งรุ่นล่าสุดเข้า registryi
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.
Make sound with SuperCollider: synths, drums, chords, melodies and soundscapes over OSC.
ส่วนนี้คัดจาก repo ของผู้ทำตามต้นฉบับ PopNx MCP ไม่ได้ตรวจหรือรับรองเนื้อหา ตัด HTML รูปภาพ และสคริปต์ออกแล้ว ลิงก์จะพาออกนอกเว็บ
Find the hooks that run before you do. One scan, every auto-exec surface, one verdict.
npx @reneturcios/hookcheck scan ./some-repo
Modern supply chain attacks do not need you to run anything. They persist through hooks that run automatically: an npm postinstall script, a Claude Code SessionStart hook in .claude/settings.json, a VS Code task set to run on folder open. The Shai-Hulud npm worm used exactly this pattern, planting a preinstall dropper and then persisting through agent and editor hooks so it survived cleanup and stole credentials on the next open.
hookcheck scans a local repo for every auto-exec hook, classifies each one, and prints a single verdict: CLEAN, REVIEW, or COMPROMISED-PATTERN, with the file, command, rules, and evidence for every finding. Deterministic only. No LLM, no network in the core scan.
Reports in October 2026 described AI agents orchestrating attacks and a self-propagating npm worm that backdoored hundreds of packages. Its persistence layer was not exotic malware. It was ordinary config files that every developer and every coding agent already trusts: package.json install scripts, .claude/settings.json hooks, and .vscode/tasks.json folderOpen tasks. Defenders needed a fast local check that an agent itself can call before opening, installing, or trusting a repo. That is hookcheck.
| surface | what it looks for | |
|---|---|---|
package.json | preinstall, install, postinstall, prepare scripts. The root package.json and nested workspace package.json files (for example packages/*/package.json, apps/*/package.json, any directory up to 8 levels below the root, node_modules excluded) are inventoried the same way | |
.claude/settings.json and settings.local.json | SessionStart, PreToolUse, and other hook commands. Also discovered: the GNU stow layout (claude/settings.json, no dot) and settings nested one level under a config dir (for example config/.claude/settings.json). A settings path that exists as a directory is reported as a warning, not silently skipped | |
| Claude Code plugin hooks | hooks/hooks.json in a plugin root: at the repo root, or nested under plugin directories (for example plugins/ /hooks/hooks.json), with the plugin root at most 8 levels below the scan root. The file carries the same events map as the hooks key of .claude/settings.json (SessionStart, PreToolUse, PostToolUse, Stop, UserPromptSubmit, and the rest of the Claude Code event set) and its commands are classified exactly the same way. The sibling hooks/hooks-cursor.json variant carries the same command object shape under Cursor event names and is inventoried the same way | |
.vscode/tasks.json | tasks with runOn folderOpen, plus task inventory | |
.husky/ | every hook file and its full command content | |
lefthook.yml | run commands | |
.pre-commit-config.yaml | entry commands and remote repo URLs | |
.cursor/ and .codex/ | config commands and files | |
| MCP configs | .mcp.json, mcp.json, .vscode/mcp.json, .cursor/mcp.json, .windsurf/mcp.json, .codex/mcp.json servers that autostart, each inventoried exactly once | |
.github/workflows/ | run steps, as inventory, including block scalar bodies (`run: | and run: >, with chomping variants). Every workflow finding carries the line number of its run:` key |
.githooks/ | hook files |
Every surface above is discovered by one bounded recursive walk, not only at the scan root: a surface that appears inside a subdirectory (for example packages/foo/.claude/settings.json, sub/.vscode/tasks.json, or sub/.github/workflows/ci.yml) is inventoried from a parent scan with its real relative path in the finding, within the discovery bound documented under Limits. A surface file is inventoried exactly once however many discovery paths reach it.
Classification per hook:
env -u NAME unsets a variable, os.environ.setdefault(...) writes one key with a default, and a [ -n "$NAME" ] test checks existence. A hook whose target is a shell script is classified by that script's contents, not by the fact that it is a shell script. Comments are stripped before classification, so a docs URL inside a comment is not a fetch, and a fetch whose every URL is loopback (a localhost health check) is not treated as network behavior. In script files (JavaScript, Python), the contents of string and regex literals are data, not behavior: a defensive guard whose pattern list quotes eval( or curl | sh performs neither, and hookcheck does not classify it as an attacker. Literals handed to exec style calls (execSync("..."), os.system("...")) are code by construction and are still matched. Two rules are about value flow, not co-occurrence: ENV-EXFIL fires only when a credential value demonstrably flows into an outbound send (a request payload, a query string, a pipe into a network tool, or printed output next to a send), and CRED-EXFIL when credential store contents are piped or posted straight into a network send. Environment inheritance is not a send: when the only environment movement is inheritance or bulk access passed as a subprocess environment (env=os.environ, a {**os.environ, ...} spread, dict(os.environ), os.environ.copy(), and their JavaScript forms), ENV-EXFIL additionally requires a non-loopback destination in the flow, so a test harness that starts its own server on 127.0.0.1 and hands it the inherited environment does not verdict as exfiltration; the same forms serialized into a payload, or paired with a remote destination, still fire. The ENV-BULK evidence names the form it saw: inheritance and copy forms are described as bulk access in inheritance or copy form, not as a serialization or dump. A token used as authentication (an Authorization header, the userinfo of a push URL to the repo's own host, a --token flag) is authentication, not exfiltration, and never fires either rule. Authentication positions are recognized by shape, not only by the Authorization spelling: any curl -H header argument whatever its name, any dict or object entry whose key is an authentication header name (Authorization, x-api-key, *-api-key, *-auth-token, and friends) in a one hop script file, and any header shaped entry inside a headers construction (a dict assigned to a headers variable, a headers= argument, a headers property of a request options object) all count as authentication positions. Further authentication positions are recognized by the same shape principle: a curl -u or --user basic auth pair, the value configured for a git http.extraheader key (an Authorization header value however it was built), an ::add-mask:: registration (printing a value there is the masking idiom itself), a credential hand-off position (the value configured for a git credential.helper key, including the body of a shell function the helper value names: git reads the helper's echoed username and password lines as its authentication protocol, so that echo is presentation, not disclosure; the same family covers a core.askPass value, a GIT_ASKPASS or SSH_ASKPASS assignment, input piped into a git credential invocation, and a heredoc feeding one), and destination positions in the URL of a send itself: a credential that is an entire URL path segment, the whole URL, or the URL origin (capability URLs are credentials by design: a build hook URL, a preview token in the path, a token query parameter), the value of an authentication named query parameter (token, api_key, access_token, auth, secret, signature, key), a bare reference in the curl URL operand position, and the destination first argument of a fetch style call. One more authentication position in the same family (since v0.1.18): a credential written to ~/.npmrc, the npm client's own auth file, as the target of a redirection (the canonical npm publish step, echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc) is presented to its own client and does not count as credential store access on its own; reading ~/.npmrc, copying it, or posting its contents or the token anywhere else still counts at full strength. The body test decides: a credential presented in one of those positions with no credential or bulk data in the body is authentication, while the same credential in a body, in a data named query parameter, in a partial URL path segment, or in printed output still counts as exfiltration, and a capability URL shape plus credential data in the body still fires. Taint does not pass through a locally defined helper function's return value unless the helper itself returns tainted data (its body is analyzed as part of the same text), a printed length or count of a value (len(x), x.length, ${#NAME}) is metadata rather than a disclosure of the value, and a name occurrence that is an object key, a property name, an assignment target, or a parameter declaration is a binding, not a reference to the variable of that name. A shell variable assigned in the same text from a value containing no credential atom (a token named response variable bound to a literal file path, used as a curl --output sink), or bound via a command substitution whose only credential atoms sit in destination positions (an HTTP status code variable), is not itself a credential value. Fetch detection requires command position: curl or wget counts as a network fetch only when it is the invoked command (the head of a statement or pipeline segment, after sudo or env wrappers, or the head of a command string handed to an interpreter), never when the word merely names a package in an install line (pacman -S curl, apt-get install curl). In script files the remote URL rule follows the same data view as the other behavior rules: a URL that appears only inside a string literal or a comment (an error message, a documentation link, an API base constant) is data, while a URL handed to a network call (fetch, requests, http.get, urlopen, axios, sendBeacon) is a fetch target and still fires. Inline code execution (DOWNLOAD-EXEC) fires only when the evaluated content itself carries a download or remote reference; constant inline code such as a node -e version comparison downloads nothing and is not flagged. Since v0.1.17 the same rule also fires on the file level loader shape: when content fetched over the network reaches an eval style call (eval, Function, exec, a vm runIn context call), directly or through variables (a fetch result bound to one variable, its body text bound to a second, the second handed to eval), the finding fires DOWNLOAD-EXEC together with REMOTE-EVAL and verdicts COMPROMISED-PATTERN. A fetch whose result is only logged or parsed as data, and an eval of a constant or of a variable with no fetch in its derivation, do not fireตัดมาแค่ช่วงต้น อ่านต่อใน repo