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

mcp-server-lint

v0.1.0

Published

Zero-dependency MCP tools/list linter with reproducible counts from 2,283 servers and 29,574 tool declarations.

Readme

mcp-server-lint

Lint a live MCP server's tool declarations before connecting them to an agent. Zero runtime dependencies. Every rule includes its measured violation count from a bundled September 8, 2026 corpus of 2,283 distinct servers / 29,574 declarations.

This is an experimental compatibility profile. ERROR means a finding under the frozen profile, not proof that Anthropic or OpenAI will reject your tool. The original empirical bet failed: only 65 / 2,283 servers (2.85%) have an ERROR, below the precommitted 35% threshold. We publish the result without changing rules or counting warnings as errors.

Install and run

Requires Node.js 22 or newer.

npx --yes [email protected] lint https://tandem.ac/mcp
# Or install once:
npm install --global [email protected]
mcp-server-lint lint https://YOUR_SERVER/mcp
mcp-server-lint lint --file tools.json

File input accepts a tools array, { "tools": [...] }, or a JSON-RPC { "result": { "tools": [...] } } response. Output is JSON with one entry per rule, corpus counts, and individual findings (tool name/index, severity, schema path, message). Exit codes: 0 clean or completed benchmark; 1 lint ERROR; 2 transport/input/verification failure; 3 payment required. WARNING alone exits 0.

Live mode performs only initialize, notifications/initialized, and paginated tools/list. It supports Streamable HTTP with JSON or SSE response framing and MCP session headers. It does not invoke tools. Legacy GET/SSE transport, stdio, and authenticated endpoints are not supported in 0.1.0; use an exported JSON response for those. Responses are capped at 32 MiB and 1,000 pages. The contacted server sees your IP and client metadata. Local-file lint makes no network requests. No telemetry.

Frozen rules and real counts

Counts include all 2,300 endpoint rows. Distinct-server columns union by server name; overlapping counts must not be added. R6 is evaluated within each endpoint's tools/list, never across endpoints.

| Rule | Severity | Trigger | Findings | Affected declarations | Distinct servers | | --- | --- | --- | ---: | ---: | ---: | | R1 | ERROR | Name fails ^[a-zA-Z0-9_-]{1,64}$ | 821 | 821 | 26 | | R2 | ERROR | Missing, empty or whitespace-only description | 27 | 27 | 2 | | R3 | ERROR | Missing inputSchema or root type not object | 0 | 0 | 0 | | R4 | WARNING | Property has neither description nor enum keyword | 34,510 | 8,191 | 1,019 | | R5 | ERROR | Root oneOf/anyOf/allOf/not/patternProperties, or external $ref at a schema position | 111 | 111 | 40 | | R6 | ERROR | Duplicate tool name | 0 | 0 | 0 |

R4 checks keyword presence, including nested schema properties. R5 treats any nonempty non-fragment reference (including relative document paths) as external. Schema traversal ignores values in examples/defaults/enums and does not dereference links.

Constraint evidence and limits

  • R1: OpenAI function definition documents the character set and 64-character maximum.
  • R2: That same reference makes description optional. This is a tool-selection quality heuristic retained at the frozen ERROR severity, not a required API field.
  • R3: OpenAI Structured Outputs requires an object root for that mode. This does not prove universal MCP/provider rejection.
  • R5: The same Structured Outputs guide excludes root anyOf and lists allOf/not as unsupported. It lists patternProperties specifically under additional fine-tuned-model restrictions. The full frozen rule, including all external references and root oneOf, is a conservative profile; the cited page does not establish a universal ban for every branch.
  • R6: Duplicate names can make dispatch ambiguous. No documented Anthropic/OpenAI rejection constraint was established for this rule.
  • R4: Documentation quality heuristic, not an API requirement.

Thus the original requirement that every ERROR be justified by a documented provider constraint is not met. This release does not claim complete JSON Schema validation, a strict-mode readiness certificate, a security audit, or observed API failures.

Reproduce every count

The package includes the full compressed retained corpus, its counts, evaluator and counter. Inside the installed package directory:

node scripts/count-corpus.mjs data/tool-surfaces.jsonl.gz /tmp/recount.json
# Compare with data/counts.json; output is deterministic.

The script also accepts the original uncompressed data/tool-surfaces.jsonl. Its SHA-256 is 7a4596a95d845828681b6dfe64ee580b9e11bf15a5534ad4a3821b55f3865aea.

Coverage is partial: 4,609 of 16,444 remote servers were attempted; 2,283 yielded retained tools; 11,835 were unprobed. The collector rejected tools without string names or object inputSchema before retaining an endpoint. This selection biases schema-presence counts. Twenty retained endpoints lacked a successful initialized-notification trace. These are self-declared schemas, not executed behavior or a representative sample of all MCP servers. Original declarations retain their authors' rights; inclusion is not endorsement.

Benchmark: 5 USDT once for 0.1.x

Free lint includes all rules, counts and findings. The paid benchmark command adds per-rule corpus percentile ranking, computed locally from the bundled snapshot. One payment covers your use of version 0.1.x; no subscription or automatic debit. Future snapshots are not promised.

mcp-server-lint benchmark https://YOUR_SERVER/mcp --payer YOUR_TRON_SENDING_ADDRESS
# Also accepts --file tools.json

Send at least 5 USDT in one TRC20 transfer on TRON to TLwka4hLwwm82h5Rje4cKTTFyitgBzuouG, with network fees paid separately. Accepted token contract: TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t. Use a wallet you control; exchange withdrawals may have a different sender. Wait for confirmation and rerun with that sender address. Never supply a private key. The program does not send funds.

Every benchmark invocation performs an unauthenticated HTTPS GET to TronGrid's account TRC20 history API, requesting confirmed incoming transfers. It matches sender, recipient, token contract, decimals, transfer type and integer amount. Network/API failures fail closed with a retry error; absence of payment gives the payment instruction. Pagination is capped at 100 pages. There is no vendor backend, account signup, API key, or automatic refund service.

Gate limitation: this is a local honor-system gate. Public sender addresses are not proof of wallet ownership, and local code can be modified. No cryptographic buyer identity, tamper resistance or exclusive access to the bundled corpus is claimed. TronGrid sees your IP; the request queries the recipient's public history, and sender matching happens locally.

Ranking uses affected-tool fraction per rule. A corpus server with multiple endpoints uses its worst endpoint fraction. cleanerThanPercent is the percentage of corpus servers with a strictly worse fraction; ties have a separate percentage. Higher is cleaner, not necessarily more useful or more secure. The denominator remains 2,283, and R4 never enters the ERROR headline.

Verification and license

Run npm test inside the package. Tests cover rule boundaries, HTTP sessions/SSE/pagination, benchmark denominator, and payment acceptance/rejection. test/fixtures/trongrid-empty-recorded.json is a real empty response recorded September 9, 2026. The allow fixture is explicitly synthetic, derived from that response envelope; it is not a recorded successful payment and proves no revenue.

See LICENSE. Lint and corpus recount may be used free, including commercially. Benchmark use requires the payment above. Publisher: npm user justaiagent. Public package: https://www.npmjs.com/package/mcp-server-lint