aftertaste
Provides a plain git hook to check staged files only, integrating with Git's pre-commit workflow.
Integrates with GitHub code scanning by outputting SARIF files, and provides GitHub-style annotations for CI.
Provides a GitHub Action to run aftertaste in CI, with inputs for path, fail-on, min-score, offline, sarif, and args. Findings appear as annotations on the PR diff and as a table in the job summary.
Checks package imports and declared dependencies against the npm registry to detect hallucinated or undeclared packages, and provides a tool to verify a package exists before installation.
Provides a pre-commit hook to scan staged files for issues before committing.
Checks package imports and declared dependencies against the PyPI registry to detect hallucinated or undeclared packages, and provides a tool to verify a package exists before installation.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@aftertastescan my changed files for hallucinated packages and exposed secrets"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
aftertaste
Find what your AI coding agent left behind.
Agents write a lot of code very quickly, and some of what they leave behind compiles fine and still breaks things: an import of a package that doesn't exist, an OpenAI key in a NEXT_PUBLIC_ variable, // ... existing code ... where your function used to be, a describe.only that quietly switched off the rest of your tests. Linters don't look for this kind of thing. aftertaste does.
npx aftertasteThere's nothing to install or configure and no account to make. It has zero dependencies and takes about a second on most repos. It understands JavaScript, TypeScript and Python projects, and checks for secrets and leftovers in Go, Rust, Java, C#, Ruby and shell too.
That's the real output for examples/vibe-app, a small Next.js app with the usual problems. Try it yourself: npx aftertaste examples/vibe-app.
What it catches
Rule | What it finds | |
Leaks | API keys and tokens for 25+ providers, private keys, database URLs with passwords | |
| ||
| ||
Broken builds | Imports and dependencies that don't exist on npm or PyPI, plus brand-new lookalike packages | |
Imports that work on the agent's machine but aren't in | ||
| ||
Leftover | ||
| ||
Shortcuts |
| |
"In a real app you would...", mock data, simulated payments, | ||
| ||
Mess |
| |
| ||
The same helper copied into several files | ||
| ||
Env vars the code reads that | ||
Packages installed for an approach the agent later abandoned | ||
"Here's the updated code", "I've added...", | ||
Files that have grown past what an agent can edit safely |
Each rule has a page in docs/rules.md covering why it matters and how to fix it. You can also run npx aftertaste explain <rule>.
Related MCP server: CodeMore MCP Server
Hallucinated packages
Models make up package names that sound right. A USENIX Security 2025 study looked at 576,000 generated code samples and found that about 5.2% of the packages commercial models recommended didn't exist, and about 21.7% for open-source models. Attackers know this. They register those names and wait for someone to npm install them, which is called slopsquatting.
So aftertaste checks every import and every declared dependency against the registry:
Doesn't exist on npm or PyPI →
error, with the file and line where it's usedExists but is new and barely used (under 60 days old, under 100 downloads a week) →
warning, because that's what a squatted name looks likeExists but isn't declared →
error, since a fresh install will crash
Only package names go to the registries; your code never does. Packages that exist are cached for a week in ~/.cache/aftertaste, or wherever AFTERTASTE_CACHE_DIR points, so repeat runs make almost no requests. Scopes from .npmrc with a private registry are never looked up. Pass --offline to skip the network entirely. You still get undeclared imports reported, just without confirmation from the registry.
Make your agent check its own work
Claude Code
npx aftertaste init --claudeThis adds two things:
a Stop hook. When Claude says it's done, aftertaste scans the files it changed. If it finds errors, Claude gets the list and keeps working. It only nudges once per stop, so it can't loop.
the MCP server. Claude can call
aftertaste_check_packagebefore installing anything andaftertaste_scanafter a change.
.claude/settings.json:
{
"hooks": {
"Stop": [{ "hooks": [{ "type": "command", "command": "npx --yes aftertaste@1 --agent-hook", "timeout": 120 }] }]
}
}.mcp.json:
{ "mcpServers": { "aftertaste": { "command": "npx", "args": ["--yes", "aftertaste@1", "mcp"] } } }Cursor, Windsurf, Cline, Zed, or any MCP client
Add the same server to the client's MCP config:
{ "mcpServers": { "aftertaste": { "command": "npx", "args": ["--yes", "aftertaste@1", "mcp"] } } }Tool | What it does |
| Scans the project (or only changed files) and returns the findings as a fix list |
| Confirms a package exists on npm/PyPI and flags suspicious new ones, before the agent installs it |
Any chat window
npx aftertaste -f prompt | pbcopy # Windows: | clipPaste the result into any agent. It's a prioritized list grouped by rule, with fix instructions for each one.
In CI and git hooks
GitHub Actions
# .github/workflows/aftertaste.yml (or: npx aftertaste init --github)
name: aftertaste
on: [pull_request, push]
permissions:
contents: read
jobs:
aftertaste:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: WiseeCarrot/aftertaste@v1Findings show up as annotations on the PR diff and as a table in the job summary. The action takes these inputs: path, fail-on (default error), min-score, offline, sarif (a file path, for GitHub code scanning) and args.
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: WiseeCarrot/aftertaste@v1
with:
sarif: aftertaste.sarif
fail-on: never
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: aftertaste.sarifPre-commit
npx aftertaste init --hook # plain git hook: checks staged files onlyOr with the pre-commit framework:
repos:
- repo: https://github.com/WiseeCarrot/aftertaste
rev: v1.0.0
hooks:
- id: aftertasteOther CI
npx aftertaste --since origin/main -f github # GitHub-style annotations
npx aftertaste -f sarif:aftertaste.sarif # SARIF file
npx aftertaste -f json # everything, machine readableBadge
npx aftertaste --badge docs/aftertaste.svgThat writes a local SVG badge (no third-party service) and prints a shields.io link if you'd rather use that:
[](https://github.com/WiseeCarrot/aftertaste)The score starts at 100. Each rule that fires costs 15 for an error, 5 for a warning and 1 for info, multiplied by 1 + log2(count). A rule that fires many times still counts, but one noisy rule can't sink the whole score. Grades: A ≥ 90, B ≥ 80, C ≥ 70, D ≥ 60.
Configuration
Everything is optional. Add aftertaste.config.json (or run npx aftertaste init):
{
"$schema": "https://raw.githubusercontent.com/WiseeCarrot/aftertaste/main/schema.json",
"ignore": ["legacy/", "**/*.generated.ts"],
"failOn": "error",
"minScore": 80,
"rules": {
"debug-leftover": "off",
"localhost-url": "warning",
"oversized-file": ["info", { "maxLines": 1500 }],
"duplicate-block": ["warning", { "minLines": 12 }],
"unused-dependency": ["info", { "ignore": ["@fontsource/inter"] }]
}
}You can also put the same object under an "aftertaste" key in package.json. Files ignored by .gitignore are always skipped.
Silencing a single finding. Put a comment on the line, or the line above. Naming the rule is optional, and anything after -- is a note for whoever reads it next:
const sql = `SELECT * FROM ${table}`; // aftertaste-ignore-line unsafe-code -- table name comes from an allowlist
// aftertaste-ignore-next-line debug-leftover
console.log(report);
// aftertaste-ignore-file truncated-code (anywhere in the file)CLI
aftertaste [path | files...] scan a project (default: current directory)
aftertaste init [--hook] [--github] [--claude]
aftertaste rules list every rule
aftertaste explain <rule> what a rule catches and how to fix it
aftertaste mcp run as an MCP server
--staged / --changed / --since <ref> only report on changed files
--rule <id> run only this rule (repeatable)
-f, --format <name>[:file] pretty, json, sarif, github, markdown, prompt (repeatable)
--badge <file.svg> write a score badge
-q, --quiet hide info-level findings
--fail-on <error|warning|info|never> exit 1 at this level (default: error)
--min-score <n> also exit 1 below this score
--offline never contact npm or PyPI
--fix add missing .gitignore entriesExit codes: 0 clean, 1 findings at or above --fail-on (or score below --min-score), 2 usage or config error.
How it works, and what it won't do
It aims for precision over recall. A tool that cries wolf gets uninstalled, so each rule only fires on patterns that are almost never intentional. Explained ignores (a comment inside the
catch,pass # best effort) aren't flagged, and neither are test files, mocks, seeds or stories where fakes are the point. This repository runs aftertaste on itself in CI withfailOn: "info".It's aware of comments and strings. A small lexer splits every file into code, comments and strings. That way
"eval("in an error message isn't aneval, and a comment that quotes"... existing code ..."isn't an elision.It never prints a whole secret. Reports show
sk-p…Xq (164 chars).It isn't a replacement for your type checker, ESLint/Ruff, a full secret scanner like gitleaks, or a dead-code tool like knip. It covers the gaps between them that agent-written code tends to fall into, and runs alongside all of them.
API
import { scan, prompt } from 'aftertaste';
const result = await scan({ root: '.', online: false });
console.log(result.score, result.grade, result.findings);
console.log(prompt(result)); // the agent-ready fix listContributing
False positives are bugs. Please open an issue with the smallest snippet that triggers it. New rules are welcome too: they need a test that proves they fire, and one that proves they stay quiet on ordinary code. See CONTRIBUTING.md.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
Lets coding agents check their own code for leaked secrets, risky dependencies and AI-code mistakes
Protects AI coding agents from installing malicious open source packages. Every npm and PyPI package is checked against SafeDep’s real-time threat intelligence before installation.
Check packages for CVEs, slopsquatting, and CISA KEV before your AI agent installs them.
Security reviews for coding agents: diffs checked against your org policy and live infrastructure.
Related MCP Servers
- AlicenseAqualityDmaintenanceActs as a security checkpoint for AI coding agents by intercepting package installations to verify existence, check against CVE databases, and block vulnerable or hallucinated dependencies before they reach your codebase. Provides seven security tools including pre-install gates, full project audits, safe version recommendations, and deep transitive dependency scanning for npm and PyPI packages.715 npm4MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to scan code for security and quality issues and receive machine-readable reports with suggested fixes and verification criteria.63 npm2MIT
- AlicenseNot gradedqualityCmaintenanceValidates and checks packages across 19 ecosystems to prevent AI agents from installing hallucinated, deprecated, or malicious packages.20 npmAGPL 3.0
- AlicenseNot gradedqualityBmaintenanceEnables AI coding agents to automatically verify npm packages against the live registry before installation, flagging hallucinated, slopsquatted, or otherwise suspicious packages with risk verdicts.1 npmMIT