npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

fencescan

v0.1.4

Published

Find tool calls that could fire the same effect twice. Reports evidence, renders no verdict. Zero dependencies.

Readme

fencescan

Find tool calls that could fire the same effect twice.

npx fencescan

No install, no dependencies, no network, no config. Scans the current directory and prints what's worth a second look.

What it does

An agent that retries a tool call after a timeout doesn't know whether the first attempt landed. The request reached the server, the work happened, the response never came back. Nothing failed loudly — it succeeded twice.

fencescan reads your code and lists the tools where that would cost you something, alongside what else it found: write call sites, anything that looks like an idempotency guard, and retry logic.

fencescan /your/mcp-server
  42 files · 11 tool declarations found

Tools worth checking  (candidates, not verdicts)
  money   createCampaign   src/tools/ads.py:609 · 12 write call(s) in this file
  money   chargeAccount    src/tools/billing.py:88
  effect  sendTemplate     src/tools/whatsapp.py:151

What else is in this codebase
  59 write call site(s)
  0 idempotency-looking guard(s)  ← none found in this repo
  14 retry site(s)

Worth a close look: retry logic and no visible dedup key.

What it deliberately does not do

It renders no verdict. There is no risk score, no "AT RISK", no severity. An outsider reading a codebase usually cannot prove a double-fire: the guard often lives in a service the code calls or a sibling package, and a function whose name sounds like a write may only build a payload for someone else to sign. Every run prints what it cannot see.

This isn't modesty. An earlier version did print verdicts, and hand-verification killed 4 of its first 7. Each of those failures is now a fixed behaviour with a test:

| It got this wrong | Why | Fixed by | |---|---|---| | Said a repo had no idempotency when it shipped a whole module | \b(idempot…) cannot match deriveIdempotencyKey — no word boundary before a mid-word capital. Same blindness hid requestId, clientToken | anchors removed | | Flagged read-only tools | A flat window after a tool name ran into the next declaration, so reads inherited writes' vocabulary | brace-matched to the tool's own block; a read verb in the name vetoes | | Found no writes at all | Writes live in shared helpers, not in the tool declaration | collected per file as corroboration, never claimed as "this tool writes" | | Missed method: body ? "POST" : "GET" | String literals were stripped before matching — but the HTTP verb is a literal | matched on the raw line |

If it flags something and you're sure it's wrong, that's worth an issue. A false positive here costs more than a miss.

Usage

npx fencescan              # scan the current directory
npx fencescan ./server     # scan a path
npx fencescan --json       # machine-readable

Exit codes: 0 scanned, 2 bad path. It never exits non-zero for findings — findings are for a human to read, not for a build to fail on.

Recognises MCP-style tool declarations (registerTool, @mcp.tool, Tool(...), @tool) across TypeScript, JavaScript, Python, Go, Rust, Java and Ruby.

If it finds something

Open each candidate and ask one question: if this ran twice, would anyone notice?

If the answer is no, that's the bug — not the crash you'd have caught.

For fixes: once is an exactly-once kernel for TypeScript and Python, and effectfence is an MCP server that fences tool calls. Both free. If you'd rather someone did the work, the Fence Audit.

Licence

MIT