npm & PyPI Package Checker for AI Agents — ExactGround
Pricing
from $2.00 / 1,000 symbol checkeds
npm & PyPI Package Checker for AI Agents — ExactGround
npm and PyPI package checker: confirms packages exist and that functions, classes and methods exist in the exact version you use, before an AI agent writes or installs code. MCP endpoint + REST. Pay per check.
Pricing
from $2.00 / 1,000 symbol checkeds
Rating
0.0
(0)
Developer
drop-in apis
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
5 hours ago
Last modified
Categories
Share
ExactGround is an npm and PyPI package checker: before an AI coding agent writes or installs code, it confirms that the package exists and that the function, class or method it is about to call exists in the exact version in your lockfile. It reads the published package itself (TypeScript .d.ts declarations, DefinitelyTyped, Python wheel source and .pyi stubs) without running any of its code, and answers over MCP or REST. You pay per check.
At a glance: pay per check, no subscription: $0.002 per symbol, $0.0005 per package, $0.01 per diff (a typical agent session of 5 package checks, 20 symbol checks and 2 diffs costs about $0.06; failed requests are not charged). Try it with no code: click Start with the prefilled input (batch mode), or connect your agent to the MCP endpoint below.
Last updated: 2026-10-03 · Free local install guard and source: https://github.com/alidaram99/exactground
Why this exists:
-
A USENIX Security 2025 study (Spracklen et al.) had 16 models write 576,000 code samples. Of the 2.23 million package recommendations in them, 440,445 (19.7%) named packages that do not exist, 205,474 unique names. In a separate test, the authors re-ran 500 prompts that had produced a hallucination 10 times each, and 43% of those hallucinated names came back in all 10 runs (paper). Predictable names are what make "slopsquatting" (registering the invented name with malware) practical.
-
The quieter failure is calling an API that is real, but not in your version. Two examples:
useActionStateis in React 19 but not React 18.numpy.asfarraywas removed in NumPy 2.0.
The code looks right, passes review, and breaks at runtime.
What it checks
| Tool (MCP) / endpoint (REST) | Question it answers | Event and price |
|---|---|---|
check_symbols · POST /v1/symbols | Does symbol exist in package@version? Returns exists, kind, signature, @deprecated notes and did-you-mean suggestions | symbol-check $0.002 per symbol |
check_packages · POST /v1/packages | Does the package exist? Was the pinned version published? Is it a look-alike of a popular package, brand new, rarely downloaded, deprecated, or an npm security placeholder? Verdict ok / warn / block | package-check $0.0005 per package |
check_diff · POST /v1/diff | For a git diff or apply_patch: every added JS/TS and Python import, whether its package exists, and whether each imported name and alias.member use exists in the version you pass | diff-check $0.01 per diff |
exists: null means "cannot be known statically". Examples: a Python module that defines __getattr__, a compiled extension, or an npm package with no types at all. ExactGround never reports a guess as a fact.
Connect an agent (MCP)
The server speaks MCP over Streamable HTTP at the Actor's Standby URL plus /mcp. Authenticate with your Apify API token as a Bearer token.
Claude Code
claude mcp add --transport http exactground https://dropin-apis--exactground-api.apify.actor/mcp \--header "Authorization: Bearer $APIFY_TOKEN"
Cursor / VS Code / any MCP client (mcp.json)
{"mcpServers": {"exactground": {"url": "https://dropin-apis--exactground-api.apify.actor/mcp","headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }}}}
Codex (~/.codex/config.toml)
[mcp_servers.exactground]url = "https://dropin-apis--exactground-api.apify.actor/mcp"bearer_token_env_var = "APIFY_TOKEN"
The server's instructions tell the agent when to call each tool:
check_packagesbefore installing;check_symbolswith the lockfile version before using an API it is unsure of;check_diffbefore claiming the change is done.
Agents connected to the Apify MCP server can also find this Actor with search-actors and run it in batch mode with call-actor.
REST examples (real output)
curl -s https://dropin-apis--exactground-api.apify.actor/v1/symbols \-H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \-d '{"ecosystem":"pypi","package":"numpy","version":"2.1.0","symbols":["numpy.linalg.norm","numpy.asfarray"]}'
{"ecosystem": "pypi", "package": "numpy", "version": "2.1.0", "packageExists": true,"artifact": "numpy-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl","results": [{ "symbol": "numpy.linalg.norm", "exists": true, "kind": "function","signature": "(x: ArrayLike, ord: None | float | L[\"fro\", \"nuc\"] = ..., axis: SupportsInt | SupportsIndex | tuple[int, ...] = ..., keepdims: bool = ...,) -> Any","declaredIn": "numpy/linalg/_linalg.pyi" },{ "symbol": "numpy.asfarray", "exists": false, "missingAt": "numpy.asfarray","suggestions": ["array", "asarray", "NDArray", "ndarray", "recarray"] }]}
react@18.2.0 with ["useState", "useActionState"] returns useState with its signature (types read from @types/react@18.3.31 (DefinitelyTyped)) and useActionState: exists: false.
curl -s https://dropin-apis--exactground-api.apify.actor/v1/packages \-H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \-d '{"packages":["react@18.2.0","pypi:requests","pypi:reqeusts"]}'
{ "results": [{ "ecosystem": "npm", "name": "react", "spec": "18.2.0", "verdict": "ok", "registry": "exists", "popularRank": 441, "reasons": [] },{ "ecosystem": "pypi", "name": "requests", "verdict": "ok", "registry": "exists", "popularRank": 7, "reasons": [] },{ "ecosystem": "pypi", "name": "reqeusts", "verdict": "block", "registry": "missing","reasons": ["\"reqeusts\" does not exist on PyPI", "did you mean \"requests\"? (it is 1 edit away)"] } ],"blocked": 1, "warned": 0 }
In one check_diff test, a single diff added imports from React, axios, zod, NumPy, requests and an invented package. ExactGround reported:
- the invented package;
useActionStateagainst React 18.2.0;axios.fetchJson,z.objektandrequests.fetch;numpy.asfarrayagainst NumPy 2.1.0.
That took 4.4 seconds with cold caches.
How it works, and its limits
- npm:
- The package tarball for the resolved version (exact, range or dist-tag) is read in memory.
- Its declaration entry is found through
exports/types/typesVersions, and the TypeScript compiler resolves the export path. - Packages without types fall back to the matching-major
@types/*package; the response says so intypesSource.
- PyPI:
- The pure-Python wheel (or the platform wheel, or the sdist) is read in memory and parsed statically.
- The parser understands re-exports,
from x import *with__all__, class members and base classes, and.pyistubs (preferred when present). - Dynamic attributes and compiled modules return
null, notfalse.
- Not executed, ever: no install scripts, no imports. Archives are capped at 60 MB.
- Private registries are not reachable. The free local guard lets you allow-list private names and scopes.
Pricing
Pay per event, no subscription:
| Event | Price |
|---|---|
symbol-check | $0.002 per symbol ($2 per 1,000) |
package-check | $0.0005 per package ($0.50 per 1,000) |
diff-check | $0.01 per diff |
- A typical agent session (5 package checks, 20 symbol checks, 2 diffs) costs about $0.06.
- Requests that fail (invalid input, registry unreachable) are not charged.
- If you set a maximum total charge, the API stops with HTTP 402 when it is reached.
- Standby requests have no per-run start fee beyond Apify's standard Actor start event.
Batch mode (no code)
Run the Actor normally with packages, symbolChecks and/or diff in the input to get one dataset row per check. This is also what Apify's call-actor uses.
FAQ
How do I stop Claude Code or Cursor from installing hallucinated packages?
Install the free ExactGround hook (https://github.com/alidaram99/exactground). It blocks npm/pnpm/yarn/bun/npx and pip/uv/poetry/pdm/pipx installs of names that do not exist, before they run. This Actor adds the version-exact API checks on top.
What is slopsquatting?
Attackers publish malware under package names that AI models tend to invent. When an agent runs pip install <invented-name>, it installs the attacker's code.
How is this different from Context7?
Context7 puts documentation text into the prompt. ExactGround answers a yes/no question from the published artifact ("does z.objekt exist in zod 4.6.5?") and returns a verdict (ok, warn or block); the free local ExactGround hook is what refuses an install. They work well together.
How is this different from Socket?
Socket scores the security of packages that exist and blocks known malware at install. ExactGround focuses on names and APIs that do not exist in the version you use. Its local guard runs inside the agent's hooks.
Which languages are supported?
npm (JavaScript and TypeScript) and PyPI (Python).
Is my code sent anywhere?
Only what you send in a request: package names and symbol paths, or the diff for check_diff. Nothing is stored after the response.
Is this affiliated with npm, PyPI, Anthropic, OpenAI or Cursor?
No. It is an independent tool.